Skip to content

nodejs: enforce exact npm dependency policy - #2700

Open
roblourens wants to merge 2 commits into
mainfrom
agents/enforce-npm-dependency-policy
Open

roblourens wants to merge 2 commits into
mainfrom
agents/enforce-npm-dependency-policy

Conversation

@roblourens

@roblourens roblourens commented Sep 16, 2026

Copy link
Copy Markdown

Summary

  • pin the Node.js SDK's published direct production dependencies to exact versions
  • default future npm saves to exact versions
  • verify every external package/version in the resolved production closure from package-lock.json has been published for at least seven full days before release packaging
  • include optional and devOptional production nodes while excluding strictly dev-only, local-link, and bundled entries
  • verify packed release manifests preserve the exact source dependency requirements

Why

The seven-day Dependabot cooldown added in #2596 delays dependency update PRs, but a published semver range can still resolve to a newer release immediately. Exact direct requirements keep downstream direct dependency selection deterministic while Dependabot continues to automate upgrades after the cooldown.

The lockfile closure check also prevents releasing when the complete production graph resolved and tested by CI contains a newly published external transitive or optional package. This includes Koffi's 18 @koromix/* optional platform packages.

This unblocks downstream mirrored feeds such as VS Code's ADO feed, where newly published public npm packages may not be available until their quarantine period ends.

Scope

A library's package-lock.json is not published or honored for consumer installs. This check therefore proves that the release-time production graph is at least seven days old, but it does not freeze future consumer resolution of ranges declared by transitive dependencies. Fully freezing that graph would require publishing an npm-shrinkwrap.json or bundling dependencies, which is outside this change.

The selected direct production dependency versions and authoritative npm publication timestamps are:

  • koffi@3.2.12026-09-04T07:39:01.277Z
  • vscode-jsonrpc@8.2.12024-05-21T13:53:09.125Z
  • zod@4.3.62026-01-22T19:14:35.382Z

