knitting 0.1.56 → 0.1.60

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 (77) hide show
  1. package/README.md +254 -40
  2. package/knitting.d.ts +3 -2
  3. package/knitting.js +3 -3
  4. package/map.md +21 -9
  5. package/package.json +7 -1
  6. package/scripts/build-compiled-worker.ts +276 -0
  7. package/scripts/compiled-worker/porffor.d.ts +5 -0
  8. package/scripts/compiled-worker/runtime.ts +529 -0
  9. package/scripts/compiled-worker/task-shim.ts +32 -0
  10. package/src/api.d.ts +24 -1
  11. package/src/api.js +199 -20
  12. package/src/common/module-url.d.ts +1 -0
  13. package/src/common/module-url.js +30 -2
  14. package/src/common/node-compat.d.ts +2 -0
  15. package/src/common/path-canonical.js +20 -6
  16. package/src/common/runtime.d.ts +2 -1
  17. package/src/common/runtime.js +18 -4
  18. package/src/common/task-source.d.ts +2 -0
  19. package/src/common/task-source.js +17 -1
  20. package/src/common/worker-runtime.js +16 -7
  21. package/src/connections/buffer-reference-native.js +25 -1
  22. package/src/connections/buffer-reference.d.ts +23 -2
  23. package/src/connections/buffer-reference.js +143 -29
  24. package/src/connections/bun.d.ts +1 -1
  25. package/src/connections/bun.js +25 -14
  26. package/src/connections/deno.d.ts +3 -1
  27. package/src/connections/deno.js +35 -14
  28. package/src/connections/external-array-buffer.d.ts +2 -0
  29. package/src/connections/external-array-buffer.js +33 -0
  30. package/src/connections/node-addons.d.ts +11 -0
  31. package/src/connections/node-addons.js +49 -1
  32. package/src/connections/node-ffi-api.d.ts +19 -0
  33. package/src/connections/node-ffi-api.js +28 -0
  34. package/src/connections/node-ffi.d.ts +27 -0
  35. package/src/connections/node-ffi.js +262 -0
  36. package/src/connections/node.d.ts +1 -1
  37. package/src/connections/node.js +20 -20
  38. package/src/connections/package-assets.js +25 -15
  39. package/src/connections/posix.d.ts +42 -0
  40. package/src/connections/posix.js +101 -0
  41. package/src/connections/process-shared-buffer.d.ts +1 -0
  42. package/src/connections/process-shared-buffer.js +11 -45
  43. package/src/connections/types.d.ts +4 -0
  44. package/src/connections/windows.d.ts +19 -10
  45. package/src/connections/windows.js +87 -66
  46. package/src/error.d.ts +7 -6
  47. package/src/error.js +8 -7
  48. package/src/memory/lock.d.ts +87 -77
  49. package/src/memory/lock.js +103 -84
  50. package/src/memory/payloadCodec.d.ts +11 -2
  51. package/src/memory/payloadCodec.js +127 -31
  52. package/src/permission/compatibility.d.ts +28 -0
  53. package/src/permission/compatibility.js +239 -0
  54. package/src/permission/index.d.ts +2 -0
  55. package/src/permission/index.js +1 -0
  56. package/src/permission/protocol.d.ts +1 -0
  57. package/src/permission/protocol.js +54 -14
  58. package/src/runtime/compiled-artifact.d.ts +21 -0
  59. package/src/runtime/compiled-artifact.js +260 -0
  60. package/src/runtime/compiled-builder.d.ts +8 -0
  61. package/src/runtime/compiled-builder.js +42 -0
  62. package/src/runtime/compiled-worker.d.ts +27 -0
  63. package/src/runtime/compiled-worker.js +562 -0
  64. package/src/runtime/inline-executor.js +27 -15
  65. package/src/runtime/pool.d.ts +7 -2
  66. package/src/runtime/pool.js +74 -6
  67. package/src/runtime/process-worker.d.ts +1 -1
  68. package/src/runtime/process-worker.js +48 -8
  69. package/src/runtime/tx-queue.d.ts +3 -1
  70. package/src/runtime/tx-queue.js +4 -1
  71. package/src/types.d.ts +48 -6
  72. package/src/worker/composable-runners.js +3 -2
  73. package/src/worker/loop.d.ts +1 -0
  74. package/src/worker/loop.js +25 -12
  75. package/src/worker/task-loader.d.ts +5 -4
  76. package/src/worker/task-loader.js +13 -7
  77. package/src/worker/timers.js +8 -6
