@volter/tabnode 0.2.14-volter.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +78 -0
- package/THIRD-PARTY-NOTICES.md +12 -0
- package/dist/__sw__.js +526 -0
- package/dist/assets/runtime-worker-4MKaHFVb.js +75221 -0
- package/dist/assets/runtime-worker-4MKaHFVb.js.map +1 -0
- package/dist/code-transforms.d.ts +21 -0
- package/dist/code-transforms.d.ts.map +1 -0
- package/dist/config/cdn.d.ts +12 -0
- package/dist/config/cdn.d.ts.map +1 -0
- package/dist/create-runtime.d.ts +22 -0
- package/dist/create-runtime.d.ts.map +1 -0
- package/dist/host-globals.d.ts +62 -0
- package/dist/host-globals.d.ts.map +1 -0
- package/dist/index.cjs +36021 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +127 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +36020 -0
- package/dist/index.mjs.map +1 -0
- package/dist/node-internals.d.ts +68 -0
- package/dist/node-internals.d.ts.map +1 -0
- package/dist/node-lib/binding/blob.d.ts +12 -0
- package/dist/node-lib/binding/blob.d.ts.map +1 -0
- package/dist/node-lib/binding/buffer.d.ts +144 -0
- package/dist/node-lib/binding/buffer.d.ts.map +1 -0
- package/dist/node-lib/binding/cares_wrap.d.ts +14 -0
- package/dist/node-lib/binding/cares_wrap.d.ts.map +1 -0
- package/dist/node-lib/binding/fds.d.ts +30 -0
- package/dist/node-lib/binding/fds.d.ts.map +1 -0
- package/dist/node-lib/binding/fs.d.ts +157 -0
- package/dist/node-lib/binding/fs.d.ts.map +1 -0
- package/dist/node-lib/binding/handles.d.ts +81 -0
- package/dist/node-lib/binding/handles.d.ts.map +1 -0
- package/dist/node-lib/binding/http_parser.d.ts +122 -0
- package/dist/node-lib/binding/http_parser.d.ts.map +1 -0
- package/dist/node-lib/binding/index.d.ts +3 -0
- package/dist/node-lib/binding/index.d.ts.map +1 -0
- package/dist/node-lib/binding/llhttp-wasm.d.ts +22 -0
- package/dist/node-lib/binding/llhttp-wasm.d.ts.map +1 -0
- package/dist/node-lib/binding/misc.d.ts +123 -0
- package/dist/node-lib/binding/misc.d.ts.map +1 -0
- package/dist/node-lib/binding/pipe_wrap.d.ts +84 -0
- package/dist/node-lib/binding/pipe_wrap.d.ts.map +1 -0
- package/dist/node-lib/binding/process_wrap.d.ts +173 -0
- package/dist/node-lib/binding/process_wrap.d.ts.map +1 -0
- package/dist/node-lib/binding/spawn_sync.d.ts +30 -0
- package/dist/node-lib/binding/spawn_sync.d.ts.map +1 -0
- package/dist/node-lib/binding/stream_wrap.d.ts +191 -0
- package/dist/node-lib/binding/stream_wrap.d.ts.map +1 -0
- package/dist/node-lib/binding/stubs.d.ts +23 -0
- package/dist/node-lib/binding/stubs.d.ts.map +1 -0
- package/dist/node-lib/binding/tcp_wrap.d.ts +98 -0
- package/dist/node-lib/binding/tcp_wrap.d.ts.map +1 -0
- package/dist/node-lib/binding/tty_wrap.d.ts +36 -0
- package/dist/node-lib/binding/tty_wrap.d.ts.map +1 -0
- package/dist/node-lib/binding/util.d.ts +126 -0
- package/dist/node-lib/binding/util.d.ts.map +1 -0
- package/dist/node-lib/binding/uv.d.ts +32 -0
- package/dist/node-lib/binding/uv.d.ts.map +1 -0
- package/dist/node-lib/binding/zlib-pako.d.ts +22 -0
- package/dist/node-lib/binding/zlib-pako.d.ts.map +1 -0
- package/dist/node-lib/binding/zlib.d.ts +38 -0
- package/dist/node-lib/binding/zlib.d.ts.map +1 -0
- package/dist/node-lib/buffer-module.d.ts +51 -0
- package/dist/node-lib/buffer-module.d.ts.map +1 -0
- package/dist/node-lib/child-process-module.d.ts +26 -0
- package/dist/node-lib/child-process-module.d.ts.map +1 -0
- package/dist/node-lib/events-module.d.ts +58 -0
- package/dist/node-lib/events-module.d.ts.map +1 -0
- package/dist/node-lib/fs-module.d.ts +5 -0
- package/dist/node-lib/fs-module.d.ts.map +1 -0
- package/dist/node-lib/http-bridge.d.ts +43 -0
- package/dist/node-lib/http-bridge.d.ts.map +1 -0
- package/dist/node-lib/http-module.d.ts +35 -0
- package/dist/node-lib/http-module.d.ts.map +1 -0
- package/dist/node-lib/internals/addresses.d.ts +40 -0
- package/dist/node-lib/internals/addresses.d.ts.map +1 -0
- package/dist/node-lib/internals/buffer-and-streams.d.ts +96 -0
- package/dist/node-lib/internals/buffer-and-streams.d.ts.map +1 -0
- package/dist/node-lib/internals/child-process.d.ts +27 -0
- package/dist/node-lib/internals/child-process.d.ts.map +1 -0
- package/dist/node-lib/internals/events-util.d.ts +83 -0
- package/dist/node-lib/internals/events-util.d.ts.map +1 -0
- package/dist/node-lib/internals/index.d.ts +2 -0
- package/dist/node-lib/internals/index.d.ts.map +1 -0
- package/dist/node-lib/internals/modules.d.ts +67 -0
- package/dist/node-lib/internals/modules.d.ts.map +1 -0
- package/dist/node-lib/internals/readline-access.d.ts +2 -0
- package/dist/node-lib/internals/readline-access.d.ts.map +1 -0
- package/dist/node-lib/internals/runtime.d.ts +150 -0
- package/dist/node-lib/internals/runtime.d.ts.map +1 -0
- package/dist/node-lib/internals/util.d.ts +33 -0
- package/dist/node-lib/internals/util.d.ts.map +1 -0
- package/dist/node-lib/lazy.d.ts +31 -0
- package/dist/node-lib/lazy.d.ts.map +1 -0
- package/dist/node-lib/load.d.ts +27 -0
- package/dist/node-lib/load.d.ts.map +1 -0
- package/dist/node-lib/module-hooks.d.ts +89 -0
- package/dist/node-lib/module-hooks.d.ts.map +1 -0
- package/dist/node-lib/net-module.d.ts +181 -0
- package/dist/node-lib/net-module.d.ts.map +1 -0
- package/dist/node-lib/node-versions.d.ts +27 -0
- package/dist/node-lib/node-versions.d.ts.map +1 -0
- package/dist/node-lib/public-modules.d.ts +2 -0
- package/dist/node-lib/public-modules.d.ts.map +1 -0
- package/dist/node-lib/require-hook.d.ts +15 -0
- package/dist/node-lib/require-hook.d.ts.map +1 -0
- package/dist/node-lib/small-modules.d.ts +16 -0
- package/dist/node-lib/small-modules.d.ts.map +1 -0
- package/dist/node-lib/sources.d.ts +4 -0
- package/dist/node-lib/sources.d.ts.map +1 -0
- package/dist/node-lib/stream-module.d.ts +77 -0
- package/dist/node-lib/stream-module.d.ts.map +1 -0
- package/dist/node-lib/timers.d.ts +19 -0
- package/dist/node-lib/timers.d.ts.map +1 -0
- package/dist/node-lib/util-module.d.ts +76 -0
- package/dist/node-lib/util-module.d.ts.map +1 -0
- package/dist/node-resolution.d.ts +50 -0
- package/dist/node-resolution.d.ts.map +1 -0
- package/dist/node-resolver.d.ts +7 -0
- package/dist/node-resolver.d.ts.map +1 -0
- package/dist/npm/index.d.ts +73 -0
- package/dist/npm/index.d.ts.map +1 -0
- package/dist/npm/registry.d.ts +111 -0
- package/dist/npm/registry.d.ts.map +1 -0
- package/dist/npm/resolver.d.ts +79 -0
- package/dist/npm/resolver.d.ts.map +1 -0
- package/dist/npm/tarball.d.ts +28 -0
- package/dist/npm/tarball.d.ts.map +1 -0
- package/dist/process-tokens.d.ts +77 -0
- package/dist/process-tokens.d.ts.map +1 -0
- package/dist/punycode-source.d.ts +3 -0
- package/dist/punycode-source.d.ts.map +1 -0
- package/dist/runtime-interface.d.ts +74 -0
- package/dist/runtime-interface.d.ts.map +1 -0
- package/dist/runtime.d.ts +144 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/server-bridge.d.ts +147 -0
- package/dist/server-bridge.d.ts.map +1 -0
- package/dist/shims/async_hooks.d.ts +50 -0
- package/dist/shims/async_hooks.d.ts.map +1 -0
- package/dist/shims/child_process.d.ts +104 -0
- package/dist/shims/child_process.d.ts.map +1 -0
- package/dist/shims/cluster.d.ts +52 -0
- package/dist/shims/cluster.d.ts.map +1 -0
- package/dist/shims/command-line.d.ts +43 -0
- package/dist/shims/command-line.d.ts.map +1 -0
- package/dist/shims/constants.d.ts +222 -0
- package/dist/shims/constants.d.ts.map +1 -0
- package/dist/shims/crypto-constants.d.ts +12 -0
- package/dist/shims/crypto-constants.d.ts.map +1 -0
- package/dist/shims/crypto.d.ts +144 -0
- package/dist/shims/crypto.d.ts.map +1 -0
- package/dist/shims/dgram.d.ts +34 -0
- package/dist/shims/dgram.d.ts.map +1 -0
- package/dist/shims/dns.d.ts +91 -0
- package/dist/shims/dns.d.ts.map +1 -0
- package/dist/shims/domain.d.ts +25 -0
- package/dist/shims/domain.d.ts.map +1 -0
- package/dist/shims/esbuild.d.ts +205 -0
- package/dist/shims/esbuild.d.ts.map +1 -0
- package/dist/shims/fs.d.ts +25 -0
- package/dist/shims/fs.d.ts.map +1 -0
- package/dist/shims/http2.d.ts +81 -0
- package/dist/shims/http2.d.ts.map +1 -0
- package/dist/shims/inspector.d.ts +25 -0
- package/dist/shims/inspector.d.ts.map +1 -0
- package/dist/shims/module.d.ts +22 -0
- package/dist/shims/module.d.ts.map +1 -0
- package/dist/shims/os.d.ts +159 -0
- package/dist/shims/os.d.ts.map +1 -0
- package/dist/shims/path.d.ts +52 -0
- package/dist/shims/path.d.ts.map +1 -0
- package/dist/shims/perf_hooks.d.ts +50 -0
- package/dist/shims/perf_hooks.d.ts.map +1 -0
- package/dist/shims/process.d.ts +226 -0
- package/dist/shims/process.d.ts.map +1 -0
- package/dist/shims/rollup.d.ts +71 -0
- package/dist/shims/rollup.d.ts.map +1 -0
- package/dist/shims/string_decoder.d.ts +41 -0
- package/dist/shims/string_decoder.d.ts.map +1 -0
- package/dist/shims/sync-child.d.ts +64 -0
- package/dist/shims/sync-child.d.ts.map +1 -0
- package/dist/shims/tls.d.ts +53 -0
- package/dist/shims/tls.d.ts.map +1 -0
- package/dist/shims/url.d.ts +84 -0
- package/dist/shims/url.d.ts.map +1 -0
- package/dist/shims/v8.d.ts +73 -0
- package/dist/shims/v8.d.ts.map +1 -0
- package/dist/shims/vfs-adapter.d.ts +126 -0
- package/dist/shims/vfs-adapter.d.ts.map +1 -0
- package/dist/shims/vm.d.ts +52 -0
- package/dist/shims/vm.d.ts.map +1 -0
- package/dist/shims/wasi.d.ts +78 -0
- package/dist/shims/wasi.d.ts.map +1 -0
- package/dist/shims/worker_threads.d.ts +66 -0
- package/dist/shims/worker_threads.d.ts.map +1 -0
- package/dist/shims/zlib-constants.d.ts +66 -0
- package/dist/shims/zlib-constants.d.ts.map +1 -0
- package/dist/shims/zlib.d.ts +102 -0
- package/dist/shims/zlib.d.ts.map +1 -0
- package/dist/transform.d.ts +24 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/types/package-json.d.ts +18 -0
- package/dist/types/package-json.d.ts.map +1 -0
- package/dist/utils/binary-encoding.d.ts +13 -0
- package/dist/utils/binary-encoding.d.ts.map +1 -0
- package/dist/utils/glob.d.ts +19 -0
- package/dist/utils/glob.d.ts.map +1 -0
- package/dist/utils/hash.d.ts +6 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/virtual-fs.d.ts +259 -0
- package/dist/virtual-fs.d.ts.map +1 -0
- package/dist/vite-plugin.cjs +50 -0
- package/dist/vite-plugin.cjs.map +1 -0
- package/dist/vite-plugin.d.ts +36 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.mjs +31 -0
- package/dist/vite-plugin.mjs.map +1 -0
- package/dist/worker/runtime-worker.d.ts +8 -0
- package/dist/worker/runtime-worker.d.ts.map +1 -0
- package/dist/worker-runtime.d.ts +50 -0
- package/dist/worker-runtime.d.ts.map +1 -0
- package/package.json +88 -0
- package/src/code-transforms.ts +560 -0
- package/src/config/cdn.ts +13 -0
- package/src/create-runtime.ts +89 -0
- package/src/host-globals.ts +121 -0
- package/src/index.ts +235 -0
- package/src/node-internals.ts +283 -0
- package/src/node-lib/LICENSE +47 -0
- package/src/node-lib/_http_agent.js +547 -0
- package/src/node-lib/_http_client.js +996 -0
- package/src/node-lib/_http_common.js +269 -0
- package/src/node-lib/_http_incoming.js +453 -0
- package/src/node-lib/_http_outgoing.js +1262 -0
- package/src/node-lib/_http_server.js +1230 -0
- package/src/node-lib/assert/strict.js +3 -0
- package/src/node-lib/assert.js +844 -0
- package/src/node-lib/binding/blob.ts +35 -0
- package/src/node-lib/binding/buffer.ts +465 -0
- package/src/node-lib/binding/cares_wrap.ts +57 -0
- package/src/node-lib/binding/fds.ts +80 -0
- package/src/node-lib/binding/fs.ts +857 -0
- package/src/node-lib/binding/handles.ts +187 -0
- package/src/node-lib/binding/http_parser.ts +536 -0
- package/src/node-lib/binding/index.ts +77 -0
- package/src/node-lib/binding/llhttp-wasm.ts +670 -0
- package/src/node-lib/binding/misc.ts +252 -0
- package/src/node-lib/binding/pipe_wrap.ts +166 -0
- package/src/node-lib/binding/process_wrap.ts +371 -0
- package/src/node-lib/binding/spawn_sync.ts +141 -0
- package/src/node-lib/binding/stream_wrap.ts +413 -0
- package/src/node-lib/binding/stubs.ts +19 -0
- package/src/node-lib/binding/tcp_wrap.ts +254 -0
- package/src/node-lib/binding/tty_wrap.ts +56 -0
- package/src/node-lib/binding/util.ts +274 -0
- package/src/node-lib/binding/uv.ts +102 -0
- package/src/node-lib/binding/zlib-pako.ts +50 -0
- package/src/node-lib/binding/zlib.ts +259 -0
- package/src/node-lib/buffer-module.ts +70 -0
- package/src/node-lib/buffer.js +1373 -0
- package/src/node-lib/child-process-module.ts +58 -0
- package/src/node-lib/child_process.js +1046 -0
- package/src/node-lib/constants.js +41 -0
- package/src/node-lib/diagnostics_channel.js +438 -0
- package/src/node-lib/events-module.ts +80 -0
- package/src/node-lib/events.js +1223 -0
- package/src/node-lib/fs-module.ts +43 -0
- package/src/node-lib/fs.js +3377 -0
- package/src/node-lib/http-bridge.ts +299 -0
- package/src/node-lib/http-module.ts +53 -0
- package/src/node-lib/http.js +198 -0
- package/src/node-lib/https.js +429 -0
- package/src/node-lib/internal/assert/assertion_error.js +417 -0
- package/src/node-lib/internal/assert/calltracker.js +153 -0
- package/src/node-lib/internal/assert/myers_diff.js +177 -0
- package/src/node-lib/internal/assert/utils.js +278 -0
- package/src/node-lib/internal/buffer.js +1120 -0
- package/src/node-lib/internal/child_process/serialization.js +169 -0
- package/src/node-lib/internal/child_process.js +1135 -0
- package/src/node-lib/internal/constants.js +56 -0
- package/src/node-lib/internal/deps/minimatch/index.js +1628 -0
- package/src/node-lib/internal/errors.js +1912 -0
- package/src/node-lib/internal/events/abort_listener.js +54 -0
- package/src/node-lib/internal/events/symbols.js +11 -0
- package/src/node-lib/internal/fixed_queue.js +119 -0
- package/src/node-lib/internal/freelist.js +30 -0
- package/src/node-lib/internal/fs/cp/cp-sync.js +242 -0
- package/src/node-lib/internal/fs/cp/cp.js +382 -0
- package/src/node-lib/internal/fs/dir.js +369 -0
- package/src/node-lib/internal/fs/glob.js +763 -0
- package/src/node-lib/internal/fs/promises.js +1312 -0
- package/src/node-lib/internal/fs/read/context.js +129 -0
- package/src/node-lib/internal/fs/recursive_watch.js +275 -0
- package/src/node-lib/internal/fs/rimraf.js +311 -0
- package/src/node-lib/internal/fs/streams.js +571 -0
- package/src/node-lib/internal/fs/sync_write_stream.js +49 -0
- package/src/node-lib/internal/fs/utils.js +987 -0
- package/src/node-lib/internal/fs/watchers.js +418 -0
- package/src/node-lib/internal/http.js +62 -0
- package/src/node-lib/internal/mime.js +389 -0
- package/src/node-lib/internal/modules/customization_hooks.js +406 -0
- package/src/node-lib/internal/net.js +95 -0
- package/src/node-lib/internal/querystring.js +119 -0
- package/src/node-lib/internal/readline/callbacks.js +132 -0
- package/src/node-lib/internal/readline/emitKeypressEvents.js +99 -0
- package/src/node-lib/internal/readline/interface.js +1418 -0
- package/src/node-lib/internal/readline/promises.js +144 -0
- package/src/node-lib/internal/readline/utils.js +405 -0
- package/src/node-lib/internal/socket_list.js +108 -0
- package/src/node-lib/internal/stream_base_commons.js +280 -0
- package/src/node-lib/internal/streams/add-abort-signal.js +60 -0
- package/src/node-lib/internal/streams/compose.js +257 -0
- package/src/node-lib/internal/streams/destroy.js +361 -0
- package/src/node-lib/internal/streams/duplex.js +203 -0
- package/src/node-lib/internal/streams/duplexify.js +379 -0
- package/src/node-lib/internal/streams/duplexpair.js +62 -0
- package/src/node-lib/internal/streams/end-of-stream.js +331 -0
- package/src/node-lib/internal/streams/from.js +208 -0
- package/src/node-lib/internal/streams/lazy_transform.js +57 -0
- package/src/node-lib/internal/streams/legacy.js +125 -0
- package/src/node-lib/internal/streams/operators.js +436 -0
- package/src/node-lib/internal/streams/passthrough.js +47 -0
- package/src/node-lib/internal/streams/pipeline.js +465 -0
- package/src/node-lib/internal/streams/readable.js +1762 -0
- package/src/node-lib/internal/streams/state.js +51 -0
- package/src/node-lib/internal/streams/transform.js +204 -0
- package/src/node-lib/internal/streams/utils.js +363 -0
- package/src/node-lib/internal/streams/writable.js +1163 -0
- package/src/node-lib/internal/tty.js +260 -0
- package/src/node-lib/internal/util/colors.js +40 -0
- package/src/node-lib/internal/util/comparisons.js +1028 -0
- package/src/node-lib/internal/util/debuglog.js +524 -0
- package/src/node-lib/internal/util/inspect.js +2535 -0
- package/src/node-lib/internal/util/parse_args/parse_args.js +412 -0
- package/src/node-lib/internal/util/parse_args/utils.js +197 -0
- package/src/node-lib/internal/util.js +1003 -0
- package/src/node-lib/internal/validators.js +654 -0
- package/src/node-lib/internals/addresses.ts +159 -0
- package/src/node-lib/internals/buffer-and-streams.ts +224 -0
- package/src/node-lib/internals/child-process.ts +64 -0
- package/src/node-lib/internals/events-util.ts +105 -0
- package/src/node-lib/internals/index.ts +66 -0
- package/src/node-lib/internals/modules.ts +117 -0
- package/src/node-lib/internals/readline-access.ts +16 -0
- package/src/node-lib/internals/runtime.ts +277 -0
- package/src/node-lib/internals/util.ts +98 -0
- package/src/node-lib/lazy.ts +158 -0
- package/src/node-lib/load.ts +390 -0
- package/src/node-lib/module-hooks.ts +281 -0
- package/src/node-lib/net-module.ts +248 -0
- package/src/node-lib/net.js +2540 -0
- package/src/node-lib/node-versions.ts +34 -0
- package/src/node-lib/os.js +356 -0
- package/src/node-lib/path.js +1694 -0
- package/src/node-lib/primordials.js +743 -0
- package/src/node-lib/public-modules.ts +109 -0
- package/src/node-lib/punycode.js +455 -0
- package/src/node-lib/querystring.js +484 -0
- package/src/node-lib/readline/promises.js +73 -0
- package/src/node-lib/readline.js +523 -0
- package/src/node-lib/require-hook.ts +27 -0
- package/src/node-lib/small-modules.ts +35 -0
- package/src/node-lib/sources.ts +200 -0
- package/src/node-lib/stream/promises.js +44 -0
- package/src/node-lib/stream-module.ts +101 -0
- package/src/node-lib/stream.js +152 -0
- package/src/node-lib/timers.ts +33 -0
- package/src/node-lib/tty.js +168 -0
- package/src/node-lib/util-module.ts +104 -0
- package/src/node-lib/util.js +689 -0
- package/src/node-lib/wasi.js +179 -0
- package/src/node-lib/zlib.js +1036 -0
- package/src/node-resolution.ts +241 -0
- package/src/node-resolver.ts +33 -0
- package/src/npm/index.ts +425 -0
- package/src/npm/registry.ts +235 -0
- package/src/npm/resolver.ts +589 -0
- package/src/npm/tarball.ts +246 -0
- package/src/process-tokens.ts +156 -0
- package/src/punycode-source.ts +6 -0
- package/src/runtime-interface.ts +85 -0
- package/src/runtime.ts +3073 -0
- package/src/server-bridge.ts +971 -0
- package/src/shims/async_hooks.ts +181 -0
- package/src/shims/child_process.ts +1591 -0
- package/src/shims/cluster.ts +79 -0
- package/src/shims/command-line.ts +79 -0
- package/src/shims/constants.ts +68 -0
- package/src/shims/crypto-constants.ts +11 -0
- package/src/shims/crypto.ts +1061 -0
- package/src/shims/dgram.ts +47 -0
- package/src/shims/dns.ts +185 -0
- package/src/shims/domain.ts +58 -0
- package/src/shims/esbuild.ts +1638 -0
- package/src/shims/fs.ts +82 -0
- package/src/shims/http2.ts +96 -0
- package/src/shims/inspector.ts +30 -0
- package/src/shims/module.ts +86 -0
- package/src/shims/os.ts +195 -0
- package/src/shims/path.ts +246 -0
- package/src/shims/perf_hooks.ts +92 -0
- package/src/shims/process.ts +804 -0
- package/src/shims/rollup.ts +149 -0
- package/src/shims/string_decoder.ts +396 -0
- package/src/shims/sync-child.ts +469 -0
- package/src/shims/tls.ts +94 -0
- package/src/shims/url.ts +217 -0
- package/src/shims/v8.ts +113 -0
- package/src/shims/vfs-adapter.ts +408 -0
- package/src/shims/vm.ts +132 -0
- package/src/shims/wasi.ts +1440 -0
- package/src/shims/worker_threads.ts +114 -0
- package/src/shims/zlib-constants.ts +65 -0
- package/src/shims/zlib.ts +228 -0
- package/src/transform.ts +324 -0
- package/src/types/external.d.ts +55 -0
- package/src/types/package-json.ts +17 -0
- package/src/types/pako-zlib.d.ts +30 -0
- package/src/types/sha.js.d.ts +14 -0
- package/src/utils/binary-encoding.ts +43 -0
- package/src/utils/glob.ts +54 -0
- package/src/utils/hash.ts +12 -0
- package/src/virtual-fs.ts +1048 -0
- package/src/vite-plugin.ts +76 -0
- package/src/worker/runtime-worker.ts +128 -0
- package/src/worker-runtime.ts +145 -0
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Virtual File System - In-memory file tree with POSIX-like operations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { VFSSnapshot, VFSFileEntry } from './runtime-interface';
|
|
6
|
+
import { uint8ToBase64, base64ToUint8 } from './utils/binary-encoding';
|
|
7
|
+
import { Readable } from './node-lib/stream-module';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A read stream is a real `Readable`, so a guest can pipe it, iterate it, or
|
|
11
|
+
* listen for `open` and `ready`; the hand-rolled object it used to answer had
|
|
12
|
+
* only `on` and `pipe`, so anything that treated it as a stream got nothing.
|
|
13
|
+
* `start`, `end` and `encoding` are honoured as Node honours them.
|
|
14
|
+
*
|
|
15
|
+
* What `createReadStream` accepts: the subset of Node's options bag used here.
|
|
16
|
+
*/
|
|
17
|
+
export interface ReadStreamOptions {
|
|
18
|
+
encoding?: string;
|
|
19
|
+
start?: number;
|
|
20
|
+
end?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** A `Readable` carrying the two properties Node's fs read stream carries. */
|
|
24
|
+
export interface FsReadStream extends Readable {
|
|
25
|
+
path: string;
|
|
26
|
+
bytesRead: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
export interface FSNode {
|
|
32
|
+
type: 'file' | 'directory' | 'symlink';
|
|
33
|
+
content?: Uint8Array;
|
|
34
|
+
/** Where a symlink points, exactly as it was written. */
|
|
35
|
+
target?: string;
|
|
36
|
+
children?: Map<string, FSNode>;
|
|
37
|
+
mtime: number;
|
|
38
|
+
/**
|
|
39
|
+
* When the file was last read. Node reports it apart from the write time
|
|
40
|
+
* and `utimes` sets the two separately; this filesystem reported the write
|
|
41
|
+
* time in its place, so a program that set an access time and read it back
|
|
42
|
+
* got the other one (Node's own test/wasi `stat` fixture asserts them
|
|
43
|
+
* apart). Absent until something sets it, which is the write time.
|
|
44
|
+
*/
|
|
45
|
+
atime?: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Simple EventEmitter for VFS change notifications
|
|
49
|
+
type VFSChangeListener = (path: string, content: string) => void;
|
|
50
|
+
type VFSDeleteListener = (path: string) => void;
|
|
51
|
+
type VFSEventListener = VFSChangeListener | VFSDeleteListener;
|
|
52
|
+
|
|
53
|
+
export interface Stats {
|
|
54
|
+
isFile(): boolean;
|
|
55
|
+
isDirectory(): boolean;
|
|
56
|
+
isSymbolicLink(): boolean;
|
|
57
|
+
isBlockDevice(): boolean;
|
|
58
|
+
isCharacterDevice(): boolean;
|
|
59
|
+
isFIFO(): boolean;
|
|
60
|
+
isSocket(): boolean;
|
|
61
|
+
size: number;
|
|
62
|
+
mode: number;
|
|
63
|
+
mtime: Date;
|
|
64
|
+
atime: Date;
|
|
65
|
+
ctime: Date;
|
|
66
|
+
birthtime: Date;
|
|
67
|
+
mtimeMs: number;
|
|
68
|
+
atimeMs: number;
|
|
69
|
+
ctimeMs: number;
|
|
70
|
+
birthtimeMs: number;
|
|
71
|
+
nlink: number;
|
|
72
|
+
uid: number;
|
|
73
|
+
gid: number;
|
|
74
|
+
dev: number;
|
|
75
|
+
ino: number;
|
|
76
|
+
rdev: number;
|
|
77
|
+
blksize: number;
|
|
78
|
+
blocks: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type WatchEventType = 'change' | 'rename';
|
|
82
|
+
export type WatchListener = (eventType: WatchEventType, filename: string | null) => void;
|
|
83
|
+
|
|
84
|
+
export interface FSWatcher {
|
|
85
|
+
close(): void;
|
|
86
|
+
ref(): this;
|
|
87
|
+
unref(): this;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface WatcherEntry {
|
|
91
|
+
listener: WatchListener;
|
|
92
|
+
recursive: boolean;
|
|
93
|
+
closed: boolean;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Create a Node.js-style error with code property
|
|
98
|
+
*/
|
|
99
|
+
export interface NodeError extends Error {
|
|
100
|
+
code: string;
|
|
101
|
+
errno: number;
|
|
102
|
+
syscall: string;
|
|
103
|
+
path?: string;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function createNodeError(
|
|
107
|
+
code: 'ENOENT' | 'ENOTDIR' | 'EISDIR' | 'EEXIST' | 'ENOTEMPTY' | 'ELOOP' | 'EINVAL',
|
|
108
|
+
syscall: string,
|
|
109
|
+
path: string,
|
|
110
|
+
message?: string
|
|
111
|
+
): NodeError {
|
|
112
|
+
const errno: Record<string, number> = {
|
|
113
|
+
ENOENT: -2,
|
|
114
|
+
ENOTDIR: -20,
|
|
115
|
+
EISDIR: -21,
|
|
116
|
+
EEXIST: -17,
|
|
117
|
+
ENOTEMPTY: -39,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const messages: Record<string, string> = {
|
|
121
|
+
ENOENT: 'no such file or directory',
|
|
122
|
+
ENOTDIR: 'not a directory',
|
|
123
|
+
EISDIR: 'is a directory',
|
|
124
|
+
EEXIST: 'file already exists',
|
|
125
|
+
ENOTEMPTY: 'directory not empty',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const err = new Error(
|
|
129
|
+
message || `${code}: ${messages[code]}, ${syscall} '${path}'`
|
|
130
|
+
) as NodeError;
|
|
131
|
+
err.code = code;
|
|
132
|
+
err.errno = errno[code];
|
|
133
|
+
err.syscall = syscall;
|
|
134
|
+
err.path = path;
|
|
135
|
+
return err;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export class VirtualFS {
|
|
139
|
+
private root: FSNode;
|
|
140
|
+
private encoder = new TextEncoder();
|
|
141
|
+
private decoder = new TextDecoder();
|
|
142
|
+
private watchers = new Map<string, Set<WatcherEntry>>();
|
|
143
|
+
private eventListeners = new Map<string, Set<VFSEventListener>>();
|
|
144
|
+
|
|
145
|
+
constructor() {
|
|
146
|
+
this.root = {
|
|
147
|
+
type: 'directory',
|
|
148
|
+
children: new Map(),
|
|
149
|
+
mtime: Date.now(),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Add event listener (for change notifications to workers)
|
|
155
|
+
*/
|
|
156
|
+
on(event: 'change', listener: VFSChangeListener): this;
|
|
157
|
+
on(event: 'delete', listener: VFSDeleteListener): this;
|
|
158
|
+
on(event: string, listener: VFSEventListener): this {
|
|
159
|
+
if (!this.eventListeners.has(event)) {
|
|
160
|
+
this.eventListeners.set(event, new Set());
|
|
161
|
+
}
|
|
162
|
+
this.eventListeners.get(event)!.add(listener);
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Remove event listener
|
|
168
|
+
*/
|
|
169
|
+
off(event: 'change', listener: VFSChangeListener): this;
|
|
170
|
+
off(event: 'delete', listener: VFSDeleteListener): this;
|
|
171
|
+
off(event: string, listener: VFSEventListener): this {
|
|
172
|
+
const listeners = this.eventListeners.get(event);
|
|
173
|
+
if (listeners) {
|
|
174
|
+
listeners.delete(listener);
|
|
175
|
+
}
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Emit event to listeners
|
|
181
|
+
*/
|
|
182
|
+
private emit(event: 'change', path: string, content: string): void;
|
|
183
|
+
private emit(event: 'delete', path: string): void;
|
|
184
|
+
private emit(event: string, ...args: unknown[]): void {
|
|
185
|
+
const listeners = this.eventListeners.get(event);
|
|
186
|
+
if (listeners) {
|
|
187
|
+
for (const listener of listeners) {
|
|
188
|
+
try {
|
|
189
|
+
(listener as (...args: unknown[]) => void)(...args);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
console.error('Error in VFS event listener:', err);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Serialize the entire file tree to a snapshot (for worker transfer)
|
|
199
|
+
*/
|
|
200
|
+
toSnapshot(): VFSSnapshot {
|
|
201
|
+
const files: VFSFileEntry[] = [];
|
|
202
|
+
this.serializeNode('/', this.root, files);
|
|
203
|
+
return { files };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
private serializeNode(path: string, node: FSNode, files: VFSFileEntry[]): void {
|
|
207
|
+
if (node.type === 'file') {
|
|
208
|
+
// Encode binary content as base64
|
|
209
|
+
let content = '';
|
|
210
|
+
if (node.content && node.content.length > 0) {
|
|
211
|
+
content = uint8ToBase64(node.content);
|
|
212
|
+
}
|
|
213
|
+
files.push({ path, type: 'file', content });
|
|
214
|
+
} else if (node.type === 'symlink') {
|
|
215
|
+
files.push({ path, type: 'symlink', target: node.target } as unknown as VFSFileEntry);
|
|
216
|
+
} else if (node.type === 'directory') {
|
|
217
|
+
files.push({ path, type: 'directory' });
|
|
218
|
+
if (node.children) {
|
|
219
|
+
for (const [name, child] of node.children) {
|
|
220
|
+
const childPath = path === '/' ? `/${name}` : `${path}/${name}`;
|
|
221
|
+
this.serializeNode(childPath, child, files);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Create a VirtualFS from a snapshot
|
|
229
|
+
*/
|
|
230
|
+
static fromSnapshot(snapshot: VFSSnapshot): VirtualFS {
|
|
231
|
+
const vfs = new VirtualFS();
|
|
232
|
+
|
|
233
|
+
// Sort entries to ensure directories are created before their contents
|
|
234
|
+
const sortedFiles = snapshot.files
|
|
235
|
+
.map((entry, i) => ({ entry, depth: entry.path.split('/').length, i }))
|
|
236
|
+
.sort((a, b) => a.depth - b.depth || a.i - b.i)
|
|
237
|
+
.map(x => x.entry);
|
|
238
|
+
|
|
239
|
+
for (const entry of sortedFiles) {
|
|
240
|
+
if (entry.path === '/') continue; // Skip root
|
|
241
|
+
|
|
242
|
+
if (entry.type === 'directory') {
|
|
243
|
+
vfs.mkdirSync(entry.path, { recursive: true });
|
|
244
|
+
} else if ((entry as { type: string }).type === 'symlink') {
|
|
245
|
+
const parentPath = entry.path.substring(0, entry.path.lastIndexOf('/')) || '/';
|
|
246
|
+
if (parentPath !== '/' && !vfs.existsSync(parentPath)) {
|
|
247
|
+
vfs.mkdirSync(parentPath, { recursive: true });
|
|
248
|
+
}
|
|
249
|
+
vfs.symlinkSync((entry as unknown as { target: string }).target, entry.path);
|
|
250
|
+
} else if (entry.type === 'file') {
|
|
251
|
+
// Decode base64 content
|
|
252
|
+
let content: Uint8Array;
|
|
253
|
+
if (entry.content) {
|
|
254
|
+
content = base64ToUint8(entry.content);
|
|
255
|
+
} else {
|
|
256
|
+
content = new Uint8Array(0);
|
|
257
|
+
}
|
|
258
|
+
// Ensure parent directory exists
|
|
259
|
+
const parentPath = entry.path.substring(0, entry.path.lastIndexOf('/')) || '/';
|
|
260
|
+
if (parentPath !== '/' && !vfs.existsSync(parentPath)) {
|
|
261
|
+
vfs.mkdirSync(parentPath, { recursive: true });
|
|
262
|
+
}
|
|
263
|
+
vfs.writeFileSyncInternal(entry.path, content, false); // Don't emit events during restore
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return vfs;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Internal write that optionally emits events
|
|
272
|
+
*/
|
|
273
|
+
private writeFileSyncInternal(path: string, data: string | Uint8Array, emitEvent: boolean): void {
|
|
274
|
+
const normalized = this.normalizePath(path);
|
|
275
|
+
const parentPath = this.getParentPath(normalized);
|
|
276
|
+
const basename = this.getBasename(normalized);
|
|
277
|
+
|
|
278
|
+
if (!basename) {
|
|
279
|
+
throw new Error(`EISDIR: illegal operation on a directory, '${path}'`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const parent = this.ensureDirectory(parentPath);
|
|
283
|
+
const existing = parent.children!.get(basename);
|
|
284
|
+
const existed = existing !== void 0;
|
|
285
|
+
|
|
286
|
+
const content = typeof data === 'string' ? this.encoder.encode(data) : data;
|
|
287
|
+
|
|
288
|
+
// A write changes a file's bytes; it does not make a new file. Replacing
|
|
289
|
+
// the node on every write moved the file's identity, and an inode or an
|
|
290
|
+
// open descriptor that held it was left pointing at a file nobody had.
|
|
291
|
+
if (existed && existing!.type === 'file') {
|
|
292
|
+
existing!.content = content;
|
|
293
|
+
existing!.mtime = Date.now();
|
|
294
|
+
} else {
|
|
295
|
+
parent.children!.set(basename, {
|
|
296
|
+
type: 'file',
|
|
297
|
+
content,
|
|
298
|
+
mtime: Date.now(),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (emitEvent) {
|
|
303
|
+
// Notify watchers
|
|
304
|
+
this.notifyWatchers(normalized, existed ? 'change' : 'rename');
|
|
305
|
+
// Emit change event for worker sync
|
|
306
|
+
this.emit('change', normalized, typeof data === 'string' ? data : this.decoder.decode(data));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Normalize path - resolve . and .. segments, ensure leading /
|
|
312
|
+
*/
|
|
313
|
+
private normalizePath(path: string): string {
|
|
314
|
+
if (!path.startsWith('/')) {
|
|
315
|
+
path = '/' + path;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const parts = path.split('/').filter(Boolean);
|
|
319
|
+
const resolved: string[] = [];
|
|
320
|
+
|
|
321
|
+
for (const part of parts) {
|
|
322
|
+
if (part === '..') {
|
|
323
|
+
resolved.pop();
|
|
324
|
+
} else if (part !== '.') {
|
|
325
|
+
resolved.push(part);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return '/' + resolved.join('/');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Get path segments from normalized path
|
|
334
|
+
*/
|
|
335
|
+
private getPathSegments(path: string): string[] {
|
|
336
|
+
return this.normalizePath(path).split('/').filter(Boolean);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Get parent directory path
|
|
341
|
+
*/
|
|
342
|
+
private getParentPath(path: string): string {
|
|
343
|
+
const normalized = this.normalizePath(path);
|
|
344
|
+
const lastSlash = normalized.lastIndexOf('/');
|
|
345
|
+
return lastSlash <= 0 ? '/' : normalized.slice(0, lastSlash);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Get basename from path
|
|
350
|
+
*/
|
|
351
|
+
private getBasename(path: string): string {
|
|
352
|
+
const normalized = this.normalizePath(path);
|
|
353
|
+
const lastSlash = normalized.lastIndexOf('/');
|
|
354
|
+
return normalized.slice(lastSlash + 1);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Get node at path, returns undefined if not found
|
|
359
|
+
*/
|
|
360
|
+
getNode(path: string): FSNode | undefined {
|
|
361
|
+
return this.__substrateNode(path, true, 0);
|
|
362
|
+
}
|
|
363
|
+
/** The node at a path, following every link on the way and, when `follow`, the last one too. */
|
|
364
|
+
__substrateNode(path: string, follow: boolean, depth: number): FSNode | undefined {
|
|
365
|
+
if (depth > 40) throw createNodeError('ELOOP', 'stat', path);
|
|
366
|
+
const segments = this.getPathSegments(path);
|
|
367
|
+
let current = this.root;
|
|
368
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
369
|
+
if (current.type !== 'directory' || !current.children) {
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
const child = current.children.get(segments[index]);
|
|
373
|
+
if (!child) {
|
|
374
|
+
return undefined;
|
|
375
|
+
}
|
|
376
|
+
if (child.type === 'symlink' && (follow || index < segments.length - 1)) {
|
|
377
|
+
const linkPath = '/' + segments.slice(0, index + 1).join('/');
|
|
378
|
+
const rest = segments.slice(index + 1).join('/');
|
|
379
|
+
const target = this.__substrateLinkTarget(linkPath, child.target!);
|
|
380
|
+
return this.__substrateNode(rest ? target + '/' + rest : target, follow, depth + 1);
|
|
381
|
+
}
|
|
382
|
+
current = child;
|
|
383
|
+
}
|
|
384
|
+
return current;
|
|
385
|
+
}
|
|
386
|
+
/** A link's target as an absolute path: absolute as written, or relative to the link's directory. */
|
|
387
|
+
__substrateLinkTarget(linkPath: string, target: string): string {
|
|
388
|
+
return this.normalizePath(target.startsWith('/') ? target : this.getParentPath(linkPath) + '/' + target);
|
|
389
|
+
}
|
|
390
|
+
symlinkSync(target: string, path: string): void {
|
|
391
|
+
const normalized = this.normalizePath(path);
|
|
392
|
+
const parentPath = this.getParentPath(normalized);
|
|
393
|
+
const basename = this.getBasename(normalized);
|
|
394
|
+
const parent = this.getNode(parentPath);
|
|
395
|
+
if (!parent || parent.type !== 'directory') {
|
|
396
|
+
throw createNodeError('ENOENT', 'symlink', path);
|
|
397
|
+
}
|
|
398
|
+
if (parent.children!.has(basename)) {
|
|
399
|
+
throw createNodeError('EEXIST', 'symlink', path);
|
|
400
|
+
}
|
|
401
|
+
parent.children!.set(basename, { type: 'symlink', target: String(target), mtime: Date.now() });
|
|
402
|
+
this.notifyWatchers(normalized, 'rename');
|
|
403
|
+
}
|
|
404
|
+
readlinkSync(path: string): string {
|
|
405
|
+
const node = this.__substrateNode(path, false, 0);
|
|
406
|
+
if (!node) {
|
|
407
|
+
throw createNodeError('ENOENT', 'readlink', path);
|
|
408
|
+
}
|
|
409
|
+
if (node.type !== 'symlink') {
|
|
410
|
+
throw createNodeError('EINVAL', 'readlink', path);
|
|
411
|
+
}
|
|
412
|
+
return node.target!;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Get or create directory at path (for mkdir -p behavior)
|
|
417
|
+
*/
|
|
418
|
+
private ensureDirectory(path: string): FSNode {
|
|
419
|
+
const segments = this.getPathSegments(path);
|
|
420
|
+
let current = this.root;
|
|
421
|
+
|
|
422
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
423
|
+
const segment = segments[index];
|
|
424
|
+
if (!current.children) {
|
|
425
|
+
current.children = new Map();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
let child = current.children.get(segment);
|
|
429
|
+
if (!child) {
|
|
430
|
+
child = { type: 'directory', children: new Map(), mtime: Date.now() };
|
|
431
|
+
current.children.set(segment, child);
|
|
432
|
+
// A directory that appears is a `rename` in its parent, as Node
|
|
433
|
+
// reports one, and it is the only event a watcher of a tree has to
|
|
434
|
+
// start watching the new directory from.
|
|
435
|
+
this.notifyWatchers('/' + segments.slice(0, index + 1).join('/'), 'rename');
|
|
436
|
+
} else if (child.type === 'symlink') {
|
|
437
|
+
// A directory reached through a link is the link's target.
|
|
438
|
+
const linkPath = '/' + segments.slice(0, index + 1).join('/');
|
|
439
|
+
const rest = segments.slice(index + 1).join('/');
|
|
440
|
+
const target = this.__substrateLinkTarget(linkPath, child.target!);
|
|
441
|
+
return this.ensureDirectory(rest ? target + '/' + rest : target);
|
|
442
|
+
} else if (child.type !== 'directory') {
|
|
443
|
+
throw new Error(`ENOTDIR: not a directory, '${path}'`);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
current = child;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return current;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Check if path exists
|
|
454
|
+
*/
|
|
455
|
+
existsSync(path: string): boolean {
|
|
456
|
+
return this.getNode(path) !== undefined;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Get stats for path
|
|
461
|
+
*/
|
|
462
|
+
statSync(path: string): Stats {
|
|
463
|
+
const node = this.getNode(path);
|
|
464
|
+
if (!node) {
|
|
465
|
+
throw createNodeError('ENOENT', 'stat', path);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const size = node.type === 'file' ? (node.content?.length || 0) : 0;
|
|
469
|
+
const mtime = node.mtime;
|
|
470
|
+
const atime = node.atime ?? mtime;
|
|
471
|
+
|
|
472
|
+
return {
|
|
473
|
+
isFile: () => node.type === 'file',
|
|
474
|
+
isDirectory: () => node.type === 'directory',
|
|
475
|
+
isSymbolicLink: () => false,
|
|
476
|
+
isBlockDevice: () => false,
|
|
477
|
+
isCharacterDevice: () => false,
|
|
478
|
+
isFIFO: () => false,
|
|
479
|
+
isSocket: () => false,
|
|
480
|
+
size,
|
|
481
|
+
mode: node.type === 'directory' ? 0o755 : 0o644,
|
|
482
|
+
mtime: new Date(mtime),
|
|
483
|
+
atime: new Date(atime),
|
|
484
|
+
ctime: new Date(mtime),
|
|
485
|
+
birthtime: new Date(mtime),
|
|
486
|
+
mtimeMs: mtime,
|
|
487
|
+
atimeMs: atime,
|
|
488
|
+
ctimeMs: mtime,
|
|
489
|
+
birthtimeMs: mtime,
|
|
490
|
+
nlink: 1,
|
|
491
|
+
uid: 1000,
|
|
492
|
+
gid: 1000,
|
|
493
|
+
dev: 0,
|
|
494
|
+
ino: 0,
|
|
495
|
+
rdev: 0,
|
|
496
|
+
blksize: 4096,
|
|
497
|
+
blocks: Math.ceil(size / 512),
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* lstatSync - same as statSync for our virtual FS (no symlinks)
|
|
503
|
+
*/
|
|
504
|
+
lstatSync(path: string): Stats {
|
|
505
|
+
const node = this.__substrateNode(path, false, 0);
|
|
506
|
+
if (!node) {
|
|
507
|
+
throw createNodeError('ENOENT', 'lstat', path);
|
|
508
|
+
}
|
|
509
|
+
if (node.type !== 'symlink') return this.statSync(path);
|
|
510
|
+
const mtime = node.mtime;
|
|
511
|
+
const atime = node.atime ?? mtime;
|
|
512
|
+
return {
|
|
513
|
+
isFile: () => false,
|
|
514
|
+
isDirectory: () => false,
|
|
515
|
+
isSymbolicLink: () => true,
|
|
516
|
+
isBlockDevice: () => false,
|
|
517
|
+
isCharacterDevice: () => false,
|
|
518
|
+
isFIFO: () => false,
|
|
519
|
+
isSocket: () => false,
|
|
520
|
+
size: node.target!.length,
|
|
521
|
+
mode: 41471,
|
|
522
|
+
mtime: new Date(mtime),
|
|
523
|
+
atime: new Date(atime),
|
|
524
|
+
ctime: new Date(mtime),
|
|
525
|
+
birthtime: new Date(mtime),
|
|
526
|
+
mtimeMs: mtime,
|
|
527
|
+
atimeMs: atime,
|
|
528
|
+
ctimeMs: mtime,
|
|
529
|
+
birthtimeMs: mtime,
|
|
530
|
+
nlink: 1,
|
|
531
|
+
uid: 1000,
|
|
532
|
+
gid: 1000,
|
|
533
|
+
dev: 0,
|
|
534
|
+
ino: 0,
|
|
535
|
+
rdev: 0,
|
|
536
|
+
blksize: 4096,
|
|
537
|
+
blocks: 0
|
|
538
|
+
} as unknown as Stats;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Read file contents as Uint8Array
|
|
543
|
+
*/
|
|
544
|
+
readFileSync(path: string): Uint8Array;
|
|
545
|
+
readFileSync(path: string, encoding: 'utf8' | 'utf-8'): string;
|
|
546
|
+
readFileSync(path: string, encoding?: 'utf8' | 'utf-8'): Uint8Array | string {
|
|
547
|
+
const node = this.getNode(path);
|
|
548
|
+
|
|
549
|
+
if (!node) {
|
|
550
|
+
throw createNodeError('ENOENT', 'open', path);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (node.type !== 'file') {
|
|
554
|
+
throw createNodeError('EISDIR', 'read', path);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const content = node.content || new Uint8Array(0);
|
|
558
|
+
|
|
559
|
+
if (encoding === 'utf8' || encoding === 'utf-8') {
|
|
560
|
+
return this.decoder.decode(content);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
return content;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Write data to file, creating parent directories as needed
|
|
568
|
+
*/
|
|
569
|
+
writeFileSync(path: string, data: string | Uint8Array): void {
|
|
570
|
+
this.writeFileSyncInternal(path, data, true);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Create directory, optionally with recursive parent creation
|
|
575
|
+
*/
|
|
576
|
+
mkdirSync(path: string, options?: { recursive?: boolean }): void {
|
|
577
|
+
const normalized = this.normalizePath(path);
|
|
578
|
+
|
|
579
|
+
if (options?.recursive) {
|
|
580
|
+
this.ensureDirectory(normalized);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const parentPath = this.getParentPath(normalized);
|
|
585
|
+
const basename = this.getBasename(normalized);
|
|
586
|
+
|
|
587
|
+
if (!basename) {
|
|
588
|
+
return; // Root directory already exists
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const parent = this.getNode(parentPath);
|
|
592
|
+
|
|
593
|
+
if (!parent) {
|
|
594
|
+
throw createNodeError('ENOENT', 'mkdir', parentPath);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
if (parent.type !== 'directory') {
|
|
598
|
+
throw createNodeError('ENOTDIR', 'mkdir', parentPath);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (parent.children!.has(basename)) {
|
|
602
|
+
throw createNodeError('EEXIST', 'mkdir', path);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
parent.children!.set(basename, {
|
|
606
|
+
type: 'directory',
|
|
607
|
+
children: new Map(),
|
|
608
|
+
mtime: Date.now(),
|
|
609
|
+
});
|
|
610
|
+
this.notifyWatchers(normalized, 'rename');
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/**
|
|
614
|
+
* Read directory contents
|
|
615
|
+
*/
|
|
616
|
+
readdirSync(path: string): string[] {
|
|
617
|
+
const node = this.getNode(path);
|
|
618
|
+
|
|
619
|
+
if (!node) {
|
|
620
|
+
throw createNodeError('ENOENT', 'scandir', path);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (node.type !== 'directory') {
|
|
624
|
+
throw createNodeError('ENOTDIR', 'scandir', path);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return Array.from(node.children!.keys());
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Remove file
|
|
632
|
+
*/
|
|
633
|
+
unlinkSync(path: string): void {
|
|
634
|
+
const normalized = this.normalizePath(path);
|
|
635
|
+
const parentPath = this.getParentPath(normalized);
|
|
636
|
+
const basename = this.getBasename(normalized);
|
|
637
|
+
|
|
638
|
+
const parent = this.getNode(parentPath);
|
|
639
|
+
|
|
640
|
+
if (!parent || parent.type !== 'directory') {
|
|
641
|
+
throw createNodeError('ENOENT', 'unlink', path);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const node = parent.children!.get(basename);
|
|
645
|
+
|
|
646
|
+
if (!node) {
|
|
647
|
+
throw createNodeError('ENOENT', 'unlink', path);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
if (node.type !== 'file' && node.type !== 'symlink') {
|
|
651
|
+
throw createNodeError('EISDIR', 'unlink', path);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
parent.children!.delete(basename);
|
|
655
|
+
|
|
656
|
+
// Notify watchers
|
|
657
|
+
this.notifyWatchers(normalized, 'rename');
|
|
658
|
+
// Emit delete event for worker sync
|
|
659
|
+
this.emit('delete', normalized);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Remove directory (must be empty)
|
|
664
|
+
*/
|
|
665
|
+
rmdirSync(path: string): void {
|
|
666
|
+
const normalized = this.normalizePath(path);
|
|
667
|
+
const parentPath = this.getParentPath(normalized);
|
|
668
|
+
const basename = this.getBasename(normalized);
|
|
669
|
+
|
|
670
|
+
if (!basename) {
|
|
671
|
+
throw new Error(`EPERM: operation not permitted, '${path}'`);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const parent = this.getNode(parentPath);
|
|
675
|
+
|
|
676
|
+
if (!parent || parent.type !== 'directory') {
|
|
677
|
+
throw createNodeError('ENOENT', 'rmdir', path);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const node = parent.children!.get(basename);
|
|
681
|
+
|
|
682
|
+
if (!node) {
|
|
683
|
+
throw createNodeError('ENOENT', 'rmdir', path);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if (node.type !== 'directory') {
|
|
687
|
+
throw createNodeError('ENOTDIR', 'rmdir', path);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
if (node.children!.size > 0) {
|
|
691
|
+
throw createNodeError('ENOTEMPTY', 'rmdir', path);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
parent.children!.delete(basename);
|
|
695
|
+
// A directory that goes is a `rename` in its parent too, as its removal is.
|
|
696
|
+
this.notifyWatchers(normalized, 'rename');
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Rename/move file or directory
|
|
701
|
+
*/
|
|
702
|
+
renameSync(oldPath: string, newPath: string): void {
|
|
703
|
+
const normalizedOld = this.normalizePath(oldPath);
|
|
704
|
+
const normalizedNew = this.normalizePath(newPath);
|
|
705
|
+
|
|
706
|
+
const oldParentPath = this.getParentPath(normalizedOld);
|
|
707
|
+
const oldBasename = this.getBasename(normalizedOld);
|
|
708
|
+
const newParentPath = this.getParentPath(normalizedNew);
|
|
709
|
+
const newBasename = this.getBasename(normalizedNew);
|
|
710
|
+
|
|
711
|
+
const oldParent = this.getNode(oldParentPath);
|
|
712
|
+
|
|
713
|
+
if (!oldParent || oldParent.type !== 'directory') {
|
|
714
|
+
throw createNodeError('ENOENT', 'rename', oldPath);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const node = oldParent.children!.get(oldBasename);
|
|
718
|
+
|
|
719
|
+
if (!node) {
|
|
720
|
+
throw createNodeError('ENOENT', 'rename', oldPath);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
const newParent = this.ensureDirectory(newParentPath);
|
|
724
|
+
|
|
725
|
+
oldParent.children!.delete(oldBasename);
|
|
726
|
+
newParent.children!.set(newBasename, node);
|
|
727
|
+
|
|
728
|
+
// Notify watchers
|
|
729
|
+
this.notifyWatchers(normalizedOld, 'rename');
|
|
730
|
+
this.notifyWatchers(normalizedNew, 'rename');
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Read file with optional options parameter
|
|
735
|
+
*/
|
|
736
|
+
readFile(
|
|
737
|
+
path: string,
|
|
738
|
+
optionsOrCallback?: string | { encoding?: string } | ((err: Error | null, data?: Uint8Array | string) => void),
|
|
739
|
+
callback?: (err: Error | null, data?: Uint8Array | string) => void
|
|
740
|
+
): void | Promise<Uint8Array | string> {
|
|
741
|
+
const actualCallback = typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
|
|
742
|
+
const encoding = typeof optionsOrCallback === 'string' ? optionsOrCallback
|
|
743
|
+
: optionsOrCallback && typeof optionsOrCallback === 'object' ? optionsOrCallback.encoding : void 0;
|
|
744
|
+
const read = () => encoding ? this.readFileSync(path, encoding as 'utf8') : this.readFileSync(path);
|
|
745
|
+
if (!actualCallback) {
|
|
746
|
+
return new Promise((resolve, reject) => {
|
|
747
|
+
try { resolve(read()); } catch (err) { reject(err); }
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
const data = read();
|
|
752
|
+
queueMicrotask(() => actualCallback(null, data));
|
|
753
|
+
} catch (err) {
|
|
754
|
+
queueMicrotask(() => actualCallback(err as Error));
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Async stat
|
|
760
|
+
*/
|
|
761
|
+
stat(path: string, callback: (err: Error | null, stats?: Stats) => void): void {
|
|
762
|
+
try {
|
|
763
|
+
const stats = this.statSync(path);
|
|
764
|
+
queueMicrotask(() => callback(null, stats));
|
|
765
|
+
} catch (err) {
|
|
766
|
+
queueMicrotask(() => callback(err as Error));
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Async lstat
|
|
772
|
+
*/
|
|
773
|
+
lstat(path: string, callback: (err: Error | null, stats?: Stats) => void): void {
|
|
774
|
+
try {
|
|
775
|
+
const stats = this.lstatSync(path);
|
|
776
|
+
queueMicrotask(() => callback(null, stats));
|
|
777
|
+
} catch (err) {
|
|
778
|
+
queueMicrotask(() => callback(err as Error));
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
/**
|
|
783
|
+
* Async readdir
|
|
784
|
+
*/
|
|
785
|
+
readdir(
|
|
786
|
+
path: string,
|
|
787
|
+
optionsOrCallback?: { withFileTypes?: boolean } | ((err: Error | null, files?: string[]) => void),
|
|
788
|
+
callback?: (err: Error | null, files?: string[]) => void
|
|
789
|
+
): void {
|
|
790
|
+
const actualCallback = typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
|
|
791
|
+
|
|
792
|
+
try {
|
|
793
|
+
const files = this.readdirSync(path);
|
|
794
|
+
if (actualCallback) {
|
|
795
|
+
queueMicrotask(() => actualCallback(null, files));
|
|
796
|
+
}
|
|
797
|
+
} catch (err) {
|
|
798
|
+
if (actualCallback) {
|
|
799
|
+
queueMicrotask(() => actualCallback(err as Error));
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Async realpath
|
|
806
|
+
*/
|
|
807
|
+
realpath(path: string, callback: (err: Error | null, resolvedPath?: string) => void): void {
|
|
808
|
+
try {
|
|
809
|
+
const resolved = this.realpathSync(path);
|
|
810
|
+
queueMicrotask(() => callback(null, resolved));
|
|
811
|
+
} catch (err) {
|
|
812
|
+
queueMicrotask(() => callback(err as Error));
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Sync realpath - in our VFS, just normalize the path
|
|
818
|
+
*/
|
|
819
|
+
realpathSync(path: string): string {
|
|
820
|
+
let resolved = this.normalizePath(path);
|
|
821
|
+
for (let hops = 0; hops <= 40; hops += 1) {
|
|
822
|
+
const segments = resolved.split('/').filter(Boolean);
|
|
823
|
+
let current: FSNode | undefined = this.root;
|
|
824
|
+
let linked = false;
|
|
825
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
826
|
+
const child: FSNode | undefined = current && current.children ? current.children.get(segments[index]) : void 0;
|
|
827
|
+
if (!child) throw createNodeError('ENOENT', 'realpath', path);
|
|
828
|
+
if (child.type === 'symlink') {
|
|
829
|
+
const linkPath = '/' + segments.slice(0, index + 1).join('/');
|
|
830
|
+
const rest = segments.slice(index + 1).join('/');
|
|
831
|
+
const target = this.__substrateLinkTarget(linkPath, child.target!);
|
|
832
|
+
resolved = this.normalizePath(rest ? target + '/' + rest : target);
|
|
833
|
+
linked = true;
|
|
834
|
+
break;
|
|
835
|
+
}
|
|
836
|
+
current = child;
|
|
837
|
+
}
|
|
838
|
+
if (!linked) return resolved;
|
|
839
|
+
}
|
|
840
|
+
throw createNodeError('ELOOP', 'realpath', path);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Watch for file changes
|
|
845
|
+
*/
|
|
846
|
+
watch(
|
|
847
|
+
filename: string,
|
|
848
|
+
optionsOrListener?: { persistent?: boolean; recursive?: boolean; encoding?: string } | WatchListener,
|
|
849
|
+
listener?: WatchListener
|
|
850
|
+
): FSWatcher {
|
|
851
|
+
const normalized = this.normalizePath(filename);
|
|
852
|
+
|
|
853
|
+
// Parse arguments
|
|
854
|
+
let options: { persistent?: boolean; recursive?: boolean } = {};
|
|
855
|
+
let actualListener: WatchListener | undefined;
|
|
856
|
+
|
|
857
|
+
if (typeof optionsOrListener === 'function') {
|
|
858
|
+
actualListener = optionsOrListener;
|
|
859
|
+
} else if (optionsOrListener) {
|
|
860
|
+
options = optionsOrListener;
|
|
861
|
+
actualListener = listener;
|
|
862
|
+
} else {
|
|
863
|
+
actualListener = listener;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// Create watcher entry
|
|
867
|
+
const entry: WatcherEntry = {
|
|
868
|
+
listener: actualListener || (() => {}),
|
|
869
|
+
recursive: options.recursive || false,
|
|
870
|
+
closed: false,
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
// Add to watchers map
|
|
874
|
+
if (!this.watchers.has(normalized)) {
|
|
875
|
+
this.watchers.set(normalized, new Set());
|
|
876
|
+
}
|
|
877
|
+
this.watchers.get(normalized)!.add(entry);
|
|
878
|
+
|
|
879
|
+
// Return FSWatcher interface
|
|
880
|
+
const watcher: FSWatcher = {
|
|
881
|
+
close: () => {
|
|
882
|
+
entry.closed = true;
|
|
883
|
+
const watcherSet = this.watchers.get(normalized);
|
|
884
|
+
if (watcherSet) {
|
|
885
|
+
watcherSet.delete(entry);
|
|
886
|
+
if (watcherSet.size === 0) {
|
|
887
|
+
this.watchers.delete(normalized);
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
ref: () => watcher,
|
|
892
|
+
unref: () => watcher,
|
|
893
|
+
};
|
|
894
|
+
|
|
895
|
+
return watcher;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Notify watchers of file changes
|
|
900
|
+
*/
|
|
901
|
+
private notifyWatchers(path: string, eventType: WatchEventType): void {
|
|
902
|
+
const normalized = this.normalizePath(path);
|
|
903
|
+
const basename = this.getBasename(normalized);
|
|
904
|
+
|
|
905
|
+
// Check direct watchers on this file
|
|
906
|
+
const directWatchers = this.watchers.get(normalized);
|
|
907
|
+
if (directWatchers) {
|
|
908
|
+
for (const entry of directWatchers) {
|
|
909
|
+
if (!entry.closed) {
|
|
910
|
+
try {
|
|
911
|
+
entry.listener(eventType, basename);
|
|
912
|
+
} catch (err) {
|
|
913
|
+
console.error('Error in file watcher:', err);
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// Check parent directory watchers (recursive and non-recursive)
|
|
920
|
+
let currentPath = this.getParentPath(normalized);
|
|
921
|
+
let relativePath = basename;
|
|
922
|
+
|
|
923
|
+
while (currentPath) {
|
|
924
|
+
const parentWatchers = this.watchers.get(currentPath);
|
|
925
|
+
if (parentWatchers) {
|
|
926
|
+
for (const entry of parentWatchers) {
|
|
927
|
+
if (!entry.closed) {
|
|
928
|
+
// Non-recursive watchers only get notified for direct children
|
|
929
|
+
const isDirectChild = this.getParentPath(normalized) === currentPath;
|
|
930
|
+
if (entry.recursive || isDirectChild) {
|
|
931
|
+
try {
|
|
932
|
+
entry.listener(eventType, relativePath);
|
|
933
|
+
} catch (err) {
|
|
934
|
+
console.error('Error in file watcher:', err);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
if (currentPath === '/') break;
|
|
942
|
+
relativePath = this.getBasename(currentPath) + '/' + relativePath;
|
|
943
|
+
currentPath = this.getParentPath(currentPath);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Access check - in our VFS, always succeeds if file exists
|
|
949
|
+
*/
|
|
950
|
+
accessSync(path: string, mode?: number): void {
|
|
951
|
+
if (!this.existsSync(path)) {
|
|
952
|
+
throw createNodeError('ENOENT', 'access', path);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Async access
|
|
958
|
+
*/
|
|
959
|
+
access(path: string, modeOrCallback?: number | ((err: Error | null) => void), callback?: (err: Error | null) => void): void {
|
|
960
|
+
const actualCallback = typeof modeOrCallback === 'function' ? modeOrCallback : callback;
|
|
961
|
+
try {
|
|
962
|
+
this.accessSync(path);
|
|
963
|
+
if (actualCallback) queueMicrotask(() => actualCallback(null));
|
|
964
|
+
} catch (err) {
|
|
965
|
+
if (actualCallback) queueMicrotask(() => actualCallback(err as Error));
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* Copy file
|
|
971
|
+
*/
|
|
972
|
+
copyFileSync(src: string, dest: string): void {
|
|
973
|
+
const content = this.readFileSync(src);
|
|
974
|
+
this.writeFileSync(dest, content);
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
/**
|
|
978
|
+
* Create read stream - simplified implementation
|
|
979
|
+
*/
|
|
980
|
+
createReadStream(path: string, options?: ReadStreamOptions | string): FsReadStream {
|
|
981
|
+
const self = this;
|
|
982
|
+
const opts = typeof options === 'string' ? { encoding: options } : options || {};
|
|
983
|
+
const stream = new Readable() as FsReadStream;
|
|
984
|
+
stream.path = path;
|
|
985
|
+
stream.bytesRead = 0;
|
|
986
|
+
if (opts.encoding) stream.setEncoding(opts.encoding);
|
|
987
|
+
setTimeout(() => {
|
|
988
|
+
try {
|
|
989
|
+
let data = self.readFileSync(path);
|
|
990
|
+
const start = typeof opts.start === 'number' ? opts.start : 0;
|
|
991
|
+
const end = typeof opts.end === 'number' ? opts.end + 1 : data.length;
|
|
992
|
+
if (start > 0 || end < data.length) data = data.subarray(start, Math.max(start, end));
|
|
993
|
+
stream.bytesRead = data.length;
|
|
994
|
+
stream.emit('open');
|
|
995
|
+
stream.emit('ready');
|
|
996
|
+
if (data.length > 0) stream.push(Buffer.from(data.buffer, data.byteOffset, data.byteLength));
|
|
997
|
+
stream.push(null);
|
|
998
|
+
} catch (err) {
|
|
999
|
+
stream.emit('error', err);
|
|
1000
|
+
}
|
|
1001
|
+
}, 0);
|
|
1002
|
+
return stream;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Create write stream - simplified implementation
|
|
1007
|
+
*/
|
|
1008
|
+
createWriteStream(path: string): {
|
|
1009
|
+
write: (data: string | Uint8Array) => boolean;
|
|
1010
|
+
end: (data?: string | Uint8Array) => void;
|
|
1011
|
+
on: (event: string, cb: (...args: unknown[]) => void) => void;
|
|
1012
|
+
} {
|
|
1013
|
+
const self = this;
|
|
1014
|
+
const chunks: Uint8Array[] = [];
|
|
1015
|
+
const listeners: Record<string, ((...args: unknown[]) => void)[]> = {};
|
|
1016
|
+
const encoder = new TextEncoder();
|
|
1017
|
+
|
|
1018
|
+
return {
|
|
1019
|
+
write(data: string | Uint8Array): boolean {
|
|
1020
|
+
const chunk = typeof data === 'string' ? encoder.encode(data) : data;
|
|
1021
|
+
chunks.push(chunk);
|
|
1022
|
+
return true;
|
|
1023
|
+
},
|
|
1024
|
+
end(data?: string | Uint8Array): void {
|
|
1025
|
+
if (data) {
|
|
1026
|
+
const chunk = typeof data === 'string' ? encoder.encode(data) : data;
|
|
1027
|
+
chunks.push(chunk);
|
|
1028
|
+
}
|
|
1029
|
+
// Combine chunks
|
|
1030
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
1031
|
+
const combined = new Uint8Array(totalLength);
|
|
1032
|
+
let offset = 0;
|
|
1033
|
+
for (const chunk of chunks) {
|
|
1034
|
+
combined.set(chunk, offset);
|
|
1035
|
+
offset += chunk.length;
|
|
1036
|
+
}
|
|
1037
|
+
self.writeFileSync(path, combined);
|
|
1038
|
+
listeners['finish']?.forEach((cb) => cb());
|
|
1039
|
+
listeners['close']?.forEach((cb) => cb());
|
|
1040
|
+
},
|
|
1041
|
+
on(event: string, cb: (...args: unknown[]) => void) {
|
|
1042
|
+
if (!listeners[event]) listeners[event] = [];
|
|
1043
|
+
listeners[event].push(cb);
|
|
1044
|
+
return this;
|
|
1045
|
+
},
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
}
|