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
@@ -0,0 +1,120 @@
1
+ # Migration from 1.0.17 to 1.1.0
2
+
3
+ ## Breaking API convergence
4
+
5
+ Version 1.1.0 intentionally aligns CircuitJSON Toolkit with Gerber Toolkit,
6
+ Altium Toolkit, and KiCad Toolkit. Existing names and return shapes may change;
7
+ the previous behavior remains available through canonical services or the
8
+ explicit `circuitjson-toolkit/extensions` compatibility surface.
9
+
10
+ No feature in the 1.0.17 public baseline was silently removed. The generated
11
+ appendix pages map all 1207 exports, methods, options, fields, and
12
+ observable behaviors to their 1.1.0 owner and record availability in all four
13
+ toolkits.
14
+
15
+ ## Canonical root
16
+
17
+ The root exports these common classes:
18
+
19
+ - `Parser`
20
+ - `ProjectLoader`
21
+ - `CircuitJsonDocumentContext`
22
+ - `PcbSvgRenderer`
23
+ - `SchematicSvgRenderer`
24
+ - `BomTableRenderer`
25
+ - `PcbInteractionIndex`
26
+ - `QueryService`
27
+ - `ManufacturingService`
28
+ - `SimulationService`
29
+ - `PcbScene3dBuilder`
30
+ - `PcbScene3dPreparator`
31
+ - `ToolkitCapabilities`
32
+ - `ToolkitError`
33
+
34
+ `CircuitJsonDocument`, `CircuitJsonIndexer`, and `CircuitJsonUnits` remain
35
+ temporary root exports for `pcb-scene3d-viewer` compatibility. Other previous
36
+ root and renderer symbols moved to `circuitjson-toolkit/extensions`.
37
+
38
+ ## Parser input and result
39
+
40
+ Before:
41
+
42
+ ```js
43
+ const model = CircuitJsonParser.parseText(text, { fileName: 'board.json' })
44
+ ```
45
+
46
+ After:
47
+
48
+ ```js
49
+ const document = Parser.parse({ fileName: 'board.json', data: text })
50
+ const model = document.model
51
+ ```
52
+
53
+ The common parser input is `{ fileName, data, assets? }`. Common options are
54
+ `preserveRaw`, `decodeAssets`, `extensions`, `reports`,
55
+ `retainSource`, `worker`, `transferInput`, `signal`, and
56
+ `onProgress`. Unsupported enum values fail instead of being coerced.
57
+
58
+ `retainSource` is exactly `'none' | 'reference'`. Reference mode adds the
59
+ exact caller input as a non-enumerable `sourceReference` on direct parser
60
+ results; it does not freeze that object and serialized results omit it.
61
+ Explicit worker execution rejects reference mode because cross-thread identity
62
+ cannot be preserved, while automatic execution stays direct.
63
+
64
+ `Parser.parse` returns `ecad-toolkit.document.v1` with exact top-level
65
+ `schema`, `id`, `modelSchema`, `model`, `source`, `extensions`,
66
+ `assets`, `diagnostics`, and `statistics` fields. `Parser.tryParse`
67
+ returns either `{ ok: true, value }` or
68
+ `{ ok: false, error, diagnostics }`.
69
+
70
+ ## Project, rendering, query, manufacturing, simulation, and 3D
71
+
72
+ - `ProjectLoader` accepts named entry arrays and returns
73
+ `ecad-toolkit.project.v1`. It captures known fields once, rejects an
74
+ excessive entry count before inspecting records, and classifies that stable
75
+ snapshot.
76
+ - Renderers accept a document, model, or prepared context and use common
77
+ `top`/`bottom` sides.
78
+ - Reuse one `CircuitJsonDocumentContext` for repeated render, interaction,
79
+ query, manufacturing, simulation, and scene work.
80
+ - `PcbScene3dBuilder` is synchronous and data-only.
81
+ `PcbScene3dPreparator` performs explicit asynchronous asset resolution.
82
+ - Native source facts stay under `document.extensions[format]`; they are not
83
+ duplicated into the CircuitJSON model.
84
+ - Missing native prerequisites and unsupported operations throw typed
85
+ `ToolkitError` failures rather than returning invented empty results.
86
+
87
+ ## Workers
88
+
89
+ `Parser.parseAsync` and `ProjectLoader.loadAsync` use the shared
90
+ `ecad-toolkit.worker.v1` protocol. Inputs are not detached unless
91
+ `transferInput: true`; worker-owned output buffers are transferred. Progress
92
+ uses ordered `detect`, `decode`, `project`, `validate`, and `complete`
93
+ stages. Cancellation is request-scoped.
94
+
95
+ ## Package subpaths
96
+
97
+ - `circuitjson-toolkit/parser`
98
+ - `circuitjson-toolkit/project`
99
+ - `circuitjson-toolkit/renderers`
100
+ - `circuitjson-toolkit/interaction`
101
+ - `circuitjson-toolkit/query`
102
+ - `circuitjson-toolkit/manufacturing`
103
+ - `circuitjson-toolkit/simulation`
104
+ - `circuitjson-toolkit/scene3d`
105
+ - `circuitjson-toolkit/capabilities`
106
+ - `circuitjson-toolkit/extensions`
107
+ - `circuitjson-toolkit/testing`
108
+ - `circuitjson-toolkit/workers/parser.worker.mjs`
109
+ - `circuitjson-toolkit/styles/renderers.css`
110
+
111
+ ## Exhaustive feature mapping
112
+
113
+ The exhaustive mapping is generated from the immutable captured contracts by
114
+ `npm run sync:migration`. The pages remain deterministic and each stays below
115
+ the repository's 1,000-line limit.
116
+
117
+ - [Root entrypoint (673 mappings)](migration/root.md)
118
+ - [Parser entrypoint (43 mappings)](migration/parser.md)
119
+ - [Legacy renderer entrypoint (483 mappings)](migration/renderers.md)
120
+ - [Observable behaviors (8 mappings)](migration/behaviors.md)
@@ -1,10 +1,13 @@
1
- # Model Format
1
+ # Model and envelope format
2
2
 
