circuitjson-toolkit 1.3.0 → 1.4.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/README.md +48 -0
- package/docs/api.md +84 -0
- package/docs/release-notes-v1.4.0.md +84 -0
- package/package.json +2 -1
- package/src/core/Parser.mjs +1 -1
- package/src/core/context/BinaryDataSnapshot.mjs +59 -0
- package/src/core/context/CircuitJsonDocumentContext.mjs +55 -0
- package/src/core/context/CircuitJsonExtensionBoundary.mjs +312 -0
- package/src/core/context/CircuitJsonReadOnlyDocument.mjs +115 -59
- package/src/core/context/CircuitJsonValidationProof.mjs +122 -10
- package/src/core/context/ProtectedExtensionBinaryBoundary.mjs +233 -0
- package/src/core/context/StructuredCloneAdoption.mjs +975 -0
- package/src/core/context/StructuredCloneCollectionNormalizer.mjs +86 -0
- package/src/core/context/StructuredCloneTextAccounting.mjs +55 -0
- package/src/core/context/StructuredDataSnapshot.mjs +53 -0
- package/src/core/contracts/DocumentResult.mjs +38 -6
package/README.md
CHANGED
|
@@ -69,6 +69,14 @@ input. Cross-realm and altered-prototype buffers, typed views, byte limits,
|
|
|
69
69
|
defensive copies, and all existing return shapes remain supported.
|
|
70
70
|
See the [1.3.0 release notes](docs/release-notes-v1.3.0.md).
|
|
71
71
|
|
|
72
|
+
Version 1.4.0 adds cooperative structured-clone preparation and a documented
|
|
73
|
+
owned-document construction path for source toolkits. Hosts can yield
|
|
74
|
+
throughout large extension traversal, binary protection, and property locking,
|
|
75
|
+
and format parsers can retain the identity of graphs they just created instead
|
|
76
|
+
of defensively copying them again. The resulting model, extensions,
|
|
77
|
+
parameters, and `DocumentResult` shape are unchanged. See the
|
|
78
|
+
[1.4.0 release notes](docs/release-notes-v1.4.0.md).
|
|
79
|
+
|
|
72
80
|
Before 1.1.0:
|
|
73
81
|
|
|
74
82
|
```js
|
|
@@ -115,6 +123,8 @@ const model = document.model
|
|
|
115
123
|
cancellation, and controlled buffer transfer
|
|
116
124
|
- One-pass ownership for selected source extensions, with a separate 128 MiB /
|
|
117
125
|
4,000,000-item bound and exact direct/worker result parity
|
|
126
|
+
- Cooperative structured-clone preparation with in-place ordinary records and
|
|
127
|
+
clean alias/cycle-preserving dense-array normalization
|
|
118
128
|
- Machine-readable capability inventory and packed downstream conformance
|
|
119
129
|
harness
|
|
120
130
|
- Explicit `/extensions` surface retaining every previous specialized API
|
|
@@ -157,6 +167,43 @@ const components = QueryService.create(context).query({
|
|
|
157
167
|
console.log(document.model, svg, hits, components.items)
|
|
158
168
|
```
|
|
159
169
|
|
|
170
|
+
Browser hosts that receive the exact result of a platform structured clone can
|
|
171
|
+
prepare it cooperatively:
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
const context = await CircuitJsonDocumentContext.prepareStructuredCloneAsync(
|
|
175
|
+
message.data,
|
|
176
|
+
{
|
|
177
|
+
indexes: ['elements', 'relations'],
|
|
178
|
+
ownership: 'exclusive',
|
|
179
|
+
yield: () => scheduler.yield()
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`ownership: 'exclusive'` is required and transfers the graph destructively.
|
|
185
|
+
The caller must relinquish every alias, including shared-memory writers, until
|
|
186
|
+
the promise settles. A mutation to a node that has not been acquired yet cannot
|
|
187
|
+
be reconstructed or detected later, and rejection may leave part of the graph
|
|
188
|
+
locked. Use `prepareStructuredClone()` for uninterrupted same-thread adoption,
|
|
189
|
+
or `prepare()` for arbitrary caller-owned values.
|
|
190
|
+
|
|
191
|
+
An already prepared `CircuitJsonDocumentContext` is reused immediately and
|
|
192
|
+
does not require an ownership declaration because no graph is transferred.
|
|
193
|
+
Ordinary extension records are adopted in place. Dense arrays are normalized
|
|
194
|
+
into clean arrays while preserving aliases and cycles, preventing unsupported
|
|
195
|
+
hidden properties from carrying mutable state into the context.
|
|
196
|
+
|
|
197
|
+
The model is validated and frozen before extension adoption begins. Dense
|
|
198
|
+
arrays, Map/Set normalization, immutable text accounting, binary copying,
|
|
199
|
+
binary installation, and property locking are divided into bounded slices.
|
|
200
|
+
Individual plain extension records are limited to 16,384 properties on this
|
|
201
|
+
cooperative path. The method returns the same
|
|
202
|
+
`CircuitJsonDocumentContext` shape as the synchronous preparation methods.
|
|
203
|
+
Omit `yield` to use `scheduler.yield()` when available and a zero-delay host
|
|
204
|
+
task otherwise. This entry point accepts only exact platform structured-clone
|
|
205
|
+
results with ordinary enumerable string properties.
|
|
206
|
+
|
|
160
207
|
`Parser.parse()` returns the exact clone-safe `ecad-toolkit.document.v1`
|
|
161
208
|
envelope:
|
|
162
209
|
|
|
@@ -338,6 +385,7 @@ copy while keeping sync, direct async, and worker results mutation-isolated.
|
|
|
338
385
|
- [1.2.0 release notes](docs/release-notes-v1.2.0.md)
|
|
339
386
|
- [1.2.1 release notes](docs/release-notes-v1.2.1.md)
|
|
340
387
|
- [1.3.0 release notes](docs/release-notes-v1.3.0.md)
|
|
388
|
+
- [1.4.0 release notes](docs/release-notes-v1.4.0.md)
|
|
341
389
|
- [Library scope](spec/library-scope.md)
|
|
342
390
|
|
|
343
391
|
## Package scope
|
package/docs/api.md
CHANGED
|
@@ -131,6 +131,44 @@ cross-realm, proxy-backed, or prototype-modified input. The general path keeps
|
|
|
131
131
|
intrinsic binary slots authoritative and preserves altered-prototype
|
|
132
132
|
`ArrayBuffer`, `SharedArrayBuffer`, typed-array, and `DataView` values.
|
|
133
133
|
|
|
134
|
+
To split model validation and extension sealing across host tasks, use the
|
|
135
|
+
cooperative equivalent:
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
const context = await CircuitJsonDocumentContext.prepareStructuredCloneAsync(
|
|
139
|
+
message.data,
|
|
140
|
+
{
|
|
141
|
+
indexes: ['elements', 'relations'],
|
|
142
|
+
ownership: 'exclusive',
|
|
143
|
+
yield: () => scheduler.yield()
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`ownership: 'exclusive'` is mandatory. It transfers the exact platform
|
|
149
|
+
structured-clone graph destructively; the caller must relinquish all aliases
|
|
150
|
+
and shared-memory writers until the promise settles. The optional `yield`
|
|
151
|
+
callback is awaited after model validation and repeatedly between bounded
|
|
152
|
+
slices of dense-array traversal, Map/Set normalization, immutable text
|
|
153
|
+
accounting, binary copying and installation, and property locking. Individual
|
|
154
|
+
plain extension records are limited to 16,384 properties on this path.
|
|
155
|
+
|
|
156
|
+
Existing `CircuitJsonDocumentContext` inputs are already immutable and are
|
|
157
|
+
reused immediately without an ownership declaration. During a new transfer,
|
|
158
|
+
ordinary extension records retain their identity. Dense arrays are normalized
|
|
159
|
+
into clean arrays while retaining graph aliases and cycles, so non-clone hidden
|
|
160
|
+
properties cannot leave mutable state in the prepared context.
|
|
161
|
+
|
|
162
|
+
Acquired containers are shape-locked and their descriptors are checked during
|
|
163
|
+
sealing. This is not a transactional snapshot of retained aliases: a caller
|
|
164
|
+
that violates exclusive ownership can change a node before it is acquired, and
|
|
165
|
+
a rejected transfer may leave part of the graph locked. Without an injected
|
|
166
|
+
callback the method uses `scheduler.yield()` when available, then falls back to
|
|
167
|
+
a zero-delay host task. The promise resolves to the same prepared context
|
|
168
|
+
returned by the synchronous methods, including the same requested indexes and
|
|
169
|
+
cache behavior. Use `prepareStructuredClone()` for uninterrupted same-thread
|
|
170
|
+
adoption or `prepare()` for arbitrary caller-owned graphs.
|
|
171
|
+
|
|
134
172
|
## Root entrypoint
|
|
135
173
|
|
|
136
174
|
`circuitjson-toolkit` has an exact 17-class root. The 14 canonical classes are:
|
|
@@ -203,6 +241,24 @@ Packed release checks reject any missing or additional root export.
|
|
|
203
241
|
|
|
204
242
|
Import from the root or `circuitjson-toolkit/parser`.
|
|
205
243
|
|
|
244
|
+
### `DocumentResult.createValidatedOwned(fields, runtime?)`
|
|
245
|
+
|
|
246
|
+
Creates the same validated `ecad-toolkit.document.v1` envelope as
|
|
247
|
+
`DocumentResult.createValidated(fields, runtime?)`, but adopts ordinary model
|
|
248
|
+
and extension graph nodes that the calling toolkit just constructed. The
|
|
249
|
+
model and retained extension nodes keep their identities and are validated and
|
|
250
|
+
deeply frozen in place. The envelope schema, parameters, source-reference
|
|
251
|
+
runtime option, and public return fields are unchanged. Binary payloads still
|
|
252
|
+
pass through the defensive binary-property boundary.
|
|
253
|
+
|
|
254
|
+
This is a destructive ownership transfer for source-toolkit convergence
|
|
255
|
+
builders. Call it only when the complete ordinary graph is newly created,
|
|
256
|
+
mutable, has standard local built-ins, and is no longer shared with code that
|
|
257
|
+
expects to mutate it. Raw parser input, arbitrary caller objects, cross-realm
|
|
258
|
+
values, proxies, and prototype-modified graphs must use
|
|
259
|
+
`DocumentResult.createValidated()` instead. The method is exported from
|
|
260
|
+
`circuitjson-toolkit/parser`, not from the exact root surface.
|
|
261
|
+
|
|
206
262
|
### `Parser.parse(input, options?)`
|
|
207
263
|
|
|
208
264
|
Synchronously detects, decodes, validates, and returns one canonical document.
|
|
@@ -332,6 +388,34 @@ The class constructor is not a public construction path. It throws before
|
|
|
332
388
|
observing caller input; use `prepare()` so every context carries a
|
|
333
389
|
validation-bound authority that downstream viewers and applications can trust.
|
|
334
390
|
|
|
391
|
+
### `CircuitJsonDocumentContext.prepareStructuredCloneAsync(input, options?)`
|
|
392
|
+
|
|
393
|
+
Cooperatively validates and adopts an exact platform structured-clone result.
|
|
394
|
+
`options.ownership` must be the literal string `exclusive`; it declares a
|
|
395
|
+
destructive transfer and requires the caller to relinquish every alias and
|
|
396
|
+
shared-memory writer until settlement. `options.indexes` accepts the same names
|
|
397
|
+
as `prepare()`. `options.yield` may be an async or synchronous function; it is
|
|
398
|
+
awaited after model validation and between bounded slices of extension
|
|
399
|
+
adoption and sealing. If omitted, the runtime uses `scheduler.yield()` when
|
|
400
|
+
present or a zero-delay host task. The method returns a promise for the same
|
|
401
|
+
immutable context shape. The cooperative path enforces the normal extension
|
|
402
|
+
limits plus a 16,384-property limit on each individual plain record.
|
|
403
|
+
|
|
404
|
+
When `input` is already a `CircuitJsonDocumentContext`, the method performs no
|
|
405
|
+
transfer and reuses it immediately, so `options.ownership` is not required.
|
|
406
|
+
Transferred ordinary records are adopted in place; dense arrays are normalized
|
|
407
|
+
to clean arrays with aliases and cycles preserved.
|
|
408
|
+
|
|
409
|
+
Use this only at a provenance boundary that guarantees standard local
|
|
410
|
+
built-ins and ordinary enumerable string properties. Large strings, binary
|
|
411
|
+
payloads, dense arrays, and Map/Set values are processed across cooperative
|
|
412
|
+
pauses, then acquired containers are progressively locked before the proof and
|
|
413
|
+
envelope are sealed. Mutation before a node is acquired is outside the
|
|
414
|
+
exclusive-transfer contract and cannot be detected reliably; rejection can
|
|
415
|
+
leave a partially locked graph. Use `prepareStructuredClone()` for
|
|
416
|
+
uninterrupted same-thread adoption and `prepare()` for untrusted or otherwise
|
|
417
|
+
arbitrary caller graphs.
|
|
418
|
+
|
|
335
419
|
### `context.getIndex(name)` and `context.hasIndex(name)`
|
|
336
420
|
|
|
337
421
|
Access a prepared index or check its presence.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# circuitjson-toolkit 1.4.0
|
|
2
|
+
|
|
3
|
+
This minor release removes redundant ownership work when a source toolkit or
|
|
4
|
+
browser worker has already established an exact graph provenance boundary. It
|
|
5
|
+
also lets browser hosts return control between validation and extension
|
|
6
|
+
sealing without changing the canonical result contract.
|
|
7
|
+
|
|
8
|
+
## Owned document construction
|
|
9
|
+
|
|
10
|
+
- `DocumentResult.createValidatedOwned(fields, runtime?)` creates the same
|
|
11
|
+
validated `ecad-toolkit.document.v1` envelope as `createValidated()`.
|
|
12
|
+
- A source-toolkit convergence builder may transfer a newly constructed,
|
|
13
|
+
standard-built-in graph into the envelope. Ordinary model and extension
|
|
14
|
+
nodes retain their identities and are deeply frozen in place instead of
|
|
15
|
+
being copied into a second full graph.
|
|
16
|
+
- The method is intentionally destructive. It is only safe when the toolkit
|
|
17
|
+
exclusively owns the complete mutable graph and will not mutate it after the
|
|
18
|
+
call. Arbitrary caller values, raw untrusted input, cross-realm objects,
|
|
19
|
+
proxies, and altered prototypes must continue through `createValidated()`.
|
|
20
|
+
- Binary properties retain their defensive boundary and validation. Ownership
|
|
21
|
+
limits, validation proofs, and immutable-envelope guarantees are unchanged.
|
|
22
|
+
|
|
23
|
+
## Cooperative structured-clone preparation
|
|
24
|
+
|
|
25
|
+
- `CircuitJsonDocumentContext.prepareStructuredCloneAsync(input, options?)`
|
|
26
|
+
accepts the same structured-clone input and `indexes` option as the
|
|
27
|
+
synchronous method, plus the required `ownership: 'exclusive'` declaration
|
|
28
|
+
and an optional `yield` scheduler.
|
|
29
|
+
- This is a destructive transfer. Callers must relinquish every alias and
|
|
30
|
+
shared-memory writer until settlement. Mutation before a node is acquired is
|
|
31
|
+
outside the contract and cannot be detected reliably; rejection can leave a
|
|
32
|
+
partially locked graph.
|
|
33
|
+
- The method validates and deeply freezes the model, then yields between
|
|
34
|
+
bounded slices of dense-array traversal, Map/Set normalization, immutable
|
|
35
|
+
text accounting, binary copying and installation, and property locking
|
|
36
|
+
before sealing the canonical envelope.
|
|
37
|
+
- Acquired containers are shape-locked and their descriptors are checked while
|
|
38
|
+
sealing. Individual plain extension records are capped at 16,384 properties
|
|
39
|
+
so one record cannot create an unbounded cooperative inspection step.
|
|
40
|
+
- Existing immutable contexts are reused without a transfer declaration.
|
|
41
|
+
Transferred ordinary records retain identity; dense arrays are normalized
|
|
42
|
+
into clean arrays while preserving aliases and cycles, preventing unsupported
|
|
43
|
+
hidden properties from leaking mutable state.
|
|
44
|
+
- An injected `yield` function is awaited at each scheduling boundary. Without
|
|
45
|
+
one, the toolkit prefers `scheduler.yield()` and otherwise uses a zero-delay
|
|
46
|
+
host task.
|
|
47
|
+
- The promise resolves to the same immutable `CircuitJsonDocumentContext`
|
|
48
|
+
shape with the same indexes, caches, limits, and validation authority.
|
|
49
|
+
|
|
50
|
+
## Compatibility
|
|
51
|
+
|
|
52
|
+
- No parser option, package subpath, class, parameter, or return field is
|
|
53
|
+
removed or renamed.
|
|
54
|
+
- `DocumentResult` remains `ecad-toolkit.document.v1`; its `model`, `source`,
|
|
55
|
+
`extensions`, `assets`, `diagnostics`, and `statistics` fields are unchanged.
|
|
56
|
+
- `createValidatedOwned()` and `prepareStructuredCloneAsync()` are additive.
|
|
57
|
+
The defensive and synchronous APIs retain their previous behavior.
|
|
58
|
+
|
|
59
|
+
## Verification and performance
|
|
60
|
+
|
|
61
|
+
Synthetic regression coverage verifies ordinary-record identity retention,
|
|
62
|
+
alias/cycle-preserving clean arrays, deep freeze, chunked text and binary
|
|
63
|
+
processing, dense-array and Map/Set scheduling,
|
|
64
|
+
cooperative yield ordering, progressive shape/property locking, the exclusive
|
|
65
|
+
ownership contract, and context reuse across document batches. The full
|
|
66
|
+
adversarial suite continues to cover hostile accessors, altered and cross-realm
|
|
67
|
+
built-ins, defensive binary ownership, worker parity, synchronous mutation
|
|
68
|
+
isolation, and bounded extension graphs.
|
|
69
|
+
|
|
70
|
+
On the same browser and machine, the exact large native-PCB deep link that
|
|
71
|
+
previously produced 3.29-second and 2.17-second renderer-main tasks retained
|
|
72
|
+
the same 25,729-element PCB SVG and view box after this release. A final fresh
|
|
73
|
+
open peaked at 17.7 milliseconds on the renderer main thread. A forced reload
|
|
74
|
+
peaked at 404.4 milliseconds, consisting of 196.8 milliseconds of browser
|
|
75
|
+
structured-clone deserialization plus 205.3 milliseconds of browser garbage
|
|
76
|
+
collection; no application JavaScript task approached the previous stalls.
|
|
77
|
+
The largest scheduled parser-worker task in that reload was 5.9 milliseconds.
|
|
78
|
+
A separate 150,000-record exclusive-adoption probe yielded 880 times and
|
|
79
|
+
completed in 487.35 milliseconds, with a 1.22-millisecond p95 slice and a
|
|
80
|
+
6.94-millisecond maximum outlier. A 32 MiB immutable-text probe completed in
|
|
81
|
+
8.13 milliseconds across 513 yields; a 32 MiB binary probe completed in 3.40
|
|
82
|
+
milliseconds across 514 yields. These figures describe fixed local workloads
|
|
83
|
+
and are not runtime guarantees; deterministic shape, validation, and ownership
|
|
84
|
+
tests remain the release gates.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circuitjson-toolkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Canonical CircuitJSON parsing, project, rendering, query, manufacturing, simulation, and scene contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"circuitjson",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"docs/release-notes-v1.2.0.md",
|
|
57
57
|
"docs/release-notes-v1.2.1.md",
|
|
58
58
|
"docs/release-notes-v1.3.0.md",
|
|
59
|
+
"docs/release-notes-v1.4.0.md",
|
|
59
60
|
"docs/testing.md",
|
|
60
61
|
"spec",
|
|
61
62
|
"LICENSE",
|
package/src/core/Parser.mjs
CHANGED
|
@@ -211,7 +211,7 @@ export class Parser {
|
|
|
211
211
|
normalized.options.retainSource === 'reference'
|
|
212
212
|
? { sourceReference: normalized.sourceReference }
|
|
213
213
|
: {}
|
|
214
|
-
return DocumentResult.
|
|
214
|
+
return DocumentResult.createValidatedOwned(
|
|
215
215
|
{
|
|
216
216
|
fileName: normalized.input.fileName,
|
|
217
217
|
fileType: 'circuitjson',
|
|
@@ -241,6 +241,44 @@ export class BinaryDataSnapshot {
|
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Copies one bounded byte range into an existing isolated byte array.
|
|
246
|
+
* @param {{ buffer: ArrayBuffer | SharedArrayBuffer, byteOffset: number, byteLength: number }} range Captured intrinsic range.
|
|
247
|
+
* @param {Uint8Array} target Isolated destination bytes.
|
|
248
|
+
* @param {number} offset Visible-range byte offset.
|
|
249
|
+
* @param {number} count Number of bytes to copy.
|
|
250
|
+
* @returns {void}
|
|
251
|
+
* @internal
|
|
252
|
+
*/
|
|
253
|
+
static copyBytesInto(range, target, offset, count) {
|
|
254
|
+
if (
|
|
255
|
+
!(target instanceof Uint8Array) ||
|
|
256
|
+
!Number.isSafeInteger(offset) ||
|
|
257
|
+
!Number.isSafeInteger(count) ||
|
|
258
|
+
offset < 0 ||
|
|
259
|
+
count < 0 ||
|
|
260
|
+
offset + count > range?.byteLength ||
|
|
261
|
+
target.byteLength !== range?.byteLength
|
|
262
|
+
) {
|
|
263
|
+
throw new TypeError('Invalid binary copy range.')
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
const source = new Uint8Array(
|
|
267
|
+
range.buffer,
|
|
268
|
+
range.byteOffset + offset,
|
|
269
|
+
count
|
|
270
|
+
)
|
|
271
|
+
const destination = new Uint8Array(
|
|
272
|
+
target.buffer,
|
|
273
|
+
target.byteOffset + offset,
|
|
274
|
+
count
|
|
275
|
+
)
|
|
276
|
+
UINT8_ARRAY_SET.call(destination, source)
|
|
277
|
+
} catch {
|
|
278
|
+
throw new TypeError('Binary data changed during capture.')
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
244
282
|
/**
|
|
245
283
|
* Copies one binary metadata value while retaining its common view type.
|
|
246
284
|
* @param {unknown} value Binary metadata.
|
|
@@ -261,6 +299,27 @@ export class BinaryDataSnapshot {
|
|
|
261
299
|
return new Constructor(bytes.buffer)
|
|
262
300
|
}
|
|
263
301
|
|
|
302
|
+
/**
|
|
303
|
+
* Restores one common binary view type from completely isolated bytes.
|
|
304
|
+
* @param {unknown} value Original binary value used only for view type.
|
|
305
|
+
* @param {{ kind: 'buffer' | 'typed-array' | 'data-view' }} range Captured intrinsic kind.
|
|
306
|
+
* @param {Uint8Array} bytes Completely copied isolated bytes.
|
|
307
|
+
* @returns {ArrayBuffer | Uint8Array | DataView} Isolated binary clone.
|
|
308
|
+
* @internal
|
|
309
|
+
*/
|
|
310
|
+
static cloneFromBytes(value, range, bytes) {
|
|
311
|
+
if (!(bytes instanceof Uint8Array)) {
|
|
312
|
+
throw new TypeError('Expected isolated binary bytes.')
|
|
313
|
+
}
|
|
314
|
+
if (range.kind === 'buffer') return bytes.buffer
|
|
315
|
+
if (range.kind === 'data-view') return new DataView(bytes.buffer)
|
|
316
|
+
const Constructor = BinaryDataSnapshot.#typedArrayConstructor(value)
|
|
317
|
+
if (!Constructor || Constructor === Uint8Array) return bytes
|
|
318
|
+
const bytesPerElement = Constructor.BYTES_PER_ELEMENT
|
|
319
|
+
if (bytes.byteLength % bytesPerElement !== 0) return bytes
|
|
320
|
+
return new Constructor(bytes.buffer)
|
|
321
|
+
}
|
|
322
|
+
|
|
264
323
|
/**
|
|
265
324
|
* Calls a captured buffer length getter as a brand check.
|
|
266
325
|
* @param {Function | null | undefined} getter Intrinsic getter.
|
|
@@ -88,6 +88,31 @@ export class CircuitJsonDocumentContext {
|
|
|
88
88
|
return CircuitJsonDocumentContext.#prepare(input, options, true)
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Destructively adopts an exclusively transferred structured-clone graph
|
|
93
|
+
* while allowing hosts to service input and paint between bounded slices.
|
|
94
|
+
* @param {unknown} input Structured-cloned document result or existing context.
|
|
95
|
+
* @param {{ indexes?: unknown, ownership: 'exclusive', yield?: () => Promise<void> | void }} [options] Requested indexes, required exclusive ownership authority, and host scheduler.
|
|
96
|
+
* @returns {Promise<CircuitJsonDocumentContext>} Prepared request-scoped context.
|
|
97
|
+
*/
|
|
98
|
+
static async prepareStructuredCloneAsync(input, options = {}) {
|
|
99
|
+
if (CircuitJsonDocumentContext.#isContext(input)) {
|
|
100
|
+
return CircuitJsonDocumentContext.#prepare(input, options, true)
|
|
101
|
+
}
|
|
102
|
+
if (options?.ownership !== 'exclusive') {
|
|
103
|
+
throw new TypeError(
|
|
104
|
+
'Cooperative structured-clone preparation requires exclusive ownership.'
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
const context = await CircuitJsonDocumentContext.#fromInputAsync(
|
|
108
|
+
input,
|
|
109
|
+
true,
|
|
110
|
+
options?.yield
|
|
111
|
+
)
|
|
112
|
+
context.#indexes.ensure(options?.indexes || [])
|
|
113
|
+
return context
|
|
114
|
+
}
|
|
115
|
+
|
|
91
116
|
/**
|
|
92
117
|
* Prepares one context with explicit metadata provenance.
|
|
93
118
|
* @param {unknown} input Document result, CircuitJSON model, or context.
|
|
@@ -229,6 +254,36 @@ export class CircuitJsonDocumentContext {
|
|
|
229
254
|
)
|
|
230
255
|
}
|
|
231
256
|
|
|
257
|
+
/**
|
|
258
|
+
* Creates one context while yielding between model validation and envelope
|
|
259
|
+
* sealing for a proven structured-clone graph.
|
|
260
|
+
* @param {unknown} input Document result or CircuitJSON model.
|
|
261
|
+
* @param {boolean} standardBuiltins Whether metadata built-ins have standard local prototypes.
|
|
262
|
+
* @param {(() => Promise<void> | void) | undefined} yieldControl Host scheduler.
|
|
263
|
+
* @returns {Promise<CircuitJsonDocumentContext>} New context.
|
|
264
|
+
*/
|
|
265
|
+
static async #fromInputAsync(input, standardBuiltins, yieldControl) {
|
|
266
|
+
const document = CircuitJsonDocumentContext.#normalizeDocument(input)
|
|
267
|
+
const validationPasses = CircuitJsonValidationProof.has(document)
|
|
268
|
+
? 0
|
|
269
|
+
: 1
|
|
270
|
+
const readonlyDocument =
|
|
271
|
+
await CircuitJsonValidationProof.validateAndAttachAsync(document, {
|
|
272
|
+
standardBuiltins,
|
|
273
|
+
yield: yieldControl
|
|
274
|
+
})
|
|
275
|
+
const model = CircuitJsonDocumentContext.#ownData(
|
|
276
|
+
readonlyDocument,
|
|
277
|
+
'model'
|
|
278
|
+
)
|
|
279
|
+
return new CircuitJsonDocumentContext(
|
|
280
|
+
readonlyDocument,
|
|
281
|
+
model,
|
|
282
|
+
validationPasses,
|
|
283
|
+
CONTEXT_CONSTRUCTION_AUTHORITY
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
232
287
|
/**
|
|
233
288
|
* Normalizes supported context input into a canonical document envelope.
|
|
234
289
|
* @param {unknown} input Document result or CircuitJSON model.
|