circuitjson-toolkit 1.1.0 → 1.1.2
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 +16 -0
- package/docs/api.md +12 -1
- package/docs/migration.md +13 -0
- package/docs/release-notes-v1.1.1.md +24 -0
- package/docs/release-notes-v1.1.2.md +20 -0
- package/docs/testing.md +5 -0
- package/package.json +3 -1
- package/src/core/worker/ParserWorkerClient.mjs +3 -1
- package/src/core/worker/WorkerRequestData.mjs +27 -0
- package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +24 -2
- package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +5 -3
package/README.md
CHANGED
|
@@ -37,6 +37,18 @@ 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
|
+
|
|
46
|
+
Version 1.1.2 makes schematic SVG paint deterministic. Open primitives always
|
|
47
|
+
emit `fill="none"`, filled primitives retain authored paint or use the shared
|
|
48
|
+
theme fallback, and generic component and symbol bodies explicitly use the
|
|
49
|
+
ECAD Forge schematic palette. See the
|
|
50
|
+
[1.1.2 release notes](docs/release-notes-v1.1.2.md).
|
|
51
|
+
|
|
40
52
|
Before 1.1.0:
|
|
41
53
|
|
|
42
54
|
```js
|
|
@@ -66,6 +78,8 @@ const model = document.model
|
|
|
66
78
|
- Copy-on-write normalization of supported legacy CircuitJSON aliases through
|
|
67
79
|
`CircuitJsonDocument.normalizeModel()`
|
|
68
80
|
- Deterministic PCB, schematic, and BOM renderers
|
|
81
|
+
- Explicit schematic SVG stroke/fill paint that does not depend on browser
|
|
82
|
+
defaults
|
|
69
83
|
- Asset-backed `schematic_image` rows and hierarchical
|
|
70
84
|
`schematic_sheet_symbol` rows with shared bounds and SVG behavior
|
|
71
85
|
- Reusable exact PCB interaction and spatial indexes
|
|
@@ -294,6 +308,8 @@ copy while keeping sync, direct async, and worker results mutation-isolated.
|
|
|
294
308
|
- [Model and envelope format](docs/model-format.md)
|
|
295
309
|
- [Testing and downstream conformance](docs/testing.md)
|
|
296
310
|
- [1.1.0 release notes](docs/release-notes-v1.1.0.md)
|
|
311
|
+
- [1.1.1 release notes](docs/release-notes-v1.1.1.md)
|
|
312
|
+
- [1.1.2 release notes](docs/release-notes-v1.1.2.md)
|
|
297
313
|
- [Library scope](spec/library-scope.md)
|
|
298
314
|
|
|
299
315
|
## 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
|
|
@@ -324,7 +330,12 @@ the same option names across toolkits. Canonical `schematic_image` rows resolve
|
|
|
324
330
|
their exact `asset_id` from `document.assets`; full asset mode renders the
|
|
325
331
|
payload, while metadata-only or unresolved assets are omitted without a
|
|
326
332
|
placeholder. `schematic_sheet_symbol` renders a hierarchical child box and is
|
|
327
|
-
never treated as a selectable `schematic_sheet` page.
|
|
333
|
+
never treated as a selectable `schematic_sheet` page. Since 1.1.2, every
|
|
334
|
+
primitive emits explicit SVG fill paint: open arcs, polylines, and shapes use
|
|
335
|
+
`fill="none"`; filled primitives keep valid authored paint or fall back to
|
|
336
|
+
`var(--schematic-fill-color, #f1d8bd)`. Generic component and symbol bodies use
|
|
337
|
+
the same fill variable plus
|
|
338
|
+
`var(--schematic-default-ink-color, #008aa3)` for their stroke.
|
|
328
339
|
|
|
329
340
|
### `BomTableRenderer.render(document, options?)`
|
|
330
341
|
|
package/docs/migration.md
CHANGED
|
@@ -92,6 +92,19 @@ 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
|
+
|
|
101
|
+
Since 1.1.2, schematic SVG renderers no longer inherit the browser's default
|
|
102
|
+
black fill. Open primitives always render with `fill="none"`. A primitive with
|
|
103
|
+
`is_filled: true` uses its valid authored `fill_color`/`fillColor`, or the
|
|
104
|
+
shared `--schematic-fill-color` theme variable when no paint is authored.
|
|
105
|
+
Generic component and symbol bodies explicitly use the shared schematic fill
|
|
106
|
+
and ink variables. Public names, parameters, and return shapes are unchanged.
|
|
107
|
+
|
|
95
108
|
## Package subpaths
|
|
96
109
|
|
|
97
110
|
- `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`.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# circuitjson-toolkit 1.1.2
|
|
2
|
+
|
|
3
|
+
## Deterministic schematic paint
|
|
4
|
+
|
|
5
|
+
This patch removes browser-dependent SVG fill behavior from the canonical
|
|
6
|
+
schematic renderer.
|
|
7
|
+
|
|
8
|
+
- Open arcs, polylines, and unfilled shapes now emit `fill="none"` explicitly,
|
|
9
|
+
so they cannot become black-filled in a browser or host stylesheet.
|
|
10
|
+
- Filled schematic primitives retain a valid authored `fill_color` or
|
|
11
|
+
`fillColor`. When `is_filled: true` has no authored paint, rendering uses
|
|
12
|
+
`var(--schematic-fill-color, #f1d8bd)`.
|
|
13
|
+
- Generic `schematic_component` and `schematic_symbol` bodies now emit the
|
|
14
|
+
shared schematic fill and default-ink theme variables explicitly.
|
|
15
|
+
- Filled paths render as polygons; open paths remain polylines. This preserves
|
|
16
|
+
geometry while making fill intent unambiguous.
|
|
17
|
+
- Unsafe authored paints remain rejected by the existing SVG paint sanitizer.
|
|
18
|
+
|
|
19
|
+
Public exports, method names, parameters, CircuitJSON document envelopes, and
|
|
20
|
+
renderer return shapes are unchanged from 1.1.1.
|
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.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Canonical CircuitJSON parsing, project, rendering, query, manufacturing, simulation, and scene contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"circuitjson",
|
|
@@ -51,6 +51,8 @@
|
|
|
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",
|
|
55
|
+
"docs/release-notes-v1.1.2.md",
|
|
54
56
|
"docs/testing.md",
|
|
55
57
|
"spec",
|
|
56
58
|
"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.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { SafeXmlText } from './SafeXmlText.mjs'
|
|
2
2
|
|
|
3
|
+
const DEFAULT_FILLED_PAINT = 'var(--schematic-fill-color, #f1d8bd)'
|
|
4
|
+
|
|
3
5
|
/**
|
|
4
6
|
* Builds SVG drawing attributes for schematic primitive rows.
|
|
5
7
|
*/
|
|
@@ -46,11 +48,16 @@ export class CircuitJsonSchematicSvgPrimitiveAttributes {
|
|
|
46
48
|
'"'
|
|
47
49
|
)
|
|
48
50
|
}
|
|
49
|
-
|
|
51
|
+
const resolvedFill = CircuitJsonSchematicSvgPrimitiveAttributes.#fill(
|
|
52
|
+
element,
|
|
53
|
+
options,
|
|
54
|
+
safeFill
|
|
55
|
+
)
|
|
56
|
+
if (resolvedFill) {
|
|
50
57
|
attributes.push(
|
|
51
58
|
'fill="' +
|
|
52
59
|
CircuitJsonSchematicSvgPrimitiveAttributes.#escapeHtml(
|
|
53
|
-
|
|
60
|
+
resolvedFill
|
|
54
61
|
) +
|
|
55
62
|
'"'
|
|
56
63
|
)
|
|
@@ -71,6 +78,21 @@ export class CircuitJsonSchematicSvgPrimitiveAttributes {
|
|
|
71
78
|
return attributes.length ? ' ' + attributes.join(' ') : ''
|
|
72
79
|
}
|
|
73
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Resolves deterministic fill semantics for one SVG primitive.
|
|
83
|
+
* @param {object} element CircuitJSON element row.
|
|
84
|
+
* @param {{ fill?: boolean }} options Attribute options.
|
|
85
|
+
* @param {string} safeFill Valid authored fill paint.
|
|
86
|
+
* @returns {string} Explicit SVG fill paint.
|
|
87
|
+
*/
|
|
88
|
+
static #fill(element, options, safeFill) {
|
|
89
|
+
if (options.fill === false || element?.is_filled === false) {
|
|
90
|
+
return 'none'
|
|
91
|
+
}
|
|
92
|
+
if (safeFill) return safeFill
|
|
93
|
+
return element?.is_filled === true ? DEFAULT_FILLED_PAINT : 'none'
|
|
94
|
+
}
|
|
95
|
+
|
|
74
96
|
/**
|
|
75
97
|
* Resolves a finite number.
|
|
76
98
|
* @param {unknown} value Number candidate.
|
|
@@ -151,7 +151,7 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
151
151
|
component.schematic_component_id || ''
|
|
152
152
|
) +
|
|
153
153
|
'">' +
|
|
154
|
-
'<rect class="schematic-component__body" x="' +
|
|
154
|
+
'<rect class="schematic-component__body" fill="var(--schematic-fill-color, #f1d8bd)" stroke="var(--schematic-default-ink-color, #008aa3)" x="' +
|
|
155
155
|
CircuitJsonSchematicSvgRenderer.#formatNumber(x) +
|
|
156
156
|
'" y="' +
|
|
157
157
|
CircuitJsonSchematicSvgRenderer.#formatNumber(y) +
|
|
@@ -218,7 +218,7 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
218
218
|
CircuitJsonSchematicSvgRenderer.#escapeHtml(
|
|
219
219
|
symbol.schematic_symbol_id || ''
|
|
220
220
|
) +
|
|
221
|
-
'"><rect class="schematic-symbol__body" x="' +
|
|
221
|
+
'"><rect class="schematic-symbol__body" fill="var(--schematic-fill-color, #f1d8bd)" stroke="var(--schematic-default-ink-color, #008aa3)" x="' +
|
|
222
222
|
CircuitJsonSchematicSvgRenderer.#formatNumber(x) +
|
|
223
223
|
'" y="' +
|
|
224
224
|
CircuitJsonSchematicSvgRenderer.#formatNumber(y) +
|
|
@@ -517,7 +517,9 @@ export class CircuitJsonSchematicSvgRenderer {
|
|
|
517
517
|
CircuitJsonSchematicSvgRenderer.#pointsAttribute(points)
|
|
518
518
|
) +
|
|
519
519
|
'"' +
|
|
520
|
-
CircuitJsonSchematicSvgPrimitiveAttributes.attributes(element
|
|
520
|
+
CircuitJsonSchematicSvgPrimitiveAttributes.attributes(element, {
|
|
521
|
+
fill: tag === 'polygon'
|
|
522
|
+
}) +
|
|
521
523
|
'></' +
|
|
522
524
|
tag +
|
|
523
525
|
'>'
|