circuitjson-toolkit 1.0.17 → 1.1.0
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/NOTICE.md +31 -0
- package/README.md +267 -107
- package/docs/api.md +501 -96
- package/docs/capabilities.md +70 -0
- package/docs/migration/behaviors.md +45 -0
- package/docs/migration/parser.md +60 -0
- package/docs/migration/renderers.md +515 -0
- package/docs/migration/root.md +740 -0
- package/docs/migration.md +120 -0
- package/docs/model-format.md +175 -57
- package/docs/provenance.md +206 -0
- package/docs/release-notes-v1.1.0.md +154 -0
- package/docs/testing.md +117 -7
- package/package.json +31 -5
- package/spec/api-baseline-v1.0.17.json +1 -0
- package/spec/baseline-provenance-v1.0.17.json +7 -0
- package/spec/circuitjson-schema-snapshot.json +321 -0
- package/spec/circuitjson-schema-source.json +28 -0
- package/spec/feature-preservation.json +1 -0
- package/spec/library-scope.md +27 -20
- package/src/capabilities.mjs +1 -0
- package/src/core/ArchiveEntryPath.mjs +93 -0
- package/src/core/ArchiveLimits.mjs +31 -0
- package/src/core/ArchiveLimitsValidator.mjs +107 -0
- package/src/core/AsyncInputOwnership.mjs +56 -0
- package/src/core/AttachedValueLimits.mjs +67 -0
- package/src/core/CircuitJsonDiagnosticIndexer.mjs +184 -0
- package/src/core/CircuitJsonDocument.mjs +19 -61
- package/src/core/CircuitJsonElementTypes.mjs +10 -0
- package/src/core/CircuitJsonElementValidator.mjs +98 -847
- package/src/core/CircuitJsonIndexer.mjs +274 -194
- package/src/core/CircuitJsonManufacturingBuilder.mjs +167 -164
- package/src/core/CircuitJsonParser.mjs +75 -13
- package/src/core/CircuitJsonPcbClearanceDiagnostics.mjs +12 -6
- package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +108 -10
- package/src/core/CircuitJsonPcbPadPrimitiveModel.mjs +1 -1
- package/src/core/CircuitJsonPcbPrimitiveArtwork.mjs +44 -38
- package/src/core/CircuitJsonPcbPrimitiveBuilder.mjs +146 -28
- package/src/core/CircuitJsonPcbPrimitiveFields.mjs +70 -4
- package/src/core/CircuitJsonPcbPrimitiveIndex.mjs +18 -2
- package/src/core/CircuitJsonPcbPrimitiveOverlays.mjs +26 -9
- package/src/core/CircuitJsonPcbZonePrimitiveBuilder.mjs +7 -6
- package/src/core/CircuitJsonSerializedInputAudit.mjs +87 -0
- package/src/core/CircuitJsonSourceMetadata.mjs +5 -1
- package/src/core/CircuitJsonSupportMatrixBuilder.mjs +3 -1
- package/src/core/CircuitJsonToolkitElementSchema.mjs +218 -0
- package/src/core/CircuitJsonUnitParsers.mjs +101 -0
- package/src/core/CircuitJsonUnits.mjs +13 -87
- package/src/core/CircuitJsonUpstreamSchema.mjs +9 -0
- package/src/core/CircuitJsonUpstreamValidator.mjs +418 -0
- package/src/core/CircuitJsonValidationUnits.mjs +6 -0
- package/src/core/ManufacturingService.mjs +323 -0
- package/src/core/Parser.mjs +343 -0
- package/src/core/ParserOptions.mjs +333 -0
- package/src/core/PcbBoundsSelectionModel.mjs +55 -19
- package/src/core/PcbDiagnosticFocusModel.mjs +42 -11
- package/src/core/PcbInteractionIndex.mjs +368 -0
- package/src/core/PcbInteractionPrimitiveModel.mjs +393 -62
- package/src/core/ProjectAsyncInputOwner.mjs +70 -0
- package/src/core/ProjectLoader.mjs +975 -0
- package/src/core/SimulationService.mjs +790 -0
- package/src/core/ToolkitCapabilities.mjs +130 -0
- package/src/core/ZipArchiveInspector.mjs +649 -0
- package/src/core/context/BinaryDataSnapshot.mjs +217 -0
- package/src/core/context/CircuitJsonContextIndexes.mjs +96 -0
- package/src/core/context/CircuitJsonDerivedCache.mjs +114 -0
- package/src/core/context/CircuitJsonDocumentContext.mjs +353 -0
- package/src/core/context/CircuitJsonLegacyModel.mjs +147 -0
- package/src/core/context/CircuitJsonLegacyNormalizer.mjs +847 -0
- package/src/core/context/CircuitJsonMetadataBoundary.mjs +76 -0
- package/src/core/context/CircuitJsonModelFreezeTraversal.mjs +179 -0
- package/src/core/context/CircuitJsonReadOnlyDocument.mjs +920 -0
- package/src/core/context/CircuitJsonSchematicTableNormalizer.mjs +314 -0
- package/src/core/context/CircuitJsonValidationAuthority.mjs +39 -0
- package/src/core/context/CircuitJsonValidationProof.mjs +217 -0
- package/src/core/context/PcbPrimitivePreparation.mjs +198 -0
- package/src/core/context/PcbSpatialIndex.mjs +701 -0
- package/src/core/context/ProtectedExtensionBinaryBoundary.mjs +128 -0
- package/src/core/context/StructuredDataSnapshot.mjs +683 -0
- package/src/core/contracts/DocumentResult.mjs +198 -0
- package/src/core/contracts/ProjectResult.mjs +96 -0
- package/src/core/contracts/RuntimeProxyBoundary.mjs +48 -0
- package/src/core/contracts/ToolkitAsset.mjs +493 -0
- package/src/core/contracts/ToolkitDiagnostic.mjs +38 -0
- package/src/core/contracts/ToolkitError.mjs +176 -0
- package/src/core/contracts/ToolkitProgress.mjs +89 -0
- package/src/core/interaction/CanonicalInteractionOptions.mjs +246 -0
- package/src/core/interaction/PcbInteractionBounds.mjs +167 -0
- package/src/core/query/CircuitTraversal.mjs +343 -0
- package/src/core/query/ComponentGrouping.mjs +275 -0
- package/src/core/query/QueryNetlistBuilder.mjs +306 -0
- package/src/core/query/QueryService.mjs +435 -0
- package/src/core/query/RegexPattern.mjs +75 -0
- package/src/core/rendering/CanonicalBomOrder.mjs +81 -0
- package/src/core/rendering/CanonicalBomRows.mjs +92 -0
- package/src/core/rendering/CanonicalRenderOptions.mjs +498 -0
- package/src/core/rendering/CanonicalSvgDocument.mjs +102 -0
- package/src/core/rendering/PcbRenderPlan.mjs +429 -0
- package/src/core/rendering/SchematicSheetSelector.mjs +335 -0
- package/src/core/scene3d/PcbScene3dBuilder.mjs +906 -0
- package/src/core/scene3d/PcbScene3dPreparator.mjs +47 -0
- package/src/core/scene3d/Scene3dAssetIndex.mjs +284 -0
- package/src/core/scene3d/Scene3dBoardModel.mjs +596 -0
- package/src/core/scene3d/Scene3dDocumentMetadata.mjs +167 -0
- package/src/core/scene3d/Scene3dFreeze.mjs +37 -0
- package/src/core/scene3d/Scene3dIdRegistry.mjs +34 -0
- package/src/core/scene3d/Scene3dInputPreflight.mjs +193 -0
- package/src/core/scene3d/Scene3dMaterials.mjs +58 -0
- package/src/core/scene3d/Scene3dModelReference.mjs +134 -0
- package/src/core/scene3d/Scene3dOptions.mjs +363 -0
- package/src/core/scene3d/SceneAssetResolver.mjs +441 -0
- package/src/core/simulation/SimulationParameterCloner.mjs +543 -0
- package/src/core/worker/ParserWorkerClient.mjs +997 -0
- package/src/core/worker/ToolkitWorkerProtocol.mjs +412 -0
- package/src/core/worker/WorkerRequestData.mjs +623 -0
- package/src/core/worker/WorkerResponseData.mjs +905 -0
- package/src/extensions.mjs +37 -0
- package/src/index.mjs +14 -9
- package/src/interaction.mjs +2 -0
- package/src/manufacturing.mjs +1 -0
- package/src/parser.mjs +12 -2
- package/src/project.mjs +5 -0
- package/src/query.mjs +1 -0
- package/src/renderers.mjs +3 -29
- package/src/scene3d.mjs +3 -0
- package/src/simulation.mjs +1 -0
- package/src/styles/renderers.css +24 -0
- package/src/testing/ToolkitContractFixtures.mjs +124 -0
- package/src/testing/ToolkitLoopbackWorker.mjs +174 -0
- package/src/testing/runToolkitContract.mjs +705 -0
- package/src/testing.mjs +3 -0
- package/src/ui/BomTableRenderer.mjs +304 -0
- package/src/ui/CircuitJsonPcbBoardSvgRenderer.mjs +80 -0
- package/src/ui/CircuitJsonPcbPrimitiveAttributeRenderer.mjs +3 -5
- package/src/ui/CircuitJsonPcbSvgRenderer.mjs +63 -43
- package/src/ui/CircuitJsonPcbViaSvgRenderer.mjs +3 -5
- package/src/ui/CircuitJsonSchematicDebugRenderer.mjs +164 -0
- package/src/ui/CircuitJsonSchematicImageSvgRenderer.mjs +210 -0
- package/src/ui/CircuitJsonSchematicLineRenderer.mjs +86 -0
- package/src/ui/CircuitJsonSchematicSheetSymbolSvgRenderer.mjs +98 -0
- package/src/ui/CircuitJsonSchematicSvgArcPath.mjs +117 -17
- package/src/ui/CircuitJsonSchematicSvgPortMetadata.mjs +67 -20
- package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +45 -9
- package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +151 -148
- package/src/ui/CircuitJsonSchematicTableSvgRenderer.mjs +4 -292
- package/src/ui/PcbSvgRenderer.mjs +41 -0
- package/src/ui/SafeSvgPaint.mjs +26 -0
- package/src/ui/SafeXmlText.mjs +60 -0
- package/src/ui/SchematicGeometryBounds.mjs +540 -0
- package/src/ui/SchematicSvgRenderer.mjs +110 -0
- package/src/ui/SchematicTableGeometry.mjs +319 -0
- package/src/ui/SchematicTextAnchor.mjs +55 -0
- package/src/ui/SchematicTextBounds.mjs +98 -0
- package/src/workers/parser.worker.mjs +59 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
import { CircuitJsonValidationProof } from '../context/CircuitJsonValidationProof.mjs'
|
|
2
|
+
import { CircuitJsonReadOnlyDocument } from '../context/CircuitJsonReadOnlyDocument.mjs'
|
|
3
|
+
import { ProtectedExtensionBinaryBoundary } from '../context/ProtectedExtensionBinaryBoundary.mjs'
|
|
4
|
+
import { RuntimeProxyBoundary } from '../contracts/RuntimeProxyBoundary.mjs'
|
|
5
|
+
|
|
6
|
+
const ARRAY_BUFFER_BYTE_LENGTH_GETTER = Object.getOwnPropertyDescriptor(
|
|
7
|
+
ArrayBuffer.prototype,
|
|
8
|
+
'byteLength'
|
|
9
|
+
)?.get
|
|
10
|
+
const ARRAY_BUFFER_RESIZABLE_GETTER = Object.getOwnPropertyDescriptor(
|
|
11
|
+
ArrayBuffer.prototype,
|
|
12
|
+
'resizable'
|
|
13
|
+
)?.get
|
|
14
|
+
const SHARED_ARRAY_BUFFER_BYTE_LENGTH_GETTER =
|
|
15
|
+
typeof SharedArrayBuffer === 'function'
|
|
16
|
+
? Object.getOwnPropertyDescriptor(
|
|
17
|
+
SharedArrayBuffer.prototype,
|
|
18
|
+
'byteLength'
|
|
19
|
+
)?.get
|
|
20
|
+
: null
|
|
21
|
+
const TYPED_ARRAY_PROTOTYPE = Object.getPrototypeOf(Uint8Array.prototype)
|
|
22
|
+
const TYPED_ARRAY_BUFFER_GETTER = Object.getOwnPropertyDescriptor(
|
|
23
|
+
TYPED_ARRAY_PROTOTYPE,
|
|
24
|
+
'buffer'
|
|
25
|
+
)?.get
|
|
26
|
+
const TYPED_ARRAY_BYTE_LENGTH_GETTER = Object.getOwnPropertyDescriptor(
|
|
27
|
+
TYPED_ARRAY_PROTOTYPE,
|
|
28
|
+
'byteLength'
|
|
29
|
+
)?.get
|
|
30
|
+
const TYPED_ARRAY_BYTE_OFFSET_GETTER = Object.getOwnPropertyDescriptor(
|
|
31
|
+
TYPED_ARRAY_PROTOTYPE,
|
|
32
|
+
'byteOffset'
|
|
33
|
+
)?.get
|
|
34
|
+
const TYPED_ARRAY_TAG_GETTER = Object.getOwnPropertyDescriptor(
|
|
35
|
+
TYPED_ARRAY_PROTOTYPE,
|
|
36
|
+
Symbol.toStringTag
|
|
37
|
+
)?.get
|
|
38
|
+
const DATA_VIEW_BUFFER_GETTER = Object.getOwnPropertyDescriptor(
|
|
39
|
+
DataView.prototype,
|
|
40
|
+
'buffer'
|
|
41
|
+
)?.get
|
|
42
|
+
const DATA_VIEW_BYTE_LENGTH_GETTER = Object.getOwnPropertyDescriptor(
|
|
43
|
+
DataView.prototype,
|
|
44
|
+
'byteLength'
|
|
45
|
+
)?.get
|
|
46
|
+
const DATA_VIEW_BYTE_OFFSET_GETTER = Object.getOwnPropertyDescriptor(
|
|
47
|
+
DataView.prototype,
|
|
48
|
+
'byteOffset'
|
|
49
|
+
)?.get
|
|
50
|
+
const DATA_VIEW_CONSTRUCTOR = DataView
|
|
51
|
+
const UINT8_ARRAY_CONSTRUCTOR = Uint8Array
|
|
52
|
+
const UINT8_ARRAY_SET = Uint8Array.prototype.set
|
|
53
|
+
const MAX_REQUEST_BYTES = 100_000_000
|
|
54
|
+
const MAX_REQUEST_DEPTH = 64
|
|
55
|
+
const MAX_REQUEST_VALUES = 100_000
|
|
56
|
+
const MAX_RESULT_BYTES = 250_000_000
|
|
57
|
+
const MAX_RESULT_VALUES = 2_000_000
|
|
58
|
+
const TYPED_ARRAYS = new Map(
|
|
59
|
+
[
|
|
60
|
+
Int8Array,
|
|
61
|
+
Uint8Array,
|
|
62
|
+
Uint8ClampedArray,
|
|
63
|
+
Int16Array,
|
|
64
|
+
Uint16Array,
|
|
65
|
+
Int32Array,
|
|
66
|
+
Uint32Array,
|
|
67
|
+
Float32Array,
|
|
68
|
+
Float64Array,
|
|
69
|
+
typeof BigInt64Array === 'function' ? BigInt64Array : null,
|
|
70
|
+
typeof BigUint64Array === 'function' ? BigUint64Array : null
|
|
71
|
+
]
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
.map((Constructor) => [Constructor.name, Constructor])
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Validates and prepares bounded structured-clone worker request data.
|
|
78
|
+
*/
|
|
79
|
+
export class WorkerRequestData {
|
|
80
|
+
/**
|
|
81
|
+
* Creates an accessor-free payload and exact transfer list in one pass.
|
|
82
|
+
* @param {unknown} value Request payload.
|
|
83
|
+
* @param {{ transferInput?: boolean }} [options] Transfer options.
|
|
84
|
+
* @returns {{ value: unknown, transfer: Transferable[] }} Prepared payload.
|
|
85
|
+
*/
|
|
86
|
+
static prepare(value, options = {}) {
|
|
87
|
+
return WorkerRequestData.#prepare(value, {
|
|
88
|
+
bytes: MAX_REQUEST_BYTES,
|
|
89
|
+
copyBinary: false,
|
|
90
|
+
output: false,
|
|
91
|
+
strictDescriptors: false,
|
|
92
|
+
transferInput: options.transferInput === true,
|
|
93
|
+
trustProof: false,
|
|
94
|
+
values: MAX_REQUEST_VALUES
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Prepares a bounded worker-owned result and transfers all safe buffers.
|
|
100
|
+
* @param {unknown} value Worker result.
|
|
101
|
+
* @returns {{ value: unknown, transfer: Transferable[] }} Prepared result.
|
|
102
|
+
*/
|
|
103
|
+
static prepareResult(value) {
|
|
104
|
+
return WorkerRequestData.#prepare(value, {
|
|
105
|
+
bytes: MAX_RESULT_BYTES,
|
|
106
|
+
copyBinary: false,
|
|
107
|
+
output: true,
|
|
108
|
+
strictDescriptors: false,
|
|
109
|
+
transferInput: true,
|
|
110
|
+
trustProof: true,
|
|
111
|
+
values: MAX_RESULT_VALUES
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Creates an owned accessor-free snapshot of one received worker result.
|
|
117
|
+
* @param {unknown} value Received result candidate.
|
|
118
|
+
* @returns {unknown} Owned clone-safe result data.
|
|
119
|
+
*/
|
|
120
|
+
static prepareResponse(value) {
|
|
121
|
+
return WorkerRequestData.#prepare(value, {
|
|
122
|
+
bytes: MAX_RESULT_BYTES,
|
|
123
|
+
copyBinary: true,
|
|
124
|
+
output: true,
|
|
125
|
+
strictDescriptors: true,
|
|
126
|
+
transferInput: false,
|
|
127
|
+
trustProof: false,
|
|
128
|
+
values: MAX_RESULT_VALUES
|
|
129
|
+
}).value
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Runs the shared bounded structured-data preparation pass.
|
|
134
|
+
* @param {unknown} value Data candidate.
|
|
135
|
+
* @param {{ bytes: number, copyBinary: boolean, output: boolean, strictDescriptors: boolean, transferInput: boolean, trustProof: boolean, values: number }} limits Preparation limits.
|
|
136
|
+
* @returns {{ value: unknown, transfer: Transferable[] }} Prepared data.
|
|
137
|
+
*/
|
|
138
|
+
static #prepare(value, limits) {
|
|
139
|
+
const state = {
|
|
140
|
+
bytes: 0,
|
|
141
|
+
copyBinary: limits.copyBinary,
|
|
142
|
+
maxBytes: limits.bytes,
|
|
143
|
+
maxValues: limits.values,
|
|
144
|
+
output: limits.output,
|
|
145
|
+
prepared: new WeakMap(),
|
|
146
|
+
strictDescriptors: limits.strictDescriptors,
|
|
147
|
+
transfer: { items: [], seen: new Set() },
|
|
148
|
+
transferInput: limits.transferInput,
|
|
149
|
+
trustProof: limits.trustProof,
|
|
150
|
+
values: 0,
|
|
151
|
+
visiting: new WeakSet()
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
value: WorkerRequestData.#prepareValue(value, state, 0),
|
|
155
|
+
transfer: state.transfer.items
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Rejects accessor-backed, executable, or unbounded posted values.
|
|
161
|
+
* @param {unknown} value Request payload.
|
|
162
|
+
* @returns {void}
|
|
163
|
+
*/
|
|
164
|
+
static assertCloneSafe(value) {
|
|
165
|
+
WorkerRequestData.prepare(value)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Prepares one bounded clone-safe value without invoking caller code.
|
|
170
|
+
* @param {unknown} value Value candidate.
|
|
171
|
+
* @param {Record<string, any>} state Traversal state.
|
|
172
|
+
* @param {number} depth Container depth.
|
|
173
|
+
* @returns {unknown} Prepared value.
|
|
174
|
+
*/
|
|
175
|
+
static #prepareValue(value, state, depth) {
|
|
176
|
+
const type = typeof value
|
|
177
|
+
if (
|
|
178
|
+
value === null ||
|
|
179
|
+
['undefined', 'boolean', 'number', 'bigint'].includes(type)
|
|
180
|
+
) {
|
|
181
|
+
return value
|
|
182
|
+
}
|
|
183
|
+
if (type === 'string') {
|
|
184
|
+
WorkerRequestData.#reserveBytes(state, value.length * 2)
|
|
185
|
+
return value
|
|
186
|
+
}
|
|
187
|
+
if (type !== 'object') {
|
|
188
|
+
throw new TypeError(
|
|
189
|
+
'Worker requests may contain only clone-safe data.'
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
RuntimeProxyBoundary.assert(value, 'Worker request data')
|
|
193
|
+
if (depth > MAX_REQUEST_DEPTH) {
|
|
194
|
+
throw new TypeError('Worker request data is nested too deeply.')
|
|
195
|
+
}
|
|
196
|
+
if (state.visiting.has(value)) {
|
|
197
|
+
throw new TypeError('Worker request data must not be cyclic.')
|
|
198
|
+
}
|
|
199
|
+
if (state.prepared.has(value)) return state.prepared.get(value)
|
|
200
|
+
WorkerRequestData.#reserveValues(state, 1)
|
|
201
|
+
state.visiting.add(value)
|
|
202
|
+
try {
|
|
203
|
+
const bufferLength = WorkerRequestData.#bufferLength(value)
|
|
204
|
+
if (bufferLength !== null) {
|
|
205
|
+
WorkerRequestData.#reserveBytes(state, bufferLength)
|
|
206
|
+
const isolate =
|
|
207
|
+
state.copyBinary ||
|
|
208
|
+
WorkerRequestData.#isResizableBuffer(value)
|
|
209
|
+
const prepared = isolate
|
|
210
|
+
? WorkerRequestData.#copyBuffer(value, 0, bufferLength)
|
|
211
|
+
: value
|
|
212
|
+
state.prepared.set(value, prepared)
|
|
213
|
+
if (state.transferInput) {
|
|
214
|
+
WorkerRequestData.#addTransfer(state.transfer, prepared)
|
|
215
|
+
}
|
|
216
|
+
return prepared
|
|
217
|
+
}
|
|
218
|
+
const sharedLength = WorkerRequestData.#sharedBufferLength(value)
|
|
219
|
+
if (sharedLength !== null) {
|
|
220
|
+
WorkerRequestData.#reserveBytes(state, sharedLength)
|
|
221
|
+
const copied = WorkerRequestData.#copyBuffer(
|
|
222
|
+
value,
|
|
223
|
+
0,
|
|
224
|
+
sharedLength
|
|
225
|
+
)
|
|
226
|
+
state.prepared.set(value, copied)
|
|
227
|
+
if (state.transferInput) {
|
|
228
|
+
WorkerRequestData.#addTransfer(state.transfer, copied)
|
|
229
|
+
}
|
|
230
|
+
return copied
|
|
231
|
+
}
|
|
232
|
+
const view = WorkerRequestData.#view(value)
|
|
233
|
+
if (view) {
|
|
234
|
+
WorkerRequestData.#reserveBytes(state, view.byteLength)
|
|
235
|
+
const prepared = WorkerRequestData.#prepareView(
|
|
236
|
+
value,
|
|
237
|
+
view,
|
|
238
|
+
state
|
|
239
|
+
)
|
|
240
|
+
state.prepared.set(value, prepared)
|
|
241
|
+
return prepared
|
|
242
|
+
}
|
|
243
|
+
return WorkerRequestData.#prepareContainer(value, state, depth)
|
|
244
|
+
} finally {
|
|
245
|
+
state.visiting.delete(value)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Prepares a dense array or plain object through data descriptors only.
|
|
251
|
+
* @param {object} value Container candidate.
|
|
252
|
+
* @param {Record<string, any>} state Traversal state.
|
|
253
|
+
* @param {number} depth Container depth.
|
|
254
|
+
* @returns {object | any[]} Prepared container.
|
|
255
|
+
*/
|
|
256
|
+
static #prepareContainer(value, state, depth) {
|
|
257
|
+
let prototype
|
|
258
|
+
let descriptors
|
|
259
|
+
try {
|
|
260
|
+
prototype = Object.getPrototypeOf(value)
|
|
261
|
+
descriptors = Object.getOwnPropertyDescriptors(value)
|
|
262
|
+
} catch {
|
|
263
|
+
throw new TypeError(
|
|
264
|
+
'Worker request data could not be inspected safely.'
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
if (Array.isArray(value)) {
|
|
268
|
+
return WorkerRequestData.#prepareArray(
|
|
269
|
+
value,
|
|
270
|
+
prototype,
|
|
271
|
+
descriptors,
|
|
272
|
+
state,
|
|
273
|
+
depth
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
277
|
+
throw new TypeError(
|
|
278
|
+
'Worker requests may contain only plain data objects.'
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
const keys =
|
|
282
|
+
state.output && !state.strictDescriptors
|
|
283
|
+
? Reflect.ownKeys(descriptors).filter(
|
|
284
|
+
(key) =>
|
|
285
|
+
typeof key === 'string' && descriptors[key].enumerable
|
|
286
|
+
)
|
|
287
|
+
: Reflect.ownKeys(descriptors)
|
|
288
|
+
const documentResult =
|
|
289
|
+
state.output &&
|
|
290
|
+
state.trustProof &&
|
|
291
|
+
WorkerRequestData.#dataValue(descriptors.schema) ===
|
|
292
|
+
'ecad-toolkit.document.v1' &&
|
|
293
|
+
CircuitJsonValidationProof.has(value)
|
|
294
|
+
WorkerRequestData.#reserveValues(state, keys.length)
|
|
295
|
+
const prepared = prototype === null ? Object.create(null) : {}
|
|
296
|
+
state.prepared.set(value, prepared)
|
|
297
|
+
for (const key of keys) {
|
|
298
|
+
if (typeof key !== 'string') {
|
|
299
|
+
throw new TypeError('Worker request keys must be strings.')
|
|
300
|
+
}
|
|
301
|
+
WorkerRequestData.#reserveBytes(state, key.length * 2)
|
|
302
|
+
Object.defineProperty(prepared, key, {
|
|
303
|
+
configurable: true,
|
|
304
|
+
enumerable: true,
|
|
305
|
+
value:
|
|
306
|
+
documentResult && key === 'model'
|
|
307
|
+
? WorkerRequestData.#documentModel(descriptors[key])
|
|
308
|
+
: WorkerRequestData.#descriptorValue(
|
|
309
|
+
descriptors[key],
|
|
310
|
+
state,
|
|
311
|
+
depth
|
|
312
|
+
),
|
|
313
|
+
writable: true
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
return prepared
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Prepares one bounded dense plain array before indexed allocation.
|
|
321
|
+
* @param {any[]} value Array candidate.
|
|
322
|
+
* @param {object | null} prototype Array prototype.
|
|
323
|
+
* @param {Record<string, PropertyDescriptor>} descriptors Descriptors.
|
|
324
|
+
* @param {Record<string, any>} state Traversal state.
|
|
325
|
+
* @param {number} depth Container depth.
|
|
326
|
+
* @returns {any[]} Prepared array.
|
|
327
|
+
*/
|
|
328
|
+
static #prepareArray(value, prototype, descriptors, state, depth) {
|
|
329
|
+
const keys = Reflect.ownKeys(descriptors)
|
|
330
|
+
const length = WorkerRequestData.#dataValue(descriptors.length)
|
|
331
|
+
const visibleKeys =
|
|
332
|
+
state.output && !state.strictDescriptors
|
|
333
|
+
? keys.filter(
|
|
334
|
+
(key) =>
|
|
335
|
+
key !== 'length' &&
|
|
336
|
+
typeof key === 'string' &&
|
|
337
|
+
descriptors[key].enumerable
|
|
338
|
+
)
|
|
339
|
+
: keys
|
|
340
|
+
if (
|
|
341
|
+
prototype !== Array.prototype ||
|
|
342
|
+
!Number.isSafeInteger(length) ||
|
|
343
|
+
length < 0 ||
|
|
344
|
+
(state.output && !state.strictDescriptors
|
|
345
|
+
? visibleKeys.length !== length
|
|
346
|
+
: keys.length !== length + 1)
|
|
347
|
+
) {
|
|
348
|
+
throw new TypeError(
|
|
349
|
+
'Worker request arrays must be bounded, dense, and plain.'
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
WorkerRequestData.#reserveValues(state, length)
|
|
353
|
+
const prepared = new Array(length)
|
|
354
|
+
state.prepared.set(value, prepared)
|
|
355
|
+
for (let index = 0; index < length; index += 1) {
|
|
356
|
+
prepared[index] = WorkerRequestData.#descriptorValue(
|
|
357
|
+
descriptors[String(index)],
|
|
358
|
+
state,
|
|
359
|
+
depth
|
|
360
|
+
)
|
|
361
|
+
}
|
|
362
|
+
return prepared
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Reads one enumerable data descriptor and prepares its value.
|
|
367
|
+
* @param {PropertyDescriptor | undefined} descriptor Descriptor candidate.
|
|
368
|
+
* @param {Record<string, any>} state Traversal state.
|
|
369
|
+
* @param {number} depth Parent depth.
|
|
370
|
+
* @returns {unknown} Prepared value.
|
|
371
|
+
*/
|
|
372
|
+
static #descriptorValue(descriptor, state, depth) {
|
|
373
|
+
if (state.trustProof) {
|
|
374
|
+
const protectedData =
|
|
375
|
+
CircuitJsonReadOnlyDocument.readProtectedAssetData(descriptor)
|
|
376
|
+
if (protectedData.trusted) {
|
|
377
|
+
return WorkerRequestData.#prepareValue(
|
|
378
|
+
protectedData.value,
|
|
379
|
+
state,
|
|
380
|
+
depth + 1
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
const protectedExtensionBinary =
|
|
384
|
+
ProtectedExtensionBinaryBoundary.read(descriptor)
|
|
385
|
+
if (protectedExtensionBinary.trusted) {
|
|
386
|
+
return WorkerRequestData.#prepareValue(
|
|
387
|
+
protectedExtensionBinary.value,
|
|
388
|
+
state,
|
|
389
|
+
depth + 1
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (
|
|
394
|
+
!descriptor ||
|
|
395
|
+
!Object.hasOwn(descriptor, 'value') ||
|
|
396
|
+
descriptor.enumerable !== true
|
|
397
|
+
) {
|
|
398
|
+
throw new TypeError(
|
|
399
|
+
'Worker requests may contain only enumerable data properties.'
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
return WorkerRequestData.#prepareValue(
|
|
403
|
+
descriptor.value,
|
|
404
|
+
state,
|
|
405
|
+
depth + 1
|
|
406
|
+
)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Preserves a genuine view type while isolating partial or shared bytes.
|
|
411
|
+
* @param {object} original Original view.
|
|
412
|
+
* @param {{ buffer: ArrayBufferLike, byteOffset: number, byteLength: number, tag: string }} view Intrinsic view fields.
|
|
413
|
+
* @param {Record<string, any>} state Traversal state.
|
|
414
|
+
* @returns {ArrayBufferView} Prepared exact view.
|
|
415
|
+
*/
|
|
416
|
+
static #prepareView(original, view, state) {
|
|
417
|
+
const backingLength = WorkerRequestData.#bufferLength(view.buffer)
|
|
418
|
+
const sharedLength = WorkerRequestData.#sharedBufferLength(view.buffer)
|
|
419
|
+
const exactArrayBuffer =
|
|
420
|
+
backingLength !== null &&
|
|
421
|
+
view.byteOffset === 0 &&
|
|
422
|
+
view.byteLength === backingLength
|
|
423
|
+
if (
|
|
424
|
+
exactArrayBuffer &&
|
|
425
|
+
!state.copyBinary &&
|
|
426
|
+
!WorkerRequestData.#isResizableBuffer(view.buffer)
|
|
427
|
+
) {
|
|
428
|
+
if (state.transferInput) {
|
|
429
|
+
WorkerRequestData.#addTransfer(state.transfer, view.buffer)
|
|
430
|
+
}
|
|
431
|
+
return original
|
|
432
|
+
}
|
|
433
|
+
if (backingLength === null && sharedLength === null) {
|
|
434
|
+
throw new TypeError('Worker request binary view is invalid.')
|
|
435
|
+
}
|
|
436
|
+
const copy = WorkerRequestData.#copyBuffer(
|
|
437
|
+
view.buffer,
|
|
438
|
+
view.byteOffset,
|
|
439
|
+
view.byteLength
|
|
440
|
+
)
|
|
441
|
+
const prepared = WorkerRequestData.#recreateView(view, copy)
|
|
442
|
+
if (state.transferInput) {
|
|
443
|
+
WorkerRequestData.#addTransfer(state.transfer, copy)
|
|
444
|
+
}
|
|
445
|
+
return prepared
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Recreates one intrinsic view on an exact isolated backing buffer.
|
|
450
|
+
* @param {{ byteLength: number, tag: string }} view View fields.
|
|
451
|
+
* @param {ArrayBuffer} buffer Exact copied buffer.
|
|
452
|
+
* @returns {ArrayBufferView} Recreated view.
|
|
453
|
+
*/
|
|
454
|
+
static #recreateView(view, buffer) {
|
|
455
|
+
if (view.tag === 'DataView') return new DATA_VIEW_CONSTRUCTOR(buffer)
|
|
456
|
+
const Constructor = TYPED_ARRAYS.get(view.tag)
|
|
457
|
+
if (!Constructor || view.byteLength % Constructor.BYTES_PER_ELEMENT) {
|
|
458
|
+
throw new TypeError('Worker request binary view is unsupported.')
|
|
459
|
+
}
|
|
460
|
+
return new Constructor(
|
|
461
|
+
buffer,
|
|
462
|
+
0,
|
|
463
|
+
view.byteLength / Constructor.BYTES_PER_ELEMENT
|
|
464
|
+
)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Reads genuine typed-array or DataView internal slots.
|
|
469
|
+
* @param {unknown} value View candidate.
|
|
470
|
+
* @returns {{ buffer: ArrayBufferLike, byteOffset: number, byteLength: number, tag: string } | null} Intrinsic view fields.
|
|
471
|
+
*/
|
|
472
|
+
static #view(value) {
|
|
473
|
+
try {
|
|
474
|
+
if (
|
|
475
|
+
DATA_VIEW_BUFFER_GETTER &&
|
|
476
|
+
DATA_VIEW_BYTE_OFFSET_GETTER &&
|
|
477
|
+
DATA_VIEW_BYTE_LENGTH_GETTER
|
|
478
|
+
) {
|
|
479
|
+
const buffer = DATA_VIEW_BUFFER_GETTER.call(value)
|
|
480
|
+
return {
|
|
481
|
+
buffer,
|
|
482
|
+
byteOffset: DATA_VIEW_BYTE_OFFSET_GETTER.call(value),
|
|
483
|
+
byteLength: DATA_VIEW_BYTE_LENGTH_GETTER.call(value),
|
|
484
|
+
tag: 'DataView'
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
} catch {
|
|
488
|
+
// The typed-array intrinsic check below is independent.
|
|
489
|
+
}
|
|
490
|
+
try {
|
|
491
|
+
const tag = TYPED_ARRAY_TAG_GETTER?.call(value)
|
|
492
|
+
if (!TYPED_ARRAYS.has(tag)) return null
|
|
493
|
+
return {
|
|
494
|
+
buffer: TYPED_ARRAY_BUFFER_GETTER.call(value),
|
|
495
|
+
byteOffset: TYPED_ARRAY_BYTE_OFFSET_GETTER.call(value),
|
|
496
|
+
byteLength: TYPED_ARRAY_BYTE_LENGTH_GETTER.call(value),
|
|
497
|
+
tag
|
|
498
|
+
}
|
|
499
|
+
} catch {
|
|
500
|
+
return null
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/** @param {unknown} value Candidate. @returns {number | null} Byte length. */
|
|
505
|
+
static #bufferLength(value) {
|
|
506
|
+
if (!ARRAY_BUFFER_BYTE_LENGTH_GETTER) return null
|
|
507
|
+
try {
|
|
508
|
+
return ARRAY_BUFFER_BYTE_LENGTH_GETTER.call(value)
|
|
509
|
+
} catch {
|
|
510
|
+
return null
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/** @param {unknown} value Candidate. @returns {number | null} Byte length. */
|
|
515
|
+
static #sharedBufferLength(value) {
|
|
516
|
+
if (!SHARED_ARRAY_BUFFER_BYTE_LENGTH_GETTER) return null
|
|
517
|
+
try {
|
|
518
|
+
return SHARED_ARRAY_BUFFER_BYTE_LENGTH_GETTER.call(value)
|
|
519
|
+
} catch {
|
|
520
|
+
return null
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Returns whether a genuine ArrayBuffer can change size after accounting.
|
|
526
|
+
* @param {unknown} value Buffer candidate.
|
|
527
|
+
* @returns {boolean} Whether the buffer is resizable.
|
|
528
|
+
*/
|
|
529
|
+
static #isResizableBuffer(value) {
|
|
530
|
+
if (!ARRAY_BUFFER_RESIZABLE_GETTER) return false
|
|
531
|
+
try {
|
|
532
|
+
return ARRAY_BUFFER_RESIZABLE_GETTER.call(value) === true
|
|
533
|
+
} catch {
|
|
534
|
+
return false
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Copies one already-accounted intrinsic byte range into a fixed buffer.
|
|
540
|
+
* The explicit range prevents a growable backing store from widening the
|
|
541
|
+
* copy between its limit check and snapshot.
|
|
542
|
+
* @param {ArrayBufferLike} buffer Source buffer.
|
|
543
|
+
* @param {number} byteOffset Captured byte offset.
|
|
544
|
+
* @param {number} byteLength Captured byte length.
|
|
545
|
+
* @returns {ArrayBuffer} Fixed owned snapshot.
|
|
546
|
+
*/
|
|
547
|
+
static #copyBuffer(buffer, byteOffset, byteLength) {
|
|
548
|
+
try {
|
|
549
|
+
const source = new UINT8_ARRAY_CONSTRUCTOR(
|
|
550
|
+
buffer,
|
|
551
|
+
byteOffset,
|
|
552
|
+
byteLength
|
|
553
|
+
)
|
|
554
|
+
const copy = new UINT8_ARRAY_CONSTRUCTOR(byteLength)
|
|
555
|
+
UINT8_ARRAY_SET.call(copy, source)
|
|
556
|
+
return TYPED_ARRAY_BUFFER_GETTER.call(copy)
|
|
557
|
+
} catch {
|
|
558
|
+
throw new TypeError(
|
|
559
|
+
'Worker request binary data changed while it was copied.'
|
|
560
|
+
)
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/** @param {PropertyDescriptor | undefined} descriptor Descriptor. @returns {unknown} Data value. */
|
|
565
|
+
static #dataValue(descriptor) {
|
|
566
|
+
return descriptor && Object.hasOwn(descriptor, 'value')
|
|
567
|
+
? descriptor.value
|
|
568
|
+
: undefined
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Preserves the already validated JSON-only CircuitJSON model by reference.
|
|
573
|
+
* @param {PropertyDescriptor | undefined} descriptor Model descriptor.
|
|
574
|
+
* @returns {object[]} CircuitJSON model.
|
|
575
|
+
*/
|
|
576
|
+
static #documentModel(descriptor) {
|
|
577
|
+
if (
|
|
578
|
+
!descriptor ||
|
|
579
|
+
!Object.hasOwn(descriptor, 'value') ||
|
|
580
|
+
descriptor.enumerable !== true ||
|
|
581
|
+
!Array.isArray(descriptor.value)
|
|
582
|
+
) {
|
|
583
|
+
throw new TypeError('Worker result document model is invalid.')
|
|
584
|
+
}
|
|
585
|
+
return descriptor.value
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Adds one transferable exactly once while preserving encounter order.
|
|
590
|
+
* @param {{ items: Transferable[], seen: Set<object> }} transfer Transfer accumulator.
|
|
591
|
+
* @param {Transferable} value Transferable value.
|
|
592
|
+
* @returns {void}
|
|
593
|
+
*/
|
|
594
|
+
static #addTransfer(transfer, value) {
|
|
595
|
+
if (transfer.seen.has(value)) return
|
|
596
|
+
transfer.seen.add(value)
|
|
597
|
+
transfer.items.push(value)
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** @param {{ bytes: number }} state State. @param {number} count Bytes. */
|
|
601
|
+
static #reserveBytes(state, count) {
|
|
602
|
+
if (
|
|
603
|
+
!Number.isSafeInteger(count) ||
|
|
604
|
+
count < 0 ||
|
|
605
|
+
state.bytes + count > state.maxBytes
|
|
606
|
+
) {
|
|
607
|
+
throw new TypeError('Worker request data exceeds its byte limit.')
|
|
608
|
+
}
|
|
609
|
+
state.bytes += count
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/** @param {{ values: number }} state State. @param {number} count Values. */
|
|
613
|
+
static #reserveValues(state, count) {
|
|
614
|
+
if (
|
|
615
|
+
!Number.isSafeInteger(count) ||
|
|
616
|
+
count < 0 ||
|
|
617
|
+
state.values + count > state.maxValues
|
|
618
|
+
) {
|
|
619
|
+
throw new TypeError('Worker request data is too large.')
|
|
620
|
+
}
|
|
621
|
+
state.values += count
|
|
622
|
+
}
|
|
623
|
+
}
|