Skip to content

refactor(stasis-core): split util.js so the artifact data model loads without node:buffer/fs - #166

Open
exo-nikita wants to merge 2 commits into
mainfrom
claude/stasis-core-buffer-usage-575hr2
Open

refactor(stasis-core): split util.js so the artifact data model loads without node:buffer/fs#166
exo-nikita wants to merge 2 commits into
mainfrom
claude/stasis-core-buffer-usage-575hr2

Conversation

@exo-nikita

@exo-nikita exo-nikita commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Motivation

The Bundle/Lockfile/shard data model is pure computation over strings, Maps and Sets, but importing @exodus/stasis-core/bundle pulled all of util.js in — including node:buffer (isUtf8), node:fs (the realpathSync/statSync snapshot), node:util (parseArgs) and process.platform-touching code that none of its imports ever call. The only Buffer usage in the whole /bundle graph was three byte-classification guards in util.js that sit behind helpers the data model never reaches.

Change

  • New stasis-core/src/artifact-util.js — the pure half, moved from util.js: the format universe (KNOWN_FORMATS & friends, reconcileFormat), flat file keys (moduleFileKey, moduleFileKeys, hasNodeModulesSegment, splitNodeModulesPath), strict merges (mergeModuleMaps, mergeFormatMaps, mergeImportMaps, mergeExecutableSets), the executable-set rules (parseExecutable, serializeExecutable, narrowExecutable), the JSON↔Map converters, posixPathEscapes, sortPaths and assert. It imports no Node builtins at all, so it loads in any JS runtime. Everything moves verbatim except posixPathEscapes, which is rewritten as a dependency-free segment walk equivalent to its posix.normalize form (see below).
  • util.js keeps the Node-side half — byte/name classification for the capture walks, fs/execute-bit observation, CLI parsing — and does export * from './artifact-util.js', so @exodus/stasis-core/util keeps serving the full helper set. No importer outside the three files below changes.
  • bundle.js, lockfile.js, shard.js now import from ./artifact-util.js directly, making @exodus/stasis-core/bundle and /lockfile (and the shard wire format) load with zero builtins.
  • package.json: src/artifact-util.js added to files. The exports map is unchanged — the new module stays internal.
  • tests/artifact-graph.test.js pins the graph: artifact-util.js may import nothing, and bundle.js/lockfile.js/shard.js may import only ./artifact-util.js, so a builtin can't creep back in unnoticed. A second test asserts the /util re-export compatibility surface.

posixPathEscapes without node:path

The old form asked posix.normalize(path) whether the result is absolute or ..-prefixed. The new form walks segments: . and empty segments are skipped exactly as normalize collapses them, a real segment pushes, and a .. with nothing left to pop is precisely a normalize result that would keep a leading ... Because this is a security predicate (every parser's root-escape gate), tests/posix-path-escapes.test.js differentially tests the walk against the previous posix.normalize-based implementation verbatim over an exhaustive enumeration — every path of up to 4 segments drawn from ../.-lookalikes, real names and empty segments, each in relative, absolute and trailing-slash form (~33k cases) — plus the documented adversarial forms (a/../../x, /etc/passwd, backslash non-separators, the '' directory-capture key).

Testing

  • pnpm lint — clean (0 warnings, 0 errors on 129 files).
  • Full node --test suite run; the only failures are spawned-CLI tests that fail identically on an unmodified checkout under this container's Node 22 (repo engines require ≥ 24.14; CI matrix runs 24/26). Targeted suites (artifact-graph, posix-path-escapes, public-exports, bundle-merge, native-classify, plugins, node-modules-segment, executable, parse-leading-options, state-load-proto) all pass, plus a runtime round-trip smoke test of Bundle.parse(serialize()) and the /lockfile entry.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KhqPyNy3Xcf81eg56qAHUV

claude added 2 commits August 17, 2026 13:58
… without node:buffer/fs

The Bundle/Lockfile/shard data model is pure computation, but importing
`@exodus/stasis-core/bundle` pulled all of util.js in, including node:buffer,
node:fs, node:util and process-touching code it never calls.

Move the pure half -- the format universe, flat file keys, strict merges,
executable-set rules and JSON<->Map converters -- verbatim into
src/artifact-util.js (node:path posix only), and point bundle.js, lockfile.js
and shard.js at it. util.js keeps the byte/name classification, fs/execute-bit
observation and CLI parsing, and re-exports artifact-util.js so
`@exodus/stasis-core/util` keeps serving the full set.

tests/artifact-graph.test.js pins the graph's import specifiers so an
ambient-authority builtin can't creep back in unnoticed.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KhqPyNy3Xcf81eg56qAHUV
posix.isAbsolute is a leading-'/' check and the posix.normalize escape verdict
is equivalent to a segment walk: '.' and empty segments collapse, a real
segment pushes, and a '..' with nothing left to pop is exactly a normalize
result that keeps a leading '..'. Rewrite posixPathEscapes as that walk, so
the artifact data model imports no Node builtins at all.

tests/posix-path-escapes.test.js differentially tests the walk against the
normalize-based implementation it replaced over an exhaustive enumeration of
up to 4 segments of '..'/'.'-lookalikes (in relative, absolute and
trailing-slash forms), plus the documented adversarial cases.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KhqPyNy3Xcf81eg56qAHUV
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.

2 participants