circuitjson-toolkit 1.1.1 → 1.2.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 +18 -0
- package/docs/api.md +21 -2
- package/docs/migration.md +7 -0
- package/docs/model-format.md +34 -0
- package/docs/release-notes-v1.1.2.md +20 -0
- package/docs/release-notes-v1.2.0.md +33 -0
- package/package.json +3 -1
- package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +71 -7
- package/src/core/CircuitJsonToolkitElementSchema.mjs +127 -0
- package/src/core/CircuitJsonUpstreamValidator.mjs +10 -5
- package/src/core/context/StructuredDataSnapshot.mjs +1 -1
- package/src/core/worker/WorkerRequestData.mjs +1 -1
- package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +24 -2
- package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +5 -3
package/README.md
CHANGED
|
@@ -43,6 +43,18 @@ caller-buffer mutation; explicit transfers detach exact buffers immediately
|
|
|
43
43
|
after admission. See the
|
|
44
44
|
[1.1.1 release notes](docs/release-notes-v1.1.1.md).
|
|
45
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
|
+
|
|
52
|
+
Version 1.2.0 extends the canonical PCB contract with validated source text
|
|
53
|
+
fidelity, rotated drilled-pad bounds, and rounded-pad corner radii. Worker and
|
|
54
|
+
metadata ownership traversals now support deeply nested, valid ECAD documents
|
|
55
|
+
up to 256 levels. See the
|
|
56
|
+
[1.2.0 release notes](docs/release-notes-v1.2.0.md).
|
|
57
|
+
|
|
46
58
|
Before 1.1.0:
|
|
47
59
|
|
|
48
60
|
```js
|
|
@@ -72,8 +84,12 @@ const model = document.model
|
|
|
72
84
|
- Copy-on-write normalization of supported legacy CircuitJSON aliases through
|
|
73
85
|
`CircuitJsonDocument.normalizeModel()`
|
|
74
86
|
- Deterministic PCB, schematic, and BOM renderers
|
|
87
|
+
- Explicit schematic SVG stroke/fill paint that does not depend on browser
|
|
88
|
+
defaults
|
|
75
89
|
- Asset-backed `schematic_image` rows and hierarchical
|
|
76
90
|
`schematic_sheet_symbol` rows with shared bounds and SVG behavior
|
|
91
|
+
- Validated PCB text source-fidelity fields for independent font dimensions,
|
|
92
|
+
exact nine-position anchors, visibility, layer provenance, and native type
|
|
77
93
|
- Reusable exact PCB interaction and spatial indexes
|
|
78
94
|
- Query, manufacturing, and injected simulation services
|
|
79
95
|
- Data-only, millimeter-based, right-handed Z-up PCB 3D scenes
|
|
@@ -301,6 +317,8 @@ copy while keeping sync, direct async, and worker results mutation-isolated.
|
|
|
301
317
|
- [Testing and downstream conformance](docs/testing.md)
|
|
302
318
|
- [1.1.0 release notes](docs/release-notes-v1.1.0.md)
|
|
303
319
|
- [1.1.1 release notes](docs/release-notes-v1.1.1.md)
|
|
320
|
+
- [1.1.2 release notes](docs/release-notes-v1.1.2.md)
|
|
321
|
+
- [1.2.0 release notes](docs/release-notes-v1.2.0.md)
|
|
304
322
|
- [Library scope](spec/library-scope.md)
|
|
305
323
|
|
|
306
324
|
## Package scope
|
package/docs/api.md
CHANGED
|
@@ -144,6 +144,15 @@ The canonical union is the pinned upstream schema plus the source-neutral
|
|
|
144
144
|
snapshot, `canonicalElementTypes()` for all accepted types, and
|
|
145
145
|
`extensionElementTypes()` for the toolkit-owned additions.
|
|
146
146
|
|
|
147
|
+
Pinned PCB text rows may also retain the validated source-fidelity fields
|
|
148
|
+
`font_width`, `font_height`, `stroke_width`, `source_anchor_alignment`,
|
|
149
|
+
`is_hidden`, `source_layer`, `source_type`, and `source_text_kind`. Board note
|
|
150
|
+
rotation uses `ccw_rotation`; fabrication note mirroring uses `is_mirrored`.
|
|
151
|
+
Canonical upstream properties remain authoritative when they exist. In
|
|
152
|
+
particular, `pcb_note_text.anchor_alignment` keeps the narrower upstream enum,
|
|
153
|
+
while `source_anchor_alignment` can preserve the exact nine-position source
|
|
154
|
+
anchor for lossless rendering.
|
|
155
|
+
|
|
147
156
|
The projection covers legacy schematic table row/column/span geometry, PCB
|
|
148
157
|
artwork `points`/`width` paths, pad-clearance diagnostic relations, courtyard
|
|
149
158
|
line/path/polygon forms, outer-layer aliases, and retained stroke dash fields.
|
|
@@ -157,7 +166,12 @@ rendering and interaction consumers. Polygon `pad_outline` points determine
|
|
|
157
166
|
rotation-local outer width and height; pill drill width, height, diameter, and
|
|
158
167
|
board-space rotation remain distinct from outer-pad rotation. Legal
|
|
159
168
|
`outer_width`, `outer_height`, `rect_ccw_rotation`, and `hole_ccw_rotation`
|
|
160
|
-
variants are preserved.
|
|
169
|
+
variants are preserved. The returned geometry includes the parsed rectangular
|
|
170
|
+
`cornerRadius`. Rotated rectangular bounds use the exact visible
|
|
171
|
+
rounded-rectangle support dimensions, with the effective radius clamped from
|
|
172
|
+
zero through half the smaller outer dimension. A zero radius therefore retains
|
|
173
|
+
sharp-rectangle bounds, while a half-minor-dimension radius has the same support
|
|
174
|
+
extent as a pill. Import this retained source-neutral helper from
|
|
161
175
|
`circuitjson-toolkit/extensions`.
|
|
162
176
|
|
|
163
177
|
Packed release checks reject any missing or additional root export.
|
|
@@ -330,7 +344,12 @@ the same option names across toolkits. Canonical `schematic_image` rows resolve
|
|
|
330
344
|
their exact `asset_id` from `document.assets`; full asset mode renders the
|
|
331
345
|
payload, while metadata-only or unresolved assets are omitted without a
|
|
332
346
|
placeholder. `schematic_sheet_symbol` renders a hierarchical child box and is
|
|
333
|
-
never treated as a selectable `schematic_sheet` page.
|
|
347
|
+
never treated as a selectable `schematic_sheet` page. Since 1.1.2, every
|
|
348
|
+
primitive emits explicit SVG fill paint: open arcs, polylines, and shapes use
|
|
349
|
+
`fill="none"`; filled primitives keep valid authored paint or fall back to
|
|
350
|
+
`var(--schematic-fill-color, #f1d8bd)`. Generic component and symbol bodies use
|
|
351
|
+
the same fill variable plus
|
|
352
|
+
`var(--schematic-default-ink-color, #008aa3)` for their stroke.
|
|
334
353
|
|
|
335
354
|
### `BomTableRenderer.render(document, options?)`
|
|
336
355
|
|
package/docs/migration.md
CHANGED
|
@@ -98,6 +98,13 @@ mutations cannot alter a queued parser input, project entry, or asset. With
|
|
|
98
98
|
`transferInput: true`, exact transferable buffers detach at admission;
|
|
99
99
|
partial, resizable, and shared ranges continue to use isolated copies.
|
|
100
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
|
+
|
|
101
108
|
## Package subpaths
|
|
102
109
|
|
|
103
110
|
- `circuitjson-toolkit/parser`
|
package/docs/model-format.md
CHANGED
|
@@ -119,6 +119,40 @@ validation/proof boundary. Table cell geometry, PCB artwork routes,
|
|
|
119
119
|
pad-clearance relations, courtyards, layer aliases, and stroke dashes are
|
|
120
120
|
derived structurally and never from a source filename or fixture identity.
|
|
121
121
|
|
|
122
|
+
### PCB text source fidelity
|
|
123
|
+
|
|
124
|
+
`pcb_note_text`, `pcb_fabrication_note_text`, and `pcb_silkscreen_text` retain
|
|
125
|
+
canonical upstream fields first. Source formats with independent width and
|
|
126
|
+
height, edge-center anchors, native layer names, or hidden-text state may add:
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
{
|
|
130
|
+
type: 'pcb_note_text',
|
|
131
|
+
pcb_note_text_id: 'board_text_1',
|
|
132
|
+
text: 'BOARD MARK',
|
|
133
|
+
anchor_position: { x: 12.5, y: 4.25 },
|
|
134
|
+
layer: 'bottom',
|
|
135
|
+
ccw_rotation: 28,
|
|
136
|
+
font_size: 1.2,
|
|
137
|
+
font_width: 0.8,
|
|
138
|
+
font_height: 1.2,
|
|
139
|
+
stroke_width: 0.12,
|
|
140
|
+
anchor_alignment: 'center',
|
|
141
|
+
source_anchor_alignment: 'center_left',
|
|
142
|
+
is_mirrored_from_top_view: true,
|
|
143
|
+
is_hidden: false,
|
|
144
|
+
source_layer: 'B.SilkS',
|
|
145
|
+
source_type: 'gr_text'
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The extension fields are validated and preserved through immutable document
|
|
150
|
+
preparation. Lengths accept the same numeric millimeter or unit-suffixed style
|
|
151
|
+
as canonical dimensions. `source_anchor_alignment` accepts all nine standard
|
|
152
|
+
anchor positions. The narrower upstream anchor on board and fabrication notes
|
|
153
|
+
must remain `center` or a corner; renderers use the source anchor when they
|
|
154
|
+
need the exact edge-center placement.
|
|
155
|
+
|
|
122
156
|
## Assets and diagnostics
|
|
123
157
|
|
|
124
158
|
Assets have exact `id`, `kind`, `name`, `mediaType`, `byteLength`, `data`, and
|
|
@@ -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.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# circuitjson-toolkit 1.2.0
|
|
2
|
+
|
|
3
|
+
## Canonical PCB fidelity
|
|
4
|
+
|
|
5
|
+
This minor release expands the shared CircuitJSON contract used by the Gerber,
|
|
6
|
+
Altium, KiCad, and 3D viewer packages. Existing canonical fields remain
|
|
7
|
+
authoritative, while source formats can retain exact rendering information in
|
|
8
|
+
validated, source-neutral extension fields.
|
|
9
|
+
|
|
10
|
+
### API additions
|
|
11
|
+
|
|
12
|
+
- `pcb_note_text`, `pcb_fabrication_note_text`, and `pcb_silkscreen_text` may
|
|
13
|
+
retain independent `font_width` and `font_height`, `stroke_width`, the exact
|
|
14
|
+
nine-position `source_anchor_alignment`, `is_hidden`, `source_layer`,
|
|
15
|
+
`source_type`, and `source_text_kind`.
|
|
16
|
+
- Board-note `ccw_rotation` and fabrication-note `is_mirrored` are validated
|
|
17
|
+
when present, so downstream renderers can consume the original orientation
|
|
18
|
+
without source-format adapters.
|
|
19
|
+
- `CircuitJsonPcbHolePrimitiveModel.build()` now returns `cornerRadius` and
|
|
20
|
+
computes board-space bounds for rotated rectangles and pills instead of
|
|
21
|
+
treating their local width and height as axis-aligned.
|
|
22
|
+
|
|
23
|
+
### Behavior and performance
|
|
24
|
+
|
|
25
|
+
- Structured metadata snapshots and worker request graphs accept valid nesting
|
|
26
|
+
up to 256 levels, matching the deeper native data graphs emitted by ECAD
|
|
27
|
+
parsers while retaining the existing item and byte limits.
|
|
28
|
+
- The new fields pass through the existing immutable document ownership and
|
|
29
|
+
validation boundaries; no viewer or host-app workaround is required.
|
|
30
|
+
|
|
31
|
+
No existing public class, method, package subpath, parameter, or document
|
|
32
|
+
envelope is removed in this release. Consumers that exhaustively validate PCB
|
|
33
|
+
text or primitive return objects should accept the additive fields above.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circuitjson-toolkit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Canonical CircuitJSON parsing, project, rendering, query, manufacturing, simulation, and scene contracts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"circuitjson",
|
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
"docs/provenance.md",
|
|
53
53
|
"docs/release-notes-v1.1.0.md",
|
|
54
54
|
"docs/release-notes-v1.1.1.md",
|
|
55
|
+
"docs/release-notes-v1.1.2.md",
|
|
56
|
+
"docs/release-notes-v1.2.0.md",
|
|
55
57
|
"docs/testing.md",
|
|
56
58
|
"spec",
|
|
57
59
|
"LICENSE",
|
|
@@ -29,19 +29,25 @@ export class CircuitJsonPcbHolePrimitiveModel {
|
|
|
29
29
|
shape,
|
|
30
30
|
size
|
|
31
31
|
)
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
const cornerRadius = CircuitJsonUnits.length(
|
|
33
|
+
element.rect_border_radius ?? element.corner_radius,
|
|
34
|
+
0
|
|
35
|
+
)
|
|
36
|
+
const bounds = CircuitJsonPcbHolePrimitiveModel.#outerBounds(
|
|
37
|
+
center,
|
|
38
|
+
size,
|
|
39
|
+
shape,
|
|
40
|
+
rotation,
|
|
41
|
+
cornerRadius,
|
|
42
|
+
points
|
|
43
|
+
)
|
|
39
44
|
|
|
40
45
|
return {
|
|
41
46
|
shape,
|
|
42
47
|
width: size.width,
|
|
43
48
|
height: size.height,
|
|
44
49
|
diameter: Math.max(size.width, size.height),
|
|
50
|
+
cornerRadius,
|
|
45
51
|
holeShape: hole.shape,
|
|
46
52
|
holeDiameter: hole.diameter,
|
|
47
53
|
holeWidth: hole.width,
|
|
@@ -123,6 +129,64 @@ export class CircuitJsonPcbHolePrimitiveModel {
|
|
|
123
129
|
}
|
|
124
130
|
}
|
|
125
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Resolves the board-space axis-aligned bounds of the visible outer shape.
|
|
134
|
+
* @param {{ x: number, y: number }} center Shape center.
|
|
135
|
+
* @param {{ width: number, height: number }} size Local shape size.
|
|
136
|
+
* @param {'circle' | 'pill' | 'polygon' | 'rect'} shape Shape kind.
|
|
137
|
+
* @param {number} rotation Counter-clockwise rotation in degrees.
|
|
138
|
+
* @param {number} cornerRadius Parsed rectangular corner radius.
|
|
139
|
+
* @param {{ x: number, y: number }[]} points Polygon points.
|
|
140
|
+
* @returns {object} Board-space axis-aligned bounds.
|
|
141
|
+
*/
|
|
142
|
+
static #outerBounds(center, size, shape, rotation, cornerRadius, points) {
|
|
143
|
+
if (points.length) {
|
|
144
|
+
return CircuitJsonPcbPrimitiveGeometry.pointsBounds(points)
|
|
145
|
+
}
|
|
146
|
+
if (shape === 'circle' || rotation === 0) {
|
|
147
|
+
return CircuitJsonPcbPrimitiveGeometry.centerBounds(
|
|
148
|
+
center,
|
|
149
|
+
size.width,
|
|
150
|
+
size.height
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const radians = (rotation * Math.PI) / 180
|
|
155
|
+
if (shape === 'pill') {
|
|
156
|
+
const minor = Math.min(size.width, size.height)
|
|
157
|
+
const lineLength = Math.max(size.width, size.height) - minor
|
|
158
|
+
const axisRadians =
|
|
159
|
+
radians + (size.height > size.width ? Math.PI / 2 : 0)
|
|
160
|
+
const width = Math.abs(Math.cos(axisRadians)) * lineLength + minor
|
|
161
|
+
const height = Math.abs(Math.sin(axisRadians)) * lineLength + minor
|
|
162
|
+
return CircuitJsonPcbPrimitiveGeometry.centerBounds(
|
|
163
|
+
center,
|
|
164
|
+
width,
|
|
165
|
+
height
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const radius = Math.max(
|
|
170
|
+
0,
|
|
171
|
+
Math.min(cornerRadius, Math.min(size.width, size.height) / 2)
|
|
172
|
+
)
|
|
173
|
+
const innerWidth = size.width - radius * 2
|
|
174
|
+
const innerHeight = size.height - radius * 2
|
|
175
|
+
const width =
|
|
176
|
+
Math.abs(Math.cos(radians)) * innerWidth +
|
|
177
|
+
Math.abs(Math.sin(radians)) * innerHeight +
|
|
178
|
+
radius * 2
|
|
179
|
+
const height =
|
|
180
|
+
Math.abs(Math.sin(radians)) * innerWidth +
|
|
181
|
+
Math.abs(Math.cos(radians)) * innerHeight +
|
|
182
|
+
radius * 2
|
|
183
|
+
return CircuitJsonPcbPrimitiveGeometry.centerBounds(
|
|
184
|
+
center,
|
|
185
|
+
width,
|
|
186
|
+
height
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
126
190
|
/**
|
|
127
191
|
* Resolves polygonal outer pad points.
|
|
128
192
|
* @param {object} element Drilled PCB element.
|
|
@@ -1,7 +1,25 @@
|
|
|
1
|
+
import { optionalAngle, optionalLength } from './CircuitJsonUnitParsers.mjs'
|
|
2
|
+
|
|
1
3
|
const TOOLKIT_ELEMENT_TYPES = new Set([
|
|
2
4
|
'schematic_image',
|
|
3
5
|
'schematic_sheet_symbol'
|
|
4
6
|
])
|
|
7
|
+
const PCB_TEXT_EXTENSION_TYPES = new Set([
|
|
8
|
+
'pcb_note_text',
|
|
9
|
+
'pcb_fabrication_note_text',
|
|
10
|
+
'pcb_silkscreen_text'
|
|
11
|
+
])
|
|
12
|
+
const PCB_TEXT_ANCHOR_ALIGNMENTS = new Set([
|
|
13
|
+
'top_left',
|
|
14
|
+
'top_center',
|
|
15
|
+
'top_right',
|
|
16
|
+
'center_left',
|
|
17
|
+
'center',
|
|
18
|
+
'center_right',
|
|
19
|
+
'bottom_left',
|
|
20
|
+
'bottom_center',
|
|
21
|
+
'bottom_right'
|
|
22
|
+
])
|
|
5
23
|
|
|
6
24
|
/**
|
|
7
25
|
* Validates canonical toolkit element types that are newer than the pinned
|
|
@@ -45,6 +63,22 @@ export class CircuitJsonToolkitElementSchema {
|
|
|
45
63
|
]
|
|
46
64
|
}
|
|
47
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Validates source-fidelity fields retained on pinned upstream elements.
|
|
68
|
+
* @param {Record<string, any>} value Element value.
|
|
69
|
+
* @param {string} type Element type.
|
|
70
|
+
* @param {string} [location] Human-readable location suffix.
|
|
71
|
+
* @returns {string[]} Empty on success or one validation error.
|
|
72
|
+
*/
|
|
73
|
+
static validateExtensions(value, type, location = '') {
|
|
74
|
+
if (!PCB_TEXT_EXTENSION_TYPES.has(type)) return []
|
|
75
|
+
return CircuitJsonToolkitElementSchema.#pcbTextExtensions(value, type)
|
|
76
|
+
? []
|
|
77
|
+
: [
|
|
78
|
+
`CircuitJSON element ${type}${location} does not match the canonical toolkit extension schema.`
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
|
|
48
82
|
/**
|
|
49
83
|
* Validates an asset-backed schematic image.
|
|
50
84
|
* @param {Record<string, any>} value Image element.
|
|
@@ -156,6 +190,68 @@ export class CircuitJsonToolkitElementSchema {
|
|
|
156
190
|
)
|
|
157
191
|
}
|
|
158
192
|
|
|
193
|
+
/**
|
|
194
|
+
* Validates exact PCB text dimensions, alignment, visibility, and source provenance.
|
|
195
|
+
* @param {Record<string, any>} value Text element.
|
|
196
|
+
* @param {string} type Text element type.
|
|
197
|
+
* @returns {boolean} Whether the extension fields match their contract.
|
|
198
|
+
*/
|
|
199
|
+
static #pcbTextExtensions(value, type) {
|
|
200
|
+
for (const field of ['font_width', 'font_height']) {
|
|
201
|
+
if (
|
|
202
|
+
value[field] !== undefined &&
|
|
203
|
+
!CircuitJsonToolkitElementSchema.#positiveLength(value[field])
|
|
204
|
+
) {
|
|
205
|
+
return false
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (
|
|
209
|
+
value.stroke_width !== undefined &&
|
|
210
|
+
!CircuitJsonToolkitElementSchema.#nonNegativeLength(
|
|
211
|
+
value.stroke_width
|
|
212
|
+
)
|
|
213
|
+
) {
|
|
214
|
+
return false
|
|
215
|
+
}
|
|
216
|
+
for (const field of [
|
|
217
|
+
'source_layer',
|
|
218
|
+
'source_type',
|
|
219
|
+
'source_text_kind'
|
|
220
|
+
]) {
|
|
221
|
+
if (
|
|
222
|
+
value[field] !== undefined &&
|
|
223
|
+
!CircuitJsonToolkitElementSchema.#requiredString(value[field])
|
|
224
|
+
) {
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (
|
|
229
|
+
value.source_anchor_alignment !== undefined &&
|
|
230
|
+
!PCB_TEXT_ANCHOR_ALIGNMENTS.has(value.source_anchor_alignment)
|
|
231
|
+
) {
|
|
232
|
+
return false
|
|
233
|
+
}
|
|
234
|
+
if (
|
|
235
|
+
value.is_hidden !== undefined &&
|
|
236
|
+
typeof value.is_hidden !== 'boolean'
|
|
237
|
+
) {
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
if (
|
|
241
|
+
type === 'pcb_note_text' &&
|
|
242
|
+
value.ccw_rotation !== undefined &&
|
|
243
|
+
(!CircuitJsonToolkitElementSchema.#unitScalar(value.ccw_rotation) ||
|
|
244
|
+
optionalAngle(value.ccw_rotation) === null)
|
|
245
|
+
) {
|
|
246
|
+
return false
|
|
247
|
+
}
|
|
248
|
+
return !(
|
|
249
|
+
type === 'pcb_fabrication_note_text' &&
|
|
250
|
+
value.is_mirrored !== undefined &&
|
|
251
|
+
typeof value.is_mirrored !== 'boolean'
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
159
255
|
/**
|
|
160
256
|
* Validates a required non-empty string.
|
|
161
257
|
* @param {unknown} value Candidate.
|
|
@@ -183,6 +279,37 @@ export class CircuitJsonToolkitElementSchema {
|
|
|
183
279
|
return CircuitJsonToolkitElementSchema.#number(value) && value > 0
|
|
184
280
|
}
|
|
185
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Validates a positive CircuitJSON length with optional unit suffix.
|
|
284
|
+
* @param {unknown} value Candidate.
|
|
285
|
+
* @returns {boolean} Whether the length is positive.
|
|
286
|
+
*/
|
|
287
|
+
static #positiveLength(value) {
|
|
288
|
+
if (!CircuitJsonToolkitElementSchema.#unitScalar(value)) return false
|
|
289
|
+
const length = optionalLength(value)
|
|
290
|
+
return length !== null && length > 0
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Validates a non-negative CircuitJSON length with optional unit suffix.
|
|
295
|
+
* @param {unknown} value Candidate.
|
|
296
|
+
* @returns {boolean} Whether the length is non-negative.
|
|
297
|
+
*/
|
|
298
|
+
static #nonNegativeLength(value) {
|
|
299
|
+
if (!CircuitJsonToolkitElementSchema.#unitScalar(value)) return false
|
|
300
|
+
const length = optionalLength(value)
|
|
301
|
+
return length !== null && length >= 0
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Returns whether a unit field is a primitive parser input.
|
|
306
|
+
* @param {unknown} value Candidate.
|
|
307
|
+
* @returns {boolean} Whether the value can be parsed without coercion.
|
|
308
|
+
*/
|
|
309
|
+
static #unitScalar(value) {
|
|
310
|
+
return typeof value === 'number' || typeof value === 'string'
|
|
311
|
+
}
|
|
312
|
+
|
|
186
313
|
/**
|
|
187
314
|
* Validates one finite point.
|
|
188
315
|
* @param {unknown} value Candidate.
|
|
@@ -31,11 +31,16 @@ export class CircuitJsonUpstreamValidator {
|
|
|
31
31
|
type === 'source_net'
|
|
32
32
|
? CircuitJsonUpstreamValidator.#sourceNet(value)
|
|
33
33
|
: CircuitJsonUpstreamValidator.#matches(schema, value)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
if (!matches) {
|
|
35
|
+
return [
|
|
36
|
+
`CircuitJSON element ${type}${location} does not match the pinned upstream schema.`
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
return CircuitJsonToolkitElementSchema.validateExtensions(
|
|
40
|
+
value,
|
|
41
|
+
type,
|
|
42
|
+
location
|
|
43
|
+
)
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BinaryDataSnapshot } from './BinaryDataSnapshot.mjs'
|
|
2
2
|
|
|
3
|
-
const METADATA_MAX_DEPTH =
|
|
3
|
+
const METADATA_MAX_DEPTH = 256
|
|
4
4
|
const METADATA_MAX_ITEMS = 100_000
|
|
5
5
|
const UNBOUNDED_METADATA_BYTES = Number.MAX_SAFE_INTEGER
|
|
6
6
|
const DATE_GET_TIME = Date.prototype.getTime
|
|
@@ -52,7 +52,7 @@ const UINT8_ARRAY_CONSTRUCTOR = Uint8Array
|
|
|
52
52
|
const UINT8_ARRAY_SET = Uint8Array.prototype.set
|
|
53
53
|
const STRUCTURED_CLONE = globalThis.structuredClone
|
|
54
54
|
const MAX_REQUEST_BYTES = 100_000_000
|
|
55
|
-
const MAX_REQUEST_DEPTH =
|
|
55
|
+
const MAX_REQUEST_DEPTH = 256
|
|
56
56
|
const MAX_REQUEST_VALUES = 100_000
|
|
57
57
|
const MAX_RESULT_BYTES = 250_000_000
|
|
58
58
|
const MAX_RESULT_VALUES = 2_000_000
|
|
@@ -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
|
'>'
|