feat(fs): migrate to primitive:fs + TypeScript facade (#969)#987
Merged
Conversation
…969) Flip the user-facing `fs` and `fs/promises` modules from C#-backed built-ins to TypeScript facades over a `primitive:fs` / `primitive:fs/promises` seam, matching the pattern already used by os/process/readline/etc. The existing C# implementations (FsModuleInterpreter, FsModuleEmitter, the RuntimeEmitter Fs* IL helpers) are retained and re-keyed as the primitives; only the public name flips to stdlib/node/fs.ts. The facade re-exports the (mode-symmetric) sync surface from primitive:fs and derives the callback-async forms (fs.readFile(p, cb), ...) in TS from the promise primitives. This structurally closes the compiled callback-async gap (#970): callback fs now compiles and runs in compiled mode, byte-identical to the interpreter, instead of failing to compile. Compiled output stays standalone (no SharpTS.dll dependency). Optional trailing args are dispatched by arity in the facade (the proven process.ts/nextTick pattern) because the primitives default by argument count and spread-to-primitive is not expanded by the compiled emitter. Part of epic #968. Full xUnit suite green (14414/0); TypeScript conformance baseline unchanged. Phase 1 of #969 (sync port + flip); the readFile real-async proof follows.
…allback parity tests (#969) The TS facade derives fs's callback APIs from the promise primitives, which exposed a pre-existing interpreter bug: FsPromisesModuleInterpreter faulted its task with a raw NodeError, which the interpreter's promise settling does not recognize as a guest rejection — so fs.promises errors (and, via the facade, the callback fs.readFile(p, cb) error path) were silently dropped or surfaced as a host abort instead of reaching .then/.catch/await/the callback. Reject instead with the same guest error object the callback path already builds (FsModuleInterpreter.CreateErrorObject -> { code, syscall, path, message }), wrapped in SharpTSPromiseRejectedException. Now .then/.catch/await and the derived callbacks all receive the error with .code, matching compiled mode. This also fixes fs.promises rejection in the interpreter generally (not just readFile). Async proof (this issue's "1 async proof"): genuinely backgrounding the compiled readFile (Task.Factory.StartNew) is functionally correct and parity-clean, but $Promise(Task) does not Ref the event loop for a pending task, so fire-and-forget work races program exit (flaky). That event-loop ref-counting for adopted Tasks is the core of #971; documented in-code and on the issue. readFile stays deterministic Task.FromResult here. Adds 5 dual-mode parity tests (callback success / buffer / write-then-read / missing-file error / promise round-trip). Full xUnit suite green (14423/1, the one failure an unrelated live-DNS network smoke test that passes on retry).
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.
Part of the fs epic #968. Bottom of an 8-PR stack — review/merge bottom-up.
Flips
fs/fs/promisesfrom C# built-ins to TS facades over aprimitive:fsseam (the os/process pattern). The C# impls are retained as the primitives; the facade re-exports the sync surface and derives callback APIs from the promise primitives — structurally closing #970 (compiled had no callback fs before). Also fixes an exposed interpfs/promisesrejection bug. Standalone preserved. Full xUnit green; TS conformance unchanged.Closes #969.