{"_id":"@aminhanifm/locksignal","name":"@aminhanifm/locksignal","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aminhanifm/locksignal","version":"0.1.0","description":"Explain changes between two npm package-lock.json files and identify changes that deserve human review.","type":"module","license":"MIT","author":{"name":"Amin Hanif"},"repository":{"type":"git","url":"git+https://github.com/aminhanifm/LockSignal.git"},"homepage":"https://aminhanifm.github.io/projects/LockSignal/","bugs":{"url":"https://github.com/aminhanifm/LockSignal/issues"},"keywords":["package-lock","lockfile","dependency-review","npm","cli","typescript","supply-chain"],"engines":{"node":">=22"},"publishConfig":{"access":"public"},"overrides":{"esbuild":"0.28.1"},"bin":{"locksignal":"dist/cli/bin.js"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./cli":{"types":"./dist/cli/index.d.ts","import":"./dist/cli/index.js"}},"scripts":{"dev":"vite --config demo/vite.config.ts","build":"tsup","build:demo":"vite build --config demo/vite.config.ts","preview":"vite preview --config demo/vite.config.ts","format":"prettier --check .","format:write":"prettier --write .","lint":"eslint . --max-warnings=0","typecheck":"tsc --noEmit && tsc --noEmit -p demo/tsconfig.json && tsc --noEmit -p e2e/tsconfig.json","test":"vitest run","test:coverage":"vitest run --coverage","test:e2e":"node scripts/run-e2e.mjs","pack:dry-run":"npm pack --dry-run","verify:package":"node scripts/verify-package.mjs","check":"npm run format && npm run lint && npm run typecheck && npm run test:coverage && npm run build && npm run build:demo && npm run verify:package"},"dependencies":{"commander":"^14.0.0"},"devDependencies":{"@eslint/js":"^9.39.4","@playwright/test":"^1.58.2","@testing-library/jest-dom":"^7.0.0","@testing-library/react":"^16.3.2","@testing-library/user-event":"^14.6.1","@types/node":"^24.10.0","@types/react":"^19.2.14","@types/react-dom":"^19.2.3","@vitejs/plugin-react":"^6.0.2","@vitest/coverage-v8":"^4.0.18","eslint":"^9.39.4","eslint-plugin-react-hooks":"^7.0.1","eslint-plugin-react-refresh":"^0.4.26","globals":"^16.5.0","jsdom":"^29.1.1","prettier":"^3.8.1","react":"^19.2.6","react-dom":"^19.2.6","tsup":"^8.5.0","typescript":"^5.9.3","typescript-eslint":"^8.56.0","vite":"^8.0.13","vitest":"^4.0.18"},"gitHead":"a875d386525b69c8a7ffb6b412f0cfa326d0f41a","_id":"@aminhanifm/locksignal@0.1.0","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-SXiIOHmLqHZGf/iNI17YcosjtsI5nVSzVX26DaSQ1JGthqoLUnr84tOG6cH5CTB3sNDa8xO/01t+eiu5Ah2x1A==","shasum":"3e5e2755405d7e735f026e81c039f33a900040d2","tarball":"https://registry.npmjs.org/@aminhanifm/locksignal/-/locksignal-0.1.0.tgz","fileCount":15,"unpackedSize":360172,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFa60wci+bkQtnoVO0lMG4HFc4Os9hE/WgKHssJfAACiAiEAwOfEKQdskOAVTwUN6LbkplpZiR9dMZmkWic7LL4tC8c="}]},"_npmUser":{"name":"aminhanifm","email":"aminhanif24@gmail.com"},"directories":{},"maintainers":[{"name":"aminhanifm","email":"aminhanif24@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/locksignal_0.1.0_1784917693141_0.9660151813903199"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-24T18:28:12.963Z","0.1.0":"2026-07-24T18:28:13.324Z","modified":"2026-07-24T18:28:13.589Z"},"maintainers":[{"name":"aminhanifm","email":"aminhanif24@gmail.com"}],"description":"Explain changes between two npm package-lock.json files and identify changes that deserve human review.","homepage":"https://aminhanifm.github.io/projects/LockSignal/","keywords":["package-lock","lockfile","dependency-review","npm","cli","typescript","supply-chain"],"repository":{"type":"git","url":"git+https://github.com/aminhanifm/LockSignal.git"},"author":{"name":"Amin Hanif"},"bugs":{"url":"https://github.com/aminhanifm/LockSignal/issues"},"license":"MIT","readme":"# LockSignal\n\nLockSignal explains what changed between two npm `package-lock.json` files and highlights changes that deserve human review.\n\nLockfiles are precise but difficult to inspect manually. A one-line dependency update can replace sources, remove integrity data, introduce install scripts, or expand a transitive tree across hundreds of lines. LockSignal turns that noise into a deterministic report with the exact evidence, a plain-language reason, and a suggested review step.\n\nLockSignal is an explainability tool, not a vulnerability scanner. It does not label packages safe or malicious and cannot guarantee that a dependency is trustworthy.\n\n## Why LockSignal exists\n\nReviewers should not need to infer the meaningful parts of a lockfile change from a raw JSON diff. LockSignal answers:\n\n- Which installed package locations were added, removed, upgraded, or downgraded?\n- Was a change direct or transitive, and did its runtime/development/optional classification change?\n- Did a package move to Git, a local file, insecure HTTP, or a different registry hostname?\n- Did integrity data disappear or change unexpectedly?\n- Is a package newly marked with `hasInstallScript`?\n- Did the tree or duplicate-version count expand?\n\nEvery answer comes from the two supplied lockfiles. There are no registry lookups, accounts, API keys, telemetry, AI calls, or backend services.\n\n## What it detects\n\n- Added and removed installed package locations\n- Semantic-version upgrades, downgrades, and direct major-version changes\n- Non-semver version changes\n- Direct and transitive dependency status\n- Runtime, development, and optional classification changes\n- Resolved URL, protocol, source kind, and hostname changes\n- Integrity additions, removals, and changes\n- Newly introduced `hasInstallScript` flags\n- New Git, file, insecure HTTP, and custom-registry sources\n- Increased and reduced duplicate-version counts\n- Dependency-tree expansion and contraction\n- Scoped packages, nested installations, and multiple installed versions\n\n## What it does not detect\n\nLockSignal does not:\n\n- Query vulnerability, malware, provenance, or package-reputation databases\n- Fetch package metadata or resolved URLs\n- Install packages or execute lifecycle scripts\n- Analyze package source code or tarball contents\n- Confirm maintainer identity or registry ownership\n- Support yarn, pnpm, Bun, or npm lockfile version 1 in v0.1.0\n- Determine whether a package is safe, malicious, or suitable for production\n\n## Browser workflow\n\nThe static demo is configured for the future canonical URL:\n\n`https://aminhanifm.github.io/projects/LockSignal/`\n\nIt is not deployed by this repository setup.\n\n1. Load or drag the **Before** `package-lock.json`.\n2. Load or drag the **After** `package-lock.json`.\n3. Run the comparison.\n4. Review severity totals and package-change counts.\n5. Search or filter by severity, direct/transitive status, and change type.\n6. Expand a finding to inspect its exact lockfile evidence.\n7. Export a Markdown or versioned JSON report.\n\nThe demo includes three synthetic examples: a quiet patch, a major update with a new install script and tree expansion, and a source/integrity case. Browser inputs are limited to 10 MB per file and remain in memory for the current tab.\n\n## CLI installation and examples\n\nAfter the package is published:\n\n```sh\nnpm install --global @aminhanifm/locksignal\n```\n\nCompare two files:\n\n```sh\nlocksignal compare before-package-lock.json after-package-lock.json\n```\n\nWrite Markdown without overwriting an existing file:\n\n```sh\nlocksignal compare before-package-lock.json after-package-lock.json \\\n  --format markdown \\\n  --output lockfile-review.md\n```\n\nProduce JSON for another tool and fail when a warning or high finding is present:\n\n```sh\nlocksignal compare before-package-lock.json after-package-lock.json \\\n  --format json \\\n  --fail-on warning\n```\n\nAvailable options:\n\n```text\n--format text|markdown|json\n--output <file>\n--force\n--fail-on none|warning|high\n--no-color\n--version\n--help\n```\n\nReports go to stdout by default. Errors go to stderr. `--output` refuses to replace an existing file unless `--force` is present.\n\n### Exit codes\n\n| Code | Meaning                                                               |\n| ---: | --------------------------------------------------------------------- |\n|  `0` | Comparison completed and the `--fail-on` threshold was not reached    |\n|  `1` | Comparison completed, but a finding reached the requested threshold   |\n|  `2` | Usage, file I/O, validation, identical-input, or report-writing error |\n\n`--fail-on warning` matches warning and high findings. `--fail-on high` matches only high findings. `--fail-on none` is the default.\n\n## Noisy diff, short report\n\nA raw lockfile update might span dozens of changed lines:\n\n```diff\n- \"@fixture/builder\": \"1.8.2\"\n+ \"@fixture/builder\": \"2.0.0\"\n+ \"hasInstallScript\": true\n+ \"@fixture/adapter\": \"1.0.0\"\n+ \"@fixture/shared\": \"2.0.0\"\n```\n\nLockSignal turns it into reviewable statements:\n\n```text\nFindings  HIGH 0  WARNING 3  INFO 8\nChanges   +2  -0  ↑2  ↓0\n\n[WARNING] @fixture/builder · major-version\n  Direct dependency @fixture/builder crosses a semantic-version major boundary.\n  review: Read the migration guide and release notes, then test behavior affected\n  by breaking changes.\n\n[WARNING] @fixture/builder · install-script\n  @fixture/builder is newly marked as having an install script.\n  review: Inspect the package's install scripts before installing dependencies.\n```\n\nThe names, URLs, and hashes in committed examples are synthetic.\n\n## TypeScript API\n\n```ts\nimport {\n  compareLockfiles,\n  createMarkdownReport,\n  parseLockfile,\n  summarizeComparison,\n} from \"@aminhanifm/locksignal\";\n\nconst before = parseLockfile(beforeText, { label: \"before-package-lock.json\" });\nconst after = parseLockfile(afterText, { label: \"after-package-lock.json\" });\n\nconst comparison = compareLockfiles(before, after);\nconst summary = summarizeComparison(comparison);\nconst markdown = createMarkdownReport(comparison);\n\nconsole.log(summary.severities.high);\nconsole.log(markdown);\n```\n\nThe JSON-compatible comparison result is versioned and contains:\n\n```json\n{\n  \"schemaVersion\": \"1.0.0\",\n  \"toolVersion\": \"0.1.0\",\n  \"supportedLockfileVersions\": [2, 3],\n  \"inputs\": {\n    \"before\": {\n      \"label\": \"before-package-lock.json\",\n      \"lockfileVersion\": 3,\n      \"packageCount\": 42,\n      \"fingerprint\": \"81c15fca\"\n    },\n    \"after\": {\n      \"label\": \"after-package-lock.json\",\n      \"lockfileVersion\": 3,\n      \"packageCount\": 45,\n      \"fingerprint\": \"2ad0eb13\"\n    }\n  },\n  \"packageChanges\": [],\n  \"findings\": [],\n  \"summary\": {}\n}\n```\n\nThe actual result includes typed input metadata, package-change records, explicit findings, and complete summary counts. It does not embed either full lockfile.\n\nParsing failures throw a `LockfileError` with a structured `detail` object and one of these codes:\n\n`FILE_TOO_LARGE`, `INVALID_JSON`, `NOT_NPM_LOCKFILE`, `MISSING_FIELD`, `UNSUPPORTED_VERSION`, `INVALID_PACKAGE_ENTRY`, or `IDENTICAL_INPUTS`.\n\n## Severity rules\n\nLockSignal intentionally avoids a numeric “security score.” Findings use deterministic rules:\n\n### High\n\n- A new or changed source resolves through Git, a local file, or insecure HTTP\n- Integrity disappears from a package that still resolves as a registry tarball\n\n### Warning\n\n- A package is newly marked with `hasInstallScript`\n- Integrity changes without a corresponding version or resolved-source change\n- The source hostname changes\n- A direct dependency crosses a semantic-version major boundary\n- A package starts resolving through a non-default registry hostname\n\n### Info\n\n- A package location is added or removed\n- A version, source URL, integrity value, or dependency classification changes without a stronger rule\n- Duplicate installed versions increase or decrease\n- The installed dependency tree expands or contracts\n\nSeverity expresses review priority from lockfile evidence. It is not a claim about package intent or safety.\n\n## Privacy and security model\n\n- Comparison is read-only.\n- The core never uses filesystem or browser APIs.\n- The CLI only reads the two requested files and optionally writes a report.\n- The browser processes files in memory and uses no `localStorage` or `sessionStorage`.\n- No runtime network requests, telemetry, uploads, registry queries, or resolved-URL fetches are made.\n- Package scripts are never executed and analyzed dependencies are never installed.\n- React escapes displayed user-controlled values; Markdown and JSON exporters escape their formats.\n\nLockfiles can contain private registry URLs, organization names, and other operational details. Review exported reports before sharing them.\n\nSee [SECURITY.md](SECURITY.md) for reporting guidance and the security boundary.\n\n## Architecture\n\n```text\nsrc/core/      environment-independent parser, normalization, comparison, findings, reports\nsrc/cli/       importable CLI functions and a minimal executable wrapper\ndemo/          static React + Vite review application\ntest/fixtures/ synthetic npm v2/v3 lockfiles\ntest/          core, reporting, and CLI tests\ne2e/           Playwright browser and mobile journeys\nscripts/       packed npm consumer and browser-runner verification\n```\n\nThe browser and CLI consume the same core result. Package identity is normalized by installation location and package name, so nested and duplicate installations remain distinct. Output arrays are sorted and finding IDs are derived from stable evidence, making repeat runs deterministic.\n\nThe executable wrapper is separate from the reusable CLI module. This keeps importing CLI helpers side-effect free and ensures npm’s `.bin` wrappers work on Windows, Linux, and macOS.\n\n## Supported lockfile versions\n\nVersion 0.1.0 supports npm `package-lock.json` lockfile versions **2 and 3** with a `packages` map and root `packages[\"\"]` entry.\n\nUnsupported versions and malformed shapes are rejected explicitly. LockSignal does not silently coerce a yarn, pnpm, Bun, npm v1, shrinkwrap-only, or arbitrary JSON file into a comparison.\n\n## Limitations\n\n- Package changes are matched by installed location; a package moving locations appears as one removal and one addition.\n- Semantic-version direction and major boundaries require complete valid versions such as `1.2.3`. Other version strings are reported as changed without inferred ordering.\n- Registry classification treats `registry.npmjs.org` as the default npm registry. Other HTTPS hosts are reported as custom registries for review, even when intentionally configured.\n- Git, file, and HTTP signals identify source changes only; they do not inspect the referenced content.\n- Integrity comparison uses the literal lockfile values. LockSignal does not download tarballs to recompute hashes.\n- Workspace links and unusual npm-generated entries are normalized conservatively and may produce fewer source findings.\n- Results explain the two snapshots only; they do not infer why npm selected a transitive version.\n\n## Development and testing\n\nRequirements: Node.js 22 or 24 and npm.\n\n```sh\nnpm install\nnpm run dev\n```\n\nThe demo development URL is:\n\n`http://127.0.0.1:4174/projects/LockSignal/`\n\nQuality commands:\n\n```sh\nnpm run format\nnpm run lint\nnpm run typecheck\nnpm test\nnpm run test:coverage\nnpm run build\nnpm run build:demo\nnpm run verify:package\nnpm run test:e2e\n```\n\n`npm run build:demo` writes the static site to `demo-dist`. `npm run verify:package` creates a tarball in a temporary directory, installs it in a clean consumer, imports the API, and executes `locksignal --version`.\n\nCI runs formatting, linting, strict type checks, tests, coverage, package and demo builds, pack inspection, packed installation, and browser journeys across Ubuntu/Windows and Node.js 22/24 as appropriate.\n\n## Contributing\n\nContributions are welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md), use synthetic fixtures, and preserve the local-first, deterministic, explainable, read-only product principles.\n\n## Roadmap\n\nPotential post-0.1 work, subject to explicit design and test coverage:\n\n- More explanation for workspace and peer-dependency topology\n- Configurable policies built from explicit rules rather than a score\n- Additional report integrations that preserve local processing\n- Carefully scoped support for other lockfile ecosystems\n\nYarn, pnpm, Bun, registry lookups, vulnerability databases, GitHub Actions, and automatic remediation are intentionally outside v0.1.0.\n\n## License\n\nMIT © 2026 Amin Hanif. See [LICENSE](LICENSE).\n","readmeFilename":"README.md","_rev":"1-9626d5f46ebe960730930f02b2a3905d"}