@secure-exec/wasmvm 0.2.0-rc.1

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 (70) hide show
  1. package/LICENSE +191 -0
  2. package/dist/browser-driver.d.ts +68 -0
  3. package/dist/browser-driver.d.ts.map +1 -0
  4. package/dist/browser-driver.js +293 -0
  5. package/dist/browser-driver.js.map +1 -0
  6. package/dist/driver.d.ts +43 -0
  7. package/dist/driver.d.ts.map +1 -0
  8. package/dist/driver.js +1420 -0
  9. package/dist/driver.js.map +1 -0
  10. package/dist/fd-table.d.ts +67 -0
  11. package/dist/fd-table.d.ts.map +1 -0
  12. package/dist/fd-table.js +171 -0
  13. package/dist/fd-table.js.map +1 -0
  14. package/dist/index.d.ts +26 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +19 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/kernel-worker.d.ts +14 -0
  19. package/dist/kernel-worker.d.ts.map +1 -0
  20. package/dist/kernel-worker.js +1205 -0
  21. package/dist/kernel-worker.js.map +1 -0
  22. package/dist/module-cache.d.ts +21 -0
  23. package/dist/module-cache.d.ts.map +1 -0
  24. package/dist/module-cache.js +51 -0
  25. package/dist/module-cache.js.map +1 -0
  26. package/dist/permission-check.d.ts +36 -0
  27. package/dist/permission-check.d.ts.map +1 -0
  28. package/dist/permission-check.js +84 -0
  29. package/dist/permission-check.js.map +1 -0
  30. package/dist/ring-buffer.d.ts +64 -0
  31. package/dist/ring-buffer.d.ts.map +1 -0
  32. package/dist/ring-buffer.js +160 -0
  33. package/dist/ring-buffer.js.map +1 -0
  34. package/dist/syscall-rpc.d.ts +95 -0
  35. package/dist/syscall-rpc.d.ts.map +1 -0
  36. package/dist/syscall-rpc.js +48 -0
  37. package/dist/syscall-rpc.js.map +1 -0
  38. package/dist/user.d.ts +58 -0
  39. package/dist/user.d.ts.map +1 -0
  40. package/dist/user.js +143 -0
  41. package/dist/user.js.map +1 -0
  42. package/dist/wasi-constants.d.ts +77 -0
  43. package/dist/wasi-constants.d.ts.map +1 -0
  44. package/dist/wasi-constants.js +122 -0
  45. package/dist/wasi-constants.js.map +1 -0
  46. package/dist/wasi-file-io.d.ts +50 -0
  47. package/dist/wasi-file-io.d.ts.map +1 -0
  48. package/dist/wasi-file-io.js +10 -0
  49. package/dist/wasi-file-io.js.map +1 -0
  50. package/dist/wasi-polyfill.d.ts +368 -0
  51. package/dist/wasi-polyfill.d.ts.map +1 -0
  52. package/dist/wasi-polyfill.js +1438 -0
  53. package/dist/wasi-polyfill.js.map +1 -0
  54. package/dist/wasi-process-io.d.ts +35 -0
  55. package/dist/wasi-process-io.d.ts.map +1 -0
  56. package/dist/wasi-process-io.js +11 -0
  57. package/dist/wasi-process-io.js.map +1 -0
  58. package/dist/wasi-types.d.ts +175 -0
  59. package/dist/wasi-types.d.ts.map +1 -0
  60. package/dist/wasi-types.js +68 -0
  61. package/dist/wasi-types.js.map +1 -0
  62. package/dist/wasm-magic.d.ts +12 -0
  63. package/dist/wasm-magic.d.ts.map +1 -0
  64. package/dist/wasm-magic.js +53 -0
  65. package/dist/wasm-magic.js.map +1 -0
  66. package/dist/worker-adapter.d.ts +32 -0
  67. package/dist/worker-adapter.d.ts.map +1 -0
  68. package/dist/worker-adapter.js +147 -0
  69. package/dist/worker-adapter.js.map +1 -0
  70. package/package.json +35 -0
