knitting 0.1.56 → 0.1.61

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 (101) hide show
  1. package/BROWSER.md +134 -0
  2. package/README.md +347 -46
  3. package/knitting.browser.d.ts +6 -0
  4. package/knitting.browser.js +1 -0
  5. package/knitting.d.ts +3 -2
  6. package/knitting.js +3 -3
  7. package/map.md +21 -9
  8. package/package.json +17 -2
  9. package/prebuilds/darwin-arm64-node-127/knitting_buffer_pointer.node +0 -0
  10. package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
  11. package/prebuilds/darwin-arm64-node-127/knitting_shm.node +0 -0
  12. package/prebuilds/darwin-arm64-node-137/knitting_buffer_pointer.node +0 -0
  13. package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
  14. package/prebuilds/darwin-arm64-node-137/knitting_shm.node +0 -0
  15. package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
  16. package/prebuilds/win32-x64-node-127/knitting_buffer_pointer.node +0 -0
  17. package/prebuilds/win32-x64-node-127/knitting_shared_memory.node +0 -0
  18. package/prebuilds/win32-x64-node-127/knitting_shm.node +0 -0
  19. package/prebuilds/win32-x64-node-137/knitting_buffer_pointer.node +0 -0
  20. package/prebuilds/win32-x64-node-137/knitting_shared_memory.node +0 -0
  21. package/prebuilds/win32-x64-node-137/knitting_shm.node +0 -0
  22. package/scripts/build-compiled-worker.ts +276 -0
  23. package/scripts/compiled-worker/porffor.d.ts +5 -0
  24. package/scripts/compiled-worker/runtime.ts +529 -0
  25. package/scripts/compiled-worker/task-shim.ts +32 -0
  26. package/src/api.d.ts +24 -1
  27. package/src/api.js +311 -27
  28. package/src/common/module-url.d.ts +1 -0
  29. package/src/common/module-url.js +30 -2
  30. package/src/common/node-compat.d.ts +2 -0
  31. package/src/common/path-canonical.js +20 -6
  32. package/src/common/runtime.d.ts +3 -1
  33. package/src/common/runtime.js +25 -4
  34. package/src/common/task-source.d.ts +2 -0
  35. package/src/common/task-source.js +17 -1
  36. package/src/common/worker-runtime.js +16 -7
  37. package/src/connections/buffer-reference-native.js +25 -1
  38. package/src/connections/buffer-reference.d.ts +23 -2
  39. package/src/connections/buffer-reference.js +143 -29
  40. package/src/connections/bun.d.ts +1 -1
  41. package/src/connections/bun.js +25 -14
  42. package/src/connections/deno.d.ts +3 -1
  43. package/src/connections/deno.js +35 -14
  44. package/src/connections/external-array-buffer.d.ts +2 -0
  45. package/src/connections/external-array-buffer.js +33 -0
  46. package/src/connections/node-addons.d.ts +11 -0
  47. package/src/connections/node-addons.js +49 -1
  48. package/src/connections/node-ffi-api.d.ts +19 -0
  49. package/src/connections/node-ffi-api.js +28 -0
  50. package/src/connections/node-ffi.d.ts +27 -0
  51. package/src/connections/node-ffi.js +262 -0
  52. package/src/connections/node.d.ts +1 -1
  53. package/src/connections/node.js +20 -20
  54. package/src/connections/package-assets.js +25 -15
  55. package/src/connections/posix.d.ts +42 -0
  56. package/src/connections/posix.js +101 -0
  57. package/src/connections/process-shared-buffer.d.ts +1 -0
  58. package/src/connections/process-shared-buffer.js +11 -45
  59. package/src/connections/types.d.ts +4 -0
  60. package/src/connections/windows.d.ts +19 -10
  61. package/src/connections/windows.js +87 -66
  62. package/src/error.d.ts +7 -6
  63. package/src/error.js +8 -7
  64. package/src/memory/lock.d.ts +112 -77
  65. package/src/memory/lock.js +314 -87
  66. package/src/memory/payloadCodec.d.ts +11 -2
  67. package/src/memory/payloadCodec.js +129 -37
  68. package/src/memory/regionRegistry.d.ts +1 -3
  69. package/src/memory/regionRegistry.js +16 -14
  70. package/src/permission/compatibility.d.ts +28 -0
  71. package/src/permission/compatibility.js +239 -0
  72. package/src/permission/index.d.ts +2 -0
  73. package/src/permission/index.js +1 -0
  74. package/src/permission/protocol.d.ts +1 -0
  75. package/src/permission/protocol.js +59 -15
  76. package/src/runtime/compiled-artifact.d.ts +21 -0
  77. package/src/runtime/compiled-artifact.js +260 -0
  78. package/src/runtime/compiled-builder.d.ts +8 -0
  79. package/src/runtime/compiled-builder.js +42 -0
  80. package/src/runtime/compiled-worker.d.ts +27 -0
  81. package/src/runtime/compiled-worker.js +562 -0
  82. package/src/runtime/dispatcher.js +9 -0
  83. package/src/runtime/inline-executor.js +27 -15
  84. package/src/runtime/pool.d.ts +121 -5
  85. package/src/runtime/pool.js +263 -49
  86. package/src/runtime/process-worker.d.ts +3 -13
  87. package/src/runtime/process-worker.js +97 -101
  88. package/src/runtime/tx-queue.d.ts +18 -2
  89. package/src/runtime/tx-queue.js +56 -16
  90. package/src/runtime/worker-common.d.ts +13 -0
  91. package/src/runtime/worker-common.js +102 -0
  92. package/src/types.d.ts +89 -9
  93. package/src/worker/composable-runners.js +3 -2
  94. package/src/worker/loop.d.ts +1 -0
  95. package/src/worker/loop.js +52 -17
  96. package/src/worker/rx-queue.d.ts +11 -1
  97. package/src/worker/rx-queue.js +4 -1
  98. package/src/worker/task-loader.d.ts +5 -4
  99. package/src/worker/task-loader.js +13 -7
  100. package/src/worker/timers.d.ts +8 -1
  101. package/src/worker/timers.js +28 -23