@@ -1,5 +1,10 @@
1
1
  import RingQueue from "../ipc/tools/ring-queue.js";
2
- import { decodePayload, encodePayload } from "./payloadCodec.js";
2
+ let registeredEncodePayload;
3
+ let registeredDecodePayload;
4
+ export const registerLockPayloadCodec = (encode, decode) => {
5
+ registeredEncodePayload = encode;
6
+ registeredDecodePayload = decode;
7
+ };
3
8
  import { createSharedArrayBuffer, createWasmSharedArrayBuffer, } from "../common/runtime.js";
4
9
  import { toSharedBufferRegion, } from "../common/shared-buffer-region.js";
5
10
  import { probeLockBufferTextCompat, } from "../common/shared-buffer-text.js";
@@ -7,69 +12,76 @@ import { resolvePayloadBufferOptions, } from "./payload-config.js";
7
12
  /**
8
13
  * TODO: Compose all the instance where the array is passed as argument
9
14
  */
10
- export var PayloadSignal;
11
- (function (PayloadSignal) {
12
- PayloadSignal[PayloadSignal["UNREACHABLE"] = 0] = "UNREACHABLE";
13
- PayloadSignal[PayloadSignal["BigInt"] = 2] = "BigInt";
14
- PayloadSignal[PayloadSignal["True"] = 3] = "True";
15
- PayloadSignal[PayloadSignal["False"] = 4] = "False";
16
- PayloadSignal[PayloadSignal["Undefined"] = 5] = "Undefined";
17
- PayloadSignal[PayloadSignal["NaN"] = 6] = "NaN";
18
- PayloadSignal[PayloadSignal["Float64"] = 9] = "Float64";
19
- PayloadSignal[PayloadSignal["Null"] = 10] = "Null";
20
- })(PayloadSignal || (PayloadSignal = {}));
21
- export var PayloadBuffer;
22
- (function (PayloadBuffer) {
23
- PayloadBuffer[PayloadBuffer["BORDER_SIGNAL_BUFFER"] = 11] = "BORDER_SIGNAL_BUFFER";
24
- PayloadBuffer[PayloadBuffer["String"] = 11] = "String";
25
- PayloadBuffer[PayloadBuffer["Json"] = 12] = "Json";
26
- PayloadBuffer[PayloadBuffer["StaticString"] = 15] = "StaticString";
27
- PayloadBuffer[PayloadBuffer["StaticJson"] = 16] = "StaticJson";
28
- PayloadBuffer[PayloadBuffer["Binary"] = 17] = "Binary";
29
- PayloadBuffer[PayloadBuffer["StaticBinary"] = 18] = "StaticBinary";
30
- PayloadBuffer[PayloadBuffer["Int32Array"] = 19] = "Int32Array";
31
- PayloadBuffer[PayloadBuffer["Float64Array"] = 20] = "Float64Array";
32
- PayloadBuffer[PayloadBuffer["BigInt64Array"] = 21] = "BigInt64Array";
33
- PayloadBuffer[PayloadBuffer["BigUint64Array"] = 22] = "BigUint64Array";
34
- PayloadBuffer[PayloadBuffer["DataView"] = 23] = "DataView";
35
- PayloadBuffer[PayloadBuffer["Error"] = 24] = "Error";
36
- PayloadBuffer[PayloadBuffer["Date"] = 25] = "Date";
37
- PayloadBuffer[PayloadBuffer["Symbol"] = 26] = "Symbol";
38
- PayloadBuffer[PayloadBuffer["StaticSymbol"] = 27] = "StaticSymbol";
39
- PayloadBuffer[PayloadBuffer["BigInt"] = 28] = "BigInt";
40
- PayloadBuffer[PayloadBuffer["StaticBigInt"] = 29] = "StaticBigInt";
41
- PayloadBuffer[PayloadBuffer["StaticInt32Array"] = 31] = "StaticInt32Array";
42
- PayloadBuffer[PayloadBuffer["StaticFloat64Array"] = 32] = "StaticFloat64Array";
43
- PayloadBuffer[PayloadBuffer["StaticBigInt64Array"] = 33] = "StaticBigInt64Array";
44
- PayloadBuffer[PayloadBuffer["StaticBigUint64Array"] = 34] = "StaticBigUint64Array";
45
- PayloadBuffer[PayloadBuffer["StaticDataView"] = 35] = "StaticDataView";
46
- PayloadBuffer[PayloadBuffer["ArrayBuffer"] = 36] = "ArrayBuffer";
47
- PayloadBuffer[PayloadBuffer["StaticArrayBuffer"] = 37] = "StaticArrayBuffer";
48
- PayloadBuffer[PayloadBuffer["Buffer"] = 38] = "Buffer";
49
- PayloadBuffer[PayloadBuffer["StaticBuffer"] = 39] = "StaticBuffer";
50
- PayloadBuffer[PayloadBuffer["EnvelopeStaticHeader"] = 40] = "EnvelopeStaticHeader";
51
- PayloadBuffer[PayloadBuffer["EnvelopeDynamicHeader"] = 41] = "EnvelopeDynamicHeader";
52
- PayloadBuffer[PayloadBuffer["EnvelopeStaticHeaderString"] = 42] = "EnvelopeStaticHeaderString";
53
- PayloadBuffer[PayloadBuffer["EnvelopeDynamicHeaderString"] = 43] = "EnvelopeDynamicHeaderString";
54
- PayloadBuffer[PayloadBuffer["ExternalPayload"] = 44] = "ExternalPayload";
55
- PayloadBuffer[PayloadBuffer["StaticExternalPayload"] = 45] = "StaticExternalPayload";
56
- PayloadBuffer[PayloadBuffer["ProcessSharedBuffer"] = 46] = "ProcessSharedBuffer";
57
- PayloadBuffer[PayloadBuffer["BufferReference"] = 47] = "BufferReference";
58
- PayloadBuffer[PayloadBuffer["SharedArrayBuffer"] = 48] = "SharedArrayBuffer";
59
- PayloadBuffer[PayloadBuffer["EnvelopeStaticHeaderExternal"] = 49] = "EnvelopeStaticHeaderExternal";
60
- PayloadBuffer[PayloadBuffer["EnvelopeDynamicHeaderExternal"] = 50] = "EnvelopeDynamicHeaderExternal";
61
- PayloadBuffer[PayloadBuffer["EnvelopeStaticHeaderStringExternal"] = 51] = "EnvelopeStaticHeaderStringExternal";
62
- PayloadBuffer[PayloadBuffer["EnvelopeDynamicHeaderStringExternal"] = 52] = "EnvelopeDynamicHeaderStringExternal";
63
- PayloadBuffer[PayloadBuffer["NumericArray"] = 53] = "NumericArray";
64
- PayloadBuffer[PayloadBuffer["StaticNumericArray"] = 54] = "StaticNumericArray";
65
- })(PayloadBuffer || (PayloadBuffer = {}));
66
- export var LockBound;
67
- (function (LockBound) {
68
- LockBound[LockBound["paddingLock"] = 0] = "paddingLock";
69
- LockBound[LockBound["padding"] = 0] = "padding";
70
- LockBound[LockBound["slots"] = 32] = "slots";
71
- LockBound[LockBound["header"] = 0] = "header";
72
- })(LockBound || (LockBound = {}));
15
+ // const objects replace `enum`s throughout this module: Andromeda's Nova engine
16
+ // panics on `enum`. Value access, duplicate-value members, and type preserved;
17
+ // identical emit on Node/Deno/Bun.
18
+ export const PayloadSignal = {
19
+ UNREACHABLE: 0,
20
+ BigInt: 2,
21
+ True: 3,
22
+ False: 4,
23
+ Undefined: 5,
24
+ NaN: 6,
25
+ Float64: 9,
26
+ Null: 10,
27
+ };
28
+ export const PayloadBuffer = {
29
+ BORDER_SIGNAL_BUFFER: 11,
30
+ String: 11,
31
+ Json: 12,
32
+ StaticString: 15,
33
+ StaticJson: 16,
34
+ Binary: 17,
35
+ StaticBinary: 18,
36
+ Int32Array: 19,
37
+ Float64Array: 20,
38
+ BigInt64Array: 21,
39
+ BigUint64Array: 22,
40
+ DataView: 23,
41
+ Error: 24,
42
+ Date: 25,
43
+ Symbol: 26,
44
+ StaticSymbol: 27,
45
+ BigInt: 28,
46
+ StaticBigInt: 29,
47
+ StaticInt32Array: 31,
48
+ StaticFloat64Array: 32,
49
+ StaticBigInt64Array: 33,
50
+ StaticBigUint64Array: 34,
51
+ StaticDataView: 35,
52
+ ArrayBuffer: 36,
53
+ StaticArrayBuffer: 37,
54
+ Buffer: 38,
55
+ StaticBuffer: 39,
56
+ EnvelopeStaticHeader: 40,
57
+ EnvelopeDynamicHeader: 41,
58
+ EnvelopeStaticHeaderString: 42,
59
+ EnvelopeDynamicHeaderString: 43,
60
+ ExternalPayload: 44,
61
+ StaticExternalPayload: 45,
62
+ ProcessSharedBuffer: 46,
63
+ BufferReference: 47,
64
+ SharedArrayBuffer: 48,
65
+ EnvelopeStaticHeaderExternal: 49,
66
+ EnvelopeDynamicHeaderExternal: 50,
67
+ EnvelopeStaticHeaderStringExternal: 51,
68
+ EnvelopeDynamicHeaderStringExternal: 52,
69
+ NumericArray: 53,
70
+ StaticNumericArray: 54,
71
+ };
72
+ // Value -> name lookup (enum reverse-map), for payload-limit error labels.
73
+ // Later keys win on duplicate values (11 -> "String"), as TS enums do.
74
+ const PayloadBufferName = {};
75
+ for (const key of Object.keys(PayloadBuffer)) {
76
+ PayloadBufferName[PayloadBuffer[key]] = key;
77
+ }
78
+ export const payloadBufferName = (value) => PayloadBufferName[value] ?? String(value);
79
+ export const LockBound = {
80
+ paddingLock: 0,
81
+ padding: 0,
82
+ slots: 32,
83
+ header: 0,
84
+ };
73
85
  export const LOCK_CACHE_LINE_BYTES = 64;
