@scelar/nodepod 1.0.0
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 +43 -0
- package/README.md +240 -0
- package/dist/child_process-BJOMsZje.js +8233 -0
- package/dist/child_process-BJOMsZje.js.map +1 -0
- package/dist/child_process-Cj8vOcuc.cjs +7434 -0
- package/dist/child_process-Cj8vOcuc.cjs.map +1 -0
- package/dist/index-Cb1Cgdnd.js +35308 -0
- package/dist/index-Cb1Cgdnd.js.map +1 -0
- package/dist/index-DsMGS-xc.cjs +37195 -0
- package/dist/index-DsMGS-xc.cjs.map +1 -0
- package/dist/index.cjs +65 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +59 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +95 -0
- package/src/__tests__/smoke.test.ts +11 -0
- package/src/constants/cdn-urls.ts +18 -0
- package/src/constants/config.ts +236 -0
- package/src/cross-origin.ts +26 -0
- package/src/engine-factory.ts +176 -0
- package/src/engine-types.ts +56 -0
- package/src/helpers/byte-encoding.ts +39 -0
- package/src/helpers/digest.ts +9 -0
- package/src/helpers/event-loop.ts +96 -0
- package/src/helpers/wasm-cache.ts +133 -0
- package/src/iframe-sandbox.ts +141 -0
- package/src/index.ts +192 -0
- package/src/isolation-helpers.ts +148 -0
- package/src/memory-volume.ts +941 -0
- package/src/module-transformer.ts +368 -0
- package/src/packages/archive-extractor.ts +248 -0
- package/src/packages/browser-bundler.ts +284 -0
- package/src/packages/installer.ts +396 -0
- package/src/packages/registry-client.ts +131 -0
- package/src/packages/version-resolver.ts +411 -0
- package/src/polyfills/assert.ts +384 -0
- package/src/polyfills/async_hooks.ts +144 -0
- package/src/polyfills/buffer.ts +628 -0
- package/src/polyfills/child_process.ts +2288 -0
- package/src/polyfills/chokidar.ts +336 -0
- package/src/polyfills/cluster.ts +106 -0
- package/src/polyfills/console.ts +136 -0
- package/src/polyfills/constants.ts +123 -0
- package/src/polyfills/crypto.ts +885 -0
- package/src/polyfills/dgram.ts +87 -0
- package/src/polyfills/diagnostics_channel.ts +76 -0
- package/src/polyfills/dns.ts +134 -0
- package/src/polyfills/domain.ts +68 -0
- package/src/polyfills/esbuild.ts +854 -0
- package/src/polyfills/events.ts +276 -0
- package/src/polyfills/fs.ts +2888 -0
- package/src/polyfills/fsevents.ts +79 -0
- package/src/polyfills/http.ts +1449 -0
- package/src/polyfills/http2.ts +199 -0
- package/src/polyfills/https.ts +76 -0
- package/src/polyfills/inspector.ts +62 -0
- package/src/polyfills/lightningcss.ts +105 -0
- package/src/polyfills/module.ts +191 -0
- package/src/polyfills/net.ts +353 -0
- package/src/polyfills/os.ts +238 -0
- package/src/polyfills/path.ts +206 -0
- package/src/polyfills/perf_hooks.ts +102 -0
- package/src/polyfills/process.ts +690 -0
- package/src/polyfills/punycode.ts +159 -0
- package/src/polyfills/querystring.ts +93 -0
- package/src/polyfills/quic.ts +118 -0
- package/src/polyfills/readdirp.ts +229 -0
- package/src/polyfills/readline.ts +692 -0
- package/src/polyfills/repl.ts +134 -0
- package/src/polyfills/rollup.ts +119 -0
- package/src/polyfills/sea.ts +33 -0
- package/src/polyfills/sqlite.ts +78 -0
- package/src/polyfills/stream.ts +1620 -0
- package/src/polyfills/string_decoder.ts +25 -0
- package/src/polyfills/tailwindcss-oxide.ts +309 -0
- package/src/polyfills/test.ts +197 -0
- package/src/polyfills/timers.ts +32 -0
- package/src/polyfills/tls.ts +105 -0
- package/src/polyfills/trace_events.ts +50 -0
- package/src/polyfills/tty.ts +71 -0
- package/src/polyfills/url.ts +174 -0
- package/src/polyfills/util.ts +559 -0
- package/src/polyfills/v8.ts +126 -0
- package/src/polyfills/vm.ts +132 -0
- package/src/polyfills/volume-registry.ts +15 -0
- package/src/polyfills/wasi.ts +44 -0
- package/src/polyfills/worker_threads.ts +326 -0
- package/src/polyfills/ws.ts +595 -0
- package/src/polyfills/zlib.ts +881 -0
- package/src/request-proxy.ts +716 -0
- package/src/script-engine.ts +3375 -0
- package/src/sdk/nodepod-fs.ts +93 -0
- package/src/sdk/nodepod-process.ts +86 -0
- package/src/sdk/nodepod-terminal.ts +350 -0
- package/src/sdk/nodepod.ts +509 -0
- package/src/sdk/types.ts +70 -0
- package/src/shell/commands/bun.ts +121 -0
- package/src/shell/commands/directory.ts +297 -0
- package/src/shell/commands/file-ops.ts +525 -0
- package/src/shell/commands/git.ts +2142 -0
- package/src/shell/commands/node.ts +80 -0
- package/src/shell/commands/npm.ts +198 -0
- package/src/shell/commands/pm-types.ts +45 -0
- package/src/shell/commands/pnpm.ts +82 -0
- package/src/shell/commands/search.ts +264 -0
- package/src/shell/commands/shell-env.ts +352 -0
- package/src/shell/commands/text-processing.ts +1152 -0
- package/src/shell/commands/yarn.ts +84 -0
- package/src/shell/shell-builtins.ts +19 -0
- package/src/shell/shell-helpers.ts +250 -0
- package/src/shell/shell-interpreter.ts +514 -0
- package/src/shell/shell-parser.ts +429 -0
- package/src/shell/shell-types.ts +85 -0
- package/src/syntax-transforms.ts +561 -0
- package/src/threading/engine-worker.ts +64 -0
- package/src/threading/inline-worker.ts +372 -0
- package/src/threading/offload-types.ts +112 -0
- package/src/threading/offload-worker.ts +383 -0
- package/src/threading/offload.ts +271 -0
- package/src/threading/process-context.ts +92 -0
- package/src/threading/process-handle.ts +275 -0
- package/src/threading/process-manager.ts +956 -0
- package/src/threading/process-worker-entry.ts +854 -0
- package/src/threading/shared-vfs.ts +352 -0
- package/src/threading/sync-channel.ts +135 -0
- package/src/threading/task-queue.ts +177 -0
- package/src/threading/vfs-bridge.ts +231 -0
- package/src/threading/worker-pool.ts +233 -0
- package/src/threading/worker-protocol.ts +358 -0
- package/src/threading/worker-vfs.ts +218 -0
- package/src/types/externals.d.ts +38 -0
- package/src/types/fs-streams.ts +142 -0
- package/src/types/manifest.ts +17 -0
- package/src/worker-sandbox.ts +90 -0
|
@@ -0,0 +1,2888 @@
|
|
|
1
|
+
// fs polyfill -- buildFileSystemBridge(volume, getCwd) wraps MemoryVolume into a Node.js-compatible fs API
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
MemoryVolume,
|
|
5
|
+
FileStat,
|
|
6
|
+
FileWatchHandle,
|
|
7
|
+
WatchCallback,
|
|
8
|
+
WatchEventKind,
|
|
9
|
+
} from "../memory-volume";
|
|
10
|
+
import { makeSystemError } from "../memory-volume";
|
|
11
|
+
import { bytesToBase64, bytesToHex } from "../helpers/byte-encoding";
|
|
12
|
+
import { precompileWasm } from "../helpers/wasm-cache";
|
|
13
|
+
import { Readable, Writable } from "./stream";
|
|
14
|
+
import { Buffer } from "./buffer";
|
|
15
|
+
import type { FsReadStreamInstance, FsWriteStreamInstance, FsReadableState, FsWritableState } from "../types/fs-streams";
|
|
16
|
+
|
|
17
|
+
export type { FileStat, FileWatchHandle, WatchCallback, WatchEventKind };
|
|
18
|
+
|
|
19
|
+
const decoder = new TextDecoder();
|
|
20
|
+
const encoder = new TextEncoder();
|
|
21
|
+
|
|
22
|
+
export type PathArg = string | URL;
|
|
23
|
+
|
|
24
|
+
export interface Dirent {
|
|
25
|
+
name: string;
|
|
26
|
+
parentPath: string;
|
|
27
|
+
path: string;
|
|
28
|
+
_dir: boolean;
|
|
29
|
+
_file: boolean;
|
|
30
|
+
isDirectory(): boolean;
|
|
31
|
+
isFile(): boolean;
|
|
32
|
+
isBlockDevice(): boolean;
|
|
33
|
+
isCharacterDevice(): boolean;
|
|
34
|
+
isFIFO(): boolean;
|
|
35
|
+
isSocket(): boolean;
|
|
36
|
+
isSymbolicLink(): boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface DirentConstructor {
|
|
40
|
+
new (entryName: string, isDir: boolean, isFile: boolean, parentPath?: string): Dirent;
|
|
41
|
+
(this: any, entryName: string, isDir: boolean, isFile: boolean, parentPath?: string): void;
|
|
42
|
+
prototype: any;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const Dirent = function Dirent(this: any, entryName: string, isDir: boolean, isFile: boolean, parentPath?: string) {
|
|
46
|
+
if (!this) return;
|
|
47
|
+
this.name = entryName;
|
|
48
|
+
this._dir = isDir;
|
|
49
|
+
this._file = isFile;
|
|
50
|
+
this.parentPath = parentPath ?? "";
|
|
51
|
+
this.path = this.parentPath;
|
|
52
|
+
} as unknown as DirentConstructor;
|
|
53
|
+
|
|
54
|
+
Dirent.prototype.isDirectory = function isDirectory(this: any): boolean {
|
|
55
|
+
return this._dir;
|
|
56
|
+
};
|
|
57
|
+
Dirent.prototype.isFile = function isFile(this: any): boolean {
|
|
58
|
+
return this._file;
|
|
59
|
+
};
|
|
60
|
+
Dirent.prototype.isBlockDevice = function isBlockDevice(): boolean {
|
|
61
|
+
return false;
|
|
62
|
+
};
|
|
63
|
+
Dirent.prototype.isCharacterDevice = function isCharacterDevice(): boolean {
|
|
64
|
+
return false;
|
|
65
|
+
};
|
|
66
|
+
Dirent.prototype.isFIFO = function isFIFO(): boolean {
|
|
67
|
+
return false;
|
|
68
|
+
};
|
|
69
|
+
Dirent.prototype.isSocket = function isSocket(): boolean {
|
|
70
|
+
return false;
|
|
71
|
+
};
|
|
72
|
+
Dirent.prototype.isSymbolicLink = function isSymbolicLink(): boolean {
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export interface Dir {
|
|
77
|
+
readonly path: string;
|
|
78
|
+
_entries: Dirent[];
|
|
79
|
+
_pos: number;
|
|
80
|
+
_closed: boolean;
|
|
81
|
+
readSync(): Dirent | null;
|
|
82
|
+
read(): Promise<Dirent | null>;
|
|
83
|
+
read(cb: (err: Error | null, dirent: Dirent | null) => void): void;
|
|
84
|
+
closeSync(): void;
|
|
85
|
+
close(): Promise<void>;
|
|
86
|
+
close(cb: (err: Error | null) => void): void;
|
|
87
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<Dirent>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface DirConstructor {
|
|
91
|
+
new (dirPath: string, entries: Dirent[]): Dir;
|
|
92
|
+
(this: any, dirPath: string, entries: Dirent[]): void;
|
|
93
|
+
prototype: any;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const Dir = function Dir(this: any, dirPath: string, entries: Dirent[]) {
|
|
97
|
+
if (!this) return;
|
|
98
|
+
this.path = dirPath;
|
|
99
|
+
this._entries = entries;
|
|
100
|
+
this._pos = 0;
|
|
101
|
+
this._closed = false;
|
|
102
|
+
} as unknown as DirConstructor;
|
|
103
|
+
|
|
104
|
+
Dir.prototype.readSync = function readSync(this: any): Dirent | null {
|
|
105
|
+
if (this._closed) throw new Error("ERR_DIR_CLOSED: Directory handle was closed");
|
|
106
|
+
if (this._pos >= this._entries.length) return null;
|
|
107
|
+
return this._entries[this._pos++];
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
Dir.prototype.read = function read(this: any, cb?: (err: Error | null, dirent: Dirent | null) => void): Promise<Dirent | null> | void {
|
|
111
|
+
if (cb) {
|
|
112
|
+
try {
|
|
113
|
+
const entry = this.readSync();
|
|
114
|
+
queueMicrotask(() => cb(null, entry));
|
|
115
|
+
} catch (e) {
|
|
116
|
+
queueMicrotask(() => cb(e as Error, null));
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const self = this;
|
|
121
|
+
return new Promise((resolve, reject) => {
|
|
122
|
+
try {
|
|
123
|
+
resolve(self.readSync());
|
|
124
|
+
} catch (e) {
|
|
125
|
+
reject(e);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
Dir.prototype.closeSync = function closeSync(this: any): void {
|
|
131
|
+
this._closed = true;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
Dir.prototype.close = function close(this: any, cb?: (err: Error | null) => void): Promise<void> | void {
|
|
135
|
+
this._closed = true;
|
|
136
|
+
if (cb) {
|
|
137
|
+
queueMicrotask(() => cb(null));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
return Promise.resolve();
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
Dir.prototype[Symbol.asyncIterator] = function(this: any): AsyncIterableIterator<Dirent> {
|
|
144
|
+
const self = this;
|
|
145
|
+
return {
|
|
146
|
+
async next(): Promise<IteratorResult<Dirent>> {
|
|
147
|
+
const entry = self.readSync();
|
|
148
|
+
if (entry === null) return { done: true, value: undefined as any };
|
|
149
|
+
return { done: false, value: entry };
|
|
150
|
+
},
|
|
151
|
+
[Symbol.asyncIterator]() { return this; },
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export class StatFs {
|
|
156
|
+
type: number;
|
|
157
|
+
bsize: number;
|
|
158
|
+
blocks: number;
|
|
159
|
+
bfree: number;
|
|
160
|
+
bavail: number;
|
|
161
|
+
files: number;
|
|
162
|
+
ffree: number;
|
|
163
|
+
constructor() {
|
|
164
|
+
this.type = 0x61756673; // "aufs" magic number
|
|
165
|
+
this.bsize = 4096;
|
|
166
|
+
this.blocks = 262144; // ~1GB virtual
|
|
167
|
+
this.bfree = 131072; // ~512MB free
|
|
168
|
+
this.bavail = 131072;
|
|
169
|
+
this.files = 65536;
|
|
170
|
+
this.ffree = 32768;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export class StatWatcher {
|
|
175
|
+
private _listeners: Map<string, ((...args: unknown[]) => void)[]> = new Map();
|
|
176
|
+
private _interval: ReturnType<typeof setInterval> | null = null;
|
|
177
|
+
|
|
178
|
+
start(_filename: string, _persistent?: boolean, _interval?: number): void {
|
|
179
|
+
}
|
|
180
|
+
stop(): void {
|
|
181
|
+
if (this._interval) {
|
|
182
|
+
clearInterval(this._interval);
|
|
183
|
+
this._interval = null;
|
|
184
|
+
}
|
|
185
|
+
this._emit("stop");
|
|
186
|
+
}
|
|
187
|
+
ref(): this { return this; }
|
|
188
|
+
unref(): this { return this; }
|
|
189
|
+
on(event: string, listener: (...args: unknown[]) => void): this {
|
|
190
|
+
if (!this._listeners.has(event)) this._listeners.set(event, []);
|
|
191
|
+
this._listeners.get(event)!.push(listener);
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
once(event: string, listener: (...args: unknown[]) => void): this {
|
|
195
|
+
const wrapped = (...args: unknown[]) => {
|
|
196
|
+
this.removeListener(event, wrapped);
|
|
197
|
+
listener(...args);
|
|
198
|
+
};
|
|
199
|
+
return this.on(event, wrapped);
|
|
200
|
+
}
|
|
201
|
+
removeListener(event: string, listener: (...args: unknown[]) => void): this {
|
|
202
|
+
const arr = this._listeners.get(event);
|
|
203
|
+
if (arr) {
|
|
204
|
+
const idx = arr.indexOf(listener);
|
|
205
|
+
if (idx !== -1) arr.splice(idx, 1);
|
|
206
|
+
}
|
|
207
|
+
return this;
|
|
208
|
+
}
|
|
209
|
+
off(event: string, listener: (...args: unknown[]) => void): this {
|
|
210
|
+
return this.removeListener(event, listener);
|
|
211
|
+
}
|
|
212
|
+
addListener(event: string, listener: (...args: unknown[]) => void): this {
|
|
213
|
+
return this.on(event, listener);
|
|
214
|
+
}
|
|
215
|
+
removeAllListeners(event?: string): this {
|
|
216
|
+
if (event) this._listeners.delete(event);
|
|
217
|
+
else this._listeners.clear();
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
emit(event: string, ...args: unknown[]): boolean {
|
|
221
|
+
return this._emit(event, ...args);
|
|
222
|
+
}
|
|
223
|
+
private _emit(event: string, ...args: unknown[]): boolean {
|
|
224
|
+
const arr = this._listeners.get(event);
|
|
225
|
+
if (!arr || arr.length === 0) return false;
|
|
226
|
+
for (const fn of arr.slice()) fn(...args);
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
interface FsConstantsShape {
|
|
232
|
+
F_OK: number;
|
|
233
|
+
R_OK: number;
|
|
234
|
+
W_OK: number;
|
|
235
|
+
X_OK: number;
|
|
236
|
+
O_RDONLY: number;
|
|
237
|
+
O_WRONLY: number;
|
|
238
|
+
O_RDWR: number;
|
|
239
|
+
O_CREAT: number;
|
|
240
|
+
O_EXCL: number;
|
|
241
|
+
O_TRUNC: number;
|
|
242
|
+
O_APPEND: number;
|
|
243
|
+
O_DIRECTORY: number;
|
|
244
|
+
O_NOFOLLOW: number;
|
|
245
|
+
O_SYNC: number;
|
|
246
|
+
O_DSYNC: number;
|
|
247
|
+
O_NONBLOCK: number;
|
|
248
|
+
O_NOCTTY: number;
|
|
249
|
+
S_IFMT: number;
|
|
250
|
+
S_IFREG: number;
|
|
251
|
+
S_IFDIR: number;
|
|
252
|
+
S_IFLNK: number;
|
|
253
|
+
S_IFCHR: number;
|
|
254
|
+
S_IFBLK: number;
|
|
255
|
+
S_IFIFO: number;
|
|
256
|
+
S_IFSOCK: number;
|
|
257
|
+
S_IRWXU: number;
|
|
258
|
+
S_IRUSR: number;
|
|
259
|
+
S_IWUSR: number;
|
|
260
|
+
S_IXUSR: number;
|
|
261
|
+
S_IRWXG: number;
|
|
262
|
+
S_IRGRP: number;
|
|
263
|
+
S_IWGRP: number;
|
|
264
|
+
S_IXGRP: number;
|
|
265
|
+
S_IRWXO: number;
|
|
266
|
+
S_IROTH: number;
|
|
267
|
+
S_IWOTH: number;
|
|
268
|
+
S_IXOTH: number;
|
|
269
|
+
COPYFILE_EXCL: number;
|
|
270
|
+
COPYFILE_FICLONE: number;
|
|
271
|
+
COPYFILE_FICLONE_FORCE: number;
|
|
272
|
+
UV_FS_SYMLINK_DIR: number;
|
|
273
|
+
UV_FS_SYMLINK_JUNCTION: number;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
interface FsPromisesShape {
|
|
277
|
+
readFile(target: PathArg): Promise<Buffer>;
|
|
278
|
+
readFile(target: PathArg, enc: "utf8" | "utf-8"): Promise<string>;
|
|
279
|
+
readFile(
|
|
280
|
+
target: PathArg,
|
|
281
|
+
opts: { encoding: "utf8" | "utf-8" },
|
|
282
|
+
): Promise<string>;
|
|
283
|
+
writeFile(target: PathArg, data: string | Uint8Array): Promise<void>;
|
|
284
|
+
appendFile(target: PathArg, data: string | Uint8Array): Promise<void>;
|
|
285
|
+
stat(target: PathArg): Promise<FileStat>;
|
|
286
|
+
lstat(target: PathArg): Promise<FileStat>;
|
|
287
|
+
readdir(target: PathArg): Promise<string[]>;
|
|
288
|
+
mkdir(target: PathArg, opts?: { recursive?: boolean }): Promise<void>;
|
|
289
|
+
unlink(target: PathArg): Promise<void>;
|
|
290
|
+
rmdir(target: PathArg): Promise<void>;
|
|
291
|
+
rm(
|
|
292
|
+
target: PathArg,
|
|
293
|
+
opts?: { recursive?: boolean; force?: boolean },
|
|
294
|
+
): Promise<void>;
|
|
295
|
+
rename(src: PathArg, dest: PathArg): Promise<void>;
|
|
296
|
+
access(target: PathArg, mode?: number): Promise<void>;
|
|
297
|
+
realpath(target: PathArg): Promise<string>;
|
|
298
|
+
copyFile(src: PathArg, dest: PathArg): Promise<void>;
|
|
299
|
+
symlink(target: PathArg, path: PathArg, type?: string): Promise<void>;
|
|
300
|
+
readlink(target: PathArg): Promise<string>;
|
|
301
|
+
link(existingPath: PathArg, newPath: PathArg): Promise<void>;
|
|
302
|
+
chmod(target: PathArg, mode: number): Promise<void>;
|
|
303
|
+
chown(target: PathArg, uid: number, gid: number): Promise<void>;
|
|
304
|
+
lchown(target: PathArg, uid: number, gid: number): Promise<void>;
|
|
305
|
+
truncate(target: PathArg, len?: number): Promise<void>;
|
|
306
|
+
utimes(target: PathArg, atime: unknown, mtime: unknown): Promise<void>;
|
|
307
|
+
lutimes(target: PathArg, atime: unknown, mtime: unknown): Promise<void>;
|
|
308
|
+
glob(pattern: string | string[], opts?: { cwd?: string; exclude?: string[] | ((p: string) => boolean) }): AsyncIterable<string>;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
export interface FsBridge {
|
|
313
|
+
readFileSync(target: PathArg): Buffer;
|
|
314
|
+
readFileSync(target: PathArg, enc: "utf8" | "utf-8"): string;
|
|
315
|
+
readFileSync(target: PathArg, opts: { encoding: "utf8" | "utf-8" }): string;
|
|
316
|
+
readFileSync(target: PathArg, opts: { encoding?: null }): Buffer;
|
|
317
|
+
writeFileSync(target: PathArg, data: string | Uint8Array): void;
|
|
318
|
+
appendFileSync(target: PathArg, data: string | Uint8Array): void;
|
|
319
|
+
existsSync(target: PathArg): boolean;
|
|
320
|
+
mkdirSync(target: PathArg, opts?: { recursive?: boolean }): void;
|
|
321
|
+
readdirSync(target: PathArg): string[];
|
|
322
|
+
readdirSync(target: PathArg, opts: { withFileTypes: true }): Dirent[];
|
|
323
|
+
readdirSync(
|
|
324
|
+
target: PathArg,
|
|
325
|
+
opts?: { withFileTypes?: boolean; encoding?: string } | string,
|
|
326
|
+
): string[] | Dirent[];
|
|
327
|
+
statSync(target: PathArg): FileStat;
|
|
328
|
+
lstatSync(target: PathArg): FileStat;
|
|
329
|
+
fstatSync(fd: number): FileStat;
|
|
330
|
+
unlinkSync(target: PathArg): void;
|
|
331
|
+
rmdirSync(target: PathArg): void;
|
|
332
|
+
renameSync(src: PathArg, dest: PathArg): void;
|
|
333
|
+
realpathSync: ((target: PathArg) => string) & {
|
|
334
|
+
native: (target: PathArg) => string;
|
|
335
|
+
};
|
|
336
|
+
accessSync(target: PathArg, mode?: number): void;
|
|
337
|
+
copyFileSync(src: PathArg, dest: PathArg): void;
|
|
338
|
+
symlinkSync(target: PathArg, path: PathArg, type?: string): void;
|
|
339
|
+
readlinkSync(target: PathArg): string;
|
|
340
|
+
linkSync(existingPath: PathArg, newPath: PathArg): void;
|
|
341
|
+
chmodSync(target: PathArg, mode: number): void;
|
|
342
|
+
chownSync(target: PathArg, uid: number, gid: number): void;
|
|
343
|
+
truncateSync(target: PathArg, len?: number): void;
|
|
344
|
+
openSync(target: string, flags: string | number, mode?: number): number;
|
|
345
|
+
closeSync(fd: number): void;
|
|
346
|
+
readSync(
|
|
347
|
+
fd: number,
|
|
348
|
+
buf: Buffer | Uint8Array,
|
|
349
|
+
off: number,
|
|
350
|
+
len: number,
|
|
351
|
+
pos: number | null,
|
|
352
|
+
): number;
|
|
353
|
+
writeSync(
|
|
354
|
+
fd: number,
|
|
355
|
+
buf: Buffer | Uint8Array | string,
|
|
356
|
+
off?: number,
|
|
357
|
+
len?: number,
|
|
358
|
+
pos?: number | null,
|
|
359
|
+
): number;
|
|
360
|
+
ftruncateSync(fd: number, len?: number): void;
|
|
361
|
+
fsyncSync(fd: number): void;
|
|
362
|
+
fdatasyncSync(fd: number): void;
|
|
363
|
+
mkdtempSync(prefix: string): string;
|
|
364
|
+
rmSync(target: string, opts?: { recursive?: boolean; force?: boolean }): void;
|
|
365
|
+
opendirSync(target: unknown): Dir;
|
|
366
|
+
watch(
|
|
367
|
+
filename: string,
|
|
368
|
+
opts?: { persistent?: boolean; recursive?: boolean },
|
|
369
|
+
listener?: WatchCallback,
|
|
370
|
+
): FileWatchHandle;
|
|
371
|
+
watch(filename: string, listener?: WatchCallback): FileWatchHandle;
|
|
372
|
+
readFile(
|
|
373
|
+
target: string,
|
|
374
|
+
cb: (err: Error | null, data?: Uint8Array) => void,
|
|
375
|
+
): void;
|
|
376
|
+
readFile(
|
|
377
|
+
target: string,
|
|
378
|
+
opts: { encoding: string },
|
|
379
|
+
cb: (err: Error | null, data?: string) => void,
|
|
380
|
+
): void;
|
|
381
|
+
writeFile(
|
|
382
|
+
target: string,
|
|
383
|
+
data: string | Uint8Array,
|
|
384
|
+
cb: (err: Error | null) => void,
|
|
385
|
+
): void;
|
|
386
|
+
appendFile(
|
|
387
|
+
target: string,
|
|
388
|
+
data: string | Uint8Array,
|
|
389
|
+
cb: (err: Error | null) => void,
|
|
390
|
+
): void;
|
|
391
|
+
stat(target: string, cb: (err: Error | null, stats?: FileStat) => void): void;
|
|
392
|
+
lstat(
|
|
393
|
+
target: string,
|
|
394
|
+
cb: (err: Error | null, stats?: FileStat) => void,
|
|
395
|
+
): void;
|
|
396
|
+
readdir(
|
|
397
|
+
target: string,
|
|
398
|
+
cb: (err: Error | null, entries?: string[]) => void,
|
|
399
|
+
): void;
|
|
400
|
+
mkdir(
|
|
401
|
+
target: string,
|
|
402
|
+
opts: { recursive?: boolean },
|
|
403
|
+
cb: (err: Error | null) => void,
|
|
404
|
+
): void;
|
|
405
|
+
unlink(target: string, cb: (err: Error | null) => void): void;
|
|
406
|
+
rmdir(target: string, cb: (err: Error | null) => void): void;
|
|
407
|
+
rename(oldPath: string, newPath: string, cb: (err: Error | null) => void): void;
|
|
408
|
+
realpath(
|
|
409
|
+
target: string,
|
|
410
|
+
cb: (err: Error | null, resolved?: string) => void,
|
|
411
|
+
): void;
|
|
412
|
+
access(target: string, cb: (err: Error | null) => void): void;
|
|
413
|
+
access(target: string, mode: number, cb: (err: Error | null) => void): void;
|
|
414
|
+
symlink(target: string, path: string, cb: (err: Error | null) => void): void;
|
|
415
|
+
symlink(
|
|
416
|
+
target: string,
|
|
417
|
+
path: string,
|
|
418
|
+
type: string,
|
|
419
|
+
cb: (err: Error | null) => void,
|
|
420
|
+
): void;
|
|
421
|
+
readlink(
|
|
422
|
+
target: string,
|
|
423
|
+
cb: (err: Error | null, linkTarget?: string) => void,
|
|
424
|
+
): void;
|
|
425
|
+
link(
|
|
426
|
+
existingPath: string,
|
|
427
|
+
newPath: string,
|
|
428
|
+
cb: (err: Error | null) => void,
|
|
429
|
+
): void;
|
|
430
|
+
chmod(target: string, mode: number, cb: (err: Error | null) => void): void;
|
|
431
|
+
chown(
|
|
432
|
+
target: string,
|
|
433
|
+
uid: number,
|
|
434
|
+
gid: number,
|
|
435
|
+
cb: (err: Error | null) => void,
|
|
436
|
+
): void;
|
|
437
|
+
createReadStream(
|
|
438
|
+
target: string,
|
|
439
|
+
opts?: { encoding?: string; start?: number; end?: number },
|
|
440
|
+
): import("./stream").Readable;
|
|
441
|
+
createWriteStream(
|
|
442
|
+
target: string,
|
|
443
|
+
opts?: { encoding?: string; flags?: string },
|
|
444
|
+
): import("./stream").Writable;
|
|
445
|
+
cpSync(src: unknown, dest: unknown, opts?: { recursive?: boolean; force?: boolean; errorOnExist?: boolean }): void;
|
|
446
|
+
cp(src: unknown, dest: unknown, optsOrCb?: unknown, cb?: (err: Error | null) => void): void;
|
|
447
|
+
readvSync(fd: number, buffers: ArrayBufferView[], pos?: number | null): number;
|
|
448
|
+
readv(fd: number, buffers: ArrayBufferView[], posOrCb?: unknown, cb?: unknown): void;
|
|
449
|
+
globSync(pattern: string | string[], opts?: { cwd?: string; exclude?: string[] | ((p: string) => boolean) }): string[];
|
|
450
|
+
glob(pattern: string | string[], optsOrCb?: unknown, cb?: unknown): void;
|
|
451
|
+
statfsSync(target: unknown, opts?: unknown): StatFs;
|
|
452
|
+
statfs(target: unknown, optsOrCb?: unknown, cb?: unknown): void;
|
|
453
|
+
openAsBlob(target: unknown, opts?: { type?: string }): Promise<Blob>;
|
|
454
|
+
exists(target: unknown, cb: (exists: boolean) => void): void;
|
|
455
|
+
lchmodSync(target: unknown, mode: number): void;
|
|
456
|
+
lchmod(target: unknown, mode: number, cb: (err: Error | null) => void): void;
|
|
457
|
+
fdatasync(fd: number, cb: (err: Error | null) => void): void;
|
|
458
|
+
fsync(fd: number, cb: (err: Error | null) => void): void;
|
|
459
|
+
ftruncate(fd: number, lenOrCb?: unknown, cb?: unknown): void;
|
|
460
|
+
truncate(target: unknown, lenOrCb?: unknown, cb?: unknown): void;
|
|
461
|
+
mkdtemp(prefix: string, optsOrCb?: unknown, cb?: unknown): void;
|
|
462
|
+
StatFs: typeof StatFs;
|
|
463
|
+
StatWatcher: typeof StatWatcher;
|
|
464
|
+
promises: FsPromisesShape;
|
|
465
|
+
constants: FsConstantsShape;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function resolvePath(target: unknown, cwdFn?: () => string): string {
|
|
469
|
+
let p: string;
|
|
470
|
+
|
|
471
|
+
if (typeof target === "string") {
|
|
472
|
+
p = target;
|
|
473
|
+
} else if (
|
|
474
|
+
target instanceof URL ||
|
|
475
|
+
// cross-realm URL objects fail instanceof, so duck-type check
|
|
476
|
+
(target && typeof target === "object" &&
|
|
477
|
+
typeof (target as any).protocol === "string" &&
|
|
478
|
+
typeof (target as any).pathname === "string")
|
|
479
|
+
) {
|
|
480
|
+
const url = target as { protocol: string; pathname: string };
|
|
481
|
+
if (url.protocol !== "file:") {
|
|
482
|
+
throw new Error(`Unsupported protocol: ${url.protocol}`);
|
|
483
|
+
}
|
|
484
|
+
p = decodeURIComponent(url.pathname);
|
|
485
|
+
} else if (target && typeof target === "object" && "toString" in target) {
|
|
486
|
+
p = String(target);
|
|
487
|
+
} else {
|
|
488
|
+
throw new TypeError(`Path must be a string or URL. Got: ${typeof target}`);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (!p.startsWith("/") && cwdFn) {
|
|
492
|
+
const cwd = cwdFn();
|
|
493
|
+
p = cwd.endsWith("/") ? cwd + p : cwd + "/" + p;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return p;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// plain Uint8Array.toString() returns comma-separated bytes, not UTF-8 text --
|
|
500
|
+
// must wrap with Buffer.from() so .toString() works like Node's Buffer
|
|
501
|
+
function wrapAsBuffer(raw: Uint8Array): Buffer {
|
|
502
|
+
if (typeof (raw as any).readUInt8 === "function") return raw as Buffer;
|
|
503
|
+
return Buffer.from(raw) as Buffer;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
interface OpenFile {
|
|
507
|
+
filePath: string;
|
|
508
|
+
cursor: number;
|
|
509
|
+
mode: string;
|
|
510
|
+
data: Uint8Array;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
export function buildFileSystemBridge(
|
|
514
|
+
volume: MemoryVolume,
|
|
515
|
+
getCwd?: () => string,
|
|
516
|
+
): FsBridge {
|
|
517
|
+
// each bridge gets its own FD namespace (per-process isolation)
|
|
518
|
+
const openFiles = new Map<number, OpenFile>();
|
|
519
|
+
let fdCounter = 3;
|
|
520
|
+
const abs = (target: unknown) => resolvePath(target, getCwd);
|
|
521
|
+
|
|
522
|
+
const fsConst: FsConstantsShape = {
|
|
523
|
+
F_OK: 0,
|
|
524
|
+
R_OK: 4,
|
|
525
|
+
W_OK: 2,
|
|
526
|
+
X_OK: 1,
|
|
527
|
+
O_RDONLY: 0,
|
|
528
|
+
O_WRONLY: 1,
|
|
529
|
+
O_RDWR: 2,
|
|
530
|
+
O_CREAT: 64,
|
|
531
|
+
O_EXCL: 128,
|
|
532
|
+
O_TRUNC: 512,
|
|
533
|
+
O_APPEND: 1024,
|
|
534
|
+
O_DIRECTORY: 65536,
|
|
535
|
+
O_NOFOLLOW: 131072,
|
|
536
|
+
O_SYNC: 1052672,
|
|
537
|
+
O_DSYNC: 4096,
|
|
538
|
+
O_NONBLOCK: 2048,
|
|
539
|
+
O_NOCTTY: 256,
|
|
540
|
+
S_IFMT: 61440,
|
|
541
|
+
S_IFREG: 32768,
|
|
542
|
+
S_IFDIR: 16384,
|
|
543
|
+
S_IFLNK: 40960,
|
|
544
|
+
S_IFCHR: 8192,
|
|
545
|
+
S_IFBLK: 24576,
|
|
546
|
+
S_IFIFO: 4096,
|
|
547
|
+
S_IFSOCK: 49152,
|
|
548
|
+
S_IRWXU: 448, // 0o700
|
|
549
|
+
S_IRUSR: 256, // 0o400
|
|
550
|
+
S_IWUSR: 128, // 0o200
|
|
551
|
+
S_IXUSR: 64, // 0o100
|
|
552
|
+
S_IRWXG: 56, // 0o070
|
|
553
|
+
S_IRGRP: 32, // 0o040
|
|
554
|
+
S_IWGRP: 16, // 0o020
|
|
555
|
+
S_IXGRP: 8, // 0o010
|
|
556
|
+
S_IRWXO: 7, // 0o007
|
|
557
|
+
S_IROTH: 4, // 0o004
|
|
558
|
+
S_IWOTH: 2, // 0o002
|
|
559
|
+
S_IXOTH: 1, // 0o001
|
|
560
|
+
COPYFILE_EXCL: 1,
|
|
561
|
+
COPYFILE_FICLONE: 2,
|
|
562
|
+
COPYFILE_FICLONE_FORCE: 4,
|
|
563
|
+
UV_FS_SYMLINK_DIR: 1,
|
|
564
|
+
UV_FS_SYMLINK_JUNCTION: 2,
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// Helper to build Dirent array from directory entries
|
|
568
|
+
function toDirents(dirPath: string, names: string[]): Dirent[] {
|
|
569
|
+
return names.map((name) => {
|
|
570
|
+
const full = dirPath.endsWith("/")
|
|
571
|
+
? dirPath + name
|
|
572
|
+
: dirPath + "/" + name;
|
|
573
|
+
let isDir = false;
|
|
574
|
+
let isFile = false;
|
|
575
|
+
try {
|
|
576
|
+
const st = volume.statSync(full);
|
|
577
|
+
isDir = st.isDirectory();
|
|
578
|
+
isFile = st.isFile();
|
|
579
|
+
} catch {
|
|
580
|
+
isFile = true;
|
|
581
|
+
}
|
|
582
|
+
return new Dirent(name, isDir, isFile, dirPath);
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
class FileHandle {
|
|
586
|
+
fd: number;
|
|
587
|
+
constructor(fd: number) {
|
|
588
|
+
this.fd = fd;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
appendFile(data: string | Uint8Array): Promise<void> {
|
|
592
|
+
const entry = openFiles.get(this.fd);
|
|
593
|
+
if (!entry) return Promise.reject(makeBadfError("appendFile"));
|
|
594
|
+
const bytes = typeof data === "string" ? encoder.encode(data) : data;
|
|
595
|
+
const newData = new Uint8Array(entry.data.length + bytes.length);
|
|
596
|
+
newData.set(entry.data);
|
|
597
|
+
newData.set(bytes, entry.data.length);
|
|
598
|
+
entry.data = newData;
|
|
599
|
+
entry.cursor = newData.length;
|
|
600
|
+
return Promise.resolve();
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
chmod(_mode: number): Promise<void> {
|
|
604
|
+
return Promise.resolve();
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
chown(_uid: number, _gid: number): Promise<void> {
|
|
608
|
+
return Promise.resolve();
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
close(): Promise<void> {
|
|
612
|
+
try {
|
|
613
|
+
const entry = openFiles.get(this.fd);
|
|
614
|
+
if (entry) {
|
|
615
|
+
if (entry.mode.includes("w") || entry.mode.includes("a") || entry.mode.includes("+")) {
|
|
616
|
+
volume.writeFileSync(entry.filePath, entry.data);
|
|
617
|
+
}
|
|
618
|
+
openFiles.delete(this.fd);
|
|
619
|
+
}
|
|
620
|
+
return Promise.resolve();
|
|
621
|
+
} catch (e) {
|
|
622
|
+
return Promise.reject(e);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
createReadStream(opts?: { encoding?: string; start?: number; end?: number }): Readable {
|
|
627
|
+
const entry = openFiles.get(this.fd);
|
|
628
|
+
if (!entry) throw makeBadfError("createReadStream");
|
|
629
|
+
const stream: any = new Readable();
|
|
630
|
+
stream.path = entry.filePath;
|
|
631
|
+
stream.fd = this.fd;
|
|
632
|
+
queueMicrotask(() => {
|
|
633
|
+
try {
|
|
634
|
+
const data = entry.data;
|
|
635
|
+
const start = opts?.start ?? 0;
|
|
636
|
+
const end = opts?.end !== undefined ? opts.end + 1 : data.length;
|
|
637
|
+
stream.push(Buffer.from(data.slice(start, end)));
|
|
638
|
+
stream.push(null);
|
|
639
|
+
} catch (err) {
|
|
640
|
+
stream.destroy(err as Error);
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
return stream;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
createWriteStream(_opts?: { encoding?: string }): Writable {
|
|
647
|
+
const entry = openFiles.get(this.fd);
|
|
648
|
+
if (!entry) throw makeBadfError("createWriteStream");
|
|
649
|
+
const chunks: Uint8Array[] = [];
|
|
650
|
+
const stream: any = new Writable();
|
|
651
|
+
stream.path = entry.filePath;
|
|
652
|
+
stream.fd = this.fd;
|
|
653
|
+
stream._write = (chunk: Uint8Array | string, _enc: string, cb: (err?: Error | null) => void) => {
|
|
654
|
+
const bytes = typeof chunk === "string" ? encoder.encode(chunk) : chunk;
|
|
655
|
+
chunks.push(bytes);
|
|
656
|
+
cb(null);
|
|
657
|
+
};
|
|
658
|
+
stream.on("finish", () => {
|
|
659
|
+
const totalLen = chunks.reduce((sum, c) => sum + c.length, 0);
|
|
660
|
+
const merged = new Uint8Array(totalLen);
|
|
661
|
+
let pos = 0;
|
|
662
|
+
for (const c of chunks) { merged.set(c, pos); pos += c.length; }
|
|
663
|
+
entry.data = merged;
|
|
664
|
+
entry.cursor = merged.length;
|
|
665
|
+
});
|
|
666
|
+
return stream;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
datasync(): Promise<void> {
|
|
670
|
+
return Promise.resolve();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
read(
|
|
674
|
+
bufOrOpts?: Buffer | Uint8Array | { buffer: Buffer | Uint8Array; offset?: number; length?: number; position?: number | null },
|
|
675
|
+
offset?: number,
|
|
676
|
+
length?: number,
|
|
677
|
+
position?: number | null,
|
|
678
|
+
): Promise<{ bytesRead: number; buffer: Buffer | Uint8Array }> {
|
|
679
|
+
const entry = openFiles.get(this.fd);
|
|
680
|
+
if (!entry) return Promise.reject(makeBadfError("read"));
|
|
681
|
+
let buf: Buffer | Uint8Array;
|
|
682
|
+
let off: number;
|
|
683
|
+
let len: number;
|
|
684
|
+
let pos: number | null;
|
|
685
|
+
if (bufOrOpts && typeof bufOrOpts === "object" && "buffer" in bufOrOpts) {
|
|
686
|
+
const opts = bufOrOpts as { buffer: Buffer | Uint8Array; offset?: number; length?: number; position?: number | null };
|
|
687
|
+
buf = opts.buffer;
|
|
688
|
+
off = opts.offset ?? 0;
|
|
689
|
+
len = opts.length ?? buf.length;
|
|
690
|
+
pos = opts.position ?? null;
|
|
691
|
+
} else {
|
|
692
|
+
buf = bufOrOpts ? (bufOrOpts as Buffer | Uint8Array) : (Buffer.alloc(16384) as unknown as Buffer);
|
|
693
|
+
off = offset ?? 0;
|
|
694
|
+
len = length ?? buf.length;
|
|
695
|
+
pos = position ?? null;
|
|
696
|
+
}
|
|
697
|
+
const readAt = pos !== null ? pos : entry.cursor;
|
|
698
|
+
const count = Math.min(len, entry.data.length - readAt);
|
|
699
|
+
if (count <= 0) return Promise.resolve({ bytesRead: 0, buffer: buf });
|
|
700
|
+
for (let i = 0; i < count; i++) buf[off + i] = entry.data[readAt + i];
|
|
701
|
+
if (pos === null) entry.cursor += count;
|
|
702
|
+
return Promise.resolve({ bytesRead: count, buffer: buf });
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
readableWebStream(): ReadableStream<Uint8Array> {
|
|
706
|
+
const entry = openFiles.get(this.fd);
|
|
707
|
+
const data = entry ? new Uint8Array(entry.data) : new Uint8Array(0);
|
|
708
|
+
return new ReadableStream({
|
|
709
|
+
start(controller) {
|
|
710
|
+
controller.enqueue(data);
|
|
711
|
+
controller.close();
|
|
712
|
+
},
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
readFile(opts?: { encoding?: string | null } | string): Promise<Buffer | string> {
|
|
717
|
+
const entry = openFiles.get(this.fd);
|
|
718
|
+
if (!entry) return Promise.reject(makeBadfError("readFile"));
|
|
719
|
+
const enc = typeof opts === "string" ? opts : opts?.encoding;
|
|
720
|
+
if (enc === "utf8" || enc === "utf-8") {
|
|
721
|
+
return Promise.resolve(decoder.decode(entry.data));
|
|
722
|
+
}
|
|
723
|
+
return Promise.resolve(wrapAsBuffer(new Uint8Array(entry.data)));
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
readLines(): AsyncIterable<string> {
|
|
727
|
+
const entry = openFiles.get(this.fd);
|
|
728
|
+
const content = entry ? decoder.decode(entry.data) : "";
|
|
729
|
+
const lines = content.split("\n");
|
|
730
|
+
return {
|
|
731
|
+
[Symbol.asyncIterator]() {
|
|
732
|
+
let i = 0;
|
|
733
|
+
return {
|
|
734
|
+
next(): Promise<IteratorResult<string>> {
|
|
735
|
+
if (i < lines.length) return Promise.resolve({ value: lines[i++], done: false });
|
|
736
|
+
return Promise.resolve({ value: undefined as any, done: true });
|
|
737
|
+
},
|
|
738
|
+
};
|
|
739
|
+
},
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
readv(buffers: ArrayBufferView[], position?: number): Promise<{ bytesRead: number; buffers: ArrayBufferView[] }> {
|
|
744
|
+
const entry = openFiles.get(this.fd);
|
|
745
|
+
if (!entry) return Promise.reject(makeBadfError("readv"));
|
|
746
|
+
let totalRead = 0;
|
|
747
|
+
let readPos = position ?? entry.cursor;
|
|
748
|
+
for (const buf of buffers) {
|
|
749
|
+
const u8 = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
750
|
+
const count = Math.min(u8.length, entry.data.length - readPos);
|
|
751
|
+
if (count <= 0) break;
|
|
752
|
+
for (let i = 0; i < count; i++) u8[i] = entry.data[readPos + i];
|
|
753
|
+
readPos += count;
|
|
754
|
+
totalRead += count;
|
|
755
|
+
if (count < u8.length) break;
|
|
756
|
+
}
|
|
757
|
+
if (position === undefined) entry.cursor = readPos;
|
|
758
|
+
return Promise.resolve({ bytesRead: totalRead, buffers });
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
stat(): Promise<FileStat> {
|
|
762
|
+
const entry = openFiles.get(this.fd);
|
|
763
|
+
if (!entry) return Promise.reject(makeBadfError("fstat"));
|
|
764
|
+
try {
|
|
765
|
+
return Promise.resolve(volume.statSync(entry.filePath));
|
|
766
|
+
} catch (e) {
|
|
767
|
+
return Promise.reject(e);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
sync(): Promise<void> {
|
|
772
|
+
return Promise.resolve();
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
truncate(len: number = 0): Promise<void> {
|
|
776
|
+
const entry = openFiles.get(this.fd);
|
|
777
|
+
if (!entry) return Promise.reject(makeBadfError("ftruncate"));
|
|
778
|
+
if (len < entry.data.length) {
|
|
779
|
+
entry.data = entry.data.slice(0, len);
|
|
780
|
+
} else if (len > entry.data.length) {
|
|
781
|
+
const bigger = new Uint8Array(len);
|
|
782
|
+
bigger.set(entry.data);
|
|
783
|
+
entry.data = bigger;
|
|
784
|
+
}
|
|
785
|
+
return Promise.resolve();
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
utimes(_atime: unknown, _mtime: unknown): Promise<void> {
|
|
789
|
+
return Promise.resolve();
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
write(
|
|
793
|
+
buf: Buffer | Uint8Array | string,
|
|
794
|
+
offsetOrOpts?: number | { offset?: number; length?: number; position?: number | null },
|
|
795
|
+
length?: number,
|
|
796
|
+
position?: number | null,
|
|
797
|
+
): Promise<{ bytesWritten: number; buffer: Buffer | Uint8Array | string }> {
|
|
798
|
+
const entry = openFiles.get(this.fd);
|
|
799
|
+
if (!entry) return Promise.reject(makeBadfError("write"));
|
|
800
|
+
let bytes: Uint8Array;
|
|
801
|
+
let off: number;
|
|
802
|
+
let len: number;
|
|
803
|
+
let pos: number | null | undefined;
|
|
804
|
+
if (typeof buf === "string") {
|
|
805
|
+
bytes = encoder.encode(buf);
|
|
806
|
+
off = 0;
|
|
807
|
+
len = bytes.length;
|
|
808
|
+
pos = typeof offsetOrOpts === "number" ? offsetOrOpts : null;
|
|
809
|
+
} else if (typeof offsetOrOpts === "object" && offsetOrOpts !== null) {
|
|
810
|
+
bytes = buf;
|
|
811
|
+
off = offsetOrOpts.offset ?? 0;
|
|
812
|
+
len = offsetOrOpts.length ?? bytes.length - off;
|
|
813
|
+
pos = offsetOrOpts.position;
|
|
814
|
+
} else {
|
|
815
|
+
bytes = buf;
|
|
816
|
+
off = (offsetOrOpts as number) ?? 0;
|
|
817
|
+
len = length ?? bytes.length - off;
|
|
818
|
+
pos = position;
|
|
819
|
+
}
|
|
820
|
+
const writeAt = pos !== null && pos !== undefined ? pos : entry.cursor;
|
|
821
|
+
const endAt = writeAt + len;
|
|
822
|
+
if (endAt > entry.data.length) {
|
|
823
|
+
const expanded = new Uint8Array(endAt);
|
|
824
|
+
expanded.set(entry.data);
|
|
825
|
+
entry.data = expanded;
|
|
826
|
+
}
|
|
827
|
+
for (let i = 0; i < len; i++) entry.data[writeAt + i] = bytes[off + i];
|
|
828
|
+
if (pos === null || pos === undefined) entry.cursor = endAt;
|
|
829
|
+
return Promise.resolve({ bytesWritten: len, buffer: buf });
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
writeFile(data: string | Uint8Array): Promise<void> {
|
|
833
|
+
const entry = openFiles.get(this.fd);
|
|
834
|
+
if (!entry) return Promise.reject(makeBadfError("writeFile"));
|
|
835
|
+
const bytes = typeof data === "string" ? encoder.encode(data) : data;
|
|
836
|
+
entry.data = new Uint8Array(bytes);
|
|
837
|
+
entry.cursor = bytes.length;
|
|
838
|
+
return Promise.resolve();
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
writev(buffers: ArrayBufferView[], position?: number): Promise<{ bytesWritten: number; buffers: ArrayBufferView[] }> {
|
|
842
|
+
const entry = openFiles.get(this.fd);
|
|
843
|
+
if (!entry) return Promise.reject(makeBadfError("writev"));
|
|
844
|
+
let totalWritten = 0;
|
|
845
|
+
let writePos = position ?? entry.cursor;
|
|
846
|
+
for (const buf of buffers) {
|
|
847
|
+
const u8 = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
848
|
+
const endAt = writePos + u8.length;
|
|
849
|
+
if (endAt > entry.data.length) {
|
|
850
|
+
const expanded = new Uint8Array(endAt);
|
|
851
|
+
expanded.set(entry.data);
|
|
852
|
+
entry.data = expanded;
|
|
853
|
+
}
|
|
854
|
+
for (let i = 0; i < u8.length; i++) entry.data[writePos + i] = u8[i];
|
|
855
|
+
writePos += u8.length;
|
|
856
|
+
totalWritten += u8.length;
|
|
857
|
+
}
|
|
858
|
+
if (position === undefined) entry.cursor = writePos;
|
|
859
|
+
return Promise.resolve({ bytesWritten: totalWritten, buffers });
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
[Symbol.asyncDispose](): Promise<void> {
|
|
863
|
+
return this.close();
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function makeBadfError(syscall: string): Error & { code: string; errno: number } {
|
|
868
|
+
const err = new Error(`EBADF: bad file descriptor, ${syscall}`) as Error & { code: string; errno: number };
|
|
869
|
+
err.code = "EBADF";
|
|
870
|
+
err.errno = -9;
|
|
871
|
+
return err;
|
|
872
|
+
}
|
|
873
|
+
const promisesApi: FsPromisesShape = {
|
|
874
|
+
readFile(
|
|
875
|
+
target: unknown,
|
|
876
|
+
encOrOpts?: string | { encoding?: string | null },
|
|
877
|
+
): Promise<Buffer | string> {
|
|
878
|
+
return new Promise((ok, fail) => {
|
|
879
|
+
try {
|
|
880
|
+
const p = abs(target);
|
|
881
|
+
let enc: string | undefined;
|
|
882
|
+
if (typeof encOrOpts === "string") enc = encOrOpts;
|
|
883
|
+
else if (encOrOpts?.encoding) enc = encOrOpts.encoding;
|
|
884
|
+
|
|
885
|
+
if (enc === "utf8" || enc === "utf-8") {
|
|
886
|
+
ok(volume.readFileSync(p, "utf8"));
|
|
887
|
+
} else {
|
|
888
|
+
ok(wrapAsBuffer(volume.readFileSync(p)));
|
|
889
|
+
}
|
|
890
|
+
} catch (e) {
|
|
891
|
+
fail(e);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
},
|
|
895
|
+
writeFile(target: unknown, data: string | Uint8Array): Promise<void> {
|
|
896
|
+
return new Promise((ok, fail) => {
|
|
897
|
+
try {
|
|
898
|
+
const wp = abs(target);
|
|
899
|
+
volume.writeFileSync(wp, data);
|
|
900
|
+
if (wp.endsWith(".wasm") && typeof data !== "string") {
|
|
901
|
+
precompileWasm(data);
|
|
902
|
+
}
|
|
903
|
+
ok();
|
|
904
|
+
} catch (e) {
|
|
905
|
+
fail(e);
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
},
|
|
909
|
+
stat(target: unknown): Promise<FileStat> {
|
|
910
|
+
return new Promise((ok, fail) => {
|
|
911
|
+
try {
|
|
912
|
+
ok(volume.statSync(abs(target)));
|
|
913
|
+
} catch (e) {
|
|
914
|
+
fail(e);
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
},
|
|
918
|
+
mkdir(target: unknown, opts?: { recursive?: boolean }): Promise<void> {
|
|
919
|
+
return new Promise((ok, fail) => {
|
|
920
|
+
try {
|
|
921
|
+
volume.mkdirSync(abs(target), opts);
|
|
922
|
+
ok();
|
|
923
|
+
} catch (e) {
|
|
924
|
+
fail(e);
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
},
|
|
928
|
+
unlink(target: unknown): Promise<void> {
|
|
929
|
+
return new Promise((ok, fail) => {
|
|
930
|
+
try {
|
|
931
|
+
volume.unlinkSync(abs(target));
|
|
932
|
+
ok();
|
|
933
|
+
} catch (e) {
|
|
934
|
+
fail(e);
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
},
|
|
938
|
+
rmdir(target: unknown): Promise<void> {
|
|
939
|
+
return new Promise((ok, fail) => {
|
|
940
|
+
try {
|
|
941
|
+
volume.rmdirSync(abs(target) as string);
|
|
942
|
+
ok();
|
|
943
|
+
} catch (e) {
|
|
944
|
+
fail(e);
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
},
|
|
948
|
+
rename(src: unknown, dest: unknown): Promise<void> {
|
|
949
|
+
return new Promise((ok, fail) => {
|
|
950
|
+
try {
|
|
951
|
+
volume.renameSync(abs(src), abs(dest));
|
|
952
|
+
ok();
|
|
953
|
+
} catch (e) {
|
|
954
|
+
fail(e);
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
},
|
|
958
|
+
access(target: unknown, mode?: number): Promise<void> {
|
|
959
|
+
return new Promise((ok, fail) => {
|
|
960
|
+
try {
|
|
961
|
+
volume.accessSync(abs(target), mode);
|
|
962
|
+
ok();
|
|
963
|
+
} catch (e) {
|
|
964
|
+
fail(e);
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
},
|
|
968
|
+
realpath(target: unknown): Promise<string> {
|
|
969
|
+
return new Promise((ok, fail) => {
|
|
970
|
+
try {
|
|
971
|
+
ok(volume.realpathSync(abs(target)));
|
|
972
|
+
} catch (e) {
|
|
973
|
+
fail(e);
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
},
|
|
977
|
+
copyFile(src: unknown, dest: unknown): Promise<void> {
|
|
978
|
+
return new Promise((ok, fail) => {
|
|
979
|
+
try {
|
|
980
|
+
volume.copyFileSync(abs(src), abs(dest));
|
|
981
|
+
ok();
|
|
982
|
+
} catch (e) {
|
|
983
|
+
fail(e);
|
|
984
|
+
}
|
|
985
|
+
});
|
|
986
|
+
},
|
|
987
|
+
appendFile(target: unknown, data: string | Uint8Array): Promise<void> {
|
|
988
|
+
return new Promise((ok, fail) => {
|
|
989
|
+
try {
|
|
990
|
+
volume.appendFileSync(abs(target), data);
|
|
991
|
+
ok();
|
|
992
|
+
} catch (e) {
|
|
993
|
+
fail(e);
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
},
|
|
997
|
+
symlink(target: unknown, path: unknown, type?: string): Promise<void> {
|
|
998
|
+
return new Promise((ok, fail) => {
|
|
999
|
+
try {
|
|
1000
|
+
volume.symlinkSync(abs(target), abs(path), type);
|
|
1001
|
+
ok();
|
|
1002
|
+
} catch (e) {
|
|
1003
|
+
fail(e);
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
},
|
|
1007
|
+
readlink(target: unknown): Promise<string> {
|
|
1008
|
+
return new Promise((ok, fail) => {
|
|
1009
|
+
try {
|
|
1010
|
+
ok(volume.readlinkSync(abs(target)));
|
|
1011
|
+
} catch (e) {
|
|
1012
|
+
fail(e);
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
},
|
|
1016
|
+
link(existingPath: unknown, newPath: unknown): Promise<void> {
|
|
1017
|
+
return new Promise((ok, fail) => {
|
|
1018
|
+
try {
|
|
1019
|
+
volume.linkSync(abs(existingPath), abs(newPath));
|
|
1020
|
+
ok();
|
|
1021
|
+
} catch (e) {
|
|
1022
|
+
fail(e);
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
},
|
|
1026
|
+
chmod(target: unknown, mode: number): Promise<void> {
|
|
1027
|
+
return new Promise((ok, fail) => {
|
|
1028
|
+
try {
|
|
1029
|
+
volume.chmodSync(abs(target), mode);
|
|
1030
|
+
ok();
|
|
1031
|
+
} catch (e) {
|
|
1032
|
+
fail(e);
|
|
1033
|
+
}
|
|
1034
|
+
});
|
|
1035
|
+
},
|
|
1036
|
+
chown(target: unknown, uid: number, gid: number): Promise<void> {
|
|
1037
|
+
return new Promise((ok, fail) => {
|
|
1038
|
+
try {
|
|
1039
|
+
volume.chownSync(abs(target), uid, gid);
|
|
1040
|
+
ok();
|
|
1041
|
+
} catch (e) {
|
|
1042
|
+
fail(e);
|
|
1043
|
+
}
|
|
1044
|
+
});
|
|
1045
|
+
},
|
|
1046
|
+
truncate(target: unknown, len?: number): Promise<void> {
|
|
1047
|
+
return new Promise((ok, fail) => {
|
|
1048
|
+
try {
|
|
1049
|
+
volume.truncateSync(abs(target), len);
|
|
1050
|
+
ok();
|
|
1051
|
+
} catch (e) {
|
|
1052
|
+
fail(e);
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
},
|
|
1056
|
+
rm(
|
|
1057
|
+
target: unknown,
|
|
1058
|
+
opts?: { recursive?: boolean; force?: boolean },
|
|
1059
|
+
): Promise<void> {
|
|
1060
|
+
return new Promise((ok, fail) => {
|
|
1061
|
+
try {
|
|
1062
|
+
bridge.rmSync(abs(target), opts);
|
|
1063
|
+
ok();
|
|
1064
|
+
} catch (e) {
|
|
1065
|
+
fail(e);
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
},
|
|
1069
|
+
lstat(target: unknown): Promise<FileStat> {
|
|
1070
|
+
return new Promise((ok, fail) => {
|
|
1071
|
+
try {
|
|
1072
|
+
ok(volume.lstatSync(abs(target)));
|
|
1073
|
+
} catch (e) {
|
|
1074
|
+
fail(e);
|
|
1075
|
+
}
|
|
1076
|
+
});
|
|
1077
|
+
},
|
|
1078
|
+
utimes(_target: unknown, _atime: unknown, _mtime: unknown): Promise<void> {
|
|
1079
|
+
// VFS doesn't track timestamps
|
|
1080
|
+
return Promise.resolve();
|
|
1081
|
+
},
|
|
1082
|
+
lchown(_target: unknown, _uid: number, _gid: number): Promise<void> {
|
|
1083
|
+
// VFS doesn't track symlink ownership
|
|
1084
|
+
return Promise.resolve();
|
|
1085
|
+
},
|
|
1086
|
+
lutimes(_target: unknown, _atime: unknown, _mtime: unknown): Promise<void> {
|
|
1087
|
+
// VFS doesn't track timestamps
|
|
1088
|
+
return Promise.resolve();
|
|
1089
|
+
},
|
|
1090
|
+
opendir(target: unknown, _opts?: unknown): Promise<Dir> {
|
|
1091
|
+
try {
|
|
1092
|
+
const p = abs(target);
|
|
1093
|
+
const names = volume.readdirSync(p);
|
|
1094
|
+
const entries = toDirents(p, names);
|
|
1095
|
+
return Promise.resolve(new Dir(p, entries));
|
|
1096
|
+
} catch (e) {
|
|
1097
|
+
return Promise.reject(e);
|
|
1098
|
+
}
|
|
1099
|
+
},
|
|
1100
|
+
readdir(
|
|
1101
|
+
target: unknown,
|
|
1102
|
+
opts?: { withFileTypes?: boolean; encoding?: string } | string,
|
|
1103
|
+
): Promise<string[] | Dirent[]> {
|
|
1104
|
+
try {
|
|
1105
|
+
const p = abs(target);
|
|
1106
|
+
const names = volume.readdirSync(p);
|
|
1107
|
+
const o = typeof opts === "string" ? { encoding: opts } : opts;
|
|
1108
|
+
if (o?.withFileTypes) {
|
|
1109
|
+
return Promise.resolve(toDirents(p, names));
|
|
1110
|
+
}
|
|
1111
|
+
return Promise.resolve(names);
|
|
1112
|
+
} catch (e) {
|
|
1113
|
+
return Promise.reject(e);
|
|
1114
|
+
}
|
|
1115
|
+
},
|
|
1116
|
+
glob(
|
|
1117
|
+
pattern: string | string[],
|
|
1118
|
+
opts?: { cwd?: string; exclude?: string[] | ((p: string) => boolean) },
|
|
1119
|
+
): AsyncIterable<string> {
|
|
1120
|
+
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
1121
|
+
const cwd = opts?.cwd ? abs(opts.cwd) : (getCwd ? getCwd() : "/");
|
|
1122
|
+
const exclude = opts?.exclude;
|
|
1123
|
+
|
|
1124
|
+
// Expand brace patterns like {tsx,ts,jsx,js} → individual alternatives
|
|
1125
|
+
function expandBraces(pat: string): string[] {
|
|
1126
|
+
const m = pat.match(/^([^{]*)\{([^}]+)\}(.*)$/);
|
|
1127
|
+
if (!m) return [pat];
|
|
1128
|
+
const prefix = m[1], alts = m[2].split(","), suffix = m[3];
|
|
1129
|
+
const result: string[] = [];
|
|
1130
|
+
for (const alt of alts) {
|
|
1131
|
+
result.push(...expandBraces(prefix + alt + suffix));
|
|
1132
|
+
}
|
|
1133
|
+
return result;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
// Convert a glob pattern to a RegExp
|
|
1137
|
+
function globToRegex(pat: string): RegExp {
|
|
1138
|
+
let re = "";
|
|
1139
|
+
let i = 0;
|
|
1140
|
+
while (i < pat.length) {
|
|
1141
|
+
const ch = pat[i];
|
|
1142
|
+
if (ch === "*" && pat[i + 1] === "*") {
|
|
1143
|
+
// ** matches any number of path segments
|
|
1144
|
+
if (pat[i + 2] === "/") {
|
|
1145
|
+
re += "(?:.+/)?";
|
|
1146
|
+
i += 3;
|
|
1147
|
+
} else {
|
|
1148
|
+
re += ".*";
|
|
1149
|
+
i += 2;
|
|
1150
|
+
}
|
|
1151
|
+
} else if (ch === "*") {
|
|
1152
|
+
re += "[^/]*";
|
|
1153
|
+
i++;
|
|
1154
|
+
} else if (ch === "?") {
|
|
1155
|
+
re += "[^/]";
|
|
1156
|
+
i++;
|
|
1157
|
+
} else if (ch === "." || ch === "(" || ch === ")" || ch === "+" || ch === "^" || ch === "$" || ch === "|" || ch === "\\") {
|
|
1158
|
+
re += "\\" + ch;
|
|
1159
|
+
i++;
|
|
1160
|
+
} else {
|
|
1161
|
+
re += ch;
|
|
1162
|
+
i++;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
return new RegExp("^" + re + "$");
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// Recursively collect all file paths under a directory (relative to base)
|
|
1169
|
+
function walk(dir: string, base: string): string[] {
|
|
1170
|
+
const results: string[] = [];
|
|
1171
|
+
let entries: string[];
|
|
1172
|
+
try {
|
|
1173
|
+
entries = volume.readdirSync(dir);
|
|
1174
|
+
} catch {
|
|
1175
|
+
return results;
|
|
1176
|
+
}
|
|
1177
|
+
for (const name of entries) {
|
|
1178
|
+
const full = dir.endsWith("/") ? dir + name : dir + "/" + name;
|
|
1179
|
+
const rel = base ? base + "/" + name : name;
|
|
1180
|
+
let isDir = false;
|
|
1181
|
+
try {
|
|
1182
|
+
isDir = volume.statSync(full).isDirectory();
|
|
1183
|
+
} catch {
|
|
1184
|
+
// skip broken entries
|
|
1185
|
+
}
|
|
1186
|
+
if (isDir) {
|
|
1187
|
+
results.push(...walk(full, rel));
|
|
1188
|
+
} else {
|
|
1189
|
+
results.push(rel);
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
return results;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
// Build all expanded patterns → regexes
|
|
1196
|
+
const regexes: RegExp[] = [];
|
|
1197
|
+
for (const p of patterns) {
|
|
1198
|
+
for (const expanded of expandBraces(p)) {
|
|
1199
|
+
regexes.push(globToRegex(expanded));
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// Build exclude matchers
|
|
1204
|
+
let excludeFn: ((p: string) => boolean) | null = null;
|
|
1205
|
+
if (typeof exclude === "function") {
|
|
1206
|
+
excludeFn = exclude;
|
|
1207
|
+
} else if (Array.isArray(exclude) && exclude.length > 0) {
|
|
1208
|
+
const exRegexes: RegExp[] = [];
|
|
1209
|
+
for (const ep of exclude) {
|
|
1210
|
+
for (const expanded of expandBraces(ep)) {
|
|
1211
|
+
exRegexes.push(globToRegex(expanded));
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
excludeFn = (p: string) => exRegexes.some((r) => r.test(p));
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
const allFiles = walk(cwd, "");
|
|
1218
|
+
const matched = allFiles.filter((f) => {
|
|
1219
|
+
if (excludeFn && excludeFn(f)) return false;
|
|
1220
|
+
return regexes.some((r) => r.test(f));
|
|
1221
|
+
});
|
|
1222
|
+
|
|
1223
|
+
// Return an async iterable
|
|
1224
|
+
return {
|
|
1225
|
+
[Symbol.asyncIterator]() {
|
|
1226
|
+
let i = 0;
|
|
1227
|
+
return {
|
|
1228
|
+
next() {
|
|
1229
|
+
if (i < matched.length) {
|
|
1230
|
+
return Promise.resolve({ value: matched[i++], done: false });
|
|
1231
|
+
}
|
|
1232
|
+
return Promise.resolve({ value: undefined as any, done: true });
|
|
1233
|
+
},
|
|
1234
|
+
};
|
|
1235
|
+
},
|
|
1236
|
+
};
|
|
1237
|
+
},
|
|
1238
|
+
open(target: unknown, flags?: string | number, _mode?: number): Promise<FileHandle> {
|
|
1239
|
+
try {
|
|
1240
|
+
const f = flags ?? "r";
|
|
1241
|
+
const fd = bridge.openSync(abs(target), f, _mode);
|
|
1242
|
+
return Promise.resolve(new FileHandle(fd));
|
|
1243
|
+
} catch (e) {
|
|
1244
|
+
return Promise.reject(e);
|
|
1245
|
+
}
|
|
1246
|
+
},
|
|
1247
|
+
mkdtemp(prefix: string): Promise<string> {
|
|
1248
|
+
try {
|
|
1249
|
+
return Promise.resolve(bridge.mkdtempSync(prefix));
|
|
1250
|
+
} catch (e) {
|
|
1251
|
+
return Promise.reject(e);
|
|
1252
|
+
}
|
|
1253
|
+
},
|
|
1254
|
+
watch(
|
|
1255
|
+
filename: unknown,
|
|
1256
|
+
opts?: { persistent?: boolean; recursive?: boolean; signal?: AbortSignal },
|
|
1257
|
+
): AsyncIterable<{ eventType: string; filename: string | null }> {
|
|
1258
|
+
const p = abs(filename);
|
|
1259
|
+
const events: { eventType: string; filename: string | null }[] = [];
|
|
1260
|
+
let resolve: (() => void) | null = null;
|
|
1261
|
+
let closed = false;
|
|
1262
|
+
|
|
1263
|
+
const handle = volume.watch(p, { persistent: opts?.persistent, recursive: opts?.recursive }, (event, name) => {
|
|
1264
|
+
events.push({ eventType: event, filename: name });
|
|
1265
|
+
if (resolve) { resolve(); resolve = null; }
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
if (opts?.signal) {
|
|
1269
|
+
opts.signal.addEventListener("abort", () => { closed = true; handle.close(); if (resolve) { resolve(); resolve = null; } }, { once: true });
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
return {
|
|
1273
|
+
[Symbol.asyncIterator]() {
|
|
1274
|
+
return {
|
|
1275
|
+
next(): Promise<IteratorResult<{ eventType: string; filename: string | null }>> {
|
|
1276
|
+
if (events.length > 0) {
|
|
1277
|
+
return Promise.resolve({ value: events.shift()!, done: false });
|
|
1278
|
+
}
|
|
1279
|
+
if (closed) return Promise.resolve({ value: undefined as any, done: true });
|
|
1280
|
+
return new Promise<IteratorResult<{ eventType: string; filename: string | null }>>((res) => {
|
|
1281
|
+
resolve = () => {
|
|
1282
|
+
if (events.length > 0) res({ value: events.shift()!, done: false });
|
|
1283
|
+
else res({ value: undefined as any, done: true });
|
|
1284
|
+
};
|
|
1285
|
+
});
|
|
1286
|
+
},
|
|
1287
|
+
return(): Promise<IteratorResult<{ eventType: string; filename: string | null }>> {
|
|
1288
|
+
closed = true;
|
|
1289
|
+
handle.close();
|
|
1290
|
+
return Promise.resolve({ value: undefined as any, done: true });
|
|
1291
|
+
},
|
|
1292
|
+
};
|
|
1293
|
+
},
|
|
1294
|
+
};
|
|
1295
|
+
},
|
|
1296
|
+
statfs(_target: unknown): Promise<StatFs> {
|
|
1297
|
+
return Promise.resolve(new StatFs());
|
|
1298
|
+
},
|
|
1299
|
+
cp(
|
|
1300
|
+
src: unknown,
|
|
1301
|
+
dest: unknown,
|
|
1302
|
+
opts?: { recursive?: boolean; force?: boolean; errorOnExist?: boolean },
|
|
1303
|
+
): Promise<void> {
|
|
1304
|
+
try {
|
|
1305
|
+
bridge.cpSync(src, dest, opts);
|
|
1306
|
+
return Promise.resolve();
|
|
1307
|
+
} catch (e) {
|
|
1308
|
+
return Promise.reject(e);
|
|
1309
|
+
}
|
|
1310
|
+
},
|
|
1311
|
+
FileHandle: FileHandle as any,
|
|
1312
|
+
} as FsPromisesShape;
|
|
1313
|
+
const realpathSyncFn = function realpathSync(target: unknown): string {
|
|
1314
|
+
return volume.realpathSync(abs(target));
|
|
1315
|
+
};
|
|
1316
|
+
(realpathSyncFn as any).native = function native(target: unknown): string {
|
|
1317
|
+
return volume.realpathSync(abs(target));
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
const bridge: FsBridge = {
|
|
1321
|
+
readFileSync(
|
|
1322
|
+
target: unknown,
|
|
1323
|
+
encOrOpts?: string | { encoding?: string | null },
|
|
1324
|
+
): Buffer | string {
|
|
1325
|
+
const p = abs(target);
|
|
1326
|
+
let enc: string | undefined;
|
|
1327
|
+
if (typeof encOrOpts === "string") enc = encOrOpts;
|
|
1328
|
+
else if (encOrOpts?.encoding) enc = encOrOpts.encoding;
|
|
1329
|
+
|
|
1330
|
+
if (enc === "utf8" || enc === "utf-8") {
|
|
1331
|
+
return volume.readFileSync(p, "utf8");
|
|
1332
|
+
}
|
|
1333
|
+
const raw = volume.readFileSync(p);
|
|
1334
|
+
if (p.endsWith(".wasm")) precompileWasm(raw);
|
|
1335
|
+
return wrapAsBuffer(raw);
|
|
1336
|
+
},
|
|
1337
|
+
|
|
1338
|
+
writeFileSync(target: unknown, data: string | Uint8Array): void {
|
|
1339
|
+
if (typeof target === "number") {
|
|
1340
|
+
const entry = openFiles.get(target);
|
|
1341
|
+
if (!entry) {
|
|
1342
|
+
const err = new Error(
|
|
1343
|
+
"EBADF: bad file descriptor, write",
|
|
1344
|
+
) as Error & { code: string; errno: number };
|
|
1345
|
+
err.code = "EBADF";
|
|
1346
|
+
err.errno = -9;
|
|
1347
|
+
throw err;
|
|
1348
|
+
}
|
|
1349
|
+
const bytes = typeof data === "string" ? encoder.encode(data) : data;
|
|
1350
|
+
entry.data = new Uint8Array(bytes);
|
|
1351
|
+
entry.cursor = bytes.length;
|
|
1352
|
+
return;
|
|
1353
|
+
}
|
|
1354
|
+
const wp = abs(target);
|
|
1355
|
+
volume.writeFileSync(wp, data);
|
|
1356
|
+
if (wp.endsWith(".wasm") && typeof data !== "string") {
|
|
1357
|
+
precompileWasm(data);
|
|
1358
|
+
}
|
|
1359
|
+
},
|
|
1360
|
+
|
|
1361
|
+
existsSync(target: unknown): boolean {
|
|
1362
|
+
return volume.existsSync(abs(target));
|
|
1363
|
+
},
|
|
1364
|
+
|
|
1365
|
+
mkdirSync(target: unknown, opts?: { recursive?: boolean }): void {
|
|
1366
|
+
volume.mkdirSync(abs(target), opts);
|
|
1367
|
+
},
|
|
1368
|
+
|
|
1369
|
+
readdirSync(
|
|
1370
|
+
target: unknown,
|
|
1371
|
+
opts?: { withFileTypes?: boolean; encoding?: string } | string,
|
|
1372
|
+
): string[] | Dirent[] {
|
|
1373
|
+
const p = abs(target);
|
|
1374
|
+
const names = volume.readdirSync(p);
|
|
1375
|
+
const o = typeof opts === "string" ? { encoding: opts } : opts;
|
|
1376
|
+
if (o?.withFileTypes) {
|
|
1377
|
+
return toDirents(p, names);
|
|
1378
|
+
}
|
|
1379
|
+
return names;
|
|
1380
|
+
},
|
|
1381
|
+
|
|
1382
|
+
statSync(target: unknown): FileStat {
|
|
1383
|
+
return volume.statSync(abs(target));
|
|
1384
|
+
},
|
|
1385
|
+
|
|
1386
|
+
lstatSync(target: unknown): FileStat {
|
|
1387
|
+
return volume.lstatSync(abs(target));
|
|
1388
|
+
},
|
|
1389
|
+
|
|
1390
|
+
fstatSync(fd: number): FileStat {
|
|
1391
|
+
const entry = openFiles.get(fd);
|
|
1392
|
+
if (!entry) {
|
|
1393
|
+
const err = new Error("EBADF: bad file descriptor, fstat") as Error & {
|
|
1394
|
+
code: string;
|
|
1395
|
+
errno: number;
|
|
1396
|
+
};
|
|
1397
|
+
err.code = "EBADF";
|
|
1398
|
+
err.errno = -9;
|
|
1399
|
+
throw err;
|
|
1400
|
+
}
|
|
1401
|
+
return volume.statSync(entry.filePath);
|
|
1402
|
+
},
|
|
1403
|
+
|
|
1404
|
+
unlinkSync(target: unknown): void {
|
|
1405
|
+
volume.unlinkSync(abs(target));
|
|
1406
|
+
},
|
|
1407
|
+
|
|
1408
|
+
rmdirSync(target: unknown): void {
|
|
1409
|
+
volume.rmdirSync(abs(target));
|
|
1410
|
+
},
|
|
1411
|
+
|
|
1412
|
+
renameSync(src: unknown, dest: unknown): void {
|
|
1413
|
+
volume.renameSync(abs(src), abs(dest));
|
|
1414
|
+
},
|
|
1415
|
+
|
|
1416
|
+
realpathSync: realpathSyncFn as FsBridge["realpathSync"],
|
|
1417
|
+
|
|
1418
|
+
accessSync(target: unknown, _mode?: number): void {
|
|
1419
|
+
volume.accessSync(abs(target));
|
|
1420
|
+
},
|
|
1421
|
+
|
|
1422
|
+
copyFileSync(src: unknown, dest: unknown): void {
|
|
1423
|
+
const bytes = volume.readFileSync(abs(src));
|
|
1424
|
+
volume.writeFileSync(abs(dest), bytes);
|
|
1425
|
+
},
|
|
1426
|
+
|
|
1427
|
+
symlinkSync(target: unknown, path: unknown, _type?: string): void {
|
|
1428
|
+
volume.symlinkSync(abs(target), abs(path), _type);
|
|
1429
|
+
},
|
|
1430
|
+
|
|
1431
|
+
readlinkSync(target: unknown): string {
|
|
1432
|
+
return volume.readlinkSync(abs(target));
|
|
1433
|
+
},
|
|
1434
|
+
|
|
1435
|
+
linkSync(existingPath: unknown, newPath: unknown): void {
|
|
1436
|
+
volume.linkSync(abs(existingPath), abs(newPath));
|
|
1437
|
+
},
|
|
1438
|
+
|
|
1439
|
+
chmodSync(target: unknown, mode: number): void {
|
|
1440
|
+
volume.chmodSync(abs(target), mode);
|
|
1441
|
+
},
|
|
1442
|
+
|
|
1443
|
+
chownSync(target: unknown, uid: number, gid: number): void {
|
|
1444
|
+
volume.chownSync(abs(target), uid, gid);
|
|
1445
|
+
},
|
|
1446
|
+
|
|
1447
|
+
lchownSync(_target: unknown, _uid: number, _gid: number): void {
|
|
1448
|
+
// VFS doesn't track symlink ownership — no-op
|
|
1449
|
+
},
|
|
1450
|
+
|
|
1451
|
+
utimesSync(_target: unknown, _atime: unknown, _mtime: unknown): void {
|
|
1452
|
+
// VFS doesn't track timestamps — no-op
|
|
1453
|
+
},
|
|
1454
|
+
|
|
1455
|
+
lutimesSync(_target: unknown, _atime: unknown, _mtime: unknown): void {
|
|
1456
|
+
// VFS doesn't track timestamps — no-op
|
|
1457
|
+
},
|
|
1458
|
+
|
|
1459
|
+
futimesSync(_fd: number, _atime: unknown, _mtime: unknown): void {
|
|
1460
|
+
// VFS doesn't track timestamps — no-op
|
|
1461
|
+
},
|
|
1462
|
+
|
|
1463
|
+
fchownSync(_fd: number, _uid: number, _gid: number): void {
|
|
1464
|
+
// VFS doesn't track fd ownership — no-op
|
|
1465
|
+
},
|
|
1466
|
+
|
|
1467
|
+
fchmodSync(_fd: number, _mode: number): void {
|
|
1468
|
+
// VFS doesn't track fd permissions — no-op
|
|
1469
|
+
},
|
|
1470
|
+
|
|
1471
|
+
appendFileSync(target: unknown, data: string | Uint8Array): void {
|
|
1472
|
+
volume.appendFileSync(abs(target), data);
|
|
1473
|
+
},
|
|
1474
|
+
|
|
1475
|
+
truncateSync(target: unknown, len?: number): void {
|
|
1476
|
+
volume.truncateSync(abs(target), len);
|
|
1477
|
+
},
|
|
1478
|
+
openSync(target: unknown, flags: string | number, _mode?: number): number {
|
|
1479
|
+
const p = abs(target);
|
|
1480
|
+
const flagStr = typeof flags === "number" ? "r" : flags;
|
|
1481
|
+
const exists = volume.existsSync(p);
|
|
1482
|
+
const isWrite = flagStr.includes("w") || flagStr.includes("a");
|
|
1483
|
+
const isReadOnly = flagStr.includes("r") && !flagStr.includes("+");
|
|
1484
|
+
|
|
1485
|
+
if (!exists && isReadOnly) {
|
|
1486
|
+
const err = new Error(
|
|
1487
|
+
`ENOENT: no such file or directory, open '${p}'`,
|
|
1488
|
+
) as Error & { code: string; errno: number; path: string };
|
|
1489
|
+
err.code = "ENOENT";
|
|
1490
|
+
err.errno = -2;
|
|
1491
|
+
err.path = p;
|
|
1492
|
+
throw err;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
let content: Uint8Array;
|
|
1496
|
+
if (exists && !flagStr.includes("w")) {
|
|
1497
|
+
content = volume.readFileSync(p);
|
|
1498
|
+
} else {
|
|
1499
|
+
content = new Uint8Array(0);
|
|
1500
|
+
if (isWrite) {
|
|
1501
|
+
const parent = p.substring(0, p.lastIndexOf("/")) || "/";
|
|
1502
|
+
if (!volume.existsSync(parent)) {
|
|
1503
|
+
volume.mkdirSync(parent, { recursive: true });
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
const fd = fdCounter++;
|
|
1509
|
+
openFiles.set(fd, {
|
|
1510
|
+
filePath: p,
|
|
1511
|
+
cursor: flagStr.includes("a") ? content.length : 0,
|
|
1512
|
+
mode: flagStr,
|
|
1513
|
+
data: new Uint8Array(content),
|
|
1514
|
+
});
|
|
1515
|
+
return fd;
|
|
1516
|
+
},
|
|
1517
|
+
|
|
1518
|
+
closeSync(fd: number): void {
|
|
1519
|
+
const entry = openFiles.get(fd);
|
|
1520
|
+
if (!entry) return;
|
|
1521
|
+
if (
|
|
1522
|
+
entry.mode.includes("w") ||
|
|
1523
|
+
entry.mode.includes("a") ||
|
|
1524
|
+
entry.mode.includes("+")
|
|
1525
|
+
) {
|
|
1526
|
+
volume.writeFileSync(entry.filePath, entry.data);
|
|
1527
|
+
}
|
|
1528
|
+
openFiles.delete(fd);
|
|
1529
|
+
},
|
|
1530
|
+
|
|
1531
|
+
readSync(
|
|
1532
|
+
fd: number,
|
|
1533
|
+
buf: Buffer | Uint8Array,
|
|
1534
|
+
off: number,
|
|
1535
|
+
len: number,
|
|
1536
|
+
pos: number | null,
|
|
1537
|
+
): number {
|
|
1538
|
+
const entry = openFiles.get(fd);
|
|
1539
|
+
if (!entry) {
|
|
1540
|
+
const err = new Error("EBADF: bad file descriptor, read") as Error & {
|
|
1541
|
+
code: string;
|
|
1542
|
+
errno: number;
|
|
1543
|
+
};
|
|
1544
|
+
err.code = "EBADF";
|
|
1545
|
+
err.errno = -9;
|
|
1546
|
+
throw err;
|
|
1547
|
+
}
|
|
1548
|
+
const readAt = pos !== null ? pos : entry.cursor;
|
|
1549
|
+
const count = Math.min(len, entry.data.length - readAt);
|
|
1550
|
+
if (count <= 0) return 0;
|
|
1551
|
+
for (let i = 0; i < count; i++) {
|
|
1552
|
+
buf[off + i] = entry.data[readAt + i];
|
|
1553
|
+
}
|
|
1554
|
+
if (pos === null) entry.cursor += count;
|
|
1555
|
+
return count;
|
|
1556
|
+
},
|
|
1557
|
+
|
|
1558
|
+
writeSync(
|
|
1559
|
+
fd: number,
|
|
1560
|
+
buf: Buffer | Uint8Array | string,
|
|
1561
|
+
off?: number,
|
|
1562
|
+
len?: number,
|
|
1563
|
+
pos?: number | null,
|
|
1564
|
+
): number {
|
|
1565
|
+
const entry = openFiles.get(fd);
|
|
1566
|
+
if (!entry) {
|
|
1567
|
+
const err = new Error("EBADF: bad file descriptor, write") as Error & {
|
|
1568
|
+
code: string;
|
|
1569
|
+
errno: number;
|
|
1570
|
+
};
|
|
1571
|
+
err.code = "EBADF";
|
|
1572
|
+
err.errno = -9;
|
|
1573
|
+
throw err;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
let bytes: Uint8Array;
|
|
1577
|
+
if (typeof buf === "string") {
|
|
1578
|
+
bytes = encoder.encode(buf);
|
|
1579
|
+
off = 0;
|
|
1580
|
+
len = bytes.length;
|
|
1581
|
+
} else {
|
|
1582
|
+
bytes = buf;
|
|
1583
|
+
off = off ?? 0;
|
|
1584
|
+
len = len ?? bytes.length - off;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
const writeAt = pos !== null && pos !== undefined ? pos : entry.cursor;
|
|
1588
|
+
const endAt = writeAt + len;
|
|
1589
|
+
|
|
1590
|
+
if (endAt > entry.data.length) {
|
|
1591
|
+
const expanded = new Uint8Array(endAt);
|
|
1592
|
+
expanded.set(entry.data);
|
|
1593
|
+
entry.data = expanded;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
for (let i = 0; i < len; i++) {
|
|
1597
|
+
entry.data[writeAt + i] = bytes[off + i];
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
if (pos === null || pos === undefined) entry.cursor = endAt;
|
|
1601
|
+
return len;
|
|
1602
|
+
},
|
|
1603
|
+
|
|
1604
|
+
writevSync(fd: number, buffers: ArrayBufferView[], pos?: number | null): number {
|
|
1605
|
+
let totalWritten = 0;
|
|
1606
|
+
for (const buf of buffers) {
|
|
1607
|
+
const u8 = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
1608
|
+
const n = bridge.writeSync(fd, u8 as unknown as Buffer, 0, u8.length, pos != null ? pos + totalWritten : undefined);
|
|
1609
|
+
totalWritten += n;
|
|
1610
|
+
}
|
|
1611
|
+
return totalWritten;
|
|
1612
|
+
},
|
|
1613
|
+
|
|
1614
|
+
ftruncateSync(fd: number, len: number = 0): void {
|
|
1615
|
+
const entry = openFiles.get(fd);
|
|
1616
|
+
if (!entry) {
|
|
1617
|
+
const err = new Error(
|
|
1618
|
+
"EBADF: bad file descriptor, ftruncate",
|
|
1619
|
+
) as Error & { code: string; errno: number };
|
|
1620
|
+
err.code = "EBADF";
|
|
1621
|
+
err.errno = -9;
|
|
1622
|
+
throw err;
|
|
1623
|
+
}
|
|
1624
|
+
if (len < entry.data.length) {
|
|
1625
|
+
entry.data = entry.data.slice(0, len);
|
|
1626
|
+
} else if (len > entry.data.length) {
|
|
1627
|
+
const bigger = new Uint8Array(len);
|
|
1628
|
+
bigger.set(entry.data);
|
|
1629
|
+
entry.data = bigger;
|
|
1630
|
+
}
|
|
1631
|
+
},
|
|
1632
|
+
|
|
1633
|
+
fsyncSync(_fd: number): void {
|
|
1634
|
+
/* no-op */
|
|
1635
|
+
},
|
|
1636
|
+
fdatasyncSync(_fd: number): void {
|
|
1637
|
+
/* no-op */
|
|
1638
|
+
},
|
|
1639
|
+
|
|
1640
|
+
mkdtempSync(prefix: string): string {
|
|
1641
|
+
const rand = Math.random().toString(36).substring(2, 8);
|
|
1642
|
+
const dirPath = abs(`${prefix}${rand}`);
|
|
1643
|
+
volume.mkdirSync(dirPath, { recursive: true });
|
|
1644
|
+
return dirPath;
|
|
1645
|
+
},
|
|
1646
|
+
|
|
1647
|
+
rmSync(
|
|
1648
|
+
target: unknown,
|
|
1649
|
+
opts?: { recursive?: boolean; force?: boolean },
|
|
1650
|
+
): void {
|
|
1651
|
+
const p = abs(target);
|
|
1652
|
+
if (!volume.existsSync(p)) {
|
|
1653
|
+
if (opts?.force) return;
|
|
1654
|
+
throw makeSystemError("ENOENT", "rm", p);
|
|
1655
|
+
}
|
|
1656
|
+
const st = volume.statSync(p);
|
|
1657
|
+
if (st.isDirectory()) {
|
|
1658
|
+
if (opts?.recursive) {
|
|
1659
|
+
const children = volume.readdirSync(p);
|
|
1660
|
+
for (const child of children) {
|
|
1661
|
+
const childPath = p.endsWith("/") ? p + child : p + "/" + child;
|
|
1662
|
+
bridge.rmSync(childPath, opts);
|
|
1663
|
+
}
|
|
1664
|
+
volume.rmdirSync(p);
|
|
1665
|
+
} else {
|
|
1666
|
+
throw makeSystemError("EISDIR", "rm", p);
|
|
1667
|
+
}
|
|
1668
|
+
} else {
|
|
1669
|
+
volume.unlinkSync(p);
|
|
1670
|
+
}
|
|
1671
|
+
},
|
|
1672
|
+
watch(
|
|
1673
|
+
filename: unknown,
|
|
1674
|
+
optsOrCb?: { persistent?: boolean; recursive?: boolean } | WatchCallback,
|
|
1675
|
+
cb?: WatchCallback,
|
|
1676
|
+
): FileWatchHandle {
|
|
1677
|
+
const p = abs(filename);
|
|
1678
|
+
return volume.watch(
|
|
1679
|
+
p,
|
|
1680
|
+
optsOrCb as { persistent?: boolean; recursive?: boolean },
|
|
1681
|
+
cb,
|
|
1682
|
+
);
|
|
1683
|
+
},
|
|
1684
|
+
|
|
1685
|
+
watchFile(
|
|
1686
|
+
_filename: unknown,
|
|
1687
|
+
_optsOrListener?: unknown,
|
|
1688
|
+
_listener?: unknown,
|
|
1689
|
+
): { unref(): void; ref(): void } {
|
|
1690
|
+
// Stub — polling-based file watch not implemented in VFS
|
|
1691
|
+
return { unref() {}, ref() {} };
|
|
1692
|
+
},
|
|
1693
|
+
|
|
1694
|
+
unwatchFile(_filename: unknown, _listener?: unknown): void {
|
|
1695
|
+
// Stub — polling-based file unwatch not implemented in VFS
|
|
1696
|
+
},
|
|
1697
|
+
readFile(
|
|
1698
|
+
target: unknown,
|
|
1699
|
+
optsOrCb?:
|
|
1700
|
+
| string
|
|
1701
|
+
| { encoding?: string | null }
|
|
1702
|
+
| ((err: Error | null, data?: Buffer | string) => void),
|
|
1703
|
+
cb?: (err: Error | null, data?: Buffer | string) => void,
|
|
1704
|
+
): void {
|
|
1705
|
+
const p = abs(target);
|
|
1706
|
+
let actualCb: ((err: Error | null, data?: Buffer | string) => void) | undefined;
|
|
1707
|
+
let enc: string | undefined;
|
|
1708
|
+
if (typeof optsOrCb === "function") {
|
|
1709
|
+
actualCb = optsOrCb as (err: Error | null, data?: Buffer | string) => void;
|
|
1710
|
+
} else {
|
|
1711
|
+
actualCb = cb;
|
|
1712
|
+
if (typeof optsOrCb === "string") {
|
|
1713
|
+
enc = optsOrCb;
|
|
1714
|
+
} else if (optsOrCb && typeof optsOrCb === "object") {
|
|
1715
|
+
enc = (optsOrCb as { encoding?: string | null }).encoding ?? undefined;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
try {
|
|
1719
|
+
if (enc && enc !== "buffer" && (enc === "utf8" || enc === "utf-8")) {
|
|
1720
|
+
const data = volume.readFileSync(p, "utf8");
|
|
1721
|
+
if (actualCb) setTimeout(() => actualCb(null, data), 0);
|
|
1722
|
+
} else {
|
|
1723
|
+
const raw = volume.readFileSync(p);
|
|
1724
|
+
if (actualCb) setTimeout(() => actualCb(null, wrapAsBuffer(raw)), 0);
|
|
1725
|
+
}
|
|
1726
|
+
} catch (e) {
|
|
1727
|
+
if (actualCb) setTimeout(() => actualCb(e as Error), 0);
|
|
1728
|
+
}
|
|
1729
|
+
},
|
|
1730
|
+
|
|
1731
|
+
writeFile(
|
|
1732
|
+
target: unknown,
|
|
1733
|
+
data: unknown,
|
|
1734
|
+
optsOrCb?: unknown,
|
|
1735
|
+
maybeCb?: (err: Error | null) => void,
|
|
1736
|
+
): void {
|
|
1737
|
+
const cb = typeof optsOrCb === "function"
|
|
1738
|
+
? optsOrCb as (err: Error | null) => void
|
|
1739
|
+
: maybeCb;
|
|
1740
|
+
try {
|
|
1741
|
+
const wp = abs(target);
|
|
1742
|
+
const writeData = typeof data === "string" ? data : data as Uint8Array;
|
|
1743
|
+
volume.writeFileSync(wp, writeData);
|
|
1744
|
+
if (wp.endsWith(".wasm") && typeof data !== "string") {
|
|
1745
|
+
precompileWasm(data as Uint8Array);
|
|
1746
|
+
}
|
|
1747
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1748
|
+
} catch (e) {
|
|
1749
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
1750
|
+
}
|
|
1751
|
+
},
|
|
1752
|
+
|
|
1753
|
+
stat(
|
|
1754
|
+
target: unknown,
|
|
1755
|
+
optsOrCb?: unknown,
|
|
1756
|
+
maybeCb?: (err: Error | null, stats?: FileStat) => void,
|
|
1757
|
+
): void {
|
|
1758
|
+
// Support both stat(path, cb) and stat(path, opts, cb)
|
|
1759
|
+
const cb = typeof optsOrCb === "function"
|
|
1760
|
+
? optsOrCb as (err: Error | null, stats?: FileStat) => void
|
|
1761
|
+
: maybeCb as (err: Error | null, stats?: FileStat) => void;
|
|
1762
|
+
const p = abs(target);
|
|
1763
|
+
volume.stat(p, cb);
|
|
1764
|
+
},
|
|
1765
|
+
|
|
1766
|
+
lstat(
|
|
1767
|
+
target: unknown,
|
|
1768
|
+
optsOrCb?: unknown,
|
|
1769
|
+
maybeCb?: (err: Error | null, stats?: FileStat) => void,
|
|
1770
|
+
): void {
|
|
1771
|
+
// Support both lstat(path, cb) and lstat(path, opts, cb)
|
|
1772
|
+
const cb = typeof optsOrCb === "function"
|
|
1773
|
+
? optsOrCb as (err: Error | null, stats?: FileStat) => void
|
|
1774
|
+
: maybeCb as (err: Error | null, stats?: FileStat) => void;
|
|
1775
|
+
const p = abs(target);
|
|
1776
|
+
volume.lstat(p, cb);
|
|
1777
|
+
},
|
|
1778
|
+
|
|
1779
|
+
readdir(
|
|
1780
|
+
target: unknown,
|
|
1781
|
+
optsOrCb?:
|
|
1782
|
+
| { withFileTypes?: boolean }
|
|
1783
|
+
| ((err: Error | null, files?: string[] | Dirent[]) => void),
|
|
1784
|
+
cb?: (err: Error | null, files?: string[] | Dirent[]) => void,
|
|
1785
|
+
): void {
|
|
1786
|
+
const actualCb = typeof optsOrCb === "function" ? optsOrCb : cb;
|
|
1787
|
+
const opts = typeof optsOrCb === "function" ? undefined : optsOrCb;
|
|
1788
|
+
const p = abs(target);
|
|
1789
|
+
try {
|
|
1790
|
+
const names = volume.readdirSync(p);
|
|
1791
|
+
if (opts?.withFileTypes) {
|
|
1792
|
+
actualCb?.(null, toDirents(p, names));
|
|
1793
|
+
} else {
|
|
1794
|
+
actualCb?.(null, names);
|
|
1795
|
+
}
|
|
1796
|
+
} catch (e) {
|
|
1797
|
+
actualCb?.(e as Error);
|
|
1798
|
+
}
|
|
1799
|
+
},
|
|
1800
|
+
|
|
1801
|
+
mkdir(
|
|
1802
|
+
target: unknown,
|
|
1803
|
+
optsOrCb?: { recursive?: boolean } | ((err: Error | null) => void),
|
|
1804
|
+
cb?: (err: Error | null) => void,
|
|
1805
|
+
): void {
|
|
1806
|
+
const actualCb = typeof optsOrCb === "function" ? optsOrCb : cb;
|
|
1807
|
+
const opts = typeof optsOrCb === "object" ? optsOrCb : undefined;
|
|
1808
|
+
try {
|
|
1809
|
+
volume.mkdirSync(abs(target), opts);
|
|
1810
|
+
if (actualCb) setTimeout(() => actualCb(null), 0);
|
|
1811
|
+
} catch (e) {
|
|
1812
|
+
if (actualCb) setTimeout(() => actualCb(e as Error), 0);
|
|
1813
|
+
}
|
|
1814
|
+
},
|
|
1815
|
+
|
|
1816
|
+
unlink(target: unknown, cb?: (err: Error | null) => void): void {
|
|
1817
|
+
try {
|
|
1818
|
+
volume.unlinkSync(abs(target));
|
|
1819
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1820
|
+
} catch (e) {
|
|
1821
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
1822
|
+
}
|
|
1823
|
+
},
|
|
1824
|
+
|
|
1825
|
+
rmdir(target: unknown, optsOrCb?: unknown, maybeCb?: (err: Error | null) => void): void {
|
|
1826
|
+
const cb = typeof optsOrCb === "function"
|
|
1827
|
+
? optsOrCb as (err: Error | null) => void
|
|
1828
|
+
: maybeCb;
|
|
1829
|
+
try {
|
|
1830
|
+
volume.rmdirSync(abs(target));
|
|
1831
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1832
|
+
} catch (e) {
|
|
1833
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
1834
|
+
}
|
|
1835
|
+
},
|
|
1836
|
+
|
|
1837
|
+
rename(oldPath: unknown, newPath: unknown, cb?: (err: Error | null) => void): void {
|
|
1838
|
+
try {
|
|
1839
|
+
volume.renameSync(abs(oldPath), abs(newPath));
|
|
1840
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1841
|
+
} catch (e) {
|
|
1842
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
1843
|
+
}
|
|
1844
|
+
},
|
|
1845
|
+
|
|
1846
|
+
realpath(
|
|
1847
|
+
target: unknown,
|
|
1848
|
+
optsOrCb?: unknown,
|
|
1849
|
+
maybeCb?: (err: Error | null, resolved?: string) => void,
|
|
1850
|
+
): void {
|
|
1851
|
+
const cb = typeof optsOrCb === "function"
|
|
1852
|
+
? optsOrCb as (err: Error | null, resolved?: string) => void
|
|
1853
|
+
: maybeCb;
|
|
1854
|
+
volume.realpath(abs(target), cb);
|
|
1855
|
+
},
|
|
1856
|
+
|
|
1857
|
+
access(
|
|
1858
|
+
target: unknown,
|
|
1859
|
+
modeOrCb?: number | ((err: Error | null) => void),
|
|
1860
|
+
cb?: (err: Error | null) => void,
|
|
1861
|
+
): void {
|
|
1862
|
+
volume.access(abs(target), modeOrCb, cb);
|
|
1863
|
+
},
|
|
1864
|
+
|
|
1865
|
+
appendFile(
|
|
1866
|
+
target: unknown,
|
|
1867
|
+
data: string | Uint8Array,
|
|
1868
|
+
optsOrCb?: unknown,
|
|
1869
|
+
maybeCb?: (err: Error | null) => void,
|
|
1870
|
+
): void {
|
|
1871
|
+
const cb = typeof optsOrCb === "function"
|
|
1872
|
+
? optsOrCb as (err: Error | null) => void
|
|
1873
|
+
: maybeCb;
|
|
1874
|
+
try {
|
|
1875
|
+
volume.appendFileSync(abs(target), data);
|
|
1876
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1877
|
+
} catch (e) {
|
|
1878
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
1879
|
+
}
|
|
1880
|
+
},
|
|
1881
|
+
|
|
1882
|
+
symlink(
|
|
1883
|
+
target: unknown,
|
|
1884
|
+
path: unknown,
|
|
1885
|
+
typeOrCb?: string | ((err: Error | null) => void),
|
|
1886
|
+
cb?: (err: Error | null) => void,
|
|
1887
|
+
): void {
|
|
1888
|
+
const actualCb = typeof typeOrCb === "function" ? typeOrCb : cb;
|
|
1889
|
+
try {
|
|
1890
|
+
volume.symlinkSync(abs(target), abs(path));
|
|
1891
|
+
if (actualCb) setTimeout(() => actualCb(null), 0);
|
|
1892
|
+
} catch (e) {
|
|
1893
|
+
if (actualCb) setTimeout(() => actualCb(e as Error), 0);
|
|
1894
|
+
}
|
|
1895
|
+
},
|
|
1896
|
+
|
|
1897
|
+
readlink(
|
|
1898
|
+
target: unknown,
|
|
1899
|
+
optsOrCb?: unknown,
|
|
1900
|
+
maybeCb?: (err: Error | null, linkTarget?: string) => void,
|
|
1901
|
+
): void {
|
|
1902
|
+
const cb = typeof optsOrCb === "function"
|
|
1903
|
+
? optsOrCb as (err: Error | null, linkTarget?: string) => void
|
|
1904
|
+
: maybeCb;
|
|
1905
|
+
try {
|
|
1906
|
+
const result = volume.readlinkSync(abs(target));
|
|
1907
|
+
if (cb) setTimeout(() => cb(null, result), 0);
|
|
1908
|
+
} catch (e) {
|
|
1909
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
1910
|
+
}
|
|
1911
|
+
},
|
|
1912
|
+
|
|
1913
|
+
link(
|
|
1914
|
+
existingPath: unknown,
|
|
1915
|
+
newPath: unknown,
|
|
1916
|
+
cb?: (err: Error | null) => void,
|
|
1917
|
+
): void {
|
|
1918
|
+
try {
|
|
1919
|
+
volume.linkSync(abs(existingPath), abs(newPath));
|
|
1920
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1921
|
+
} catch (e) {
|
|
1922
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
1923
|
+
}
|
|
1924
|
+
},
|
|
1925
|
+
|
|
1926
|
+
chmod(
|
|
1927
|
+
target: unknown,
|
|
1928
|
+
mode: unknown,
|
|
1929
|
+
cb?: (err: Error | null) => void,
|
|
1930
|
+
): void {
|
|
1931
|
+
try {
|
|
1932
|
+
volume.chmodSync(abs(target), mode as number);
|
|
1933
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1934
|
+
} catch (e) {
|
|
1935
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
1936
|
+
}
|
|
1937
|
+
},
|
|
1938
|
+
|
|
1939
|
+
chown(
|
|
1940
|
+
target: unknown,
|
|
1941
|
+
uid: number,
|
|
1942
|
+
gid: number,
|
|
1943
|
+
cb?: (err: Error | null) => void,
|
|
1944
|
+
): void {
|
|
1945
|
+
try {
|
|
1946
|
+
volume.chownSync(abs(target), uid, gid);
|
|
1947
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1948
|
+
} catch (e) {
|
|
1949
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
1950
|
+
}
|
|
1951
|
+
},
|
|
1952
|
+
|
|
1953
|
+
lchown(
|
|
1954
|
+
target: unknown,
|
|
1955
|
+
_uid: number,
|
|
1956
|
+
_gid: number,
|
|
1957
|
+
cb: (err: Error | null) => void,
|
|
1958
|
+
): void {
|
|
1959
|
+
// VFS doesn't track symlink ownership — succeed silently
|
|
1960
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1961
|
+
},
|
|
1962
|
+
|
|
1963
|
+
utimes(
|
|
1964
|
+
target: unknown,
|
|
1965
|
+
_atime: number | string | Date,
|
|
1966
|
+
_mtime: number | string | Date,
|
|
1967
|
+
cb: (err: Error | null) => void,
|
|
1968
|
+
): void {
|
|
1969
|
+
// VFS doesn't track timestamps — succeed silently
|
|
1970
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1971
|
+
},
|
|
1972
|
+
|
|
1973
|
+
lutimes(
|
|
1974
|
+
target: unknown,
|
|
1975
|
+
_atime: number | string | Date,
|
|
1976
|
+
_mtime: number | string | Date,
|
|
1977
|
+
cb: (err: Error | null) => void,
|
|
1978
|
+
): void {
|
|
1979
|
+
// VFS doesn't track timestamps — succeed silently
|
|
1980
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
1981
|
+
},
|
|
1982
|
+
open(
|
|
1983
|
+
target: unknown,
|
|
1984
|
+
flagsOrCb: string | number | ((err: Error | null, fd?: number) => void),
|
|
1985
|
+
modeOrCb?: number | ((err: Error | null, fd?: number) => void),
|
|
1986
|
+
cb?: (err: Error | null, fd?: number) => void,
|
|
1987
|
+
): void {
|
|
1988
|
+
let flags: string | number = "r";
|
|
1989
|
+
let mode: number | undefined;
|
|
1990
|
+
let callback: (err: Error | null, fd?: number) => void;
|
|
1991
|
+
if (typeof flagsOrCb === "function") {
|
|
1992
|
+
callback = flagsOrCb;
|
|
1993
|
+
} else {
|
|
1994
|
+
flags = flagsOrCb;
|
|
1995
|
+
if (typeof modeOrCb === "function") {
|
|
1996
|
+
callback = modeOrCb;
|
|
1997
|
+
} else {
|
|
1998
|
+
mode = modeOrCb;
|
|
1999
|
+
callback = cb!;
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
try {
|
|
2003
|
+
const fd = bridge.openSync(abs(target), flags, mode);
|
|
2004
|
+
if (callback) setTimeout(() => callback(null, fd), 0);
|
|
2005
|
+
} catch (e) {
|
|
2006
|
+
if (callback) setTimeout(() => callback(e as Error), 0);
|
|
2007
|
+
}
|
|
2008
|
+
},
|
|
2009
|
+
|
|
2010
|
+
close(fd: number, cb?: (err: Error | null) => void): void {
|
|
2011
|
+
try {
|
|
2012
|
+
bridge.closeSync(fd);
|
|
2013
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
2014
|
+
} catch (e) {
|
|
2015
|
+
if (cb) setTimeout(() => cb(e as Error), 0);
|
|
2016
|
+
}
|
|
2017
|
+
},
|
|
2018
|
+
|
|
2019
|
+
read(
|
|
2020
|
+
fd: number,
|
|
2021
|
+
bufOrOpts:
|
|
2022
|
+
| Buffer
|
|
2023
|
+
| Uint8Array
|
|
2024
|
+
| {
|
|
2025
|
+
buffer: Buffer | Uint8Array;
|
|
2026
|
+
offset?: number;
|
|
2027
|
+
length?: number;
|
|
2028
|
+
position?: number | null;
|
|
2029
|
+
},
|
|
2030
|
+
offsetOrCb?:
|
|
2031
|
+
| number
|
|
2032
|
+
| ((
|
|
2033
|
+
err: Error | null,
|
|
2034
|
+
bytesRead?: number,
|
|
2035
|
+
buffer?: Buffer | Uint8Array,
|
|
2036
|
+
) => void),
|
|
2037
|
+
length?: number,
|
|
2038
|
+
position?: number | null,
|
|
2039
|
+
cb?: (
|
|
2040
|
+
err: Error | null,
|
|
2041
|
+
bytesRead?: number,
|
|
2042
|
+
buffer?: Buffer | Uint8Array,
|
|
2043
|
+
) => void,
|
|
2044
|
+
): void {
|
|
2045
|
+
let buf: Buffer | Uint8Array;
|
|
2046
|
+
let off: number;
|
|
2047
|
+
let len: number;
|
|
2048
|
+
let pos: number | null;
|
|
2049
|
+
let callback: (
|
|
2050
|
+
err: Error | null,
|
|
2051
|
+
bytesRead?: number,
|
|
2052
|
+
buffer?: Buffer | Uint8Array,
|
|
2053
|
+
) => void;
|
|
2054
|
+
|
|
2055
|
+
if (typeof offsetOrCb === "function") {
|
|
2056
|
+
// read(fd, opts, cb) form
|
|
2057
|
+
const opts = bufOrOpts as {
|
|
2058
|
+
buffer: Buffer | Uint8Array;
|
|
2059
|
+
offset?: number;
|
|
2060
|
+
length?: number;
|
|
2061
|
+
position?: number | null;
|
|
2062
|
+
};
|
|
2063
|
+
buf = opts.buffer;
|
|
2064
|
+
off = opts.offset ?? 0;
|
|
2065
|
+
len = opts.length ?? buf.length;
|
|
2066
|
+
pos = opts.position ?? null;
|
|
2067
|
+
callback = offsetOrCb;
|
|
2068
|
+
} else {
|
|
2069
|
+
buf = bufOrOpts as Buffer | Uint8Array;
|
|
2070
|
+
off = (offsetOrCb as number) ?? 0;
|
|
2071
|
+
len = length ?? buf.length;
|
|
2072
|
+
pos = position ?? null;
|
|
2073
|
+
callback = cb!;
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
try {
|
|
2077
|
+
const n = bridge.readSync(fd, buf, off, len, pos);
|
|
2078
|
+
if (callback) setTimeout(() => callback(null, n, buf), 0);
|
|
2079
|
+
} catch (e) {
|
|
2080
|
+
if (callback) setTimeout(() => callback(e as Error, 0, buf), 0);
|
|
2081
|
+
}
|
|
2082
|
+
},
|
|
2083
|
+
|
|
2084
|
+
write(
|
|
2085
|
+
fd: number,
|
|
2086
|
+
buf: Buffer | Uint8Array | string,
|
|
2087
|
+
offsetOrCb?:
|
|
2088
|
+
| number
|
|
2089
|
+
| ((
|
|
2090
|
+
err: Error | null,
|
|
2091
|
+
written?: number,
|
|
2092
|
+
buffer?: Buffer | Uint8Array | string,
|
|
2093
|
+
) => void),
|
|
2094
|
+
lengthOrEnc?: number | string,
|
|
2095
|
+
positionOrCb?:
|
|
2096
|
+
| number
|
|
2097
|
+
| null
|
|
2098
|
+
| ((
|
|
2099
|
+
err: Error | null,
|
|
2100
|
+
written?: number,
|
|
2101
|
+
buffer?: Buffer | Uint8Array | string,
|
|
2102
|
+
) => void),
|
|
2103
|
+
cb?: (
|
|
2104
|
+
err: Error | null,
|
|
2105
|
+
written?: number,
|
|
2106
|
+
buffer?: Buffer | Uint8Array | string,
|
|
2107
|
+
) => void,
|
|
2108
|
+
): void {
|
|
2109
|
+
let callback: (
|
|
2110
|
+
err: Error | null,
|
|
2111
|
+
written?: number,
|
|
2112
|
+
buffer?: Buffer | Uint8Array | string,
|
|
2113
|
+
) => void;
|
|
2114
|
+
if (typeof offsetOrCb === "function") {
|
|
2115
|
+
callback = offsetOrCb;
|
|
2116
|
+
try {
|
|
2117
|
+
const n = bridge.writeSync(fd, buf);
|
|
2118
|
+
setTimeout(() => callback(null, n, buf), 0);
|
|
2119
|
+
} catch (e) {
|
|
2120
|
+
setTimeout(() => callback(e as Error, 0, buf), 0);
|
|
2121
|
+
}
|
|
2122
|
+
return;
|
|
2123
|
+
}
|
|
2124
|
+
if (typeof positionOrCb === "function") {
|
|
2125
|
+
callback = positionOrCb;
|
|
2126
|
+
} else {
|
|
2127
|
+
callback = cb!;
|
|
2128
|
+
}
|
|
2129
|
+
try {
|
|
2130
|
+
const off = typeof offsetOrCb === "number" ? offsetOrCb : undefined;
|
|
2131
|
+
const len = typeof lengthOrEnc === "number" ? lengthOrEnc : undefined;
|
|
2132
|
+
const pos = typeof positionOrCb === "number" ? positionOrCb : undefined;
|
|
2133
|
+
const n = bridge.writeSync(fd, buf, off, len, pos);
|
|
2134
|
+
if (callback) setTimeout(() => callback(null, n, buf), 0);
|
|
2135
|
+
} catch (e) {
|
|
2136
|
+
if (callback) setTimeout(() => callback(e as Error, 0, buf), 0);
|
|
2137
|
+
}
|
|
2138
|
+
},
|
|
2139
|
+
|
|
2140
|
+
writev(
|
|
2141
|
+
fd: number,
|
|
2142
|
+
buffers: ArrayBufferView[],
|
|
2143
|
+
positionOrCb?:
|
|
2144
|
+
| number
|
|
2145
|
+
| null
|
|
2146
|
+
| ((err: Error | null, bytesWritten?: number, buffers?: ArrayBufferView[]) => void),
|
|
2147
|
+
cb?: (err: Error | null, bytesWritten?: number, buffers?: ArrayBufferView[]) => void,
|
|
2148
|
+
): void {
|
|
2149
|
+
const callback = typeof positionOrCb === "function" ? positionOrCb : cb;
|
|
2150
|
+
const pos = typeof positionOrCb === "number" ? positionOrCb : null;
|
|
2151
|
+
try {
|
|
2152
|
+
let totalWritten = 0;
|
|
2153
|
+
for (const buf of buffers) {
|
|
2154
|
+
const u8 = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
2155
|
+
const n = bridge.writeSync(fd, u8 as unknown as Buffer, 0, u8.length, pos !== null ? pos + totalWritten : undefined);
|
|
2156
|
+
totalWritten += n;
|
|
2157
|
+
}
|
|
2158
|
+
if (callback) setTimeout(() => callback(null, totalWritten, buffers), 0);
|
|
2159
|
+
} catch (e) {
|
|
2160
|
+
if (callback) setTimeout(() => callback(e as Error, 0, buffers), 0);
|
|
2161
|
+
}
|
|
2162
|
+
},
|
|
2163
|
+
|
|
2164
|
+
fstat(fd: number, optsOrCb?: unknown, maybeCb?: (err: Error | null, stats?: FileStat) => void): void {
|
|
2165
|
+
const cb = typeof optsOrCb === "function"
|
|
2166
|
+
? optsOrCb as (err: Error | null, stats?: FileStat) => void
|
|
2167
|
+
: maybeCb;
|
|
2168
|
+
const entry = openFiles.get(fd);
|
|
2169
|
+
if (!entry) {
|
|
2170
|
+
const err = new Error(`EBADF: bad file descriptor, fstat`) as Error & {
|
|
2171
|
+
code: string;
|
|
2172
|
+
};
|
|
2173
|
+
err.code = "EBADF";
|
|
2174
|
+
if (cb) setTimeout(() => cb(err), 0);
|
|
2175
|
+
return;
|
|
2176
|
+
}
|
|
2177
|
+
volume.stat(entry.filePath, cb);
|
|
2178
|
+
},
|
|
2179
|
+
|
|
2180
|
+
futimes(
|
|
2181
|
+
fd: number,
|
|
2182
|
+
_atime: number | string | Date,
|
|
2183
|
+
_mtime: number | string | Date,
|
|
2184
|
+
cb: (err: Error | null) => void,
|
|
2185
|
+
): void {
|
|
2186
|
+
// VFS doesn't track timestamps — succeed silently
|
|
2187
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
2188
|
+
},
|
|
2189
|
+
|
|
2190
|
+
fchown(
|
|
2191
|
+
fd: number,
|
|
2192
|
+
_uid: number,
|
|
2193
|
+
_gid: number,
|
|
2194
|
+
cb: (err: Error | null) => void,
|
|
2195
|
+
): void {
|
|
2196
|
+
// VFS doesn't track ownership — succeed silently
|
|
2197
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
2198
|
+
},
|
|
2199
|
+
|
|
2200
|
+
fchmod(
|
|
2201
|
+
fd: number,
|
|
2202
|
+
_mode: number,
|
|
2203
|
+
cb: (err: Error | null) => void,
|
|
2204
|
+
): void {
|
|
2205
|
+
// VFS doesn't track permissions — succeed silently
|
|
2206
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
2207
|
+
},
|
|
2208
|
+
// function constructor, not class -- graceful-fs calls fs$ReadStream.apply(this, args)
|
|
2209
|
+
ReadStream: (() => {
|
|
2210
|
+
function FsReadStream(this: FsReadStreamInstance, pathArg: unknown, opts?: Record<string, unknown>) {
|
|
2211
|
+
if (!(this instanceof FsReadStream)) return new (FsReadStream as unknown as new (p: unknown, o?: Record<string, unknown>) => FsReadStreamInstance)(pathArg, opts);
|
|
2212
|
+
const self: FsReadStreamInstance = this;
|
|
2213
|
+
self._queue = [];
|
|
2214
|
+
self._active = false;
|
|
2215
|
+
self._terminated = false;
|
|
2216
|
+
self._endFired = false;
|
|
2217
|
+
self._endEmitted = false;
|
|
2218
|
+
self._objectMode = false;
|
|
2219
|
+
self._reading = false;
|
|
2220
|
+
self._highWaterMark = 16384;
|
|
2221
|
+
self._autoDestroy = true;
|
|
2222
|
+
self._encoding = null;
|
|
2223
|
+
self._readableByteLength = 0;
|
|
2224
|
+
self._draining = false;
|
|
2225
|
+
self.readable = true;
|
|
2226
|
+
self.readableEnded = false;
|
|
2227
|
+
self.readableFlowing = null;
|
|
2228
|
+
self.destroyed = false;
|
|
2229
|
+
self.closed = false;
|
|
2230
|
+
self.errored = null;
|
|
2231
|
+
self.readableObjectMode = false;
|
|
2232
|
+
self.readableHighWaterMark = 16384;
|
|
2233
|
+
self.readableDidRead = false;
|
|
2234
|
+
self.readableAborted = false;
|
|
2235
|
+
self._readableState = {
|
|
2236
|
+
get objectMode() { return self._objectMode; },
|
|
2237
|
+
get highWaterMark() { return self._highWaterMark; },
|
|
2238
|
+
get ended() { return self._terminated; },
|
|
2239
|
+
get endEmitted() { return self._endEmitted; },
|
|
2240
|
+
set endEmitted(v: boolean) { self._endEmitted = v; },
|
|
2241
|
+
get flowing() { return self.readableFlowing; },
|
|
2242
|
+
set flowing(v: boolean | null) { self.readableFlowing = v; },
|
|
2243
|
+
get reading() { return self._reading; },
|
|
2244
|
+
get length() { return self._queue ? self._queue.length : 0; },
|
|
2245
|
+
get destroyed() { return self.destroyed; },
|
|
2246
|
+
get errored() { return self.errored; },
|
|
2247
|
+
get closed() { return self.closed; },
|
|
2248
|
+
pipes: [],
|
|
2249
|
+
awaitDrainWriters: null,
|
|
2250
|
+
multiAwaitDrain: false,
|
|
2251
|
+
readableListening: false,
|
|
2252
|
+
resumeScheduled: false,
|
|
2253
|
+
paused: true,
|
|
2254
|
+
emitClose: true,
|
|
2255
|
+
get autoDestroy() { return self._autoDestroy; },
|
|
2256
|
+
defaultEncoding: "utf8",
|
|
2257
|
+
needReadable: false,
|
|
2258
|
+
emittedReadable: false,
|
|
2259
|
+
readingMore: false,
|
|
2260
|
+
dataEmitted: false,
|
|
2261
|
+
};
|
|
2262
|
+
self.path = abs(pathArg);
|
|
2263
|
+
self.fd = (opts?.fd as number | null) ?? null;
|
|
2264
|
+
self.flags = (opts?.flags as string) ?? "r";
|
|
2265
|
+
self.mode = (opts?.mode as number) ?? 0o666;
|
|
2266
|
+
self.autoClose = opts?.autoClose !== false;
|
|
2267
|
+
queueMicrotask(() => self.open());
|
|
2268
|
+
}
|
|
2269
|
+
FsReadStream.prototype = Object.create(Readable.prototype);
|
|
2270
|
+
FsReadStream.prototype.constructor = FsReadStream;
|
|
2271
|
+
|
|
2272
|
+
FsReadStream.prototype.open = function () {
|
|
2273
|
+
try {
|
|
2274
|
+
this.fd = bridge.openSync(this.path, this.flags, this.mode);
|
|
2275
|
+
this.emit("open", this.fd);
|
|
2276
|
+
this.emit("ready");
|
|
2277
|
+
this._read();
|
|
2278
|
+
} catch (err) {
|
|
2279
|
+
this.destroy(err);
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
2282
|
+
|
|
2283
|
+
FsReadStream.prototype._read = function () {
|
|
2284
|
+
if (this.fd === null) return;
|
|
2285
|
+
try {
|
|
2286
|
+
const data = volume.readFileSync(this.path);
|
|
2287
|
+
this.push(Buffer.from(data));
|
|
2288
|
+
this.push(null);
|
|
2289
|
+
} catch (err) {
|
|
2290
|
+
this.destroy(err);
|
|
2291
|
+
}
|
|
2292
|
+
};
|
|
2293
|
+
|
|
2294
|
+
FsReadStream.prototype.close = function (cb?: (err?: Error | null) => void) {
|
|
2295
|
+
if (this.fd !== null) {
|
|
2296
|
+
try {
|
|
2297
|
+
bridge.closeSync(this.fd);
|
|
2298
|
+
} catch {}
|
|
2299
|
+
this.fd = null;
|
|
2300
|
+
}
|
|
2301
|
+
this.destroy();
|
|
2302
|
+
if (cb) cb(null);
|
|
2303
|
+
};
|
|
2304
|
+
|
|
2305
|
+
return FsReadStream as any;
|
|
2306
|
+
})(),
|
|
2307
|
+
|
|
2308
|
+
// function constructor, not class -- graceful-fs calls fs$WriteStream.apply(this, args)
|
|
2309
|
+
WriteStream: (() => {
|
|
2310
|
+
function FsWriteStream(this: FsWriteStreamInstance, pathArg: unknown, opts?: Record<string, unknown>) {
|
|
2311
|
+
if (!(this instanceof FsWriteStream)) return new (FsWriteStream as unknown as new (p: unknown, o?: Record<string, unknown>) => FsWriteStreamInstance)(pathArg, opts);
|
|
2312
|
+
const self: FsWriteStreamInstance = this;
|
|
2313
|
+
self._parts = [];
|
|
2314
|
+
self._closed = false;
|
|
2315
|
+
self._objectMode = false;
|
|
2316
|
+
self._highWaterMark = 16384;
|
|
2317
|
+
self._autoDestroy = true;
|
|
2318
|
+
self._corked = 0;
|
|
2319
|
+
self._corkedWrites = [];
|
|
2320
|
+
self._writableByteLength = 0;
|
|
2321
|
+
self.writable = true;
|
|
2322
|
+
self.writableEnded = false;
|
|
2323
|
+
self.writableFinished = false;
|
|
2324
|
+
self.writableNeedDrain = false;
|
|
2325
|
+
self.destroyed = false;
|
|
2326
|
+
self.closed = false;
|
|
2327
|
+
self.errored = null;
|
|
2328
|
+
self.writableObjectMode = false;
|
|
2329
|
+
self.writableHighWaterMark = 16384;
|
|
2330
|
+
self.writableCorked = 0;
|
|
2331
|
+
self._writableState = {
|
|
2332
|
+
get objectMode() { return self._objectMode; },
|
|
2333
|
+
get highWaterMark() { return self._highWaterMark; },
|
|
2334
|
+
get finished() { return self.writableFinished; },
|
|
2335
|
+
set finished(v: boolean) { self.writableFinished = v; },
|
|
2336
|
+
get ended() { return self.writableEnded; },
|
|
2337
|
+
set ended(v: boolean) { self.writableEnded = v; },
|
|
2338
|
+
get destroyed() { return self.destroyed; },
|
|
2339
|
+
get errored() { return self.errored; },
|
|
2340
|
+
get closed() { return self.closed; },
|
|
2341
|
+
get corked() { return self._corked; },
|
|
2342
|
+
get length() { return self._writableByteLength; },
|
|
2343
|
+
get needDrain() { return self.writableNeedDrain; },
|
|
2344
|
+
writing: false,
|
|
2345
|
+
errorEmitted: false,
|
|
2346
|
+
emitClose: true,
|
|
2347
|
+
get autoDestroy() { return self._autoDestroy; },
|
|
2348
|
+
defaultEncoding: "utf8",
|
|
2349
|
+
finalCalled: false,
|
|
2350
|
+
ending: false,
|
|
2351
|
+
bufferedIndex: 0,
|
|
2352
|
+
};
|
|
2353
|
+
self.path = abs(pathArg);
|
|
2354
|
+
self.fd = (opts?.fd as number | null) ?? null;
|
|
2355
|
+
self.flags = (opts?.flags as string) ?? "w";
|
|
2356
|
+
self.mode = (opts?.mode as number) ?? 0o666;
|
|
2357
|
+
self.autoClose = opts?.autoClose !== false;
|
|
2358
|
+
self.bytesWritten = 0;
|
|
2359
|
+
self._chunks = [] as Uint8Array[];
|
|
2360
|
+
self._enc = new TextEncoder();
|
|
2361
|
+
queueMicrotask(() => self.open());
|
|
2362
|
+
}
|
|
2363
|
+
FsWriteStream.prototype = Object.create(Writable.prototype);
|
|
2364
|
+
FsWriteStream.prototype.constructor = FsWriteStream;
|
|
2365
|
+
|
|
2366
|
+
FsWriteStream.prototype.open = function () {
|
|
2367
|
+
try {
|
|
2368
|
+
this.fd = bridge.openSync(this.path, this.flags, this.mode);
|
|
2369
|
+
this.emit("open", this.fd);
|
|
2370
|
+
this.emit("ready");
|
|
2371
|
+
} catch (err) {
|
|
2372
|
+
this.destroy(err);
|
|
2373
|
+
}
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2376
|
+
FsWriteStream.prototype._write = function (
|
|
2377
|
+
chunk: Uint8Array | string,
|
|
2378
|
+
_encoding: string,
|
|
2379
|
+
callback: (err?: Error | null) => void,
|
|
2380
|
+
) {
|
|
2381
|
+
const bytes =
|
|
2382
|
+
typeof chunk === "string" ? this._enc.encode(chunk) : chunk;
|
|
2383
|
+
this._chunks.push(bytes);
|
|
2384
|
+
this.bytesWritten += bytes.length;
|
|
2385
|
+
callback(null);
|
|
2386
|
+
};
|
|
2387
|
+
|
|
2388
|
+
FsWriteStream.prototype.close = function (cb?: (err?: Error | null) => void) {
|
|
2389
|
+
this._flushChunks();
|
|
2390
|
+
if (this.fd !== null) {
|
|
2391
|
+
try {
|
|
2392
|
+
bridge.closeSync(this.fd);
|
|
2393
|
+
} catch {}
|
|
2394
|
+
this.fd = null;
|
|
2395
|
+
}
|
|
2396
|
+
this.emit("finish");
|
|
2397
|
+
this.emit("close");
|
|
2398
|
+
if (cb) cb(null);
|
|
2399
|
+
};
|
|
2400
|
+
|
|
2401
|
+
FsWriteStream.prototype._flushChunks = function () {
|
|
2402
|
+
if (!this._chunks || this._chunks.length === 0) return;
|
|
2403
|
+
const totalLen = this._chunks.reduce((sum: number, c: Uint8Array) => sum + c.length, 0);
|
|
2404
|
+
const merged = new Uint8Array(totalLen);
|
|
2405
|
+
let pos = 0;
|
|
2406
|
+
for (const c of this._chunks) {
|
|
2407
|
+
merged.set(c, pos);
|
|
2408
|
+
pos += c.length;
|
|
2409
|
+
}
|
|
2410
|
+
const isAppend = this.flags === "a";
|
|
2411
|
+
if (isAppend) {
|
|
2412
|
+
volume.appendFileSync(this.path, merged);
|
|
2413
|
+
} else {
|
|
2414
|
+
volume.writeFileSync(this.path, merged);
|
|
2415
|
+
}
|
|
2416
|
+
this._chunks = [];
|
|
2417
|
+
};
|
|
2418
|
+
|
|
2419
|
+
FsWriteStream.prototype.end = function (chunkOrCb?: any, encOrCb?: any, cb?: any) {
|
|
2420
|
+
if (typeof chunkOrCb === "function") {
|
|
2421
|
+
cb = chunkOrCb;
|
|
2422
|
+
chunkOrCb = undefined;
|
|
2423
|
+
} else if (typeof encOrCb === "function") {
|
|
2424
|
+
cb = encOrCb;
|
|
2425
|
+
encOrCb = undefined;
|
|
2426
|
+
}
|
|
2427
|
+
if (chunkOrCb !== undefined) {
|
|
2428
|
+
const bytes =
|
|
2429
|
+
typeof chunkOrCb === "string"
|
|
2430
|
+
? this._enc.encode(chunkOrCb)
|
|
2431
|
+
: chunkOrCb;
|
|
2432
|
+
this._chunks.push(bytes);
|
|
2433
|
+
this.bytesWritten += bytes.length;
|
|
2434
|
+
}
|
|
2435
|
+
this._flushChunks();
|
|
2436
|
+
if (this.fd !== null && this.autoClose) {
|
|
2437
|
+
try {
|
|
2438
|
+
bridge.closeSync(this.fd);
|
|
2439
|
+
} catch {}
|
|
2440
|
+
this.fd = null;
|
|
2441
|
+
}
|
|
2442
|
+
this.emit("finish");
|
|
2443
|
+
this.emit("close");
|
|
2444
|
+
if (cb) cb();
|
|
2445
|
+
return this;
|
|
2446
|
+
};
|
|
2447
|
+
|
|
2448
|
+
return FsWriteStream as any;
|
|
2449
|
+
})(),
|
|
2450
|
+
|
|
2451
|
+
createReadStream(
|
|
2452
|
+
target: unknown,
|
|
2453
|
+
opts?: { encoding?: string; start?: number; end?: number },
|
|
2454
|
+
): Readable {
|
|
2455
|
+
const p = abs(target);
|
|
2456
|
+
const stream: any = new Readable();
|
|
2457
|
+
stream.path = p;
|
|
2458
|
+
stream.fd = null;
|
|
2459
|
+
stream.close = function (cb?: (err?: Error | null) => void) {
|
|
2460
|
+
stream.destroy();
|
|
2461
|
+
if (cb) cb(null);
|
|
2462
|
+
};
|
|
2463
|
+
stream.open = function () {
|
|
2464
|
+
stream.fd = 42;
|
|
2465
|
+
stream.emit("open", stream.fd);
|
|
2466
|
+
};
|
|
2467
|
+
setTimeout(() => {
|
|
2468
|
+
try {
|
|
2469
|
+
stream.open();
|
|
2470
|
+
const data = volume.readFileSync(p);
|
|
2471
|
+
const start = opts?.start ?? 0;
|
|
2472
|
+
const end = opts?.end !== undefined ? opts.end + 1 : data.length;
|
|
2473
|
+
const chunk = data.slice(start, end);
|
|
2474
|
+
stream.push(Buffer.from(chunk));
|
|
2475
|
+
stream.push(null);
|
|
2476
|
+
} catch (err) {
|
|
2477
|
+
stream.destroy(err as Error);
|
|
2478
|
+
}
|
|
2479
|
+
}, 0);
|
|
2480
|
+
return stream;
|
|
2481
|
+
},
|
|
2482
|
+
|
|
2483
|
+
createWriteStream(
|
|
2484
|
+
target: unknown,
|
|
2485
|
+
opts?: { encoding?: string; flags?: string },
|
|
2486
|
+
): Writable {
|
|
2487
|
+
const p = abs(target);
|
|
2488
|
+
const isAppend = opts?.flags === "a";
|
|
2489
|
+
const chunks: Uint8Array[] = [];
|
|
2490
|
+
const enc = new TextEncoder();
|
|
2491
|
+
let bytesWritten = 0;
|
|
2492
|
+
let closed = false;
|
|
2493
|
+
|
|
2494
|
+
const stream: any = new Writable();
|
|
2495
|
+
stream.path = p;
|
|
2496
|
+
stream.fd = null;
|
|
2497
|
+
stream.open = function () {
|
|
2498
|
+
stream.fd = 43;
|
|
2499
|
+
stream.emit("open", stream.fd);
|
|
2500
|
+
};
|
|
2501
|
+
Object.defineProperty(stream, "bytesWritten", {
|
|
2502
|
+
get: () => bytesWritten,
|
|
2503
|
+
enumerable: true,
|
|
2504
|
+
});
|
|
2505
|
+
queueMicrotask(() => stream.open());
|
|
2506
|
+
|
|
2507
|
+
const flushAndClose = function (cb?: (err?: Error | null) => void) {
|
|
2508
|
+
if (closed) {
|
|
2509
|
+
if (cb) cb(null);
|
|
2510
|
+
return;
|
|
2511
|
+
}
|
|
2512
|
+
closed = true;
|
|
2513
|
+
const totalLen = chunks.reduce((sum, c) => sum + c.length, 0);
|
|
2514
|
+
const merged = new Uint8Array(totalLen);
|
|
2515
|
+
let pos = 0;
|
|
2516
|
+
for (const c of chunks) {
|
|
2517
|
+
merged.set(c, pos);
|
|
2518
|
+
pos += c.length;
|
|
2519
|
+
}
|
|
2520
|
+
|
|
2521
|
+
try {
|
|
2522
|
+
if (isAppend) {
|
|
2523
|
+
volume.appendFileSync(p, merged);
|
|
2524
|
+
} else {
|
|
2525
|
+
volume.writeFileSync(p, merged);
|
|
2526
|
+
}
|
|
2527
|
+
} catch (e) {
|
|
2528
|
+
if (cb) cb(e as Error);
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
stream.emit("finish");
|
|
2533
|
+
stream.emit("close");
|
|
2534
|
+
if (cb) cb(null);
|
|
2535
|
+
};
|
|
2536
|
+
|
|
2537
|
+
stream.write = function (
|
|
2538
|
+
chunk: Uint8Array | string,
|
|
2539
|
+
encOrCb?: string | ((err?: Error | null) => void),
|
|
2540
|
+
cb?: (err?: Error | null) => void,
|
|
2541
|
+
): boolean {
|
|
2542
|
+
const bytes = typeof chunk === "string" ? enc.encode(chunk) : chunk;
|
|
2543
|
+
chunks.push(bytes);
|
|
2544
|
+
bytesWritten += bytes.length;
|
|
2545
|
+
const callback = typeof encOrCb === "function" ? encOrCb : cb;
|
|
2546
|
+
if (callback) queueMicrotask(() => callback(null));
|
|
2547
|
+
return true;
|
|
2548
|
+
};
|
|
2549
|
+
|
|
2550
|
+
stream.end = function (
|
|
2551
|
+
chunkOrCb?: Uint8Array | string | (() => void),
|
|
2552
|
+
encOrCb?: string | (() => void),
|
|
2553
|
+
cb?: () => void,
|
|
2554
|
+
): Writable {
|
|
2555
|
+
if (typeof chunkOrCb === "function") {
|
|
2556
|
+
cb = chunkOrCb;
|
|
2557
|
+
} else if (chunkOrCb !== undefined) {
|
|
2558
|
+
const bytes =
|
|
2559
|
+
typeof chunkOrCb === "string" ? enc.encode(chunkOrCb) : chunkOrCb;
|
|
2560
|
+
chunks.push(bytes);
|
|
2561
|
+
bytesWritten += bytes.length;
|
|
2562
|
+
}
|
|
2563
|
+
if (typeof encOrCb === "function") cb = encOrCb;
|
|
2564
|
+
|
|
2565
|
+
flushAndClose(cb as any);
|
|
2566
|
+
return stream;
|
|
2567
|
+
};
|
|
2568
|
+
|
|
2569
|
+
stream.close = function (cb?: (err?: Error | null) => void) {
|
|
2570
|
+
flushAndClose(cb);
|
|
2571
|
+
};
|
|
2572
|
+
|
|
2573
|
+
return stream;
|
|
2574
|
+
},
|
|
2575
|
+
opendirSync(target: unknown, _opts?: unknown): Dir {
|
|
2576
|
+
const p = abs(target);
|
|
2577
|
+
const names = volume.readdirSync(p);
|
|
2578
|
+
const entries = toDirents(p, names);
|
|
2579
|
+
return new Dir(p, entries);
|
|
2580
|
+
},
|
|
2581
|
+
|
|
2582
|
+
opendir(
|
|
2583
|
+
target: unknown,
|
|
2584
|
+
optsOrCb?: unknown | ((err: Error | null, dir?: Dir) => void),
|
|
2585
|
+
cb?: (err: Error | null, dir?: Dir) => void,
|
|
2586
|
+
): void | Promise<Dir> {
|
|
2587
|
+
const callback = typeof optsOrCb === "function" ? optsOrCb as (err: Error | null, dir?: Dir) => void : cb;
|
|
2588
|
+
try {
|
|
2589
|
+
const dir = bridge.opendirSync(target);
|
|
2590
|
+
if (callback) {
|
|
2591
|
+
queueMicrotask(() => callback(null, dir));
|
|
2592
|
+
return;
|
|
2593
|
+
}
|
|
2594
|
+
return Promise.resolve(dir);
|
|
2595
|
+
} catch (e) {
|
|
2596
|
+
if (callback) {
|
|
2597
|
+
queueMicrotask(() => callback(e as Error));
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2600
|
+
return Promise.reject(e);
|
|
2601
|
+
}
|
|
2602
|
+
},
|
|
2603
|
+
exists(target: unknown, cb: (exists: boolean) => void): void {
|
|
2604
|
+
const result = volume.existsSync(abs(target));
|
|
2605
|
+
setTimeout(() => cb(result), 0);
|
|
2606
|
+
},
|
|
2607
|
+
lchmodSync(_target: unknown, _mode: number): void {
|
|
2608
|
+
// VFS doesn't track symlink permissions — no-op
|
|
2609
|
+
},
|
|
2610
|
+
|
|
2611
|
+
lchmod(
|
|
2612
|
+
_target: unknown,
|
|
2613
|
+
_mode: number,
|
|
2614
|
+
cb: (err: Error | null) => void,
|
|
2615
|
+
): void {
|
|
2616
|
+
// VFS doesn't track symlink permissions — succeed silently
|
|
2617
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
2618
|
+
},
|
|
2619
|
+
fdatasync(fd: number, cb: (err: Error | null) => void): void {
|
|
2620
|
+
// VFS is always sync — no-op
|
|
2621
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
2622
|
+
},
|
|
2623
|
+
|
|
2624
|
+
fsync(fd: number, cb: (err: Error | null) => void): void {
|
|
2625
|
+
// VFS is always sync — no-op
|
|
2626
|
+
if (cb) setTimeout(() => cb(null), 0);
|
|
2627
|
+
},
|
|
2628
|
+
|
|
2629
|
+
ftruncate(
|
|
2630
|
+
fd: number,
|
|
2631
|
+
lenOrCb?: number | ((err: Error | null) => void),
|
|
2632
|
+
cb?: (err: Error | null) => void,
|
|
2633
|
+
): void {
|
|
2634
|
+
const callback = typeof lenOrCb === "function" ? lenOrCb : cb;
|
|
2635
|
+
const len = typeof lenOrCb === "number" ? lenOrCb : 0;
|
|
2636
|
+
try {
|
|
2637
|
+
bridge.ftruncateSync(fd, len);
|
|
2638
|
+
if (callback) setTimeout(() => callback(null), 0);
|
|
2639
|
+
} catch (e) {
|
|
2640
|
+
if (callback) setTimeout(() => callback(e as Error), 0);
|
|
2641
|
+
}
|
|
2642
|
+
},
|
|
2643
|
+
|
|
2644
|
+
truncate(
|
|
2645
|
+
target: unknown,
|
|
2646
|
+
lenOrCb?: number | ((err: Error | null) => void),
|
|
2647
|
+
cb?: (err: Error | null) => void,
|
|
2648
|
+
): void {
|
|
2649
|
+
const callback = typeof lenOrCb === "function" ? lenOrCb : cb;
|
|
2650
|
+
const len = typeof lenOrCb === "number" ? lenOrCb : 0;
|
|
2651
|
+
try {
|
|
2652
|
+
volume.truncateSync(abs(target), len);
|
|
2653
|
+
if (callback) setTimeout(() => callback(null), 0);
|
|
2654
|
+
} catch (e) {
|
|
2655
|
+
if (callback) setTimeout(() => callback(e as Error), 0);
|
|
2656
|
+
}
|
|
2657
|
+
},
|
|
2658
|
+
|
|
2659
|
+
mkdtemp(
|
|
2660
|
+
prefix: string,
|
|
2661
|
+
optsOrCb?: unknown | ((err: Error | null, folder?: string) => void),
|
|
2662
|
+
cb?: (err: Error | null, folder?: string) => void,
|
|
2663
|
+
): void {
|
|
2664
|
+
const callback = typeof optsOrCb === "function"
|
|
2665
|
+
? optsOrCb as (err: Error | null, folder?: string) => void
|
|
2666
|
+
: cb;
|
|
2667
|
+
try {
|
|
2668
|
+
const result = bridge.mkdtempSync(prefix);
|
|
2669
|
+
if (callback) setTimeout(() => callback(null, result), 0);
|
|
2670
|
+
} catch (e) {
|
|
2671
|
+
if (callback) setTimeout(() => callback(e as Error), 0);
|
|
2672
|
+
}
|
|
2673
|
+
},
|
|
2674
|
+
readvSync(fd: number, buffers: ArrayBufferView[], pos?: number | null): number {
|
|
2675
|
+
let totalRead = 0;
|
|
2676
|
+
for (const buf of buffers) {
|
|
2677
|
+
const u8 = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
2678
|
+
const n = bridge.readSync(
|
|
2679
|
+
fd,
|
|
2680
|
+
u8 as unknown as Buffer,
|
|
2681
|
+
0,
|
|
2682
|
+
u8.length,
|
|
2683
|
+
pos != null ? pos + totalRead : null,
|
|
2684
|
+
);
|
|
2685
|
+
totalRead += n;
|
|
2686
|
+
if (n < u8.length) break; // short read — EOF
|
|
2687
|
+
}
|
|
2688
|
+
return totalRead;
|
|
2689
|
+
},
|
|
2690
|
+
|
|
2691
|
+
readv(
|
|
2692
|
+
fd: number,
|
|
2693
|
+
buffers: ArrayBufferView[],
|
|
2694
|
+
positionOrCb?:
|
|
2695
|
+
| number
|
|
2696
|
+
| null
|
|
2697
|
+
| ((err: Error | null, bytesRead?: number, buffers?: ArrayBufferView[]) => void),
|
|
2698
|
+
cb?: (err: Error | null, bytesRead?: number, buffers?: ArrayBufferView[]) => void,
|
|
2699
|
+
): void {
|
|
2700
|
+
const callback = typeof positionOrCb === "function" ? positionOrCb : cb;
|
|
2701
|
+
const pos = typeof positionOrCb === "number" ? positionOrCb : null;
|
|
2702
|
+
try {
|
|
2703
|
+
const n = bridge.readvSync(fd, buffers, pos);
|
|
2704
|
+
if (callback) setTimeout(() => callback(null, n, buffers), 0);
|
|
2705
|
+
} catch (e) {
|
|
2706
|
+
if (callback) setTimeout(() => callback(e as Error, 0, buffers), 0);
|
|
2707
|
+
}
|
|
2708
|
+
},
|
|
2709
|
+
cpSync(
|
|
2710
|
+
src: unknown,
|
|
2711
|
+
dest: unknown,
|
|
2712
|
+
opts?: { recursive?: boolean; force?: boolean; errorOnExist?: boolean },
|
|
2713
|
+
): void {
|
|
2714
|
+
const srcPath = abs(src);
|
|
2715
|
+
const destPath = abs(dest);
|
|
2716
|
+
const st = volume.statSync(srcPath);
|
|
2717
|
+
if (st.isDirectory()) {
|
|
2718
|
+
if (!opts?.recursive) {
|
|
2719
|
+
const err = new Error(
|
|
2720
|
+
`EISDIR: illegal operation on a directory, cp '${srcPath}' -> '${destPath}'`,
|
|
2721
|
+
) as Error & { code: string };
|
|
2722
|
+
err.code = "EISDIR";
|
|
2723
|
+
throw err;
|
|
2724
|
+
}
|
|
2725
|
+
if (!volume.existsSync(destPath)) {
|
|
2726
|
+
volume.mkdirSync(destPath, { recursive: true });
|
|
2727
|
+
}
|
|
2728
|
+
const children = volume.readdirSync(srcPath);
|
|
2729
|
+
for (const child of children) {
|
|
2730
|
+
const childSrc = srcPath.endsWith("/") ? srcPath + child : srcPath + "/" + child;
|
|
2731
|
+
const childDest = destPath.endsWith("/") ? destPath + child : destPath + "/" + child;
|
|
2732
|
+
bridge.cpSync(childSrc, childDest, opts);
|
|
2733
|
+
}
|
|
2734
|
+
} else {
|
|
2735
|
+
if (opts?.errorOnExist && volume.existsSync(destPath)) {
|
|
2736
|
+
const err = new Error(
|
|
2737
|
+
`EEXIST: file already exists, cp '${srcPath}' -> '${destPath}'`,
|
|
2738
|
+
) as Error & { code: string };
|
|
2739
|
+
err.code = "EEXIST";
|
|
2740
|
+
throw err;
|
|
2741
|
+
}
|
|
2742
|
+
if (!opts?.force && volume.existsSync(destPath)) return;
|
|
2743
|
+
const data = volume.readFileSync(srcPath);
|
|
2744
|
+
const parent = destPath.substring(0, destPath.lastIndexOf("/")) || "/";
|
|
2745
|
+
if (!volume.existsSync(parent)) {
|
|
2746
|
+
volume.mkdirSync(parent, { recursive: true });
|
|
2747
|
+
}
|
|
2748
|
+
volume.writeFileSync(destPath, data);
|
|
2749
|
+
}
|
|
2750
|
+
},
|
|
2751
|
+
|
|
2752
|
+
cp(
|
|
2753
|
+
src: unknown,
|
|
2754
|
+
dest: unknown,
|
|
2755
|
+
optsOrCb?:
|
|
2756
|
+
| { recursive?: boolean; force?: boolean; errorOnExist?: boolean }
|
|
2757
|
+
| ((err: Error | null) => void),
|
|
2758
|
+
cb?: (err: Error | null) => void,
|
|
2759
|
+
): void {
|
|
2760
|
+
const callback = typeof optsOrCb === "function" ? optsOrCb : cb;
|
|
2761
|
+
const opts = typeof optsOrCb === "object" ? optsOrCb : undefined;
|
|
2762
|
+
try {
|
|
2763
|
+
bridge.cpSync(src, dest, opts);
|
|
2764
|
+
if (callback) setTimeout(() => callback(null), 0);
|
|
2765
|
+
} catch (e) {
|
|
2766
|
+
if (callback) setTimeout(() => callback(e as Error), 0);
|
|
2767
|
+
}
|
|
2768
|
+
},
|
|
2769
|
+
statfsSync(_target: unknown, _opts?: unknown): StatFs {
|
|
2770
|
+
return new StatFs();
|
|
2771
|
+
},
|
|
2772
|
+
|
|
2773
|
+
statfs(
|
|
2774
|
+
target: unknown,
|
|
2775
|
+
optsOrCb?: unknown | ((err: Error | null, stats?: StatFs) => void),
|
|
2776
|
+
cb?: (err: Error | null, stats?: StatFs) => void,
|
|
2777
|
+
): void {
|
|
2778
|
+
const callback = typeof optsOrCb === "function"
|
|
2779
|
+
? optsOrCb as (err: Error | null, stats?: StatFs) => void
|
|
2780
|
+
: cb;
|
|
2781
|
+
const result = new StatFs();
|
|
2782
|
+
if (callback) setTimeout(() => callback(null, result), 0);
|
|
2783
|
+
},
|
|
2784
|
+
globSync(
|
|
2785
|
+
pattern: string | string[],
|
|
2786
|
+
opts?: { cwd?: string; exclude?: string[] | ((p: string) => boolean) },
|
|
2787
|
+
): string[] {
|
|
2788
|
+
// Reuse the glob logic from promises
|
|
2789
|
+
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
2790
|
+
const cwd = opts?.cwd ? abs(opts.cwd) : (getCwd ? getCwd() : "/");
|
|
2791
|
+
const exclude = opts?.exclude;
|
|
2792
|
+
|
|
2793
|
+
function expandBraces(pat: string): string[] {
|
|
2794
|
+
const m = pat.match(/^([^{]*)\{([^}]+)\}(.*)$/);
|
|
2795
|
+
if (!m) return [pat];
|
|
2796
|
+
const prefix = m[1], alts = m[2].split(","), suffix = m[3];
|
|
2797
|
+
const result: string[] = [];
|
|
2798
|
+
for (const alt of alts) result.push(...expandBraces(prefix + alt + suffix));
|
|
2799
|
+
return result;
|
|
2800
|
+
}
|
|
2801
|
+
function globToRegex(pat: string): RegExp {
|
|
2802
|
+
let re = "";
|
|
2803
|
+
let i = 0;
|
|
2804
|
+
while (i < pat.length) {
|
|
2805
|
+
const ch = pat[i];
|
|
2806
|
+
if (ch === "*" && pat[i + 1] === "*") {
|
|
2807
|
+
if (pat[i + 2] === "/") { re += "(?:.+/)?"; i += 3; }
|
|
2808
|
+
else { re += ".*"; i += 2; }
|
|
2809
|
+
} else if (ch === "*") { re += "[^/]*"; i++; }
|
|
2810
|
+
else if (ch === "?") { re += "[^/]"; i++; }
|
|
2811
|
+
else if (".()^$|\\+".includes(ch)) { re += "\\" + ch; i++; }
|
|
2812
|
+
else { re += ch; i++; }
|
|
2813
|
+
}
|
|
2814
|
+
return new RegExp("^" + re + "$");
|
|
2815
|
+
}
|
|
2816
|
+
function walk(dir: string, base: string): string[] {
|
|
2817
|
+
const results: string[] = [];
|
|
2818
|
+
let entries: string[];
|
|
2819
|
+
try { entries = volume.readdirSync(dir); } catch { return results; }
|
|
2820
|
+
for (const name of entries) {
|
|
2821
|
+
const full = dir.endsWith("/") ? dir + name : dir + "/" + name;
|
|
2822
|
+
const rel = base ? base + "/" + name : name;
|
|
2823
|
+
let isDir = false;
|
|
2824
|
+
try { isDir = volume.statSync(full).isDirectory(); } catch {}
|
|
2825
|
+
if (isDir) results.push(...walk(full, rel));
|
|
2826
|
+
else results.push(rel);
|
|
2827
|
+
}
|
|
2828
|
+
return results;
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
const regexes: RegExp[] = [];
|
|
2832
|
+
for (const p of patterns) {
|
|
2833
|
+
for (const expanded of expandBraces(p)) regexes.push(globToRegex(expanded));
|
|
2834
|
+
}
|
|
2835
|
+
let excludeFn: ((p: string) => boolean) | null = null;
|
|
2836
|
+
if (typeof exclude === "function") excludeFn = exclude;
|
|
2837
|
+
else if (Array.isArray(exclude) && exclude.length > 0) {
|
|
2838
|
+
const exRegexes: RegExp[] = [];
|
|
2839
|
+
for (const ep of exclude) {
|
|
2840
|
+
for (const expanded of expandBraces(ep)) exRegexes.push(globToRegex(expanded));
|
|
2841
|
+
}
|
|
2842
|
+
excludeFn = (p: string) => exRegexes.some((r) => r.test(p));
|
|
2843
|
+
}
|
|
2844
|
+
const allFiles = walk(cwd, "");
|
|
2845
|
+
return allFiles.filter((f) => {
|
|
2846
|
+
if (excludeFn && excludeFn(f)) return false;
|
|
2847
|
+
return regexes.some((r) => r.test(f));
|
|
2848
|
+
});
|
|
2849
|
+
},
|
|
2850
|
+
|
|
2851
|
+
glob(
|
|
2852
|
+
pattern: string | string[],
|
|
2853
|
+
optsOrCb?:
|
|
2854
|
+
| { cwd?: string; exclude?: string[] | ((p: string) => boolean) }
|
|
2855
|
+
| ((err: Error | null, matches?: string[]) => void),
|
|
2856
|
+
cb?: (err: Error | null, matches?: string[]) => void,
|
|
2857
|
+
): void {
|
|
2858
|
+
const callback = typeof optsOrCb === "function" ? optsOrCb : cb;
|
|
2859
|
+
const opts = typeof optsOrCb === "object" ? optsOrCb : undefined;
|
|
2860
|
+
try {
|
|
2861
|
+
const result = bridge.globSync(pattern, opts);
|
|
2862
|
+
if (callback) setTimeout(() => callback(null, result), 0);
|
|
2863
|
+
} catch (e) {
|
|
2864
|
+
if (callback) setTimeout(() => callback(e as Error), 0);
|
|
2865
|
+
}
|
|
2866
|
+
},
|
|
2867
|
+
openAsBlob(target: unknown, _opts?: { type?: string }): Promise<Blob> {
|
|
2868
|
+
try {
|
|
2869
|
+
const p = abs(target);
|
|
2870
|
+
const data = volume.readFileSync(p);
|
|
2871
|
+
const type = _opts?.type || "";
|
|
2872
|
+
const copy = new Uint8Array(data).buffer;
|
|
2873
|
+
return Promise.resolve(new Blob([copy], { type }));
|
|
2874
|
+
} catch (e) {
|
|
2875
|
+
return Promise.reject(e);
|
|
2876
|
+
}
|
|
2877
|
+
},
|
|
2878
|
+
StatFs: StatFs as any,
|
|
2879
|
+
StatWatcher: StatWatcher as any,
|
|
2880
|
+
|
|
2881
|
+
promises: promisesApi,
|
|
2882
|
+
constants: fsConst,
|
|
2883
|
+
} as FsBridge;
|
|
2884
|
+
|
|
2885
|
+
return bridge;
|
|
2886
|
+
}
|
|
2887
|
+
|
|
2888
|
+
export default buildFileSystemBridge;
|