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
Open
refactor(stasis-core): split util.js so the artifact data model loads without node:buffer/fs#166exo-nikita wants to merge 2 commits into
exo-nikita wants to merge 2 commits into
Conversation
… 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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
The
Bundle/Lockfile/shard data model is pure computation over strings,Maps andSets, but importing@exodus/stasis-core/bundlepulled all ofutil.jsin — includingnode:buffer(isUtf8),node:fs(therealpathSync/statSyncsnapshot),node:util(parseArgs) andprocess.platform-touching code that none of its imports ever call. The onlyBufferusage in the whole/bundlegraph was three byte-classification guards inutil.jsthat sit behind helpers the data model never reaches.Change
stasis-core/src/artifact-util.js— the pure half, moved fromutil.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,sortPathsandassert. It imports no Node builtins at all, so it loads in any JS runtime. Everything moves verbatim exceptposixPathEscapes, which is rewritten as a dependency-free segment walk equivalent to itsposix.normalizeform (see below).util.jskeeps the Node-side half — byte/name classification for the capture walks, fs/execute-bit observation, CLI parsing — and doesexport * from './artifact-util.js', so@exodus/stasis-core/utilkeeps serving the full helper set. No importer outside the three files below changes.bundle.js,lockfile.js,shard.jsnow import from./artifact-util.jsdirectly, making@exodus/stasis-core/bundleand/lockfile(and the shard wire format) load with zero builtins.package.json:src/artifact-util.jsadded tofiles. Theexportsmap is unchanged — the new module stays internal.tests/artifact-graph.test.jspins the graph:artifact-util.jsmay import nothing, andbundle.js/lockfile.js/shard.jsmay import only./artifact-util.js, so a builtin can't creep back in unnoticed. A second test asserts the/utilre-export compatibility surface.posixPathEscapeswithoutnode:pathThe 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.jsdifferentially tests the walk against the previousposix.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).node --testsuite 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 ofBundle.parse(serialize())and the/lockfileentry.🤖 Generated with Claude Code
https://claude.ai/code/session_01KhqPyNy3Xcf81eg56qAHUV