74
86
  export const LOCK_SECTOR_BYTES = 256;
75
87
  export const PayloadTransportFinalizer = Symbol.for("knitting.payloadCodec.transportFinalizer");
@@ -123,34 +135,33 @@ export const runTaskFinalizers = (task) => {
123
135
  }
124
136
  }
125
137
  };
126
- export var TaskIndex;
127
- (function (TaskIndex) {
138
+ export const TaskIndex = {
128
139
  /**
129
140
  * Worker -> host response flags word.
130
141
  */
131
- TaskIndex[TaskIndex["FlagsToHost"] = 0] = "FlagsToHost";
142
+ FlagsToHost: 0,
132
143
  /**
133
144
  * Host -> worker request function id (low 16 bits).
134
145
  * High 16 bits are reserved for caller metadata on request path.
135
146
  * NOTE: shares the same storage word as `FlagsToHost`.
136
147
  */
137
- TaskIndex[TaskIndex["FunctionID"] = 0] = "FunctionID";
138
- TaskIndex[TaskIndex["ID"] = 1] = "ID";
139
- TaskIndex[TaskIndex["Type"] = 2] = "Type";
140
- TaskIndex[TaskIndex["Start"] = 3] = "Start";
141
- TaskIndex[TaskIndex["End"] = 4] = "End";
142
- TaskIndex[TaskIndex["PayloadLen"] = 5] = "PayloadLen";
148
+ FunctionID: 0,
149
+ ID: 1,
150
+ Type: 2,
151
+ Start: 3,
152
+ End: 4,
153
+ PayloadLen: 5,
143
154
  /**
144
155
  * Low 5 bits: region slot index (0..31).
145
156
  * High 27 bits: reserved for caller metadata (e.g. enqueue timing).
146
157
  */
147
- TaskIndex[TaskIndex["slotBuffer"] = 6] = "slotBuffer";
148
- TaskIndex[TaskIndex["Size"] = 8] = "Size";
158
+ slotBuffer: 6,
159
+ Size: 8,
149
160
  /**
150
161
  * Total slot length in Uint32 words, including the task header.
151
162
  */
152
- TaskIndex[TaskIndex["TotalBuff"] = 144] = "TotalBuff";
153
- })(TaskIndex || (TaskIndex = {}));
163
+ TotalBuff: 144,
164
+ };
154
165
  export const TASK_SLOT_INDEX_BITS = 5;