@@ -1,4 +1,4 @@
1
- import { attachPayloadTransportFinalizer, 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, payloadBufferName, PayloadSignal, registerLockPayloadCodec, 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";
@@ -6,8 +6,9 @@ import { encoderError, ErrorKnitting } from "../error.js";
6
6
  import { Envelope } from "../common/envelope.js";
7
7
  import { resolvePayloadBufferOptions, } from "./payload-config.js";
8
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
+ import { BUFFER_REFERENCE_NUMERIC_TRANSFER, BufferReference, isBufferReferenceValue, } from "../connections/buffer-reference.js";
10
+ import { isProcessSharedBufferValue, ProcessSharedBuffer, } from "../connections/process-shared-buffer.js";
9
11
  import { isNumericArray, numericArrayFromFloat64, } from "../connections/numeric-array.js";
10
- const BUFFER_REFERENCE_NUMERIC_TRANSFER = Symbol.for("knitting.bufferReference.numericTransfer");
11
12
  const memory = new ArrayBuffer(8);
12
13
  const Float64View = new Float64Array(memory);
13
14
  const BigInt64View = new BigInt64Array(memory);
@@ -45,6 +46,13 @@ const ENVELOPE_HEADER_DETAIL = "Envelope header must be a JSON-like value or str
45
46
  const ENVELOPE_PROMISE_DETAIL = "Envelope header cannot contain Promise values.";
46
47
  const DYNAMIC_PAYLOAD_LIMIT_DETAIL = "Dynamic payload exceeds maxPayloadBytes.";
47
48
  const DYNAMIC_PAYLOAD_CAPACITY_DETAIL = "Dynamic payload buffer capacity exceeded.";
49
+ const PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL = "SharedArrayBuffer and BufferReference are process-local pointer payloads " +
50
+ "and cannot cross a process-worker boundary; use ProcessSharedBuffer instead.";
51
+ const RESERVED_EXTERNAL_PAYLOAD_DETAIL = "Reserved Knitting external payload codec cannot be forged.";
52
+ const isProcessLocalPointerCodec = (codecId) => codecId === SHARED_ARRAY_BUFFER_CODEC_ID ||
53
+ codecId === BUFFER_REFERENCE_CODEC_ID;
54
+ const isReservedExternalPayloadCodec = (codecId) => isProcessLocalPointerCodec(codecId) ||
55
+ codecId === PROCESS_SHARED_BUFFER_CODEC_ID;
48
56
  const isPlainJsonObject = (value) => {
49
57
  const proto = objectGetPrototypeOf(value);
50
58
  return proto === objectPrototype || proto === null;
@@ -82,7 +90,16 @@ const isU32 = (value) => typeof value === "number" &&
82
90
  const isExternalPayloadLike = (value) => typeof value.toMetadata === "function" &&
83
91
  typeof value[EXTERNAL_PAYLOAD_BRAND] ===
84
92
  "string";
85
- const decodeExternalPayload = (raw) => {
93
+ const readTrustedExternalPayloadMetadata = (value) => {
94
+ if (isBufferReferenceValue(value)) {
95
+ return BufferReference.prototype.toMetadata.call(value);
96
+ }
97
+ if (isProcessSharedBufferValue(value)) {
98
+ return ProcessSharedBuffer.prototype.toMetadata.call(value);
99
+ }
100
+ return value.toMetadata();
101
+ };
102
+ const decodeExternalPayload = (raw, processBoundary) => {
86
103
  const payload = parseJSON(raw);
87
104
  if (!arrayIsArray(payload) || payload.length !== 2)
88
105
  return payload;
@@ -91,6 +108,9 @@ const decodeExternalPayload = (raw) => {
91
108
  if (typeof codecId !== "string") {
92
109
  return { codec: codecId, metadata };
93
110
  }
111
+ if (processBoundary && isProcessLocalPointerCodec(codecId)) {
112
+ throw new TypeError(PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL);
113
+ }
94
114
  const codec = externalPayloadGlobal.__KNITTING_PAYLOAD_CODECS__?.[codecId];
95
115
  return typeof codec?.decode === "function"
96
116
  ? codec.decode(metadata)
@@ -284,16 +304,14 @@ const requireStaticIO = (headersBuffer, headerSlotStrideU32, textCompat) => {
284
304
  * Returns `true` when the payload is encoded successfully.
285
305
  * Returns `false` when dynamic payload space could not be reserved.
286
306
  */
287
- export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, onPromise, }) => {
307
+ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, onPromise, processBoundary = false, }) => {
288
308
  const payloadSab = payload?.sab ?? sab;
289
309
  const resolvedPayloadConfig = resolvePayloadBufferOptions({
290
310
  sab: payloadSab,
291
311
  options: payload?.config ?? payloadConfig,
292
312
  });
293
313
  const maxPayloadBytes = resolvedPayloadConfig.maxPayloadBytes;
294
- const { allocTask, setSlotLength, free } = register({
295
- lockSector,
296
- });
314
+ const { allocTask, setSlotLength, free } = register({ lockSector });
297
315
  const { writeBinary: writeDynamicBinary, writeBuffer: writeDynamicBuffer, writeArrayBuffer: writeDynamicArrayBuffer, write8Binary: writeDynamic8Binary, writeUtf8: writeDynamicUtf8, } = createSharedDynamicBufferIO({
298
316
  sab: payloadSab,
299
317
  payloadConfig: resolvedPayloadConfig,
@@ -421,7 +439,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
421
439
  }
422
440
  }
423
441
  task[TaskIndex.Type] = dynamicType;
424
- if (!ensureWithinDynamicLimit(task, bytes, PayloadBuffer[dynamicType])) {
442
+ if (!ensureWithinDynamicLimit(task, bytes, payloadBufferName(dynamicType))) {
425
443
  return false;
426
444
  }
427
445
  const reservedSlot = reserveDynamicObject(task, bytes);
@@ -590,7 +608,9 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
590
608
  return true;
591
609
  };
592
610
  const tryEncodeBufferReferenceNumeric = (task, slotIndex, value) => {
593
- const words = value[BUFFER_REFERENCE_NUMERIC_TRANSFER]?.();
611
+ const words = isBufferReferenceValue(value)
612
+ ? BufferReference.prototype[BUFFER_REFERENCE_NUMERIC_TRANSFER].call(value)
613
+ : value[BUFFER_REFERENCE_NUMERIC_TRANSFER]?.();
594
614
  if (words === undefined)
595
615
  return false;
596
616
  task[TaskIndex.Type] = PayloadBuffer.BufferReference;
@@ -616,7 +636,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
616
636
  task.value = null;
617
637
  return true;
618
638
  };
619
- const encodeObjectExternalPayload = (task, slotIndex, externalPayload) => {
639
+ const encodeObjectExternalPayload = (task, slotIndex, externalPayload, trustedReservedCodec = false) => {
620
640
  const codecId = readExternalPayloadCodecId(externalPayload);
621
641
  if (codecId === undefined) {
622
642
  return encoderError({
@@ -626,21 +646,46 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
626
646
  detail: UNSUPPORTED_OBJECT_DETAIL,
627
647
  });
628
648
  }
629
- if (codecId === SHARED_ARRAY_BUFFER_CODEC_ID &&
630
- tryEncodeSharedArrayBufferNumeric(task, slotIndex, externalPayload)) {
631
- return true;
632
- }
633
- if (codecId === BUFFER_REFERENCE_CODEC_ID &&
634
- tryEncodeBufferReferenceNumeric(task, slotIndex, externalPayload)) {
635
- return true;
649
+ if (trustedReservedCodec) {
650
+ if (processBoundary && isProcessLocalPointerCodec(codecId)) {
651
+ return encoderError({
652
+ task,
653
+ type: ErrorKnitting.Serializable,
654
+ onPromise,
655
+ detail: PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL,
656
+ });
657
+ }
658
+ if (codecId === SHARED_ARRAY_BUFFER_CODEC_ID &&
659
+ tryEncodeSharedArrayBufferNumeric(task, slotIndex, externalPayload)) {
660
+ return true;
661
+ }
662
+ if (codecId === BUFFER_REFERENCE_CODEC_ID &&
663
+ tryEncodeBufferReferenceNumeric(task, slotIndex, externalPayload)) {
664
+ return true;
665
+ }
666
+ if (codecId === PROCESS_SHARED_BUFFER_CODEC_ID &&
667
+ tryEncodeProcessSharedBufferNumeric(task, slotIndex, externalPayload)) {
668
+ return true;
669
+ }
636
670
  }
637
- if (codecId === PROCESS_SHARED_BUFFER_CODEC_ID &&
638
- tryEncodeProcessSharedBufferNumeric(task, slotIndex, externalPayload)) {
639
- return true;
671
+ else if (isReservedExternalPayloadCodec(codecId)) {
672
+ return encoderError({
673
+ task,
674
+ type: ErrorKnitting.Serializable,
675
+ onPromise,
676
+ detail: processBoundary && isProcessLocalPointerCodec(codecId)
677
+ ? PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL
678
+ : RESERVED_EXTERNAL_PAYLOAD_DETAIL,
679
+ });
640
680
  }
641
681
  let text;
642
682
  try {
643
- text = stringifyJSON([codecId, externalPayload.toMetadata()]);
683
+ text = stringifyJSON([
684
+ codecId,
685
+ trustedReservedCodec
686
+ ? readTrustedExternalPayloadMetadata(externalPayload)
687
+ : externalPayload.toMetadata(),
688
+ ]);
644
689
  }
645
690
  catch (error) {
646
691
  const detail = error instanceof Error ? error.message : String(error);
@@ -727,10 +772,21 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
727
772
  if (body === null || typeof body !== "object")
728
773
  return undefined;
729
774
  const sharedArrayBuffer = getSharedArrayBufferPayload(body);
730
- if (sharedArrayBuffer !== undefined)
731
- return sharedArrayBuffer;
775
+ if (sharedArrayBuffer !== undefined) {
776
+ return { payload: sharedArrayBuffer, trustedReservedCodec: true };
777
+ }
778
+ if (isBufferReferenceValue(body) ||
779
+ isProcessSharedBufferValue(body)) {
780
+ return {
781
+ payload: body,
782
+ trustedReservedCodec: true,
783
+ };
784
+ }
732
785
  if (isExternalPayloadLike(body)) {
733
- return body;
786
+ return {
787
+ payload: body,
788
+ trustedReservedCodec: false,
789
+ };
734
790
  }
735
791
  return undefined;
736
792
  };
@@ -782,7 +838,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
782
838
  task.value = null;
783
839
  return true;
784
840
  };
785
- const encodeEnvelopeExternalBody = (task, slotIndex, headerText, headerIsString, externalBody) => {
841
+ const encodeEnvelopeExternalBody = (task, slotIndex, headerText, headerIsString, externalBody, trustedReservedCodec) => {
786
842
  const codecId = readExternalPayloadCodecId(externalBody);
787
843
  if (codecId === undefined) {
788
844
  return encoderError({
@@ -792,9 +848,34 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
792
848
  detail: ENVELOPE_PAYLOAD_DETAIL,
793
849
  });
794
850
  }
851
+ if (trustedReservedCodec) {
852
+ if (processBoundary && isProcessLocalPointerCodec(codecId)) {
853
+ return encoderError({
854
+ task,
855
+ type: ErrorKnitting.Serializable,
856
+ onPromise,
857
+ detail: PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL,
858
+ });
859
+ }
860
+ }
861
+ else if (isReservedExternalPayloadCodec(codecId)) {
862
+ return encoderError({
863
+ task,
864
+ type: ErrorKnitting.Serializable,
865
+ onPromise,
866
+ detail: processBoundary && isProcessLocalPointerCodec(codecId)
867
+ ? PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL
868
+ : RESERVED_EXTERNAL_PAYLOAD_DETAIL,
869
+ });
870
+ }
795
871
  let bodyText;
796
872
  try {
797
- bodyText = stringifyJSON([codecId, externalBody.toMetadata()]);
873
+ bodyText = stringifyJSON([
874
+ codecId,
875
+ trustedReservedCodec
876
+ ? readTrustedExternalPayloadMetadata(externalBody)
877
+ : externalBody.toMetadata(),
878
+ ]);
798
879
  }
799
880
  catch (error) {
800
881
  const detail = error instanceof Error ? error.message : String(error);
@@ -874,7 +955,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
874
955
  const headerText = encodeEnvelopeHeaderText(task, header, headerIsString);
875
956
  if (headerText === undefined)
876
957
  return false;
877
- return encodeEnvelopeExternalBody(task, slotIndex, headerText, headerIsString, externalBody);
958
+ return encodeEnvelopeExternalBody(task, slotIndex, headerText, headerIsString, externalBody.payload, externalBody.trustedReservedCodec);
878
959
  }
879
960
  return encoderError({
880
961
  task,
@@ -947,7 +1028,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
947
1028
  const objectValue = args;
948
1029
  const sharedArrayBufferPayload = getSharedArrayBufferPayload(objectValue);
949
1030
  if (sharedArrayBufferPayload !== undefined) {
950
- return encodeObjectExternalPayload(task, slotIndex, sharedArrayBufferPayload);
1031
+ return encodeObjectExternalPayload(task, slotIndex, sharedArrayBufferPayload, true);
951
1032
  }
952
1033
  const objectProto = objectGetPrototypeOf(objectValue);
953
1034
  if (isRuntimeUint8Array(objectValue)) {
@@ -997,6 +1078,10 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
997
1078
  task.value = null;
998
1079
  return true;
999
1080
  }
1081
+ if (isBufferReferenceValue(objectValue) ||
1082
+ isProcessSharedBufferValue(objectValue)) {
1083
+ return encodeObjectExternalPayload(task, slotIndex, objectValue, true);
1084
+ }
1000
1085
  const objectCtor = objectValue
1001
1086
  .constructor;
1002
1087
  if (isRuntimeBuffer(objectValue)) {
@@ -1126,15 +1211,13 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1126
1211
  };
1127
1212
  return encodeDispatch;
1128
1213
  };
1129
- export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, host, }) => {
1214
+ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, host, processBoundary = false, }) => {
1130
1215
  const payloadSab = payload?.sab ?? sab;
1131
1216
  const resolvedPayloadConfig = resolvePayloadBufferOptions({
1132
1217
  sab: payloadSab,
1133
1218
  options: payload?.config ?? payloadConfig,
1134
1219
  });
1135
- const { free } = register({
1136
- lockSector,
1137
- });
1220
+ const { free } = register({ lockSector });
1138
1221
  const freeTaskSlot = (task) => free(getTaskSlotIndex(task));
1139
1222
  const { readUtf8: readDynamicUtf8, readBytesCopy: readDynamicBytesCopy, readBytesBufferCopy: readDynamicBufferCopy, readBufferCopy: readDynamicBuffer, readBytesArrayBufferCopy: readDynamicArrayBufferCopy, readArrayBufferCopy: readDynamicArrayBuffer, read8BytesFloatCopy: readDynamic8BytesFloatCopy, read8BytesFloatView: readDynamic8BytesFloatView, } = createSharedDynamicBufferIO({
1140
1223
  sab: payloadSab,
@@ -1152,7 +1235,13 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1152
1235
  // store the slot in their Task, to just get it when it comes
1153
1236
  // to the static versions of decoding
1154
1237
  return (task, slotIndex, specialFlags) => {
1155
- switch (task[TaskIndex.Type]) {
1238
+ const payloadType = task[TaskIndex.Type];
1239
+ if (processBoundary &&
1240
+ (payloadType === PayloadBuffer.SharedArrayBuffer ||
1241
+ payloadType === PayloadBuffer.BufferReference)) {
1242
+ throw new TypeError(PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL);
1243
+ }
1244
+ switch (payloadType) {
1156
1245
  case PayloadSignal.BigInt:
1157
1246
  Uint32View[0] = task[TaskIndex.Start];
1158
1247
  Uint32View[1] = task[TaskIndex.End];
@@ -1230,7 +1319,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1230
1319
  ? rawHeader
1231
1320
  : parseJSON(rawHeader);
1232
1321
  const bodyStart = task[TaskIndex.Start];
1233
- const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]));
1322
+ const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]), processBoundary);
1234
1323
  task.value = new Envelope(header, body);
1235
1324
  freeTaskSlot(task);
1236
1325
  return;
@@ -1244,7 +1333,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1244
1333
  PayloadBuffer.EnvelopeDynamicHeaderStringExternal
1245
1334
  ? rawHeader
1246
1335
  : parseJSON(rawHeader);
1247
- const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]));
1336
+ const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]), processBoundary);
1248
1337
  task.value = new Envelope(header, body);
1249
1338
  freeTaskSlot(task);
1250
1339
  return;
@@ -1324,11 +1413,11 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1324
1413
  return;
1325
1414
  }
