{"_id":"@azaru/tscrap","name":"@azaru/tscrap","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.2":{"name":"@azaru/tscrap","version":"0.1.2","description":"Strict CRAP score gate for TypeScript and JavaScript projects","type":"module","license":"MIT","repository":{"type":"git","url":"git+https://github.com/azaru/tscrap.git"},"homepage":"https://github.com/azaru/tscrap#readme","bugs":{"url":"https://github.com/azaru/tscrap/issues"},"engines":{"node":">=22.12.0"},"bin":{"tscrap":"dist/cli.js"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"types":"./dist/index.d.ts","publishConfig":{"access":"public"},"scripts":{"build":"tsc -p tsconfig.build.json","format":"biome format --write .","format:check":"biome format .","lint":"biome lint .","typecheck":"tsc -p tsconfig.json --noEmit","test:unit":"vitest run","test:coverage":"vitest run --coverage","architecture":"depcruise --config .dependency-cruiser.cjs src test scripts","quality:quick":"npm run format:check && npm run lint && npm run typecheck && npm run test:unit && npm run architecture","quality:crap":"npm run build && node dist/cli.js --src src --coverage coverage/coverage-final.json --threshold 8","quality:sonar":"node --env-file-if-exists=.env scripts/quality/run-sonar.mjs","quality:mutation":"stryker run","quality:full":"npm run quality:quick && npm run test:coverage && npm run quality:crap && npm run quality:sonar && npm run quality:mutation","sonar:provision":"node --env-file-if-exists=.env scripts/setup/provision-sonar.mjs","hooks:install":"node scripts/setup/install-hooks.mjs","prepack":"npm run build"},"dependencies":{"typescript":"^5.9.3"},"devDependencies":{"@biomejs/biome":"^2.3.11","@sonar/scan":"5.0.0","@stryker-mutator/core":"^9.6.1","@stryker-mutator/typescript-checker":"^9.6.1","@stryker-mutator/vitest-runner":"^9.6.1","@types/node":"^22.20.1","@vitest/coverage-v8":"^4.1.10","dependency-cruiser":"^17.3.7","fast-check":"^4.5.3","vitest":"^4.1.10"},"gitHead":"09a7215cf5fe26b2782bda58c633ec97fecb2f75","_id":"@azaru/tscrap@0.1.2","_nodeVersion":"22.22.3","_npmVersion":"12.0.1","dist":{"integrity":"sha512-O/GteK4QRgUuIQ9wwqnm9X4zs9qito94Ilx4MtTrAPl2KA4bqFeOUvEYWmbyPZ+n39p7W+LM+ibNEpM/yHVYYg==","shasum":"2f76cb7bb2d32f7883eedb4a81d851952aa715c0","tarball":"https://registry.npmjs.org/@azaru/tscrap/-/tscrap-0.1.2.tgz","fileCount":43,"unpackedSize":94816,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFjD3TTUYd+Rb6Mv7DpyW1QYLkKIZop+n9dV9KQ+0bxqAiEA3uZjrx5nLDTKBABQJPM/XXpl3hOvkztwhEdRjQR1JnA="}]},"_npmUser":{"name":"azaru","email":"azaru100@gmail.com"},"directories":{},"maintainers":[{"name":"azaru","email":"azaru100@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/tscrap_0.1.2_1785435796409_0.07483753748170319"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-30T18:23:16.235Z","0.1.2":"2026-07-30T18:23:16.569Z","modified":"2026-07-30T18:23:16.801Z"},"maintainers":[{"name":"azaru","email":"azaru100@gmail.com"}],"description":"Strict CRAP score gate for TypeScript and JavaScript projects","homepage":"https://github.com/azaru/tscrap#readme","repository":{"type":"git","url":"git+https://github.com/azaru/tscrap.git"},"bugs":{"url":"https://github.com/azaru/tscrap/issues"},"license":"MIT","readme":"# tscrap\n\n`tscrap` is a strict CRAP score gate for TypeScript and JavaScript projects. It\ncombines cyclomatic complexity with source-mapped Istanbul statement coverage\nand exits non-zero when code is too risky to change safely.\n\n## Install\n\n`tscrap` requires Node.js 22.12 or newer.\n\n```sh\nnpm install --save-dev @azaru/tscrap\n```\n\n```sh\npnpm add --save-dev @azaru/tscrap\n```\n\n## Quick start\n\nGenerate a source-mapped Istanbul `coverage-final.json` with your test runner,\nthen point `tscrap` at the production source and coverage report:\n\n```sh\nnpx tscrap --src src --coverage coverage/coverage-final.json\n```\n\nFor repeatable local and CI runs, add the command to `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"quality:crap\": \"tscrap --src src --coverage coverage/coverage-final.json\"\n  }\n}\n```\n\n```sh\nnpm run quality:crap\n```\n\nA passing run prints a compact summary:\n\n```text\ntscrap 0.1.2: PASSED\nthreshold: 8\nfiles: 10, units: 114, violations: 0\n```\n\n## What it measures\n\nThe CRAP metric combines the cyclomatic complexity of a unit with the fraction\nof its statements covered by tests:\n\n```text\nCRAP = complexity² × (1 − coverage)³ + complexity\n```\n\nThe metric was introduced by Alberto Savoia and Bob Evans. The threshold is a\nseparate policy decision: `tscrap` deliberately defaults to 8. You can select\nany finite positive threshold that fits your project.\n\nComplex, poorly covered code receives a high score. Increasing meaningful test\ncoverage or reducing branching lowers the score. Fully covered code still\nretains its cyclomatic complexity as its minimum score.\n\n## Strict analysis\n\n`tscrap` treats incomplete evidence as an analysis error instead of producing\nan optimistic score. It fails when:\n\n- no supported source file or executable unit is found;\n- the coverage report is missing, empty, malformed, or older than the source;\n- a production source file is absent from coverage;\n- a function, method, or module unit has no attributable statement coverage;\n- coverage locations cannot be mapped safely.\n\nFunctions, methods, constructors, accessors, and executable top-level module\ncode are measured independently. Nested functions do not add their decisions or\nstatements to their parent. Declaration-only files are discovered but do not\nrequire coverage.\n\nSupported source extensions are `.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`,\n`.mjs`, and `.cjs`. The `coverage`, `dist`, `node_modules`, and `.git`\ndirectories are ignored during source discovery.\n\n## CLI\n\n```text\ntscrap --src <path> --coverage <coverage-final.json> [options]\n```\n\n| Option | Description |\n| --- | --- |\n| `--src <path>` | Source root or file. Repeat for multiple roots. |\n| `--coverage <path>` | Source-mapped Istanbul `coverage-final.json`. |\n| `--threshold <number>` | Maximum accepted CRAP score. Default: `8`. |\n| `--exclude <glob>` | Explicit source exclusion. Repeat for multiple patterns. |\n| `--format <text\\|json>` | Report format. Default: `text`. |\n| `--output <path>` | Write the report to a file instead of stdout. |\n| `--verbose` | Include every metric and the complete source inventory. |\n| `--help` | Show CLI help. |\n| `--version` | Show the installed version. |\n\nMultiple roots, explicit exclusions, and a machine-readable report can be\ncombined:\n\n```sh\ntscrap \\\n  --src packages/core/src \\\n  --src packages/web/src \\\n  --coverage coverage/coverage-final.json \\\n  --exclude \"packages/web/src/generated/**\" \\\n  --format json \\\n  --output reports/crap.json\n```\n\n### Exit codes\n\n| Code | Meaning |\n| --- | --- |\n| `0` | Analysis completed and every unit passed. |\n| `1` | Input or coverage was invalid or incomplete. |\n| `2` | Analysis completed and at least one unit exceeded the threshold. |\n\nText output shows the summary and only the information needed to act on a\nfailure. JSON output always contains the complete versioned report, including\nnormalized inputs, source and coverage inventories, per-unit evidence,\ndiagnostics, and summary values.\n\n## Library API\n\nThe analyzer and formula are also available as an ES module:\n\n```ts\nimport { analyzeProject, calculateCrap } from \"@azaru/tscrap\";\n\nconst score = calculateCrap(4, 0.5); // 6\n\nconst report = await analyzeProject({\n  sourceRoots: [\"src\"],\n  coverageFile: \"coverage/coverage-final.json\",\n  threshold: 8,\n});\n\nif (!report.passed) {\n  console.error(report.diagnostics);\n}\n```\n\n`calculateCrap` accepts coverage as a fraction from `0` to `1`.\n`analyzeProject` returns an `AnalysisReport` with schema version `1`.\n\n## License\n\n[MIT](LICENSE)\n","readmeFilename":"README.md","_rev":"1-4a901d2cb838dd1f2fe49c63ee6f3f33"}