Skip to content

fix(lib-node): version-key extraction path to bust ESM module cache - #7

Merged
albanm merged 3 commits into
mainfrom
npm-cache-busting
Jun 3, 2026
Merged

fix(lib-node): version-key extraction path to bust ESM module cache#7
albanm merged 3 commits into
mainfrom
npm-cache-busting

Conversation

@albanm

@albanm albanm commented Jun 3, 2026

Copy link
Copy Markdown
Member

Rework lib-node's ensureArtefact to extract each artefact into a content-versioned directory behind a stable per-buildTuple pointer file, so a changed artefact resolves to a brand-new path and Node's ESM module registry reloads the entire graph (entry + siblings + bundled deps), not just the query-busted entry.

Why: plugins were not refreshed in memory after a registry update in long-lived processes. ensureArtefact re-extracted to a stable path, so the ESM registry (keyed by resolved URL) kept serving the stale module graph; the existing ?imported=Date.now() only re-evaluated the entry module.

New cache layout:

<artefactId>/
  .pointer-<buildTuple>.json        # stable: { dataUpdatedAt, version, dir }
  <version>+<epochSeconds>/<buildTuple>/

The stable pointer drives the conditional GET. On 304 we return its existing dir; on 200 we extract to a new dir, atomically repoint, then prune dirs no pointer references. The path discriminator is dataUpdatedAt at epoch-second precision, matching the registry's If-Modified-Since criterion exactly — the same equivalence relation gates both the 304/200 decision and the cache-bust. Keying on dataUpdatedAt (not version) also covers same-version re-publishes and an empty x-artefact-version header. Released as @data-fair/lib-node-registry 0.7.0.

Regression risks:

  • One-time effect on upgrade: existing caches use the old <buildTuple>/.meta.json layout, so the first call per artefact reads as cold (one re-download) and the old bare <buildTuple> dir is pruned. Intended, not a silent no-op.
  • The returned path is no longer stable across versions (by design). Any consumer that persisted the path across an update must re-call ensureArtefact; the processings API already re-calls on every prepare.
  • Pruning is lazy/best-effort: a concurrent in-flight run reading a dir being pruned is tolerated (errors swallowed). Fine on Linux; could surface as EBUSY on Windows.
  • hasNativeModules is no longer stored in the cache metadata — safe, it was only ever read from the response header on the 200 path.

albanm and others added 3 commits June 3, 2026 14:52
ensureArtefact extracted updated plugins to a stable path
(<artefactId>/<buildTuple>), so a long-lived process re-importing the
plugin kept serving the stale module graph: Node's ESM registry is keyed
by resolved URL, and the existing ?imported=Date.now() cache-bust only
re-evaluated the entry module, not its siblings or bundled deps.

Extract into a content-versioned dir instead:

  <artefactId>/
    .pointer-<buildTuple>.json      # stable: { dataUpdatedAt, version, dir }
    <version>+<epochSeconds>/<buildTuple>/

The stable pointer drives the conditional GET; on 304 we return its
existing dir, on 200 we extract to a new dir, atomically repoint, and
prune dirs no pointer references. Pruning keeps every dir referenced by
any buildTuple pointer, so a build-side download can't delete the js
consumer's still-current dir.

The path discriminator is dataUpdatedAt at epoch-second precision,
matching the registry's If-Modified-Since criterion exactly: the same
equivalence relation gates both the 304/200 decision and the cache-bust.
Keying on dataUpdatedAt (not version) also handles same-version
re-publishes and an empty x-artefact-version header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The discriminator switched to epoch-seconds; the JSDoc still said <hash>.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@albanm
albanm merged commit 41d45d8 into main Jun 3, 2026
4 checks passed
@github-actions github-actions Bot added the fix label Jun 3, 2026
@albanm
albanm deleted the npm-cache-busting branch June 3, 2026 13:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant