convex-rust 0.0.0-stage → 0.1.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 (90) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +137 -2
  3. package/dist/cli/bin.d.ts +2 -0
  4. package/dist/cli/bin.js +200 -0
  5. package/dist/cli/build.d.ts +146 -0
  6. package/dist/cli/build.js +902 -0
  7. package/dist/cli/demangle.d.ts +7 -0
  8. package/dist/cli/demangle.js +328 -0
  9. package/dist/cli/diagnostics.d.ts +34 -0
  10. package/dist/cli/diagnostics.js +296 -0
  11. package/dist/cli/generate.d.ts +29 -0
  12. package/dist/cli/generate.js +82 -0
  13. package/dist/cli/manifest.d.ts +24 -0
  14. package/dist/cli/manifest.js +75 -0
  15. package/dist/cli/rustApi.d.ts +78 -0
  16. package/dist/cli/rustApi.js +609 -0
  17. package/dist/cli/scaffold.d.ts +22 -0
  18. package/dist/cli/scaffold.js +114 -0
  19. package/dist/cli/shim.d.ts +30 -0
  20. package/dist/cli/shim.js +140 -0
  21. package/dist/cli/size.d.ts +17 -0
  22. package/dist/cli/size.js +73 -0
  23. package/dist/cli/split.d.ts +20 -0
  24. package/dist/cli/split.js +140 -0
  25. package/dist/cli/symbols.d.ts +30 -0
  26. package/dist/cli/symbols.js +133 -0
  27. package/dist/cli/toolchain.d.ts +53 -0
  28. package/dist/cli/toolchain.js +167 -0
  29. package/dist/cli/validators.d.ts +6 -0
  30. package/dist/cli/validators.js +66 -0
  31. package/dist/cli/wasisdk.d.ts +82 -0
  32. package/dist/cli/wasisdk.js +220 -0
  33. package/dist/cli/wasmHints.d.ts +29 -0
  34. package/dist/cli/wasmHints.js +172 -0
  35. package/dist/runtime/base64.d.ts +4 -0
  36. package/dist/runtime/base64.js +77 -0
  37. package/dist/runtime/crypto.d.ts +7 -0
  38. package/dist/runtime/crypto.js +421 -0
  39. package/dist/runtime/fastjson.d.ts +3 -0
  40. package/dist/runtime/fastjson.js +170 -0
  41. package/dist/runtime/host.d.ts +73 -0
  42. package/dist/runtime/host.js +1230 -0
  43. package/dist/runtime/http.d.ts +40 -0
  44. package/dist/runtime/http.js +157 -0
  45. package/dist/runtime/index.d.ts +2 -0
  46. package/dist/runtime/index.js +1 -0
  47. package/dist/runtime/intl.d.ts +5 -0
  48. package/dist/runtime/intl.js +69 -0
  49. package/dist/runtime/tmpfs.d.ts +129 -0
  50. package/dist/runtime/tmpfs.js +624 -0
  51. package/dist/runtime/wasi.d.ts +33 -0
  52. package/dist/runtime/wasi.js +216 -0
  53. package/package.json +66 -4
  54. package/rust/convex_fn/Cargo.toml +32 -0
  55. package/rust/convex_fn/LICENSE +201 -0
  56. package/rust/convex_fn/build.rs +3 -0
  57. package/rust/convex_fn/src/args.rs +416 -0
  58. package/rust/convex_fn/src/crypto.rs +237 -0
  59. package/rust/convex_fn/src/ctx.rs +1044 -0
  60. package/rust/convex_fn/src/dns.rs +356 -0
  61. package/rust/convex_fn/src/error.rs +179 -0
  62. package/rust/convex_fn/src/executor.rs +577 -0
  63. package/rust/convex_fn/src/fanout.rs +106 -0
  64. package/rust/convex_fn/src/fs.rs +19 -0
  65. package/rust/convex_fn/src/function_ref.rs +219 -0
  66. package/rust/convex_fn/src/http.rs +441 -0
  67. package/rust/convex_fn/src/intl.rs +75 -0
  68. package/rust/convex_fn/src/lib.rs +242 -0
  69. package/rust/convex_fn/src/logging.rs +222 -0
  70. package/rust/convex_fn/src/native.rs +360 -0
  71. package/rust/convex_fn/src/net.rs +122 -0
  72. package/rust/convex_fn/src/sys.rs +178 -0
  73. package/rust/convex_fn/src/values.rs +121 -0
  74. package/rust/convex_fn/src/wire.rs +579 -0
  75. package/rust/convex_fn_macros/Cargo.toml +13 -0
  76. package/rust/convex_fn_macros/LICENSE +201 -0
  77. package/rust/convex_fn_macros/src/derive.rs +632 -0
  78. package/rust/convex_fn_macros/src/lib.rs +338 -0
  79. package/rust/convex_reqwest/Cargo.toml +69 -0
  80. package/rust/convex_reqwest/LICENSE +201 -0
  81. package/rust/convex_reqwest/src/blocking.rs +287 -0
  82. package/rust/convex_reqwest/src/body.rs +152 -0
  83. package/rust/convex_reqwest/src/client.rs +359 -0
  84. package/rust/convex_reqwest/src/config_types.rs +150 -0
  85. package/rust/convex_reqwest/src/error.rs +241 -0
  86. package/rust/convex_reqwest/src/into_url.rs +57 -0
  87. package/rust/convex_reqwest/src/lib.rs +42 -0
  88. package/rust/convex_reqwest/src/multipart.rs +140 -0
  89. package/rust/convex_reqwest/src/request.rs +358 -0
  90. package/rust/convex_reqwest/src/response.rs +310 -0
