knitting 0.1.51 → 0.1.53

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 (69) hide show
  1. package/README.md +213 -54
  2. package/knitting.d.ts +1 -0
  3. package/map.md +15 -3
  4. package/package.json +14 -2
  5. package/prebuilds/darwin-arm64-node-127/knitting_buffer_pointer.node +0 -0
  6. package/prebuilds/darwin-arm64-node-137/knitting_buffer_pointer.node +0 -0
  7. package/prebuilds/darwin-x64-node-127/knitting_buffer_pointer.node +0 -0
  8. package/prebuilds/darwin-x64-node-137/knitting_buffer_pointer.node +0 -0
  9. package/prebuilds/linux-x64-node-127/knitting_buffer_pointer.node +0 -0
  10. package/prebuilds/linux-x64-node-137/knitting_buffer_pointer.node +0 -0
  11. package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
  12. package/prebuilds/win32-x64-node-127/knitting_buffer_pointer.node +0 -0
  13. package/prebuilds/win32-x64-node-127/knitting_shared_memory.node +0 -0
  14. package/prebuilds/win32-x64-node-127/knitting_shm.node +0 -0
  15. package/prebuilds/win32-x64-node-137/knitting_buffer_pointer.node +0 -0
  16. package/prebuilds/win32-x64-node-137/knitting_shared_memory.node +0 -0
  17. package/prebuilds/win32-x64-node-137/knitting_shm.node +0 -0
  18. package/scripts/build-native-addons.ts +5 -0
  19. package/src/api.d.ts +29 -16
  20. package/src/api.js +98 -28
  21. package/src/common/envelope.d.ts +9 -3
  22. package/src/common/envelope.js +14 -0
  23. package/src/common/worker-runtime.d.ts +2 -0
  24. package/src/common/worker-runtime.js +9 -0
  25. package/src/connections/buffer-reference-native.d.ts +56 -0
  26. package/src/connections/buffer-reference-native.js +217 -0
  27. package/src/connections/buffer-reference.d.ts +78 -0
  28. package/src/connections/buffer-reference.js +461 -0
  29. package/src/connections/index.d.ts +1 -0
  30. package/src/connections/index.js +1 -0
  31. package/src/connections/node-addons.d.ts +1 -1
  32. package/src/connections/node-buffer-pointer.d.ts +20 -0
  33. package/src/connections/node-buffer-pointer.js +16 -0
  34. package/src/connections/process-shared-buffer.d.ts +6 -0
  35. package/src/connections/process-shared-buffer.js +6 -0
  36. package/src/connections/shared-array-buffer-payload.d.ts +36 -0
  37. package/src/connections/shared-array-buffer-payload.js +235 -0
  38. package/src/debug/env-diff.d.ts +26 -0
  39. package/src/debug/env-diff.js +49 -0
  40. package/src/debug/gate.d.ts +18 -0
  41. package/src/debug/gate.js +69 -0
  42. package/src/debug/handle.d.ts +23 -0
  43. package/src/debug/handle.js +48 -0
  44. package/src/ipc/transport/shared-memory.d.ts +1 -3
  45. package/src/knitting_buffer_pointer.cc +425 -0
  46. package/src/memory/lock.d.ts +12 -1
  47. package/src/memory/lock.js +47 -4
  48. package/src/memory/payload-config.d.ts +9 -0
  49. package/src/memory/payloadCodec.js +220 -37
  50. package/src/permission/protocol.d.ts +1 -1
  51. package/src/permission/protocol.js +30 -20
  52. package/src/runtime/pool.d.ts +3 -5
  53. package/src/runtime/pool.js +18 -18
  54. package/src/runtime/process-worker.js +5 -2
  55. package/src/runtime/tx-queue.d.ts +3 -2
  56. package/src/runtime/tx-queue.js +18 -13
  57. package/src/types.d.ts +54 -50
  58. package/src/utils/http.d.ts +29 -0
  59. package/src/utils/http.js +100 -0
  60. package/src/worker/loop.js +76 -14
  61. package/src/worker/rx-queue.d.ts +4 -1
  62. package/src/worker/rx-queue.js +53 -4
  63. package/src/worker/safety/startup.d.ts +2 -3
  64. package/src/worker/safety/startup.js +1 -4
  65. package/src/worker/timers.js +7 -2
  66. package/unsafe.d.ts +1 -0
  67. package/unsafe.js +1 -0
  68. package/utils.d.ts +1 -0
  69. package/utils.js +1 -0
@@ -1,10 +1,12 @@
1
- import { beginPromisePayload, finishPromisePayload, getTaskSlotIndex, HEADER_BYTE_LENGTH, HEADER_SLOT_STRIDE_U32, HEADER_STATIC_PAYLOAD_U32, LockBound, PayloadBuffer, PayloadSignal, TaskIndex, } from "./lock.js";
1
+ import { attachPayloadTransportFinalizer, beginPromisePayload, finishPromisePayload, getTaskSlotIndex, HEADER_BYTE_LENGTH, HEADER_SLOT_STRIDE_U32, HEADER_STATIC_PAYLOAD_U32, LockBound, PayloadBuffer, PayloadSignal, TaskIndex, } from "./lock.js";
2
2
  import { register } from "./regionRegistry.js";
3
3
  import { createSharedDynamicBufferIO, createSharedStaticBufferIO, } from "./shared-buffer-io.js";
4
4
  import { getStridedRegionSpanBytes } from "./byte-carpet.js";
5
5
  import { encoderError, ErrorKnitting } from "../error.js";
6
6
  import { Envelope } from "../common/envelope.js";
