@secure-exec/core 0.1.1-rc.3 → 0.2.0-rc.2

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.
Files changed (102) hide show
  1. package/dist/esm-compiler.d.ts +5 -1
  2. package/dist/esm-compiler.js +5 -1
  3. package/dist/fs-helpers.d.ts +1 -1
  4. package/dist/generated/isolate-runtime.d.ts +15 -15
  5. package/dist/generated/isolate-runtime.js +15 -15
  6. package/dist/index.d.ts +24 -5
  7. package/dist/index.js +23 -3
  8. package/dist/isolate-runtime/apply-custom-global-policy.js +3 -3
  9. package/dist/isolate-runtime/apply-timing-mitigation-freeze.js +2 -2
  10. package/dist/isolate-runtime/apply-timing-mitigation-off.js +2 -2
  11. package/dist/isolate-runtime/bridge-attach.js +2 -2
  12. package/dist/isolate-runtime/bridge-initial-globals.js +145 -6
  13. package/dist/isolate-runtime/eval-script-result.js +1 -1
  14. package/dist/isolate-runtime/global-exposure-helpers.js +2 -2
  15. package/dist/isolate-runtime/init-commonjs-module-globals.js +2 -2
  16. package/dist/isolate-runtime/override-process-cwd.js +1 -1
  17. package/dist/isolate-runtime/override-process-env.js +1 -1
  18. package/dist/isolate-runtime/require-setup.js +2868 -494
  19. package/dist/isolate-runtime/set-commonjs-file-globals.js +2 -2
  20. package/dist/isolate-runtime/set-stdin-data.js +1 -1
  21. package/dist/isolate-runtime/setup-dynamic-import.js +78 -19
  22. package/dist/isolate-runtime/setup-fs-facade.js +62 -23
  23. package/dist/kernel/command-registry.d.ts +44 -0
  24. package/dist/kernel/command-registry.js +114 -0
  25. package/dist/kernel/device-layer.d.ts +12 -0
  26. package/dist/kernel/device-layer.js +262 -0
  27. package/dist/kernel/dns-cache.d.ts +29 -0
  28. package/dist/kernel/dns-cache.js +52 -0
  29. package/dist/kernel/fd-table.d.ts +84 -0
  30. package/dist/kernel/fd-table.js +278 -0
  31. package/dist/kernel/file-lock.d.ts +34 -0
  32. package/dist/kernel/file-lock.js +122 -0
  33. package/dist/kernel/host-adapter.d.ts +50 -0
  34. package/dist/kernel/host-adapter.js +8 -0
  35. package/dist/kernel/index.d.ts +36 -0
  36. package/dist/kernel/index.js +34 -0
  37. package/dist/kernel/inode-table.d.ts +43 -0
  38. package/dist/kernel/inode-table.js +85 -0
  39. package/dist/kernel/kernel.d.ts +9 -0
  40. package/dist/kernel/kernel.js +1393 -0
  41. package/dist/kernel/permissions.d.ts +27 -0
  42. package/dist/kernel/permissions.js +118 -0
  43. package/dist/kernel/pipe-manager.d.ts +64 -0
  44. package/dist/kernel/pipe-manager.js +267 -0
  45. package/dist/kernel/proc-layer.d.ts +11 -0
  46. package/dist/kernel/proc-layer.js +501 -0
  47. package/dist/kernel/process-table.d.ts +124 -0
  48. package/dist/kernel/process-table.js +631 -0
  49. package/dist/kernel/pty.d.ts +108 -0
  50. package/dist/kernel/pty.js +541 -0
  51. package/dist/kernel/socket-table.d.ts +312 -0
  52. package/dist/kernel/socket-table.js +1188 -0
  53. package/dist/kernel/timer-table.d.ts +54 -0
  54. package/dist/kernel/timer-table.js +108 -0
  55. package/dist/kernel/types.d.ts +500 -0
  56. package/dist/kernel/types.js +89 -0
  57. package/dist/kernel/user.d.ts +29 -0
  58. package/dist/kernel/user.js +35 -0
  59. package/dist/kernel/vfs.d.ts +54 -0
  60. package/dist/kernel/vfs.js +8 -0
  61. package/dist/kernel/wait.d.ts +45 -0
  62. package/dist/kernel/wait.js +112 -0
  63. package/dist/kernel/wstatus.d.ts +21 -0
  64. package/dist/kernel/wstatus.js +33 -0
  65. package/dist/module-resolver.d.ts +4 -0
  66. package/dist/module-resolver.js +4 -0
  67. package/dist/package-bundler.d.ts +6 -1
  68. package/dist/runtime-driver.d.ts +3 -1
  69. package/dist/shared/bridge-contract.d.ts +349 -22
  70. package/dist/shared/bridge-contract.js +62 -5
  71. package/dist/shared/console-formatter.js +8 -4
  72. package/dist/shared/global-exposure.js +364 -19
  73. package/dist/shared/in-memory-fs.d.ts +33 -11
  74. package/dist/shared/in-memory-fs.js +439 -130
  75. package/dist/shared/permissions.d.ts +4 -6
  76. package/dist/shared/permissions.js +19 -39
  77. package/dist/types.d.ts +8 -159
  78. package/dist/types.js +5 -0
  79. package/package.json +12 -22
  80. package/dist/bridge/active-handles.d.ts +0 -22
  81. package/dist/bridge/active-handles.js +0 -55
  82. package/dist/bridge/child-process.d.ts +0 -99
  83. package/dist/bridge/child-process.js +0 -670
  84. package/dist/bridge/fs.d.ts +0 -281
  85. package/dist/bridge/fs.js +0 -2235
  86. package/dist/bridge/index.d.ts +0 -10
  87. package/dist/bridge/index.js +0 -41
  88. package/dist/bridge/module.d.ts +0 -75
  89. package/dist/bridge/module.js +0 -308
  90. package/dist/bridge/network.d.ts +0 -350
  91. package/dist/bridge/network.js +0 -2050
  92. package/dist/bridge/os.d.ts +0 -13
  93. package/dist/bridge/os.js +0 -256
  94. package/dist/bridge/polyfills.d.ts +0 -2
  95. package/dist/bridge/polyfills.js +0 -11
  96. package/dist/bridge/process.d.ts +0 -89
  97. package/dist/bridge/process.js +0 -1015
  98. package/dist/bridge.js +0 -12496
  99. package/dist/python-runtime.d.ts +0 -16
  100. package/dist/python-runtime.js +0 -45
  101. package/dist/runtime.d.ts +0 -31
  102. package/dist/runtime.js +0 -69