@@ -0,0 +1,624 @@
1
+ // In-memory `/tmp` for the guest: a WASI preview1 file system with one preopened directory.
2
+ //
3
+ // Per call and discarded after it (the host resets it when a call starts and when it ends cleanly).
4
+ // File data lives in the GUEST's linear memory when the module exports `convex_alloc` (segments are
5
+ // allocated through it and handed back through `convex_free`), so it does not count toward the JS heap
6
+ // of the isolate (64 MiB in the default Convex runtime). Without those exports (hand-written test
7
+ // modules) the segments are plain `Uint8Array`s. Either way the total is capped (`maxBytes`, default
8
+ // 64 MiB, ENOSPC beyond it). contracts/ABI.md "Optional WASI: /tmp".
9
+ export const TMP_DIR = "/tmp";
10
+ /** Default cap on the allocated bytes of all files. */
11
+ export const DEFAULT_TMP_BYTES = 64 * 1024 * 1024;
12
+ const FIRST_SEG = 4096;
13
+ const MAX_SEG = 1024 * 1024;
14
+ const PREOPEN_FD = 3;
15
+ const E = {
16
+ SUCCESS: 0,
17
+ ACCES: 2,
18
+ BADF: 8,
19
+ EXIST: 20,
20
+ INVAL: 28,
21
+ ISDIR: 31,
22
+ NOENT: 44,
23
+ NOSPC: 51,
24
+ NOSYS: 52,
25
+ NOTDIR: 54,
26
+ NOTEMPTY: 55,
27
+ PERM: 63,
28
+ SPIPE: 70,
29
+ NOTCAPABLE: 76,
30
+ };
31
+ const FT_DIR = 3;
32
+ const FT_FILE = 4;
33
+ // oflags
34
+ const O_CREAT = 1;
35
+ const O_DIRECTORY = 2;
36
+ const O_EXCL = 4;
37
+ const O_TRUNC = 8;
38
+ // fdflags
39
+ const FD_APPEND = 1;
40
+ // rights we care about (the rest are granted freely: the whole tree is the guest's own)
41
+ const RIGHT_FD_WRITE = 1n << 6n;
42
+ const ALL_RIGHTS = 0x1fffffffn;
43
+ const enc = new TextEncoder();
44
+ const dec = new TextDecoder();
45
+ const nowNs = () => BigInt(Date.now()) * 1000000n;
46
+ class Errno extends Error {
47
+ code;
48
+ constructor(code) {
49
+ super(`errno ${code}`);
50
+ this.code = code;
51
+ }
52
+ }
53
+ export class TmpFs {
54
+ maxBytes;
55
+ opts;
56
+ root;
57
+ fds = new Map();
58
+ nextFd = PREOPEN_FD + 1;
59
+ nextIno = 1;
60
+ /** Sum of the allocated segment capacities. */
61
+ used = 0;
62
+ constructor(opts) {
63
+ this.opts = opts;
64
+ this.maxBytes = opts.maxBytes ?? DEFAULT_TMP_BYTES;
65
+ this.reset(false);
66
+ }
67
+ /** Empties the file system. `release` hands the guest segments back to the guest allocator. */
68
+ reset(release) {
69
+ if (release && this.root)
70
+ this.freeTree(this.root);
71
+ this.fds.clear();
72
+ this.nextFd = PREOPEN_FD + 1;
73
+ this.used = 0;
74
+ this.root = this.mkdir(null);
75
+ }
76
+ freeTree(n) {
77
+ if (n.kind === "dir")
78
+ for (const c of n.entries.values())
79
+ this.freeTree(c);
80
+ else
81
+ this.freeSegs(n);
82
+ }
83
+ // ---- storage ----
84
+ newBase(parent) {
85
+ const t = nowNs();
86
+ return { ino: this.nextIno++, parent, atim: t, mtim: t, ctim: t, open: 0, unlinked: false };
87
+ }
88
+ mkdir(parent) {
89
+ return { ...this.newBase(parent), kind: "dir", entries: new Map() };
90
+ }
91
+ mkfile(parent) {
92
+ return { ...this.newBase(parent), kind: "file", segs: [], size: 0 };
93
+ }
94
+ segCap(i) {
95
+ return Math.min(FIRST_SEG * 2 ** Math.min(i, 20), MAX_SEG);
96
+ }
97
+ view(s) {
98
+ return s.arr ?? new Uint8Array(this.opts.memory().buffer, s.ptr, s.cap);
99
+ }
100
+ freeSegs(f) {
101
+ for (const s of f.segs) {
102
+ if (s.ptr >= 0 && this.opts.free)
103
+ this.opts.free(s.ptr, s.cap);
104
+ this.used -= s.cap;
105
+ }
106
+ f.segs = [];
107
+ f.size = 0;
108
+ }
109
+ /** Makes sure the file has segments for `size` bytes; throws ENOSPC beyond the cap or when the guest cannot allocate. */
110
+ reserve(f, size) {
111
+ let have = f.segs.reduce((n, s) => n + s.cap, 0);
112
+ while (have < size) {
113
+ const cap = this.segCap(f.segs.length);
114
+ if (this.used + cap > this.maxBytes)
115
+ throw new Errno(E.NOSPC);
116
+ let seg;
117
+ if (this.opts.alloc) {
118
+ let ptr = 0;
119
+ try {
120
+ ptr = this.opts.alloc(cap) >>> 0;
121
+ }
122
+ catch {
123
+ throw new Errno(E.NOSPC);
124
+ }
125
+ if (ptr === 0)
126
+ throw new Errno(E.NOSPC);
127
+ // convex_alloc hands out uninitialised memory, a fresh segment must read as zeros.
128
+ new Uint8Array(this.opts.memory().buffer, ptr, cap).fill(0);
129
+ seg = { ptr, cap };
130
+ }
131
+ else {
132
+ seg = { ptr: -1, arr: new Uint8Array(cap), cap };
133
+ }
134
+ f.segs.push(seg);
135
+ this.used += cap;
136
+ have += cap;
137
+ }
138
+ }
139
+ /** Copies between the file and `buf`; callers have reserved the range for writes. */
140
+ xfer(f, pos, buf, write) {
141
+ let done = 0;
142
+ let base = 0;
143
+ for (let i = 0; i < f.segs.length && done < buf.length; i++) {
144
+ const s = f.segs[i];
145
+ const end = base + s.cap;
146
+ if (pos + done < end) {
147
+ const off = pos + done - base;
148
+ const n = Math.min(s.cap - off, buf.length - done);
149
+ const v = this.view(s);
150
+ if (write)
151
+ v.set(buf.subarray(done, done + n), off);
152
+ else
153
+ buf.set(v.subarray(off, off + n), done);
154
+ done += n;
155
+ }
156
+ base = end;
157
+ }
158
+ return done;
159
+ }
160
+ zero(f, from, to) {
161
+ if (to <= from)
162
+ return;
163
+ this.xfer(f, from, new Uint8Array(to - from), true);
164
+ }
165
+ setSize(f, size) {
166
+ if (size > f.size) {
167
+ this.reserve(f, size);
168
+ this.zero(f, f.size, size); // truncated tails keep stale bytes: zero them
169
+ }
170
+ f.size = size;
171
+ }
172
+ release(n) {
173
+ if (n.kind === "file" && n.unlinked && n.open === 0)
174
+ this.freeSegs(n);
175
+ }
176
+ // ---- paths ----
177
+ dirOf(fd) {
178
+ if (fd === PREOPEN_FD)
179
+ return this.root;
180
+ const o = this.fds.get(fd);
181
+ if (!o)
182
+ throw new Errno(E.BADF);
183
+ if (o.node.kind !== "dir")
184
+ throw new Errno(E.NOTDIR);
185
+ return o.node;
186
+ }
187
+ /** Resolves `path` relative to `dir`. Returns the parent directory, the last name and the node (if it exists). */
188
+ walk(dir, path) {
189
+ if (path.startsWith("/"))
190
+ throw new Errno(E.NOTCAPABLE);
191
+ const parts = path.split("/").filter((p) => p !== "" && p !== ".");
192
+ let cur = dir;
193
+ if (parts.length === 0)
194
+ return { parent: dir, name: "", node: dir };
195
+ for (let i = 0; i < parts.length; i++) {
196
+ const last = i === parts.length - 1;
197
+ const p = parts[i];
198
+ if (p === "..") {
199
+ if (!cur.parent)
200
+ throw new Errno(E.NOTCAPABLE); // would leave the sandbox
201
+ cur = cur.parent;
202
+ if (last)
203
+ return { parent: cur.parent ?? cur, name: "..", node: cur };
204
+ continue;
205
+ }
206
+ if (p.length > 255)
207
+ throw new Errno(E.INVAL);
208
+ const next = cur.entries.get(p);
209
+ if (last)
210
+ return { parent: cur, name: p, node: next };
211
+ if (!next)
212
+ throw new Errno(E.NOENT);
213
+ if (next.kind !== "dir")
214
+ throw new Errno(E.NOTDIR);
215
+ cur = next;
216
+ }
217
+ throw new Errno(E.INVAL);
218
+ }
219
+ unlinkNode(parent, name, n) {
220
+ parent.entries.delete(name);
221
+ parent.mtim = parent.ctim = nowNs();
222
+ n.unlinked = true;
223
+ this.release(n);
224
+ }
225
+ // ---- operations (errno in, errno out) ----
226
+ open(dirfd, path, oflags, rightsBase, fdflags) {
227
+ const dir = this.dirOf(dirfd);
228
+ const { parent, name, node } = this.walk(dir, path);
229
+ let target = node;
230
+ if (!target) {
231
+ if (!(oflags & O_CREAT))
232
+ throw new Errno(E.NOENT);
233
+ if (oflags & O_DIRECTORY)
234
+ throw new Errno(E.INVAL);
235
+ if (name === "" || name === "..")
236
+ throw new Errno(E.INVAL);
237
+ const f = this.mkfile(parent);
238
+ parent.entries.set(name, f);
239
+ parent.mtim = parent.ctim = nowNs();
240
+ target = f;
241
+ }
242
+ else {
243
+ if (oflags & O_CREAT && oflags & O_EXCL)
244
+ throw new Errno(E.EXIST);
245
+ if (oflags & O_DIRECTORY && target.kind !== "dir")
246
+ throw new Errno(E.NOTDIR);
247
+ if (target.kind === "file" && oflags & O_TRUNC) {
248
+ this.setSize(target, 0);
249
+ target.mtim = target.ctim = nowNs();
250
+ }
251
+ }
252
+ if (target.kind === "dir" && oflags & O_TRUNC)
253
+ throw new Errno(E.ISDIR);
254
+ const fd = this.nextFd++;
255
+ target.open++;
256
+ this.fds.set(fd, {
257
+ node: target,
258
+ pos: 0,
259
+ append: (fdflags & FD_APPEND) !== 0,
260
+ writable: (rightsBase & RIGHT_FD_WRITE) !== 0n,
261
+ });
262
+ return fd;
263
+ }
264
+ close(fd) {
265
+ const o = this.fds.get(fd);
266
+ if (!o)
267
+ throw new Errno(E.BADF);
268
+ this.fds.delete(fd);
269
+ o.node.open--;
270
+ this.release(o.node);
271
+ }
272
+ has(fd) {
273
+ return fd === PREOPEN_FD || this.fds.has(fd);
274
+ }
275
+ file(fd) {
276
+ const o = this.fds.get(fd);
277
+ if (!o)
278
+ throw new Errno(fd === PREOPEN_FD ? E.ISDIR : E.BADF);
279
+ if (o.node.kind !== "file")
280
+ throw new Errno(E.ISDIR);
281
+ return o;
282
+ }
283
+ read(fd, bufs, at) {
284
+ const o = this.file(fd);
285
+ let pos = at ?? o.pos;
286
+ let total = 0;
287
+ for (const b of bufs) {
288
+ const n = Math.max(0, Math.min(b.length, o.node.size - pos));
289
+ if (n === 0)
290
+ break;
291
+ this.xfer(o.node, pos, b.subarray(0, n), false);
292
+ pos += n;
293
+ total += n;
294
+ if (n < b.length)
295
+ break;
296
+ }
297
+ if (at === undefined)
298
+ o.pos = pos;
299
+ o.node.atim = nowNs();
300
+ return total;
301
+ }
302
+ /** `chunks` are copied before any allocation (an allocation can grow, and so detach, the guest memory). */
303
+ write(fd, chunks, at) {
304
+ const o = this.file(fd);
305
+ if (!o.writable)
306
+ throw new Errno(E.BADF);
307
+ const len = chunks.reduce((n, c) => n + c.length, 0);
308
+ let pos = at ?? (o.append ? o.node.size : o.pos);
309
+ if (pos + len > 0xffffffff)
310
+ throw new Errno(E.NOSPC);
311
+ const joined = new Uint8Array(len);
312
+ let k = 0;
313
+ for (const c of chunks) {
314
+ joined.set(c, k);
315
+ k += c.length;
316
+ }
317
+ this.reserve(o.node, pos + len);
318
+ if (pos > o.node.size)
319
+ this.zero(o.node, o.node.size, pos);
320
+ this.xfer(o.node, pos, joined, true);
321
+ pos += len;
322
+ if (pos > o.node.size)
323
+ o.node.size = pos;
324
+ if (at === undefined)
325
+ o.pos = pos;
326
+ o.node.mtim = o.node.ctim = nowNs();
327
+ return len;
328
+ }
329
+ seek(fd, offset, whence) {
330
+ const o = this.fds.get(fd);
331
+ if (!o)
332
+ throw new Errno(E.BADF);
333
+ if (o.node.kind !== "file")
334
+ throw new Errno(E.SPIPE);
335
+ const size = o.node.size;
336
+ const base = whence === 0 ? 0 : whence === 1 ? o.pos : whence === 2 ? size : -1;
337
+ if (base < 0)
338
+ throw new Errno(E.INVAL);
339
+ const target = BigInt(base) + offset;
340
+ if (target < 0n || target > 0xffffffffn)
341
+ throw new Errno(E.INVAL);
342
+ o.pos = Number(target);
343
+ return o.pos;
344
+ }
345
+ tell(fd) {
346
+ const o = this.fds.get(fd);
347
+ if (!o)
348
+ throw new Errno(E.BADF);
349
+ return o.pos;
350
+ }
351
+ truncate(fd, size) {
352
+ const o = this.file(fd);
353
+ if (!o.writable)
354
+ throw new Errno(E.BADF);
355
+ if (size > 0xffffffffn)
356
+ throw new Errno(E.NOSPC);
357
+ this.setSize(o.node, Number(size));
358
+ o.node.mtim = o.node.ctim = nowNs();
359
+ }
360
+ allocate(fd, offset, len) {
361
+ const o = this.file(fd);
362
+ const end = offset + len;
363
+ if (end > 0xffffffffn)
364
+ throw new Errno(E.NOSPC);
365
+ if (Number(end) > o.node.size)
366
+ this.setSize(o.node, Number(end));
367
+ }
368
+ node(fd) {
369
+ if (fd === PREOPEN_FD)
370
+ return this.root;
371
+ const o = this.fds.get(fd);
372
+ if (!o)
373
+ throw new Errno(E.BADF);
374
+ return o.node;
375
+ }
376
+ stat(dirfd, path) {
377
+ const dir = this.dirOf(dirfd);
378
+ const { node } = this.walk(dir, path);
379
+ if (!node)
380
+ throw new Errno(E.NOENT);
381
+ return node;
382
+ }
383
+ createDir(dirfd, path) {
384
+ const { parent, name, node } = this.walk(this.dirOf(dirfd), path);
385
+ if (node)
386
+ throw new Errno(E.EXIST);
387
+ if (name === "")
388
+ throw new Errno(E.EXIST);
389
+ parent.entries.set(name, this.mkdir(parent));
390
+ parent.mtim = parent.ctim = nowNs();
391
+ }
392
+ removeDir(dirfd, path) {
393
+ const { parent, name, node } = this.walk(this.dirOf(dirfd), path);
394
+ if (!node)
395
+ throw new Errno(E.NOENT);
396
+ if (node.kind !== "dir")
397
+ throw new Errno(E.NOTDIR);
398
+ if (name === "" || name === "..")
399
+ throw new Errno(E.INVAL);
400
+ if (node.entries.size > 0)
401
+ throw new Errno(E.NOTEMPTY);
402
+ this.unlinkNode(parent, name, node);
403
+ }
404
+ unlink(dirfd, path) {
405
+ const { parent, name, node } = this.walk(this.dirOf(dirfd), path);
406
+ if (!node)
407
+ throw new Errno(E.NOENT);
408
+ if (node.kind === "dir")
409
+ throw new Errno(E.ISDIR);
410
+ this.unlinkNode(parent, name, node);
411
+ }
412
+ rename(oldfd, oldPath, newfd, newPath) {
413
+ const a = this.walk(this.dirOf(oldfd), oldPath);
414
+ const b = this.walk(this.dirOf(newfd), newPath);
415
+ if (!a.node)
416
+ throw new Errno(E.NOENT);
417
+ if (a.name === "" || a.name === ".." || b.name === "" || b.name === "..")
418
+ throw new Errno(E.INVAL);
419
+ if (a.node === b.node)
420
+ return;
421
+ if (a.node.kind === "dir") {
422
+ for (let p = b.parent; p; p = p.parent)
423
+ if (p === a.node)
424
+ throw new Errno(E.INVAL);
425
+ }
426
+ if (b.node) {
427
+ if (a.node.kind === "dir" && b.node.kind !== "dir")
428
+ throw new Errno(E.NOTDIR);
429
+ if (a.node.kind !== "dir" && b.node.kind === "dir")
430
+ throw new Errno(E.ISDIR);
431
+ if (b.node.kind === "dir" && b.node.entries.size > 0)
432
+ throw new Errno(E.NOTEMPTY);
433
+ this.unlinkNode(b.parent, b.name, b.node);
434
+ }
435
+ a.parent.entries.delete(a.name);
436
+ b.parent.entries.set(b.name, a.node);
437
+ a.node.parent = b.parent;
438
+ a.parent.mtim = b.parent.mtim = a.node.ctim = nowNs();
439
+ }
440
+ /** Directory listing for `fd_readdir`: `.`, `..`, then the entries in creation order. */
441
+ list(fd) {
442
+ const o = this.fds.get(fd);
443
+ const dir = fd === PREOPEN_FD ? this.root : o?.node;
444
+ if (!dir)
445
+ throw new Errno(E.BADF);
446
+ if (dir.kind !== "dir")
447
+ throw new Errno(E.NOTDIR);
448
+ return [
449
+ { name: ".", node: dir },
450
+ { name: "..", node: dir.parent ?? dir },
451
+ ...[...dir.entries].map(([name, node]) => ({ name, node })),
452
+ ];
453
+ }
454
+ setTimes(n, atim, mtim) {
455
+ if (atim !== null)
456
+ n.atim = atim;
457
+ if (mtim !== null)
458
+ n.mtim = mtim;
459
+ }
460
+ fdInfo(fd) {
461
+ const n = this.node(fd);
462
+ return { filetype: n.kind === "dir" ? FT_DIR : FT_FILE, append: this.fds.get(fd)?.append ?? false };
463
+ }
464
+ }
465
+ /** The `wasi_snapshot_preview1` file-system functions backed by `fs`. Merged into the table of `makeWasi`. */
466
+ export function tmpFsImports(fs, memory) {
467
+ const view = () => new DataView(memory().buffer);
468
+ const bytes = () => new Uint8Array(memory().buffer);
469
+ const FAULT = 21;
470
+ // Guest pointers are u32; a wasm i32 import argument arrives sign-extended when >= 2 GiB.
471
+ const str = (ptr, len) => {
472
+ ptr >>>= 0;
473
+ len >>>= 0;
474
+ const m = bytes();
475
+ if (ptr + len > m.length)
476
+ throw new Errno(FAULT);
477
+ return dec.decode(m.subarray(ptr, ptr + len));
478
+ };
479
+ const iovs = (ptr, n) => {
480
+ ptr >>>= 0;
481
+ n >>>= 0;
482
+ const v = view();
483
+ const m = bytes();
484
+ if (ptr + n * 8 > m.length)
485
+ throw new Errno(FAULT);
486
+ const out = [];
487
+ for (let i = 0; i < n; i++) {
488
+ const base = v.getUint32(ptr + i * 8, true);
489
+ const len = v.getUint32(ptr + i * 8 + 4, true);
490
+ if (base + len > m.length)
491
+ throw new Errno(FAULT);
492
+ out.push(m.subarray(base, base + len));
493
+ }
494
+ return out;
495
+ };
496
+ const guard = (f) => (...a) => {
497
+ try {
498
+ f(...a.map((x) => (typeof x === "number" ? x >>> 0 : x)));
499
+ return E.SUCCESS;
500
+ }
501
+ catch (e) {
502
+ if (e instanceof Errno)
503
+ return e.code;
504
+ if (e instanceof RangeError)
505
+ return FAULT; // pointer outside linear memory
506
+ throw e;
507
+ }
508
+ };
509
+ const writeStat = (ptr, n) => {
510
+ const v = view();
511
+ v.setBigUint64(ptr, 0n, true);
512
+ v.setBigUint64(ptr + 8, BigInt(n.ino), true);
513
+ v.setUint8(ptr + 16, n.kind === "dir" ? FT_DIR : FT_FILE);
514
+ v.setBigUint64(ptr + 24, n.unlinked ? 0n : 1n, true);
515
+ v.setBigUint64(ptr + 32, BigInt(n.kind === "file" ? n.size : 4096), true);
516
+ v.setBigUint64(ptr + 40, n.atim, true);
517
+ v.setBigUint64(ptr + 48, n.mtim, true);
518
+ v.setBigUint64(ptr + 56, n.ctim, true);
519
+ };
520
+ const times = (atim, mtim, flags) => {
521
+ const t = nowNs();
522
+ return [
523
+ flags & 1 ? atim : flags & 2 ? t : null,
524
+ flags & 4 ? mtim : flags & 8 ? t : null,
525
+ ];
526
+ };
527
+ return {
528
+ fd_prestat_get: guard((fd, ptr) => {
529
+ if (fd !== PREOPEN_FD)
530
+ throw new Errno(E.BADF);
531
+ const v = view();
532
+ v.setUint8(ptr, 0);
533
+ v.setUint32(ptr + 4, TMP_DIR.length, true);
534
+ }),
535
+ fd_prestat_dir_name: guard((fd, ptr, len) => {
536
+ if (fd !== PREOPEN_FD)
537
+ throw new Errno(E.BADF);
538
+ if (len < TMP_DIR.length)
539
+ throw new Errno(E.INVAL);
540
+ bytes().set(enc.encode(TMP_DIR), ptr);
541
+ }),
542
+ path_open: guard((dirfd, _dirflags, pathPtr, pathLen, oflags, rightsBase, _rightsInh, fdflags, outPtr) => {
543
+ const fd = fs.open(dirfd, str(pathPtr, pathLen), oflags, BigInt(rightsBase), fdflags);
544
+ view().setUint32(outPtr, fd, true);
545
+ }),
546
+ fd_close: guard((fd) => fs.close(fd)),
547
+ fd_read: guard((fd, iov, n, outPtr) => {
548
+ const r = fs.read(fd, iovs(iov, n));
549
+ view().setUint32(outPtr, r, true);
550
+ }),
551
+ fd_pread: guard((fd, iov, n, off, outPtr) => {
552
+ const r = fs.read(fd, iovs(iov, n), Number(off));
553
+ view().setUint32(outPtr, r, true);
554
+ }),
555
+ fd_pwrite: guard((fd, iov, n, off, outPtr) => {
556
+ const r = fs.write(fd, iovs(iov, n), Number(off));
557
+ view().setUint32(outPtr, r, true);
558
+ }),
559
+ fd_seek: guard((fd, off, whence, outPtr) => {
560
+ const p = fs.seek(fd, BigInt(off), whence);
561
+ view().setBigUint64(outPtr, BigInt(p), true);
562
+ }),
563
+ fd_tell: guard((fd, outPtr) => {
564
+ view().setBigUint64(outPtr, BigInt(fs.tell(fd)), true);
565
+ }),
566
+ fd_sync: guard((fd) => void fs.node(fd)),
567
+ fd_datasync: guard((fd) => void fs.node(fd)),
568
+ fd_advise: guard((fd) => void fs.node(fd)),
569
+ fd_allocate: guard((fd, off, len) => fs.allocate(fd, BigInt(off), BigInt(len))),
570
+ fd_fdstat_get: guard((fd, ptr) => {
571
+ const info = fs.fdInfo(fd);
572
+ const v = view();
573
+ v.setUint8(ptr, info.filetype);
574
+ v.setUint16(ptr + 2, info.append ? FD_APPEND : 0, true);
575
+ v.setBigUint64(ptr + 8, ALL_RIGHTS, true);
576
+ v.setBigUint64(ptr + 16, ALL_RIGHTS, true);
577
+ }),
578
+ fd_fdstat_set_flags: guard((fd) => void fs.node(fd)),
579
+ fd_filestat_get: guard((fd, ptr) => writeStat(ptr, fs.node(fd))),
580
+ fd_filestat_set_size: guard((fd, size) => fs.truncate(fd, BigInt(size))),
581
+ fd_filestat_set_times: guard((fd, a, m, flags) => {
582
+ const [at, mt] = times(BigInt(a), BigInt(m), flags);
583
+ fs.setTimes(fs.node(fd), at, mt);
584
+ }),
585
+ path_filestat_get: guard((fd, _flags, p, l, out) => writeStat(out, fs.stat(fd, str(p, l)))),
586
+ path_filestat_set_times: guard((fd, _fl, p, l, a, m, flags) => {
587
+ const [at, mt] = times(BigInt(a), BigInt(m), flags);
588
+ fs.setTimes(fs.stat(fd, str(p, l)), at, mt);
589
+ }),
590
+ path_create_directory: guard((fd, p, l) => fs.createDir(fd, str(p, l))),
591
+ path_remove_directory: guard((fd, p, l) => fs.removeDir(fd, str(p, l))),
592
+ path_unlink_file: guard((fd, p, l) => fs.unlink(fd, str(p, l))),
593
+ path_rename: guard((ofd, op, ol, nfd, np, nl) => fs.rename(ofd, str(op, ol), nfd, str(np, nl))),
594
+ path_readlink: () => E.INVAL,
595
+ path_symlink: () => E.PERM,
596
+ path_link: () => E.PERM,
597
+ fd_readdir: guard((fd, buf, bufLen, cookie, usedPtr) => {
598
+ const entries = fs.list(fd);
599
+ const out = [];
600
+ for (let i = Number(cookie); i < entries.length && out.length < bufLen; i++) {
601
+ const { name, node } = entries[i];
602
+ const nb = enc.encode(name);
603
+ const rec = new Uint8Array(24 + nb.length);
604
+ const dv = new DataView(rec.buffer);
605
+ dv.setBigUint64(0, BigInt(i + 1), true);
606
+ dv.setBigUint64(8, BigInt(node.ino), true);
607
+ dv.setUint32(16, nb.length, true);
608
+ dv.setUint8(20, node.kind === "dir" ? FT_DIR : FT_FILE);
609
+ rec.set(nb, 24);
610
+ for (const b of rec)
611
+ out.push(b);
612
+ }
613
+ const n = Math.min(out.length, bufLen);
614
+ bytes().set(out.slice(0, n), buf);
615
+ view().setUint32(usedPtr, n, true);
616
+ }),
617
+ /** fd_write for descriptors of this file system (stdout/stderr stay with the caller). */
618
+ write: (fd, iov, n, outPtr) => guard(() => {
619
+ outPtr >>>= 0;
620
+ const r = fs.write(fd, iovs(iov, n));
621
+ view().setUint32(outPtr, r, true);
622
+ })(),
623
+ };
624
+ }
@@ -0,0 +1,33 @@
1
+ import { type TmpFs } from "./tmpfs.js";
2
+ export declare const ESUCCESS = 0;
3
+ export declare const EBADF = 8;
4
+ export declare const EINVAL = 28;
5
+ export declare const ENOSYS = 52;
6
+ export interface WasiEnv {
7
+ memory(): WebAssembly.Memory;
8
+ /** Evaluated on each `environ_sizes_get` / `environ_get`, so a reused instance sees the current values. */
9
+ environ: string[] | (() => string[]);
10
+ /** One finished stdout (fd 1) / stderr (fd 2) line. */
11
+ line(fd: 1 | 2, text: string): void;
12
+ /** In-memory `/tmp` (preopened as fd 3). Without it the guest sees no preopens. */
13
+ tmp?: TmpFs;
14
+ }
15
+ export declare class ProcExit extends Error {
16
+ code: number;
17
+ constructor(code: number);
18
+ }
19
+ /** Collects bytes written to fds 1/2 and hands out complete lines. */
20
+ export declare class LineBuffer {
21
+ private emit;
22
+ private chunks;
23
+ private size;
24
+ constructor(emit: (line: string) => void);
25
+ write(chunk: Uint8Array): void;
26
+ private join;
27
+ /** Emits a trailing partial line. */
28
+ finish(): void;
29
+ }
30
+ export declare function makeWasi(env: WasiEnv, buffers: {
31
+ 1: LineBuffer;
32
+ 2: LineBuffer;
33
+ }): Record<string, (...a: any[]) => any>;