quickjs-wasi-reactor 0.0.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.
package/dist/fs-mem.js ADDED
@@ -0,0 +1,679 @@
1
+ // In-memory filesystem implementations for WASI
2
+ import * as wasi from "./wasi-defs.js";
3
+ import { Fd, Inode } from "./fd.js";
4
+ export class OpenFile extends Fd {
5
+ file;
6
+ file_pos = 0n;
7
+ constructor(file) {
8
+ super();
9
+ this.file = file;
10
+ }
11
+ fd_allocate(offset, len) {
12
+ if (this.file.size > offset + len) {
13
+ // already big enough
14
+ }
15
+ else {
16
+ const new_data = new Uint8Array(Number(offset + len));
17
+ new_data.set(this.file.data, 0);
18
+ this.file.data = new_data;
19
+ }
20
+ return wasi.ERRNO_SUCCESS;
21
+ }
22
+ fd_fdstat_get() {
23
+ return { ret: 0, fdstat: new wasi.Fdstat(wasi.FILETYPE_REGULAR_FILE, 0) };
24
+ }
25
+ fd_filestat_set_size(size) {
26
+ if (this.file.size > size) {
27
+ this.file.data = new Uint8Array(this.file.data.buffer.slice(0, Number(size)));
28
+ }
29
+ else {
30
+ const new_data = new Uint8Array(Number(size));
31
+ new_data.set(this.file.data, 0);
32
+ this.file.data = new_data;
33
+ }
34
+ return wasi.ERRNO_SUCCESS;
35
+ }
36
+ fd_read(size) {
37
+ const slice = this.file.data.slice(Number(this.file_pos), Number(this.file_pos + BigInt(size)));
38
+ this.file_pos += BigInt(slice.length);
39
+ return { ret: 0, data: slice };
40
+ }
41
+ fd_pread(size, offset) {
42
+ const slice = this.file.data.slice(Number(offset), Number(offset + BigInt(size)));
43
+ return { ret: 0, data: slice };
44
+ }
45
+ fd_seek(offset, whence) {
46
+ let calculated_offset;
47
+ switch (whence) {
48
+ case wasi.WHENCE_SET:
49
+ calculated_offset = offset;
50
+ break;
51
+ case wasi.WHENCE_CUR:
52
+ calculated_offset = this.file_pos + offset;
53
+ break;
54
+ case wasi.WHENCE_END:
55
+ calculated_offset = BigInt(this.file.data.byteLength) + offset;
56
+ break;
57
+ default:
58
+ return { ret: wasi.ERRNO_INVAL, offset: 0n };
59
+ }
60
+ if (calculated_offset < 0) {
61
+ return { ret: wasi.ERRNO_INVAL, offset: 0n };
62
+ }
63
+ this.file_pos = calculated_offset;
64
+ return { ret: 0, offset: this.file_pos };
65
+ }
66
+ fd_tell() {
67
+ return { ret: 0, offset: this.file_pos };
68
+ }
69
+ fd_write(data) {
70
+ if (this.file.readonly)
71
+ return { ret: wasi.ERRNO_BADF, nwritten: 0 };
72
+ if (this.file_pos + BigInt(data.byteLength) > this.file.size) {
73
+ const old = this.file.data;
74
+ this.file.data = new Uint8Array(Number(this.file_pos + BigInt(data.byteLength)));
75
+ this.file.data.set(old);
76
+ }
77
+ this.file.data.set(data, Number(this.file_pos));
78
+ this.file_pos += BigInt(data.byteLength);
79
+ return { ret: 0, nwritten: data.byteLength };
80
+ }
81
+ fd_pwrite(data, offset) {
82
+ if (this.file.readonly)
83
+ return { ret: wasi.ERRNO_BADF, nwritten: 0 };
84
+ if (offset + BigInt(data.byteLength) > this.file.size) {
85
+ const old = this.file.data;
86
+ this.file.data = new Uint8Array(Number(offset + BigInt(data.byteLength)));
87
+ this.file.data.set(old);
88
+ }
89
+ this.file.data.set(data, Number(offset));
90
+ return { ret: 0, nwritten: data.byteLength };
91
+ }
92
+ fd_filestat_get() {
93
+ return { ret: 0, filestat: this.file.stat() };
94
+ }
95
+ }
96
+ export class OpenDirectory extends Fd {
97
+ dir;
98
+ constructor(dir) {
99
+ super();
100
+ this.dir = dir;
101
+ }
102
+ fd_seek(_offset, _whence) {
103
+ return { ret: wasi.ERRNO_BADF, offset: 0n };
104
+ }
105
+ fd_tell() {
106
+ return { ret: wasi.ERRNO_BADF, offset: 0n };
107
+ }
108
+ fd_allocate(_offset, _len) {
109
+ return wasi.ERRNO_BADF;
110
+ }
111
+ fd_fdstat_get() {
112
+ return { ret: 0, fdstat: new wasi.Fdstat(wasi.FILETYPE_DIRECTORY, 0) };
113
+ }
114
+ fd_readdir_single(cookie) {
115
+ if (cookie == 0n) {
116
+ return {
117
+ ret: wasi.ERRNO_SUCCESS,
118
+ dirent: new wasi.Dirent(1n, this.dir.ino, ".", wasi.FILETYPE_DIRECTORY),
119
+ };
120
+ }
121
+ else if (cookie == 1n) {
122
+ return {
123
+ ret: wasi.ERRNO_SUCCESS,
124
+ dirent: new wasi.Dirent(2n, this.dir.parent_ino(), "..", wasi.FILETYPE_DIRECTORY),
125
+ };
126
+ }
127
+ if (cookie >= BigInt(this.dir.contents.size) + 2n) {
128
+ return { ret: 0, dirent: null };
129
+ }
130
+ const [name, entry] = Array.from(this.dir.contents.entries())[Number(cookie - 2n)];
131
+ return {
132
+ ret: 0,
133
+ dirent: new wasi.Dirent(cookie + 1n, entry.ino, name, entry.stat().filetype),
134
+ };
135
+ }
136
+ path_filestat_get(_flags, path_str) {
137
+ const { ret: path_err, path } = Path.from(path_str);
138
+ if (path == null) {
139
+ return { ret: path_err, filestat: null };
140
+ }
141
+ const { ret, entry } = this.dir.get_entry_for_path(path);
142
+ if (entry == null) {
143
+ return { ret, filestat: null };
144
+ }
145
+ return { ret: 0, filestat: entry.stat() };
146
+ }
147
+ path_lookup(path_str, _dirflags) {
148
+ const { ret: path_ret, path } = Path.from(path_str);
149
+ if (path == null) {
150
+ return { ret: path_ret, inode_obj: null };
151
+ }
152
+ const { ret, entry } = this.dir.get_entry_for_path(path);
153
+ if (entry == null) {
154
+ return { ret, inode_obj: null };
155
+ }
156
+ return { ret: wasi.ERRNO_SUCCESS, inode_obj: entry };
157
+ }
158
+ path_open(_dirflags, path_str, oflags, fs_rights_base, _fs_rights_inheriting, fd_flags) {
159
+ const { ret: path_ret, path } = Path.from(path_str);
160
+ if (path == null) {
161
+ return { ret: path_ret, fd_obj: null };
162
+ }
163
+ const { ret: lookupRet, entry: lookupEntry } = this.dir.get_entry_for_path(path);
164
+ let entry = lookupEntry;
165
+ if (entry == null) {
166
+ if (lookupRet != wasi.ERRNO_NOENT) {
167
+ return { ret: lookupRet, fd_obj: null };
168
+ }
169
+ if ((oflags & wasi.OFLAGS_CREAT) == wasi.OFLAGS_CREAT) {
170
+ const { ret: createRet, entry: newEntry } = this.dir.create_entry_for_path(path_str, (oflags & wasi.OFLAGS_DIRECTORY) == wasi.OFLAGS_DIRECTORY);
171
+ if (newEntry == null) {
172
+ return { ret: createRet, fd_obj: null };
173
+ }
174
+ entry = newEntry;
175
+ }
176
+ else {
177
+ return { ret: wasi.ERRNO_NOENT, fd_obj: null };
178
+ }
179
+ }
180
+ else if ((oflags & wasi.OFLAGS_EXCL) == wasi.OFLAGS_EXCL) {
181
+ return { ret: wasi.ERRNO_EXIST, fd_obj: null };
182
+ }
183
+ if ((oflags & wasi.OFLAGS_DIRECTORY) == wasi.OFLAGS_DIRECTORY &&
184
+ entry.stat().filetype !== wasi.FILETYPE_DIRECTORY) {
185
+ return { ret: wasi.ERRNO_NOTDIR, fd_obj: null };
186
+ }
187
+ return entry.path_open(oflags, fs_rights_base, fd_flags);
188
+ }
189
+ path_create_directory(path) {
190
+ return this.path_open(0, path, wasi.OFLAGS_CREAT | wasi.OFLAGS_DIRECTORY, 0n, 0n, 0).ret;
191
+ }
192
+ path_link(path_str, inode, allow_dir) {
193
+ const { ret: path_ret, path } = Path.from(path_str);
194
+ if (path == null) {
195
+ return path_ret;
196
+ }
197
+ if (path.is_dir) {
198
+ return wasi.ERRNO_NOENT;
199
+ }
200
+ const { ret: parent_ret, parent_entry, filename, entry, } = this.dir.get_parent_dir_and_entry_for_path(path, true);
201
+ if (parent_entry == null || filename == null) {
202
+ return parent_ret;
203
+ }
204
+ if (entry != null) {
205
+ const source_is_dir = inode.stat().filetype == wasi.FILETYPE_DIRECTORY;
206
+ const target_is_dir = entry.stat().filetype == wasi.FILETYPE_DIRECTORY;
207
+ if (source_is_dir && target_is_dir) {
208
+ if (allow_dir && entry instanceof Directory) {
209
+ if (entry.contents.size != 0) {
210
+ return wasi.ERRNO_NOTEMPTY;
211
+ }
212
+ }
213
+ else {
214
+ return wasi.ERRNO_EXIST;
215
+ }
216
+ }
217
+ else if (source_is_dir && !target_is_dir) {
218
+ return wasi.ERRNO_NOTDIR;
219
+ }
220
+ else if (!source_is_dir && target_is_dir) {
221
+ return wasi.ERRNO_ISDIR;
222
+ }
223
+ else if (inode.stat().filetype != wasi.FILETYPE_REGULAR_FILE ||
224
+ entry.stat().filetype != wasi.FILETYPE_REGULAR_FILE) {
225
+ return wasi.ERRNO_EXIST;
226
+ }
227
+ }
228
+ if (!allow_dir && inode.stat().filetype == wasi.FILETYPE_DIRECTORY) {
229
+ return wasi.ERRNO_PERM;
230
+ }
231
+ parent_entry.contents.set(filename, inode);
232
+ return wasi.ERRNO_SUCCESS;
233
+ }
234
+ path_unlink(path_str) {
235
+ const { ret: path_ret, path } = Path.from(path_str);
236
+ if (path == null) {
237
+ return { ret: path_ret, inode_obj: null };
238
+ }
239
+ const { ret: parent_ret, parent_entry, filename, entry, } = this.dir.get_parent_dir_and_entry_for_path(path, true);
240
+ if (parent_entry == null || filename == null) {
241
+ return { ret: parent_ret, inode_obj: null };
242
+ }
243
+ if (entry == null) {
244
+ return { ret: wasi.ERRNO_NOENT, inode_obj: null };
245
+ }
246
+ parent_entry.contents.delete(filename);
247
+ return { ret: wasi.ERRNO_SUCCESS, inode_obj: entry };
248
+ }
249
+ path_unlink_file(path_str) {
250
+ const { ret: path_ret, path } = Path.from(path_str);
251
+ if (path == null) {
252
+ return path_ret;
253
+ }
254
+ const { ret: parent_ret, parent_entry, filename, entry, } = this.dir.get_parent_dir_and_entry_for_path(path, false);
255
+ if (parent_entry == null || filename == null || entry == null) {
256
+ return parent_ret;
257
+ }
258
+ if (entry.stat().filetype === wasi.FILETYPE_DIRECTORY) {
259
+ return wasi.ERRNO_ISDIR;
260
+ }
261
+ parent_entry.contents.delete(filename);
262
+ return wasi.ERRNO_SUCCESS;
263
+ }
264
+ path_remove_directory(path_str) {
265
+ const { ret: path_ret, path } = Path.from(path_str);
266
+ if (path == null) {
267
+ return path_ret;
268
+ }
269
+ const { ret: parent_ret, parent_entry, filename, entry, } = this.dir.get_parent_dir_and_entry_for_path(path, false);
270
+ if (parent_entry == null || filename == null || entry == null) {
271
+ return parent_ret;
272
+ }
273
+ if (!(entry instanceof Directory) ||
274
+ entry.stat().filetype !== wasi.FILETYPE_DIRECTORY) {
275
+ return wasi.ERRNO_NOTDIR;
276
+ }
277
+ if (entry.contents.size !== 0) {
278
+ return wasi.ERRNO_NOTEMPTY;
279
+ }
280
+ if (!parent_entry.contents.delete(filename)) {
281
+ return wasi.ERRNO_NOENT;
282
+ }
283
+ return wasi.ERRNO_SUCCESS;
284
+ }
285
+ fd_filestat_get() {
286
+ return { ret: 0, filestat: this.dir.stat() };
287
+ }
288
+ fd_filestat_set_size(_size) {
289
+ return wasi.ERRNO_BADF;
290
+ }
291
+ fd_read(_size) {
292
+ return { ret: wasi.ERRNO_BADF, data: new Uint8Array() };
293
+ }
294
+ fd_pread(_size, _offset) {
295
+ return { ret: wasi.ERRNO_BADF, data: new Uint8Array() };
296
+ }
297
+ fd_write(_data) {
298
+ return { ret: wasi.ERRNO_BADF, nwritten: 0 };
299
+ }
300
+ fd_pwrite(_data, _offset) {
301
+ return { ret: wasi.ERRNO_BADF, nwritten: 0 };
302
+ }
303
+ }
304
+ export class PreopenDirectory extends OpenDirectory {
305
+ prestat_name;
306
+ constructor(name, contents) {
307
+ super(new Directory(contents));
308
+ this.prestat_name = name;
309
+ }
310
+ fd_prestat_get() {
311
+ return {
312
+ ret: 0,
313
+ prestat: wasi.Prestat.dir(this.prestat_name),
314
+ };
315
+ }
316
+ }
317
+ export class File extends Inode {
318
+ data;
319
+ readonly;
320
+ constructor(data, options) {
321
+ super();
322
+ this.data = new Uint8Array(data);
323
+ this.readonly = !!options?.readonly;
324
+ }
325
+ path_open(oflags, fs_rights_base, fd_flags) {
326
+ if (this.readonly &&
327
+ (fs_rights_base & BigInt(wasi.RIGHTS_FD_WRITE)) ==
328
+ BigInt(wasi.RIGHTS_FD_WRITE)) {
329
+ return { ret: wasi.ERRNO_PERM, fd_obj: null };
330
+ }
331
+ if ((oflags & wasi.OFLAGS_TRUNC) == wasi.OFLAGS_TRUNC) {
332
+ if (this.readonly)
333
+ return { ret: wasi.ERRNO_PERM, fd_obj: null };
334
+ this.data = new Uint8Array([]);
335
+ }
336
+ const file = new OpenFile(this);
337
+ if (fd_flags & wasi.FDFLAGS_APPEND)
338
+ file.fd_seek(0n, wasi.WHENCE_END);
339
+ return { ret: wasi.ERRNO_SUCCESS, fd_obj: file };
340
+ }
341
+ get size() {
342
+ return BigInt(this.data.byteLength);
343
+ }
344
+ stat() {
345
+ return new wasi.Filestat(this.ino, wasi.FILETYPE_REGULAR_FILE, this.size);
346
+ }
347
+ }
348
+ class Path {
349
+ parts = [];
350
+ is_dir = false;
351
+ static from(path) {
352
+ const result = new Path();
353
+ result.is_dir = path.endsWith("/");
354
+ if (path.startsWith("/")) {
355
+ return { ret: wasi.ERRNO_NOTCAPABLE, path: null };
356
+ }
357
+ if (path.includes("\0")) {
358
+ return { ret: wasi.ERRNO_INVAL, path: null };
359
+ }
360
+ for (const component of path.split("/")) {
361
+ if (component === "" || component === ".") {
362
+ continue;
363
+ }
364
+ if (component === "..") {
365
+ if (result.parts.pop() == undefined) {
366
+ return { ret: wasi.ERRNO_NOTCAPABLE, path: null };
367
+ }
368
+ continue;
369
+ }
370
+ result.parts.push(component);
371
+ }
372
+ return { ret: wasi.ERRNO_SUCCESS, path: result };
373
+ }
374
+ to_path_string() {
375
+ let s = this.parts.join("/");
376
+ if (this.is_dir) {
377
+ s += "/";
378
+ }
379
+ return s;
380
+ }
381
+ }
382
+ export class Directory extends Inode {
383
+ contents;
384
+ parent = null;
385
+ constructor(contents) {
386
+ super();
387
+ if (contents instanceof Array) {
388
+ this.contents = new Map(contents);
389
+ }
390
+ else {
391
+ this.contents = contents;
392
+ }
393
+ for (const entry of this.contents.values()) {
394
+ if (entry instanceof Directory) {
395
+ entry.parent = this;
396
+ }
397
+ }
398
+ }
399
+ parent_ino() {
400
+ if (this.parent == null) {
401
+ return Inode.root_ino();
402
+ }
403
+ return this.parent.ino;
404
+ }
405
+ path_open(_oflags, _fs_rights_base, _fd_flags) {
406
+ return { ret: wasi.ERRNO_SUCCESS, fd_obj: new OpenDirectory(this) };
407
+ }
408
+ stat() {
409
+ return new wasi.Filestat(this.ino, wasi.FILETYPE_DIRECTORY, 0n);
410
+ }
411
+ get_entry_for_path(path) {
412
+ const result = path.parts.reduce((acc, component) => {
413
+ if (acc.entry === null) {
414
+ return acc;
415
+ }
416
+ if (!(acc.entry instanceof Directory)) {
417
+ return { ret: wasi.ERRNO_NOTDIR, entry: null };
418
+ }
419
+ const child = acc.entry.contents.get(component);
420
+ if (child === undefined) {
421
+ return { ret: wasi.ERRNO_NOENT, entry: null };
422
+ }
423
+ return { ret: wasi.ERRNO_SUCCESS, entry: child };
424
+ }, { ret: wasi.ERRNO_SUCCESS, entry: this });
425
+ if (result.entry === null) {
426
+ return result;
427
+ }
428
+ if (path.is_dir &&
429
+ result.entry.stat().filetype != wasi.FILETYPE_DIRECTORY) {
430
+ return { ret: wasi.ERRNO_NOTDIR, entry: null };
431
+ }
432
+ return result;
433
+ }
434
+ get_parent_dir_and_entry_for_path(path, allow_undefined) {
435
+ const filename = path.parts.pop();
436
+ if (filename === undefined) {
437
+ return {
438
+ ret: wasi.ERRNO_INVAL,
439
+ parent_entry: null,
440
+ filename: null,
441
+ entry: null,
442
+ };
443
+ }
444
+ const { ret: entry_ret, entry: parent_entry } = this.get_entry_for_path(path);
445
+ if (parent_entry == null) {
446
+ return {
447
+ ret: entry_ret,
448
+ parent_entry: null,
449
+ filename: null,
450
+ entry: null,
451
+ };
452
+ }
453
+ if (!(parent_entry instanceof Directory)) {
454
+ return {
455
+ ret: wasi.ERRNO_NOTDIR,
456
+ parent_entry: null,
457
+ filename: null,
458
+ entry: null,
459
+ };
460
+ }
461
+ const entry = parent_entry.contents.get(filename);
462
+ if (entry === undefined) {
463
+ if (!allow_undefined) {
464
+ return {
465
+ ret: wasi.ERRNO_NOENT,
466
+ parent_entry: null,
467
+ filename: null,
468
+ entry: null,
469
+ };
470
+ }
471
+ return { ret: wasi.ERRNO_SUCCESS, parent_entry, filename, entry: null };
472
+ }
473
+ if (path.is_dir) {
474
+ if (entry.stat().filetype != wasi.FILETYPE_DIRECTORY) {
475
+ return {
476
+ ret: wasi.ERRNO_NOTDIR,
477
+ parent_entry: null,
478
+ filename: null,
479
+ entry: null,
480
+ };
481
+ }
482
+ }
483
+ return { ret: wasi.ERRNO_SUCCESS, parent_entry, filename, entry };
484
+ }
485
+ create_entry_for_path(path_str, is_dir) {
486
+ const { ret: path_ret, path } = Path.from(path_str);
487
+ if (path == null) {
488
+ return { ret: path_ret, entry: null };
489
+ }
490
+ const { ret: parent_ret, parent_entry, filename, entry: existingEntry, } = this.get_parent_dir_and_entry_for_path(path, true);
491
+ if (parent_entry == null || filename == null) {
492
+ return { ret: parent_ret, entry: null };
493
+ }
494
+ if (existingEntry != null) {
495
+ return { ret: wasi.ERRNO_EXIST, entry: null };
496
+ }
497
+ const newChild = is_dir
498
+ ? new Directory(new Map())
499
+ : new File(new ArrayBuffer(0));
500
+ parent_entry.contents.set(filename, newChild);
501
+ return { ret: wasi.ERRNO_SUCCESS, entry: newChild };
502
+ }
503
+ }
504
+ export class ConsoleStdout extends Fd {
505
+ ino;
506
+ write;
507
+ constructor(write) {
508
+ super();
509
+ this.ino = Inode.issue_ino();
510
+ this.write = write;
511
+ }
512
+ fd_filestat_get() {
513
+ const filestat = new wasi.Filestat(this.ino, wasi.FILETYPE_CHARACTER_DEVICE, BigInt(0));
514
+ return { ret: 0, filestat };
515
+ }
516
+ fd_fdstat_get() {
517
+ const fdstat = new wasi.Fdstat(wasi.FILETYPE_CHARACTER_DEVICE, 0);
518
+ fdstat.fs_rights_base = BigInt(wasi.RIGHTS_FD_WRITE);
519
+ return { ret: 0, fdstat };
520
+ }
521
+ fd_write(data) {
522
+ this.write(data);
523
+ return { ret: 0, nwritten: data.byteLength };
524
+ }
525
+ static lineBuffered(write) {
526
+ const dec = new TextDecoder("utf-8", { fatal: false });
527
+ let line_buf = "";
528
+ return new ConsoleStdout((buffer) => {
529
+ line_buf += dec.decode(buffer, { stream: true });
530
+ const lines = line_buf.split("\n");
531
+ for (const [i, line] of lines.entries()) {
532
+ if (i < lines.length - 1) {
533
+ write(line);
534
+ }
535
+ else {
536
+ line_buf = line;
537
+ }
538
+ }
539
+ });
540
+ }
541
+ }
542
+ /**
543
+ * PollableStdin is a stdin implementation that supports async polling.
544
+ * Data can be pushed to it at any time, and poll_oneoff will detect when data is available.
545
+ */
546
+ export class PollableStdin extends Fd {
547
+ ino;
548
+ buffer = [];
549
+ bufferSize = 0;
550
+ closed = false;
551
+ constructor() {
552
+ super();
553
+ this.ino = Inode.issue_ino();
554
+ }
555
+ /** Push data to be read from stdin */
556
+ push(data) {
557
+ if (this.closed)
558
+ return;
559
+ this.buffer.push(data);
560
+ this.bufferSize += data.byteLength;
561
+ }
562
+ /** Close stdin (future reads will return EOF) */
563
+ close() {
564
+ this.closed = true;
565
+ }
566
+ /** Check if data is available */
567
+ hasData() {
568
+ return this.bufferSize > 0;
569
+ }
570
+ /** Check if closed */
571
+ isClosed() {
572
+ return this.closed;
573
+ }
574
+ fd_fdstat_get() {
575
+ const fdstat = new wasi.Fdstat(wasi.FILETYPE_CHARACTER_DEVICE, 0);
576
+ fdstat.fs_rights_base = BigInt(wasi.RIGHTS_FD_READ);
577
+ return { ret: 0, fdstat };
578
+ }
579
+ fd_filestat_get() {
580
+ const filestat = new wasi.Filestat(this.ino, wasi.FILETYPE_CHARACTER_DEVICE, BigInt(0));
581
+ return { ret: 0, filestat };
582
+ }
583
+ fd_read(size) {
584
+ if (this.bufferSize === 0) {
585
+ return { ret: 0, data: new Uint8Array(0) };
586
+ }
587
+ const result = [];
588
+ let remaining = size;
589
+ while (remaining > 0 && this.buffer.length > 0) {
590
+ const chunk = this.buffer[0];
591
+ if (chunk.byteLength <= remaining) {
592
+ result.push(...chunk);
593
+ remaining -= chunk.byteLength;
594
+ this.bufferSize -= chunk.byteLength;
595
+ this.buffer.shift();
596
+ }
597
+ else {
598
+ result.push(...chunk.slice(0, remaining));
599
+ this.buffer[0] = chunk.slice(remaining);
600
+ this.bufferSize -= remaining;
601
+ remaining = 0;
602
+ }
603
+ }
604
+ return { ret: 0, data: new Uint8Array(result) };
605
+ }
606
+ fd_poll(eventtype) {
607
+ if (eventtype === wasi.EVENTTYPE_FD_READ) {
608
+ const hasData = this.bufferSize > 0;
609
+ const isClosed = this.closed;
610
+ return {
611
+ ready: hasData || isClosed,
612
+ nbytes: BigInt(this.bufferSize),
613
+ flags: isClosed && !hasData ? wasi.EVENTRWFLAGS_FD_READWRITE_HANGUP : 0,
614
+ };
615
+ }
616
+ return { ready: false, nbytes: 0n, flags: 0 };
617
+ }
618
+ }
619
+ /**
620
+ * DevOut is a write-only file descriptor for output.
621
+ * Used for /dev/out to send data from QuickJS to the host.
622
+ */
623
+ export class DevOut extends Fd {
624
+ ino;
625
+ onWrite;
626
+ constructor(onWrite) {
627
+ super();
628
+ this.ino = Inode.issue_ino();
629
+ this.onWrite = onWrite;
630
+ }
631
+ fd_fdstat_get() {
632
+ const fdstat = new wasi.Fdstat(wasi.FILETYPE_CHARACTER_DEVICE, 0);
633
+ fdstat.fs_rights_base = BigInt(wasi.RIGHTS_FD_WRITE);
634
+ return { ret: 0, fdstat };
635
+ }
636
+ fd_filestat_get() {
637
+ const filestat = new wasi.Filestat(this.ino, wasi.FILETYPE_CHARACTER_DEVICE, BigInt(0));
638
+ return { ret: 0, filestat };
639
+ }
640
+ fd_write(data) {
641
+ this.onWrite(data);
642
+ return { ret: 0, nwritten: data.byteLength };
643
+ }
644
+ fd_poll(eventtype) {
645
+ if (eventtype === wasi.EVENTTYPE_FD_WRITE) {
646
+ return { ready: true, nbytes: 0n, flags: 0 };
647
+ }
648
+ return { ready: false, nbytes: 0n, flags: 0 };
649
+ }
650
+ }
651
+ /**
652
+ * DevDirectory is a preopened directory that contains custom device files.
653
+ */
654
+ export class DevDirectory extends Fd {
655
+ devices;
656
+ prestat_name;
657
+ constructor(name, devices) {
658
+ super();
659
+ this.prestat_name = name;
660
+ this.devices = devices;
661
+ }
662
+ fd_fdstat_get() {
663
+ return { ret: 0, fdstat: new wasi.Fdstat(wasi.FILETYPE_DIRECTORY, 0) };
664
+ }
665
+ fd_prestat_get() {
666
+ return {
667
+ ret: 0,
668
+ prestat: wasi.Prestat.dir(this.prestat_name),
669
+ };
670
+ }
671
+ path_open(_dirflags, path, _oflags, _fs_rights_base, _fs_rights_inheriting, _fd_flags) {
672
+ const device = this.devices.get(path);
673
+ if (device) {
674
+ return { ret: 0, fd_obj: device };
675
+ }
676
+ return { ret: wasi.ERRNO_NOENT, fd_obj: null };
677
+ }
678
+ }
679
+ //# sourceMappingURL=fs-mem.js.map