7
7
  import { resolvePayloadBufferOptions, } from "./payload-config.js";
8
+ import { getSharedArrayBufferPayload, SHARED_ARRAY_BUFFER_CODEC_ID, SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER, SHARED_ARRAY_BUFFER_NUMERIC_WORDS, } from "../connections/shared-array-buffer-payload.js";
9
+ const BUFFER_REFERENCE_NUMERIC_TRANSFER = Symbol.for("knitting.bufferReference.numericTransfer");
8
10
  const memory = new ArrayBuffer(8);
9
11
  const Float64View = new Float64Array(memory);
10
12
  const BigInt64View = new BigInt64Array(memory);
@@ -28,6 +30,7 @@ const BIGINT64_MAX = (1n << 63n) - 1n;
28
30
  const { parse: parseJSON, stringify: stringifyJSON } = JSON;
29
31
  const { for: symbolFor, keyFor: symbolKeyFor } = Symbol;
30
32
  const EXTERNAL_PAYLOAD_BRAND = symbolFor("knitting.payloadCodec");
33
+ const BUFFER_REFERENCE_CODEC_ID = "knitting.bufferReference";
31
34
  const PROCESS_SHARED_BUFFER_CODEC_ID = "knitting.processSharedBuffer";
32
35
  const externalPayloadGlobal = globalThis;
33
36
  const objectGetPrototypeOf = Object.getPrototypeOf;
@@ -35,7 +38,8 @@ const objectHasOwn = Object.prototype.hasOwnProperty;
35
38
  const arrayIsArray = Array.isArray;
36
39
  const objectPrototype = Object.prototype;
37
40
  const UNSUPPORTED_OBJECT_DETAIL = "Unsupported object type. Allowed: plain object, array, Error, Date, Envelope, Buffer, ArrayBuffer, DataView, typed arrays, and registered external payloads. Serialize it yourself.";
38
- const ENVELOPE_PAYLOAD_DETAIL = "Envelope payload must be an ArrayBuffer.";
41
+ const ENVELOPE_PAYLOAD_DETAIL = "Envelope payload must be an ArrayBuffer, SharedArrayBuffer, " +
42
+ "ProcessSharedBuffer, or BufferReference.";
39
43
  const ENVELOPE_HEADER_DETAIL = "Envelope header must be a JSON-like value or string.";
40
44
  const ENVELOPE_PROMISE_DETAIL = "Envelope header cannot contain Promise values.";
41
45
  const DYNAMIC_PAYLOAD_LIMIT_DETAIL = "Dynamic payload exceeds maxPayloadBytes.";
@@ -92,15 +96,18 @@ const decodeExternalPayload = (raw) => {
92
96
  : { codec: codecId, metadata };
93
97
  };
94
98
  const PROCESS_SHARED_BUFFER_NUMERIC_WORDS = 8;
99
+ const BUFFER_REFERENCE_NUMERIC_WORDS = 8;
95
100
  const NUMERIC_SENTINEL = 0xffffffff;