3
- CircuitJSON Toolkit works with serialized CircuitJSON element arrays.
3
+ CircuitJSON Toolkit uses a standards-only CircuitJSON element array as its
4
+ shared model. Public parsers wrap that array in typed, clone-safe envelopes so
5
+ all ECAD toolkits return the same top-level shape.
4
6
 
5
- ## Top-Level Shape
7
+ ## CircuitJSON model
6
8
 
7
- The expected top-level value is an array:
9
+ `DocumentResult.model` is an immutable array of CircuitJSON `0.0.446`
10
+ elements:
8
11
 
9
12
  ```json
10
13
  [
@@ -19,78 +22,193 @@ The expected top-level value is an array:
19
22
  ]
20
23
  ```
21
24
 
22
- Every element must be an object with a known non-empty string `type` field and
23
- the required fields for that element. The validator also checks the standard
24
- identifier convention for known element types, plus strict core fields for
25
- boards, source components, source ports, schematic components, PCB components,
26
- and SMT pads.
25
+ Every element must have a known non-empty `type` and satisfy its standard
26
+ field contract. Source toolkits may not add private element types, hidden
27
+ renderer graphs, array expando properties, or native records to this model.
27
28
 
28
- ## Metadata
29
+ Acceptance is defined over the serialized input contract of the pinned
30
+ `circuit-json@0.0.446` union. The browser runtime is generated from that union
31
+ at development time and preserves required fields, unions, refinements,
32
+ pipelines, and transform rejection boundaries. Upstream random default IDs are
33
+ not materialized because doing so would make identical input nondeterministic;
34
+ an omitted random identity instead produces the
35
+ `CIRCUITJSON_UPSTREAM_DEFAULT_ID_OMITTED` warning and increments
36
+ `statistics.upstreamDefaultIdentityOmissions`.
29
37
 
30
- `CircuitJsonParser` and `CircuitJsonDocument.attachMetadata()` attach
31
- non-structural metadata directly to the returned array:
38
+ ## DocumentResult
39
+
40
+ `Parser.parse()` and `Parser.parseAsync()` return this exact enumerable shape:
32
41
 
