circuitjson-toolkit 1.1.0 → 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.
package/README.md CHANGED
@@ -37,6 +37,12 @@ See the [migration guide](docs/migration.md) and its generated
37
37
  [1.1.0 release notes](docs/release-notes-v1.1.0.md) provide the concise change
38
38
  summary.
39
39
 
40
+ Version 1.1.1 makes queued worker-request ownership synchronous. Parser and
41
+ project requests waiting behind active work can no longer observe later
42
+ caller-buffer mutation; explicit transfers detach exact buffers immediately
43
+ after admission. See the
44
+ [1.1.1 release notes](docs/release-notes-v1.1.1.md).
45
+
40
46
  Before 1.1.0:
41
47
 
42
48
  ```js
@@ -294,6 +300,7 @@ copy while keeping sync, direct async, and worker results mutation-isolated.
294
300
  - [Model and envelope format](docs/model-format.md)
295
301
  - [Testing and downstream conformance](docs/testing.md)
296
302
  - [1.1.0 release notes](docs/release-notes-v1.1.0.md)
303
+ - [1.1.1 release notes](docs/release-notes-v1.1.1.md)
297
304
  - [Library scope](spec/library-scope.md)
298
305
 
299
306
  ## Package scope
package/docs/api.md CHANGED
@@ -80,6 +80,12 @@ Asynchronous parser and project calls accept:
80
80
  unavailable. Protocol, post-message, parser, and runtime errors stay visible.
81
81
  Worker and direct results have the same serialized shape.
82
82
 
83
+ Requests waiting behind active worker work take ownership immediately after
84
+ admission. The default mode clones the exact binary graph into a private queue
85
+ snapshot and leaves caller buffers intact. `transferInput: true` detaches exact
86
+ transferable buffers at admission; partial views, resizable buffers, and
87
+ shared buffers are isolated without detaching unrelated caller bytes.
88
+
83
89
  `retainSource` is exactly `'none' | 'reference'` and defaults to `'none'`.
84
90
  `'reference'` is an in-process parser option: the returned document has a
85
91
  non-enumerable `sourceReference` property whose value is the exact caller input
package/docs/migration.md CHANGED
@@ -92,6 +92,12 @@ returns either `{ ok: true, value }` or
92
92
  uses ordered `detect`, `decode`, `project`, `validate`, and `complete`
93
93
  stages. Cancellation is request-scoped.
94
94
 
95
+ Since 1.1.1, a request waiting behind active worker work is owned immediately
96
+ after admission. With the default `transferInput: false`, later caller
97
+ mutations cannot alter a queued parser input, project entry, or asset. With
98
+ `transferInput: true`, exact transferable buffers detach at admission;
99
+ partial, resizable, and shared ranges continue to use isolated copies.
100
+
95
101
  ## Package subpaths
96
102
 
97
103
  - `circuitjson-toolkit/parser`
@@ -0,0 +1,24 @@
1
+ # circuitjson-toolkit 1.1.1
2
+
3
+ ## Synchronous queued-request ownership
4
+
5
+ This patch makes the common worker client own every accepted parser and
6
+ project request when it enters the queue behind active work.
7
+
8
+ - Default `transferInput: false` requests clone their exact binary graph into
9
+ a private queue snapshot immediately. Later caller mutation cannot change a
10
+ queued parser input, project entry, or attached asset.
11
+ - `transferInput: true` detaches exact transferable caller buffers immediately
12
+ after queue admission. Partial views, resizable buffers, and shared buffers
13
+ keep their isolated-copy behavior without detaching unrelated caller bytes.
14
+ - Shared backing-buffer aliases remain shared in the owned request, and queued
15
+ snapshots transfer to the worker without another binary copy when posted.
16
+ - Disposed, pre-cancelled, over-limit, and initial worker-construction failures
17
+ reject before ownership. Automatic direct fallback therefore retains valid
18
+ binary input.
19
+ - Parser and project queues use the same bounded, accessor-safe traversal and
20
+ retain existing cancellation, error, and response behavior.
21
+
22
+ Public names, parameters, package subpaths, document envelopes, and project
23
+ envelopes are unchanged from 1.1.0. Gerber, Altium, KiCad, viewers, and ECAD
24
+ Forge receive the corrected behavior through the shared `ParserWorkerClient`.
package/docs/testing.md CHANGED
@@ -80,6 +80,11 @@ Worker checks install an in-memory `ecad-toolkit.worker.v1` loopback with real
80
80
  structured-clone and transfer boundaries, then require `worker: true`. A direct
81
81
  fallback cannot satisfy these checks.
82
82
 
83
+ Queue regressions hold one worker operation active while parser and project
84
+ requests wait behind it. They mutate default-mode caller buffers and require
85
+ the original request snapshot, then verify explicit-transfer buffers detach
86
+ only after admission and preserve shared backing-buffer aliases.
87
+
83
88
  Extension ownership tests use a realistic native graph above the former
84
89
  compact-metadata item ceiling. They require one bounded immutable capture,
85
90
  mutation isolation, a direct capture under two seconds, exact worker-result
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circuitjson-toolkit",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Canonical CircuitJSON parsing, project, rendering, query, manufacturing, simulation, and scene contracts",
5
5
  "keywords": [
6
6
  "circuitjson",
@@ -51,6 +51,7 @@
51
51
  "docs/model-format.md",
52
52
  "docs/provenance.md",
53
53
  "docs/release-notes-v1.1.0.md",
54
+ "docs/release-notes-v1.1.1.md",
54
55
  "docs/testing.md",
55
56
  "spec",
56
57
  "LICENSE",
@@ -232,6 +232,8 @@ export class ParserWorkerClient {
232
232
  if (this.#pending.size >= MAX_PENDING_REQUESTS) {
233
233
  throw ParserWorkerClient.#queueError()
234
234
  }
235
+ const wasQueued = Boolean(this.#activeRequestId)
236
+ if (wasQueued) prepared = WorkerRequestData.ownForQueue(prepared)
235
237
  const requestId = `worker-${this.#nextRequestId}`
236
238
  this.#nextRequestId += 1
237
239
 
@@ -245,7 +247,7 @@ export class ParserWorkerClient {
245
247
  onAbort: null,
246
248
  onProgress: prepared.onProgress,
247
249
  previousProgress: null,
248
- attemptToken
250
+ attemptToken: wasQueued ? null : attemptToken
249
251
  }
250
252
  if (prepared.signal) {
251
253
  pending.onAbort = () => this.cancel(requestId)
@@ -50,6 +50,7 @@ const DATA_VIEW_BYTE_OFFSET_GETTER = Object.getOwnPropertyDescriptor(
50
50
  const DATA_VIEW_CONSTRUCTOR = DataView
51
51
  const UINT8_ARRAY_CONSTRUCTOR = Uint8Array
52
52
  const UINT8_ARRAY_SET = Uint8Array.prototype.set
53
+ const STRUCTURED_CLONE = globalThis.structuredClone
53
54
  const MAX_REQUEST_BYTES = 100_000_000
54
55
  const MAX_REQUEST_DEPTH = 64
55
56
  const MAX_REQUEST_VALUES = 100_000
@@ -95,6 +96,32 @@ export class WorkerRequestData {
95
96
  })
96
97
  }
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
+
98
125
  /**
99
126
  * Prepares a bounded worker-owned result and transfers all safe buffers.
100
127
  * @param {unknown} value Worker result.