155
166
  export const TASK_SLOT_INDEX_MASK = (1 << TASK_SLOT_INDEX_BITS) - 1;
156
167
  export const TASK_SLOT_META_BITS = 32 - TASK_SLOT_INDEX_BITS;
@@ -186,10 +197,9 @@ export const setTaskSlotMeta = (task, value) => {
186
197
  task[TaskIndex.slotBuffer] =
187
198
  ((task[TaskIndex.slotBuffer] & TASK_SLOT_INDEX_MASK) | encodedMeta) >>> 0;
188
199
  };
189
- export var TaskFlag;
190
- (function (TaskFlag) {
191
- TaskFlag[TaskFlag["Reject"] = 1] = "Reject";
192
- })(TaskFlag || (TaskFlag = {}));
200
+ export const TaskFlag = {
201
+ Reject: 1,
202
+ };
193
203
  // Main queue lock layout in bytes.
194
204
  // The queue protocol uses two Int32 signal words, each on its own cache line:
195
205
  // - hostBits at byte 0
@@ -240,6 +250,8 @@ export const makeTask = () => {
240
250
  const fillTaskFrom = (task, array, at) => {
241
251
  task[0] = array[at];
242
252
  task[1] = array[at + 1];
253
+ // Raw numeric tag word; const-object payload "enums" are strict literal unions
254
+ // (numeric `enum` accepted any number), so cast back on restore.
243
255
  task[2] = array[at + 2];
244
256
  task[3] = array[at + 3];
245
257
  task[4] = array[at + 4];
@@ -264,7 +276,7 @@ const settleTask = (task) => {
264
276
  task[TaskIndex["FlagsToHost"]] = 0;
265
277
  }
266
278
  };
267
- export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload, payloadConfig, payloadSector, textCompat, resultList, toSentList, recycleList, }) => {
279
+ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload, payloadConfig, payloadSector, textCompat, resultList, toSentList, recycleList, processBoundary, }) => {
268
280
  // Layout within `lockSectorRegion`:
269
281
  // - hostBits starts at byte 0
270
282
  // - workerBits starts at byte 64
@@ -297,7 +309,12 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
297
309
  payload: payloadSAB,
298
310
  });
299
311
  let promiseHandler;
300
- const encodeTask = encodePayload({
312
+ if (registeredEncodePayload === undefined ||
313
+ registeredDecodePayload === undefined) {
314
+ throw new Error("Payload codec not registered before lock2(). Ensure the module that " +
315
+ 'builds locks imports "./payloadCodec.ts" (it self-registers on load).');
316
+ }
317
+ const encodeTask = registeredEncodePayload({
301
318
  payload: {
302
319
  sab: payloadSAB,
303
320
  config: resolvedPayloadConfig,
@@ -306,6 +323,7 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
306
323
  headerSlotStrideU32: headersSlotStride,
307
324
  lockSector: payloadLockRegion,
308
325
  textCompat: resolvedTextCompat,
326
+ processBoundary,
309
327
  onPromise: (task, isRejected, value) => {
310
328
  if ((task[TASK_LOCAL_FLAGS_INDEX] & TASK_LOCAL_PROMISE_TRACKED_FLAG) !==
311
329
  0 &&
@@ -318,7 +336,7 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
318
336
  promiseHandler(task, isRejected, value);
319
337
  },
320
338
  });
321
- const decodeTask = decodePayload({
339
+ const decodeTask = registeredDecodePayload({
322
340
  payload: {
323
341
  sab: payloadSAB,
324
342
  config: resolvedPayloadConfig,
@@ -327,6 +345,7 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
327
345
  headerSlotStrideU32: headersSlotStride,
328
346
  lockSector: payloadLockRegion,
329
347
  textCompat: resolvedTextCompat,
348
+ processBoundary,
330
349
  });
331
350
  let LastLocal = 0 | 0;
332
351
  let LastWorker = 0 | 0;
@@ -6,7 +6,7 @@ import type { SharedBufferSource } from "../common/shared-buffer-region.js";
6
6
  * Returns `true` when the payload is encoded successfully.
7
7
  * Returns `false` when dynamic payload space could not be reserved.
8
8
  */
9
- export declare const encodePayload: ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, onPromise, }: {
9
+ export declare const encodePayload: ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, onPromise, processBoundary, }: {
10
10
  lockSector?: SharedBufferSource;
11
11
  payload?: {
12
12
  sab?: SharedBufferSource;
@@ -24,8 +24,13 @@ export declare const encodePayload: ({ lockSector, payload, sab, payloadConfig,
24
24
  headerSlotStrideU32?: number;
25
25
  textCompat?: LockBufferTextCompat;
26
26
  onPromise?: PromisePayloadHandler;
27
+ /**
28
+ * Reject process-local pointer payloads while preserving ProcessSharedBuffer.
29
+ * Process transports must set this on both encoders and decoders.
30
+ */
31
+ processBoundary?: boolean;
27
32
  }) => (task: Task, slotIndex: number) => boolean;
28
- export declare const decodePayload: ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, host, }: {
33
+ export declare const decodePayload: ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, host, processBoundary, }: {
29
34
  lockSector?: SharedBufferSource;
30
35
  payload?: {
31
36
  sab?: SharedBufferSource;
@@ -43,4 +48,8 @@ export declare const decodePayload: ({ lockSector, payload, sab, payloadConfig,
43
48
  headerSlotStrideU32?: number;
44
49
  textCompat?: LockBufferTextCompat;
45
50
  host?: true;
51
+ /**
52
+ * Reject process-local pointer payloads before reading pointer/token metadata.
53
+ */
54
+ processBoundary?: boolean;
46
55
  }) => (task: Task, slotIndex: number, specialFlags?: number) => void;
@@ -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,7 +304,7 @@ 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,
@@ -421,7 +441,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
421
441
  }
422
442
  }
423
443
  task[TaskIndex.Type] = dynamicType;
424
- if (!ensureWithinDynamicLimit(task, bytes, PayloadBuffer[dynamicType])) {
444
+ if (!ensureWithinDynamicLimit(task, bytes, payloadBufferName(dynamicType))) {
425
445
  return false;
426
446
  }
427
447
  const reservedSlot = reserveDynamicObject(task, bytes);
@@ -590,7 +610,9 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
590
610
  return true;
591
611
  };
592
612
  const tryEncodeBufferReferenceNumeric = (task, slotIndex, value) => {
593
- const words = value[BUFFER_REFERENCE_NUMERIC_TRANSFER]?.();
613
+ const words = isBufferReferenceValue(value)
614
+ ? BufferReference.prototype[BUFFER_REFERENCE_NUMERIC_TRANSFER].call(value)
615
+ : value[BUFFER_REFERENCE_NUMERIC_TRANSFER]?.();
594
616
  if (words === undefined)
595
617
  return false;
596
618
  task[TaskIndex.Type] = PayloadBuffer.BufferReference;
@@ -616,7 +638,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
616
638
  task.value = null;
617
639
  return true;
618
640
  };
619
- const encodeObjectExternalPayload = (task, slotIndex, externalPayload) => {
641
+ const encodeObjectExternalPayload = (task, slotIndex, externalPayload, trustedReservedCodec = false) => {
620
642
  const codecId = readExternalPayloadCodecId(externalPayload);
621
643
  if (codecId === undefined) {
622
644
  return encoderError({
@@ -626,21 +648,46 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
626
648
  detail: UNSUPPORTED_OBJECT_DETAIL,
627
649
  });
628
650
  }
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;
651
+ if (trustedReservedCodec) {
652
+ if (processBoundary && isProcessLocalPointerCodec(codecId)) {
653
+ return encoderError({
654
+ task,
655
+ type: ErrorKnitting.Serializable,
656
+ onPromise,
657
+ detail: PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL,
658
+ });
659
+ }
660
+ if (codecId === SHARED_ARRAY_BUFFER_CODEC_ID &&
661
+ tryEncodeSharedArrayBufferNumeric(task, slotIndex, externalPayload)) {
662
+ return true;
663
+ }
664
+ if (codecId === BUFFER_REFERENCE_CODEC_ID &&
665
+ tryEncodeBufferReferenceNumeric(task, slotIndex, externalPayload)) {
666
+ return true;
667
+ }
668
+ if (codecId === PROCESS_SHARED_BUFFER_CODEC_ID &&
669
+ tryEncodeProcessSharedBufferNumeric(task, slotIndex, externalPayload)) {
670
+ return true;
671
+ }
636
672
  }
637
- if (codecId === PROCESS_SHARED_BUFFER_CODEC_ID &&
638
- tryEncodeProcessSharedBufferNumeric(task, slotIndex, externalPayload)) {
639
- return true;
673
+ else if (isReservedExternalPayloadCodec(codecId)) {
674
+ return encoderError({
675
+ task,
676
+ type: ErrorKnitting.Serializable,
677
+ onPromise,
678
+ detail: processBoundary && isProcessLocalPointerCodec(codecId)
679
+ ? PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL
680
+ : RESERVED_EXTERNAL_PAYLOAD_DETAIL,
681
+ });
640
682
  }
641
683
  let text;
642
684
  try {
643
- text = stringifyJSON([codecId, externalPayload.toMetadata()]);
685
+ text = stringifyJSON([
686
+ codecId,
687
+ trustedReservedCodec
688
+ ? readTrustedExternalPayloadMetadata(externalPayload)
689
+ : externalPayload.toMetadata(),
690
+ ]);
644
691
  }
645
692
  catch (error) {
646
693
  const detail = error instanceof Error ? error.message : String(error);
@@ -727,10 +774,21 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
727
774
  if (body === null || typeof body !== "object")
728
775
  return undefined;
729
776
  const sharedArrayBuffer = getSharedArrayBufferPayload(body);
730
- if (sharedArrayBuffer !== undefined)
731
- return sharedArrayBuffer;
777
+ if (sharedArrayBuffer !== undefined) {
778
+ return { payload: sharedArrayBuffer, trustedReservedCodec: true };
779
+ }
780
+ if (isBufferReferenceValue(body) ||
781
+ isProcessSharedBufferValue(body)) {
782
+ return {
783
+ payload: body,
784
+ trustedReservedCodec: true,
785
+ };
786
+ }
732
787
  if (isExternalPayloadLike(body)) {
733
- return body;
788
+ return {
789
+ payload: body,
790
+ trustedReservedCodec: false,
791
+ };
734
792
  }
735
793
  return undefined;
736
794
  };
@@ -782,7 +840,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
782
840
  task.value = null;
783
841
  return true;
784
842
  };
785
- const encodeEnvelopeExternalBody = (task, slotIndex, headerText, headerIsString, externalBody) => {
843
+ const encodeEnvelopeExternalBody = (task, slotIndex, headerText, headerIsString, externalBody, trustedReservedCodec) => {
786
844
  const codecId = readExternalPayloadCodecId(externalBody);
787
845
  if (codecId === undefined) {
788
846
  return encoderError({
@@ -792,9 +850,34 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
792
850
  detail: ENVELOPE_PAYLOAD_DETAIL,
793
851
  });
794
852
  }
853
+ if (trustedReservedCodec) {
854
+ if (processBoundary && isProcessLocalPointerCodec(codecId)) {
855
+ return encoderError({
856
+ task,
857
+ type: ErrorKnitting.Serializable,
858
+ onPromise,
859
+ detail: PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL,
860
+ });
861
+ }
862
+ }
863
+ else if (isReservedExternalPayloadCodec(codecId)) {
864
+ return encoderError({
865
+ task,
866
+ type: ErrorKnitting.Serializable,
867
+ onPromise,
868
+ detail: processBoundary && isProcessLocalPointerCodec(codecId)
869
+ ? PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL
870
+ : RESERVED_EXTERNAL_PAYLOAD_DETAIL,
871
+ });
872
+ }
795
873
  let bodyText;
796
874
  try {
797
- bodyText = stringifyJSON([codecId, externalBody.toMetadata()]);
875
+ bodyText = stringifyJSON([
876
+ codecId,
877
+ trustedReservedCodec
878
+ ? readTrustedExternalPayloadMetadata(externalBody)
879
+ : externalBody.toMetadata(),
880
+ ]);
798
881
  }
799
882
  catch (error) {
800
883
  const detail = error instanceof Error ? error.message : String(error);
@@ -874,7 +957,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
874
957
  const headerText = encodeEnvelopeHeaderText(task, header, headerIsString);
875
958
  if (headerText === undefined)
876
959
  return false;
877
- return encodeEnvelopeExternalBody(task, slotIndex, headerText, headerIsString, externalBody);
960
+ return encodeEnvelopeExternalBody(task, slotIndex, headerText, headerIsString, externalBody.payload, externalBody.trustedReservedCodec);
878
961
  }
879
962
  return encoderError({
880
963
  task,
@@ -947,7 +1030,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
947
1030
  const objectValue = args;
948
1031
  const sharedArrayBufferPayload = getSharedArrayBufferPayload(objectValue);
949
1032
  if (sharedArrayBufferPayload !== undefined) {
950
- return encodeObjectExternalPayload(task, slotIndex, sharedArrayBufferPayload);
1033
+ return encodeObjectExternalPayload(task, slotIndex, sharedArrayBufferPayload, true);
951
1034
  }
952
1035
  const objectProto = objectGetPrototypeOf(objectValue);
953
1036
  if (isRuntimeUint8Array(objectValue)) {
@@ -997,6 +1080,10 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
997
1080
  task.value = null;
998
1081
  return true;
999
1082
  }
1083
+ if (isBufferReferenceValue(objectValue) ||
1084
+ isProcessSharedBufferValue(objectValue)) {
1085
+ return encodeObjectExternalPayload(task, slotIndex, objectValue, true);
1086
+ }
1000
1087
  const objectCtor = objectValue
1001
1088
  .constructor;
1002
1089
  if (isRuntimeBuffer(objectValue)) {
@@ -1126,7 +1213,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1126
1213
  };
1127
1214
  return encodeDispatch;
1128
1215
  };
1129
- export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, host, }) => {
1216
+ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headersBuffer, headerSlotStrideU32, textCompat, host, processBoundary = false, }) => {
1130
1217
  const payloadSab = payload?.sab ?? sab;
1131
1218
  const resolvedPayloadConfig = resolvePayloadBufferOptions({
1132
1219
  sab: payloadSab,
@@ -1152,7 +1239,13 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1152
1239
  // store the slot in their Task, to just get it when it comes
1153
1240
  // to the static versions of decoding
1154
1241
  return (task, slotIndex, specialFlags) => {
1155
- switch (task[TaskIndex.Type]) {
1242
+ const payloadType = task[TaskIndex.Type];
1243
+ if (processBoundary &&
1244
+ (payloadType === PayloadBuffer.SharedArrayBuffer ||
1245
+ payloadType === PayloadBuffer.BufferReference)) {
1246
+ throw new TypeError(PROCESS_BOUNDARY_POINTER_PAYLOAD_DETAIL);
1247
+ }
1248
+ switch (payloadType) {
1156
1249
  case PayloadSignal.BigInt:
1157
1250
  Uint32View[0] = task[TaskIndex.Start];
1158
1251
  Uint32View[1] = task[TaskIndex.End];
@@ -1230,7 +1323,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1230
1323
  ? rawHeader
1231
1324
  : parseJSON(rawHeader);
1232
1325
  const bodyStart = task[TaskIndex.Start];
1233
- const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]));
1326
+ const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]), processBoundary);
1234
1327
  task.value = new Envelope(header, body);
1235
1328
  freeTaskSlot(task);
1236
1329
  return;
@@ -1244,7 +1337,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1244
1337
  PayloadBuffer.EnvelopeDynamicHeaderStringExternal
1245
1338
  ? rawHeader
1246
1339
  : parseJSON(rawHeader);
1247
- const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]));
1340
+ const body = decodeExternalPayload(readDynamicUtf8(bodyStart, bodyStart + task[TaskIndex.End]), processBoundary);
1248
1341
  task.value = new Envelope(header, body);
1249
1342
  freeTaskSlot(task);
1250
1343
  return;
@@ -1324,11 +1417,11 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1324
1417
  return;
1325
1418
  }
1326
1419
  case PayloadBuffer.ExternalPayload:
1327
- task.value = decodeExternalPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]));
1420
+ task.value = decodeExternalPayload(readDynamicUtf8(task[TaskIndex.Start], task[TaskIndex.Start] + task[TaskIndex.PayloadLen]), processBoundary);
1328
1421
  freeTaskSlot(task);
1329
1422
  return;
1330
1423
  case PayloadBuffer.StaticExternalPayload:
1331
- task.value = decodeExternalPayload(readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex));
1424
+ task.value = decodeExternalPayload(readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex), processBoundary);
1332
1425
  return;
1333
1426
  case PayloadBuffer.ProcessSharedBuffer:
1334
1427
  task.value = decodeProcessSharedBufferNumericWords(readStaticU32Words(processSharedBufferWords, PROCESS_SHARED_BUFFER_NUMERIC_WORDS, slotIndex));
@@ -1375,3 +1468,6 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1375
1468
  }
1376
1469
  };
1377
1470
  };
1471
+ // Break the lock.ts <-> payloadCodec.ts cycle (see lock.ts): register the codec
1472
+ // factories on load instead of having lock.ts import them.
1473
+ registerLockPayloadCodec(encodePayload, decodePayload);
@@ -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, };