33
42
  ```js
34
43
  {
35
- fileName: 'board.json',
36
- fileType: 'circuitjson',
37
- kind: 'pcb',
38
- sourceFormat: 'circuitjson'
44
+ schema: 'ecad-toolkit.document.v1',
45
+ id: 'document-...',
46
+ modelSchema: {
47
+ name: 'circuit-json',
48
+ version: '0.0.446'
49
+ },
50
+ model: [],
51
+ source: {
52
+ format: 'circuitjson',
53
+ fileName: 'board.json',
54
+ fileType: 'circuitjson'
55
+ },
56
+ extensions: {},
57
+ assets: [],
58
+ diagnostics: [],
59
+ statistics: {}
39
60
  }
40
61
  ```
41
62
 
42
- These fields are enumerable so they survive `structuredClone()` and align with
43
- the parser packages' document-model conventions.
63
+ `id` derives from normalized source identity rather than the complete payload.
64
+ The validated model is deeply immutable. A non-enumerable in-process proof lets
65
+ `CircuitJsonDocumentContext` reuse validation; structured cloning intentionally
66
+ drops that proof and the receiving process validates once.
67
+
68
+ For direct asynchronous requests, binary parser inputs, project entries, and
69
+ selected assets are captured before any progress callback or host yield.
70
+ Partial and shared-buffer views preserve only their exact visible range.
71
+ Worker-received request graphs are already process-owned by structured clone,
72
+ so the worker marks and reuses that boundary instead of making a second input
73
+ copy.
44
74
 
45
- ## Identifiers
75
+ The `CircuitJsonParser` legacy array is mutable for migration-only metadata and
76
+ slot replacement, while every unchanged element graph is deeply immutable. If
77
+ a caller replaces an element slot, optimized consumers discard the proof-bound
78
+ shortcut and validate the replacement before use.
46
79
 
47
- `CircuitJsonIndexer` recognizes common CircuitJSON ID fields, including:
80
+ ## Source extensions
48
81
 
49
- - `pcb_board_id`
50
- - `pcb_component_id`
51
- - `pcb_hole_id`
52
- - `pcb_plated_hole_id`
53
- - `pcb_port_id`
54
- - `pcb_smtpad_id`
55
- - `pcb_trace_id`
56
- - `pcb_via_id`
57
- - `source_component_id`
58
- - `source_net_id`
59
- - `source_port_id`
60
- - `source_trace_id`
82
+ CircuitJSON input has an empty extension map. `extensions: 'none'` also returns
83
+ exactly `{}` for every source format. When source-native facts are selected,
84
+ the document has one owned namespace:
61
85
 
62
- Indexed element IDs are stored with a type prefix, such as
63
- `pcb_board:board_1`, so different element classes can reuse local IDs without
64
- colliding.
86
+ ```js
87
+ {
88
+ altium: {
89
+ $meta: {
90
+ schema: 'ecad-toolkit.extension.v1',
91
+ completeness: 'canonical',
92
+ included: ['layers'],
93
+ omitted: ['raw-records']
94
+ },
95
+ layers: []
96
+ }
97
+ }
98
+ ```
99
+
100
+ Extensions contain only explicitly selected native facts and references. They
101
+ must not duplicate the CircuitJSON model or keep a renamed renderer model.
102
+ Default parsing excludes raw/base64/full-payload graphs.
103
+
104
+ When a caller explicitly selects a native extension, the toolkit captures and
105
+ freezes that graph once under a distinct ceiling of 2,000,000 structured items
106
+ and 128 MiB of string or binary content. The ceiling is shared across the
107
+ selected namespace, applies equally after worker transfer, and remains below
108
+ the worker's 250 MB whole-result limit. General document metadata keeps its
109
+ smaller budget, so a large native projection cannot consume the canonical
110
+ metadata allowance. Oversized graphs are rejected; they are never truncated or
111
+ partially retained. Binary extension values remain byte-backed with their
112
+ common buffer/view type and are exposed as defensive copies, preserving
113
+ mutation isolation without expanding bytes into plain numeric arrays.
114
+
115
+ Supported pre-union CircuitJSON aliases may be projected through
116
+ `CircuitJsonDocument.normalizeModel()`. The copy-on-write form preserves exact
117
+ canonical identities; the owned form is for source toolkits before their one
118
+ validation/proof boundary. Table cell geometry, PCB artwork routes,
119
+ pad-clearance relations, courtyards, layer aliases, and stroke dashes are
120
+ derived structurally and never from a source filename or fixture identity.
121
+
122
+ ## Assets and diagnostics
123
+
124
+ Assets have exact `id`, `kind`, `name`, `mediaType`, `byteLength`, `data`, and
125
+ `source` fields. `data` is `null`, a string, or a supported binary view.
126
+ `decodeAssets` controls whether no assets, metadata, or full payloads are
127
+ returned.
128
+
129
+ Schematic images are canonical model rows, not inline payload containers:
130
+
131
+ ```js
132
+ {
133
+ type: 'schematic_image',
134
+ schematic_image_id: 'schematic_image_logo',
135
+ asset_id: 'asset_logo',
136
+ center: { x: 20, y: 10 },
137
+ size: { width: 8, height: 4 },
138
+ rotation: 0,
139
+ opacity: 1,
140
+ preserve_aspect_ratio: true,
141
+ render_order: 0,
142
+ source_name: 'logo.png'
143
+ }
144
+ ```
65
145
 
