LockSignal explains what changed between two npm package-lock.json files and highlights changes that deserve human review.
Lockfiles 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.
LockSignal is an explainability tool, not a vulnerability scanner. It does not label packages safe or malicious and cannot guarantee that a dependency is trustworthy.
Reviewers should not need to infer the meaningful parts of a lockfile change from a raw JSON diff. LockSignal answers:
- Which installed package locations were added, removed, upgraded, or downgraded?
- Was a change direct or transitive, and did its runtime/development/optional classification change?
- Did a package move to Git, a local file, insecure HTTP, or a different registry hostname?
- Did integrity data disappear or change unexpectedly?
- Is a package newly marked with
hasInstallScript? - Did the tree or duplicate-version count expand?
Every answer comes from the two supplied lockfiles. There are no registry lookups, accounts, API keys, telemetry, AI calls, or backend services.
- Added and removed installed package locations
- Semantic-version upgrades, downgrades, and direct major-version changes
- Non-semver version changes
- Direct and transitive dependency status
- Runtime, development, and optional classification changes
- Resolved URL, protocol, source kind, and hostname changes
- Integrity additions, removals, and changes
- Newly introduced
hasInstallScriptflags - New Git, file, insecure HTTP, and custom-registry sources
- Increased and reduced duplicate-version counts
- Dependency-tree expansion and contraction
- Scoped packages, nested installations, and multiple installed versions
LockSignal does not:
- Query vulnerability, malware, provenance, or package-reputation databases
- Fetch package metadata or resolved URLs
- Install packages or execute lifecycle scripts
- Analyze package source code or tarball contents
- Confirm maintainer identity or registry ownership
- Support yarn, pnpm, Bun, or npm lockfile version 1 in v0.1.0
- Determine whether a package is safe, malicious, or suitable for production
Try the interactive LockSignal demo
The static browser application runs at the canonical portfolio URL above.
- Load or drag the Before
package-lock.json. - Load or drag the After
package-lock.json. - Run the comparison.
- Review severity totals and package-change counts.
- Search or filter by severity, direct/transitive status, and change type.
- Expand a finding to inspect its exact lockfile evidence.
- Export a Markdown or versioned JSON report.
The 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.
After the package is published:
npm install --global @aminhanifm/locksignalCompare two files:
locksignal compare before-package-lock.json after-package-lock.jsonWrite Markdown without overwriting an existing file:
locksignal compare before-package-lock.json after-package-lock.json \
--format markdown \
--output lockfile-review.mdProduce JSON for another tool and fail when a warning or high finding is present:
locksignal compare before-package-lock.json after-package-lock.json \
--format json \
--fail-on warningAvailable options:
--format text|markdown|json
--output <file>
--force
--fail-on none|warning|high
--no-color
--version
--help
Reports go to stdout by default. Errors go to stderr. --output refuses to replace an existing file unless --force is present.
| Code | Meaning |
|---|---|
0 |
Comparison completed and the --fail-on threshold was not reached |
1 |
Comparison completed, but a finding reached the requested threshold |
2 |
Usage, file I/O, validation, identical-input, or report-writing error |
--fail-on warning matches warning and high findings. --fail-on high matches only high findings. --fail-on none is the default.
A raw lockfile update might span dozens of changed lines:
- "@fixture/builder": "1.8.2"
+ "@fixture/builder": "2.0.0"
+ "hasInstallScript": true
+ "@fixture/adapter": "1.0.0"
+ "@fixture/shared": "2.0.0"LockSignal turns it into reviewable statements:
Findings HIGH 0 WARNING 3 INFO 8
Changes +2 -0 ↑2 ↓0
[WARNING] @fixture/builder · major-version
Direct dependency @fixture/builder crosses a semantic-version major boundary.
review: Read the migration guide and release notes, then test behavior affected
by breaking changes.
[WARNING] @fixture/builder · install-script
@fixture/builder is newly marked as having an install script.
review: Inspect the package's install scripts before installing dependencies.
The names, URLs, and hashes in committed examples are synthetic.
import {
compareLockfiles,
createMarkdownReport,
parseLockfile,
summarizeComparison,
} from "@aminhanifm/locksignal";
const before = parseLockfile(beforeText, { label: "before-package-lock.json" });
const after = parseLockfile(afterText, { label: "after-package-lock.json" });
const comparison = compareLockfiles(before, after);
const summary = summarizeComparison(comparison);
const markdown = createMarkdownReport(comparison);
console.log(summary.severities.high);
console.log(markdown);The JSON-compatible comparison result is versioned and contains:
{
"schemaVersion": "1.0.0",
"toolVersion": "0.1.0",
"supportedLockfileVersions": [2, 3],
"inputs": {
"before": {
"label": "before-package-lock.json",
"lockfileVersion": 3,
"packageCount": 42,
"fingerprint": "81c15fca"
},
"after": {
"label": "after-package-lock.json",
"lockfileVersion": 3,
"packageCount": 45,
"fingerprint": "2ad0eb13"
}
},
"packageChanges": [],
"findings": [],
"summary": {}
}The actual result includes typed input metadata, package-change records, explicit findings, and complete summary counts. It does not embed either full lockfile.
Parsing failures throw a LockfileError with a structured detail object and one of these codes:
FILE_TOO_LARGE, INVALID_JSON, NOT_NPM_LOCKFILE, MISSING_FIELD, UNSUPPORTED_VERSION, INVALID_PACKAGE_ENTRY, or IDENTICAL_INPUTS.
LockSignal intentionally avoids a numeric “security score.” Findings use deterministic rules:
- A new or changed source resolves through Git, a local file, or insecure HTTP
- Integrity disappears from a package that still resolves as a registry tarball
- A package is newly marked with
hasInstallScript - Integrity changes without a corresponding version or resolved-source change
- The source hostname changes
- A direct dependency crosses a semantic-version major boundary
- A package starts resolving through a non-default registry hostname
- A package location is added or removed
- A version, source URL, integrity value, or dependency classification changes without a stronger rule
- Duplicate installed versions increase or decrease
- The installed dependency tree expands or contracts
Severity expresses review priority from lockfile evidence. It is not a claim about package intent or safety.
- Comparison is read-only.
- The core never uses filesystem or browser APIs.
- The CLI only reads the two requested files and optionally writes a report.
- The browser processes files in memory and uses no
localStorageorsessionStorage. - No runtime network requests, telemetry, uploads, registry queries, or resolved-URL fetches are made.
- Package scripts are never executed and analyzed dependencies are never installed.
- React escapes displayed user-controlled values; Markdown and JSON exporters escape their formats.
Lockfiles can contain private registry URLs, organization names, and other operational details. Review exported reports before sharing them.
See SECURITY.md for reporting guidance and the security boundary.
src/core/ environment-independent parser, normalization, comparison, findings, reports
src/cli/ importable CLI functions and a minimal executable wrapper
demo/ static React + Vite review application
test/fixtures/ synthetic npm v2/v3 lockfiles
test/ core, reporting, and CLI tests
e2e/ Playwright browser and mobile journeys
scripts/ packed npm consumer and browser-runner verification
The 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.
The 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.
Version 0.1.0 supports npm package-lock.json lockfile versions 2 and 3 with a packages map and root packages[""] entry.
Unsupported 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.
- Package changes are matched by installed location; a package moving locations appears as one removal and one addition.
- 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. - Registry classification treats
registry.npmjs.orgas the default npm registry. Other HTTPS hosts are reported as custom registries for review, even when intentionally configured. - Git, file, and HTTP signals identify source changes only; they do not inspect the referenced content.
- Integrity comparison uses the literal lockfile values. LockSignal does not download tarballs to recompute hashes.
- Workspace links and unusual npm-generated entries are normalized conservatively and may produce fewer source findings.
- Results explain the two snapshots only; they do not infer why npm selected a transitive version.
Requirements: Node.js 22 or 24 and npm.
npm install
npm run devThe demo development URL is:
http://127.0.0.1:4174/projects/LockSignal/
Quality commands:
npm run format
npm run lint
npm run typecheck
npm test
npm run test:coverage
npm run build
npm run build:demo
npm run verify:package
npm run test:e2enpm 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.
CI 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.
Contributions are welcome. Start with CONTRIBUTING.md, use synthetic fixtures, and preserve the local-first, deterministic, explainable, read-only product principles.
Potential post-0.1 work, subject to explicit design and test coverage:
- More explanation for workspace and peer-dependency topology
- Configurable policies built from explicit rules rather than a score
- Additional report integrations that preserve local processing
- Carefully scoped support for other lockfile ecosystems
Yarn, pnpm, Bun, registry lookups, vulnerability databases, GitHub Actions, and automatic remediation are intentionally outside v0.1.0.
MIT © 2026 Amin Hanif. See LICENSE.