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,76 @@
1
+ /**
2
+ * Replaces metadata roots before a canonical document becomes immutable.
3
+ */
4
+ export class CircuitJsonMetadataBoundary {
5
+ /**
6
+ * Normalizes document and asset metadata through one trusted callback.
7
+ * @param {object} document Canonical document envelope.
8
+ * @param {unknown} assets Canonical asset array.
9
+ * @param {(value: unknown) => unknown} normalize Metadata normalizer.
10
+ * @returns {void}
11
+ */
12
+ static normalize(document, assets, normalize) {
13
+ for (const key of ['source', 'extensions']) {
14
+ CircuitJsonMetadataBoundary.#normalizeProperty(
15
+ document,
16
+ key,
17
+ normalize
18
+ )
19
+ }
20
+ if (!Array.isArray(assets)) return
21
+ const length = Object.getOwnPropertyDescriptor(assets, 'length')?.value
22
+ for (let index = 0; index < length; index += 1) {
23
+ const descriptor = Object.getOwnPropertyDescriptor(
24
+ assets,
25
+ String(index)
26
+ )
27
+ const asset = descriptor?.value
28
+ if (asset && typeof asset === 'object') {
29
+ CircuitJsonMetadataBoundary.#normalizeProperty(
30
+ asset,
31
+ 'source',
32
+ normalize
33
+ )
34
+ }
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Normalizes one own data property and replaces only changed roots.
40
+ * @param {object} owner Property owner.
41
+ * @param {string} key Property key.
42
+ * @param {(value: unknown) => unknown} normalize Metadata normalizer.
43
+ * @returns {void}
44
+ */
45
+ static #normalizeProperty(owner, key, normalize) {
46
+ let descriptor
47
+ try {
48
+ descriptor = Object.getOwnPropertyDescriptor(owner, key)
49
+ } catch {
50
+ throw new TypeError(
51
+ 'Canonical metadata could not be inspected safely.'
52
+ )
53
+ }
54
+ if (!descriptor) return
55
+ if (!Object.hasOwn(descriptor, 'value')) {
56
+ throw new TypeError(
57
+ 'Canonical metadata must be stored in own data properties.'
58
+ )
59
+ }
60
+ const normalized = normalize(descriptor.value)
61
+ if (normalized === descriptor.value) return
62
+ try {
63
+ Object.defineProperty(owner, key, {
64
+ ...descriptor,
65
+ value: normalized
66
+ })
67
+ } catch {
68
+ throw new TypeError(
69
+ 'Canonical metadata could not be normalized safely.'
70
+ )
71
+ }
72
+ }
73
+ }
74
+
75
+ Object.freeze(CircuitJsonMetadataBoundary.prototype)
76
+ Object.freeze(CircuitJsonMetadataBoundary)
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Collects JSON-shaped model values and freezes them only after validation.
3
+ */
4
+ export class CircuitJsonModelFreezeTraversal {
5
+ #cyclic = false
6
+ #enabled
7
+ #model
8
+ #seen
9
+ #targets = []
10
+ #unsupportedAccessor = false
11
+ #unsupportedContainer = false
12
+ #unsupportedShape = false
13
+ #visiting
14
+
15
+ /**
16
+ * Creates a postorder model freeze traversal.
17
+ * @param {object[]} model Root CircuitJSON model.
18
+ * @param {boolean} enabled Whether values should be collected.
19
+ */
20
+ constructor(model, enabled) {
21
+ this.#enabled = enabled
22
+ this.#model = model
23
+ this.#seen = new Set()
24
+ this.#visiting = new Set()
25
+ this.visit(model)
26
+ }
27
+
28
+ /**
29
+ * Visits one value in iterative child-first order.
30
+ * @param {unknown} value Candidate model value.
31
+ * @returns {void}
32
+ */
33
+ visit(value) {
34
+ const stack = [{ exit: false, value }]
35
+ while (stack.length) {
36
+ const frame = stack.pop()
37
+ if (frame.exit) {
38
+ this.#visiting.delete(frame.value)
39
+ if (this.#enabled) this.#targets.push(frame.value)
40
+ continue
41
+ }
42
+ const current = frame.value
43
+ if (typeof current === 'function') {
44
+ this.#unsupportedContainer = true
45
+ continue
46
+ }
47
+ if (current === null || typeof current !== 'object') continue
48
+ if (!CircuitJsonModelFreezeTraversal.#plain(current)) {
49
+ this.#unsupportedContainer = true
50
+ continue
51
+ }
52
+ if (this.#visiting.has(current)) {
53
+ this.#cyclic = true
54
+ continue
55
+ }
56
+ if (this.#seen.has(current)) continue
57
+ this.#seen.add(current)
58
+ this.#visiting.add(current)
59
+
60
+ const children = Array.isArray(current)
61
+ ? this.#arrayChildren(current)
62
+ : this.#recordChildren(current)
63
+ stack.push({ exit: true, value: current })
64
+ for (let index = children.length - 1; index >= 0; index -= 1) {
65
+ stack.push({ exit: false, value: children[index] })
66
+ }
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Visits one dense array through enumerable index data descriptors.
72
+ * @param {any[]} value Array value.
73
+ * @returns {unknown[]} Child values.
74
+ */
75
+ #arrayChildren(value) {
76
+ const keys = Reflect.ownKeys(value)
77
+ const length = Object.getOwnPropertyDescriptor(value, 'length')?.value
78
+ if (
79
+ !Number.isSafeInteger(length) ||
80
+ length < 0 ||
81
+ keys.length !== length + 1
82
+ ) {
83
+ this.#unsupportedShape = true
84
+ return []
85
+ }
86
+ const children = []
87
+ for (let index = 0; index < length; index += 1) {
88
+ const descriptor = Object.getOwnPropertyDescriptor(
89
+ value,
90
+ String(index)
91
+ )
92
+ if (!descriptor || !Object.hasOwn(descriptor, 'value')) {
93
+ this.#unsupportedAccessor = true
94
+ continue
95
+ }
96
+ if (descriptor.enumerable !== true) {
97
+ this.#unsupportedShape = true
98
+ continue
99
+ }
100
+ children.push(descriptor.value)
101
+ }
102
+ return children
103
+ }
104
+
105
+ /**
106
+ * Visits one record through enumerable string-keyed data descriptors.
107
+ * @param {Record<string, unknown>} value Plain record value.
108
+ * @returns {unknown[]} Child values.
109
+ */
110
+ #recordChildren(value) {
111
+ const children = []
112
+ for (const key of Reflect.ownKeys(value)) {
113
+ const descriptor = Object.getOwnPropertyDescriptor(value, key)
114
+ if (!Object.hasOwn(descriptor, 'value')) {
115
+ this.#unsupportedAccessor = true
116
+ continue
117
+ }
118
+ if (typeof key !== 'string' || descriptor.enumerable !== true) {
119
+ this.#unsupportedShape = true
120
+ continue
121
+ }
122
+ children.push(descriptor.value)
123
+ }
124
+ return children
125
+ }
126
+
127
+ /**
128
+ * Returns immutable-model shape errors found during traversal.
129
+ * @returns {string[]} Validation errors.
130
+ */
131
+ errors() {
132
+ const errors = []
133
+ if (this.#unsupportedContainer) {
134
+ errors.push(
135
+ 'Immutable CircuitJSON models may contain only primitives, plain objects and arrays.'
136
+ )
137
+ }
138
+ if (this.#unsupportedAccessor) {
139
+ errors.push(
140
+ 'Immutable CircuitJSON models may contain only data properties.'
141
+ )
142
+ }
143
+ if (this.#unsupportedShape) {
144
+ errors.push(
145
+ 'Immutable CircuitJSON models must use dense arrays and enumerable string-keyed properties.'
146
+ )
147
+ }
148
+ if (this.#cyclic) {
149
+ errors.push('Immutable CircuitJSON models must not contain cycles.')
150
+ }
151
+ return errors
152
+ }
153
+
154
+ /**
155
+ * Freezes collected values when the complete model passed validation.
156
+ * @param {boolean} valid Whether validation succeeded.
157
+ * @returns {void}
158
+ */
159
+ commit(valid) {
160
+ if (!this.#enabled || !valid) return
161
+ for (const target of this.#targets) Object.freeze(target)
162
+ }
163
+
164
+ /**
165
+ * Returns true for arrays and true plain object records.
166
+ * @param {unknown} value Candidate.
167
+ * @returns {boolean}
168
+ */
169
+ static #plain(value) {
170
+ if (Array.isArray(value)) {
171
+ return Object.getPrototypeOf(value) === Array.prototype
172
+ }
173
+ const prototype = Object.getPrototypeOf(value)
174
+ return prototype === Object.prototype || prototype === null
175
+ }
176
+ }
177
+
178
+ Object.freeze(CircuitJsonModelFreezeTraversal.prototype)
179
+ Object.freeze(CircuitJsonModelFreezeTraversal)