66
- PCB courtyard artwork rows use the normal `type_id` identifier convention. The
67
- validator accepts generic `pcb_courtyard` rows plus shape-specific variants such
68
- as `pcb_courtyard_rect`, `pcb_courtyard_circle`, `pcb_courtyard_outline`,
69
- `pcb_courtyard_path`, and `pcb_courtyard_line`.
146
+ The referenced ToolkitAsset uses `kind: 'schematic-image'`. Payload bytes live
147
+ only in `assets[].data` when `decodeAssets: 'full'`; metadata mode retains
148
+ `byteLength` and sets `data: null`.
70
149
 
71
- ## Group Indexing
150
+ Hierarchical child boxes use `schematic_sheet_symbol` with
151
+ `schematic_sheet_symbol_id`, `name`, optional `source_file_name`,
152
+ `center`, `width`, `height`, and standard stroke/fill fields. Child
153
+ `schematic_port` rows reference `schematic_sheet_symbol_id`.
154
+ `schematic_sheet` remains reserved for actual selectable document pages.
72
155
 
73
- `CircuitJsonIndexer` builds `groupsById` and `elementsByGroupId` from direct
74
- group fields such as `source_group_id`, `pcb_group_id`,
75
- `schematic_group_id`, `group_id`, and `group_ids`. It also treats member-style
76
- fields such as `member_source_group_ids`, `member_pcb_group_ids`,
77
- `member_schematic_group_ids`, and `member_group_ids` as group memberships, so
78
- source nets and other non-group elements can participate in group indexes
79
- without duplicating direct group fields.
156
+ Diagnostics have exact `code`, `severity`, `message`, `source`, `location`, and
157
+ `details` fields. Severity is `info`, `warning`, or `error`.
80
158
 
81
- ## Units
159
+ ## ProjectResult
82
160
 
83
- CircuitJSON PCB coordinates and dimensions are millimeter-based. The toolkit
84
- provides unit helpers for consumers such as 3D render adapters that need mils:
161
+ `ProjectLoader` returns:
85
162
 
86
163
  ```js
87
- const widthMil = CircuitJsonUnits.mmToMil(board.width)
88
- const centerMil = CircuitJsonUnits.pointMmToMil(board.center)
164
+ {
165
+ schema: 'ecad-toolkit.project.v1',
166
+ id: 'project-...',
167
+ source: { format: 'circuitjson', entryNames: [] },
168
+ documents: [],
169
+ project: null,
170
+ extensions: {},
171
+ assets: [],
172
+ diagnostics: [],
173
+ statistics: {}
174
+ }
89
175
  ```
90
176
 
