circuitjson-toolkit 1.4.1 → 1.4.3
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 +7 -0
- package/docs/api.md +19 -3
- package/docs/model-format.md +4 -4
- package/docs/release-notes-v1.4.2.md +32 -0
- package/package.json +2 -1
- package/src/core/SelfAdjustingComputation.mjs +89 -100
- package/src/core/context/CircuitJsonExtensionBoundary.mjs +1 -1
- package/src/core/context/StructuredCloneAdoption.mjs +9 -25
- package/src/core/context/StructuredCloneTextAccounting.mjs +38 -0
- package/src/core/contracts/DocumentResult.mjs +9 -0
- package/src/core/worker/NativeParserWorkerTransport.mjs +201 -0
- package/src/core/worker/ParserWorkerClient.mjs +18 -20
- package/src/core/worker/WorkerBinaryData.mjs +201 -0
- package/src/core/worker/WorkerRequestData.mjs +64 -213
- package/src/core/worker/WorkerResponseData.mjs +9 -2
- package/src/core/worker/WorkerResultShape.mjs +6 -1
|
@@ -3,82 +3,21 @@ import { CircuitJsonReadOnlyDocument } from '../context/CircuitJsonReadOnlyDocum
|
|
|
3
3
|
import { ProtectedExtensionBinaryBoundary } from '../context/ProtectedExtensionBinaryBoundary.mjs'
|
|
4
4
|
import { RuntimeProxyBoundary } from '../contracts/RuntimeProxyBoundary.mjs'
|
|
5
5
|
import { WorkerResultShape } from './WorkerResultShape.mjs'
|
|
6
|
+
import { WorkerBinaryData } from './WorkerBinaryData.mjs'
|
|
7
|
+
import { NativeParserWorkerTransport } from './NativeParserWorkerTransport.mjs'
|
|
6
8
|
|
|
7
|
-
const
|
|
8
|
-
ArrayBuffer.prototype,
|
|
9
|
-
'byteLength'
|
|
10
|
-
)?.get
|
|
11
|
-
const ARRAY_BUFFER_RESIZABLE_GETTER = Object.getOwnPropertyDescriptor(
|
|
12
|
-
ArrayBuffer.prototype,
|
|
13
|
-
'resizable'
|
|
14
|
-
)?.get
|
|
15
|
-
const SHARED_ARRAY_BUFFER_BYTE_LENGTH_GETTER =
|
|
16
|
-
typeof SharedArrayBuffer === 'function'
|
|
17
|
-
? Object.getOwnPropertyDescriptor(
|
|
18
|
-
SharedArrayBuffer.prototype,
|
|
19
|
-
'byteLength'
|
|
20
|
-
)?.get
|
|
21
|
-
: null
|
|
22
|
-
const TYPED_ARRAY_PROTOTYPE = Object.getPrototypeOf(Uint8Array.prototype)
|
|
23
|
-
const TYPED_ARRAY_BUFFER_GETTER = Object.getOwnPropertyDescriptor(
|
|
24
|
-
TYPED_ARRAY_PROTOTYPE,
|
|
25
|
-
'buffer'
|
|
26
|
-
)?.get
|
|
27
|
-
const TYPED_ARRAY_BYTE_LENGTH_GETTER = Object.getOwnPropertyDescriptor(
|
|
28
|
-
TYPED_ARRAY_PROTOTYPE,
|
|
29
|
-
'byteLength'
|
|
30
|
-
)?.get
|
|
31
|
-
const TYPED_ARRAY_BYTE_OFFSET_GETTER = Object.getOwnPropertyDescriptor(
|
|
32
|
-
TYPED_ARRAY_PROTOTYPE,
|
|
33
|
-
'byteOffset'
|
|
34
|
-
)?.get
|
|
35
|
-
const TYPED_ARRAY_TAG_GETTER = Object.getOwnPropertyDescriptor(
|
|
36
|
-
TYPED_ARRAY_PROTOTYPE,
|
|
37
|
-
Symbol.toStringTag
|
|
38
|
-
)?.get
|
|
39
|
-
const DATA_VIEW_BUFFER_GETTER = Object.getOwnPropertyDescriptor(
|
|
40
|
-
DataView.prototype,
|
|
41
|
-
'buffer'
|
|
42
|
-
)?.get
|
|
43
|
-
const DATA_VIEW_BYTE_LENGTH_GETTER = Object.getOwnPropertyDescriptor(
|
|
44
|
-
DataView.prototype,
|
|
45
|
-
'byteLength'
|
|
46
|
-
)?.get
|
|
47
|
-
const DATA_VIEW_BYTE_OFFSET_GETTER = Object.getOwnPropertyDescriptor(
|
|
48
|
-
DataView.prototype,
|
|
49
|
-
'byteOffset'
|
|
50
|
-
)?.get
|
|
51
|
-
const DATA_VIEW_CONSTRUCTOR = DataView
|
|
52
|
-
const UINT8_ARRAY_CONSTRUCTOR = Uint8Array
|
|
53
|
-
const UINT8_ARRAY_SET = Uint8Array.prototype.set
|
|
9
|
+
const ARRAY_IS_ARRAY = Array.isArray
|
|
54
10
|
const STRUCTURED_CLONE = globalThis.structuredClone
|
|
55
11
|
const MAX_REQUEST_BYTES = 100_000_000
|
|
56
12
|
const MAX_REQUEST_DEPTH = 256
|
|
57
13
|
const MAX_REQUEST_VALUES = 100_000
|
|
58
14
|
const MAX_RESULT_BYTES = 250_000_000
|
|
59
15
|
const MAX_RESULT_VALUES = 2_000_000
|
|
60
|
-
const MAX_DOCUMENT_RESULT_VALUES =
|
|
16
|
+
const MAX_DOCUMENT_RESULT_VALUES = 10_000_000
|
|
61
17
|
// Project documents retain the single-result ceiling independently while this
|
|
62
18
|
// aggregate ceiling bounds total worker traversal regardless of document count.
|
|
63
|
-
const MAX_PROJECT_RESULT_VALUES =
|
|
19
|
+
const MAX_PROJECT_RESULT_VALUES = 16_000_000
|
|
64
20
|
const MAX_PROJECT_RESULT_BYTES = 256 * 1024 * 1024
|
|
65
|
-
const TYPED_ARRAYS = new Map(
|
|
66
|
-
[
|
|
67
|
-
Int8Array,
|
|
68
|
-
Uint8Array,
|
|
69
|
-
Uint8ClampedArray,
|
|
70
|
-
Int16Array,
|
|
71
|
-
Uint16Array,
|
|
72
|
-
Int32Array,
|
|
73
|
-
Uint32Array,
|
|
74
|
-
Float32Array,
|
|
75
|
-
Float64Array,
|
|
76
|
-
typeof BigInt64Array === 'function' ? BigInt64Array : null,
|
|
77
|
-
typeof BigUint64Array === 'function' ? BigUint64Array : null
|
|
78
|
-
]
|
|
79
|
-
.filter(Boolean)
|
|
80
|
-
.map((Constructor) => [Constructor.name, Constructor])
|
|
81
|
-
)
|
|
82
21
|
|
|
83
22
|
/**
|
|
84
23
|
* Validates and prepares bounded structured-clone worker request data.
|
|
@@ -151,10 +90,12 @@ export class WorkerRequestData {
|
|
|
151
90
|
* @returns {unknown} Owned clone-safe result data.
|
|
152
91
|
*/
|
|
153
92
|
static prepareResponse(value) {
|
|
154
|
-
|
|
93
|
+
const received = NativeParserWorkerTransport.consumeResult(value)
|
|
94
|
+
return WorkerRequestData.#prepare(received ? received.value : value, {
|
|
155
95
|
bytes: MAX_RESULT_BYTES,
|
|
156
|
-
copyBinary:
|
|
96
|
+
copyBinary: !received,
|
|
157
97
|
output: true,
|
|
98
|
+
standardBuiltins: Boolean(received),
|
|
158
99
|
strictDescriptors: true,
|
|
159
100
|
transferInput: false,
|
|
160
101
|
trustProof: false,
|
|
@@ -165,7 +106,7 @@ export class WorkerRequestData {
|
|
|
165
106
|
/**
|
|
166
107
|
* Runs the shared bounded structured-data preparation pass.
|
|
167
108
|
* @param {unknown} value Data candidate.
|
|
168
|
-
* @param {{ bytes: number, copyBinary: boolean, output: boolean, strictDescriptors: boolean, transferInput: boolean, trustProof: boolean, values: number }} limits Preparation limits.
|
|
109
|
+
* @param {{ bytes: number, copyBinary: boolean, output: boolean, standardBuiltins?: boolean, strictDescriptors: boolean, transferInput: boolean, trustProof: boolean, values: number }} limits Preparation limits.
|
|
169
110
|
* @returns {{ value: unknown, transfer: Transferable[] }} Prepared data.
|
|
170
111
|
*/
|
|
171
112
|
static #prepare(value, limits) {
|
|
@@ -187,6 +128,7 @@ export class WorkerRequestData {
|
|
|
187
128
|
projectResult: false,
|
|
188
129
|
reaccountValues: 0,
|
|
189
130
|
strictDescriptors: limits.strictDescriptors,
|
|
131
|
+
standardBuiltins: limits.standardBuiltins === true,
|
|
190
132
|
transfer: { items: [], seen: new Set() },
|
|
191
133
|
transferInput: limits.transferInput,
|
|
192
134
|
trustProof: limits.trustProof,
|
|
@@ -265,14 +207,36 @@ export class WorkerRequestData {
|
|
|
265
207
|
WorkerRequestData.#accountedScope(state)?.add(value)
|
|
266
208
|
state.visiting.add(value)
|
|
267
209
|
try {
|
|
268
|
-
|
|
210
|
+
// Array and view brands are disjoint from raw buffers, including
|
|
211
|
+
// cross-realm values and values with replaced prototypes.
|
|
212
|
+
if (ARRAY_IS_ARRAY(value)) {
|
|
213
|
+
return WorkerRequestData.#prepareContainer(value, state, depth)
|
|
214
|
+
}
|
|
215
|
+
if (
|
|
216
|
+
state.standardBuiltins &&
|
|
217
|
+
WorkerBinaryData.isStandardRecord(value)
|
|
218
|
+
) {
|
|
219
|
+
return WorkerRequestData.#prepareContainer(value, state, depth)
|
|
220
|
+
}
|
|
221
|
+
const view = WorkerBinaryData.view(value)
|
|
222
|
+
if (view) {
|
|
223
|
+
WorkerRequestData.#reserveBytes(state, view.byteLength)
|
|
224
|
+
const prepared = WorkerRequestData.#prepareView(
|
|
225
|
+
value,
|
|
226
|
+
view,
|
|
227
|
+
state
|
|
228
|
+
)
|
|
229
|
+
state.prepared.set(value, prepared)
|
|
230
|
+
return prepared
|
|
231
|
+
}
|
|
232
|
+
const bufferLength = WorkerBinaryData.bufferLength(value)
|
|
269
233
|
if (bufferLength !== null) {
|
|
270
234
|
WorkerRequestData.#reserveBytes(state, bufferLength)
|
|
271
235
|
const isolate =
|
|
272
236
|
state.copyBinary ||
|
|
273
|
-
|
|
237
|
+
WorkerBinaryData.isResizableBuffer(value)
|
|
274
238
|
const prepared = isolate
|
|
275
|
-
?
|
|
239
|
+
? WorkerBinaryData.copyBuffer(value, 0, bufferLength)
|
|
276
240
|
: value
|
|
277
241
|
state.prepared.set(value, prepared)
|
|
278
242
|
if (state.transferInput) {
|
|
@@ -280,10 +244,10 @@ export class WorkerRequestData {
|
|
|
280
244
|
}
|
|
281
245
|
return prepared
|
|
282
246
|
}
|
|
283
|
-
const sharedLength =
|
|
247
|
+
const sharedLength = WorkerBinaryData.sharedBufferLength(value)
|
|
284
248
|
if (sharedLength !== null) {
|
|
285
249
|
WorkerRequestData.#reserveBytes(state, sharedLength)
|
|
286
|
-
const copied =
|
|
250
|
+
const copied = WorkerBinaryData.copyBuffer(
|
|
287
251
|
value,
|
|
288
252
|
0,
|
|
289
253
|
sharedLength
|
|
@@ -294,17 +258,6 @@ export class WorkerRequestData {
|
|
|
294
258
|
}
|
|
295
259
|
return copied
|
|
296
260
|
}
|
|
297
|
-
const view = WorkerRequestData.#view(value)
|
|
298
|
-
if (view) {
|
|
299
|
-
WorkerRequestData.#reserveBytes(state, view.byteLength)
|
|
300
|
-
const prepared = WorkerRequestData.#prepareView(
|
|
301
|
-
value,
|
|
302
|
-
view,
|
|
303
|
-
state
|
|
304
|
-
)
|
|
305
|
-
state.prepared.set(value, prepared)
|
|
306
|
-
return prepared
|
|
307
|
-
}
|
|
308
261
|
return WorkerRequestData.#prepareContainer(value, state, depth)
|
|
309
262
|
} finally {
|
|
310
263
|
state.visiting.delete(value)
|
|
@@ -598,21 +551,32 @@ export class WorkerRequestData {
|
|
|
598
551
|
if (accounted.has(value)) return
|
|
599
552
|
accounted.add(value)
|
|
600
553
|
WorkerRequestData.#reserveAccountedValues(state, 1)
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
WorkerRequestData.#reserveAccountedBytes(state, bufferLength)
|
|
554
|
+
if (ARRAY_IS_ARRAY(value)) {
|
|
555
|
+
WorkerRequestData.#accountPreparedContainer(value, state, depth)
|
|
604
556
|
return
|
|
605
557
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
558
|
+
if (
|
|
559
|
+
state.standardBuiltins &&
|
|
560
|
+
WorkerBinaryData.isStandardRecord(value)
|
|
561
|
+
) {
|
|
562
|
+
WorkerRequestData.#accountPreparedContainer(value, state, depth)
|
|
609
563
|
return
|
|
610
564
|
}
|
|
611
|
-
const view =
|
|
565
|
+
const view = WorkerBinaryData.view(value)
|
|
612
566
|
if (view) {
|
|
613
567
|
WorkerRequestData.#reserveAccountedBytes(state, view.byteLength)
|
|
614
568
|
return
|
|
615
569
|
}
|
|
570
|
+
const bufferLength = WorkerBinaryData.bufferLength(value)
|
|
571
|
+
if (bufferLength !== null) {
|
|
572
|
+
WorkerRequestData.#reserveAccountedBytes(state, bufferLength)
|
|
573
|
+
return
|
|
574
|
+
}
|
|
575
|
+
const sharedLength = WorkerBinaryData.sharedBufferLength(value)
|
|
576
|
+
if (sharedLength !== null) {
|
|
577
|
+
WorkerRequestData.#reserveAccountedBytes(state, sharedLength)
|
|
578
|
+
return
|
|
579
|
+
}
|
|
616
580
|
WorkerRequestData.#accountPreparedContainer(value, state, depth)
|
|
617
581
|
}
|
|
618
582
|
|
|
@@ -708,8 +672,11 @@ export class WorkerRequestData {
|
|
|
708
672
|
* @returns {ArrayBufferView} Prepared exact view.
|
|
709
673
|
*/
|
|
710
674
|
static #prepareView(original, view, state) {
|
|
711
|
-
const backingLength =
|
|
712
|
-
const sharedLength =
|
|
675
|
+
const backingLength = WorkerBinaryData.bufferLength(view.buffer)
|
|
676
|
+
const sharedLength =
|
|
677
|
+
backingLength === null
|
|
678
|
+
? WorkerBinaryData.sharedBufferLength(view.buffer)
|
|
679
|
+
: null
|
|
713
680
|
const exactArrayBuffer =
|
|
714
681
|
backingLength !== null &&
|
|
715
682
|
view.byteOffset === 0 &&
|
|
@@ -717,7 +684,7 @@ export class WorkerRequestData {
|
|
|
717
684
|
if (
|
|
718
685
|
exactArrayBuffer &&
|
|
719
686
|
!state.copyBinary &&
|
|
720
|
-
!
|
|
687
|
+
!WorkerBinaryData.isResizableBuffer(view.buffer)
|
|
721
688
|
) {
|
|
722
689
|
if (state.transferInput) {
|
|
723
690
|
WorkerRequestData.#addTransfer(state.transfer, view.buffer)
|
|
@@ -727,134 +694,18 @@ export class WorkerRequestData {
|
|
|
727
694
|
if (backingLength === null && sharedLength === null) {
|
|
728
695
|
throw new TypeError('Worker request binary view is invalid.')
|
|
729
696
|
}
|
|
730
|
-
const copy =
|
|
697
|
+
const copy = WorkerBinaryData.copyBuffer(
|
|
731
698
|
view.buffer,
|
|
732
699
|
view.byteOffset,
|
|
733
700
|
view.byteLength
|
|
734
701
|
)
|
|
735
|
-
const prepared =
|
|
702
|
+
const prepared = WorkerBinaryData.recreateView(view, copy)
|
|
736
703
|
if (state.transferInput) {
|
|
737
704
|
WorkerRequestData.#addTransfer(state.transfer, copy)
|
|
738
705
|
}
|
|
739
706
|
return prepared
|
|
740
707
|
}
|
|
741
708
|
|
|
742
|
-
/**
|
|
743
|
-
* Recreates one intrinsic view on an exact isolated backing buffer.
|
|
744
|
-
* @param {{ byteLength: number, tag: string }} view View fields.
|
|
745
|
-
* @param {ArrayBuffer} buffer Exact copied buffer.
|
|
746
|
-
* @returns {ArrayBufferView} Recreated view.
|
|
747
|
-
*/
|
|
748
|
-
static #recreateView(view, buffer) {
|
|
749
|
-
if (view.tag === 'DataView') return new DATA_VIEW_CONSTRUCTOR(buffer)
|
|
750
|
-
const Constructor = TYPED_ARRAYS.get(view.tag)
|
|
751
|
-
if (!Constructor || view.byteLength % Constructor.BYTES_PER_ELEMENT) {
|
|
752
|
-
throw new TypeError('Worker request binary view is unsupported.')
|
|
753
|
-
}
|
|
754
|
-
return new Constructor(
|
|
755
|
-
buffer,
|
|
756
|
-
0,
|
|
757
|
-
view.byteLength / Constructor.BYTES_PER_ELEMENT
|
|
758
|
-
)
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
/**
|
|
762
|
-
* Reads genuine typed-array or DataView internal slots.
|
|
763
|
-
* @param {unknown} value View candidate.
|
|
764
|
-
* @returns {{ buffer: ArrayBufferLike, byteOffset: number, byteLength: number, tag: string } | null} Intrinsic view fields.
|
|
765
|
-
*/
|
|
766
|
-
static #view(value) {
|
|
767
|
-
try {
|
|
768
|
-
if (
|
|
769
|
-
DATA_VIEW_BUFFER_GETTER &&
|
|
770
|
-
DATA_VIEW_BYTE_OFFSET_GETTER &&
|
|
771
|
-
DATA_VIEW_BYTE_LENGTH_GETTER
|
|
772
|
-
) {
|
|
773
|
-
const buffer = DATA_VIEW_BUFFER_GETTER.call(value)
|
|
774
|
-
return {
|
|
775
|
-
buffer,
|
|
776
|
-
byteOffset: DATA_VIEW_BYTE_OFFSET_GETTER.call(value),
|
|
777
|
-
byteLength: DATA_VIEW_BYTE_LENGTH_GETTER.call(value),
|
|
778
|
-
tag: 'DataView'
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
} catch {
|
|
782
|
-
// The typed-array intrinsic check below is independent.
|
|
783
|
-
}
|
|
784
|
-
try {
|
|
785
|
-
const tag = TYPED_ARRAY_TAG_GETTER?.call(value)
|
|
786
|
-
if (!TYPED_ARRAYS.has(tag)) return null
|
|
787
|
-
return {
|
|
788
|
-
buffer: TYPED_ARRAY_BUFFER_GETTER.call(value),
|
|
789
|
-
byteOffset: TYPED_ARRAY_BYTE_OFFSET_GETTER.call(value),
|
|
790
|
-
byteLength: TYPED_ARRAY_BYTE_LENGTH_GETTER.call(value),
|
|
791
|
-
tag
|
|
792
|
-
}
|
|
793
|
-
} catch {
|
|
794
|
-
return null
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
/** @param {unknown} value Candidate. @returns {number | null} Byte length. */
|
|
799
|
-
static #bufferLength(value) {
|
|
800
|
-
if (!ARRAY_BUFFER_BYTE_LENGTH_GETTER) return null
|
|
801
|
-
try {
|
|
802
|
-
return ARRAY_BUFFER_BYTE_LENGTH_GETTER.call(value)
|
|
803
|
-
} catch {
|
|
804
|
-
return null
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
/** @param {unknown} value Candidate. @returns {number | null} Byte length. */
|
|
809
|
-
static #sharedBufferLength(value) {
|
|
810
|
-
if (!SHARED_ARRAY_BUFFER_BYTE_LENGTH_GETTER) return null
|
|
811
|
-
try {
|
|
812
|
-
return SHARED_ARRAY_BUFFER_BYTE_LENGTH_GETTER.call(value)
|
|
813
|
-
} catch {
|
|
814
|
-
return null
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
/**
|
|
819
|
-
* Returns whether a genuine ArrayBuffer can change size after accounting.
|
|
820
|
-
* @param {unknown} value Buffer candidate.
|
|
821
|
-
* @returns {boolean} Whether the buffer is resizable.
|
|
822
|
-
*/
|
|
823
|
-
static #isResizableBuffer(value) {
|
|
824
|
-
if (!ARRAY_BUFFER_RESIZABLE_GETTER) return false
|
|
825
|
-
try {
|
|
826
|
-
return ARRAY_BUFFER_RESIZABLE_GETTER.call(value) === true
|
|
827
|
-
} catch {
|
|
828
|
-
return false
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
/**
|
|
833
|
-
* Copies one already-accounted intrinsic byte range into a fixed buffer.
|
|
834
|
-
* The explicit range prevents a growable backing store from widening the
|
|
835
|
-
* copy between its limit check and snapshot.
|
|
836
|
-
* @param {ArrayBufferLike} buffer Source buffer.
|
|
837
|
-
* @param {number} byteOffset Captured byte offset.
|
|
838
|
-
* @param {number} byteLength Captured byte length.
|
|
839
|
-
* @returns {ArrayBuffer} Fixed owned snapshot.
|
|
840
|
-
*/
|
|
841
|
-
static #copyBuffer(buffer, byteOffset, byteLength) {
|
|
842
|
-
try {
|
|
843
|
-
const source = new UINT8_ARRAY_CONSTRUCTOR(
|
|
844
|
-
buffer,
|
|
845
|
-
byteOffset,
|
|
846
|
-
byteLength
|
|
847
|
-
)
|
|
848
|
-
const copy = new UINT8_ARRAY_CONSTRUCTOR(byteLength)
|
|
849
|
-
UINT8_ARRAY_SET.call(copy, source)
|
|
850
|
-
return TYPED_ARRAY_BUFFER_GETTER.call(copy)
|
|
851
|
-
} catch {
|
|
852
|
-
throw new TypeError(
|
|
853
|
-
'Worker request binary data changed while it was copied.'
|
|
854
|
-
)
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
|
|
858
709
|
/** @param {PropertyDescriptor | undefined} descriptor Descriptor. @returns {unknown} Data value. */
|
|
859
710
|
static #dataValue(descriptor) {
|
|
860
711
|
return descriptor && Object.hasOwn(descriptor, 'value')
|
|
@@ -5,6 +5,9 @@ import { CircuitJsonValidationProof } from '../context/CircuitJsonValidationProo
|
|
|
5
5
|
import { TOOLKIT_WORKER_PROTOCOL } from './ToolkitWorkerProtocol.mjs'
|
|
6
6
|
import { WorkerRequestData } from './WorkerRequestData.mjs'
|
|
7
7
|
|
|
8
|
+
const PREPARE_RESPONSE = WorkerRequestData.prepareResponse
|
|
9
|
+
const VALIDATE_AND_ATTACH = CircuitJsonValidationProof.validateAndAttach
|
|
10
|
+
|
|
8
11
|
const ARRAY_BUFFER_BYTE_LENGTH_GETTER = Object.getOwnPropertyDescriptor(
|
|
9
12
|
ArrayBuffer.prototype,
|
|
10
13
|
'byteLength'
|
|
@@ -154,7 +157,7 @@ export class WorkerResponseData {
|
|
|
154
157
|
* @returns {object} Original validated result.
|
|
155
158
|
*/
|
|
156
159
|
static result(operation, value) {
|
|
157
|
-
const prepared =
|
|
160
|
+
const prepared = PREPARE_RESPONSE(value)
|
|
158
161
|
const fields = WorkerResponseData.#record(
|
|
159
162
|
prepared,
|
|
160
163
|
'Toolkit worker result'
|
|
@@ -330,7 +333,11 @@ export class WorkerResponseData {
|
|
|
330
333
|
'statistics'
|
|
331
334
|
])
|
|
332
335
|
try {
|
|
333
|
-
|
|
336
|
+
// Preparation allocated every container and normalized every binary
|
|
337
|
+
// built-in without exposing the owned graph to caller callbacks.
|
|
338
|
+
VALIDATE_AND_ATTACH(document, {
|
|
339
|
+
standardBuiltins: true
|
|
340
|
+
})
|
|
334
341
|
} catch (error) {
|
|
335
342
|
throw WorkerResponseData.#error(
|
|
336
343
|
'Toolkit worker document model is invalid.',
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { RuntimeProxyBoundary } from '../contracts/RuntimeProxyBoundary.mjs'
|
|
2
2
|
|
|
3
|
+
const ARRAY_IS_ARRAY = Array.isArray
|
|
4
|
+
|
|
3
5
|
const DOCUMENT_FIELDS = Object.freeze([
|
|
4
6
|
'schema',
|
|
5
7
|
'id',
|
|
@@ -64,7 +66,10 @@ export class WorkerResultShape {
|
|
|
64
66
|
)
|
|
65
67
|
: keys
|
|
66
68
|
if (
|
|
67
|
-
|
|
69
|
+
// A foreign intrinsic Array.prototype has the same array brand.
|
|
70
|
+
// Only the receiver's own descriptors are used below; inherited
|
|
71
|
+
// properties are never read or copied into the normalized array.
|
|
72
|
+
(prototype !== Array.prototype && !ARRAY_IS_ARRAY(prototype)) ||
|
|
68
73
|
!Number.isSafeInteger(length) ||
|
|
69
74
|
length < 0 ||
|
|
70
75
|
(output && !strictDescriptors
|