@bytecodealliance/preview2-shim 0.20.1 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,10 @@
1
- import { createReadStream, createWriteStream, read } from "node:fs";
1
+ import fs, { createReadStream, createWriteStream, read } from "node:fs";
2
2
  import process, { hrtime, nextTick, stderr, stdout } from "node:process";
3
3
  import { PassThrough, Readable } from "node:stream";
4
4
  import { parentPort, workerData } from "node:worker_threads";
5
5
  import { Agent, createServer, request } from "node:http";
6
6
  import { Agent as Agent$1, request as request$1 } from "node:https";
7
+ import { promisify } from "node:util";
7
8
  import { Server, Socket, isIP } from "node:net";
8
9
  import { lookup } from "node:dns/promises";
9
10
  import { createSocket } from "node:dgram";
@@ -78,6 +79,17 @@ var calls_exports = /* @__PURE__ */ __exportAll({
78
79
  CLOCKS_DURATION_SUBSCRIBE: () => CLOCKS_DURATION_SUBSCRIBE,
79
80
  CLOCKS_INSTANT_SUBSCRIBE: () => CLOCKS_INSTANT_SUBSCRIBE,
80
81
  FILE: () => FILE,
82
+ FILESYSTEM_DESCRIPTOR_CLOSE: () => FILESYSTEM_DESCRIPTOR_CLOSE,
83
+ FILESYSTEM_DESCRIPTOR_GET_TYPE: () => FILESYSTEM_DESCRIPTOR_GET_TYPE,
84
+ FILESYSTEM_DESCRIPTOR_METADATA_HASH: () => FILESYSTEM_DESCRIPTOR_METADATA_HASH,
85
+ FILESYSTEM_DESCRIPTOR_OPEN: () => FILESYSTEM_DESCRIPTOR_OPEN,
86
+ FILESYSTEM_DESCRIPTOR_READ: () => FILESYSTEM_DESCRIPTOR_READ,
87
+ FILESYSTEM_DESCRIPTOR_SET_SIZE: () => FILESYSTEM_DESCRIPTOR_SET_SIZE,
88
+ FILESYSTEM_DESCRIPTOR_SET_TIMES: () => FILESYSTEM_DESCRIPTOR_SET_TIMES,
89
+ FILESYSTEM_DESCRIPTOR_STAT: () => FILESYSTEM_DESCRIPTOR_STAT,
90
+ FILESYSTEM_DESCRIPTOR_SYNC: () => FILESYSTEM_DESCRIPTOR_SYNC,
91
+ FILESYSTEM_DESCRIPTOR_SYNC_DATA: () => FILESYSTEM_DESCRIPTOR_SYNC_DATA,
92
+ FILESYSTEM_DESCRIPTOR_WRITE: () => FILESYSTEM_DESCRIPTOR_WRITE,
81
93
  FUTURE_DISPOSE: () => FUTURE_DISPOSE,
82
94
  FUTURE_SUBSCRIBE: () => FUTURE_SUBSCRIBE,
83
95
  FUTURE_TAKE_VALUE: () => FUTURE_TAKE_VALUE,
@@ -195,6 +207,17 @@ const OUTPUT_STREAM_BLOCKING_SPLICE = ++call_id << 24;
195
207
  const OUTPUT_STREAM_SUBSCRIBE = ++call_id << 24;
196
208
  const OUTPUT_STREAM_DISPOSE = ++call_id << 24;
197
209
  const OUTPUT_STREAM_GET_TOTAL_BYTES = ++call_id << 24;
210
+ const FILESYSTEM_DESCRIPTOR_OPEN = ++call_id << 24;
211
+ const FILESYSTEM_DESCRIPTOR_CLOSE = ++call_id << 24;
212
+ const FILESYSTEM_DESCRIPTOR_SYNC_DATA = ++call_id << 24;
213
+ const FILESYSTEM_DESCRIPTOR_GET_TYPE = ++call_id << 24;
214
+ const FILESYSTEM_DESCRIPTOR_SET_SIZE = ++call_id << 24;
215
+ const FILESYSTEM_DESCRIPTOR_SET_TIMES = ++call_id << 24;
216
+ const FILESYSTEM_DESCRIPTOR_READ = ++call_id << 24;
217
+ const FILESYSTEM_DESCRIPTOR_WRITE = ++call_id << 24;
218
+ const FILESYSTEM_DESCRIPTOR_SYNC = ++call_id << 24;
219
+ const FILESYSTEM_DESCRIPTOR_STAT = ++call_id << 24;
220
+ const FILESYSTEM_DESCRIPTOR_METADATA_HASH = ++call_id << 24;
198
221
  const POLL_POLLABLE_READY = ++call_id << 24;
199
222
  const POLL_POLLABLE_BLOCK = ++call_id << 24;
200
223
  const POLL_POLLABLE_DISPOSE = ++call_id << 24;
@@ -403,6 +426,117 @@ function getFirstError(e) {
403
426
  return e;
404
427
  }
405
428
  //#endregion
429
+ //#region dist/io/worker-filesystem.js
430
+ const closeAsync = promisify(fs.close);
431
+ const fdatasyncAsync = promisify(fs.fdatasync);
432
+ const fstatAsync = promisify(fs.fstat);
433
+ const fsyncAsync = promisify(fs.fsync);
434
+ const ftruncateAsync = promisify(fs.ftruncate);
435
+ const futimesAsync = promisify(fs.futimes);
436
+ const openAsync = promisify(fs.open);
437
+ const readAsync = promisify(fs.read);
438
+ const writeAsync = promisify(fs.write);
439
+ let nextFileResourceId = 0;
440
+ const fileResources = /* @__PURE__ */ new Map();
441
+ function lookupType(obj) {
442
+ if (obj.isFile()) return "regular-file";
443
+ if (obj.isSocket()) return "socket";
444
+ if (obj.isSymbolicLink()) return "symbolic-link";
445
+ if (obj.isFIFO()) return "fifo";
446
+ if (obj.isDirectory()) return "directory";
447
+ if (obj.isCharacterDevice()) return "character-device";
448
+ if (obj.isBlockDevice()) return "block-device";
449
+ return "unknown";
450
+ }
451
+ function getFileResource(id) {
452
+ const resource = fileResources.get(id);
453
+ if (!resource) {
454
+ const error = /* @__PURE__ */ new Error(`Invalid filesystem descriptor resource: ${id}`);
455
+ error.code = "EBADF";
456
+ throw error;
457
+ }
458
+ return resource;
459
+ }
460
+ async function openFileResource(path, flags) {
461
+ const fd = await openAsync(path, flags);
462
+ let type;
463
+ try {
464
+ type = lookupType(await fstatAsync(fd, { bigint: true }));
465
+ } catch (error) {
466
+ await closeAsync(fd);
467
+ throw error;
468
+ }
469
+ const id = ++nextFileResourceId;
470
+ fileResources.set(id, {
471
+ fd,
472
+ refs: 1
473
+ });
474
+ return {
475
+ id,
476
+ type
477
+ };
478
+ }
479
+ function retainFileResource(id) {
480
+ getFileResource(id).refs++;
481
+ return id;
482
+ }
483
+ async function releaseFileResource(id) {
484
+ const resource = getFileResource(id);
485
+ if (--resource.refs === 0) {
486
+ fileResources.delete(id);
487
+ await closeAsync(resource.fd);
488
+ }
489
+ }
490
+ function fileResourceFd(id) {
491
+ return getFileResource(id).fd;
492
+ }
493
+ async function syncFileResourceData(id) {
494
+ await fdatasyncAsync(fileResourceFd(id));
495
+ }
496
+ async function getFileResourceType(id) {
497
+ return lookupType(await fstatAsync(fileResourceFd(id), { bigint: true }));
498
+ }
499
+ async function setFileResourceSize(id, size) {
500
+ await ftruncateAsync(fileResourceFd(id), size);
501
+ }
502
+ async function setFileResourceTimes(id, atime, mtime) {
503
+ await futimesAsync(fileResourceFd(id), atime, mtime);
504
+ }
505
+ async function readFileResource(id, length, offset) {
506
+ const buffer = new Uint8Array(length);
507
+ const result = await readAsync(fileResourceFd(id), buffer, 0, length, offset);
508
+ const bytesRead = typeof result === "number" ? result : result.bytesRead;
509
+ return {
510
+ bytes: buffer.subarray(0, bytesRead),
511
+ eof: bytesRead === 0
512
+ };
513
+ }
514
+ async function writeFileResource(id, buffer, offset) {
515
+ const result = await writeAsync(fileResourceFd(id), buffer, 0, buffer.byteLength, offset);
516
+ return typeof result === "number" ? result : result.bytesWritten;
517
+ }
518
+ async function syncFileResource(id) {
519
+ await fsyncAsync(fileResourceFd(id));
520
+ }
521
+ async function statFileResource(id) {
522
+ const stats = await fstatAsync(fileResourceFd(id), { bigint: true });
523
+ return {
524
+ type: lookupType(stats),
525
+ nlink: stats.nlink,
526
+ size: stats.size,
527
+ atimeNs: stats.atimeNs,
528
+ mtimeNs: stats.mtimeNs,
529
+ ctimeNs: stats.ctimeNs
530
+ };
531
+ }
532
+ async function metadataHashFileResource(id) {
533
+ const stats = await fstatAsync(fileResourceFd(id), { bigint: true });
534
+ return {
535
+ upper: stats.mtimeNs,
536
+ lower: stats.ino
537
+ };
538
+ }
539
+ //#endregion
406
540
  //#region dist/io/worker-sockets.js
407
541
  let stateCnt = 0;
408
542
  const SOCKET_STATE_INIT = ++stateCnt;
@@ -585,18 +719,19 @@ async function getDefaultReceiveBufferSize() {
585
719
  }
586
720
  //#endregion
587
721
  //#region dist/io/worker-socket-tcp.js
588
- const { TCP, constants: TCPConstants } = process.binding("tcp_wrap");
589
722
  const win = process.platform === "win32";
590
723
  const tcpSockets = /* @__PURE__ */ new Map();
591
724
  let tcpSocketCnt = 0;
592
725
  function createTcpSocket() {
593
- const handle = new TCP(TCPConstants.SOCKET);
594
726
  tcpSockets.set(++tcpSocketCnt, {
595
727
  state: SOCKET_STATE_INIT,
596
728
  future: null,
597
729
  tcpSocket: null,
730
+ server: null,
731
+ localAddress: null,
598
732
  listenBacklogSize: 128,
599
- handle,
733
+ keepAlive: false,
734
+ keepAliveIdleTime: 0,
600
735
  pendingAccepts: [],
601
736
  pollState: {
602
737
  ready: true,
@@ -619,7 +754,7 @@ function socketTcpFinish(id, fromState, toState) {
619
754
  throw val;
620
755
  } else {
621
756
  socket.state = toState;
622
- if (toState === SOCKET_STATE_LISTENER) socket.pollState.ready = false;
757
+ if (toState === SOCKET_STATE_LISTENER) socket.pollState.ready = socket.pendingAccepts.length > 0;
623
758
  return val;
624
759
  }
625
760
  }
@@ -628,85 +763,109 @@ function socketTcpBindStart(id, localAddress, family) {
628
763
  if (socket.state !== SOCKET_STATE_INIT) throw "invalid-state";
629
764
  if (family !== localAddress.tag || !isUnicastIpAddress(localAddress) || isIPv4MappedAddress(localAddress)) throw "invalid-argument";
630
765
  socket.state = SOCKET_STATE_BIND;
631
- const { handle } = socket;
632
- socket.future = createFuture((async () => {
766
+ socket.future = createFuture(new Promise((resolve, reject) => {
633
767
  const address = serializeIpAddress(localAddress);
634
768
  const port = localAddress.val.port;
635
- const code = localAddress.tag === "ipv6" ? handle.bind6(address, port, TCPConstants.UV_TCP_IPV6ONLY) : handle.bind(address, port);
636
- if (code !== 0) throw convertSocketErrorCode(-code);
637
- {
638
- const code = handle.getsockname({});
639
- if (code !== 0) throw convertSocketErrorCode(-code);
769
+ const server = socket.server = createTcpServer(socket);
770
+ function handleErr(err) {
771
+ server.off("listening", handleListen);
772
+ reject(convertSocketError(err));
640
773
  }
641
- })(), socket.pollState);
774
+ function handleListen() {
775
+ server.off("error", handleErr);
776
+ const boundAddress = server.address();
777
+ if (!boundAddress || typeof boundAddress === "string") {
778
+ reject("invalid-state");
779
+ return;
780
+ }
781
+ socket.localAddress = ipSocketAddress(boundAddress.family.toLowerCase(), boundAddress.address, boundAddress.port);
782
+ resolve();
783
+ }
784
+ server.once("listening", handleListen);
785
+ server.once("error", handleErr);
786
+ server.listen({
787
+ host: address ?? void 0,
788
+ port,
789
+ backlog: socket.listenBacklogSize,
790
+ ipv6Only: family === "ipv6"
791
+ });
792
+ }), socket.pollState);
793
+ }
794
+ function createTcpServer(socket) {
795
+ const server = new Server({
796
+ pauseOnConnect: true,
797
+ allowHalfOpen: true
798
+ });
799
+ server.on("connection", (tcpSocket) => {
800
+ if (socket.state !== SOCKET_STATE_LISTEN && socket.state !== SOCKET_STATE_LISTENER) {
801
+ tcpSocket.destroy();
802
+ return;
803
+ }
804
+ pollStateReady(socket.pollState);
805
+ const pollState = createReadableStreamPollState(tcpSocket);
806
+ socket.pendingAccepts.push({
807
+ tcpSocket,
808
+ err: null,
809
+ pollState
810
+ });
811
+ });
812
+ server.on("error", (err) => {
813
+ if (socket.state === SOCKET_STATE_LISTEN || socket.state === SOCKET_STATE_LISTENER) {
814
+ pollStateReady(socket.pollState);
815
+ socket.pendingAccepts.push({
816
+ tcpSocket: null,
817
+ err,
818
+ pollState: null
819
+ });
820
+ }
821
+ });
822
+ return server;
823
+ }
824
+ function closeTcpServer(socket) {
825
+ const server = socket.server;
826
+ socket.server = null;
827
+ if (!server) return Promise.resolve();
828
+ return new Promise((resolve, reject) => {
829
+ server.close((err) => err ? reject(err) : resolve());
830
+ });
642
831
  }
643
832
  function socketTcpConnectStart(id, remoteAddress, family) {
644
833
  const socket = tcpSockets.get(id);
645
834
  if (socket.state !== SOCKET_STATE_INIT && socket.state !== SOCKET_STATE_BOUND) throw "invalid-state";
646
835
  if (isWildcardAddress(remoteAddress) || family !== remoteAddress.tag || !isUnicastIpAddress(remoteAddress) || isMulticastIpAddress(remoteAddress) || remoteAddress.val.port === 0 || isIPv4MappedAddress(remoteAddress)) throw "invalid-argument";
647
836
  socket.state = SOCKET_STATE_CONNECT;
648
- socket.future = createFuture(new Promise((resolve, reject) => {
649
- const tcpSocket = socket.tcpSocket = new Socket({
650
- handle: socket.handle,
651
- pauseOnCreate: true,
652
- allowHalfOpen: true
653
- });
654
- function handleErr(err) {
655
- tcpSocket.off("connect", handleConnect);
656
- reject(convertSocketError(err));
657
- }
658
- function handleConnect() {
659
- tcpSocket.off("error", handleErr);
660
- resolve([createReadableStream(tcpSocket), createWritableStream(tcpSocket)]);
661
- }
662
- tcpSocket.once("connect", handleConnect);
663
- tcpSocket.once("error", handleErr);
664
- tcpSocket.connect({
665
- port: remoteAddress.val.port,
666
- host: serializeIpAddress(remoteAddress) ?? void 0,
667
- lookup: noLookup
837
+ socket.future = createFuture((async () => {
838
+ const localAddress = socket.localAddress;
839
+ await closeTcpServer(socket);
840
+ const tcpSocket = socket.tcpSocket = new Socket({ allowHalfOpen: true });
841
+ tcpSocket.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
842
+ await new Promise((resolve, reject) => {
843
+ function handleErr(err) {
844
+ tcpSocket.off("connect", handleConnect);
845
+ reject(convertSocketError(err));
846
+ }
847
+ function handleConnect() {
848
+ tcpSocket.off("error", handleErr);
849
+ resolve();
850
+ }
851
+ tcpSocket.once("connect", handleConnect);
852
+ tcpSocket.once("error", handleErr);
853
+ tcpSocket.connect({
854
+ port: remoteAddress.val.port,
855
+ host: serializeIpAddress(remoteAddress) ?? void 0,
856
+ lookup: noLookup,
857
+ localAddress: localAddress ? serializeIpAddress(localAddress) ?? void 0 : void 0,
858
+ localPort: localAddress?.val.port
859
+ });
668
860
  });
669
- }), socket.pollState);
861
+ return [createReadableStream(tcpSocket), createWritableStream(tcpSocket)];
862
+ })(), socket.pollState);
670
863
  }
671
864
  function socketTcpListenStart(id) {
672
865
  const socket = tcpSockets.get(id);
673
866
  if (socket.state !== SOCKET_STATE_BOUND) throw "invalid-state";
674
- const { handle } = socket;
675
867
  socket.state = SOCKET_STATE_LISTEN;
676
- socket.future = createFuture(new Promise((resolve, reject) => {
677
- const server = new Server({
678
- pauseOnConnect: true,
679
- allowHalfOpen: true
680
- });
681
- function handleErr(err) {
682
- server.off("listening", handleListen);
683
- reject(convertSocketError(err));
684
- }
685
- function handleListen() {
686
- server.off("error", handleErr);
687
- server.on("connection", (tcpSocket) => {
688
- pollStateReady(socket.pollState);
689
- const pollState = createReadableStreamPollState(tcpSocket);
690
- socket.pendingAccepts.push({
691
- tcpSocket,
692
- err: null,
693
- pollState
694
- });
695
- });
696
- server.on("error", (err) => {
697
- pollStateReady(socket.pollState);
698
- socket.pendingAccepts.push({
699
- tcpSocket: null,
700
- err,
701
- pollState: null
702
- });
703
- });
704
- resolve();
705
- }
706
- server.once("listening", handleListen);
707
- server.once("error", handleErr);
708
- server.listen(handle, socket.listenBacklogSize);
709
- }), socket.pollState);
868
+ socket.future = createFuture(Promise.resolve(), socket.pollState);
710
869
  }
711
870
  function socketTcpAccept(id) {
712
871
  const socket = tcpSockets.get(id);
@@ -722,8 +881,11 @@ function socketTcpAccept(id) {
722
881
  state: SOCKET_STATE_CONNECTION,
723
882
  future: null,
724
883
  tcpSocket: accept.tcpSocket,
884
+ server: null,
885
+ localAddress: null,
725
886
  listenBacklogSize: 128,
726
- handle: accept.tcpSocket?._handle,
887
+ keepAlive: false,
888
+ keepAliveIdleTime: 0,
727
889
  pendingAccepts: [],
728
890
  pollState: accept.pollState
729
891
  });
@@ -740,18 +902,16 @@ function socketTcpSetListenBacklogSize(id, backlogSize) {
740
902
  socket.listenBacklogSize = Number(backlogSize);
741
903
  }
742
904
  function socketTcpGetLocalAddress(id) {
743
- const { handle } = tcpSockets.get(id);
744
- const out = {};
745
- const code = handle.getsockname(out);
746
- if (code !== 0) throw convertSocketErrorCode(-code);
747
- return ipSocketAddress(out.family.toLowerCase(), out.address, out.port);
905
+ const socket = tcpSockets.get(id);
906
+ const address = socket.tcpSocket?.address();
907
+ if (address && typeof address !== "string" && "family" in address) return ipSocketAddress(address.family.toLowerCase(), address.address, address.port);
908
+ if (socket.localAddress) return socket.localAddress;
909
+ throw "invalid-state";
748
910
  }
749
911
  function socketTcpGetRemoteAddress(id) {
750
- const { handle } = tcpSockets.get(id);
751
- const out = {};
752
- const code = handle.getpeername(out);
753
- if (code !== 0) throw convertSocketErrorCode(-code);
754
- return ipSocketAddress(out.family.toLowerCase(), out.address, out.port);
912
+ const { tcpSocket } = tcpSockets.get(id);
913
+ if (!tcpSocket?.remoteFamily || !tcpSocket.remoteAddress || !tcpSocket.remotePort) throw "invalid-state";
914
+ return ipSocketAddress(tcpSocket.remoteFamily.toLowerCase(), tcpSocket.remoteAddress, tcpSocket.remotePort);
755
915
  }
756
916
  function socketTcpShutdown(id, _shutdownType) {
757
917
  const socket = tcpSockets.get(id);
@@ -760,14 +920,16 @@ function socketTcpShutdown(id, _shutdownType) {
760
920
  else socket.tcpSocket?.destroy();
761
921
  }
762
922
  function socketTcpSetKeepAlive(id, { keepAlive, keepAliveIdleTime }) {
763
- const { handle } = tcpSockets.get(id);
764
- const code = handle.setKeepAlive(keepAlive, Number(keepAliveIdleTime / 1000000000n));
765
- if (code !== 0) throw convertSocketErrorCode(-code);
923
+ const socket = tcpSockets.get(id);
924
+ socket.keepAlive = keepAlive;
925
+ socket.keepAliveIdleTime = Number(keepAliveIdleTime / 1000000000n);
926
+ socket.tcpSocket?.setKeepAlive(socket.keepAlive, socket.keepAliveIdleTime);
766
927
  }
767
928
  function socketTcpDispose(id) {
768
929
  const socket = tcpSockets.get(id);
769
930
  verifyPollsDroppedForDrop(socket.pollState, "tcp socket");
770
- socket.handle.close();
931
+ socket.server?.close();
932
+ socket.tcpSocket?.destroy();
771
933
  tcpSockets.delete(id);
772
934
  }
773
935
  //#endregion
@@ -1067,9 +1229,10 @@ async function socketOutgoingDatagramStreamSend(id, datagrams) {
1067
1229
  const address = remoteAddress ? serializeIpAddress(remoteAddress) : socket.remoteAddress;
1068
1230
  const port = remoteAddress?.val.port ?? socket.remotePort;
1069
1231
  let sendLastBatch = true;
1070
- if (sendQueue.length > 0) if (sendQueueAddress === address && sendQueuePort === port) sendQueue.push(data);
1071
- else sendLastBatch = true;
1072
- else {
1232
+ if (sendQueue.length > 0) {
1233
+ if (sendQueueAddress === address && sendQueuePort === port) sendQueue.push(data);
1234
+ else sendLastBatch = true;
1235
+ } else {
1073
1236
  sendQueueAddress = address;
1074
1237
  sendQueuePort = port;
1075
1238
  sendQueue.push(data);
@@ -1419,24 +1582,41 @@ function handle(call, id, payload) {
1419
1582
  subscribeInstant(pollState, payload);
1420
1583
  return createPoll(pollState);
1421
1584
  }
1585
+ case FILESYSTEM_DESCRIPTOR_OPEN: return openFileResource(payload.path, payload.flags);
1586
+ case FILESYSTEM_DESCRIPTOR_CLOSE: return releaseFileResource(id);
1587
+ case FILESYSTEM_DESCRIPTOR_SYNC_DATA: return syncFileResourceData(id);
1588
+ case FILESYSTEM_DESCRIPTOR_GET_TYPE: return getFileResourceType(id);
1589
+ case FILESYSTEM_DESCRIPTOR_SET_SIZE: return setFileResourceSize(id, payload);
1590
+ case FILESYSTEM_DESCRIPTOR_SET_TIMES: return setFileResourceTimes(id, payload.atime, payload.mtime);
1591
+ case FILESYSTEM_DESCRIPTOR_READ: return readFileResource(id, payload.length, payload.offset);
1592
+ case FILESYSTEM_DESCRIPTOR_WRITE: return writeFileResource(id, payload.buffer, payload.offset);
1593
+ case FILESYSTEM_DESCRIPTOR_SYNC: return syncFileResource(id);
1594
+ case FILESYSTEM_DESCRIPTOR_STAT: return statFileResource(id);
1595
+ case FILESYSTEM_DESCRIPTOR_METADATA_HASH: return metadataHashFileResource(id);
1422
1596
  case INPUT_STREAM_CREATE | FILE: {
1423
- const { fd, offset } = payload;
1424
- return createReadableStream(createReadStream(null, {
1597
+ const { descriptorId, offset } = payload;
1598
+ const fd = fileResourceFd(descriptorId);
1599
+ const streamId = createReadableStream(createReadStream(null, {
1425
1600
  fd,
1426
1601
  autoClose: false,
1427
- highWaterMark: 64 * 1024,
1602
+ highWaterMark: 65536,
1428
1603
  start: Number(offset)
1429
1604
  }));
1605
+ streams.get(streamId).fileResourceId = retainFileResource(descriptorId);
1606
+ return streamId;
1430
1607
  }
1431
1608
  case OUTPUT_STREAM_CREATE | FILE: {
1432
- const { fd, offset } = payload;
1433
- return createWritableStream(createWriteStream(null, {
1609
+ const { descriptorId, offset } = payload;
1610
+ const fd = fileResourceFd(descriptorId);
1611
+ const streamId = createWritableStream(createWriteStream(null, {
1434
1612
  fd,
1435
1613
  autoClose: false,
1436
1614
  emitClose: false,
1437
- highWaterMark: 64 * 1024,
1615
+ highWaterMark: 65536,
1438
1616
  start: Number(offset)
1439
1617
  }));
1618
+ streams.get(streamId).fileResourceId = retainFileResource(descriptorId);
1619
+ return streamId;
1440
1620
  }
1441
1621
  }
1442
1622
  switch (call & CALL_MASK) {
@@ -1457,10 +1637,13 @@ function handle(call, id, payload) {
1457
1637
  case INPUT_STREAM_SKIP: return handle(INPUT_STREAM_READ | call & CALL_TYPE_MASK, id, new Uint8Array(Number(payload)));
1458
1638
  case INPUT_STREAM_BLOCKING_SKIP: return handle(INPUT_STREAM_BLOCKING_READ | call & CALL_TYPE_MASK, id, new Uint8Array(Number(payload)));
1459
1639
  case INPUT_STREAM_SUBSCRIBE: return createPoll(streams.get(id).pollState);
1460
- case INPUT_STREAM_DISPOSE:
1461
- verifyPollsDroppedForDrop(streams.get(id).pollState, "input stream");
1640
+ case INPUT_STREAM_DISPOSE: {
1641
+ const stream = streams.get(id);
1642
+ verifyPollsDroppedForDrop(stream.pollState, "input stream");
1462
1643
  streams.delete(id);
1644
+ if (stream.fileResourceId) return releaseFileResource(stream.fileResourceId);
1463
1645
  return;
1646
+ }
1464
1647
  case OUTPUT_STREAM_CHECK_WRITE: {
1465
1648
  const { stream, pollState } = getStreamOrThrow(id);
1466
1649
  const bytes = stream.writableHighWaterMark - stream.writableLength;
@@ -1566,9 +1749,12 @@ function handle(call, id, payload) {
1566
1749
  case OUTPUT_STREAM_DISPOSE: {
1567
1750
  const stream = streams.get(id);
1568
1751
  verifyPollsDroppedForDrop(stream.pollState, "output stream");
1569
- stream.stream.end();
1570
1752
  streams.delete(id);
1571
- return;
1753
+ if (stream.fileResourceId) return new Promise((resolve) => stream.stream.end(resolve)).then(() => releaseFileResource(stream.fileResourceId));
1754
+ else {
1755
+ stream.stream.end();
1756
+ return;
1757
+ }
1572
1758
  }
1573
1759
  case POLL_POLLABLE_READY: return polls.get(id).ready;
1574
1760
  case POLL_POLLABLE_BLOCK: payload = [id];
@@ -9,6 +9,7 @@ export interface Stream {
9
9
  stream: NodeJS.ReadableStream | NodeJS.WritableStream;
10
10
  flushPromise: Promise<void> | null;
11
11
  pollState: any;
12
+ fileResourceId?: number;
12
13
  }
13
14
  export interface Future {
14
15
  future: {
@@ -6,7 +6,8 @@ import { clearOutgoingResponse, createHttpRequest, setOutgoingResponse, startHtt
6
6
  import { Readable } from "node:stream";
7
7
  import { read } from "node:fs";
8
8
  import { nextTick } from "node:process";
9
- import { CALL_MASK, CALL_TYPE_MASK, CLOCKS_DURATION_SUBSCRIBE, CLOCKS_INSTANT_SUBSCRIBE, FILE, FUTURE_DISPOSE, FUTURE_SUBSCRIBE, FUTURE_TAKE_VALUE, HTTP, HTTP_CREATE_REQUEST, HTTP_OUTGOING_BODY_DISPOSE, HTTP_OUTPUT_STREAM_FINISH, HTTP_SERVER_CLEAR_OUTGOING_RESPONSE, HTTP_SERVER_SET_OUTGOING_RESPONSE, HTTP_SERVER_START, HTTP_SERVER_STOP, HTTP_SERVER_GET_ADDRESS, INPUT_STREAM_BLOCKING_READ, INPUT_STREAM_BLOCKING_SKIP, INPUT_STREAM_CREATE, INPUT_STREAM_DISPOSE, INPUT_STREAM_READ, INPUT_STREAM_SKIP, INPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_BLOCKING_FLUSH, OUTPUT_STREAM_BLOCKING_SPLICE, OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH, OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH, OUTPUT_STREAM_CHECK_WRITE, OUTPUT_STREAM_CREATE, OUTPUT_STREAM_DISPOSE, OUTPUT_STREAM_FLUSH, OUTPUT_STREAM_SPLICE, OUTPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_WRITE, OUTPUT_STREAM_WRITE_ZEROES, POLL_POLLABLE_BLOCK, POLL_POLLABLE_DISPOSE, POLL_POLLABLE_READY, POLL_POLL_LIST, SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST, SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST, SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST, SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST, SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE, SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE, SOCKET_TCP_ACCEPT, SOCKET_TCP_BIND_FINISH, SOCKET_TCP_BIND_START, SOCKET_TCP_CONNECT_FINISH, SOCKET_TCP_CONNECT_START, SOCKET_TCP_CREATE_HANDLE, SOCKET_TCP_DISPOSE, SOCKET_TCP_GET_LOCAL_ADDRESS, SOCKET_TCP_GET_REMOTE_ADDRESS, SOCKET_TCP_IS_LISTENING, SOCKET_TCP_LISTEN_FINISH, SOCKET_TCP_LISTEN_START, SOCKET_TCP_SET_KEEP_ALIVE, SOCKET_TCP_SET_LISTEN_BACKLOG_SIZE, SOCKET_TCP_SHUTDOWN, SOCKET_TCP_SUBSCRIBE, SOCKET_UDP_BIND_FINISH, SOCKET_UDP_BIND_START, SOCKET_UDP_CREATE_HANDLE, SOCKET_UDP_DISPOSE, SOCKET_UDP_GET_LOCAL_ADDRESS, SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE, SOCKET_UDP_GET_REMOTE_ADDRESS, SOCKET_UDP_GET_SEND_BUFFER_SIZE, SOCKET_UDP_GET_UNICAST_HOP_LIMIT, SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE, SOCKET_UDP_SET_SEND_BUFFER_SIZE, SOCKET_UDP_SET_UNICAST_HOP_LIMIT, SOCKET_UDP_STREAM, SOCKET_UDP_SUBSCRIBE, SOCKET_INCOMING_DATAGRAM_STREAM_RECEIVE, SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND, SOCKET_OUTGOING_DATAGRAM_STREAM_SEND, SOCKET_DATAGRAM_STREAM_SUBSCRIBE, SOCKET_DATAGRAM_STREAM_DISPOSE, STDERR, STDIN, STDOUT, reverseMap, } from "./calls.js";
9
+ import { CALL_MASK, CALL_TYPE_MASK, CLOCKS_DURATION_SUBSCRIBE, CLOCKS_INSTANT_SUBSCRIBE, FILE, FILESYSTEM_DESCRIPTOR_CLOSE, FILESYSTEM_DESCRIPTOR_GET_TYPE, FILESYSTEM_DESCRIPTOR_METADATA_HASH, FILESYSTEM_DESCRIPTOR_OPEN, FILESYSTEM_DESCRIPTOR_READ, FILESYSTEM_DESCRIPTOR_SET_SIZE, FILESYSTEM_DESCRIPTOR_SET_TIMES, FILESYSTEM_DESCRIPTOR_STAT, FILESYSTEM_DESCRIPTOR_SYNC, FILESYSTEM_DESCRIPTOR_SYNC_DATA, FILESYSTEM_DESCRIPTOR_WRITE, FUTURE_DISPOSE, FUTURE_SUBSCRIBE, FUTURE_TAKE_VALUE, HTTP, HTTP_CREATE_REQUEST, HTTP_OUTGOING_BODY_DISPOSE, HTTP_OUTPUT_STREAM_FINISH, HTTP_SERVER_CLEAR_OUTGOING_RESPONSE, HTTP_SERVER_SET_OUTGOING_RESPONSE, HTTP_SERVER_START, HTTP_SERVER_STOP, HTTP_SERVER_GET_ADDRESS, INPUT_STREAM_BLOCKING_READ, INPUT_STREAM_BLOCKING_SKIP, INPUT_STREAM_CREATE, INPUT_STREAM_DISPOSE, INPUT_STREAM_READ, INPUT_STREAM_SKIP, INPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_BLOCKING_FLUSH, OUTPUT_STREAM_BLOCKING_SPLICE, OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH, OUTPUT_STREAM_BLOCKING_WRITE_ZEROES_AND_FLUSH, OUTPUT_STREAM_CHECK_WRITE, OUTPUT_STREAM_CREATE, OUTPUT_STREAM_DISPOSE, OUTPUT_STREAM_FLUSH, OUTPUT_STREAM_SPLICE, OUTPUT_STREAM_SUBSCRIBE, OUTPUT_STREAM_WRITE, OUTPUT_STREAM_WRITE_ZEROES, POLL_POLLABLE_BLOCK, POLL_POLLABLE_DISPOSE, POLL_POLLABLE_READY, POLL_POLL_LIST, SOCKET_RESOLVE_ADDRESS_CREATE_REQUEST, SOCKET_RESOLVE_ADDRESS_SUBSCRIBE_REQUEST, SOCKET_RESOLVE_ADDRESS_DISPOSE_REQUEST, SOCKET_RESOLVE_ADDRESS_TAKE_REQUEST, SOCKET_GET_DEFAULT_RECEIVE_BUFFER_SIZE, SOCKET_GET_DEFAULT_SEND_BUFFER_SIZE, SOCKET_TCP_ACCEPT, SOCKET_TCP_BIND_FINISH, SOCKET_TCP_BIND_START, SOCKET_TCP_CONNECT_FINISH, SOCKET_TCP_CONNECT_START, SOCKET_TCP_CREATE_HANDLE, SOCKET_TCP_DISPOSE, SOCKET_TCP_GET_LOCAL_ADDRESS, SOCKET_TCP_GET_REMOTE_ADDRESS, SOCKET_TCP_IS_LISTENING, SOCKET_TCP_LISTEN_FINISH, SOCKET_TCP_LISTEN_START, SOCKET_TCP_SET_KEEP_ALIVE, SOCKET_TCP_SET_LISTEN_BACKLOG_SIZE, SOCKET_TCP_SHUTDOWN, SOCKET_TCP_SUBSCRIBE, SOCKET_UDP_BIND_FINISH, SOCKET_UDP_BIND_START, SOCKET_UDP_CREATE_HANDLE, SOCKET_UDP_DISPOSE, SOCKET_UDP_GET_LOCAL_ADDRESS, SOCKET_UDP_GET_RECEIVE_BUFFER_SIZE, SOCKET_UDP_GET_REMOTE_ADDRESS, SOCKET_UDP_GET_SEND_BUFFER_SIZE, SOCKET_UDP_GET_UNICAST_HOP_LIMIT, SOCKET_UDP_SET_RECEIVE_BUFFER_SIZE, SOCKET_UDP_SET_SEND_BUFFER_SIZE, SOCKET_UDP_SET_UNICAST_HOP_LIMIT, SOCKET_UDP_STREAM, SOCKET_UDP_SUBSCRIBE, SOCKET_INCOMING_DATAGRAM_STREAM_RECEIVE, SOCKET_OUTGOING_DATAGRAM_STREAM_CHECK_SEND, SOCKET_OUTGOING_DATAGRAM_STREAM_SEND, SOCKET_DATAGRAM_STREAM_SUBSCRIBE, SOCKET_DATAGRAM_STREAM_DISPOSE, STDERR, STDIN, STDOUT, reverseMap, } from "./calls.js";
10
+ import { fileResourceFd, getFileResourceType, metadataHashFileResource, openFileResource, readFileResource, releaseFileResource, retainFileResource, setFileResourceSize, setFileResourceTimes, statFileResource, syncFileResource, syncFileResourceData, writeFileResource, } from "./worker-filesystem.js";
10
11
  import { SOCKET_STATE_BIND, SOCKET_STATE_BOUND, SOCKET_STATE_CONNECT, SOCKET_STATE_CONNECTION, SOCKET_STATE_LISTEN, SOCKET_STATE_LISTENER, socketResolveAddress, getDefaultSendBufferSize, getDefaultReceiveBufferSize, } from "./worker-sockets.js";
11
12
  import { createTcpSocket, socketTcpAccept, socketTcpBindStart, socketTcpConnectStart, socketTcpDispose, socketTcpFinish, socketTcpGetLocalAddress, socketTcpGetRemoteAddress, socketTcpListenStart, socketTcpSetKeepAlive, socketTcpSetListenBacklogSize, socketTcpShutdown, tcpSockets, } from "./worker-socket-tcp.js";
12
13
  import { createUdpSocket, datagramStreams, socketDatagramStreamDispose, socketIncomingDatagramStreamReceive, socketOutgoingDatagramStreamCheckSend, socketOutgoingDatagramStreamSend, socketUdpBindFinish, socketUdpBindStart, socketUdpDispose, socketUdpGetLocalAddress, socketUdpGetReceiveBufferSize, socketUdpGetRemoteAddress, socketUdpGetSendBufferSize, socketUdpGetUnicastHopLimit, socketUdpSetReceiveBufferSize, socketUdpSetSendBufferSize, socketUdpSetUnicastHopLimit, socketUdpStream, udpSockets, } from "./worker-socket-udp.js";
@@ -365,18 +366,44 @@ function handle(call, id, payload) {
365
366
  return createPoll(pollState);
366
367
  }
367
368
  // Filesystem
369
+ case FILESYSTEM_DESCRIPTOR_OPEN:
370
+ return openFileResource(payload.path, payload.flags);
371
+ case FILESYSTEM_DESCRIPTOR_CLOSE:
372
+ return releaseFileResource(id);
373
+ case FILESYSTEM_DESCRIPTOR_SYNC_DATA:
374
+ return syncFileResourceData(id);
375
+ case FILESYSTEM_DESCRIPTOR_GET_TYPE:
376
+ return getFileResourceType(id);
377
+ case FILESYSTEM_DESCRIPTOR_SET_SIZE:
378
+ return setFileResourceSize(id, payload);
379
+ case FILESYSTEM_DESCRIPTOR_SET_TIMES:
380
+ return setFileResourceTimes(id, payload.atime, payload.mtime);
381
+ case FILESYSTEM_DESCRIPTOR_READ:
382
+ return readFileResource(id, payload.length, payload.offset);
383
+ case FILESYSTEM_DESCRIPTOR_WRITE:
384
+ return writeFileResource(id, payload.buffer, payload.offset);
385
+ case FILESYSTEM_DESCRIPTOR_SYNC:
386
+ return syncFileResource(id);
387
+ case FILESYSTEM_DESCRIPTOR_STAT:
388
+ return statFileResource(id);
389
+ case FILESYSTEM_DESCRIPTOR_METADATA_HASH:
390
+ return metadataHashFileResource(id);
368
391
  case INPUT_STREAM_CREATE | FILE: {
369
- const { fd, offset } = payload;
392
+ const { descriptorId, offset } = payload;
393
+ const fd = fileResourceFd(descriptorId);
370
394
  const stream = createReadStream(null, {
371
395
  fd,
372
396
  autoClose: false,
373
397
  highWaterMark: 64 * 1024,
374
398
  start: Number(offset),
375
399
  });
376
- return createReadableStream(stream);
400
+ const streamId = createReadableStream(stream);
401
+ streams.get(streamId).fileResourceId = retainFileResource(descriptorId);
402
+ return streamId;
377
403
  }
378
404
  case OUTPUT_STREAM_CREATE | FILE: {
379
- const { fd, offset } = payload;
405
+ const { descriptorId, offset } = payload;
406
+ const fd = fileResourceFd(descriptorId);
380
407
  const stream = createWriteStream(null, {
381
408
  fd,
382
409
  autoClose: false,
@@ -384,7 +411,9 @@ function handle(call, id, payload) {
384
411
  highWaterMark: 64 * 1024,
385
412
  start: Number(offset),
386
413
  });
387
- return createWritableStream(stream);
414
+ const streamId = createWritableStream(stream);
415
+ streams.get(streamId).fileResourceId = retainFileResource(descriptorId);
416
+ return streamId;
388
417
  }
389
418
  }
390
419
  // Generic call implementations (streams + polls)
@@ -421,6 +450,9 @@ function handle(call, id, payload) {
421
450
  const stream = streams.get(id);
422
451
  verifyPollsDroppedForDrop(stream.pollState, "input stream");
423
452
  streams.delete(id);
453
+ if (stream.fileResourceId) {
454
+ return releaseFileResource(stream.fileResourceId);
455
+ }
424
456
  return;
425
457
  }
426
458
  case OUTPUT_STREAM_CHECK_WRITE: {
@@ -599,9 +631,14 @@ function handle(call, id, payload) {
599
631
  case OUTPUT_STREAM_DISPOSE: {
600
632
  const stream = streams.get(id);
601
633
  verifyPollsDroppedForDrop(stream.pollState, "output stream");
602
- stream.stream.end();
603
634
  streams.delete(id);
604
- return;
635
+ if (stream.fileResourceId) {
636
+ return new Promise((resolve) => stream.stream.end(resolve)).then(() => releaseFileResource(stream.fileResourceId));
637
+ }
638
+ else {
639
+ stream.stream.end();
640
+ return;
641
+ }
605
642
  }
606
643
  case POLL_POLLABLE_READY:
607
644
  return polls.get(id).ready;