91
- ## Scope
177
+ `documents` contains canonical `DocumentResult` objects. `project` is `null`
178
+ for a collection without project metadata or an exact descriptor with `id`,
179
+ `name`, `format`, `documentIds`, and `relationships`.
180
+
181
+ ## Prepared contexts
182
+
183
+ `CircuitJsonDocumentContext.prepare(document, options)` accepts a document
184
+ envelope, bare CircuitJSON array, or an existing context. It validates a bare
185
+ or cloned input once and owns request-scoped element, relation, connectivity,
186
+ spatial, render, query, manufacturing, simulation, and scene-derived caches.
187
+ The `identifiers` index is a compact set-only view for membership checks and
188
+ clone boundaries that do not need duplicate element values.
189
+
190
+ Contexts can only be constructed by `prepare()`. Calling the exported class
191
+ constructor directly throws before reading caller input, so an arbitrary model
192
+ cannot be branded as validated and handed to a renderer, viewer, or app.
193
+
194
+ Serialized results never contain context caches, validation tokens, callbacks,
195
+ signals, workers, or caller source references.
196
+
197
+ ## Shared drilled geometry
198
+
199
+ `CircuitJsonPcbHolePrimitiveModel` converts `pcb_hole` and `pcb_plated_hole`
200
+ rows into one source-neutral primitive. For `hole_with_polygon_pad`, the model
201
+ inverse-rotates `pad_outline` around the authored center before measuring the
202
+ outer width and height. Its global `bounds` remain global, and pill drill
203
+ `outer_width`/`outer_height`, rectangular-pad dimensions, and independent
204
+ `rect_ccw_rotation`/`hole_ccw_rotation` variants remain distinct. `holeWidth`,
205
+ `holeHeight`, `holeDiameter`, `rotation`, and board-space `holeRotation` remain
206
+ available
207
+ separately. This lets every viewer consume the same canonical geometry without
208
+ format-specific repairs.
209
+
210
+ ## Units and scene coordinates
92
211
 
