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.
- package/BROWSER.md +134 -0
- package/README.md +347 -46
- package/knitting.browser.d.ts +6 -0
- package/knitting.browser.js +1 -0
- package/knitting.d.ts +3 -2
- package/knitting.js +3 -3
- package/map.md +21 -9
- package/package.json +17 -2
- package/prebuilds/darwin-arm64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-arm64-node-127/knitting_shm.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_shm.node +0 -0
- package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
- package/prebuilds/win32-x64-node-127/knitting_buffer_pointer.node +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shm.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_buffer_pointer.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shm.node +0 -0
- 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 +311 -27
- 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 +3 -1
- package/src/common/runtime.js +25 -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 +112 -77
- package/src/memory/lock.js +314 -87
- package/src/memory/payloadCodec.d.ts +11 -2
- package/src/memory/payloadCodec.js +129 -37
- package/src/memory/regionRegistry.d.ts +1 -3
- package/src/memory/regionRegistry.js +16 -14
- 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 +59 -15
- 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/dispatcher.js +9 -0
- package/src/runtime/inline-executor.js +27 -15
- package/src/runtime/pool.d.ts +121 -5
- package/src/runtime/pool.js +263 -49
- package/src/runtime/process-worker.d.ts +3 -13
- package/src/runtime/process-worker.js +97 -101
- package/src/runtime/tx-queue.d.ts +18 -2
- package/src/runtime/tx-queue.js +56 -16
- package/src/runtime/worker-common.d.ts +13 -0
- package/src/runtime/worker-common.js +102 -0
- package/src/types.d.ts +89 -9
- package/src/worker/composable-runners.js +3 -2
- package/src/worker/loop.d.ts +1 -0
- package/src/worker/loop.js +52 -17
- package/src/worker/rx-queue.d.ts +11 -1
- package/src/worker/rx-queue.js +4 -1
- package/src/worker/task-loader.d.ts +5 -4
- package/src/worker/task-loader.js +13 -7
- package/src/worker/timers.d.ts +8 -1
- 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
|
|
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,
|
|
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
|
|
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 (
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
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
|
|
638
|
-
|
|
639
|
-
|
|
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([
|
|
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
|
|
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([
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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, };
|