circuitjson-toolkit 1.0.17 → 1.1.1

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