1326
1415
  case PayloadBuffer.ExternalPayload:
1327
- task.value = decodeExternalPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]));
1416
+ task.value = decodeExternalPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]), processBoundary);
1328
1417
  freeTaskSlot(task);
1329
1418
  return;
1330
1419
  case PayloadBuffer.StaticExternalPayload:
1331
- task.value = decodeExternalPayload(readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex));
1420
+ task.value = decodeExternalPayload(readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex), processBoundary);
1332
1421
  return;
1333
1422
  case PayloadBuffer.ProcessSharedBuffer:
1334
1423
  task.value = decodeProcessSharedBufferNumericWords(readStaticU32Words(processSharedBufferWords, PROCESS_SHARED_BUFFER_NUMERIC_WORDS, slotIndex));
@@ -1375,3 +1464,6 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1375
1464
  }
1376
1465
  };
1377
1466
  };
1467
+ // Break the lock.ts <-> payloadCodec.ts cycle (see lock.ts): register the codec
1468
+ // factories on load instead of having lock.ts import them.
1469
+ registerLockPayloadCodec(encodePayload, decodePayload);
@@ -1,10 +1,8 @@
1
1
  import type { Task } from "./lock.js";
2
2
  import { type SharedBufferSource } from "../common/shared-buffer-region.js";