@github/* packages remain exempt from the public npm publication-age check, matching the existing Dependabot cooldown configuration.

Validation

  • npm ci --ignore-scripts for the Node SDK, test harness, and samples
  • npm run format:check
  • npm run lint (five existing warnings, no errors)
  • npm run typecheck
  • npm run build
  • npm test — 1,147 passed, 11 skipped
  • dependency policy tests — 14 passed
  • npm run verify:dependency-policy — 3 exact direct dependencies and 21 resolved production package versions passed the seven-day check
  • policy runtime — 0.62 seconds locally
  • npm run pack:release
  • npm run verify:release-packages — nine release package tarballs verified
  • git diff --check

(Written by Copilot)

Pin Node.js production dependencies at their existing resolved versions and require external npm releases to age seven full days before release packaging. Preserve exact requirements in packed packages and default future npm saves to exact versions.

(Written by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 823a3ba3-43a5-4b6a-be71-625e7660994a
@github-actions

This comment has been minimized.

@stephentoub

Copy link
Copy Markdown
Collaborator

Blocking issue: the seven-day publication-age verification currently checks only the direct dependencies and optionalDependencies declared in the root package.json. It does not walk the resolved production graph in package-lock.json, so transitive or optional packages—such as Koffi's @koromix/* packages—can be newer than seven days while the check still passes. This leaves the transitive-dependency failure mode from the proposed policy uncovered. Please verify every resolved external production package/version, while accounting for the fact that exact top-level requirements alone do not freeze ranged transitive dependencies for downstream consumers.

Generated by Copilot

@stephentoub stephentoub left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with the blocking transitive-dependency policy gap documented in the separate review comment.

Generated by Copilot

@stephentoub
stephentoub marked this pull request as ready for review September 16, 2026 21:12
@stephentoub
stephentoub requested a review from a team as a code owner September 16, 2026 21:12
Copilot AI balanced review requested due to automatic review settings September 16, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The exact-version, cooldown, packaging-verification, and test changes are consistent and complete.

Review effort: Balanced (auto)
Findings: None

Note

Copilot is running an experiment and ran this review at Balanced.

What changed in this PR

Enforces deterministic Node.js production dependencies and a seven-day npm publication cooldown before release packaging.

Changes:

  • Pins production dependencies and defaults future saves to exact versions.
  • Adds publication-age validation and release-manifest checks.
  • Adds focused policy tests.
File Description
nodejs/​.npmrc Enables exact npm saves.
nodejs/​package.json Pins dependencies and adds policy verification.
nodejs/​package-lock.json Synchronizes resolved metadata.
nodejs/​samples/​package-lock.json Synchronizes the sample lockfile.
nodejs/​scripts/​dependency-policy.ts Implements dependency and publication-age rules.
nodejs/​scripts/​verify-dependency-policy.ts Runs policy checks before packaging.
nodejs/​scripts/​verify-release-packages.ts Verifies packed dependency manifests.
nodejs/​test/​dependency-policy.test.ts Tests policy behavior and registry failures.
nodejs/​tsconfig.test.json Includes the new test in type-checking.
Files not reviewed (2)
  • nodejs/package-lock.json: Generated file
  • nodejs/samples/package-lock.json: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Walk the package-lock v3 production closure so optional and transitive package versions are subject to the seven-day npm publication policy while dev-only packages remain excluded.

(Written by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 823a3ba3-43a5-4b6a-be71-625e7660994a
@roblourens

Copy link
Copy Markdown
Author

Addressed the documented gap in 4e0ab19.

The policy now walks the packages map in package-lock.json and verifies every unique resolved external production package/version. It includes optional and devOptional production nodes (including all 18 Koffi @koromix/* platform packages) and excludes entries npm marks strictly dev-only, local links, and packages bundled inside another tarball.

The live check now covers 21 resolved production package versions and takes 0.62 seconds locally. I also added tests for optional, devOptional, strictly dev-only, nested, duplicate-version, link, bundled, and malformed lockfile entries.

The PR description now explicitly states the remaining npm limitation: this validates the graph resolved and tested at release time, but a library's package-lock.json does not freeze later consumer resolution. That stronger guarantee would require shrinkwrap or bundling.

(Written by Copilot)

@github-actions

Copy link
Copy Markdown
Contributor

SDK Consistency Review — PR #2700

Scope of change: This PR only touches the Node.js package's build/release tooling — it adds a dependency-policy.ts script (and verify-dependency-policy.ts wrapper + test) that enforces:

  • Exact (non-range) SemVer pins for production dependencies/optionalDependencies in package.json
  • A minimum 7-day npm publication-age cooldown for resolved production dependency versions (via the npm registry API)

It also pins koffi, vscode-jsonrpc, and zod to exact versions in package.json/lockfiles and wires the new check into pack:release and verify:release-packages.

Findings: No cross-SDK consistency issues. This change:

  • Does not modify any public SDK client API (no changes to nodejs/src/client.ts or equivalent surfaces in Python/Go/.NET/Java/Rust)
  • Is inherently npm/registry-specific tooling (uses the npm registry HTTP API and SemVer semantics) tied to how the Node.js package is packed and released
  • Has no equivalent method/parameter/return-type surface to compare against other languages

This falls under the "language-specific optimization/tooling" category — similar supply-chain protections (exact pinning + publication-age cooldown) could be a reasonable enhancement for other SDKs' release pipelines (uv/pip for Python, go.sum for Go, NuGet for .NET, Maven for Java, Cargo for Rust), but that would be a separate, non-blocking follow-up rather than a parity gap introduced by this PR.

No inline review comments needed.

Generated by SDK Consistency Review Agent for #2700 · copilot · sonnet50 · 26.9 AIC · ⌖ 12.2 AIC · ⊞ 8.3K ·

@stephentoub
stephentoub added this pull request to the merge queue Sep 16, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants