circuitjson-toolkit 1.0.17 → 1.1.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.
Files changed (154) hide show
  1. package/NOTICE.md +31 -0
  2. package/README.md +267 -107
  3. package/docs/api.md +501 -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 +120 -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/testing.md +117 -7
  14. package/package.json +31 -5
  15. package/spec/api-baseline-v1.0.17.json +1 -0
  16. package/spec/baseline-provenance-v1.0.17.json +7 -0
  17. package/spec/circuitjson-schema-snapshot.json +321 -0
  18. package/spec/circuitjson-schema-source.json +28 -0
  19. package/spec/feature-preservation.json +1 -0
  20. package/spec/library-scope.md +27 -20
  21. package/src/capabilities.mjs +1 -0
  22. package/src/core/ArchiveEntryPath.mjs +93 -0
  23. package/src/core/ArchiveLimits.mjs +31 -0
  24. package/src/core/ArchiveLimitsValidator.mjs +107 -0
  25. package/src/core/AsyncInputOwnership.mjs +56 -0
  26. package/src/core/AttachedValueLimits.mjs +67 -0
  27. package/src/core/CircuitJsonDiagnosticIndexer.mjs +184 -0
  28. package/src/core/CircuitJsonDocument.mjs +19 -61
  29. package/src/core/CircuitJsonElementTypes.mjs +10 -0
  30. package/src/core/CircuitJsonElementValidator.mjs +98 -847
  31. package/src/core/CircuitJsonIndexer.mjs +274 -194
  32. package/src/core/CircuitJsonManufacturingBuilder.mjs +167 -164
  33. package/src/core/CircuitJsonParser.mjs +75 -13
  34. package/src/core/CircuitJsonPcbClearanceDiagnostics.mjs +12 -6
  35. package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +108 -10
  36. package/src/core/CircuitJsonPcbPadPrimitiveModel.mjs +1 -1
  37. package/src/core/CircuitJsonPcbPrimitiveArtwork.mjs +44 -38
  38. package/src/core/CircuitJsonPcbPrimitiveBuilder.mjs +146 -28
  39. package/src/core/CircuitJsonPcbPrimitiveFields.mjs +70 -4
  40. package/src/core/CircuitJsonPcbPrimitiveIndex.mjs +18 -2
  41. package/src/core/CircuitJsonPcbPrimitiveOverlays.mjs +26 -9
  42. package/src/core/CircuitJsonPcbZonePrimitiveBuilder.mjs +7 -6
  43. package/src/core/CircuitJsonSerializedInputAudit.mjs +87 -0
  44. package/src/core/CircuitJsonSourceMetadata.mjs +5 -1
  45. package/src/core/CircuitJsonSupportMatrixBuilder.mjs +3 -1
  46. package/src/core/CircuitJsonToolkitElementSchema.mjs +218 -0
  47. package/src/core/CircuitJsonUnitParsers.mjs +101 -0
  48. package/src/core/CircuitJsonUnits.mjs +13 -87
  49. package/src/core/CircuitJsonUpstreamSchema.mjs +9 -0
  50. package/src/core/CircuitJsonUpstreamValidator.mjs +418 -0
  51. package/src/core/CircuitJsonValidationUnits.mjs +6 -0
  52. package/src/core/ManufacturingService.mjs +323 -0
  53. package/src/core/Parser.mjs +343 -0
  54. package/src/core/ParserOptions.mjs +333 -0
  55. package/src/core/PcbBoundsSelectionModel.mjs +55 -19
  56. package/src/core/PcbDiagnosticFocusModel.mjs +42 -11
  57. package/src/core/PcbInteractionIndex.mjs +368 -0
  58. package/src/core/PcbInteractionPrimitiveModel.mjs +393 -62
  59. package/src/core/ProjectAsyncInputOwner.mjs +70 -0
  60. package/src/core/ProjectLoader.mjs +975 -0
  61. package/src/core/SimulationService.mjs +790 -0
  62. package/src/core/ToolkitCapabilities.mjs +130 -0
  63. package/src/core/ZipArchiveInspector.mjs +649 -0
  64. package/src/core/context/BinaryDataSnapshot.mjs +217 -0
  65. package/src/core/context/CircuitJsonContextIndexes.mjs +96 -0
  66. package/src/core/context/CircuitJsonDerivedCache.mjs +114 -0
  67. package/src/core/context/CircuitJsonDocumentContext.mjs +353 -0
  68. package/src/core/context/CircuitJsonLegacyModel.mjs +147 -0
  69. package/src/core/context/CircuitJsonLegacyNormalizer.mjs +847 -0
  70. package/src/core/context/CircuitJsonMetadataBoundary.mjs +76 -0
  71. package/src/core/context/CircuitJsonModelFreezeTraversal.mjs +179 -0
  72. package/src/core/context/CircuitJsonReadOnlyDocument.mjs +920 -0
  73. package/src/core/context/CircuitJsonSchematicTableNormalizer.mjs +314 -0
  74. package/src/core/context/CircuitJsonValidationAuthority.mjs +39 -0
  75. package/src/core/context/CircuitJsonValidationProof.mjs +217 -0
  76. package/src/core/context/PcbPrimitivePreparation.mjs +198 -0
  77. package/src/core/context/PcbSpatialIndex.mjs +701 -0
  78. package/src/core/context/ProtectedExtensionBinaryBoundary.mjs +128 -0
  79. package/src/core/context/StructuredDataSnapshot.mjs +683 -0
  80. package/src/core/contracts/DocumentResult.mjs +198 -0
  81. package/src/core/contracts/ProjectResult.mjs +96 -0
  82. package/src/core/contracts/RuntimeProxyBoundary.mjs +48 -0
  83. package/src/core/contracts/ToolkitAsset.mjs +493 -0
  84. package/src/core/contracts/ToolkitDiagnostic.mjs +38 -0
  85. package/src/core/contracts/ToolkitError.mjs +176 -0
  86. package/src/core/contracts/ToolkitProgress.mjs +89 -0
  87. package/src/core/interaction/CanonicalInteractionOptions.mjs +246 -0
  88. package/src/core/interaction/PcbInteractionBounds.mjs +167 -0
  89. package/src/core/query/CircuitTraversal.mjs +343 -0
  90. package/src/core/query/ComponentGrouping.mjs +275 -0
  91. package/src/core/query/QueryNetlistBuilder.mjs +306 -0
  92. package/src/core/query/QueryService.mjs +435 -0
  93. package/src/core/query/RegexPattern.mjs +75 -0
  94. package/src/core/rendering/CanonicalBomOrder.mjs +81 -0
  95. package/src/core/rendering/CanonicalBomRows.mjs +92 -0
  96. package/src/core/rendering/CanonicalRenderOptions.mjs +498 -0
  97. package/src/core/rendering/CanonicalSvgDocument.mjs +102 -0
  98. package/src/core/rendering/PcbRenderPlan.mjs +429 -0
  99. package/src/core/rendering/SchematicSheetSelector.mjs +335 -0
  100. package/src/core/scene3d/PcbScene3dBuilder.mjs +906 -0
  101. package/src/core/scene3d/PcbScene3dPreparator.mjs +47 -0
  102. package/src/core/scene3d/Scene3dAssetIndex.mjs +284 -0
  103. package/src/core/scene3d/Scene3dBoardModel.mjs +596 -0
  104. package/src/core/scene3d/Scene3dDocumentMetadata.mjs +167 -0
  105. package/src/core/scene3d/Scene3dFreeze.mjs +37 -0
  106. package/src/core/scene3d/Scene3dIdRegistry.mjs +34 -0
  107. package/src/core/scene3d/Scene3dInputPreflight.mjs +193 -0
  108. package/src/core/scene3d/Scene3dMaterials.mjs +58 -0
  109. package/src/core/scene3d/Scene3dModelReference.mjs +134 -0
  110. package/src/core/scene3d/Scene3dOptions.mjs +363 -0
  111. package/src/core/scene3d/SceneAssetResolver.mjs +441 -0
  112. package/src/core/simulation/SimulationParameterCloner.mjs +543 -0
  113. package/src/core/worker/ParserWorkerClient.mjs +997 -0
  114. package/src/core/worker/ToolkitWorkerProtocol.mjs +412 -0
  115. package/src/core/worker/WorkerRequestData.mjs +623 -0
  116. package/src/core/worker/WorkerResponseData.mjs +905 -0
  117. package/src/extensions.mjs +37 -0
  118. package/src/index.mjs +14 -9
  119. package/src/interaction.mjs +2 -0
  120. package/src/manufacturing.mjs +1 -0
  121. package/src/parser.mjs +12 -2
  122. package/src/project.mjs +5 -0
  123. package/src/query.mjs +1 -0
  124. package/src/renderers.mjs +3 -29
  125. package/src/scene3d.mjs +3 -0
  126. package/src/simulation.mjs +1 -0
  127. package/src/styles/renderers.css +24 -0
  128. package/src/testing/ToolkitContractFixtures.mjs +124 -0
  129. package/src/testing/ToolkitLoopbackWorker.mjs +174 -0
  130. package/src/testing/runToolkitContract.mjs +705 -0
  131. package/src/testing.mjs +3 -0
  132. package/src/ui/BomTableRenderer.mjs +304 -0
  133. package/src/ui/CircuitJsonPcbBoardSvgRenderer.mjs +80 -0
  134. package/src/ui/CircuitJsonPcbPrimitiveAttributeRenderer.mjs +3 -5
  135. package/src/ui/CircuitJsonPcbSvgRenderer.mjs +63 -43
  136. package/src/ui/CircuitJsonPcbViaSvgRenderer.mjs +3 -5
  137. package/src/ui/CircuitJsonSchematicDebugRenderer.mjs +164 -0
  138. package/src/ui/CircuitJsonSchematicImageSvgRenderer.mjs +210 -0
  139. package/src/ui/CircuitJsonSchematicLineRenderer.mjs +86 -0
  140. package/src/ui/CircuitJsonSchematicSheetSymbolSvgRenderer.mjs +98 -0
  141. package/src/ui/CircuitJsonSchematicSvgArcPath.mjs +117 -17
  142. package/src/ui/CircuitJsonSchematicSvgPortMetadata.mjs +67 -20
  143. package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +45 -9
  144. package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +151 -148
  145. package/src/ui/CircuitJsonSchematicTableSvgRenderer.mjs +4 -292
  146. package/src/ui/PcbSvgRenderer.mjs +41 -0
  147. package/src/ui/SafeSvgPaint.mjs +26 -0
  148. package/src/ui/SafeXmlText.mjs +60 -0
  149. package/src/ui/SchematicGeometryBounds.mjs +540 -0
  150. package/src/ui/SchematicSvgRenderer.mjs +110 -0
  151. package/src/ui/SchematicTableGeometry.mjs +319 -0
  152. package/src/ui/SchematicTextAnchor.mjs +55 -0
  153. package/src/ui/SchematicTextBounds.mjs +98 -0
  154. package/src/workers/parser.worker.mjs +59 -0
package/NOTICE.md CHANGED
@@ -28,3 +28,34 @@ For applications with an "About", "Licenses", or "Legal Notices" screen,
28
28
  include a reasonable reference to this project and its original author there.
29
29
 
30
30
  Package-manager dependencies retain their own licenses.
31
+
32
+ ## Canonical ECAD toolkit convergence
33
+
34
+ Version 1.1.0 defines the common parser, project, renderer, interaction, query,
35
+ manufacturing, simulation, data-only 3D scene, capability, error, and worker
36
+ contracts used by CircuitJSON Toolkit, Gerber Toolkit, Altium Toolkit, and
37
+ KiCad Toolkit.
38
+
39
+ Source-format packages retain their own native decoders, fidelity engines,
40
+ assets, reports, and license terms. Shared behavior generalized here operates
41
+ on standards-only CircuitJSON and does not copy source-format parser models or
42
+ renderer graphs. `docs/provenance.md` records consulted behavior and adapted
43
+ ownership boundaries.
44
+
45
+ The `circuit-json` schema package is used only for development-time schema
46
+ verification. It is not a runtime dependency or bundled production edge.
47
+
48
+ ## Query behavior references
49
+
50
+ The shared CircuitJSON query contract was independently implemented after
51
+ reviewing the byte-identical netlist-query behavior in these projects:
52
+
53
+ - Altium Toolkit (`https://github.com/SunboX/altium-toolkit`), source commit
54
+ `e8a8cd551ad103cd0cf96bb5b5f5b816874ed72b`;
55
+ - KiCad Toolkit (`https://github.com/SunboX/kicad-toolkit`), source commit
56
+ `02e38fe0b961a09d2ff25462b9b00207326743d2`.
57
+
58
+ Those consulted files are Copyright (C) 2026 André Fiedler and licensed
59
+ `GPL-3.0-or-later`. CircuitJSON Toolkit does not copy their source text or
60
+ package-native model logic; `docs/provenance.md` records the per-module
61
+ behavior-only reuse decisions.
package/README.md CHANGED
@@ -6,169 +6,321 @@ SPDX-License-Identifier: CC-BY-SA-4.0
6
6
 
7
7
  # CircuitJSON Toolkit
8
8
 
9
- CircuitJSON Toolkit is an ESM JavaScript library for parsing, validating,
10
- indexing, and inspecting serialized CircuitJSON element arrays.
9
+ CircuitJSON Toolkit is the dependency-free common runtime for the ECAD toolkit
10
+ family. It provides the same parser, project, rendering, interaction, query,
11
+ manufacturing, simulation, 3D scene, capability, error, and worker contracts
12
+ used by `gerber-toolkit`, `altium-toolkit`, and `kicad-toolkit`.
13
+
14
+ CircuitJSON is the shared immutable model. Source-format packages keep their
15
+ native decoders and fidelity data in explicit extension namespaces while
16
+ common services operate on `DocumentResult.model` or a reused
17
+ `CircuitJsonDocumentContext`.
18
+
19
+ The package is browser- and Node-compatible, local-first, and has no runtime
20
+ dependencies. It does not import Three.js, the DOM, filesystem APIs, or network
21
+ clients.
22
+
23
+ ## Breaking API convergence
24
+
25
+ Version 1.1.0 intentionally changes names, parameters, return shapes, and
26
+ package subpaths to match the Gerber, Altium, and KiCad toolkits. The root is an
27
+ exact 17-class surface: 14 common classes plus three deprecated viewer
28
+ compatibility classes. Previous CircuitJSON-specific utilities were not
29
+ deleted: 37 source-neutral compatibility exports moved to
30
+ `circuitjson-toolkit/extensions`.
31
+
32
+ See the [migration guide](docs/migration.md) and its generated
33
+ [root](docs/migration/root.md), [parser](docs/migration/parser.md),
34
+ [renderer](docs/migration/renderers.md), and
35
+ [behavior](docs/migration/behaviors.md) appendices for all 1,207 preserved
36
+ 1.0.17 features. The
37
+ [1.1.0 release notes](docs/release-notes-v1.1.0.md) provide the concise change
38
+ summary.
39
+
40
+ Before 1.1.0:
11
41
 
