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.
- package/README.md +254 -40
- package/knitting.d.ts +3 -2
- package/knitting.js +3 -3
- package/map.md +21 -9
- package/package.json +7 -1
- package/scripts/build-compiled-worker.ts +276 -0
- package/scripts/compiled-worker/porffor.d.ts +5 -0
- package/scripts/compiled-worker/runtime.ts +529 -0
- package/scripts/compiled-worker/task-shim.ts +32 -0
- package/src/api.d.ts +24 -1
- package/src/api.js +199 -20
- package/src/common/module-url.d.ts +1 -0
- package/src/common/module-url.js +30 -2
- package/src/common/node-compat.d.ts +2 -0
- package/src/common/path-canonical.js +20 -6
- package/src/common/runtime.d.ts +2 -1
- package/src/common/runtime.js +18 -4
- package/src/common/task-source.d.ts +2 -0
- package/src/common/task-source.js +17 -1
- package/src/common/worker-runtime.js +16 -7
- package/src/connections/buffer-reference-native.js +25 -1
- package/src/connections/buffer-reference.d.ts +23 -2
- package/src/connections/buffer-reference.js +143 -29
- package/src/connections/bun.d.ts +1 -1
- package/src/connections/bun.js +25 -14
- package/src/connections/deno.d.ts +3 -1
- package/src/connections/deno.js +35 -14
- package/src/connections/external-array-buffer.d.ts +2 -0
- package/src/connections/external-array-buffer.js +33 -0
- package/src/connections/node-addons.d.ts +11 -0
- package/src/connections/node-addons.js +49 -1
- package/src/connections/node-ffi-api.d.ts +19 -0
- package/src/connections/node-ffi-api.js +28 -0
- package/src/connections/node-ffi.d.ts +27 -0
- package/src/connections/node-ffi.js +262 -0
- package/src/connections/node.d.ts +1 -1
- package/src/connections/node.js +20 -20
- package/src/connections/package-assets.js +25 -15
- package/src/connections/posix.d.ts +42 -0
- package/src/connections/posix.js +101 -0
- package/src/connections/process-shared-buffer.d.ts +1 -0
- package/src/connections/process-shared-buffer.js +11 -45
- package/src/connections/types.d.ts +4 -0
- package/src/connections/windows.d.ts +19 -10
- package/src/connections/windows.js +87 -66
- package/src/error.d.ts +7 -6
- package/src/error.js +8 -7
- package/src/memory/lock.d.ts +87 -77
- package/src/memory/lock.js +103 -84
- package/src/memory/payloadCodec.d.ts +11 -2
- package/src/memory/payloadCodec.js +127 -31
- package/src/permission/compatibility.d.ts +28 -0
- package/src/permission/compatibility.js +239 -0
- package/src/permission/index.d.ts +2 -0
- package/src/permission/index.js +1 -0
- package/src/permission/protocol.d.ts +1 -0
- package/src/permission/protocol.js +54 -14
- package/src/runtime/compiled-artifact.d.ts +21 -0
- package/src/runtime/compiled-artifact.js +260 -0
- package/src/runtime/compiled-builder.d.ts +8 -0
- package/src/runtime/compiled-builder.js +42 -0
- package/src/runtime/compiled-worker.d.ts +27 -0
- package/src/runtime/compiled-worker.js +562 -0
- package/src/runtime/inline-executor.js +27 -15
- package/src/runtime/pool.d.ts +7 -2
- package/src/runtime/pool.js +74 -6
- package/src/runtime/process-worker.d.ts +1 -1
- package/src/runtime/process-worker.js +48 -8
- package/src/runtime/tx-queue.d.ts +3 -1
- package/src/runtime/tx-queue.js +4 -1
- package/src/types.d.ts +48 -6
- package/src/worker/composable-runners.js +3 -2
- package/src/worker/loop.d.ts +1 -0
- package/src/worker/loop.js +25 -12
- package/src/worker/task-loader.d.ts +5 -4
- package/src/worker/task-loader.js +13 -7
- package/src/worker/timers.js +8 -6
package/src/memory/lock.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import RingQueue from "../ipc/tools/ring-queue.js";
|
|
2
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
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
|
|
127
|
-
(function (TaskIndex) {
|
|
138
|
+
export const TaskIndex = {
|
|
128
139
|
/**
|
|
129
140
|
* Worker -> host response flags word.
|
|
130
141
|
*/
|
|
131
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
-
|
|
148
|
-
|
|
158
|
+
slotBuffer: 6,
|
|
159
|
+
Size: 8,
|
|
149
160
|
/**
|
|
150
161
|
* Total slot length in Uint32 words, including the task header.
|
|
151
162
|
*/
|
|
152
|
-
|
|
153
|
-
}
|
|
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
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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,
|
|
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
|
|
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 (
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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
|
|
638
|
-
|
|
639
|
-
|
|
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([
|
|
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
|
|
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([
|
|
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
|
-
|
|
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, };
|