package/dist/bridge/fs.js DELETED
@@ -1,2235 +0,0 @@
1
- // fs polyfill module for isolated-vm
2
- // This module runs inside the isolate and provides Node.js fs API compatibility
3
- // It communicates with the host via the _fs Reference object
4
- import { Buffer } from "buffer";
5
- // File descriptor table — capped to prevent resource exhaustion
6
- const MAX_BRIDGE_FDS = 1024;
7
- const fdTable = new Map();
8
- let nextFd = 3;
9
- const O_RDONLY = 0;
10
- const O_WRONLY = 1;
11
- const O_RDWR = 2;
12
- const O_ACCMODE = 3;
13
- const O_CREAT = 64;
14
- const O_EXCL = 128;
15
- const O_TRUNC = 512;
16
- const O_APPEND = 1024;
17
- // Stats class
18
- class Stats {
19
- dev;
20
- ino;
21
- mode;
22
- nlink;
23
- uid;
24
- gid;
25
- rdev;
26
- size;
27
- blksize;
28
- blocks;
29
- atimeMs;
30
- mtimeMs;
31
- ctimeMs;
32
- birthtimeMs;
33
- atime;
34
- mtime;
35
- ctime;
36
- birthtime;
37
- constructor(init) {
38
- this.dev = init.dev ?? 0;
39
- this.ino = init.ino ?? 0;
40
- this.mode = init.mode;
41
- this.nlink = init.nlink ?? 1;
42
- this.uid = init.uid ?? 0;
43
- this.gid = init.gid ?? 0;
44
- this.rdev = init.rdev ?? 0;
45
- this.size = init.size;
46
- this.blksize = init.blksize ?? 4096;
47
- this.blocks = init.blocks ?? Math.ceil(init.size / 512);
48
- this.atimeMs = init.atimeMs ?? Date.now();
49
- this.mtimeMs = init.mtimeMs ?? Date.now();
50
- this.ctimeMs = init.ctimeMs ?? Date.now();
51
- this.birthtimeMs = init.birthtimeMs ?? Date.now();
52
- this.atime = new Date(this.atimeMs);
53
- this.mtime = new Date(this.mtimeMs);
54
- this.ctime = new Date(this.ctimeMs);
55
- this.birthtime = new Date(this.birthtimeMs);
56
- }
57
- isFile() {
58
- return (this.mode & 61440) === 32768;
59
- }
60
- isDirectory() {
61
- return (this.mode & 61440) === 16384;
62
- }
63
- isSymbolicLink() {
64
- return (this.mode & 61440) === 40960;
65
- }
66
- isBlockDevice() {
67
- return false;
68
- }
69
- isCharacterDevice() {
70
- return false;
71
- }
72
- isFIFO() {
73
- return false;
74
- }
75
- isSocket() {
76
- return false;
77
- }
78
- }
79
- // Dirent class for readdir with withFileTypes
80
- class Dirent {
81
- name;
82
- parentPath;
83
- path; // Deprecated alias for parentPath
84
- _isDir;
85
- constructor(name, isDir, parentPath = "") {
86
- this.name = name;
87
- this._isDir = isDir;
88
- this.parentPath = parentPath;
89
- this.path = parentPath;
90
- }
91
- isFile() {
92
- return !this._isDir;
93
- }
94
- isDirectory() {
95
- return this._isDir;
96
- }
97
- isSymbolicLink() {
98
- return false;
99
- }
100
- isBlockDevice() {
101
- return false;
102
- }
103
- isCharacterDevice() {
104
- return false;
105
- }
106
- isFIFO() {
107
- return false;
108
- }
109
- isSocket() {
110
- return false;
111
- }
112
- }
113
- // Dir class for opendir — async-iterable directory handle
114
- class Dir {
115
- path;
116
- _entries = null;
117
- _index = 0;
118
- _closed = false;
119
- constructor(dirPath) {
120
- this.path = dirPath;
121
- }
122
- _load() {
123
- if (this._entries === null) {
124
- this._entries = fs.readdirSync(this.path, { withFileTypes: true });
125
- }
126
- return this._entries;
127
- }
128
- readSync() {
129
- if (this._closed)
130
- throw new Error("Directory handle was closed");
131
- const entries = this._load();
132
- if (this._index >= entries.length)
133
- return null;
134
- return entries[this._index++];
135
- }
136
- async read() {
137
- return this.readSync();
138
- }
139
- closeSync() {
140
- this._closed = true;
141
- }
142
- async close() {
143
- this.closeSync();
144
- }
145
- async *[Symbol.asyncIterator]() {
146
- const entries = this._load();
147
- for (const entry of entries) {
148
- if (this._closed)
149
- return;
150
- yield entry;
151
- }
152
- this._closed = true;
153
- }
154
- }
155
- // ReadStream class for createReadStream
156
- // Provides a proper readable stream implementation that works with stream.pipeline
157
- class ReadStream {
158
- _options;
159
- // ReadStream-specific properties
160
- bytesRead = 0;
161
- path;
162
- pending = true;
163
- // Readable stream properties
164
- readable = true;
165
- readableAborted = false;
166
- readableDidRead = false;
167
- readableEncoding = null;
168
- readableEnded = false;
169
- readableFlowing = null;
170
- readableHighWaterMark = 65536;
171
- readableLength = 0;
172
- readableObjectMode = false;
173
- destroyed = false;
174
- closed = false;
175
- errored = null;
176
- // Internal state
177
- _content = null;
178
- _listeners = new Map();
179
- _started = false;
180
- constructor(filePath, _options) {
181
- this._options = _options;
182
- this.path = filePath;
183
- if (_options?.encoding) {
184
- this.readableEncoding = _options.encoding;
185
- }
186
- if (_options?.highWaterMark) {
187
- this.readableHighWaterMark = _options.highWaterMark;
188
- }
189
- }
190
- _loadContent() {
191
- if (this._content === null) {
192
- const pathStr = typeof this.path === 'string' ? this.path : this.path.toString();
193
- // readFileSync already normalizes the path
194
- this._content = fs.readFileSync(pathStr);
195
- this.pending = false;
196
- }
197
- return this._content;
198
- }
199
- // Start reading - called when 'data' listener is added or resume() is called
200
- _startReading() {
201
- if (this._started || this.destroyed)
202
- return;
203
- this._started = true;
204
- this.readableFlowing = true;
205
- Promise.resolve().then(() => {
206
- try {
207
- const content = this._loadContent();
208
- this.readableDidRead = true;
209
- // Determine start/end positions
210
- const start = this._options?.start ?? 0;
211
- const end = this._options?.end ?? content.length;
212
- const chunk = content.slice(start, end);
213
- this.bytesRead = chunk.length;
214
- // Emit data event
215
- this.emit('data', chunk);
216
- // Emit end and close
217
- Promise.resolve().then(() => {
218
- this.readable = false;
219
- this.readableEnded = true;
220
- this.emit('end');
221
- Promise.resolve().then(() => {
222
- this.closed = true;
223
- this.emit('close');
224
- });
225
- });
226
- }
227
- catch (err) {
228
- this.errored = err;
229
- this.emit('error', err);
230
- this.destroy(err);
231
- }
232
- });
233
- }
234
- // Event handling
235
- on(event, listener) {
236
- if (!this._listeners.has(event)) {
237
- this._listeners.set(event, []);
238
- }
239
- this._listeners.get(event).push(listener);
240
- // Start reading when 'data' listener is added (flowing mode)
241
- if (event === 'data' && !this._started) {
242
- this._startReading();
243
- }
244
- return this;
245
- }
246
- once(event, listener) {
247
- const wrapper = (...args) => {
248
- this.off(event, wrapper);
249
- listener(...args);
250
- };
251
- wrapper._originalListener = listener;
252
- return this.on(event, wrapper);
253
- }
254
- off(event, listener) {
255
- const listeners = this._listeners.get(event);
256
- if (listeners) {
257
- const idx = listeners.findIndex(fn => fn === listener || fn._originalListener === listener);
258
- if (idx !== -1)
259
- listeners.splice(idx, 1);
260
- }
261
- return this;
262
- }
263
- removeListener(event, listener) {
264
- return this.off(event, listener);
265
- }
266
- removeAllListeners(event) {
267
- if (event) {
268
- this._listeners.delete(event);
269
- }
270
- else {
271
- this._listeners.clear();
272
- }
273
- return this;
274
- }
275
- emit(event, ...args) {
276
- const listeners = this._listeners.get(event);
277
- if (listeners && listeners.length > 0) {
278
- listeners.slice().forEach(fn => fn(...args));
279
- return true;
280
- }
281
- return false;
282
- }
283
- // Readable methods
284
- read(_size) {
285
- if (this.readableEnded || this.destroyed)
286
- return null;
287
- try {
288
- const content = this._loadContent();
289
- const start = this._options?.start ?? 0;
290
- const end = this._options?.end ?? content.length;
291
- const chunk = content.slice(start, end);
292
- this.bytesRead = chunk.length;
293
- this.readableDidRead = true;
294
- this.readable = false;
295
- this.readableEnded = true;
296
- // Schedule end event
297
- Promise.resolve().then(() => {
298
- this.emit('end');
299
- Promise.resolve().then(() => {
300
- this.closed = true;
301
- this.emit('close');
302
- });
303
- });
304
- return this.readableEncoding ? chunk.toString(this.readableEncoding) : chunk;
305
- }
306
- catch (err) {
307
- this.errored = err;
308
- this.emit('error', err);
309
- return null;
310
- }
311
- }
312
- pipe(destination, _options) {
313
- const content = this._loadContent();
314
- const start = this._options?.start ?? 0;
315
- const end = this._options?.end ?? content.length;
316
- const chunk = content.slice(start, end);
317
- this.bytesRead = chunk.length;
318
- this.readableDidRead = true;
319
- if (typeof destination.write === 'function') {
320
- destination.write(chunk);
321
- }
322
- if (typeof destination.end === 'function') {
323
- Promise.resolve().then(() => destination.end());
324
- }
325
- this.readable = false;
326
- this.readableEnded = true;
327
- this.closed = true;
328
- Promise.resolve().then(() => {
329
- this.emit('end');
330
- this.emit('close');
331
- });
332
- return destination;
333
- }
334
- unpipe(_destination) {
335
- return this;
336
- }
337
- pause() {
338
- this.readableFlowing = false;
339
- return this;
340
- }
341
- resume() {
342
- this.readableFlowing = true;
343
- if (!this._started) {
344
- this._startReading();
345
- }
346
- return this;
347
- }
348
- setEncoding(encoding) {
349
- this.readableEncoding = encoding;
350
- return this;
351
- }
352
- destroy(error) {
353
- if (this.destroyed)
354
- return this;
355
- this.destroyed = true;
356
- this.readable = false;
357
- if (error) {
358
- this.errored = error;
359
- this.emit('error', error);
360
- }
361
- this.emit('close');
362
- this.closed = true;
363
- return this;
364
- }
365
- close(callback) {
366
- if (this.closed) {
367
- if (callback)
368
- Promise.resolve().then(() => callback(null));
369
- return;
370
- }
371
- this.closed = true;
372
- this.readable = false;
373
- this.destroyed = true;
374
- Promise.resolve().then(() => {
375
- this.emit('close');
376
- if (callback)
377
- callback(null);
378
- });
379
- }
380
- // Symbol.asyncIterator for async iteration
381
- async *[Symbol.asyncIterator]() {
382
- const content = this._loadContent();
383
- const start = this._options?.start ?? 0;
384
- const end = this._options?.end ?? content.length;
385
- const chunk = content.slice(start, end);
386
- yield this.readableEncoding ? chunk.toString(this.readableEncoding) : chunk;
387
- }
388
- }
389
- // WriteStream class for createWriteStream
390
- // This provides a type-safe implementation that satisfies nodeFs.WriteStream
391
- const MAX_WRITE_STREAM_BYTES = 16 * 1024 * 1024; // 16MB cap to prevent memory exhaustion
392
- // We use 'as' assertion at the return site since the full interface is complex
393
- class WriteStream {
394
- // WriteStream-specific properties
395
- bytesWritten = 0;
396
- path;
397
- pending = false;
398
- // Writable stream properties
399
- writable = true;
400
- writableAborted = false;
401
- writableEnded = false;
402
- writableFinished = false;
403
- writableHighWaterMark = 16384;
404
- writableLength = 0;
405
- writableObjectMode = false;
406
- writableCorked = 0;
407
- destroyed = false;
408
- closed = false;
409
- errored = null;
410
- writableNeedDrain = false;
411
- // Internal state
412
- _chunks = [];
413
- _listeners = new Map();
414
- constructor(filePath, _options) {
415
- this.path = filePath;
416
- }
417
- // WriteStream-specific methods
418
- close(callback) {
419
- if (this.closed) {
420
- if (callback)
421
- Promise.resolve().then(() => callback(null));
422
- return;
423
- }
424
- this.closed = true;
425
- this.writable = false;
426
- Promise.resolve().then(() => {
427
- this.emit("close");
428
- if (callback)
429
- callback(null);
430
- });
431
- }
432
- // Writable methods
433
- write(chunk, encodingOrCallback, callback) {
434
- if (this.writableEnded || this.destroyed) {
435
- const err = new Error("write after end");
436
- if (typeof encodingOrCallback === "function") {
437
- Promise.resolve().then(() => encodingOrCallback(err));
438
- }
439
- else if (callback) {
440
- Promise.resolve().then(() => callback(err));
441
- }
442
- return false;
443
- }
444
- let data;
445
- if (typeof chunk === "string") {
446
- data = Buffer.from(chunk, typeof encodingOrCallback === "string" ? encodingOrCallback : "utf8");
447
- }
448
- else if (Buffer.isBuffer(chunk)) {
449
- data = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
450
- }
451
- else if (chunk instanceof Uint8Array) {
452
- data = chunk;
453
- }
454
- else {
455
- data = Buffer.from(String(chunk));
456
- }
457
- // Cap buffered data to prevent memory exhaustion
458
- if (this.writableLength + data.length > MAX_WRITE_STREAM_BYTES) {
459
- const err = new Error(`WriteStream buffer exceeded ${MAX_WRITE_STREAM_BYTES} bytes`);
460
- this.errored = err;
461
- this.destroyed = true;
462
- this.writable = false;
463
- const cb = typeof encodingOrCallback === "function" ? encodingOrCallback : callback;
464
- if (cb)
465
- Promise.resolve().then(() => cb(err));
466
- Promise.resolve().then(() => this.emit("error", err));
467
- return false;
468
- }
469
- this._chunks.push(data);
470
- this.bytesWritten += data.length;
471
- this.writableLength += data.length;
472
- const cb = typeof encodingOrCallback === "function" ? encodingOrCallback : callback;
473
- if (cb)
474
- Promise.resolve().then(() => cb(null));
475
- return true;
476
- }
477
- end(chunkOrCb, encodingOrCallback, callback) {
478
- if (this.writableEnded)
479
- return this;
480
- let cb;
481
- if (typeof chunkOrCb === "function") {
482
- cb = chunkOrCb;
483
- }
484
- else if (typeof encodingOrCallback === "function") {
485
- cb = encodingOrCallback;
486
- if (chunkOrCb !== undefined && chunkOrCb !== null) {
487
- this.write(chunkOrCb);
488
- }
489
- }
490
- else {
491
- cb = callback;
492
- if (chunkOrCb !== undefined && chunkOrCb !== null) {
493
- this.write(chunkOrCb, encodingOrCallback);
494
- }
495
- }
496
- this.writableEnded = true;
497
- // Concatenate and write all chunks
498
- const totalLength = this._chunks.reduce((sum, c) => sum + c.length, 0);
499
- const result = new Uint8Array(totalLength);
500
- let offset = 0;
501
- for (const c of this._chunks) {
502
- result.set(c, offset);
503
- offset += c.length;
504
- }
505
- // Write to filesystem
506
- const pathStr = typeof this.path === "string" ? this.path : this.path.toString();
507
- fs.writeFileSync(pathStr, result);
508
- this.writable = false;
509
- this.writableFinished = true;
510
- this.writableLength = 0;
511
- Promise.resolve().then(() => {
512
- this.emit("finish");
513
- this.emit("close");
514
- this.closed = true;
515
- if (cb)
516
- cb();
517
- });
518
- return this;
519
- }
520
- setDefaultEncoding(_encoding) {
521
- return this;
522
- }
523
- cork() {
524
- this.writableCorked++;
525
- }
526
- uncork() {
527
- if (this.writableCorked > 0)
528
- this.writableCorked--;
529
- }
530
- destroy(error) {
531
- if (this.destroyed)
532
- return this;
533
- this.destroyed = true;
534
- this.writable = false;
535
- if (error) {
536
- this.errored = error;
537
- Promise.resolve().then(() => {
538
- this.emit("error", error);
539
- this.emit("close");
540
- this.closed = true;
541
- });
542
- }
543
- else {
544
- Promise.resolve().then(() => {
545
- this.emit("close");
546
- this.closed = true;
547
- });
548
- }
549
- return this;
550
- }
551
- // Internal methods (required by Writable interface but not typically called directly)
552
- _write(_chunk, _encoding, callback) {
553
- callback();
554
- }
555
- _destroy(_error, callback) {
556
- callback();
557
- }
558
- _final(callback) {
559
- callback();
560
- }
561
- // EventEmitter methods
562
- addListener(event, listener) {
563
- return this.on(event, listener);
564
- }
565
- on(event, listener) {
566
- const listeners = this._listeners.get(event) || [];
567
- listeners.push(listener);
568
- this._listeners.set(event, listeners);
569
- return this;
570
- }
571
- once(event, listener) {
572
- const wrapper = (...args) => {
573
- this.removeListener(event, wrapper);
574
- listener(...args);
575
- };
576
- return this.on(event, wrapper);
577
- }
578
- prependListener(event, listener) {
579
- const listeners = this._listeners.get(event) || [];
580
- listeners.unshift(listener);
581
- this._listeners.set(event, listeners);
582
- return this;
583
- }
584
- prependOnceListener(event, listener) {
585
- const wrapper = (...args) => {
586
- this.removeListener(event, wrapper);
587
- listener(...args);
588
- };
589
- return this.prependListener(event, wrapper);
590
- }
591
- removeListener(event, listener) {
592
- const listeners = this._listeners.get(event);
593
- if (listeners) {
594
- const idx = listeners.indexOf(listener);
595
- if (idx !== -1)
596
- listeners.splice(idx, 1);
597
- }
598
- return this;
599
- }
600
- off(event, listener) {
601
- return this.removeListener(event, listener);
602
- }
603
- removeAllListeners(event) {
604
- if (event !== undefined) {
605
- this._listeners.delete(event);
606
- }
607
- else {
608
- this._listeners.clear();
609
- }
610
- return this;
611
- }
612
- emit(event, ...args) {
613
- const listeners = this._listeners.get(event);
614
- if (listeners && listeners.length > 0) {
615
- listeners.slice().forEach(l => l(...args));
616
- return true;
617
- }
618
- return false;
619
- }
620
- listeners(event) {
621
- return [...(this._listeners.get(event) || [])];
622
- }
623
- rawListeners(event) {
624
- return this.listeners(event);
625
- }
626
- listenerCount(event) {
627
- return (this._listeners.get(event) || []).length;
628
- }
629
- eventNames() {
630
- return [...this._listeners.keys()];
631
- }
632
- getMaxListeners() {
633
- return 10;
634
- }
635
- setMaxListeners(_n) {
636
- return this;
637
- }
638
- // Pipe methods (minimal implementation)
639
- pipe(destination, _options) {
640
- return destination;
641
- }
642
- unpipe(_destination) {
643
- return this;
644
- }
645
- // Additional required methods
646
- compose(_stream, _options) {
647
- throw new Error("compose not implemented in sandbox");
648
- }
649
- [Symbol.asyncDispose]() {
650
- return Promise.resolve();
651
- }
652
- }
653
- // Parse flags string to number
654
- function parseFlags(flags) {
655
- if (typeof flags === "number")
656
- return flags;
657
- const flagMap = {
658
- r: O_RDONLY,
659
- "r+": O_RDWR,
660
- w: O_WRONLY | O_CREAT | O_TRUNC,
661
- "w+": O_RDWR | O_CREAT | O_TRUNC,
662
- a: O_WRONLY | O_APPEND | O_CREAT,
663
- "a+": O_RDWR | O_APPEND | O_CREAT,
664
- wx: O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
665
- xw: O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
666
- "wx+": O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
667
- "xw+": O_RDWR | O_CREAT | O_TRUNC | O_EXCL,
668
- ax: O_WRONLY | O_APPEND | O_CREAT | O_EXCL,
669
- xa: O_WRONLY | O_APPEND | O_CREAT | O_EXCL,
670
- "ax+": O_RDWR | O_APPEND | O_CREAT | O_EXCL,
671
- "xa+": O_RDWR | O_APPEND | O_CREAT | O_EXCL,
672
- };
673
- if (flags in flagMap)
674
- return flagMap[flags];
675
- throw new Error("Unknown file flag: " + flags);
676
- }
677
- // Check if flags allow reading
678
- function canRead(flags) {
679
- const mode = flags & O_ACCMODE;
680
- return mode === 0 || mode === 2;
681
- }
682
- // Check if flags allow writing
683
- function canWrite(flags) {
684
- const mode = flags & O_ACCMODE;
685
- return mode === 1 || mode === 2;
686
- }
687
- // Helper to create fs errors
688
- function createFsError(code, message, syscall, path) {
689
- const err = new Error(message);
690
- err.code = code;
691
- err.errno = code === "ENOENT" ? -2 : code === "EACCES" ? -13 : code === "EBADF" ? -9 : code === "EMFILE" ? -24 : -1;
692
- err.syscall = syscall;
693
- if (path)
694
- err.path = path;
695
- return err;
696
- }
697
- /** Wrap a bridge call with ENOENT/EACCES error re-creation. */
698
- function bridgeCall(fn, syscall, path) {
699
- try {
700
- return fn();
701
- }
702
- catch (err) {
703
- const msg = err.message || String(err);
704
- if (msg.includes("ENOENT") || msg.includes("no such file or directory") || msg.includes("not found")) {
705
- throw createFsError("ENOENT", `ENOENT: no such file or directory, ${syscall} '${path}'`, syscall, path);
706
- }
707
- if (msg.includes("EACCES") || msg.includes("permission denied")) {
708
- throw createFsError("EACCES", `EACCES: permission denied, ${syscall} '${path}'`, syscall, path);
709
- }
710
- if (msg.includes("EEXIST") || msg.includes("file already exists")) {
711
- throw createFsError("EEXIST", `EEXIST: file already exists, ${syscall} '${path}'`, syscall, path);
712
- }
713
- if (msg.includes("EINVAL") || msg.includes("invalid argument")) {
714
- throw createFsError("EINVAL", `EINVAL: invalid argument, ${syscall} '${path}'`, syscall, path);
715
- }
716
- throw err;
717
- }
718
- }
719
- // Glob pattern matching helper — converts glob to regex and walks VFS recursively
720
- function _globToRegex(pattern) {
721
- // Determine base directory vs glob portion
722
- let regexStr = "";
723
- let i = 0;
724
- while (i < pattern.length) {
725
- const ch = pattern[i];
726
- if (ch === "*" && pattern[i + 1] === "*") {
727
- // ** matches any depth of directories
728
- if (pattern[i + 2] === "/") {
729
- regexStr += "(?:.+/)?";
730
- i += 3;
731
- }
732
- else {
733
- regexStr += ".*";
734
- i += 2;
735
- }
736
- }
737
- else if (ch === "*") {
738
- regexStr += "[^/]*";
739
- i++;
740
- }
741
- else if (ch === "?") {
742
- regexStr += "[^/]";
743
- i++;
744
- }
745
- else if (ch === "{") {
746
- const close = pattern.indexOf("}", i);
747
- if (close !== -1) {
748
- const alternatives = pattern.slice(i + 1, close).split(",");
749
- regexStr += "(?:" + alternatives.map(a => a.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, "[^/]*")).join("|") + ")";
750
- i = close + 1;
751
- }
752
- else {
753
- regexStr += "\\{";
754
- i++;
755
- }
756
- }
757
- else if (ch === "[") {
758
- const close = pattern.indexOf("]", i);
759
- if (close !== -1) {
760
- regexStr += pattern.slice(i, close + 1);
761
- i = close + 1;
762
- }
763
- else {
764
- regexStr += "\\[";
765
- i++;
766
- }
767
- }
768
- else if (".+^${}()|[]\\".includes(ch)) {
769
- regexStr += "\\" + ch;
770
- i++;
771
- }
772
- else {
773
- regexStr += ch;
774
- i++;
775
- }
776
- }
777
- return new RegExp("^" + regexStr + "$");
778
- }
779
- function _globGetBase(pattern) {
780
- // Find the longest directory prefix that has no glob characters
781
- const parts = pattern.split("/");
782
- const baseParts = [];
783
- for (const part of parts) {
784
- if (/[*?{}\[\]]/.test(part))
785
- break;
786
- baseParts.push(part);
787
- }
788
- return baseParts.join("/") || "/";
789
- }
790
- // Recursively walk VFS directory and collect matching paths
791
- // We use a reference to `fs` via late-binding in the fs object method
792
- const MAX_GLOB_DEPTH = 100; // Prevent stack overflow on deeply nested trees
793
- function _globCollect(pattern, results) {
794
- const regex = _globToRegex(pattern);
795
- const base = _globGetBase(pattern);
796
- const walk = (dir, depth) => {
797
- if (depth > MAX_GLOB_DEPTH)
798
- return;
799
- let entries;
800
- try {
801
- entries = _globReadDir(dir);
802
- }
803
- catch {
804
- return; // Directory doesn't exist or not readable
805
- }
806
- for (const entry of entries) {
807
- const fullPath = dir === "/" ? "/" + entry : dir + "/" + entry;
808
- // Check if this path matches the pattern
809
- if (regex.test(fullPath)) {
810
- results.push(fullPath);
811
- }
812
- // Recurse into directories if pattern has ** or more segments
813
- try {
814
- const stat = _globStat(fullPath);
815
- if (stat.isDirectory()) {
816
- walk(fullPath, depth + 1);
817
- }
818
- }
819
- catch {
820
- // Not a directory or stat failed — skip
821
- }
822
- }
823
- };
824
- // Start walking from the base directory
825
- try {
826
- // Check if base itself matches (edge case)
827
- if (regex.test(base)) {
828
- const stat = _globStat(base);
829
- if (!stat.isDirectory()) {
830
- results.push(base);
831
- return;
832
- }
833
- }
834
- walk(base, 0);
835
- }
836
- catch {
837
- // Base doesn't exist — no matches
838
- }
839
- }
840
- // Late-bound references — these get assigned after fs is defined
841
- let _globReadDir;
842
- let _globStat;
843
- // Helper to convert PathLike to string
844
- function toPathString(path) {
845
- if (typeof path === "string")
846
- return path;
847
- if (Buffer.isBuffer(path))
848
- return path.toString("utf8");
849
- if (path instanceof URL)
850
- return path.pathname;
851
- return String(path);
852
- }
853
- // Note: Path normalization is handled by VirtualFileSystem, not here.
854
- // The VFS expects /data/* paths for Directory access, so we pass paths through unchanged.
855
- // The fs module implementation
856
- const fs = {
857
- // Constants
858
- constants: {
859
- // File Access Constants
860
- F_OK: 0,
861
- R_OK: 4,
862
- W_OK: 2,
863
- X_OK: 1,
864
- // File Copy Constants
865
- COPYFILE_EXCL: 1,
866
- COPYFILE_FICLONE: 2,
867
- COPYFILE_FICLONE_FORCE: 4,
868
- // File Open Constants
869
- O_RDONLY,
870
- O_WRONLY,
871
- O_RDWR,
872
- O_CREAT,
873
- O_EXCL,
874
- O_NOCTTY: 256,
875
- O_TRUNC,
876
- O_APPEND,
877
- O_DIRECTORY: 65536,
878
- O_NOATIME: 262144,
879
- O_NOFOLLOW: 131072,
880
- O_SYNC: 1052672,
881
- O_DSYNC: 4096,
882
- O_SYMLINK: 2097152,
883
- O_DIRECT: 16384,
884
- O_NONBLOCK: 2048,
885
- // File Type Constants
886
- S_IFMT: 61440,
887
- S_IFREG: 32768,
888
- S_IFDIR: 16384,
889
- S_IFCHR: 8192,
890
- S_IFBLK: 24576,
891
- S_IFIFO: 4096,
892
- S_IFLNK: 40960,
893
- S_IFSOCK: 49152,
894
- // File Mode Constants
895
- S_IRWXU: 448,
896
- S_IRUSR: 256,
897
- S_IWUSR: 128,
898
- S_IXUSR: 64,
899
- S_IRWXG: 56,
900
- S_IRGRP: 32,
901
- S_IWGRP: 16,
902
- S_IXGRP: 8,
903
- S_IRWXO: 7,
904
- S_IROTH: 4,
905
- S_IWOTH: 2,
906
- S_IXOTH: 1,
907
- UV_FS_O_FILEMAP: 536870912,
908
- },
909
- Stats,
910
- Dirent,
911
- Dir,
912
- // Sync methods
913
- readFileSync(path, options) {
914
- const rawPath = typeof path === "number" ? fdTable.get(path)?.path : toPathString(path);
915
- if (!rawPath)
916
- throw createFsError("EBADF", "EBADF: bad file descriptor", "read");
917
- const pathStr = rawPath;
918
- const encoding = typeof options === "string" ? options : options?.encoding;
919
- try {
920
- if (encoding) {
921
- // Text mode - use text read
922
- const content = _fs.readFile.applySyncPromise(undefined, [pathStr]);
923
- return content;
924
- }
925
- else {
926
- // Binary mode - use binary read with base64 encoding
927
- const base64Content = _fs.readFileBinary.applySyncPromise(undefined, [pathStr]);
928
- return Buffer.from(base64Content, "base64");
929
- }
930
- }
931
- catch (err) {
932
- const errMsg = err.message || String(err);
933
- // Convert various "not found" errors to proper ENOENT
934
- if (errMsg.includes("entry not found") ||
935
- errMsg.includes("not found") ||
936
- errMsg.includes("ENOENT") ||
937
- errMsg.includes("no such file or directory")) {
938
- throw createFsError("ENOENT", `ENOENT: no such file or directory, open '${rawPath}'`, "open", rawPath);
939
- }
940
- // Convert permission errors to proper EACCES
941
- if (errMsg.includes("EACCES") || errMsg.includes("permission denied")) {
942
- throw createFsError("EACCES", `EACCES: permission denied, open '${rawPath}'`, "open", rawPath);
943
- }
944
- throw err;
945
- }
946
- },
947
- writeFileSync(file, data, _options) {
948
- const rawPath = typeof file === "number" ? fdTable.get(file)?.path : toPathString(file);
949
- if (!rawPath)
950
- throw createFsError("EBADF", "EBADF: bad file descriptor", "write");
951
- const pathStr = rawPath;
952
- if (typeof data === "string") {
953
- // Text mode - use text write
954
- // Return the result so async callers (fs.promises) can await it.
955
- return _fs.writeFile.applySyncPromise(undefined, [pathStr, data]);
956
- }
957
- else if (ArrayBuffer.isView(data)) {
958
- // Binary mode - convert to base64 and use binary write
959
- const uint8 = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
960
- const base64 = Buffer.from(uint8).toString("base64");
961
- return _fs.writeFileBinary.applySyncPromise(undefined, [pathStr, base64]);
962
- }
963
- else {
964
- // Fallback to text mode
965
- return _fs.writeFile.applySyncPromise(undefined, [pathStr, String(data)]);
966
- }
967
- },
968
- appendFileSync(path, data, options) {
969
- const existing = fs.existsSync(path)
970
- ? fs.readFileSync(path, "utf8")
971
- : "";
972
- const content = typeof data === "string" ? data : String(data);
973
- fs.writeFileSync(path, existing + content, options);
974
- },
975
- readdirSync(path, options) {
976
- const rawPath = toPathString(path);
977
- const pathStr = rawPath;
978
- let entriesJson;
979
- try {
980
- entriesJson = _fs.readDir.applySyncPromise(undefined, [pathStr]);
981
- }
982
- catch (err) {
983
- // Convert "entry not found" and similar errors to proper ENOENT
984
- const errMsg = err.message || String(err);
985
- if (errMsg.includes("entry not found") || errMsg.includes("not found")) {
986
- throw createFsError("ENOENT", `ENOENT: no such file or directory, scandir '${rawPath}'`, "scandir", rawPath);
987
- }
988
- throw err;
989
- }
990
- const entries = JSON.parse(entriesJson);
991
- if (options?.withFileTypes) {
992
- return entries.map((e) => new Dirent(e.name, e.isDirectory, rawPath));
993
- }
994
- return entries.map((e) => e.name);
995
- },
996
- mkdirSync(path, options) {
997
- const rawPath = toPathString(path);
998
- const pathStr = rawPath;
999
- const recursive = typeof options === "object" ? options?.recursive ?? false : false;
1000
- _fs.mkdir.applySyncPromise(undefined, [pathStr, recursive]);
1001
- return recursive ? rawPath : undefined;
1002
- },
1003
- rmdirSync(path, _options) {
1004
- const pathStr = toPathString(path);
1005
- _fs.rmdir.applySyncPromise(undefined, [pathStr]);
1006
- },
1007
- rmSync(path, options) {
1008
- const pathStr = toPathString(path);
1009
- const opts = options || {};
1010
- try {
1011
- const stats = fs.statSync(pathStr);
1012
- if (stats.isDirectory()) {
1013
- if (opts.recursive) {
1014
- // Recursively remove directory contents
1015
- const entries = fs.readdirSync(pathStr);
1016
- for (const entry of entries) {
1017
- const entryPath = pathStr.endsWith("/") ? pathStr + entry : pathStr + "/" + entry;
1018
- const entryStats = fs.statSync(entryPath);
1019
- if (entryStats.isDirectory()) {
1020
- fs.rmSync(entryPath, { recursive: true });
1021
- }
1022
- else {
1023
- fs.unlinkSync(entryPath);
1024
- }
1025
- }
1026
- fs.rmdirSync(pathStr);
1027
- }
1028
- else {
1029
- fs.rmdirSync(pathStr);
1030
- }
1031
- }
1032
- else {
1033
- fs.unlinkSync(pathStr);
1034
- }
1035
- }
1036
- catch (e) {
1037
- if (opts.force && e.code === "ENOENT") {
1038
- return; // Ignore ENOENT when force is true
1039
- }
1040
- throw e;
1041
- }
1042
- },
1043
- existsSync(path) {
1044
- const pathStr = toPathString(path);
1045
- return _fs.exists.applySyncPromise(undefined, [pathStr]);
1046
- },
1047
- statSync(path, _options) {
1048
- const rawPath = toPathString(path);
1049
- const pathStr = rawPath;
1050
- let statJson;
1051
- try {
1052
- statJson = _fs.stat.applySyncPromise(undefined, [pathStr]);
1053
- }
1054
- catch (err) {
1055
- // Convert various "not found" errors to proper ENOENT
1056
- const errMsg = err.message || String(err);
1057
- if (errMsg.includes("entry not found") ||
1058
- errMsg.includes("not found") ||
1059
- errMsg.includes("ENOENT") ||
1060
- errMsg.includes("no such file or directory")) {
1061
- throw createFsError("ENOENT", `ENOENT: no such file or directory, stat '${rawPath}'`, "stat", rawPath);
1062
- }
1063
- throw err;
1064
- }
1065
- const stat = JSON.parse(statJson);
1066
- return new Stats(stat);
1067
- },
1068
- lstatSync(path, _options) {
1069
- const pathStr = toPathString(path);
1070
- const statJson = bridgeCall(() => _fs.lstat.applySyncPromise(undefined, [pathStr]), "lstat", pathStr);
1071
- const stat = JSON.parse(statJson);
1072
- return new Stats(stat);
1073
- },
1074
- unlinkSync(path) {
1075
- const pathStr = toPathString(path);
1076
- _fs.unlink.applySyncPromise(undefined, [pathStr]);
1077
- },
1078
- renameSync(oldPath, newPath) {
1079
- const oldPathStr = toPathString(oldPath);
1080
- const newPathStr = toPathString(newPath);
1081
- _fs.rename.applySyncPromise(undefined, [oldPathStr, newPathStr]);
1082
- },
1083
- copyFileSync(src, dest, _mode) {
1084
- // readFileSync and writeFileSync already normalize paths
1085
- const content = fs.readFileSync(src);
1086
- fs.writeFileSync(dest, content);
1087
- },
1088
- // Recursive copy
1089
- cpSync(src, dest, options) {
1090
- const srcPath = toPathString(src);
1091
- const destPath = toPathString(dest);
1092
- const opts = options || {};
1093
- const srcStat = fs.statSync(srcPath);
1094
- if (srcStat.isDirectory()) {
1095
- if (!opts.recursive) {
1096
- throw createFsError("ERR_FS_EISDIR", `Path is a directory: cp '${srcPath}'`, "cp", srcPath);
1097
- }
1098
- // Create destination directory
1099
- try {
1100
- fs.mkdirSync(destPath, { recursive: true });
1101
- }
1102
- catch {
1103
- // May already exist
1104
- }
1105
- // Copy contents recursively
1106
- const entries = fs.readdirSync(srcPath);
1107
- for (const entry of entries) {
1108
- const srcEntry = srcPath.endsWith("/") ? srcPath + entry : srcPath + "/" + entry;
1109
- const destEntry = destPath.endsWith("/") ? destPath + entry : destPath + "/" + entry;
1110
- fs.cpSync(srcEntry, destEntry, opts);
1111
- }
1112
- }
1113
- else {
1114
- // File copy
1115
- if (opts.errorOnExist && fs.existsSync(destPath)) {
1116
- throw createFsError("EEXIST", `EEXIST: file already exists, cp '${srcPath}' -> '${destPath}'`, "cp", destPath);
1117
- }
1118
- if (!opts.force && opts.force !== undefined && fs.existsSync(destPath)) {
1119
- return; // Skip without error when force is false
1120
- }
1121
- fs.copyFileSync(srcPath, destPath);
1122
- }
1123
- },
1124
- // Temp directory creation
1125
- mkdtempSync(prefix, _options) {
1126
- const suffix = Math.random().toString(36).slice(2, 8);
1127
- const dirPath = prefix + suffix;
1128
- fs.mkdirSync(dirPath, { recursive: true });
1129
- return dirPath;
1130
- },
1131
- // Directory handle (sync)
1132
- opendirSync(path, _options) {
1133
- const pathStr = toPathString(path);
1134
- // Verify directory exists
1135
- const stat = fs.statSync(pathStr);
1136
- if (!stat.isDirectory()) {
1137
- throw createFsError("ENOTDIR", `ENOTDIR: not a directory, opendir '${pathStr}'`, "opendir", pathStr);
1138
- }
1139
- return new Dir(pathStr);
1140
- },
1141
- // File descriptor methods
1142
- openSync(path, flags, _mode) {
1143
- // Enforce bridge-side FD limit
1144
- if (fdTable.size >= MAX_BRIDGE_FDS) {
1145
- throw createFsError("EMFILE", "EMFILE: too many open files, open '" + toPathString(path) + "'", "open", toPathString(path));
1146
- }
1147
- const rawPath = toPathString(path);
1148
- const pathStr = rawPath;
1149
- const numFlags = parseFlags(flags);
1150
- const fd = nextFd++;
1151
- // Check if file exists (existsSync already normalizes)
1152
- const exists = fs.existsSync(path);
1153
- // Handle O_CREAT - create file if it doesn't exist
1154
- if (numFlags & 64 && !exists) {
1155
- fs.writeFileSync(path, "");
1156
- }
1157
- else if (!exists && !(numFlags & 64)) {
1158
- throw createFsError("ENOENT", `ENOENT: no such file or directory, open '${rawPath}'`, "open", rawPath);
1159
- }
1160
- // Handle O_TRUNC - truncate file
1161
- if (numFlags & 512 && exists) {
1162
- fs.writeFileSync(path, "");
1163
- }
1164
- // Store normalized path in fd table for subsequent operations
1165
- fdTable.set(fd, { path: pathStr, flags: numFlags, position: 0 });
1166
- return fd;
1167
- },
1168
- closeSync(fd) {
1169
- if (!fdTable.has(fd)) {
1170
- throw createFsError("EBADF", "EBADF: bad file descriptor, close", "close");
1171
- }
1172
- fdTable.delete(fd);
1173
- },
1174
- readSync(fd, buffer, offset, length, position) {
1175
- const entry = fdTable.get(fd);
1176
- if (!entry) {
1177
- throw createFsError("EBADF", "EBADF: bad file descriptor, read", "read");
1178
- }
1179
- if (!canRead(entry.flags)) {
1180
- throw createFsError("EBADF", "EBADF: bad file descriptor, read", "read");
1181
- }
1182
- const content = fs.readFileSync(entry.path, "utf8");
1183
- const readOffset = offset ?? 0;
1184
- const readLength = length ?? (buffer.byteLength - readOffset);
1185
- const pos = position !== null && position !== undefined ? Number(position) : entry.position;
1186
- const toRead = content.slice(pos, pos + readLength);
1187
- const bytes = Buffer.from(toRead);
1188
- const targetBuffer = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
1189
- for (let i = 0; i < bytes.length && i < readLength; i++) {
1190
- targetBuffer[readOffset + i] = bytes[i];
1191
- }
1192
- if (position === null || position === undefined) {
1193
- entry.position += bytes.length;
1194
- }
1195
- return bytes.length;
1196
- },
1197
- writeSync(fd, buffer, offsetOrPosition, lengthOrEncoding, position) {
1198
- const entry = fdTable.get(fd);
1199
- if (!entry) {
1200
- throw createFsError("EBADF", "EBADF: bad file descriptor, write", "write");
1201
- }
1202
- // fs.writeSync
1203
- if (!canWrite(entry.flags)) {
1204
- throw createFsError("EBADF", "EBADF: bad file descriptor, write", "write");
1205
- }
1206
- // Handle string or buffer
1207
- let data;
1208
- let writePosition;
1209
- if (typeof buffer === "string") {
1210
- data = buffer;
1211
- writePosition = offsetOrPosition;
1212
- }
1213
- else {
1214
- const offset = offsetOrPosition ?? 0;
1215
- const length = (typeof lengthOrEncoding === "number" ? lengthOrEncoding : null) ?? (buffer.byteLength - offset);
1216
- const view = new Uint8Array(buffer.buffer, buffer.byteOffset + offset, length);
1217
- data = new TextDecoder().decode(view);
1218
- writePosition = position;
1219
- }
1220
- // Read existing content
1221
- let content = "";
1222
- if (fs.existsSync(entry.path)) {
1223
- content = fs.readFileSync(entry.path, "utf8");
1224
- }
1225
- // Determine write position
1226
- let writePos;
1227
- if (entry.flags & 1024) {
1228
- // O_APPEND
1229
- writePos = content.length;
1230
- }
1231
- else if (writePosition !== null && writePosition !== undefined) {
1232
- writePos = writePosition;
1233
- }
1234
- else {
1235
- writePos = entry.position;
1236
- }
1237
- // Pad with nulls if writing past end
1238
- while (content.length < writePos) {
1239
- content += "\0";
1240
- }
1241
- // Write data
1242
- const newContent = content.slice(0, writePos) + data + content.slice(writePos + data.length);
1243
- fs.writeFileSync(entry.path, newContent);
1244
- // Update position if not using explicit position
1245
- if (writePosition === null || writePosition === undefined) {
1246
- entry.position = writePos + data.length;
1247
- }
1248
- return data.length;
1249
- },
1250
- fstatSync(fd) {
1251
- const entry = fdTable.get(fd);
1252
- if (!entry) {
1253
- throw createFsError("EBADF", "EBADF: bad file descriptor, fstat", "fstat");
1254
- }
1255
- return fs.statSync(entry.path);
1256
- },
1257
- ftruncateSync(fd, len) {
1258
- const entry = fdTable.get(fd);
1259
- if (!entry) {
1260
- throw createFsError("EBADF", "EBADF: bad file descriptor, ftruncate", "ftruncate");
1261
- }
1262
- const content = fs.existsSync(entry.path)
1263
- ? fs.readFileSync(entry.path, "utf8")
1264
- : "";
1265
- const newLen = len ?? 0;
1266
- if (content.length > newLen) {
1267
- fs.writeFileSync(entry.path, content.slice(0, newLen));
1268
- }
1269
- else {
1270
- let padded = content;
1271
- while (padded.length < newLen)
1272
- padded += "\0";
1273
- fs.writeFileSync(entry.path, padded);
1274
- }
1275
- },
1276
- // fsync / fdatasync — no-op for in-memory VFS (nothing to flush to disk)
1277
- fsyncSync(fd) {
1278
- if (!fdTable.has(fd)) {
1279
- throw createFsError("EBADF", "EBADF: bad file descriptor, fsync", "fsync");
1280
- }
1281
- },
1282
- fdatasyncSync(fd) {
1283
- if (!fdTable.has(fd)) {
1284
- throw createFsError("EBADF", "EBADF: bad file descriptor, fdatasync", "fdatasync");
1285
- }
1286
- },
1287
- // readv — scatter-read into multiple buffers
1288
- readvSync(fd, buffers, position) {
1289
- const entry = fdTable.get(fd);
1290
- if (!entry) {
1291
- throw createFsError("EBADF", "EBADF: bad file descriptor, readv", "readv");
1292
- }
1293
- if (!canRead(entry.flags)) {
1294
- throw createFsError("EBADF", "EBADF: bad file descriptor, readv", "readv");
1295
- }
1296
- let totalBytesRead = 0;
1297
- for (const buffer of buffers) {
1298
- const target = buffer instanceof Uint8Array
1299
- ? buffer
1300
- : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
1301
- const bytesRead = fs.readSync(fd, target, 0, target.byteLength, position);
1302
- totalBytesRead += bytesRead;
1303
- if (position !== null && position !== undefined) {
1304
- position += bytesRead;
1305
- }
1306
- // EOF — stop filling further buffers
1307
- if (bytesRead < target.byteLength)
1308
- break;
1309
- }
1310
- return totalBytesRead;
1311
- },
1312
- // statfs — return synthetic filesystem stats for the in-memory VFS
1313
- statfsSync(path, _options) {
1314
- const pathStr = toPathString(path);
1315
- // Verify path exists
1316
- if (!fs.existsSync(pathStr)) {
1317
- throw createFsError("ENOENT", `ENOENT: no such file or directory, statfs '${pathStr}'`, "statfs", pathStr);
1318
- }
1319
- // Return synthetic stats — in-memory VFS has no real block device
1320
- return {
1321
- type: 0x01021997, // TMPFS_MAGIC
1322
- bsize: 4096,
1323
- blocks: 262144, // 1GB virtual capacity
1324
- bfree: 262144,
1325
- bavail: 262144,
1326
- files: 1000000,
1327
- ffree: 999999,
1328
- };
1329
- },
1330
- // glob — pattern matching over VFS files
1331
- globSync(pattern, _options) {
1332
- const patterns = Array.isArray(pattern) ? pattern : [pattern];
1333
- const results = [];
1334
- for (const pat of patterns) {
1335
- _globCollect(pat, results);
1336
- }
1337
- return [...new Set(results)].sort();
1338
- },
1339
- // Metadata and link sync methods — delegate to VFS via host refs
1340
- chmodSync(path, mode) {
1341
- const pathStr = toPathString(path);
1342
- const modeNum = typeof mode === "string" ? parseInt(mode, 8) : mode;
1343
- bridgeCall(() => _fs.chmod.applySyncPromise(undefined, [pathStr, modeNum]), "chmod", pathStr);
1344
- },
1345
- chownSync(path, uid, gid) {
1346
- const pathStr = toPathString(path);
1347
- bridgeCall(() => _fs.chown.applySyncPromise(undefined, [pathStr, uid, gid]), "chown", pathStr);
1348
- },
1349
- linkSync(existingPath, newPath) {
1350
- const existingStr = toPathString(existingPath);
1351
- const newStr = toPathString(newPath);
1352
- bridgeCall(() => _fs.link.applySyncPromise(undefined, [existingStr, newStr]), "link", newStr);
1353
- },
1354
- symlinkSync(target, path, _type) {
1355
- const targetStr = toPathString(target);
1356
- const pathStr = toPathString(path);
1357
- bridgeCall(() => _fs.symlink.applySyncPromise(undefined, [targetStr, pathStr]), "symlink", pathStr);
1358
- },
1359
- readlinkSync(path, _options) {
1360
- const pathStr = toPathString(path);
1361
- return bridgeCall(() => _fs.readlink.applySyncPromise(undefined, [pathStr]), "readlink", pathStr);
1362
- },
1363
- truncateSync(path, len) {
1364
- const pathStr = toPathString(path);
1365
- bridgeCall(() => _fs.truncate.applySyncPromise(undefined, [pathStr, len ?? 0]), "truncate", pathStr);
1366
- },
1367
- utimesSync(path, atime, mtime) {
1368
- const pathStr = toPathString(path);
1369
- const atimeNum = typeof atime === "number" ? atime : new Date(atime).getTime() / 1000;
1370
- const mtimeNum = typeof mtime === "number" ? mtime : new Date(mtime).getTime() / 1000;
1371
- bridgeCall(() => _fs.utimes.applySyncPromise(undefined, [pathStr, atimeNum, mtimeNum]), "utimes", pathStr);
1372
- },
1373
- // Async methods - wrap sync methods in callbacks/promises
1374
- //
1375
- // IMPORTANT: Low-level fd operations (open, close, read, write) and operations commonly
1376
- // used by streaming libraries (stat, lstat, rename, unlink) must defer their callbacks
1377
- // using queueMicrotask(). This is critical for proper stream operation.
1378
- //
1379
- // Why: Node.js streams (like tar, minipass, fs-minipass) use callback chains where each
1380
- // callback triggers the next read/write operation. These streams also rely on events like
1381
- // 'drain' to know when to resume writing. If callbacks fire synchronously, the event loop
1382
- // never gets a chance to process these events, causing streams to stall after the first chunk.
1383
- //
1384
- // Example problem without queueMicrotask:
1385
- // 1. tar calls fs.read() with callback
1386
- // 2. Our sync implementation calls callback immediately
1387
- // 3. Callback writes to stream, stream buffer fills, returns false (needs drain)
1388
- // 4. Code sets up 'drain' listener and returns
1389
- // 5. But we never returned to event loop, so 'drain' never fires
1390
- // 6. Stream hangs forever
1391
- //
1392
- // With queueMicrotask, step 2 defers the callback, allowing the event loop to process
1393
- // pending events (including 'drain') before the next operation starts.
1394
- readFile(path, options, callback) {
1395
- if (typeof options === "function") {
1396
- callback = options;
1397
- options = undefined;
1398
- }
1399
- if (callback) {
1400
- try {
1401
- callback(null, fs.readFileSync(path, options));
1402
- }
1403
- catch (e) {
1404
- callback(e);
1405
- }
1406
- }
1407
- else {
1408
- return Promise.resolve(fs.readFileSync(path, options));
1409
- }
1410
- },
1411
- writeFile(path, data, options, callback) {
1412
- if (typeof options === "function") {
1413
- callback = options;
1414
- options = undefined;
1415
- }
1416
- if (callback) {
1417
- try {
1418
- fs.writeFileSync(path, data, options);
1419
- callback(null);
1420
- }
1421
- catch (e) {
1422
- callback(e);
1423
- }
1424
- }
1425
- else {
1426
- return Promise.resolve(fs.writeFileSync(path, data, options));
1427
- }
1428
- },
1429
- appendFile(path, data, options, callback) {
1430
- if (typeof options === "function") {
1431
- callback = options;
1432
- options = undefined;
1433
- }
1434
- if (callback) {
1435
- try {
1436
- fs.appendFileSync(path, data, options);
1437
- callback(null);
1438
- }
1439
- catch (e) {
1440
- callback(e);
1441
- }
1442
- }
1443
- else {
1444
- return Promise.resolve(fs.appendFileSync(path, data, options));
1445
- }
1446
- },
1447
- readdir(path, options, callback) {
1448
- if (typeof options === "function") {
1449
- callback = options;
1450
- options = undefined;
1451
- }
1452
- if (callback) {
1453
- try {
1454
- callback(null, fs.readdirSync(path, options));
1455
- }
1456
- catch (e) {
1457
- callback(e);
1458
- }
1459
- }
1460
- else {
1461
- return Promise.resolve(fs.readdirSync(path, options));
1462
- }
1463
- },
1464
- mkdir(path, options, callback) {
1465
- if (typeof options === "function") {
1466
- callback = options;
1467
- options = undefined;
1468
- }
1469
- if (callback) {
1470
- try {
1471
- fs.mkdirSync(path, options);
1472
- callback(null);
1473
- }
1474
- catch (e) {
1475
- callback(e);
1476
- }
1477
- }
1478
- else {
1479
- fs.mkdirSync(path, options);
1480
- return Promise.resolve();
1481
- }
1482
- },
1483
- rmdir(path, callback) {
1484
- if (callback) {
1485
- // Defer callback to next tick to allow event loop to process stream events
1486
- const cb = callback;
1487
- try {
1488
- fs.rmdirSync(path);
1489
- queueMicrotask(() => cb(null));
1490
- }
1491
- catch (e) {
1492
- queueMicrotask(() => cb(e));
1493
- }
1494
- }
1495
- else {
1496
- return Promise.resolve(fs.rmdirSync(path));
1497
- }
1498
- },
1499
- // rm - remove files or directories (with recursive support)
1500
- rm(path, options, callback) {
1501
- let opts = {};
1502
- let cb;
1503
- if (typeof options === "function") {
1504
- cb = options;
1505
- }
1506
- else if (options) {
1507
- opts = options;
1508
- cb = callback;
1509
- }
1510
- else {
1511
- cb = callback;
1512
- }
1513
- const doRm = () => {
1514
- try {
1515
- const stats = fs.statSync(path);
1516
- if (stats.isDirectory()) {
1517
- if (opts.recursive) {
1518
- // Recursively remove directory contents
1519
- const entries = fs.readdirSync(path);
1520
- for (const entry of entries) {
1521
- const entryPath = path.endsWith("/") ? path + entry : path + "/" + entry;
1522
- const entryStats = fs.statSync(entryPath);
1523
- if (entryStats.isDirectory()) {
1524
- fs.rmSync(entryPath, { recursive: true });
1525
- }
1526
- else {
1527
- fs.unlinkSync(entryPath);
1528
- }
1529
- }
1530
- fs.rmdirSync(path);
1531
- }
1532
- else {
1533
- fs.rmdirSync(path);
1534
- }
1535
- }
1536
- else {
1537
- fs.unlinkSync(path);
1538
- }
1539
- }
1540
- catch (e) {
1541
- if (opts.force && e.code === "ENOENT") {
1542
- return; // Ignore ENOENT when force is true
1543
- }
1544
- throw e;
1545
- }
1546
- };
1547
- if (cb) {
1548
- // Defer callback to next tick to allow event loop to process stream events
1549
- try {
1550
- doRm();
1551
- queueMicrotask(() => cb(null));
1552
- }
1553
- catch (e) {
1554
- queueMicrotask(() => cb(e));
1555
- }
1556
- }
1557
- else {
1558
- doRm();
1559
- return Promise.resolve();
1560
- }
1561
- },
1562
- exists(path, callback) {
1563
- if (callback) {
1564
- callback(fs.existsSync(path));
1565
- }
1566
- else {
1567
- return Promise.resolve(fs.existsSync(path));
1568
- }
1569
- },
1570
- stat(path, callback) {
1571
- if (callback) {
1572
- // Defer callback to next tick to allow event loop to process stream events
1573
- const cb = callback;
1574
- try {
1575
- const stats = fs.statSync(path);
1576
- queueMicrotask(() => cb(null, stats));
1577
- }
1578
- catch (e) {
1579
- queueMicrotask(() => cb(e));
1580
- }
1581
- }
1582
- else {
1583
- return Promise.resolve(fs.statSync(path));
1584
- }
1585
- },
1586
- lstat(path, callback) {
1587
- if (callback) {
1588
- // Defer callback to next tick to allow event loop to process stream events
1589
- const cb = callback;
1590
- try {
1591
- const stats = fs.lstatSync(path);
1592
- queueMicrotask(() => cb(null, stats));
1593
- }
1594
- catch (e) {
1595
- queueMicrotask(() => cb(e));
1596
- }
1597
- }
1598
- else {
1599
- return Promise.resolve(fs.lstatSync(path));
1600
- }
1601
- },
1602
- unlink(path, callback) {
1603
- if (callback) {
1604
- // Defer callback to next tick to allow event loop to process stream events
1605
- const cb = callback;
1606
- try {
1607
- fs.unlinkSync(path);
1608
- queueMicrotask(() => cb(null));
1609
- }
1610
- catch (e) {
1611
- queueMicrotask(() => cb(e));
1612
- }
1613
- }
1614
- else {
1615
- return Promise.resolve(fs.unlinkSync(path));
1616
- }
1617
- },
1618
- rename(oldPath, newPath, callback) {
1619
- if (callback) {
1620
- // Defer callback to next tick to allow event loop to process stream events
1621
- const cb = callback;
1622
- try {
1623
- fs.renameSync(oldPath, newPath);
1624
- queueMicrotask(() => cb(null));
1625
- }
1626
- catch (e) {
1627
- queueMicrotask(() => cb(e));
1628
- }
1629
- }
1630
- else {
1631
- return Promise.resolve(fs.renameSync(oldPath, newPath));
1632
- }
1633
- },
1634
- copyFile(src, dest, callback) {
1635
- if (callback) {
1636
- try {
1637
- fs.copyFileSync(src, dest);
1638
- callback(null);
1639
- }
1640
- catch (e) {
1641
- callback(e);
1642
- }
1643
- }
1644
- else {
1645
- return Promise.resolve(fs.copyFileSync(src, dest));
1646
- }
1647
- },
1648
- cp(src, dest, options, callback) {
1649
- if (typeof options === "function") {
1650
- callback = options;
1651
- options = undefined;
1652
- }
1653
- if (callback) {
1654
- try {
1655
- fs.cpSync(src, dest, options);
1656
- callback(null);
1657
- }
1658
- catch (e) {
1659
- callback(e);
1660
- }
1661
- }
1662
- else {
1663
- return Promise.resolve(fs.cpSync(src, dest, options));
1664
- }
1665
- },
1666
- mkdtemp(prefix, options, callback) {
1667
- if (typeof options === "function") {
1668
- callback = options;
1669
- options = undefined;
1670
- }
1671
- if (callback) {
1672
- try {
1673
- callback(null, fs.mkdtempSync(prefix, options));
1674
- }
1675
- catch (e) {
1676
- callback(e);
1677
- }
1678
- }
1679
- else {
1680
- return Promise.resolve(fs.mkdtempSync(prefix, options));
1681
- }
1682
- },
1683
- opendir(path, options, callback) {
1684
- if (typeof options === "function") {
1685
- callback = options;
1686
- options = undefined;
1687
- }
1688
- if (callback) {
1689
- try {
1690
- callback(null, fs.opendirSync(path, options));
1691
- }
1692
- catch (e) {
1693
- callback(e);
1694
- }
1695
- }
1696
- else {
1697
- return Promise.resolve(fs.opendirSync(path, options));
1698
- }
1699
- },
1700
- open(path, flags, mode, callback) {
1701
- if (typeof mode === "function") {
1702
- callback = mode;
1703
- mode = undefined;
1704
- }
1705
- if (callback) {
1706
- // Defer callback to next tick to allow event loop to process stream events
1707
- const cb = callback;
1708
- try {
1709
- const fd = fs.openSync(path, flags, mode);
1710
- queueMicrotask(() => cb(null, fd));
1711
- }
1712
- catch (e) {
1713
- queueMicrotask(() => cb(e));
1714
- }
1715
- }
1716
- else {
1717
- return Promise.resolve(fs.openSync(path, flags, mode));
1718
- }
1719
- },
1720
- close(fd, callback) {
1721
- if (callback) {
1722
- // Defer callback to next tick to allow event loop to process stream events
1723
- const cb = callback;
1724
- try {
1725
- fs.closeSync(fd);
1726
- queueMicrotask(() => cb(null));
1727
- }
1728
- catch (e) {
1729
- queueMicrotask(() => cb(e));
1730
- }
1731
- }
1732
- else {
1733
- return Promise.resolve(fs.closeSync(fd));
1734
- }
1735
- },
1736
- read(fd, buffer, offset, length, position, callback) {
1737
- if (callback) {
1738
- // Defer callback to next tick to allow event loop to process stream events
1739
- const cb = callback;
1740
- try {
1741
- const bytesRead = fs.readSync(fd, buffer, offset, length, position);
1742
- queueMicrotask(() => cb(null, bytesRead, buffer));
1743
- }
1744
- catch (e) {
1745
- queueMicrotask(() => cb(e));
1746
- }
1747
- }
1748
- else {
1749
- return Promise.resolve(fs.readSync(fd, buffer, offset, length, position));
1750
- }
1751
- },
1752
- write(fd, buffer, offset, length, position, callback) {
1753
- if (typeof offset === "function") {
1754
- callback = offset;
1755
- offset = undefined;
1756
- length = undefined;
1757
- position = undefined;
1758
- }
1759
- else if (typeof length === "function") {
1760
- callback = length;
1761
- length = undefined;
1762
- position = undefined;
1763
- }
1764
- else if (typeof position === "function") {
1765
- callback = position;
1766
- position = undefined;
1767
- }
1768
- if (callback) {
1769
- // Defer callback to next tick to allow event loop to process stream events
1770
- const cb = callback;
1771
- try {
1772
- const bytesWritten = fs.writeSync(fd, buffer, offset, length, position);
1773
- queueMicrotask(() => cb(null, bytesWritten));
1774
- }
1775
- catch (e) {
1776
- queueMicrotask(() => cb(e));
1777
- }
1778
- }
1779
- else {
1780
- return Promise.resolve(fs.writeSync(fd, buffer, offset, length, position));
1781
- }
1782
- },
1783
- // writev - write multiple buffers to a file descriptor
1784
- writev(fd, buffers, position, callback) {
1785
- if (typeof position === "function") {
1786
- callback = position;
1787
- position = null;
1788
- }
1789
- if (callback) {
1790
- try {
1791
- const bytesWritten = fs.writevSync(fd, buffers, position);
1792
- callback(null, bytesWritten, buffers);
1793
- }
1794
- catch (e) {
1795
- callback(e);
1796
- }
1797
- }
1798
- },
1799
- writevSync(fd, buffers, position) {
1800
- let totalBytesWritten = 0;
1801
- for (const buffer of buffers) {
1802
- const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
1803
- totalBytesWritten += fs.writeSync(fd, bytes, 0, bytes.length, position);
1804
- if (position !== null && position !== undefined) {
1805
- position += bytes.length;
1806
- }
1807
- }
1808
- return totalBytesWritten;
1809
- },
1810
- fstat(fd, callback) {
1811
- if (callback) {
1812
- try {
1813
- callback(null, fs.fstatSync(fd));
1814
- }
1815
- catch (e) {
1816
- callback(e);
1817
- }
1818
- }
1819
- else {
1820
- return Promise.resolve(fs.fstatSync(fd));
1821
- }
1822
- },
1823
- // fsync / fdatasync async callback forms
1824
- fsync(fd, callback) {
1825
- if (callback) {
1826
- try {
1827
- fs.fsyncSync(fd);
1828
- callback(null);
1829
- }
1830
- catch (e) {
1831
- callback(e);
1832
- }
1833
- }
1834
- else {
1835
- return Promise.resolve(fs.fsyncSync(fd));
1836
- }
1837
- },
1838
- fdatasync(fd, callback) {
1839
- if (callback) {
1840
- try {
1841
- fs.fdatasyncSync(fd);
1842
- callback(null);
1843
- }
1844
- catch (e) {
1845
- callback(e);
1846
- }
1847
- }
1848
- else {
1849
- return Promise.resolve(fs.fdatasyncSync(fd));
1850
- }
1851
- },
1852
- // readv async callback form
1853
- readv(fd, buffers, position, callback) {
1854
- if (typeof position === "function") {
1855
- callback = position;
1856
- position = null;
1857
- }
1858
- if (callback) {
1859
- try {
1860
- const bytesRead = fs.readvSync(fd, buffers, position);
1861
- callback(null, bytesRead, buffers);
1862
- }
1863
- catch (e) {
1864
- callback(e);
1865
- }
1866
- }
1867
- },
1868
- // statfs async callback form
1869
- statfs(path, options, callback) {
1870
- if (typeof options === "function") {
1871
- callback = options;
1872
- options = undefined;
1873
- }
1874
- if (callback) {
1875
- try {
1876
- callback(null, fs.statfsSync(path, options));
1877
- }
1878
- catch (e) {
1879
- callback(e);
1880
- }
1881
- }
1882
- else {
1883
- return Promise.resolve(fs.statfsSync(path, options));
1884
- }
1885
- },
1886
- // glob async callback form
1887
- glob(pattern, options, callback) {
1888
- if (typeof options === "function") {
1889
- callback = options;
1890
- options = undefined;
1891
- }
1892
- if (callback) {
1893
- try {
1894
- callback(null, fs.globSync(pattern, options));
1895
- }
1896
- catch (e) {
1897
- callback(e);
1898
- }
1899
- }
1900
- },
1901
- // fs.promises API
1902
- // Note: Using async functions to properly catch sync errors and return rejected promises
1903
- promises: {
1904
- async readFile(path, options) {
1905
- return fs.readFileSync(path, options);
1906
- },
1907
- async writeFile(path, data, options) {
1908
- return fs.writeFileSync(path, data, options);
1909
- },
1910
- async appendFile(path, data, options) {
1911
- return fs.appendFileSync(path, data, options);
1912
- },
1913
- async readdir(path, options) {
1914
- return fs.readdirSync(path, options);
1915
- },
1916
- async mkdir(path, options) {
1917
- return fs.mkdirSync(path, options);
1918
- },
1919
- async rmdir(path) {
1920
- return fs.rmdirSync(path);
1921
- },
1922
- async stat(path) {
1923
- return fs.statSync(path);
1924
- },
1925
- async lstat(path) {
1926
- return fs.lstatSync(path);
1927
- },
1928
- async unlink(path) {
1929
- return fs.unlinkSync(path);
1930
- },
1931
- async rename(oldPath, newPath) {
1932
- return fs.renameSync(oldPath, newPath);
1933
- },
1934
- async copyFile(src, dest) {
1935
- return fs.copyFileSync(src, dest);
1936
- },
1937
- async cp(src, dest, options) {
1938
- return fs.cpSync(src, dest, options);
1939
- },
1940
- async mkdtemp(prefix, options) {
1941
- return fs.mkdtempSync(prefix, options);
1942
- },
1943
- async opendir(path, options) {
1944
- return fs.opendirSync(path, options);
1945
- },
1946
- async statfs(path, options) {
1947
- return fs.statfsSync(path, options);
1948
- },
1949
- async glob(pattern, _options) {
1950
- return fs.globSync(pattern, _options);
1951
- },
1952
- async access(path) {
1953
- if (!fs.existsSync(path)) {
1954
- throw createFsError("ENOENT", `ENOENT: no such file or directory, access '${path}'`, "access", path);
1955
- }
1956
- },
1957
- async rm(path, options) {
1958
- return fs.rmSync(path, options);
1959
- },
1960
- async chmod(path, mode) {
1961
- return fs.chmodSync(path, mode);
1962
- },
1963
- async chown(path, uid, gid) {
1964
- return fs.chownSync(path, uid, gid);
1965
- },
1966
- async link(existingPath, newPath) {
1967
- return fs.linkSync(existingPath, newPath);
1968
- },
1969
- async symlink(target, path) {
1970
- return fs.symlinkSync(target, path);
1971
- },
1972
- async readlink(path) {
1973
- return fs.readlinkSync(path);
1974
- },
1975
- async truncate(path, len) {
1976
- return fs.truncateSync(path, len);
1977
- },
1978
- async utimes(path, atime, mtime) {
1979
- return fs.utimesSync(path, atime, mtime);
1980
- },
1981
- },
1982
- // Compatibility methods
1983
- accessSync(path) {
1984
- // existsSync already normalizes the path
1985
- if (!fs.existsSync(path)) {
1986
- throw createFsError("ENOENT", `ENOENT: no such file or directory, access '${path}'`, "access", path);
1987
- }
1988
- },
1989
- access(path, mode, callback) {
1990
- if (typeof mode === "function") {
1991
- callback = mode;
1992
- mode = undefined;
1993
- }
1994
- if (callback) {
1995
- try {
1996
- fs.accessSync(path);
1997
- callback(null);
1998
- }
1999
- catch (e) {
2000
- callback(e);
2001
- }
2002
- }
2003
- else {
2004
- return fs.promises.access(path);
2005
- }
2006
- },
2007
- realpathSync: Object.assign(function realpathSync(path) {
2008
- // Resolve symlinks by walking each path component via lstat + readlink
2009
- const MAX_SYMLINK_DEPTH = 40;
2010
- let symlinksFollowed = 0;
2011
- const raw = toPathString(path);
2012
- // Build initial queue: normalize . and .. segments
2013
- const pending = [];
2014
- for (const seg of raw.split("/")) {
2015
- if (!seg || seg === ".")
2016
- continue;
2017
- if (seg === "..") {
2018
- if (pending.length > 0)
2019
- pending.pop();
2020
- }
2021
- else
2022
- pending.push(seg);
2023
- }
2024
- // Walk each component, resolving symlinks via a queue
2025
- const resolved = [];
2026
- while (pending.length > 0) {
2027
- const seg = pending.shift();
2028
- if (seg === ".")
2029
- continue;
2030
- if (seg === "..") {
2031
- if (resolved.length > 0)
2032
- resolved.pop();
2033
- continue;
2034
- }
2035
- resolved.push(seg);
2036
- const currentPath = "/" + resolved.join("/");
2037
- try {
2038
- const stat = fs.lstatSync(currentPath);
2039
- if (stat.isSymbolicLink()) {
2040
- if (++symlinksFollowed > MAX_SYMLINK_DEPTH) {
2041
- const err = new Error(`ELOOP: too many levels of symbolic links, realpath '${raw}'`);
2042
- err.code = "ELOOP";
2043
- err.syscall = "realpath";
2044
- err.path = raw;
2045
- throw err;
2046
- }
2047
- const target = fs.readlinkSync(currentPath);
2048
- // Prepend target segments to pending for re-resolution
2049
- const targetSegs = target.split("/").filter(Boolean);
2050
- if (target.startsWith("/")) {
2051
- // Absolute symlink — restart from root
2052
- resolved.length = 0;
2053
- }
2054
- else {
2055
- // Relative symlink — drop current component
2056
- resolved.pop();
2057
- }
2058
- // Prepend target segments so they're processed next
2059
- pending.unshift(...targetSegs);
2060
- }
2061
- }
2062
- catch (e) {
2063
- const err = e;
2064
- if (err.code === "ELOOP")
2065
- throw e;
2066
- if (err.code === "ENOENT" || err.code === "ENOTDIR") {
2067
- const enoent = new Error(`ENOENT: no such file or directory, realpath '${raw}'`);
2068
- enoent.code = "ENOENT";
2069
- enoent.syscall = "realpath";
2070
- enoent.path = raw;
2071
- throw enoent;
2072
- }
2073
- break;
2074
- }
2075
- }
2076
- return "/" + resolved.join("/") || "/";
2077
- }, {
2078
- native(path) {
2079
- return fs.realpathSync(path);
2080
- }
2081
- }),
2082
- realpath: Object.assign(function realpath(path, callback) {
2083
- if (callback) {
2084
- callback(null, fs.realpathSync(path));
2085
- }
2086
- else {
2087
- return Promise.resolve(fs.realpathSync(path));
2088
- }
2089
- }, {
2090
- native(path, callback) {
2091
- if (callback) {
2092
- callback(null, fs.realpathSync.native(path));
2093
- }
2094
- else {
2095
- return Promise.resolve(fs.realpathSync.native(path));
2096
- }
2097
- }
2098
- }),
2099
- createReadStream(path, options) {
2100
- const pathStr = typeof path === "string" ? path : path instanceof Buffer ? path.toString() : String(path);
2101
- const opts = typeof options === "string" ? { encoding: options } : options;
2102
- // Use type assertion since our ReadStream has all the methods npm needs
2103
- // but not all the complex overloaded signatures of the full Node.js interface
2104
- return new ReadStream(pathStr, opts);
2105
- },
2106
- createWriteStream(path, options) {
2107
- const pathStr = typeof path === "string" ? path : path instanceof Buffer ? path.toString() : String(path);
2108
- const opts = typeof options === "string" ? { encoding: options } : options;
2109
- // Use type assertion since our WriteStream has all the methods npm needs
2110
- // but not all the complex overloaded signatures of the full Node.js interface
2111
- return new WriteStream(pathStr, opts);
2112
- },
2113
- // Unsupported fs APIs — watch requires kernel-level inotify, use polling instead
2114
- watch(..._args) {
2115
- throw new Error("fs.watch is not supported in sandbox — use polling");
2116
- },
2117
- watchFile(..._args) {
2118
- throw new Error("fs.watchFile is not supported in sandbox — use polling");
2119
- },
2120
- unwatchFile(..._args) {
2121
- throw new Error("fs.unwatchFile is not supported in sandbox — use polling");
2122
- },
2123
- chmod(path, mode, callback) {
2124
- if (callback) {
2125
- try {
2126
- fs.chmodSync(path, mode);
2127
- callback(null);
2128
- }
2129
- catch (e) {
2130
- callback(e);
2131
- }
2132
- }
2133
- else {
2134
- return Promise.resolve(fs.chmodSync(path, mode));
2135
- }
2136
- },
2137
- chown(path, uid, gid, callback) {
2138
- if (callback) {
2139
- try {
2140
- fs.chownSync(path, uid, gid);
2141
- callback(null);
2142
- }
2143
- catch (e) {
2144
- callback(e);
2145
- }
2146
- }
2147
- else {
2148
- return Promise.resolve(fs.chownSync(path, uid, gid));
2149
- }
2150
- },
2151
- link(existingPath, newPath, callback) {
2152
- if (callback) {
2153
- try {
2154
- fs.linkSync(existingPath, newPath);
2155
- callback(null);
2156
- }
2157
- catch (e) {
2158
- callback(e);
2159
- }
2160
- }
2161
- else {
2162
- return Promise.resolve(fs.linkSync(existingPath, newPath));
2163
- }
2164
- },
2165
- symlink(target, path, typeOrCb, callback) {
2166
- if (typeof typeOrCb === "function") {
2167
- callback = typeOrCb;
2168
- }
2169
- if (callback) {
2170
- try {
2171
- fs.symlinkSync(target, path);
2172
- callback(null);
2173
- }
2174
- catch (e) {
2175
- callback(e);
2176
- }
2177
- }
2178
- else {
2179
- return Promise.resolve(fs.symlinkSync(target, path));
2180
- }
2181
- },
2182
- readlink(path, optionsOrCb, callback) {
2183
- if (typeof optionsOrCb === "function") {
2184
- callback = optionsOrCb;
2185
- }
2186
- if (callback) {
2187
- try {
2188
- callback(null, fs.readlinkSync(path));
2189
- }
2190
- catch (e) {
2191
- callback(e);
2192
- }
2193
- }
2194
- else {
2195
- return Promise.resolve(fs.readlinkSync(path));
2196
- }
2197
- },
2198
- truncate(path, lenOrCb, callback) {
2199
- if (typeof lenOrCb === "function") {
2200
- callback = lenOrCb;
2201
- lenOrCb = 0;
2202
- }
2203
- if (callback) {
2204
- try {
2205
- fs.truncateSync(path, lenOrCb);
2206
- callback(null);
2207
- }
2208
- catch (e) {
2209
- callback(e);
2210
- }
2211
- }
2212
- else {
2213
- return Promise.resolve(fs.truncateSync(path, lenOrCb));
2214
- }
2215
- },
2216
- utimes(path, atime, mtime, callback) {
2217
- if (callback) {
2218
- try {
2219
- fs.utimesSync(path, atime, mtime);
2220
- callback(null);
2221
- }
2222
- catch (e) {
2223
- callback(e);
2224
- }
2225
- }
2226
- else {
2227
- return Promise.resolve(fs.utimesSync(path, atime, mtime));
2228
- }
2229
- },
2230
- };
2231
- // Wire late-bound glob helpers to the fs object
2232
- _globReadDir = (dir) => fs.readdirSync(dir);
2233
- _globStat = (path) => fs.statSync(path);
2234
- // Export the fs module
2235
- export default fs;