96
- const decodeProcessSharedBufferNumericWords = (words) => {
97
- const codec = externalPayloadGlobal.__KNITTING_PAYLOAD_CODECS__?.[PROCESS_SHARED_BUFFER_CODEC_ID];
98
- if (typeof codec?.decodeNumeric === "function")
101
+ const decodeNumericExternalPayload = (codecId, words) => {
102
+ const codec = externalPayloadGlobal.__KNITTING_PAYLOAD_CODECS__?.[codecId];
103
+ if (typeof codec?.decodeNumeric === "function") {
99
104
  return codec.decodeNumeric(words);
100
- // `words` is a reusable scratch; copy it on the (unreachable in practice)
101
- // codec-missing diagnostic path so the escaped value never aliases it.
102
- return { codec: PROCESS_SHARED_BUFFER_CODEC_ID, metadata: Array.from(words) };
105
+ }
106
+ return { codec: codecId, metadata: Array.from(words) };
103
107
  };
108
+ const decodeProcessSharedBufferNumericWords = (words) => decodeNumericExternalPayload(PROCESS_SHARED_BUFFER_CODEC_ID, words);
109
+ const decodeBufferReferenceNumericWords = (words) => decodeNumericExternalPayload(BUFFER_REFERENCE_CODEC_ID, words);
110
+ const decodeSharedArrayBufferNumericWords = (words) => decodeNumericExternalPayload(SHARED_ARRAY_BUFFER_CODEC_ID, words);
104
111
  const tryEncodePrimitiveTask = (task) => {
105
112
  const value = task.value;
106
113
  switch (typeof value) {
@@ -516,6 +523,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
516
523
  return true;
517
524
  };
518
525
  const processSharedBufferWords = new Uint32Array(PROCESS_SHARED_BUFFER_NUMERIC_WORDS);
526
+ const sharedArrayBufferWords = new Uint32Array(SHARED_ARRAY_BUFFER_NUMERIC_WORDS);
519
527
  const tryEncodeProcessSharedBufferNumeric = (task, slotIndex, value) => {
520
528
  const descriptor = value.descriptor;
521
529
  if (descriptor === undefined ||
@@ -549,6 +557,33 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
549
557
  task.value = null;
550
558
  return true;
551
559
  };
560
+ const tryEncodeBufferReferenceNumeric = (task, slotIndex, value) => {
561
+ const words = value[BUFFER_REFERENCE_NUMERIC_TRANSFER]?.();
562
+ if (words === undefined)
563
+ return false;
564
+ task[TaskIndex.Type] = PayloadBuffer.BufferReference;
565
+ task[TaskIndex.PayloadLen] = writeStaticU32Words(words, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex);
566
+ attachPayloadTransportFinalizer(task, value);
567
+ task.value = null;
568
+ return true;
569
+ };
570
+ const tryEncodeSharedArrayBufferNumeric = (task, slotIndex, value) => {
571
+ const words = value[SHARED_ARRAY_BUFFER_NUMERIC_TRANSFER]?.(lockSector);
572
+ if (words === undefined)
573
+ return false;
574
+ sharedArrayBufferWords[0] = words[0] ?? 0;
575
+ sharedArrayBufferWords[1] = words[1] ?? 0;
576
+ sharedArrayBufferWords[2] = words[2] ?? 0;
577
+ sharedArrayBufferWords[3] = words[3] ?? 0;
578
+ sharedArrayBufferWords[4] = words[4] ?? 0;
579
+ sharedArrayBufferWords[5] = words[5] ?? 0;
580
+ sharedArrayBufferWords[6] = words[6] ?? 0;
581
+ sharedArrayBufferWords[7] = words[7] ?? 0;
582
+ task[TaskIndex.Type] = PayloadBuffer.SharedArrayBuffer;
583
+ task[TaskIndex.PayloadLen] = writeStaticU32Words(sharedArrayBufferWords, words.length, slotIndex);
584
+ task.value = null;
585
+ return true;
586
+ };
552
587
  const encodeObjectExternalPayload = (task, slotIndex, externalPayload) => {
553
588
  const codecId = readExternalPayloadCodecId(externalPayload);
554
589
  if (codecId === undefined) {
@@ -559,6 +594,14 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
559
594
  detail: UNSUPPORTED_OBJECT_DETAIL,
560
595
  });
561
596
  }
597
+ if (codecId === SHARED_ARRAY_BUFFER_CODEC_ID &&
598
+ tryEncodeSharedArrayBufferNumeric(task, slotIndex, externalPayload)) {
599
+ return true;
600
+ }
601
+ if (codecId === BUFFER_REFERENCE_CODEC_ID &&
602
+ tryEncodeBufferReferenceNumeric(task, slotIndex, externalPayload)) {
603
+ return true;
604
+ }
562
605
  if (codecId === PROCESS_SHARED_BUFFER_CODEC_ID &&
563
606
  tryEncodeProcessSharedBufferNumeric(task, slotIndex, externalPayload)) {
564
607
  return true;
@@ -589,6 +632,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
589
632
  if (written !== -1) {
590
633
  task[TaskIndex.Type] = PayloadBuffer.StaticExternalPayload;
591
634
  task[TaskIndex.PayloadLen] = written;
635
+ attachPayloadTransportFinalizer(task, externalPayload);
592
636
  task.value = null;
593
637
  return true;
594
638
  }
@@ -603,6 +647,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
603
647
  return failDynamicWriteAfterReserve(task, reservedSlot);
604
648
  task[TaskIndex.PayloadLen] = written;
605
649
  setSlotLength(reservedSlot, written);
650
+ attachPayloadTransportFinalizer(task, externalPayload);
606
651
  task.value = null;
607
652
  return true;
608
653
  };
@@ -614,52 +659,50 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
614
659
  task.value = null;
615
660
  return true;
616
661
  };
617
- const encodeObjectEnvelope = (task, slotIndex, envelope) => {
618
- const header = envelope.header;
619
- const payload = envelope.payload;
620
- const headerIsString = typeof header === "string";
621
- if (!(payload instanceof ArrayBuffer)) {
622
- return encoderError({
623
- task,
624
- type: ErrorKnitting.Serializable,
625
- onPromise,
626
- detail: ENVELOPE_PAYLOAD_DETAIL,
627
- });
628
- }
662
+ const encodeEnvelopeHeaderText = (task, header, headerIsString) => {
629
663
  if (hasPromiseInEnvelopeHeader(header)) {
630
- return encoderError({
664
+ encoderError({
631
665
  task,
632
666
  type: ErrorKnitting.Serializable,
633
667
  onPromise,
634
668
  detail: ENVELOPE_PROMISE_DETAIL,
635
669
  });
670
+ return undefined;
636
671
  }
672
+ if (headerIsString)
673
+ return header;
637
674
  let headerText;
638
- if (headerIsString) {
639
- headerText = header;
675
+ try {
676
+ headerText = stringifyJSON(header);
640
677
  }
641
- else {
642
- try {
643
- headerText = stringifyJSON(header);
644
- }
645
- catch (error) {
646
- const detail = error instanceof Error ? error.message : String(error);
647
- return encoderError({
648
- task,
649
- type: ErrorKnitting.Json,
650
- onPromise,
651
- detail,
652
- });
653
- }
678
+ catch (error) {
679
+ const detail = error instanceof Error ? error.message : String(error);
680
+ encoderError({ task, type: ErrorKnitting.Json, onPromise, detail });
681
+ return undefined;
654
682
  }
655
683
  if (typeof headerText !== "string") {
656
- return encoderError({
684
+ encoderError({
657
685
  task,
658
686
  type: ErrorKnitting.Serializable,
659
687
  onPromise,
660
688
  detail: ENVELOPE_HEADER_DETAIL,
661
689
  });
690
+ return undefined;
691
+ }
692
+ return headerText;
693
+ };
694
+ const resolveEnvelopeExternalBody = (body) => {
695
+ if (body === null || typeof body !== "object")
696
+ return undefined;
697
+ const sharedArrayBuffer = getSharedArrayBufferPayload(body);
698
+ if (sharedArrayBuffer !== undefined)
699
+ return sharedArrayBuffer;
700
+ if (isExternalPayloadLike(body)) {
701
+ return body;
662
702
  }
703
+ return undefined;
704
+ };
705
+ const encodeEnvelopeArrayBufferBody = (task, slotIndex, headerText, headerIsString, payload) => {
663
706
  const payloadBytes = new Uint8Array(payload);
664
707
  const payloadLength = payloadBytes.byteLength;
665
708
  const payloadReserveBytes = payloadLength > 0 ? payloadLength : 1;
@@ -707,6 +750,107 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
707
750
  task.value = null;
708
751
  return true;
709
752
  };
753
+ const encodeEnvelopeExternalBody = (task, slotIndex, headerText, headerIsString, externalBody) => {
754
+ const codecId = readExternalPayloadCodecId(externalBody);
755
+ if (codecId === undefined) {
756
+ return encoderError({
757
+ task,
758
+ type: ErrorKnitting.Serializable,
759
+ onPromise,
760
+ detail: ENVELOPE_PAYLOAD_DETAIL,
761
+ });
762
+ }
763
+ let bodyText;
764
+ try {
765
+ bodyText = stringifyJSON([codecId, externalBody.toMetadata()]);
766
+ }
767
+ catch (error) {
768
+ const detail = error instanceof Error ? error.message : String(error);
769
+ return encoderError({
770
+ task,
771
+ type: ErrorKnitting.Serializable,
772
+ onPromise,
773
+ detail,
774
+ });
775
+ }
776
+ if (typeof bodyText !== "string") {
777
+ return encoderError({
778
+ task,
779
+ type: ErrorKnitting.Serializable,
780
+ onPromise,
781
+ detail: "Envelope body metadata must be JSON serializable.",
782
+ });
783
+ }
784
+ const bodyBytes = textEncode.encode(bodyText);
785
+ const bodyLength = bodyBytes.byteLength;
786
+ const staticHeaderWritten = writeStaticUtf8(headerText, slotIndex);
787
+ if (staticHeaderWritten !== -1) {
788
+ if (!ensureWithinDynamicLimit(task, bodyLength, "EnvelopeStaticHeaderExternal"))
789
+ return false;
790
+ const reservedSlot = reserveDynamicObject(task, bodyLength);
791
+ task[TaskIndex.Type] = headerIsString
792
+ ? PayloadBuffer.EnvelopeStaticHeaderStringExternal
793
+ : PayloadBuffer.EnvelopeStaticHeaderExternal;
794
+ task[TaskIndex.PayloadLen] = staticHeaderWritten;
795
+ task[TaskIndex.End] = bodyLength;
796
+ const bodyWritten = writeDynamicBinary(bodyBytes, task[TaskIndex.Start]);
797
+ if (bodyWritten < 0) {
798
+ return failDynamicWriteAfterReserve(task, reservedSlot);
799
+ }
800
+ setSlotLength(reservedSlot, bodyWritten);
801
+ attachPayloadTransportFinalizer(task, externalBody);
802
+ task.value = null;
803
+ return true;
804
+ }
805
+ const headerReserveBytes = dynamicUtf8ReserveBytesWithExtra(task, headerText, bodyLength, headerIsString
806
+ ? "EnvelopeDynamicHeaderStringExternal"
807
+ : "EnvelopeDynamicHeaderExternal");
808
+ if (headerReserveBytes < 0)
809
+ return false;
810
+ task[TaskIndex.Type] = headerIsString
811
+ ? PayloadBuffer.EnvelopeDynamicHeaderStringExternal
812
+ : PayloadBuffer.EnvelopeDynamicHeaderExternal;
813
+ const reservedSlot = reserveDynamicObject(task, headerReserveBytes + bodyLength);
814
+ const baseStart = task[TaskIndex.Start];
815
+ const writtenHeaderBytes = writeDynamicUtf8(headerText, baseStart, headerReserveBytes);
816
+ if (writtenHeaderBytes < 0) {
817
+ return failDynamicWriteAfterReserve(task, reservedSlot);
818
+ }
819
+ const bodyWritten = writeDynamicBinary(bodyBytes, baseStart + writtenHeaderBytes);
820
+ if (bodyWritten < 0) {
821
+ return failDynamicWriteAfterReserve(task, reservedSlot);
822
+ }
823
+ task[TaskIndex.PayloadLen] = writtenHeaderBytes;
824
+ task[TaskIndex.End] = bodyLength;
825
+ setSlotLength(reservedSlot, writtenHeaderBytes + bodyLength);
826
+ attachPayloadTransportFinalizer(task, externalBody);
827
+ task.value = null;
828
+ return true;
829
+ };
830
+ const encodeObjectEnvelope = (task, slotIndex, envelope) => {
831
+ const header = envelope.header;
832
+ const payload = envelope.payload;
833
+ const headerIsString = typeof header === "string";
834
+ if (payload instanceof ArrayBuffer) {
835
+ const headerText = encodeEnvelopeHeaderText(task, header, headerIsString);
836
+ if (headerText === undefined)
837
+ return false;
838
+ return encodeEnvelopeArrayBufferBody(task, slotIndex, headerText, headerIsString, payload);
839
+ }
840
+ const externalBody = resolveEnvelopeExternalBody(payload);
841
+ if (externalBody !== undefined) {
842
+ const headerText = encodeEnvelopeHeaderText(task, header, headerIsString);
843
+ if (headerText === undefined)
844
+ return false;
845
+ return encodeEnvelopeExternalBody(task, slotIndex, headerText, headerIsString, externalBody);
846
+ }
847
+ return encoderError({
848
+ task,
849
+ type: ErrorKnitting.Serializable,
850
+ onPromise,
851
+ detail: ENVELOPE_PAYLOAD_DETAIL,
852
+ });
853
+ };
710
854
  const encodeObjectPromise = (task, promise) => {
711
855
  if (beginPromisePayload(task)) {
712
856
  promise.then((value) => {
@@ -769,6 +913,10 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
769
913
  objectDynamicSlot = -1;
770
914
  try {
771
915
  const objectValue = args;
916
+ const sharedArrayBufferPayload = getSharedArrayBufferPayload(objectValue);
917
+ if (sharedArrayBufferPayload !== undefined) {
918
+ return encodeObjectExternalPayload(task, slotIndex, sharedArrayBufferPayload);
919
+ }
772
920
  const objectProto = objectGetPrototypeOf(objectValue);
773
921
  if (isRuntimeUint8Array(objectValue)) {
774
922
  return encodeObjectUint8Array(task, slotIndex, objectValue);
@@ -963,6 +1111,8 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
963
1111
  // decode is single-consumer and not re-entrant, and the words are consumed
964
1112
  // synchronously when building the ProcessSharedBuffer.
965
1113
  const processSharedBufferWords = new Uint32Array(PROCESS_SHARED_BUFFER_NUMERIC_WORDS);
1114
+ const sharedArrayBufferWords = new Uint32Array(SHARED_ARRAY_BUFFER_NUMERIC_WORDS);
1115
+ const bufferReferenceWords = new Uint32Array(BUFFER_REFERENCE_NUMERIC_WORDS);
966
1116
  // TODO: remove slotIndex and make that all their callers
967
1117
  // store the slot in their Task, to just get it when it comes
968
1118
  // to the static versions of decoding
@@ -1037,6 +1187,33 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1037
1187
  freeTaskSlot(task);
1038
1188
  return;
1039
1189
  }
1190
+ case PayloadBuffer.EnvelopeStaticHeaderExternal:
1191
+ case PayloadBuffer.EnvelopeStaticHeaderStringExternal: {
1192
+ const rawHeader = readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex);
1193
+ const header = task[TaskIndex.Type] ===
1194
+ PayloadBuffer.EnvelopeStaticHeaderStringExternal
1195
+ ? rawHeader
1196
+ : parseJSON(rawHeader);
1197
+ const bodyStart = task[TaskIndex.Start];
1198
+ const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]));
1199
+ task.value = new Envelope(header, body);
1200
+ freeTaskSlot(task);
1201
+ return;
1202
+ }
1203
+ case PayloadBuffer.EnvelopeDynamicHeaderExternal:
1204
+ case PayloadBuffer.EnvelopeDynamicHeaderStringExternal: {
1205
+ const headerStart = task[TaskIndex.Start];
1206
+ const bodyStart = headerStart + task[TaskIndex.PayloadLen];
1207
+ const rawHeader = readDynamicUtf8(headerStart, bodyStart);
1208
+ const header = task[TaskIndex.Type] ===
1209
+ PayloadBuffer.EnvelopeDynamicHeaderStringExternal
1210
+ ? rawHeader
1211
+ : parseJSON(rawHeader);
1212
+ const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]));
1213
+ task.value = new Envelope(header, body);
1214
+ freeTaskSlot(task);
1215
+ return;
1216
+ }
1040
1217
  case PayloadBuffer.BigInt:
1041
1218
  task.value = decodeBigIntBinary(readDynamicBufferCopy(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]));
1042
1219
  freeTaskSlot(task);
@@ -1113,6 +1290,12 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1113
1290
  case PayloadBuffer.ProcessSharedBuffer:
1114
1291
  task.value = decodeProcessSharedBufferNumericWords(readStaticU32Words(processSharedBufferWords, PROCESS_SHARED_BUFFER_NUMERIC_WORDS, slotIndex));
1115
1292
  return;
1293
+ case PayloadBuffer.SharedArrayBuffer:
1294
+ task.value = decodeSharedArrayBufferNumericWords(readStaticU32Words(sharedArrayBufferWords, task[TaskIndex.PayloadLen] >>> 2, slotIndex));
1295
+ return;
1296
+ case PayloadBuffer.BufferReference:
1297
+ task.value = decodeBufferReferenceNumericWords(readStaticU32Words(bufferReferenceWords, BUFFER_REFERENCE_NUMERIC_WORDS, slotIndex));
1298
+ return;
1116
1299
  case PayloadBuffer.Date:
1117
1300
  Uint32View[0] = task[TaskIndex.Start];
1118
1301
  Uint32View[1] = task[TaskIndex.End];
@@ -163,4 +163,4 @@ export declare const resolvePermissionProtocol: ({ permission, modules, }: {
163
163
  modules?: string[];
164
164
  }) => ResolvedPermissionProtocol | undefined;
165
165
  export declare const toRuntimePermissionFlags: (protocol: ResolvedPermissionProtocol | undefined) => string[];
166
- export type { PermissionPath, PermissionMode, PermissionLegacyMode, SysApiName, NodePermissionSettings, DenoPermissionSettings, PermissionEnvironment, PermissionProtocol, PermissionProtocolInput, ResolvedPermissionProtocol, };
166
+ export type { DenoPermissionSettings, NodePermissionSettings, PermissionEnvironment, PermissionLegacyMode, PermissionMode, PermissionPath, PermissionProtocol, PermissionProtocolInput, ResolvedPermissionProtocol, SysApiName, };
@@ -107,11 +107,14 @@ const normalizeSysApiList = (values) => {
107
107
  return out;
108
108
  };
109
109
  const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
110
- const normalizeProtocolInput = (input) => !input ? undefined : (typeof input === "string" ? { mode: input } : input);
110
+ const normalizeProtocolInput = (input) => !input
111
+ ? undefined
112
+ : (typeof input === "string" ? { mode: input } : input);
111
113
  const isWindows = () => {
112
114
  const nodeProcess = getNodeProcess();
113
- if (typeof nodeProcess?.platform === "string")
115
+ if (typeof nodeProcess?.platform === "string") {
114
116
  return nodeProcess.platform === "win32";
117
+ }
115
118
  const g = globalThis;
116
119
  return g.Deno?.build?.os === "windows";
117
120
  };
@@ -146,7 +149,8 @@ const getHome = () => {
146
149
  }
147
150
  const g = globalThis;
148
151
  try {
149
- const home = g.Deno?.env?.get?.("HOME") ?? g.Deno?.env?.get?.("USERPROFILE");
152
+ const home = g.Deno?.env?.get?.("HOME") ??
153
+ g.Deno?.env?.get?.("USERPROFILE");
150
154
  if (typeof home === "string" && home.length > 0)
151
155
  return home;
152
156
  }
@@ -198,7 +202,11 @@ const toPathList = (values, cwd, home) => {
198
202
  };
199
203
  const toUniquePathList = (values, cwd, home) => normalizeList(toPathList(values, cwd, home));
200
204
  const toEnvFiles = (input, cwd, home) => {
201
- const values = Array.isArray(input) ? input : input ? [input] : [DEFAULT_ENV_FILE];
205
+ const values = Array.isArray(input)
206
+ ? input
207
+ : input
208
+ ? [input]
209
+ : [DEFAULT_ENV_FILE];
202
210
  return toUniquePathList(values, cwd, home);
203
211
  };
204
212
  const rawRealpathSync = realpathSyncCompat.native ?? realpathSyncCompat;
@@ -212,7 +220,8 @@ const isPathWithin = (base, candidate) => {
212
220
  const canonicalBase = toCanonicalPath(base);
213
221
  const canonicalCandidate = toCanonicalPath(candidate);
214
222
  const relative = pathRelative(canonicalBase, canonicalCandidate);
215
- return relative === "" || (!relative.startsWith("..") && !pathIsAbsolute(relative));
223
+ return relative === "" ||
224
+ (!relative.startsWith("..") && !pathIsAbsolute(relative));
216
225
  };
217
226
  const defaultSensitiveProjectAndHomePaths = (cwd, home) => {
218
227
  const projectSensitive = DEFAULT_DENY_RELATIVE.map((entry) => pathResolve(cwd, entry));
@@ -343,6 +352,13 @@ const toDenoFlags = ({ read, readAll, write, writeAll, denyRead, denyWrite, net,
343
352
  flags.push(`--deny-env=${envDeny.join(",")}`);
344
353
  }
345
354
  for (const file of envFiles) {
355
+ try {
356
+ if (!existsSyncCompat(file))
357
+ continue;
358
+ }
359
+ catch {
360
+ continue;
361
+ }
346
362
  flags.push(`--env-file=${file}`);
347
363
  }
348
364
  if (runAll) {
@@ -476,9 +492,7 @@ export const resolvePermissionProtocol = ({ permission, modules, }) => {
476
492
  const configuredRead = readAll
477
493
  ? []
478
494
  : toPathList(Array.isArray(input.read) ? input.read : undefined, cwd, home);
479
- const configuredWrite = writeAll
480
- ? []
481
- : toPathList(Array.isArray(input.write) ? input.write : undefined, cwd, home);
495
+ const configuredWrite = writeAll ? [] : toPathList(Array.isArray(input.write) ? input.write : undefined, cwd, home);
482
496
  const resolvedRead = readAll
483
497
  ? []
484
498
  : hasExplicitRead
@@ -501,19 +515,17 @@ export const resolvePermissionProtocol = ({ permission, modules, }) => {
501
515
  : normalizeStringList(Array.isArray(input.net) ? input.net : []);
502
516
  const denyNet = normalizeStringList(input.denyNet);
503
517
  const allowImportAll = input.allowImport === true;
504
- const allowImport = allowImportAll
505
- ? []
506
- : normalizeStringList(Array.isArray(input.allowImport)
507
- ? input.allowImport
508
- : [...DEFAULT_ALLOW_IMPORT_HOSTS]);
518
+ const allowImport = allowImportAll ? [] : normalizeStringList(Array.isArray(input.allowImport)
519
+ ? input.allowImport
520
+ : [...DEFAULT_ALLOW_IMPORT_HOSTS]);
509
521
  const envAllowAll = input.env?.allow === true;
510
- const envAllow = envAllowAll
511
- ? []
512
- : normalizeStringList(Array.isArray(input.env?.allow) ? input.env.allow : []);
522
+ const envAllow = envAllowAll ? [] : normalizeStringList(Array.isArray(input.env?.allow) ? input.env.allow : []);
513
523
  const envDeny = normalizeStringList(input.env?.deny);
514
524
  const legacyRunEnabled = input.node?.allowChildProcess === true ||
515
525
  input.deno?.allowRun === true;
516
- const runSource = hasOwn(input, "run") ? input.run : (legacyRunEnabled ? true : []);
526
+ const runSource = hasOwn(input, "run")
527
+ ? input.run
528
+ : (legacyRunEnabled ? true : []);
517
529
  const runAll = runSource === true;
518
530
  const run = runAll
519
531
  ? []
@@ -526,9 +538,7 @@ export const resolvePermissionProtocol = ({ permission, modules, }) => {
526
538
  ? input.ffi
527
539
  : (input.node?.allowAddons === true ? true : false);
528
540
  const ffiAll = ffiSource === true;
529
- const ffi = ffiAll
530
- ? []
531
- : toUniquePathList(Array.isArray(ffiSource) ? ffiSource : undefined, cwd, home);
541
+ const ffi = ffiAll ? [] : toUniquePathList(Array.isArray(ffiSource) ? ffiSource : undefined, cwd, home);
532
542
  const denyFfi = toUniquePathList(input.denyFfi, cwd, home);
533
543
  const sysSource = input.sys;
534
544
  const sysAll = sysSource === true;
@@ -4,7 +4,7 @@ import { lock2 } from "../memory/lock.js";
4
4
  import type { DebugOptions, DispatcherSettings, WorkerContext, WorkerData, WorkerSettings } from "../types.js";
5
5
  import "../worker/loop.js";
6
6
  import { type PayloadBufferOptions } from "../memory/payload-config.js";
7
- export declare const spawnWorkerContext: ({ list, ids, names, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }: {
7
+ export declare const spawnWorkerContext: ({ list, ids, names, sab, thread, debug, hostDebug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, bufferReferenceReturn, abortSignalCapacity, usesAbortSignal, }: {
8
8
  list: string[];
9
9
  ids: number[];
10
10
  names: string[];
@@ -12,6 +12,7 @@ export declare const spawnWorkerContext: ({ list, ids, names, sab, thread, debug
12
12
  sab?: Sab;
13
13
  thread: number;
14
14
  debug?: DebugOptions;
15
+ hostDebug?: (message: string) => void;
15
16
  totalNumberOfThread: number;
16
17
  source?: string;
17
18
  workerOptions?: WorkerSettings;
@@ -19,10 +20,7 @@ export declare const spawnWorkerContext: ({ list, ids, names, sab, thread, debug
19
20
  permission?: WorkerData["permission"];
20
21
  host?: DispatcherSettings;
21
22
  payload?: PayloadBufferOptions;
22
- payloadInitialBytes?: number;
23
- payloadMaxBytes?: number;
24
- bufferMode?: PayloadBufferOptions["mode"];
25
- maxPayloadBytes?: number;
23
+ bufferReferenceReturn?: "copy" | "borrow";
26
24
  abortSignalCapacity?: number;
27
25
  usesAbortSignal?: boolean;
28
26
  }) => WorkerContext & {
@@ -10,6 +10,7 @@ import { probeLockBufferTextCompat } from "../common/shared-buffer-text.js";
10
10
  import { signalAbortFactory } from "../shared/abortSignal.js";
11
11
  import { createLockControlCarpet } from "../memory/byte-carpet.js";
12
12
  import { resolvePayloadBufferOptions, } from "../memory/payload-config.js";
13
+ import { createBufferReferenceReturnReleaseMessage } from "../connections/buffer-reference.js";
13
14
  const WORKER_FATAL_MESSAGE_KEY = "__knittingWorkerFatal";
14
15
  const isWorkerFatalMessage = (value) => !!value &&
15
16
  typeof value === "object" &&
@@ -33,7 +34,7 @@ const withFixedPayloadConfig = (config) => ({
33
34
  mode: "fixed",
34
35
  payloadInitialBytes: config.payloadMaxByteLength,
35
36
  });
36
- export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }) => {
37
+ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, hostDebug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, bufferReferenceReturn, abortSignalCapacity, usesAbortSignal, }) => {
37
38
  const tsFileUrl = new URL(import.meta.url);
38
39
  const poliWorker = RUNTIME_WORKER;
39
40
  const resolvedWorkerOptions = serializeWorkerBootstrapData(withDefaultWorkerTimers(workerOptions));
@@ -47,9 +48,6 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, total
47
48
  const processSharedMemorySettings = useProcessWorkerRuntime
48
49
  ? readProcessSharedMemorySettings(resolvedWorkerOptions)
49
50
  : undefined;
50
- if (debug?.logHref === true) {
51
- console.log(tsFileUrl);
52
- }
53
51
  if (!useProcessWorkerRuntime && typeof poliWorker !== "function") {
54
52
  throw new Error("Worker is not available in this runtime");
55
53
  }
@@ -62,15 +60,7 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, total
62
60
  return bytes > 0 ? bytes : undefined;
63
61
  };
64
62
  const basePayloadConfig = resolvePayloadBufferOptions({
65
- options: {
66
- ...payload,
67
- mode: payload?.mode ?? bufferMode,
68
- maxPayloadBytes: payload?.maxPayloadBytes ?? maxPayloadBytes,
69
- payloadInitialBytes: payload?.payloadInitialBytes ??
70
- sanitizeBytes(payloadInitialBytes),
71
- payloadMaxByteLength: payload?.payloadMaxByteLength ??
72
- sanitizeBytes(payloadMaxBytes),
73
- },
63
+ options: payload,
74
64
  });
75
65
  const resolvedPayloadConfig = useProcessWorkerRuntime
76
66
  ? withFixedPayloadConfig(basePayloadConfig)
@@ -182,7 +172,6 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, total
182
172
  : sab,
183
173
  isMain: true,
184
174
  thread,
185
- debug,
186
175
  });
187
176
  const signalBox = signals;
188
177
  const nativeNotifySignal = createProcessWorkerNativeSignalNotifier({
@@ -193,6 +182,11 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, total
193
182
  lock,
194
183
  returnLock,
195
184
  abortSignals,
185
+ releaseBufferReferenceReturn: bufferReferenceReturn === "borrow"
186
+ ? (token) => {
187
+ worker?.postMessage?.(createBufferReferenceReturnReleaseMessage(token));
188
+ }
189
+ : undefined,
196
190
  });
197
191
  const { enqueue, rejectAll, txIdle, } = queue;
198
192
  const channelHandler = new ChannelHandler();
@@ -202,13 +196,18 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, total
202
196
  channelHandler,
203
197
  dispatcherOptions: host,
204
198
  notifySignal: nativeNotifySignal,
205
- //thread,
206
- //debugSignal: debug?.logMain ?? false,
207
- //perf,
208
199
  });
209
200
  channelHandler.open(check);
210
201
  let worker;
211
202
  const workerUrl = source ?? tsFileUrl;
203
+ const workerMode = useProcessWorkerRuntime
204
+ ? "process"
205
+ : HAS_NODE_WORKER_THREADS
206
+ ? "worker_threads"
207
+ : "worker";
208
+ hostDebug?.(`worker thread=${thread} mode=${workerMode}` +
209
+ `${processWorkerRuntime ? ` runtime=${processWorkerRuntime}` : ""}` +
210
+ ` url=${String(workerUrl)}`);
212
211
  const workerDataPayload = {
213
212
  sab: signals.sab,
214
213
  abortSignalSAB,
@@ -227,6 +226,7 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, total
227
226
  lock: lockBuffers,
228
227
  returnLock: returnLockBuffers,
229
228
  payloadConfig: resolvedPayloadConfig,
229
+ bufferReferenceReturn,
230
230
  permission,
231
231
  };
232
232
  const baseWorkerOptions = {
@@ -358,7 +358,7 @@ export const spawnWorkerContext = ({ list, ids, names, sab, thread, debug, total
358
358
  Promise.resolve().then(check);
359
359
  // Macro lane: dispatcher check is driven by the channel callback.
360
360
  // channelHandler.notify();
361
- // Use opView as a wake counter in lock2 mode to avoid lost wakeups.
361
+ // Use opView as a wake token in lock2 mode to avoid lost wakeups.
362
362
  if (a_load(signalBox.rxStatus, 0) === 0) {
363
363
  a_add(thisSignal, 0, 1);
364
364
  notifySignal?.();
@@ -2,7 +2,8 @@ import { fileURLToPath as fileURLToPathCompat } from "node:url";
2
2
  import { HEADER_SLOT_STRIDE_U32, LOCK_SECTOR_BYTE_LENGTH, LockBound, } from "../memory/lock.js";
3
3
  import { createByteCarpet, getHeaderBlockByteLength, makeSharedBufferRegion, } from "../memory/byte-carpet.js";
4
4
  import { RUNTIME } from "../common/runtime.js";
5
- import { RUNTIME_PROCESS_WORKER_BOOT_ENV, RUNTIME_PROCESS_WORKER_BOOT_VERSION, RUNTIME_PROCESS_WORKER_ENV, } from "../common/worker-runtime.js";
5
+ import { debugHas } from "../debug/gate.js";
6
+ import { RUNTIME_POOL_DEPTH, RUNTIME_POOL_DEPTH_ENV, RUNTIME_PROCESS_WORKER_BOOT_ENV, RUNTIME_PROCESS_WORKER_BOOT_VERSION, RUNTIME_PROCESS_WORKER_ENV, } from "../common/worker-runtime.js";
6
7
  import { getNodeBuiltinModule, getNodeProcess } from "../common/node-compat.js";
7
8
  import { toSharedBufferRegion, } from "../common/shared-buffer-region.js";
8
9
  import { createBunConnectionPrimitives } from "../connections/bun.js";
@@ -72,7 +73,7 @@ export const createProcessSharedMemoryAllocator = (debug) => {
72
73
  addon = loadNodeNativeAddon(require, "knitting_shared_memory");
73
74
  }
74
75
  catch (error) {
75
- if (debug?.extras === true) {
76
+ if (debugHas(debug, "lifecycle")) {
76
77
  console.warn("Process-shared memory allocator unavailable; falling back to SharedArrayBuffer.", error);
77
78
  }
78
79
  return undefined;
@@ -100,6 +101,7 @@ const DEFAULT_NODE_BINARY = "node";
100
101
  const DENO_PROCESS_WORKER_BOOT_ENV_ALLOW = [
101
102
  RUNTIME_PROCESS_WORKER_ENV,
102
103
  RUNTIME_PROCESS_WORKER_BOOT_ENV,
104
+ RUNTIME_POOL_DEPTH_ENV,
103
105
  ].join(",");
104
106
  const DENO_PROCESS_WORKER_INTERNAL_FLAGS = [
105
107
  `--allow-env=${DENO_PROCESS_WORKER_BOOT_ENV_ALLOW}`,
@@ -354,6 +356,7 @@ const currentProcessEnv = () => ({
354
356
  const processWorkerEnv = (extra) => ({
355
357
  ...currentProcessEnv(),
356
358
  [RUNTIME_PROCESS_WORKER_ENV]: "1",
359
+ [RUNTIME_POOL_DEPTH_ENV]: String(RUNTIME_POOL_DEPTH + 1),
357
360
  ...extra,
358
361
  });
359
362
  const processWorkerBootEnv = (bootPayload) => processWorkerEnv({