3
3
  export type RegisterMalloc = ReturnType<typeof register>;
4
- export type RegionRegistryPublishMode = "plain" | "atomic";
5
- export declare const register: ({ lockSector, publishMode, }: {
4
+ export declare const register: ({ lockSector, }: {
6
5
  lockSector?: SharedBufferSource;
7
- publishMode?: RegionRegistryPublishMode;
8
6
  }) => {
9
7
  allocTask: (task: Task) => number;
10
8
  setSlotLength: (slotIndex: number, payloadLen: number) => boolean;
@@ -4,7 +4,7 @@ import { toSharedBufferRegion, } from "../common/shared-buffer-region.js";
4
4
  // Low 5 bits = slot index, high 27 bits = caller meta.
5
5
  // Inlined from setTaskSlotIndex to avoid cross-closure call on hot path.
6
6
  const SLOT_META_PACKED_MASK = 0xFFFFFFE0; // (~0x1F) >>> 0
7
- export const register = ({ lockSector, publishMode = "plain", }) => {
7
+ export const register = ({ lockSector, }) => {
8
8
  const lockRegion = toSharedBufferRegion(lockSector ?? createWasmSharedArrayBuffer(LOCK_SECTOR_BYTE_LENGTH));
9
9
  const lockSAB = lockRegion.sab;
10
10
  const hostBits = new Int32Array(lockSAB, lockRegion.byteOffset + PAYLOAD_LOCK_HOST_BITS_OFFSET_BYTES, 1);
@@ -14,7 +14,7 @@ export const register = ({ lockSector, publishMode = "plain", }) => {
14
14
  const clz32 = Math.clz32;
15
15
  const a_load = Atomics.load;
16
16
  const a_store = Atomics.store;
17
- const useAtomicPublish = publishMode === "atomic";
17
+ const a_xor = Atomics.xor;
18
18
  const EMPTY = 0xFFFFFFFF >>> 0;
19
19
  const SLOT_MASK = TASK_SLOT_INDEX_MASK;
20
20
  const START_MASK = (~SLOT_MASK) >>> 0;
@@ -23,7 +23,6 @@ export const register = ({ lockSector, publishMode = "plain", }) => {
23
23
  let usedBits = 0 | 0;
24
24
  // scalar state (faster than Uint32Array(1))
25
25
  let hostLast = 0 | 0;
26
- let workerLast = 0 | 0;
27
26
  const startAndIndexToArray = (length) => startAndIndex.slice(0, length);
28
27
  const compactFreeBitsStable = (b, freeBits) => {
29
28
  const sai = startAndIndex;
@@ -92,10 +91,14 @@ export const register = ({ lockSector, publishMode = "plain", }) => {
92
91
  let freeBits = (~(hostLast ^ w)) >>> 0;
93
92
  if (freeBits !== 0)
94
93
  freeBits &= usedBits;
95
- if (freeBits === EMPTY) {
94
+ // Every tracked slot is free, so restart the arena from offset 0. A free
95
+ // racing after this snapshot can only delay reclamation until the next
96
+ // allocation; it cannot make a live slot appear free.
97
+ if (freeBits === (usedBits >>> 0)) {
96
98
  tableLength = 0;
97
99
  usedBits = 0 | 0;
98
100
  tl = 0;
101
+ freeBits = 0 >>> 0;
99
102
  }
100
103
  else if (freeBits !== 0) {
101
104
  for (let i = 0; i < tl; i++) {
@@ -247,10 +250,7 @@ export const register = ({ lockSector, publishMode = "plain", }) => {
247
250
  const slotIndex = findAndInsert(task, size);
248
251
  if (slotIndex === -1)
249
252
  return -1;
250
- if (useAtomicPublish)
251
- a_store(hostBits, 0, hostLast);
252
- else
253
- hostBits[0] = hostLast;
253
+ a_store(hostBits, 0, hostLast);
254
254
  return slotIndex;
255
255
  };
256
256
  const setSlotLength = (slotIndex, payloadLen) => {
@@ -264,13 +264,15 @@ export const register = ({ lockSector, publishMode = "plain", }) => {
264
264
  size64bit[slotIndex] = aligned >>> 0;
265
265
  return true;
266
266
  };
267
+ // Always a read-modify-write. `free()` genuinely has two callers even without
268
+ // work stealing: the encode-side rollback (`failDynamicWriteAfterReserve`)
269
+ // and the decode-side release (`freeTaskSlot`). Each held a private
270
+ // `workerLast` shadow and blind-stored the whole word, so one overwrote the
271
+ // other's toggles — the allocator then handed out a region that was still
272
+ // live, aliasing payloads. XOR of distinct bits commutes, so one atomic
273
+ // toggle per free is correct for any number of writers.
267
274
  const free = (index) => {
268
- index = index & TASK_SLOT_INDEX_MASK;
269
- workerLast ^= 1 << index;
270
- if (useAtomicPublish)
271
- a_store(workerBits, 0, workerLast);
272
- else
273
- workerBits[0] = workerLast;
275
+ a_xor(workerBits, 0, 1 << (index & TASK_SLOT_INDEX_MASK));
274
276
  };
275
277
  return {
276
278
  allocTask,
@@ -0,0 +1,28 @@
1
+ import type { PermissionProtocolInput, ResolvedPermissionProtocol } from "./protocol.js";
2
+ type ProcessPermissionRuntime = "bun" | "deno" | "node";
3
+ type PermissionCompatibilityLevel = "exact" | "coarse" | "unsupported";
4
+ type ProcessPermissionTarget = {
5
+ runtime: ProcessPermissionRuntime;
6
+ /**
7
+ * Omitted when a wrapper or another host runtime makes the Node version
8
+ * impossible to prove synchronously.
9
+ */
10
+ nodeMajor?: number;
11
+ };
12
+ type PermissionCompatibilityCheck = {
13
+ permission: string;
14
+ level: PermissionCompatibilityLevel;
15
+ explicit: boolean;
16
+ reason: string;
17
+ };
18
+ type ProcessPermissionCompatibilityReport = {
19
+ target: ProcessPermissionTarget;
20
+ checks: PermissionCompatibilityCheck[];
21
+ };
22
+ export declare const classifyProcessPermissionCompatibility: ({ permission, resolved, target, }: {
23
+ permission: PermissionProtocolInput | undefined;
24
+ resolved: ResolvedPermissionProtocol | undefined;
25
+ target: ProcessPermissionTarget;
26
+ }) => ProcessPermissionCompatibilityReport;
27
+ export declare const enforceProcessPermissionCompatibility: (report: ProcessPermissionCompatibilityReport) => void;
28
+ export type { PermissionCompatibilityCheck, PermissionCompatibilityLevel, ProcessPermissionCompatibilityReport, ProcessPermissionRuntime, ProcessPermissionTarget, };