@@ -0,0 +1,1438 @@
1
+ /**
2
+ * WASI polyfill for wasi_snapshot_preview1.
3
+ *
4
+ * Implements all 46 wasi_snapshot_preview1 functions:
5
+ * - Core fd and prestat operations (US-007)
6
+ * - Path, directory, and filestat operations (US-008)
7
+ * - Args, env, clock, random, proc_exit, and remaining stubs (US-009)
8
+ */
9
+ import { FILETYPE_UNKNOWN, FILETYPE_REGULAR_FILE, FILETYPE_DIRECTORY, FILETYPE_CHARACTER_DEVICE, FILETYPE_SYMBOLIC_LINK, RIGHT_FD_DATASYNC, RIGHT_FD_READ, RIGHT_FD_SEEK, RIGHT_FD_FDSTAT_SET_FLAGS, RIGHT_FD_SYNC, RIGHT_FD_TELL, RIGHT_FD_WRITE, RIGHT_FD_ADVISE, RIGHT_FD_ALLOCATE, RIGHT_FD_READDIR, RIGHT_FD_FILESTAT_GET, RIGHT_FD_FILESTAT_SET_SIZE, RIGHT_FD_FILESTAT_SET_TIMES, RIGHT_PATH_CREATE_DIRECTORY, RIGHT_PATH_CREATE_FILE, RIGHT_PATH_LINK_SOURCE, RIGHT_PATH_LINK_TARGET, RIGHT_PATH_OPEN, RIGHT_PATH_READLINK, RIGHT_PATH_RENAME_SOURCE, RIGHT_PATH_RENAME_TARGET, RIGHT_PATH_FILESTAT_GET, RIGHT_PATH_FILESTAT_SET_SIZE, RIGHT_PATH_FILESTAT_SET_TIMES, RIGHT_PATH_SYMLINK, RIGHT_PATH_REMOVE_DIRECTORY, RIGHT_PATH_UNLINK_FILE, RIGHT_POLL_FD_READWRITE, ERRNO_SUCCESS, ERRNO_EBADF, ERRNO_EINVAL, } from './wasi-constants.js';
10
+ import { VfsError } from './wasi-types.js';
11
+ // Additional WASI errno codes
12
+ export const ERRNO_ESPIPE = 70;
13
+ export const ERRNO_EISDIR = 31;
14
+ export const ERRNO_ENOMEM = 48;
15
+ export const ERRNO_ENOSYS = 52;
16
+ export const ERRNO_ENOENT = 44;
17
+ export const ERRNO_EEXIST = 20;
18
+ export const ERRNO_ENOTDIR = 54;
19
+ export const ERRNO_ENOTEMPTY = 55;
20
+ export const ERRNO_ELOOP = 36;
21
+ export const ERRNO_EACCES = 2;
22
+ export const ERRNO_EPERM = 63;
23
+ export const ERRNO_EIO = 29;
24
+ // Map VfsError codes to WASI errno numbers
25
+ const ERRNO_MAP = {
26
+ ENOENT: 44,
27
+ EEXIST: 20,
28
+ ENOTDIR: 54,
29
+ EISDIR: 31,
30
+ ENOTEMPTY: 55,
31
+ EACCES: 2,
32
+ EBADF: 8,
33
+ EINVAL: 28,
34
+ EPERM: 63,
35
+ };
36
+ /** Map a caught error to a WASI errno. VfsError maps via code; unknown errors → EIO. */
37
+ function vfsErrorToErrno(e) {
38
+ if (e instanceof VfsError) {
39
+ return ERRNO_MAP[e.code] ?? ERRNO_EIO;
40
+ }
41
+ return ERRNO_EIO;
42
+ }
43
+ // Re-export for convenience
44
+ export { ERRNO_SUCCESS, ERRNO_EBADF, ERRNO_EINVAL };
45
+ // WASI seek whence values
46
+ const WHENCE_SET = 0;
47
+ const WHENCE_CUR = 1;
48
+ const WHENCE_END = 2;
49
+ // WASI lookup flags
50
+ const LOOKUP_SYMLINK_FOLLOW = 1;
51
+ // WASI open flags (oflags)
52
+ const OFLAG_CREAT = 1;
53
+ const OFLAG_DIRECTORY = 2;
54
+ const OFLAG_EXCL = 4;
55
+ const OFLAG_TRUNC = 8;
56
+ // WASI fstflags (for set_times)
57
+ const FSTFLAG_ATIM = 1;
58
+ const FSTFLAG_ATIM_NOW = 2;
59
+ const FSTFLAG_MTIM = 4;
60
+ const FSTFLAG_MTIM_NOW = 8;
61
+ // WASI preopentype
62
+ const PREOPENTYPE_DIR = 0;
63
+ // WASI clock IDs
64
+ const CLOCKID_REALTIME = 0;
65
+ const CLOCKID_MONOTONIC = 1;
66
+ const CLOCKID_PROCESS_CPUTIME_ID = 2;
67
+ const CLOCKID_THREAD_CPUTIME_ID = 3;
68
+ // WASI subscription/event types for poll_oneoff
69
+ const EVENTTYPE_CLOCK = 0;
70
+ const EVENTTYPE_FD_READ = 1;
71
+ const EVENTTYPE_FD_WRITE = 2;
72
+ /** Normalize a POSIX path — resolve `.` and `..`, collapse slashes. */
73
+ function normalizePath(path) {
74
+ const parts = path.split('/');
75
+ const resolved = [];
76
+ for (const p of parts) {
77
+ if (p === '' || p === '.')
78
+ continue;
79
+ if (p === '..') {
80
+ resolved.pop();
81
+ continue;
82
+ }
83
+ resolved.push(p);
84
+ }
85
+ return '/' + resolved.join('/');
86
+ }
87
+ /**
88
+ * Exception thrown by proc_exit to terminate WASM execution.
89
+ * Callers should catch this to extract the exit code.
90
+ */
91
+ export class WasiProcExit extends Error {
92
+ exitCode;
93
+ constructor(exitCode) {
94
+ super(`proc_exit(${exitCode})`);
95
+ this.exitCode = exitCode;
96
+ this.name = 'WasiProcExit';
97
+ }
98
+ }
99
+ // All rights for files
100
+ const RIGHTS_FILE_BASE = RIGHT_FD_DATASYNC | RIGHT_FD_READ | RIGHT_FD_SEEK |
101
+ RIGHT_FD_FDSTAT_SET_FLAGS | RIGHT_FD_SYNC | RIGHT_FD_TELL | RIGHT_FD_WRITE |
102
+ RIGHT_FD_ADVISE | RIGHT_FD_ALLOCATE | RIGHT_FD_FILESTAT_GET |
103
+ RIGHT_FD_FILESTAT_SET_SIZE | RIGHT_FD_FILESTAT_SET_TIMES |
104
+ RIGHT_POLL_FD_READWRITE;
105
+ // All rights for directories
106
+ const RIGHTS_DIR_BASE = RIGHT_FD_FDSTAT_SET_FLAGS | RIGHT_FD_SYNC |
107
+ RIGHT_FD_READDIR | RIGHT_PATH_CREATE_DIRECTORY | RIGHT_PATH_CREATE_FILE |
108
+ RIGHT_PATH_LINK_SOURCE | RIGHT_PATH_LINK_TARGET | RIGHT_PATH_OPEN |
109
+ RIGHT_PATH_READLINK | RIGHT_PATH_RENAME_SOURCE | RIGHT_PATH_RENAME_TARGET |
110
+ RIGHT_PATH_FILESTAT_GET | RIGHT_PATH_FILESTAT_SET_SIZE |
111
+ RIGHT_PATH_FILESTAT_SET_TIMES | RIGHT_PATH_SYMLINK |
112
+ RIGHT_PATH_REMOVE_DIRECTORY | RIGHT_PATH_UNLINK_FILE |
113
+ RIGHT_FD_FILESTAT_GET | RIGHT_FD_FILESTAT_SET_TIMES;
114
+ // Files opened from a pre-opened directory can inherit these rights
115
+ const RIGHTS_DIR_INHERITING = RIGHTS_FILE_BASE | RIGHTS_DIR_BASE;
116
+ /**
117
+ * Concatenate multiple Uint8Array chunks into one.
118
+ */
119
+ function concatBytes(arrays) {
120
+ if (arrays.length === 0)
121
+ return new Uint8Array(0);
122
+ if (arrays.length === 1)
123
+ return arrays[0];
124
+ const total = arrays.reduce((sum, a) => sum + a.length, 0);
125
+ const result = new Uint8Array(total);
126
+ let offset = 0;
127
+ for (const a of arrays) {
128
+ result.set(a, offset);
129
+ offset += a.length;
130
+ }
131
+ return result;
132
+ }
133
+ /**
134
+ * WASI polyfill implementing wasi_snapshot_preview1.
135
+ *
136
+ * Phase 1: Core fd and prestat operations (US-007).
137
+ * Additional operations added in US-008, US-009.
138
+ */
139
+ export class WasiPolyfill {
140
+ fdTable;
141
+ vfs;
142
+ args;
143
+ env;
144
+ memory;
145
+ exitCode;
146
+ _fileIO;
147
+ _processIO;
148
+ _stdinData;
149
+ _stdinOffset;
150
+ _stdinReader;
151
+ _stdoutWriter;
152
+ _stderrWriter;
153
+ _sleepHook;
154
+ _stdoutChunks;
155
+ _stderrChunks;
156
+ _preopens;
157
+ constructor(fdTable, vfs, options) {
158
+ this.fdTable = fdTable;
159
+ this.vfs = vfs;
160
+ this._fileIO = options.fileIO;
161
+ this.args = options.args ?? [];
162
+ this.env = options.env ?? {};
163
+ this._processIO = options.processIO;
164
+ this.memory = options.memory ?? null;
165
+ this.exitCode = null;
166
+ // Stdin
167
+ if (typeof options.stdin === 'string') {
168
+ this._stdinData = new TextEncoder().encode(options.stdin);
169
+ }
170
+ else {
171
+ this._stdinData = options.stdin ?? null;
172
+ }
173
+ this._stdinOffset = 0;
174
+ // Streaming I/O callbacks (for parallel pipelines with ring buffers)
175
+ this._stdinReader = null;
176
+ this._stdoutWriter = null;
177
+ this._stderrWriter = null;
178
+ this._sleepHook = null;
179
+ // Collected output
180
+ this._stdoutChunks = [];
181
+ this._stderrChunks = [];
182
+ // Pre-opened directories: fd -> path
183
+ this._preopens = new Map();
184
+ this._setupPreopens();
185
+ }
186
+ _setupPreopens() {
187
+ const fd = this.fdTable.open({ type: 'preopen', path: '/' }, {
188
+ filetype: FILETYPE_DIRECTORY,
189
+ rightsBase: RIGHTS_DIR_BASE,
190
+ rightsInheriting: RIGHTS_DIR_INHERITING,
191
+ fdflags: 0,
192
+ path: '/',
193
+ });
194
+ this._preopens.set(fd, '/');
195
+ }
196
+ /**
197
+ * Set the WASM memory reference (call after WebAssembly.instantiate).
198
+ */
199
+ setMemory(memory) {
200
+ this.memory = memory;
201
+ }
202
+ /**
203
+ * Set a blocking stdin reader for parallel pipeline mode.
204
+ * The reader function should have signature: (buf, offset, length) => bytesRead
205
+ * Returns 0 on EOF.
206
+ */
207
+ setStdinReader(reader) {
208
+ this._stdinReader = reader;
209
+ }
210
+ /**
211
+ * Set a blocking stdout writer for parallel pipeline mode.
212
+ * The writer function should have signature: (buf, offset, length) => void
213
+ */
214
+ setStdoutWriter(writer) {
215
+ this._stdoutWriter = writer;
216
+ }
217
+ /**
218
+ * Set a blocking stderr writer for streaming mode.
219
+ * The writer function should have signature: (buf, offset, length) => void
220
+ */
221
+ setStderrWriter(writer) {
222
+ this._stderrWriter = writer;
223
+ }
224
+ /** Set a hook to run while clock sleeps block in poll_oneoff. */
225
+ setSleepHook(hook) {
226
+ this._sleepHook = hook;
227
+ }
228
+ /** Append raw data to the stdout collection (used by inline child execution). */
229
+ appendStdout(data) {
230
+ if (data.length > 0) {
231
+ this._stdoutChunks.push(data.slice());
232
+ }
233
+ }
234
+ /** Append raw data to the stderr collection (used by inline child execution). */
235
+ appendStderr(data) {
236
+ if (data.length > 0) {
237
+ this._stderrChunks.push(data.slice());
238
+ }
239
+ }
240
+ /** Get collected stdout as Uint8Array. */
241
+ get stdout() {
242
+ return concatBytes(this._stdoutChunks);
243
+ }
244
+ /** Get collected stderr as Uint8Array. */
245
+ get stderr() {
246
+ return concatBytes(this._stderrChunks);
247
+ }
248
+ /** Get collected stdout as string. */
249
+ get stdoutString() {
250
+ return new TextDecoder().decode(this.stdout);
251
+ }
252
+ /** Get collected stderr as string. */
253
+ get stderrString() {
254
+ return new TextDecoder().decode(this.stderr);
255
+ }
256
+ // --- Memory helpers ---
257
+ _view() {
258
+ return new DataView(this.memory.buffer);
259
+ }
260
+ _bytes() {
261
+ return new Uint8Array(this.memory.buffer);
262
+ }
263
+ /**
264
+ * Read an array of iovec structs from WASM memory.
265
+ * Each iovec is { buf: u32, buf_len: u32 } = 8 bytes.
266
+ */
267
+ _readIovecs(iovs_ptr, iovs_len) {
268
+ const view = this._view();
269
+ const iovecs = [];
270
+ for (let i = 0; i < iovs_len; i++) {
271
+ const base = iovs_ptr + i * 8;
272
+ iovecs.push({
273
+ buf: view.getUint32(base, true),
274
+ buf_len: view.getUint32(base + 4, true),
275
+ });
276
+ }
277
+ return iovecs;
278
+ }
279
+ // --- Core FD operations ---
280
+ /**
281
+ * Read from a file descriptor into iovec buffers.
282
+ * Handles stdio (stdin), VFS files, and pipes.
283
+ */
284
+ fd_read(fd, iovs_ptr, iovs_len, nread_ptr) {
285
+ const entry = this.fdTable.get(fd);
286
+ if (!entry)
287
+ return ERRNO_EBADF;
288
+ if (!(entry.rightsBase & RIGHT_FD_READ))
289
+ return ERRNO_EBADF;
290
+ const iovecs = this._readIovecs(iovs_ptr, iovs_len);
291
+ const mem = this._bytes();
292
+ let totalRead = 0;
293
+ const resource = entry.resource;
294
+ if (resource.type === 'stdio' && resource.name === 'stdin') {
295
+ if (this._stdinReader) {
296
+ // Streaming mode: read from ring buffer (blocks via Atomics.wait)
297
+ for (const iov of iovecs) {
298
+ if (iov.buf_len === 0)
299
+ continue;
300
+ const tmpBuf = new Uint8Array(iov.buf_len);
301
+ const n = this._stdinReader(tmpBuf, 0, iov.buf_len);
302
+ if (n <= 0)
303
+ break; // EOF
304
+ mem.set(tmpBuf.subarray(0, n), iov.buf);
305
+ totalRead += n;
306
+ if (n < iov.buf_len)
307
+ break; // Short read -- don't block further
308
+ }
309
+ }
310
+ else {
311
+ // Buffered mode: read from pre-loaded stdin data
312
+ if (!this._stdinData || this._stdinOffset >= this._stdinData.length) {
313
+ this._view().setUint32(nread_ptr, 0, true);
314
+ return ERRNO_SUCCESS;
315
+ }
316
+ for (const iov of iovecs) {
317
+ const remaining = this._stdinData.length - this._stdinOffset;
318
+ if (remaining <= 0)
319
+ break;
320
+ const n = Math.min(iov.buf_len, remaining);
321
+ mem.set(this._stdinData.subarray(this._stdinOffset, this._stdinOffset + n), iov.buf);
322
+ this._stdinOffset += n;
323
+ totalRead += n;
324
+ }
325
+ }
326
+ }
327
+ else if (resource.type === 'vfsFile') {
328
+ // Delegate to kernel file I/O bridge
329
+ const totalRequested = iovecs.reduce((sum, iov) => sum + iov.buf_len, 0);
330
+ const result = this._fileIO.fdRead(fd, totalRequested);
331
+ if (result.errno !== ERRNO_SUCCESS)
332
+ return result.errno;
333
+ // Scatter data into iovecs
334
+ let offset = 0;
335
+ for (const iov of iovecs) {
336
+ const remaining = result.data.length - offset;
337
+ if (remaining <= 0)
338
+ break;
339
+ const n = Math.min(iov.buf_len, remaining);
340
+ mem.set(result.data.subarray(offset, offset + n), iov.buf);
341
+ offset += n;
342
+ totalRead += n;
343
+ }
344
+ }
345
+ else if (resource.type === 'pipe') {
346
+ const pipe = resource.pipe;
347
+ if (pipe && pipe.buffer) {
348
+ // Assert: only one reader may consume from a pipe's read end.
349
+ // If a different fd already claimed this pipe, throw to prevent
350
+ // silent data corruption from double-consumption.
351
+ if (pipe._readerId !== undefined && pipe._readerId !== fd) {
352
+ throw new Error(`Pipe read end consumed by multiple readers (fd ${pipe._readerId} and fd ${fd})`);
353
+ }
354
+ pipe._readerId = fd;
355
+ for (const iov of iovecs) {
356
+ const remaining = pipe.writeOffset - pipe.readOffset;
357
+ if (remaining <= 0)
358
+ break;
359
+ const n = Math.min(iov.buf_len, remaining);
360
+ mem.set(pipe.buffer.subarray(pipe.readOffset, pipe.readOffset + n), iov.buf);
361
+ pipe.readOffset += n;
362
+ totalRead += n;
363
+ }
364
+ }
365
+ }
366
+ else {
367
+ return ERRNO_EBADF;
368
+ }
369
+ this._view().setUint32(nread_ptr, totalRead, true);
370
+ return ERRNO_SUCCESS;
371
+ }
372
+ /**
373
+ * Write from iovec buffers to a file descriptor.
374
+ * Handles stdio (stdout/stderr collection), VFS files, and pipes.
375
+ */
376
+ fd_write(fd, iovs_ptr, iovs_len, nwritten_ptr) {
377
+ const entry = this.fdTable.get(fd);
378
+ if (!entry)
379
+ return ERRNO_EBADF;
380
+ if (!(entry.rightsBase & RIGHT_FD_WRITE))
381
+ return ERRNO_EBADF;
382
+ const iovecs = this._readIovecs(iovs_ptr, iovs_len);
383
+ const mem = this._bytes();
384
+ let totalWritten = 0;
385
+ const resource = entry.resource;
386
+ if (resource.type === 'stdio') {
387
+ for (const iov of iovecs) {
388
+ if (iov.buf_len === 0)
389
+ continue;
390
+ const chunk = mem.slice(iov.buf, iov.buf + iov.buf_len);
391
+ if (resource.name === 'stdout') {
392
+ if (this._stdoutWriter) {
393
+ // Streaming mode: write to ring buffer (blocks via Atomics.wait)
394
+ this._stdoutWriter(chunk, 0, chunk.length);
395
+ }
396
+ else {
397
+ this._stdoutChunks.push(chunk);
398
+ }
399
+ }
400
+ else if (resource.name === 'stderr') {
401
+ if (this._stderrWriter) {
402
+ this._stderrWriter(chunk, 0, chunk.length);
403
+ }
404
+ else {
405
+ this._stderrChunks.push(chunk);
406
+ }
407
+ }
408
+ totalWritten += iov.buf_len;
409
+ }
410
+ }
411
+ else if (resource.type === 'vfsFile') {
412
+ // Collect all write data, then delegate to kernel file I/O bridge
413
+ const chunks = [];
414
+ for (const iov of iovecs) {
415
+ if (iov.buf_len === 0)
416
+ continue;
417
+ chunks.push(mem.slice(iov.buf, iov.buf + iov.buf_len));
418
+ totalWritten += iov.buf_len;
419
+ }
420
+ if (totalWritten > 0) {
421
+ const writeData = concatBytes(chunks);
422
+ const result = this._fileIO.fdWrite(fd, writeData);
423
+ if (result.errno !== ERRNO_SUCCESS)
424
+ return result.errno;
425
+ }
426
+ }
427
+ else if (resource.type === 'pipe') {
428
+ const pipe = resource.pipe;
429
+ for (const iov of iovecs) {
430
+ if (iov.buf_len === 0)
431
+ continue;
432
+ const chunk = mem.slice(iov.buf, iov.buf + iov.buf_len);
433
+ if (pipe) {
434
+ const needed = pipe.writeOffset + chunk.length;
435
+ if (needed > pipe.buffer.length) {
436
+ const newBuf = new Uint8Array(Math.max(needed, pipe.buffer.length * 2));
437
+ newBuf.set(pipe.buffer);
438
+ pipe.buffer = newBuf;
439
+ }
440
+ pipe.buffer.set(chunk, pipe.writeOffset);
441
+ pipe.writeOffset += chunk.length;
442
+ }
443
+ totalWritten += iov.buf_len;
444
+ }
445
+ }
446
+ else {
447
+ return ERRNO_EBADF;
448
+ }
449
+ this._view().setUint32(nwritten_ptr, totalWritten, true);
450
+ return ERRNO_SUCCESS;
451
+ }
452
+ /**
453
+ * Seek within a file descriptor. Delegates to kernel file I/O bridge.
454
+ */
455
+ fd_seek(fd, offset, whence, newoffset_ptr) {
456
+ const entry = this.fdTable.get(fd);
457
+ if (!entry)
458
+ return ERRNO_EBADF;
459
+ if (entry.filetype !== FILETYPE_REGULAR_FILE)
460
+ return ERRNO_ESPIPE;
461
+ if (!(entry.rightsBase & RIGHT_FD_SEEK))
462
+ return ERRNO_EBADF;
463
+ const offsetBig = typeof offset === 'bigint' ? offset : BigInt(offset);
464
+ const result = this._fileIO.fdSeek(fd, offsetBig, whence);
465
+ if (result.errno !== ERRNO_SUCCESS)
466
+ return result.errno;
467
+ // Sync local cursor so fd_tell returns consistent values
468
+ entry.cursor = result.newOffset;
469
+ this._view().setBigUint64(newoffset_ptr, result.newOffset, true);
470
+ return ERRNO_SUCCESS;
471
+ }
472
+ /**
473
+ * Get current file position.
474
+ */
475
+ fd_tell(fd, offset_ptr) {
476
+ const entry = this.fdTable.get(fd);
477
+ if (!entry)
478
+ return ERRNO_EBADF;
479
+ if (entry.filetype !== FILETYPE_REGULAR_FILE)
480
+ return ERRNO_ESPIPE;
481
+ if (!(entry.rightsBase & RIGHT_FD_TELL))
482
+ return ERRNO_EBADF;
483
+ this._view().setBigUint64(offset_ptr, entry.cursor, true);
484
+ return ERRNO_SUCCESS;
485
+ }
486
+ /**
487
+ * Close a file descriptor. Delegates to kernel file I/O bridge.
488
+ */
489
+ fd_close(fd) {
490
+ this._preopens.delete(fd);
491
+ return this._fileIO.fdClose(fd);
492
+ }
493
+ /**
494
+ * Get file descriptor status.
495
+ * Writes fdstat struct (24 bytes) at buf_ptr:
496
+ * offset 0: fs_filetype (u8)
497
+ * offset 2: fs_flags (u16 LE)
498
+ * offset 8: fs_rights_base (u64 LE)
499
+ * offset 16: fs_rights_inheriting (u64 LE)
500
+ */
501
+ fd_fdstat_get(fd, buf_ptr) {
502
+ const stat = this._processIO.fdFdstatGet(fd);
503
+ if (stat.errno !== ERRNO_SUCCESS)
504
+ return stat.errno;
505
+ const view = this._view();
506
+ view.setUint8(buf_ptr, stat.filetype);
507
+ view.setUint8(buf_ptr + 1, 0); // padding
508
+ view.setUint16(buf_ptr + 2, stat.fdflags, true);
509
+ view.setUint32(buf_ptr + 4, 0); // padding
510
+ view.setBigUint64(buf_ptr + 8, stat.rightsBase, true);
511
+ view.setBigUint64(buf_ptr + 16, stat.rightsInheriting, true);
512
+ return ERRNO_SUCCESS;
513
+ }
514
+ /**
515
+ * Set file descriptor flags.
516
+ */
517
+ fd_fdstat_set_flags(fd, flags) {
518
+ const entry = this.fdTable.get(fd);
519
+ if (!entry)
520
+ return ERRNO_EBADF;
521
+ if (!(entry.rightsBase & RIGHT_FD_FDSTAT_SET_FLAGS))
522
+ return ERRNO_EBADF;
523
+ entry.fdflags = flags;
524
+ return ERRNO_SUCCESS;
525
+ }
526
+ /**
527
+ * Get pre-opened directory info.
528
+ * Writes prestat struct (8 bytes) at buf_ptr:
529
+ * offset 0: pr_type (u8) = 0 for dir
530
+ * offset 4: u.dir.pr_name_len (u32 LE)
531
+ */
532
+ fd_prestat_get(fd, buf_ptr) {
533
+ const path = this._preopens.get(fd);
534
+ if (path === undefined)
535
+ return ERRNO_EBADF;
536
+ const encoded = new TextEncoder().encode(path);
537
+ const view = this._view();
538
+ view.setUint8(buf_ptr, PREOPENTYPE_DIR);
539
+ view.setUint8(buf_ptr + 1, 0);
540
+ view.setUint16(buf_ptr + 2, 0);
541
+ view.setUint32(buf_ptr + 4, encoded.length, true);
542
+ return ERRNO_SUCCESS;
543
+ }
544
+ /**
545
+ * Get the name of a pre-opened directory.
546
+ */
547
+ fd_prestat_dir_name(fd, path_ptr, path_len) {
548
+ const path = this._preopens.get(fd);
549
+ if (path === undefined)
550
+ return ERRNO_EBADF;
551
+ const encoded = new TextEncoder().encode(path);
552
+ const len = Math.min(encoded.length, path_len);
553
+ this._bytes().set(encoded.subarray(0, len), path_ptr);
554
+ return ERRNO_SUCCESS;
555
+ }
556
+ // --- Helper methods for path/filestat operations (US-008) ---
557
+ /**
558
+ * Read a path string from WASM memory.
559
+ */
560
+ _readPathString(pathPtr, pathLen) {
561
+ return new TextDecoder().decode(new Uint8Array(this.memory.buffer, pathPtr, pathLen));
562
+ }
563
+ /**
564
+ * Resolve a WASI path relative to a directory fd.
565
+ */
566
+ _resolveWasiPath(dirfd, pathPtr, pathLen) {
567
+ const pathStr = this._readPathString(pathPtr, pathLen);
568
+ let basePath = this._preopens.get(dirfd);
569
+ if (basePath === undefined) {
570
+ const entry = this.fdTable.get(dirfd);
571
+ if (!entry)
572
+ return null;
573
+ basePath = entry.path || '/';
574
+ }
575
+ let fullPath;
576
+ if (pathStr.startsWith('/')) {
577
+ fullPath = pathStr;
578
+ }
579
+ else {
580
+ fullPath = basePath === '/' ? '/' + pathStr : basePath + '/' + pathStr;
581
+ }
582
+ // Normalize . and .. components (WASI paths may contain them)
583
+ return normalizePath(fullPath);
584
+ }
585
+ /**
586
+ * Convert VFS inode type to WASI filetype.
587
+ */
588
+ _inodeTypeToFiletype(type) {
589
+ switch (type) {
590
+ case 'file': return FILETYPE_REGULAR_FILE;
591
+ case 'dir': return FILETYPE_DIRECTORY;
592
+ case 'symlink': return FILETYPE_SYMBOLIC_LINK;
593
+ case 'dev': return FILETYPE_CHARACTER_DEVICE;
594
+ default: return FILETYPE_UNKNOWN;
595
+ }
596
+ }
597
+ /**
598
+ * Write a WASI filestat struct (64 bytes) at the given pointer.
599
+ */
600
+ _writeFilestat(ptr, ino, node) {
601
+ const view = this._view();
602
+ view.setBigUint64(ptr, 0n, true); // dev
603
+ view.setBigUint64(ptr + 8, BigInt(ino), true); // ino
604
+ view.setUint8(ptr + 16, this._inodeTypeToFiletype(node.type)); // filetype
605
+ for (let i = 17; i < 24; i++)
606
+ view.setUint8(ptr + i, 0); // padding
607
+ view.setBigUint64(ptr + 24, BigInt(node.nlink), true); // nlink
608
+ view.setBigUint64(ptr + 32, BigInt(node.size), true); // size
609
+ view.setBigUint64(ptr + 40, BigInt(node.atime) * 1000000n, true); // atim (ms->ns)
610
+ view.setBigUint64(ptr + 48, BigInt(node.mtime) * 1000000n, true); // mtim (ms->ns)
611
+ view.setBigUint64(ptr + 56, BigInt(node.ctime) * 1000000n, true); // ctim (ms->ns)
612
+ }
613
+ /**
614
+ * Apply timestamp changes to a VFS inode based on fstflags.
615
+ */
616
+ _applyTimestamps(node, atim, mtim, fst_flags) {
617
+ const now = Date.now();
618
+ if (fst_flags & FSTFLAG_ATIM_NOW) {
619
+ node.atime = now;
620
+ }
621
+ else if (fst_flags & FSTFLAG_ATIM) {
622
+ const atimBig = typeof atim === 'bigint' ? atim : BigInt(atim);
623
+ node.atime = Number(atimBig / 1000000n);
624
+ }
625
+ if (fst_flags & FSTFLAG_MTIM_NOW) {
626
+ node.mtime = now;
627
+ }
628
+ else if (fst_flags & FSTFLAG_MTIM) {
629
+ const mtimBig = typeof mtim === 'bigint' ? mtim : BigInt(mtim);
630
+ node.mtime = Number(mtimBig / 1000000n);
631
+ }
632
+ node.ctime = now;
633
+ }
634
+ // --- Path operations (US-008) ---
635
+ /**
636
+ * Open a file or directory at a path relative to a directory fd.
637
+ */
638
+ path_open(dirfd, dirflags, path_ptr, path_len, oflags, fs_rights_base, fs_rights_inheriting, fdflags, opened_fd_ptr) {
639
+ const dirEntry = this.fdTable.get(dirfd);
640
+ if (!dirEntry)
641
+ return ERRNO_EBADF;
642
+ if (!(dirEntry.rightsBase & RIGHT_PATH_OPEN))
643
+ return ERRNO_EBADF;
644
+ const fullPath = this._resolveWasiPath(dirfd, path_ptr, path_len);
645
+ if (!fullPath)
646
+ return ERRNO_EBADF;
647
+ // Intersect requested rights with directory's inheriting rights
648
+ const rightsBase = (typeof fs_rights_base === 'bigint' ? fs_rights_base : BigInt(fs_rights_base)) & dirEntry.rightsInheriting;
649
+ const rightsInheriting = (typeof fs_rights_inheriting === 'bigint' ? fs_rights_inheriting : BigInt(fs_rights_inheriting)) & dirEntry.rightsInheriting;
650
+ // Delegate to kernel file I/O bridge
651
+ const result = this._fileIO.fdOpen(fullPath, dirflags, oflags, fdflags, rightsBase, rightsInheriting);
652
+ if (result.errno !== ERRNO_SUCCESS)
653
+ return result.errno;
654
+ this._view().setUint32(opened_fd_ptr, result.fd, true);
655
+ return ERRNO_SUCCESS;
656
+ }
657
+ /**
658
+ * Create a directory at a path relative to a directory fd.
659
+ */
660
+ path_create_directory(dirfd, path_ptr, path_len) {
661
+ const dirEntry = this.fdTable.get(dirfd);
662
+ if (!dirEntry)
663
+ return ERRNO_EBADF;
664
+ if (!(dirEntry.rightsBase & RIGHT_PATH_CREATE_DIRECTORY))
665
+ return ERRNO_EBADF;
666
+ const fullPath = this._resolveWasiPath(dirfd, path_ptr, path_len);
667
+ if (!fullPath)
668
+ return ERRNO_EBADF;
669
+ try {
670
+ this.vfs.mkdir(fullPath);
671
+ }
672
+ catch (e) {
673
+ return vfsErrorToErrno(e);
674
+ }
675
+ return ERRNO_SUCCESS;
676
+ }
677
+ /**
678
+ * Unlink a file at a path relative to a directory fd.
679
+ */
680
+ path_unlink_file(dirfd, path_ptr, path_len) {
681
+ const dirEntry = this.fdTable.get(dirfd);
682
+ if (!dirEntry)
683
+ return ERRNO_EBADF;
684
+ if (!(dirEntry.rightsBase & RIGHT_PATH_UNLINK_FILE))
685
+ return ERRNO_EBADF;
686
+ const fullPath = this._resolveWasiPath(dirfd, path_ptr, path_len);
687
+ if (!fullPath)
688
+ return ERRNO_EBADF;
689
+ try {
690
+ this.vfs.unlink(fullPath);
691
+ }
692
+ catch (e) {
693
+ return vfsErrorToErrno(e);
694
+ }
695
+ return ERRNO_SUCCESS;
696
+ }
697
+ /**
698
+ * Remove a directory at a path relative to a directory fd.
699
+ */
700
+ path_remove_directory(dirfd, path_ptr, path_len) {
701
+ const dirEntry = this.fdTable.get(dirfd);
702
+ if (!dirEntry)
703
+ return ERRNO_EBADF;
704
+ if (!(dirEntry.rightsBase & RIGHT_PATH_REMOVE_DIRECTORY))
705
+ return ERRNO_EBADF;
706
+ const fullPath = this._resolveWasiPath(dirfd, path_ptr, path_len);
707
+ if (!fullPath)
708
+ return ERRNO_EBADF;
709
+ try {
710
+ this.vfs.rmdir(fullPath);
711
+ }
712
+ catch (e) {
713
+ return vfsErrorToErrno(e);
714
+ }
715
+ return ERRNO_SUCCESS;
716
+ }
717
+ /**
718
+ * Rename a file or directory.
719
+ */
720
+ path_rename(old_dirfd, old_path_ptr, old_path_len, new_dirfd, new_path_ptr, new_path_len) {
721
+ const oldDirEntry = this.fdTable.get(old_dirfd);
722
+ if (!oldDirEntry)
723
+ return ERRNO_EBADF;
724
+ if (!(oldDirEntry.rightsBase & RIGHT_PATH_RENAME_SOURCE))
725
+ return ERRNO_EBADF;
726
+ const newDirEntry = this.fdTable.get(new_dirfd);
727
+ if (!newDirEntry)
728
+ return ERRNO_EBADF;
729
+ if (!(newDirEntry.rightsBase & RIGHT_PATH_RENAME_TARGET))
730
+ return ERRNO_EBADF;
731
+ const oldPath = this._resolveWasiPath(old_dirfd, old_path_ptr, old_path_len);
732
+ const newPath = this._resolveWasiPath(new_dirfd, new_path_ptr, new_path_len);
733
+ if (!oldPath || !newPath)
734
+ return ERRNO_EBADF;
735
+ try {
736
+ this.vfs.rename(oldPath, newPath);
737
+ }
738
+ catch (e) {
739
+ return vfsErrorToErrno(e);
740
+ }
741
+ return ERRNO_SUCCESS;
742
+ }
743
+ /**
744
+ * Create a symbolic link.
745
+ */
746
+ path_symlink(old_path_ptr, old_path_len, dirfd, new_path_ptr, new_path_len) {
747
+ const dirEntry = this.fdTable.get(dirfd);
748
+ if (!dirEntry)
749
+ return ERRNO_EBADF;
750
+ if (!(dirEntry.rightsBase & RIGHT_PATH_SYMLINK))
751
+ return ERRNO_EBADF;
752
+ const target = this._readPathString(old_path_ptr, old_path_len);
753
+ const linkPath = this._resolveWasiPath(dirfd, new_path_ptr, new_path_len);
754
+ if (!linkPath)
755
+ return ERRNO_EBADF;
756
+ try {
757
+ this.vfs.symlink(target, linkPath);
758
+ }
759
+ catch (e) {
760
+ return vfsErrorToErrno(e);
761
+ }
762
+ return ERRNO_SUCCESS;
763
+ }
764
+ /**
765
+ * Read the target of a symbolic link.
766
+ */
767
+ path_readlink(dirfd, path_ptr, path_len, buf_ptr, buf_len, bufused_ptr) {
768
+ const dirEntry = this.fdTable.get(dirfd);
769
+ if (!dirEntry)
770
+ return ERRNO_EBADF;
771
+ if (!(dirEntry.rightsBase & RIGHT_PATH_READLINK))
772
+ return ERRNO_EBADF;
773
+ const fullPath = this._resolveWasiPath(dirfd, path_ptr, path_len);
774
+ if (!fullPath)
775
+ return ERRNO_EBADF;
776
+ let target;
777
+ try {
778
+ target = this.vfs.readlink(fullPath);
779
+ }
780
+ catch (e) {
781
+ return vfsErrorToErrno(e);
782
+ }
783
+ const encoded = new TextEncoder().encode(target);
784
+ const writeLen = Math.min(encoded.length, buf_len);
785
+ this._bytes().set(encoded.subarray(0, writeLen), buf_ptr);
786
+ this._view().setUint32(bufused_ptr, writeLen, true);
787
+ return ERRNO_SUCCESS;
788
+ }
789
+ /**
790
+ * Get file status by path.
791
+ */
792
+ path_filestat_get(dirfd, flags, path_ptr, path_len, buf_ptr) {
793
+ const dirEntry = this.fdTable.get(dirfd);
794
+ if (!dirEntry)
795
+ return ERRNO_EBADF;
796
+ if (!(dirEntry.rightsBase & RIGHT_PATH_FILESTAT_GET))
797
+ return ERRNO_EBADF;
798
+ const fullPath = this._resolveWasiPath(dirfd, path_ptr, path_len);
799
+ if (!fullPath)
800
+ return ERRNO_EBADF;
801
+ const followSymlinks = !!(flags & LOOKUP_SYMLINK_FOLLOW);
802
+ const ino = this.vfs.getIno(fullPath, followSymlinks);
803
+ if (ino === null)
804
+ return ERRNO_ENOENT;
805
+ const node = this.vfs.getInodeByIno(ino);
806
+ if (!node)
807
+ return ERRNO_ENOENT;
808
+ this._writeFilestat(buf_ptr, ino, node);
809
+ return ERRNO_SUCCESS;
810
+ }
811
+ /**
812
+ * Set file timestamps by path.
813
+ */
814
+ path_filestat_set_times(dirfd, flags, path_ptr, path_len, atim, mtim, fst_flags) {
815
+ const dirEntry = this.fdTable.get(dirfd);
816
+ if (!dirEntry)
817
+ return ERRNO_EBADF;
818
+ if (!(dirEntry.rightsBase & RIGHT_PATH_FILESTAT_SET_TIMES))
819
+ return ERRNO_EBADF;
820
+ const fullPath = this._resolveWasiPath(dirfd, path_ptr, path_len);
821
+ if (!fullPath)
822
+ return ERRNO_EBADF;
823
+ const followSymlinks = !!(flags & LOOKUP_SYMLINK_FOLLOW);
824
+ const ino = this.vfs.getIno(fullPath, followSymlinks);
825
+ if (ino === null)
826
+ return ERRNO_ENOENT;
827
+ const node = this.vfs.getInodeByIno(ino);
828
+ if (!node)
829
+ return ERRNO_ENOENT;
830
+ this._applyTimestamps(node, atim, mtim, fst_flags);
831
+ return ERRNO_SUCCESS;
832
+ }
833
+ // --- FD filestat operations (US-008) ---
834
+ /**
835
+ * Get file status by fd.
836
+ */
837
+ fd_filestat_get(fd, buf_ptr) {
838
+ const entry = this.fdTable.get(fd);
839
+ if (!entry)
840
+ return ERRNO_EBADF;
841
+ if (!(entry.rightsBase & RIGHT_FD_FILESTAT_GET))
842
+ return ERRNO_EBADF;
843
+ const resource = entry.resource;
844
+ if (resource.type === 'vfsFile' || resource.type === 'preopen') {
845
+ // Kernel-opened vfsFile resources have ino=0 (sentinel) — resolve by path
846
+ const ino = (resource.type === 'vfsFile' && resource.ino !== 0)
847
+ ? resource.ino
848
+ : this.vfs.getIno(resource.path, true);
849
+ if (ino === null)
850
+ return ERRNO_EBADF;
851
+ const node = this.vfs.getInodeByIno(ino);
852
+ if (!node)
853
+ return ERRNO_EBADF;
854
+ this._writeFilestat(buf_ptr, ino, node);
855
+ }
856
+ else {
857
+ // stdio, pipe, etc. -- return minimal stat
858
+ const view = this._view();
859
+ view.setBigUint64(buf_ptr, 0n, true); // dev
860
+ view.setBigUint64(buf_ptr + 8, 0n, true); // ino
861
+ view.setUint8(buf_ptr + 16, entry.filetype); // filetype
862
+ for (let i = 17; i < 24; i++)
863
+ view.setUint8(buf_ptr + i, 0);
864
+ view.setBigUint64(buf_ptr + 24, 1n, true); // nlink
865
+ view.setBigUint64(buf_ptr + 32, 0n, true); // size
866
+ view.setBigUint64(buf_ptr + 40, 0n, true); // atim
867
+ view.setBigUint64(buf_ptr + 48, 0n, true); // mtim
868
+ view.setBigUint64(buf_ptr + 56, 0n, true); // ctim
869
+ }
870
+ return ERRNO_SUCCESS;
871
+ }
872
+ /**
873
+ * Set file size by fd (truncate or extend).
874
+ */
875
+ fd_filestat_set_size(fd, size) {
876
+ const entry = this.fdTable.get(fd);
877
+ if (!entry)
878
+ return ERRNO_EBADF;
879
+ if (!(entry.rightsBase & RIGHT_FD_FILESTAT_SET_SIZE))
880
+ return ERRNO_EBADF;
881
+ if (entry.resource.type !== 'vfsFile')
882
+ return ERRNO_EINVAL;
883
+ const node = this.vfs.getInodeByIno(entry.resource.ino);
884
+ if (!node || node.type !== 'file')
885
+ return ERRNO_EINVAL;
886
+ const newSize = Number(typeof size === 'bigint' ? size : BigInt(size));
887
+ if (newSize === node.data.length)
888
+ return ERRNO_SUCCESS;
889
+ const newData = new Uint8Array(newSize);
890
+ newData.set(node.data.subarray(0, Math.min(node.data.length, newSize)));
891
+ node.data = newData;
892
+ node.mtime = Date.now();
893
+ node.ctime = Date.now();
894
+ return ERRNO_SUCCESS;
895
+ }
896
+ /**
897
+ * Set file timestamps by fd.
898
+ */
899
+ fd_filestat_set_times(fd, atim, mtim, fst_flags) {
900
+ const entry = this.fdTable.get(fd);
901
+ if (!entry)
902
+ return ERRNO_EBADF;
903
+ if (!(entry.rightsBase & RIGHT_FD_FILESTAT_SET_TIMES))
904
+ return ERRNO_EBADF;
905
+ if (entry.resource.type === 'vfsFile') {
906
+ const node = this.vfs.getInodeByIno(entry.resource.ino);
907
+ if (!node)
908
+ return ERRNO_EBADF;
909
+ this._applyTimestamps(node, atim, mtim, fst_flags);
910
+ }
911
+ else if (entry.resource.type === 'preopen') {
912
+ const ino = this.vfs.getIno(entry.resource.path, true);
913
+ if (ino === null)
914
+ return ERRNO_EBADF;
915
+ const node = this.vfs.getInodeByIno(ino);
916
+ if (!node)
917
+ return ERRNO_EBADF;
918
+ this._applyTimestamps(node, atim, mtim, fst_flags);
919
+ }
920
+ // For stdio/pipes, silently succeed
921
+ return ERRNO_SUCCESS;
922
+ }
923
+ // --- Directory operations (US-008) ---
924
+ /**
925
+ * Read directory entries from a directory fd.
926
+ * Writes dirent structs (24-byte header + name) into the buffer.
927
+ */
928
+ fd_readdir(fd, buf_ptr, buf_len, cookie, bufused_ptr) {
929
+ const entry = this.fdTable.get(fd);
930
+ if (!entry)
931
+ return ERRNO_EBADF;
932
+ if (!(entry.rightsBase & RIGHT_FD_READDIR))
933
+ return ERRNO_EBADF;
934
+ let ino;
935
+ if (entry.resource.type === 'preopen') {
936
+ ino = this.vfs.getIno(entry.resource.path, true);
937
+ }
938
+ else if (entry.resource.type === 'vfsFile') {
939
+ ino = entry.resource.ino;
940
+ }
941
+ else {
942
+ return ERRNO_EBADF;
943
+ }
944
+ const node = this.vfs.getInodeByIno(ino);
945
+ if (!node || node.type !== 'dir')
946
+ return ERRNO_ENOTDIR;
947
+ const entries = Array.from(node.entries.entries());
948
+ const cookieNum = Number(typeof cookie === 'bigint' ? cookie : BigInt(cookie));
949
+ const mem = this._bytes();
950
+ const view = this._view();
951
+ let offset = 0;
952
+ for (let i = cookieNum; i < entries.length; i++) {
953
+ const [name, childIno] = entries[i];
954
+ const childNode = this.vfs.getInodeByIno(childIno);
955
+ const nameBytes = new TextEncoder().encode(name);
956
+ const headerSize = 24;
957
+ const entrySize = headerSize + nameBytes.length;
958
+ if (offset + entrySize > buf_len)
959
+ break;
960
+ // Write dirent header
961
+ view.setBigUint64(buf_ptr + offset, BigInt(i + 1), true); // d_next
962
+ view.setBigUint64(buf_ptr + offset + 8, BigInt(childIno), true); // d_ino
963
+ view.setUint32(buf_ptr + offset + 16, nameBytes.length, true); // d_namlen
964
+ view.setUint8(buf_ptr + offset + 20, childNode ? this._inodeTypeToFiletype(childNode.type) : FILETYPE_UNKNOWN); // d_type
965
+ view.setUint8(buf_ptr + offset + 21, 0); // padding
966
+ view.setUint8(buf_ptr + offset + 22, 0);
967
+ view.setUint8(buf_ptr + offset + 23, 0);
968
+ offset += headerSize;
969
+ // Write name (guaranteed to fit — checked entrySize above)
970
+ mem.set(nameBytes, buf_ptr + offset);
971
+ offset += nameBytes.length;
972
+ }
973
+ view.setUint32(bufused_ptr, offset, true);
974
+ return ERRNO_SUCCESS;
975
+ }
976
+ // --- Args and environ operations (US-009) ---
977
+ /**
978
+ * Write command-line arguments into WASM memory.
979
+ * argv_ptr: pointer to array of u32 pointers (one per arg)
980
+ * argv_buf_ptr: pointer to buffer where arg strings are written (null-terminated)
981
+ */
982
+ args_get(argv_ptr, argv_buf_ptr) {
983
+ const args = this._processIO.getArgs();
984
+ const view = this._view();
985
+ const mem = this._bytes();
986
+ const encoder = new TextEncoder();
987
+ let bufOffset = argv_buf_ptr;
988
+ for (let i = 0; i < args.length; i++) {
989
+ view.setUint32(argv_ptr + i * 4, bufOffset, true);
990
+ const encoded = encoder.encode(args[i]);
991
+ mem.set(encoded, bufOffset);
992
+ mem[bufOffset + encoded.length] = 0; // null terminator
993
+ bufOffset += encoded.length + 1;
994
+ }
995
+ return ERRNO_SUCCESS;
996
+ }
997
+ /**
998
+ * Get the sizes needed for args_get.
999
+ * Writes argc (u32) at argc_ptr and total argv buffer size (u32) at argv_buf_size_ptr.
1000
+ */
1001
+ args_sizes_get(argc_ptr, argv_buf_size_ptr) {
1002
+ const args = this._processIO.getArgs();
1003
+ const view = this._view();
1004
+ const encoder = new TextEncoder();
1005
+ let bufSize = 0;
1006
+ for (const arg of args) {
1007
+ bufSize += encoder.encode(arg).length + 1; // +1 for null terminator
1008
+ }
1009
+ view.setUint32(argc_ptr, args.length, true);
1010
+ view.setUint32(argv_buf_size_ptr, bufSize, true);
1011
+ return ERRNO_SUCCESS;
1012
+ }
1013
+ /**
1014
+ * Write environment variables into WASM memory.
1015
+ * environ_ptr: pointer to array of u32 pointers (one per env entry)
1016
+ * environ_buf_ptr: pointer to buffer where "KEY=VALUE\0" strings are written
1017
+ */
1018
+ environ_get(environ_ptr, environ_buf_ptr) {
1019
+ const env = this._processIO.getEnviron();
1020
+ const view = this._view();
1021
+ const mem = this._bytes();
1022
+ const encoder = new TextEncoder();
1023
+ const entries = Object.entries(env);
1024
+ let bufOffset = environ_buf_ptr;
1025
+ for (let i = 0; i < entries.length; i++) {
1026
+ view.setUint32(environ_ptr + i * 4, bufOffset, true);
1027
+ const str = `${entries[i][0]}=${entries[i][1]}`;
1028
+ const encoded = encoder.encode(str);
1029
+ mem.set(encoded, bufOffset);
1030
+ mem[bufOffset + encoded.length] = 0; // null terminator
1031
+ bufOffset += encoded.length + 1;
1032
+ }
1033
+ return ERRNO_SUCCESS;
1034
+ }
1035
+ /**
1036
+ * Get the sizes needed for environ_get.
1037
+ * Writes environ count (u32) at environc_ptr and total buffer size (u32) at environ_buf_size_ptr.
1038
+ */
1039
+ environ_sizes_get(environc_ptr, environ_buf_size_ptr) {
1040
+ const env = this._processIO.getEnviron();
1041
+ const view = this._view();
1042
+ const encoder = new TextEncoder();
1043
+ const entries = Object.entries(env);
1044
+ let bufSize = 0;
1045
+ for (const [key, value] of entries) {
1046
+ bufSize += encoder.encode(`${key}=${value}`).length + 1;
1047
+ }
1048
+ view.setUint32(environc_ptr, entries.length, true);
1049
+ view.setUint32(environ_buf_size_ptr, bufSize, true);
1050
+ return ERRNO_SUCCESS;
1051
+ }
1052
+ // --- Clock, random, and process operations (US-009) ---
1053
+ /**
1054
+ * Get the resolution of a clock.
1055
+ * Writes resolution in nanoseconds as u64 at resolution_ptr.
1056
+ */
1057
+ clock_res_get(id, resolution_ptr) {
1058
+ const view = this._view();
1059
+ switch (id) {
1060
+ case CLOCKID_REALTIME:
1061
+ // Date.now() has ~1ms resolution
1062
+ view.setBigUint64(resolution_ptr, 1000000n, true);
1063
+ return ERRNO_SUCCESS;
1064
+ case CLOCKID_MONOTONIC:
1065
+ // performance.now() has ~1us resolution (in practice, may be less)
1066
+ view.setBigUint64(resolution_ptr, 1000n, true);
1067
+ return ERRNO_SUCCESS;
1068
+ case CLOCKID_PROCESS_CPUTIME_ID:
1069
+ case CLOCKID_THREAD_CPUTIME_ID:
1070
+ // Approximate -- no real CPU time tracking
1071
+ view.setBigUint64(resolution_ptr, 1000000n, true);
1072
+ return ERRNO_SUCCESS;
1073
+ default:
1074
+ return ERRNO_EINVAL;
1075
+ }
1076
+ }
1077
+ /**
1078
+ * Get the current time of a clock.
1079
+ * Writes time in nanoseconds as u64 at time_ptr.
1080
+ */
1081
+ clock_time_get(id, _precision, time_ptr) {
1082
+ const view = this._view();
1083
+ switch (id) {
1084
+ case CLOCKID_REALTIME: {
1085
+ const ms = Date.now();
1086
+ view.setBigUint64(time_ptr, BigInt(ms) * 1000000n, true);
1087
+ return ERRNO_SUCCESS;
1088
+ }
1089
+ case CLOCKID_MONOTONIC: {
1090
+ // Use performance.now() if available, fall back to Date.now()
1091
+ const nowMs = (typeof performance !== 'undefined' && performance.now)
1092
+ ? performance.now()
1093
+ : Date.now();
1094
+ // Convert ms (float) to nanoseconds
1095
+ const ns = BigInt(Math.round(nowMs * 1_000_000));
1096
+ view.setBigUint64(time_ptr, ns, true);
1097
+ return ERRNO_SUCCESS;
1098
+ }
1099
+ case CLOCKID_PROCESS_CPUTIME_ID:
1100
+ case CLOCKID_THREAD_CPUTIME_ID: {
1101
+ // Approximate with monotonic clock
1102
+ const nowMs = (typeof performance !== 'undefined' && performance.now)
1103
+ ? performance.now()
1104
+ : Date.now();
1105
+ const ns = BigInt(Math.round(nowMs * 1_000_000));
1106
+ view.setBigUint64(time_ptr, ns, true);
1107
+ return ERRNO_SUCCESS;
1108
+ }
1109
+ default:
1110
+ return ERRNO_EINVAL;
1111
+ }
1112
+ }
1113
+ /**
1114
+ * Fill a buffer with cryptographically secure random bytes.
1115
+ */
1116
+ random_get(buf_ptr, buf_len) {
1117
+ const mem = this._bytes();
1118
+ const target = mem.subarray(buf_ptr, buf_ptr + buf_len);
1119
+ // Use crypto.getRandomValues -- works in both browser and Node.js
1120
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
1121
+ // getRandomValues has a 65536-byte limit per call
1122
+ for (let offset = 0; offset < buf_len; offset += 65536) {
1123
+ const len = Math.min(65536, buf_len - offset);
1124
+ crypto.getRandomValues(target.subarray(offset, offset + len));
1125
+ }
1126
+ }
1127
+ else {
1128
+ // Fallback: Math.random (not cryptographically secure, but functional)
1129
+ for (let i = 0; i < buf_len; i++) {
1130
+ target[i] = Math.floor(Math.random() * 256);
1131
+ }
1132
+ }
1133
+ return ERRNO_SUCCESS;
1134
+ }
1135
+ /**
1136
+ * Terminate the process with an exit code.
1137
+ * Throws WasiProcExit to unwind the WASM call stack.
1138
+ */
1139
+ proc_exit(exitCode) {
1140
+ this.exitCode = exitCode;
1141
+ this._processIO.procExit(exitCode);
1142
+ throw new WasiProcExit(exitCode);
1143
+ }
1144
+ /**
1145
+ * Send a signal to the current process.
1146
+ * Not meaningful in WASM -- stub that returns ENOSYS.
1147
+ */
1148
+ proc_raise(_sig) {
1149
+ return ERRNO_ENOSYS;
1150
+ }
1151
+ /**
1152
+ * Yield the current thread's execution.
1153
+ * No-op in single-threaded WASM.
1154
+ */
1155
+ sched_yield() {
1156
+ return ERRNO_SUCCESS;
1157
+ }
1158
+ /**
1159
+ * Minimal poll_oneoff supporting clock subscriptions (for sleep).
1160
+ *
1161
+ * Subscription layout (48 bytes):
1162
+ * u64 userdata @ 0
1163
+ * u8 type @ 8 (0=clock, 1=fd_read, 2=fd_write)
1164
+ * -- padding to offset 16 --
1165
+ * For clock (type==0):
1166
+ * u32 clock_id @ 16
1167
+ * u64 timeout @ 24
1168
+ * u64 precision @ 32
1169
+ * u16 flags @ 40 (bit 0 = abstime)
1170
+ *
1171
+ * Event layout (32 bytes):
1172
+ * u64 userdata @ 0
1173
+ * u16 error @ 8
1174
+ * u8 type @ 10
1175
+ * -- padding --
1176
+ * u64 fd_readwrite.nbytes @ 16
1177
+ * u16 fd_readwrite.flags @ 24
1178
+ */
1179
+ poll_oneoff(in_ptr, out_ptr, nsubscriptions, nevents_ptr) {
1180
+ const view = this._view();
1181
+ const nsubs = nsubscriptions;
1182
+ let nevents = 0;
1183
+ for (let i = 0; i < nsubs; i++) {
1184
+ const subBase = in_ptr + i * 48;
1185
+ const userdata = view.getBigUint64(subBase, true);
1186
+ const eventType = view.getUint8(subBase + 8);
1187
+ const evtBase = out_ptr + nevents * 32;
1188
+ view.setBigUint64(evtBase, userdata, true); // userdata
1189
+ view.setUint16(evtBase + 8, 0, true); // error = success
1190
+ view.setUint8(evtBase + 10, eventType); // type
1191
+ if (eventType === EVENTTYPE_CLOCK) {
1192
+ // Block for the requested duration (nanosleep/sleep via poll_oneoff)
1193
+ const timeoutNs = view.getBigUint64(subBase + 24, true);
1194
+ const flags = view.getUint16(subBase + 40, true);
1195
+ const isAbstime = (flags & 1) !== 0;
1196
+ let sleepMs;
1197
+ if (isAbstime) {
1198
+ // Absolute time: sleep until the specified wallclock time
1199
+ const targetMs = Number(timeoutNs / 1000000n);
1200
+ sleepMs = Math.max(0, targetMs - Date.now());
1201
+ }
1202
+ else {
1203
+ // Relative time: sleep for the specified duration
1204
+ sleepMs = Number(timeoutNs / 1000000n);
1205
+ }
1206
+ if (sleepMs > 0) {
1207
+ const buf = new Int32Array(new SharedArrayBuffer(4));
1208
+ let remainingMs = sleepMs;
1209
+ while (remainingMs > 0) {
1210
+ const sliceMs = Math.min(remainingMs, 10);
1211
+ Atomics.wait(buf, 0, 0, sliceMs);
1212
+ remainingMs -= sliceMs;
1213
+ this._sleepHook?.();
1214
+ }
1215
+ }
1216
+ }
1217
+ else if (eventType === EVENTTYPE_FD_READ || eventType === EVENTTYPE_FD_WRITE) {
1218
+ // FD subscriptions -- report ready immediately
1219
+ view.setBigUint64(evtBase + 16, 0n, true); // nbytes
1220
+ view.setUint16(evtBase + 24, 0, true); // flags
1221
+ }
1222
+ nevents++;
1223
+ }
1224
+ view.setUint32(nevents_ptr, nevents, true);
1225
+ return ERRNO_SUCCESS;
1226
+ }
1227
+ // --- Stub/no-op fd operations (US-009) ---
1228
+ /**
1229
+ * Advise the system on intended file usage patterns.
1230
+ * No-op -- advisory only.
1231
+ */
1232
+ fd_advise(fd, _offset, _len, _advice) {
1233
+ const entry = this.fdTable.get(fd);
1234
+ if (!entry)
1235
+ return ERRNO_EBADF;
1236
+ return ERRNO_SUCCESS;
1237
+ }
1238
+ /**
1239
+ * Pre-allocate space for a file.
1240
+ * No-op in VFS (files grow dynamically).
1241
+ */
1242
+ fd_allocate(fd, _offset, _len) {
1243
+ const entry = this.fdTable.get(fd);
1244
+ if (!entry)
1245
+ return ERRNO_EBADF;
1246
+ return ERRNO_SUCCESS;
1247
+ }
1248
+ /**
1249
+ * Synchronize file data to storage.
1250
+ * No-op in in-memory VFS.
1251
+ */
1252
+ fd_datasync(fd) {
1253
+ const entry = this.fdTable.get(fd);
1254
+ if (!entry)
1255
+ return ERRNO_EBADF;
1256
+ return ERRNO_SUCCESS;
1257
+ }
1258
+ /**
1259
+ * Synchronize file data and metadata to storage.
1260
+ * No-op in in-memory VFS.
1261
+ */
1262
+ fd_sync(fd) {
1263
+ const entry = this.fdTable.get(fd);
1264
+ if (!entry)
1265
+ return ERRNO_EBADF;
1266
+ return ERRNO_SUCCESS;
1267
+ }
1268
+ /**
1269
+ * Set rights on a file descriptor (shrink only).
1270
+ * Minimal implementation -- just validates fd.
1271
+ */
1272
+ fd_fdstat_set_rights(fd, fs_rights_base, fs_rights_inheriting) {
1273
+ const entry = this.fdTable.get(fd);
1274
+ if (!entry)
1275
+ return ERRNO_EBADF;
1276
+ // Rights can only be shrunk, not expanded
1277
+ const base = typeof fs_rights_base === 'bigint' ? fs_rights_base : BigInt(fs_rights_base);
1278
+ const inheriting = typeof fs_rights_inheriting === 'bigint' ? fs_rights_inheriting : BigInt(fs_rights_inheriting);
1279
+ entry.rightsBase = entry.rightsBase & base;
1280
+ entry.rightsInheriting = entry.rightsInheriting & inheriting;
1281
+ return ERRNO_SUCCESS;
1282
+ }
1283
+ /**
1284
+ * Read from a file descriptor at a given offset without changing the cursor.
1285
+ * Delegates to kernel file I/O bridge.
1286
+ */
1287
+ fd_pread(fd, iovs_ptr, iovs_len, offset, nread_ptr) {
1288
+ const entry = this.fdTable.get(fd);
1289
+ if (!entry)
1290
+ return ERRNO_EBADF;
1291
+ if (!(entry.rightsBase & RIGHT_FD_READ))
1292
+ return ERRNO_EBADF;
1293
+ if (entry.filetype !== FILETYPE_REGULAR_FILE)
1294
+ return ERRNO_ESPIPE;
1295
+ const iovecs = this._readIovecs(iovs_ptr, iovs_len);
1296
+ const mem = this._bytes();
1297
+ const offsetBig = typeof offset === 'bigint' ? offset : BigInt(offset);
1298
+ const totalRequested = iovecs.reduce((sum, iov) => sum + iov.buf_len, 0);
1299
+ const result = this._fileIO.fdPread(fd, totalRequested, offsetBig);
1300
+ if (result.errno !== ERRNO_SUCCESS)
1301
+ return result.errno;
1302
+ let dataOffset = 0;
1303
+ let totalRead = 0;
1304
+ for (const iov of iovecs) {
1305
+ const remaining = result.data.length - dataOffset;
1306
+ if (remaining <= 0)
1307
+ break;
1308
+ const n = Math.min(iov.buf_len, remaining);
1309
+ mem.set(result.data.subarray(dataOffset, dataOffset + n), iov.buf);
1310
+ dataOffset += n;
1311
+ totalRead += n;
1312
+ }
1313
+ this._view().setUint32(nread_ptr, totalRead, true);
1314
+ return ERRNO_SUCCESS;
1315
+ }
1316
+ /**
1317
+ * Write to a file descriptor at a given offset without changing the cursor.
1318
+ * Delegates to kernel file I/O bridge.
1319
+ */
1320
+ fd_pwrite(fd, iovs_ptr, iovs_len, offset, nwritten_ptr) {
1321
+ const entry = this.fdTable.get(fd);
1322
+ if (!entry)
1323
+ return ERRNO_EBADF;
1324
+ if (!(entry.rightsBase & RIGHT_FD_WRITE))
1325
+ return ERRNO_EBADF;
1326
+ if (entry.filetype !== FILETYPE_REGULAR_FILE)
1327
+ return ERRNO_ESPIPE;
1328
+ const iovecs = this._readIovecs(iovs_ptr, iovs_len);
1329
+ const mem = this._bytes();
1330
+ const offsetBig = typeof offset === 'bigint' ? offset : BigInt(offset);
1331
+ const chunks = [];
1332
+ let totalWritten = 0;
1333
+ for (const iov of iovecs) {
1334
+ if (iov.buf_len === 0)
1335
+ continue;
1336
+ chunks.push(mem.slice(iov.buf, iov.buf + iov.buf_len));
1337
+ totalWritten += iov.buf_len;
1338
+ }
1339
+ if (totalWritten > 0) {
1340
+ const writeData = concatBytes(chunks);
1341
+ const result = this._fileIO.fdPwrite(fd, writeData, offsetBig);
1342
+ if (result.errno !== ERRNO_SUCCESS)
1343
+ return result.errno;
1344
+ }
1345
+ this._view().setUint32(nwritten_ptr, totalWritten, true);
1346
+ return ERRNO_SUCCESS;
1347
+ }
1348
+ /**
1349
+ * Renumber a file descriptor (atomically move oldFd to newFd).
1350
+ */
1351
+ fd_renumber(from_fd, to_fd) {
1352
+ this._preopens.delete(from_fd);
1353
+ this._preopens.delete(to_fd);
1354
+ return this.fdTable.renumber(from_fd, to_fd);
1355
+ }
1356
+ /**
1357
+ * Create a hard link.
1358
+ * Not supported in our VFS -- return ENOSYS.
1359
+ */
1360
+ path_link(_old_fd, _old_flags, _old_path_ptr, _old_path_len, _new_fd, _new_path_ptr, _new_path_len) {
1361
+ return ERRNO_ENOSYS;
1362
+ }
1363
+ // --- Socket stubs (US-009) -- all return ENOSYS ---
1364
+ sock_accept(_fd, _flags, _result_fd_ptr) {
1365
+ return ERRNO_ENOSYS;
1366
+ }
1367
+ sock_recv(_fd, _ri_data_ptr, _ri_data_len, _ri_flags, _ro_datalen_ptr, _ro_flags_ptr) {
1368
+ return ERRNO_ENOSYS;
1369
+ }
1370
+ sock_send(_fd, _si_data_ptr, _si_data_len, _si_flags, _so_datalen_ptr) {
1371
+ return ERRNO_ENOSYS;
1372
+ }
1373
+ sock_shutdown(_fd, _how) {
1374
+ return ERRNO_ENOSYS;
1375
+ }
1376
+ /**
1377
+ * Get the wasi_snapshot_preview1 import object.
1378
+ * All 46 wasi_snapshot_preview1 functions.
1379
+ */
1380
+ getImports() {
1381
+ return {
1382
+ // Core fd operations (US-007)
1383
+ fd_read: (fd, iovs_ptr, iovs_len, nread_ptr) => this.fd_read(fd, iovs_ptr, iovs_len, nread_ptr),
1384
+ fd_write: (fd, iovs_ptr, iovs_len, nwritten_ptr) => this.fd_write(fd, iovs_ptr, iovs_len, nwritten_ptr),
1385
+ fd_seek: (fd, offset, whence, newoffset_ptr) => this.fd_seek(fd, offset, whence, newoffset_ptr),
1386
+ fd_tell: (fd, offset_ptr) => this.fd_tell(fd, offset_ptr),
1387
+ fd_close: (fd) => this.fd_close(fd),
1388
+ fd_fdstat_get: (fd, buf_ptr) => this.fd_fdstat_get(fd, buf_ptr),
1389
+ fd_fdstat_set_flags: (fd, flags) => this.fd_fdstat_set_flags(fd, flags),
1390
+ fd_prestat_get: (fd, buf_ptr) => this.fd_prestat_get(fd, buf_ptr),
1391
+ fd_prestat_dir_name: (fd, path_ptr, path_len) => this.fd_prestat_dir_name(fd, path_ptr, path_len),
1392
+ // Path operations (US-008)
1393
+ path_open: (dirfd, dirflags, path_ptr, path_len, oflags, fs_rights_base, fs_rights_inheriting, fdflags, opened_fd_ptr) => this.path_open(dirfd, dirflags, path_ptr, path_len, oflags, fs_rights_base, fs_rights_inheriting, fdflags, opened_fd_ptr),
1394
+ path_create_directory: (dirfd, path_ptr, path_len) => this.path_create_directory(dirfd, path_ptr, path_len),
1395
+ path_unlink_file: (dirfd, path_ptr, path_len) => this.path_unlink_file(dirfd, path_ptr, path_len),
1396
+ path_remove_directory: (dirfd, path_ptr, path_len) => this.path_remove_directory(dirfd, path_ptr, path_len),
1397
+ path_rename: (old_dirfd, old_path_ptr, old_path_len, new_dirfd, new_path_ptr, new_path_len) => this.path_rename(old_dirfd, old_path_ptr, old_path_len, new_dirfd, new_path_ptr, new_path_len),
1398
+ path_symlink: (old_path_ptr, old_path_len, dirfd, new_path_ptr, new_path_len) => this.path_symlink(old_path_ptr, old_path_len, dirfd, new_path_ptr, new_path_len),
1399
+ path_readlink: (dirfd, path_ptr, path_len, buf_ptr, buf_len, bufused_ptr) => this.path_readlink(dirfd, path_ptr, path_len, buf_ptr, buf_len, bufused_ptr),
1400
+ path_filestat_get: (dirfd, flags, path_ptr, path_len, buf_ptr) => this.path_filestat_get(dirfd, flags, path_ptr, path_len, buf_ptr),
1401
+ path_filestat_set_times: (dirfd, flags, path_ptr, path_len, atim, mtim, fst_flags) => this.path_filestat_set_times(dirfd, flags, path_ptr, path_len, atim, mtim, fst_flags),
1402
+ // FD filestat and directory operations (US-008)
1403
+ fd_filestat_get: (fd, buf_ptr) => this.fd_filestat_get(fd, buf_ptr),
1404
+ fd_filestat_set_size: (fd, size) => this.fd_filestat_set_size(fd, size),
1405
+ fd_filestat_set_times: (fd, atim, mtim, fst_flags) => this.fd_filestat_set_times(fd, atim, mtim, fst_flags),
1406
+ fd_readdir: (fd, buf_ptr, buf_len, cookie, bufused_ptr) => this.fd_readdir(fd, buf_ptr, buf_len, cookie, bufused_ptr),
1407
+ // Args, env, clock, random, process (US-009)
1408
+ args_get: (argv_ptr, argv_buf_ptr) => this.args_get(argv_ptr, argv_buf_ptr),
1409
+ args_sizes_get: (argc_ptr, argv_buf_size_ptr) => this.args_sizes_get(argc_ptr, argv_buf_size_ptr),
1410
+ environ_get: (environ_ptr, environ_buf_ptr) => this.environ_get(environ_ptr, environ_buf_ptr),
1411
+ environ_sizes_get: (environc_ptr, environ_buf_size_ptr) => this.environ_sizes_get(environc_ptr, environ_buf_size_ptr),
1412
+ clock_res_get: (id, resolution_ptr) => this.clock_res_get(id, resolution_ptr),
1413
+ clock_time_get: (id, precision, time_ptr) => this.clock_time_get(id, precision, time_ptr),
1414
+ random_get: (buf_ptr, buf_len) => this.random_get(buf_ptr, buf_len),
1415
+ proc_exit: (exitCode) => this.proc_exit(exitCode),
1416
+ proc_raise: (sig) => this.proc_raise(sig),
1417
+ sched_yield: () => this.sched_yield(),
1418
+ poll_oneoff: (in_ptr, out_ptr, nsubscriptions, nevents_ptr) => this.poll_oneoff(in_ptr, out_ptr, nsubscriptions, nevents_ptr),
1419
+ // Stub fd operations (US-009)
1420
+ fd_advise: (fd, offset, len, advice) => this.fd_advise(fd, offset, len, advice),
1421
+ fd_allocate: (fd, offset, len) => this.fd_allocate(fd, offset, len),
1422
+ fd_datasync: (fd) => this.fd_datasync(fd),
1423
+ fd_sync: (fd) => this.fd_sync(fd),
1424
+ fd_fdstat_set_rights: (fd, fs_rights_base, fs_rights_inheriting) => this.fd_fdstat_set_rights(fd, fs_rights_base, fs_rights_inheriting),
1425
+ fd_pread: (fd, iovs_ptr, iovs_len, offset, nread_ptr) => this.fd_pread(fd, iovs_ptr, iovs_len, offset, nread_ptr),
1426
+ fd_pwrite: (fd, iovs_ptr, iovs_len, offset, nwritten_ptr) => this.fd_pwrite(fd, iovs_ptr, iovs_len, offset, nwritten_ptr),
1427
+ fd_renumber: (from_fd, to_fd) => this.fd_renumber(from_fd, to_fd),
1428
+ // Path stubs (US-009)
1429
+ path_link: (old_fd, old_flags, old_path_ptr, old_path_len, new_fd, new_path_ptr, new_path_len) => this.path_link(old_fd, old_flags, old_path_ptr, old_path_len, new_fd, new_path_ptr, new_path_len),
1430
+ // Socket stubs (US-009) -- all return ENOSYS
1431
+ sock_accept: (fd, flags, result_fd_ptr) => this.sock_accept(fd, flags, result_fd_ptr),
1432
+ sock_recv: (fd, ri_data_ptr, ri_data_len, ri_flags, ro_datalen_ptr, ro_flags_ptr) => this.sock_recv(fd, ri_data_ptr, ri_data_len, ri_flags, ro_datalen_ptr, ro_flags_ptr),
1433
+ sock_send: (fd, si_data_ptr, si_data_len, si_flags, so_datalen_ptr) => this.sock_send(fd, si_data_ptr, si_data_len, si_flags, so_datalen_ptr),
1434
+ sock_shutdown: (fd, how) => this.sock_shutdown(fd, how),
1435
+ };
1436
+ }
1437
+ }
1438
+ //# sourceMappingURL=wasi-polyfill.js.map