12
- The package was extracted from [ECAD Forge](https://ecadforge.app/), where it
13
- is used as the shared CircuitJSON runtime utility layer between ECAD parsers,
14
- viewer adapters, and browser-based import flows. Its parser-style API,
15
- metadata conventions, and local-first behavior are designed to line up with
16
- packages such as `altium-toolkit` and `kicad-toolkit`, while remaining
17
- independent of any source ECAD format.
42
+ ```js
43
+ import { CircuitJsonParser } from 'circuitjson-toolkit'
18
44
 
19
- This package is intentionally separate from
20
- [`pcb-scene3d-viewer`](https://www.npmjs.com/package/pcb-scene3d-viewer).
21
- `circuitjson-toolkit` owns CircuitJSON parsing, validation, indexing, and unit
22
- helpers. `pcb-scene3d-viewer` owns Three.js runtime rendering and consumes this
23
- package as a normal npm dependency.
45
+ const model = CircuitJsonParser.parseText(text, {
46
+ fileName: 'board.json'
47
+ })
48
+ ```
49
+
50
+ After 1.1.0:
51
+
52
+ ```js
53
+ import { Parser } from 'circuitjson-toolkit'
54
+
55
+ const document = Parser.parse({
56
+ fileName: 'board.json',
57
+ data: text
58
+ })
59
+ const model = document.model
60
+ ```
24
61
 
25
62
  ## Features
26
63
 
27
- - Parse standalone CircuitJSON `.json` text from strings, `ArrayBuffer`, or
28
- `Uint8Array` input
29
- - Validate serialized CircuitJSON element arrays with a small, stable document
30
- API
31
- - Attach parser-style metadata such as `fileName`, `fileType`, `kind`, and
32
- `sourceFormat`, matching the conventions used by the ECAD toolkit packages
33
- - Build lookup indexes by element type and stable CircuitJSON identifiers
34
- - Resolve source component and PCB component maps for viewer, QA, export, or
35
- reporting integrations
36
- - Reject unknown element types and invalid core element fields before consumers
37
- render or query the model
38
- - Convert CircuitJSON millimeter dimensions and points into mils for render
39
- adapters that use PCB imperial units internally
40
- - Run dependency-free SPICE transient examples or normalize injected simulator
41
- results into complete CircuitJSON simulation experiment element sets
42
- - Provide root and parser-focused ESM entrypoints
43
- - Run in browser and Node ESM environments
44
- - Run entirely with local input data; no network calls are made by the parser
45
- - Stay dependency-free at runtime
64
+ - Exact `Parser` and `ProjectLoader` contracts with typed envelopes and errors
65
+ - Immutable CircuitJSON validation proofs and request-scoped reusable indexes
66
+ - Copy-on-write normalization of supported legacy CircuitJSON aliases through
67
+ `CircuitJsonDocument.normalizeModel()`
68
+ - Deterministic PCB, schematic, and BOM renderers
69
+ - Asset-backed `schematic_image` rows and hierarchical
70
+ `schematic_sheet_symbol` rows with shared bounds and SVG behavior
71
+ - Reusable exact PCB interaction and spatial indexes
72
+ - Query, manufacturing, and injected simulation services
73
+ - Data-only, millimeter-based, right-handed Z-up PCB 3D scenes
74
+ - Descriptor-safe `ToolkitAsset` measurement and preparation with zero-copy
75
+ metadata mode, one-copy full mode, and shared ECAD model/image media-type
76
+ inference
77
+ - Explicit asynchronous scene-asset preparation without implicit I/O
78
+ - Strict `ecad-toolkit.worker.v1` parsing/project protocol with progress,
79
+ cancellation, and controlled buffer transfer
80
+ - One-pass ownership for selected source extensions, with a separate 128 MiB /
81
+ 2,000,000-item bound and exact direct/worker result parity
82
+ - Machine-readable capability inventory and packed downstream conformance
83
+ harness
84
+ - Explicit `/extensions` surface retaining every previous specialized API
85
+ - Local-only behavior and no runtime package dependencies
46
86
 
47
87
  ## Install
48
88
 
49
- The package is published on npm as
50
- [`circuitjson-toolkit`](https://www.npmjs.com/package/circuitjson-toolkit).
51
-
52
89
  ```bash
53
90
  npm install circuitjson-toolkit
54
91
  ```
55
92
 
56
- ## Usage
93
+ Node.js 20 or newer is required.
57
94
 
58
- Parse a standalone CircuitJSON file or string:
95
+ ## Parse and reuse a context
59
96
 
60
97
  ```js
61
98
  import {
62
- CircuitJsonDocument,
63
- CircuitJsonIndexer,
64
- CircuitJsonParser
99
+ CircuitJsonDocumentContext,
100
+ Parser,
101
+ PcbInteractionIndex,
102
+ PcbSvgRenderer,
103
+ QueryService
65
104
  } from 'circuitjson-toolkit'
66
105
 
67
- const circuitJson = CircuitJsonParser.parseText(fileText)
68
- CircuitJsonDocument.assertModel(circuitJson)
69
-
70
- const index = CircuitJsonIndexer.index(circuitJson)
71
- console.log(index.elementsByType.get('pcb_board'))
72
- ```
73
-
74
- The parser also accepts bytes, mirroring the parser style used by the source
75
- ECAD toolkits:
76
-
77
- ```js
78
- import { CircuitJsonParser } from 'circuitjson-toolkit'
106
+ const document = Parser.parse({
107
+ fileName: 'board.json',
108
+ data: fileText
109
+ })
79
110
 
80
- const documentModel = CircuitJsonParser.parseBytes(arrayBuffer, {
81
- fileName: file.name
111
+ const context = CircuitJsonDocumentContext.prepare(document, {
112
+ indexes: ['elements', 'relations', 'connectivity', 'spatial']
82
113
  })
83
- ```
84
114
 
85
- Parser-focused imports are available through the `parser` subpath:
115
+ const svg = PcbSvgRenderer.render(context, { side: 'top' })
116
+ const hits = PcbInteractionIndex.create(context).hitTest({ x: 10, y: 5 })
117
+ const components = QueryService.create(context).query({
118
+ select: 'components'
119
+ })
86
120
 
87
- ```js
88
- import { CircuitJsonParser } from 'circuitjson-toolkit/parser'
121
+ console.log(document.model, svg, hits, components.items)
89
122
  ```
90
123
 
91
- Build an index for renderer or reporting integrations:
124
+ `Parser.parse()` returns the exact clone-safe `ecad-toolkit.document.v1`
125
+ envelope:
92
126
 
93
127
  ```js
94
- import { CircuitJsonIndexer } from 'circuitjson-toolkit'
95
-
96
- const index = CircuitJsonIndexer.index(documentModel)
97
- const board = index.elementsByType.get('pcb_board')?.[0]
98
- const components = index.elementsByType.get('pcb_component') || []
99
- const source = index.sourceComponentById.get('source_component_1')
128
+ {
129
+ schema: 'ecad-toolkit.document.v1',
130
+ id: 'document-...',
131
+ modelSchema: { name: 'circuit-json', version: '0.0.446' },
132
+ model: [],
133
+ source: { format: 'circuitjson', fileName: 'board.json', fileType: 'circuitjson' },
134
+ extensions: {},
135
+ assets: [],
136
+ diagnostics: [],
137
+ statistics: {}
138
+ }
100
139
  ```
101
140
 
102
- Convert CircuitJSON millimeter coordinates for renderer adapters:
141
+ ## Project and asynchronous parsing
103
142
 
104
143
  ```js
105
- import { CircuitJsonUnits } from 'circuitjson-toolkit'
106
-
107
- const centerMil = CircuitJsonUnits.pointMmToMil(board.center)
108
- const widthMil = CircuitJsonUnits.mmToMil(board.width)
144
+ import { Parser, ProjectLoader } from 'circuitjson-toolkit'
145
+
146
+ const project = ProjectLoader.load(
147
+ [
148
+ {
149
+ name: 'board.json',
150
+ data: boardText,
151
+ assets: [{ name: 'body.step', data: bodyBytes }]
152
+ },
153
+ { name: 'schematic.json', data: schematicText }
154
+ ],
155
+ { decodeAssets: 'metadata' }
156
+ )
157
+
158
+ const controller = new AbortController()
159
+ const document = await Parser.parseAsync(
160
+ { fileName: 'board.json', data: boardBytes },
161
+ {
162
+ worker: 'auto',
163
+ transferInput: false,
164
+ signal: controller.signal,
165
+ onProgress: ({ stage, detail }) => console.log(stage, detail)
166
+ }
167
+ )
168
+
169
+ console.log(project.documents, document.model)
109
170
  ```
110
171
 
111
- Run a local SPICE transient example and receive CircuitJSON experiment output:
172
+ Attached asset bytes count toward both `maxEntryBytes` and `maxTotalBytes` in
173
+ direct and worker project loading, even when `decodeAssets: 'none'` omits them
174
+ from the result.
175
+
176
+ Direct and worker calls return equivalent serialized results. Caller input is
177
+ never detached unless `transferInput: true` is explicit. `worker: 'auto'`
178
+ falls back to direct execution only when worker construction is unavailable;
179
+ explicit worker and runtime failures remain visible.
180
+
181
+ Selected source-native extensions are captured once into an immutable owned
182
+ snapshot. Their separate 128 MiB payload and 2,000,000-item ceilings permit
183
+ realistic renderer/model graphs without weakening the worker protocol's 250 MB
184
+ total-result ceiling; an over-limit extension fails visibly instead of being
185
+ silently truncated. Binary extension values remain byte-backed and return
186
+ defensive copies instead of expanding into JavaScript number arrays.
187
+ When extensions are disabled, native documents and projects return the exact
188
+ empty map `{}`. Bounded ZIP consumers can preflight local/central filenames and
189
+ CRC32/size metadata with
190
+ `ZipArchiveInspector.inspect()` and validate inflated bytes with
191
+ `verifyExtractedBytes()`. Compression limits use member payload sizes, so ZIP
192
+ comments or other container padding cannot dilute the measured expansion ratio.
193
+
194
+ The shared PCB hole primitive preserves circular, pill/oval, rectangular, and
195
+ square legal variants. Square holes normalize to equal-width rectangular
196
+ apertures instead of circular fallbacks.
197
+
198
+ `retainSource: 'reference'` is available for direct parser calls that need the
199
+ exact caller input identity. It adds a non-enumerable `sourceReference`, does
200
+ not freeze the caller object, and is omitted from serialized results. Explicit
201
+ worker execution rejects this identity-only mode; automatic execution stays
202
+ direct.
203
+
204
+ ## Render, inspect, export, simulate, and build scenes
112
205
 
113
206
  ```js
114
- import { SpiceSimulationService } from 'circuitjson-toolkit'
207
+ import {
208
+ BomTableRenderer,
209
+ ManufacturingService,
210
+ PcbScene3dBuilder,
211
+ PcbScene3dPreparator,
212
+ SchematicSvgRenderer,
213
+ SimulationService
214
+ } from 'circuitjson-toolkit'
115
215
 
116
- const result = await SpiceSimulationService.simulate(`
117
- Vmain out 0 DC 3.3
118
- .PRINT TRAN V(out)
119
- .tran 1ms 2ms
120
- .END
121
- `)
216
+ const schematicSvg = SchematicSvgRenderer.render(document)
217
+ const bomHtml = BomTableRenderer.render(document)
218
+ const manufacturing = ManufacturingService.inspect(document)
219
+ const simulation = SimulationService.build(document)
220
+ const canonicalScene = PcbScene3dBuilder.build(document)
122
221
 
123
- console.log(result.simulationCircuitJson)
124
- console.log(result.graphSummary)
222
+ const preparedScene = await PcbScene3dPreparator.prepare(document, {
223
+ fidelity: 'native',
224
+ resolveAsset: async (request, { signal }) =>
225
+ await hostAssetStore.resolve(request, { signal })
226
+ })
125
227
  ```
126
228
 
127
- Pass CircuitJSON to the 3D renderer as a separate step:
229
+ 3D results use the `ecad-toolkit.scene3d.v1` data contract. Runtime rendering
230
+ belongs to packages such as
231
+ [`pcb-scene3d-viewer`](https://www.npmjs.com/package/pcb-scene3d-viewer).
128
232
 
129
- ```js
130
- import { CircuitJsonParser } from 'circuitjson-toolkit'
131
- import { PcbScene3dController } from 'pcb-scene3d-viewer'
233
+ ## Retained extension APIs
132
234
 
133
- const circuitJson = CircuitJsonParser.parseText(fileText, {
134
- fileName: 'board.circuitjson'
135
- })
235
+ Previous specialized exports remain available explicitly:
136
236
 
137
- const controller = new PcbScene3dController(viewportNode, circuitJson)
237
+ ```js
238
+ import {
239
+ CircuitJsonParser,
240
+ CircuitJsonPcbSvgRenderer,
241
+ SpiceSimulationService
242
+ } from 'circuitjson-toolkit/extensions'
138
243
  ```
139
244
 
140
- `circuitjson-toolkit` does not render, fetch, or load external model assets.
141
- That behavior belongs in host applications or renderer packages.
245
+ New integrations should use canonical classes. `/extensions` exists for
246
+ deliberate migrations, not as a second common API. Its exact 37 exports are
247
+ source-neutral and can be shared or derived by all four toolkits.
248
+
249
+ The temporary root compatibility class also exposes
250
+ `CircuitJsonDocument.normalizeModel(model, { owned })`. It projects supported
251
+ pre-union table, PCB path, pad-clearance, courtyard, and artwork aliases onto
252
+ the pinned CircuitJSON union. The default copy-on-write mode returns the exact
253
+ input when it is already canonical; `{ owned: true }` is reserved for
254
+ toolkit-owned mutable projections and may update them in place. Renderers and
255
+ viewers can therefore share the same normalization boundary instead of keeping
256
+ application adapters.
257
+
258
+ The compatibility renderer also provides
259
+ `CircuitJsonPcbSvgRenderer.renderSides(model, sides)` so callers migrating
260
+ multi-side output can prepare legacy primitives once.
261
+
262
+ `CircuitJsonPcbHolePrimitiveModel` is the shared geometry boundary for drilled
263
+ PCB elements. Polygon-plated pads derive rotation-local width and height from
264
+ `pad_outline`, while pill drill width, height, diameter, and rotation remain
265
+ available to viewers and manufacturing consumers without source adapters.
266
+
267
+ Direct asynchronous parsing and project loading own exact binary view windows
268
+ and selected assets before the first progress callback. Worker-received inputs
269
+ reuse their structured-clone ownership marker, avoiding a redundant receiver
270
+ copy while keeping sync, direct async, and worker results mutation-isolated.
271
+
272
+ ## Package entrypoints
273
+
274
+ - `circuitjson-toolkit`
275
+ - `circuitjson-toolkit/parser`
276
+ - `circuitjson-toolkit/project`
277
+ - `circuitjson-toolkit/renderers`
278
+ - `circuitjson-toolkit/interaction`
279
+ - `circuitjson-toolkit/query`
280
+ - `circuitjson-toolkit/manufacturing`
281
+ - `circuitjson-toolkit/simulation`
282
+ - `circuitjson-toolkit/scene3d`
283
+ - `circuitjson-toolkit/capabilities`
284
+ - `circuitjson-toolkit/extensions`
285
+ - `circuitjson-toolkit/testing`
286
+ - `circuitjson-toolkit/workers/parser.worker.mjs`
287
+ - `circuitjson-toolkit/styles/renderers.css`
142
288
 
143
289
  ## Documentation
144
290
 
145
- - [API](docs/api.md)
146
- - [Model Format](docs/model-format.md)
147
- - [Testing](docs/testing.md)
148
- - [Scope](spec/library-scope.md)
149
-
150
- ## Package Scope
291
+ - [API reference](docs/api.md)
292
+ - [Capability inventory](docs/capabilities.md)
293
+ - [Migration from 1.0.17](docs/migration.md)
294
+ - [Model and envelope format](docs/model-format.md)
295
+ - [Testing and downstream conformance](docs/testing.md)
296
+ - [1.1.0 release notes](docs/release-notes-v1.1.0.md)
297
+ - [Library scope](spec/library-scope.md)
151
298
 
152
- This package owns reusable CircuitJSON utility behavior only:
299
+ ## Package scope
153
300
 
154
- - parser-style document ingestion for serialized CircuitJSON;
155
- - validation and diagnostics for element-array inputs;
156
- - indexing and lookup maps for common CircuitJSON IDs;
157
- - small unit conversion helpers for downstream renderer adapters.
158
- - local SPICE transient graph helpers that return CircuitJSON elements.
301
+ This package owns common, data-only operations derived from CircuitJSON. Native
302
+ Gerber, Altium, and KiCad decoding remains in the source toolkit. Native-only
303
+ facts remain in `document.extensions[format]`; common services do not duplicate
304
+ or rename native renderer graphs.
159
305
 
160
- It does not include native Altium or KiCad parsing, schematic/PCB SVG
161
- rendering, Three.js rendering, browser UI controls, network fetching, or
162
- format-specific scene-description builders.
306
+ The package does not own Three.js runtime rendering, browser UI controls,
307
+ implicit filesystem access, implicit network access, or source-format-specific
308
+ decoders.
163
309
 
164
310
  ## Test
165
311
 
166
312
  ```bash
167
313
  npm test
314
+ npm run check:format
315
+ npm run sync:schema -- --check
316
+ npm run check:features -- --strict
317
+ npm run benchmark -- --compare benchmarks/baseline-v1.0.17.json
318
+ npm run check:packed-entrypoints
319
+ npm run check:browser-dependencies
168
320
  ```
169
321
 
170
- The test suite uses small fake CircuitJSON samples only. Do not add customer,
171
- vendor, or source project documents to this repository.
322
+ Tests use small synthetic CircuitJSON and source-format fixtures only. Do not
323
+ add customer, vendor, or source project documents.
172
324
 
173
325
  ## License
174
326
 
@@ -197,6 +349,14 @@ Commercial licensing contact: https://github.com/SunboX
197
349
  See [COMMERCIAL-LICENSE.md](COMMERCIAL-LICENSE.md). That file is a licensing
198
350
  notice, not a commercial license grant.
199
351
 
352
+ ### Downstream toolkit dependency
353
+
354
+ Gerber Toolkit, Altium Toolkit, and KiCad Toolkit use this package as their
355
+ shared runtime. Their package or commercial terms do not replace this
356
+ package's AGPL or separately granted commercial terms. A closed-source product
357
+ using a source toolkit must also have an AGPL-compatible use or a separate
358
+ commercial license for CircuitJSON Toolkit.
359
+
200
360
  ### Documentation and notices
201
361
 
202
362
  Documentation and non-code text are licensed under Creative Commons