93
- This package does not render CircuitJSON, parse native ECAD formats, or infer
94
- format-specific semantics. Renderer packages and parser packages should consume
95
- these utilities rather than moving rendering or source-format behavior into this
96
- toolkit.
212
+ CircuitJSON PCB positions and dimensions use millimeters. Canonical 3D scenes
213
+ use millimeters and `right-handed-z-up`. Scene assets are data records; loading
214
+ and runtime rendering remain explicit host responsibilities.
@@ -0,0 +1,206 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 André Fiedler
3
+ SPDX-License-Identifier: CC-BY-SA-4.0
4
+ -->
5
+
6
+ # Implementation provenance
7
+
8
+ ## Pinned CircuitJSON schema provenance
9
+
10
+ The canonical serialized-input contract is compiled at development time from
11
+ the complete `any_circuit_element` union exported by
12
+ `circuit-json@0.0.446`. `package-lock.json` pins the npm integrity, and
13
+ `spec/circuitjson-schema-source.json` records that integrity together with the
14
+ distributed module SHA-256 and compiled contract SHA-256. The generated
15
+ `src/core/CircuitJsonUpstreamSchema.mjs` contains only browser-neutral data and
16
+ runtime instructions; neither `circuit-json`, Zod, Node built-ins, nor
17
+ `format-si-unit` is a published runtime edge.
18
+
19
+ Schema compilation also pins and verifies the exact compiler dependencies
20
+ `format-si-unit@0.0.7` and `zod@3.25.76`. For each dependency, the source and
21
+ generated provenance records contain the npm lockfile integrity, the inspected
22
+ distribution entry file, and that file's SHA-256. The lock integrity covers the
23
+ complete published tarball; the distribution hash makes the exact local entry
24
+ bytes independently visible.
25
+
26
+ The compiler preserves the pinned union's object, union, refinement, pipeline,
27
+ and transform acceptance behavior. In particular, the indirect development
28
+ dependency `format-si-unit@0.0.7` can throw for malformed SI strings; the
29
+ generated validator represents that rejection boundary without executing the
30
+ upstream transform or materializing its output. `npm run sync:schema --
31
+ --check` recompiles the live dependency and rejects any difference without
32
+ writing files, including any compiler dependency version, integrity, or entry
33
+ file drift.
34
+
35
+ ## Query implementation provenance
36
+
37
+ Task 6 consolidates source-neutral query behavior in `circuitjson-toolkit`.
38
+ The implementation is an independent, contract-driven reimplementation over
39
+ CircuitJSON indexes. No source text or package-native data model from the
40
+ consulted Altium or KiCad implementations is copied into this repository.
41
+
42
+ ## Adapted-module records
43
+
44
+ Every adapted module has a complete record for both consulted repositories.
45
+ The records deliberately distinguish behavior observation from implementation
46
+ reuse.
47
+
48
+ ### RegexPattern.mjs
49
+
50
+ - Local path: `src/core/query/RegexPattern.mjs`
51
+
52
+ #### Altium Toolkit source
53
+
54
+ - Repository: `https://github.com/SunboX/altium-toolkit`
55
+ - Path: `src/core/netlist-query/RegexPattern.mjs`
56
+ - Inspected worktree commit: `9fa22e1028d96e583275093279bf6e03e8619588`
57
+ - Source-introducing commit: `e8a8cd551ad103cd0cf96bb5b5f5b816874ed72b`
58
+ - Copyright: `2026 André Fiedler`
59
+ - License: `GPL-3.0-or-later`
60
+
61
+ #### KiCad Toolkit source
62
+
63
+ - Repository: `https://github.com/SunboX/kicad-toolkit`
64
+ - Path: `src/core/netlist-query/RegexPattern.mjs`
65
+ - Inspected worktree commit: `c71c88d69d236accce123656dfa66914c0d5489c`
66
+ - Source-introducing commit: `02e38fe0b961a09d2ff25462b9b00207326743d2`
67
+ - Copyright: `2026 André Fiedler`
68
+ - License: `GPL-3.0-or-later`
69
+
70
+ #### Decision
71
+
72
+ - Consulted behavior: string-sourced regular-expression matching and reset
73
+ behavior between tests.
74
+ - Reuse classification: behavior-only, independent bounded validation. No
75
+ source text or algorithm implementation was copied.
76
+
77
+ ### ComponentGrouping.mjs
78
+
79
+ - Local path: `src/core/query/ComponentGrouping.mjs`
80
+
81
+ #### Altium Toolkit source
82
+
83
+ - Repository: `https://github.com/SunboX/altium-toolkit`
84
+ - Path: `src/core/netlist-query/ComponentGrouping.mjs`
85
+ - Inspected worktree commit: `9fa22e1028d96e583275093279bf6e03e8619588`
86
+ - Source-introducing commit: `e8a8cd551ad103cd0cf96bb5b5f5b816874ed72b`
87
+ - Copyright: `2026 André Fiedler`
88
+ - License: `GPL-3.0-or-later`
89
+
90
+ #### KiCad Toolkit source
91
+
92
+ - Repository: `https://github.com/SunboX/kicad-toolkit`
93
+ - Path: `src/core/netlist-query/ComponentGrouping.mjs`
94
+ - Inspected worktree commit: `c71c88d69d236accce123656dfa66914c0d5489c`
95
+ - Source-introducing commit: `02e38fe0b961a09d2ff25462b9b00207326743d2`
96
+ - Copyright: `2026 André Fiedler`
97
+ - License: `GPL-3.0-or-later`
98
+
99
+ #### Decision
100
+
101
+ - Consulted behavior: deterministic grouping vocabulary and stable result
102
+ ordering.
103
+ - Reuse classification: behavior-only, independent grouping over canonical
104
+ CircuitJSON relations. No source text or algorithm implementation was copied.
105
+
106
+ ### CircuitTraversal.mjs
107
+
108
+ - Local path: `src/core/query/CircuitTraversal.mjs`
109
+
110
+ #### Altium Toolkit source
111
+
112
+ - Repository: `https://github.com/SunboX/altium-toolkit`
113
+ - Path: `src/core/netlist-query/CircuitTraversal.mjs`
114
+ - Inspected worktree commit: `9fa22e1028d96e583275093279bf6e03e8619588`
115
+ - Source-introducing commit: `e8a8cd551ad103cd0cf96bb5b5f5b816874ed72b`
116
+ - Copyright: `2026 André Fiedler`
117
+ - License: `GPL-3.0-or-later`
118
+
119
+ #### KiCad Toolkit source
120
+
121
+ - Repository: `https://github.com/SunboX/kicad-toolkit`
122
+ - Path: `src/core/netlist-query/CircuitTraversal.mjs`
123
+ - Inspected worktree commit: `c71c88d69d236accce123656dfa66914c0d5489c`
124
+ - Source-introducing commit: `02e38fe0b961a09d2ff25462b9b00207326743d2`
125
+ - Copyright: `2026 André Fiedler`
126
+ - License: `GPL-3.0-or-later`
127
+
128
+ #### Decision
129
+
130
+ - Consulted behavior: ordered traversal, visited-net handling, and cycle
131
+ termination. Sibling endpoint and traversal-bound fields were not consulted.
132
+ - Reuse classification: behavior-only, independent traversal. Endpoint and
133
+ path-connector records are derived directly from CircuitJSON ports, nets, and
134
+ explicit internal connections; no source text or algorithm was copied.
135
+
136
+ ### QueryNetlistBuilder.mjs
137
+
138
+ - Local path: `src/core/query/QueryNetlistBuilder.mjs`
139
+
140
+ #### Altium Toolkit source
141
+
142
+ - Repository: `https://github.com/SunboX/altium-toolkit`
143
+ - Path: `src/core/netlist-query/QueryNetlistBuilder.mjs`
144
+ - Inspected worktree commit: `9fa22e1028d96e583275093279bf6e03e8619588`
145
+ - Source-introducing commit: `e8a8cd551ad103cd0cf96bb5b5f5b816874ed72b`
146
+ - Copyright: `2026 André Fiedler`
147
+ - License: `GPL-3.0-or-later`
148
+
149
+ #### KiCad Toolkit source
150
+
151
+ - Repository: `https://github.com/SunboX/kicad-toolkit`
152
+ - Path: `src/core/netlist-query/QueryNetlistBuilder.mjs`
153
+ - Inspected worktree commit: `c71c88d69d236accce123656dfa66914c0d5489c`
154
+ - Source-introducing commit: `02e38fe0b961a09d2ff25462b9b00207326743d2`
155
+ - Copyright: `2026 André Fiedler`
156
+ - License: `GPL-3.0-or-later`
157
+
158
+ #### Decision
159
+
160
+ - Consulted behavior: component, net, and pin result vocabulary.
161
+ - Reuse classification: behavior-only, independent construction from prepared
162
+ CircuitJSON indexes. No source text or algorithm implementation was copied.
163
+
164
+ ### QueryService.mjs
165
+
166
+ - Local path: `src/core/query/QueryService.mjs`
167
+
168
+ #### Altium Toolkit source
169
+
170
+ - Repository: `https://github.com/SunboX/altium-toolkit`
171
+ - Path: `src/core/netlist-query/LoadedDesignNetlistService.mjs`
172
+ - Inspected worktree commit: `9fa22e1028d96e583275093279bf6e03e8619588`
173
+ - Source-introducing commit: `e8a8cd551ad103cd0cf96bb5b5f5b816874ed72b`
174
+ - Copyright: `2026 André Fiedler`
175
+ - License: `GPL-3.0-or-later`
176
+
177
+ #### KiCad Toolkit source
178
+
179
+ - Repository: `https://github.com/SunboX/kicad-toolkit`
180
+ - Path: `src/core/netlist-query/LoadedDesignNetlistService.mjs`
181
+ - Inspected worktree commit: `c71c88d69d236accce123656dfa66914c0d5489c`
182
+ - Source-introducing commit: `02e38fe0b961a09d2ff25462b9b00207326743d2`
183
+ - Copyright: `2026 André Fiedler`
184
+ - License: `GPL-3.0-or-later`
185
+
186
+ #### Decision
187
+
188
+ - Consulted behavior: bound-service reuse and convenience query methods.
189
+ - Reuse classification: behavior-only, independent canonical query boundary
190
+ over a prepared CircuitJSON context. No source text or algorithm was copied.
191
+
192
+ ## CircuitJSON-owned inputs
193
+
194
+ - Repository: `https://github.com/SunboX/circuitjson-toolkit`
195
+ - Inspected commit: `ed46a237e6d71355d6400692509356fe3737c802`
196
+ - Paths: `src/core/CircuitJsonIndexer.mjs` and
197
+ `src/core/context/CircuitJsonDocumentContext.mjs`
198
+ - Copyright: `2026 André Fiedler`
199
+ - License: `AGPL-3.0-or-later`
200
+ - Reuse classification: these repository-owned APIs are consumed directly as
201
+ the single source of truth for element, relation, and connectivity data.
202
+
203
+ All new Task 6 software remains `AGPL-3.0-or-later`. Because no GPL source or
204
+ algorithm implementation is copied, no source-file relicensing occurs. The
205
+ consulted projects and their original GPL notices remain attributed here and in
206
+ `NOTICE.md`.