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.
- package/NOTICE.md +31 -0
- package/README.md +267 -107
- package/docs/api.md +501 -96
- package/docs/capabilities.md +70 -0
- package/docs/migration/behaviors.md +45 -0
- package/docs/migration/parser.md +60 -0
- package/docs/migration/renderers.md +515 -0
- package/docs/migration/root.md +740 -0
- package/docs/migration.md +120 -0
- package/docs/model-format.md +175 -57
- package/docs/provenance.md +206 -0
- package/docs/release-notes-v1.1.0.md +154 -0
- package/docs/testing.md +117 -7
- package/package.json +31 -5
- package/spec/api-baseline-v1.0.17.json +1 -0
- package/spec/baseline-provenance-v1.0.17.json +7 -0
- package/spec/circuitjson-schema-snapshot.json +321 -0
- package/spec/circuitjson-schema-source.json +28 -0
- package/spec/feature-preservation.json +1 -0
- package/spec/library-scope.md +27 -20
- package/src/capabilities.mjs +1 -0
- package/src/core/ArchiveEntryPath.mjs +93 -0
- package/src/core/ArchiveLimits.mjs +31 -0
- package/src/core/ArchiveLimitsValidator.mjs +107 -0
- package/src/core/AsyncInputOwnership.mjs +56 -0
- package/src/core/AttachedValueLimits.mjs +67 -0
- package/src/core/CircuitJsonDiagnosticIndexer.mjs +184 -0
- package/src/core/CircuitJsonDocument.mjs +19 -61
- package/src/core/CircuitJsonElementTypes.mjs +10 -0
- package/src/core/CircuitJsonElementValidator.mjs +98 -847
- package/src/core/CircuitJsonIndexer.mjs +274 -194
- package/src/core/CircuitJsonManufacturingBuilder.mjs +167 -164
- package/src/core/CircuitJsonParser.mjs +75 -13
- package/src/core/CircuitJsonPcbClearanceDiagnostics.mjs +12 -6
- package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +108 -10
- package/src/core/CircuitJsonPcbPadPrimitiveModel.mjs +1 -1
- package/src/core/CircuitJsonPcbPrimitiveArtwork.mjs +44 -38
- package/src/core/CircuitJsonPcbPrimitiveBuilder.mjs +146 -28
- package/src/core/CircuitJsonPcbPrimitiveFields.mjs +70 -4
- package/src/core/CircuitJsonPcbPrimitiveIndex.mjs +18 -2
- package/src/core/CircuitJsonPcbPrimitiveOverlays.mjs +26 -9
- package/src/core/CircuitJsonPcbZonePrimitiveBuilder.mjs +7 -6
- package/src/core/CircuitJsonSerializedInputAudit.mjs +87 -0
- package/src/core/CircuitJsonSourceMetadata.mjs +5 -1
- package/src/core/CircuitJsonSupportMatrixBuilder.mjs +3 -1
- package/src/core/CircuitJsonToolkitElementSchema.mjs +218 -0
- package/src/core/CircuitJsonUnitParsers.mjs +101 -0
- package/src/core/CircuitJsonUnits.mjs +13 -87
- package/src/core/CircuitJsonUpstreamSchema.mjs +9 -0
- package/src/core/CircuitJsonUpstreamValidator.mjs +418 -0
- package/src/core/CircuitJsonValidationUnits.mjs +6 -0
- package/src/core/ManufacturingService.mjs +323 -0
- package/src/core/Parser.mjs +343 -0
- package/src/core/ParserOptions.mjs +333 -0
- package/src/core/PcbBoundsSelectionModel.mjs +55 -19
- package/src/core/PcbDiagnosticFocusModel.mjs +42 -11
- package/src/core/PcbInteractionIndex.mjs +368 -0
- package/src/core/PcbInteractionPrimitiveModel.mjs +393 -62
- package/src/core/ProjectAsyncInputOwner.mjs +70 -0
- package/src/core/ProjectLoader.mjs +975 -0
- package/src/core/SimulationService.mjs +790 -0
- package/src/core/ToolkitCapabilities.mjs +130 -0
- package/src/core/ZipArchiveInspector.mjs +649 -0
- package/src/core/context/BinaryDataSnapshot.mjs +217 -0
- package/src/core/context/CircuitJsonContextIndexes.mjs +96 -0
- package/src/core/context/CircuitJsonDerivedCache.mjs +114 -0
- package/src/core/context/CircuitJsonDocumentContext.mjs +353 -0
- package/src/core/context/CircuitJsonLegacyModel.mjs +147 -0
- package/src/core/context/CircuitJsonLegacyNormalizer.mjs +847 -0
- package/src/core/context/CircuitJsonMetadataBoundary.mjs +76 -0
- package/src/core/context/CircuitJsonModelFreezeTraversal.mjs +179 -0
- package/src/core/context/CircuitJsonReadOnlyDocument.mjs +920 -0
- package/src/core/context/CircuitJsonSchematicTableNormalizer.mjs +314 -0
- package/src/core/context/CircuitJsonValidationAuthority.mjs +39 -0
- package/src/core/context/CircuitJsonValidationProof.mjs +217 -0
- package/src/core/context/PcbPrimitivePreparation.mjs +198 -0
- package/src/core/context/PcbSpatialIndex.mjs +701 -0
- package/src/core/context/ProtectedExtensionBinaryBoundary.mjs +128 -0
- package/src/core/context/StructuredDataSnapshot.mjs +683 -0
- package/src/core/contracts/DocumentResult.mjs +198 -0
- package/src/core/contracts/ProjectResult.mjs +96 -0
- package/src/core/contracts/RuntimeProxyBoundary.mjs +48 -0
- package/src/core/contracts/ToolkitAsset.mjs +493 -0
- package/src/core/contracts/ToolkitDiagnostic.mjs +38 -0
- package/src/core/contracts/ToolkitError.mjs +176 -0
- package/src/core/contracts/ToolkitProgress.mjs +89 -0
- package/src/core/interaction/CanonicalInteractionOptions.mjs +246 -0
- package/src/core/interaction/PcbInteractionBounds.mjs +167 -0
- package/src/core/query/CircuitTraversal.mjs +343 -0
- package/src/core/query/ComponentGrouping.mjs +275 -0
- package/src/core/query/QueryNetlistBuilder.mjs +306 -0
- package/src/core/query/QueryService.mjs +435 -0
- package/src/core/query/RegexPattern.mjs +75 -0
- package/src/core/rendering/CanonicalBomOrder.mjs +81 -0
- package/src/core/rendering/CanonicalBomRows.mjs +92 -0
- package/src/core/rendering/CanonicalRenderOptions.mjs +498 -0
- package/src/core/rendering/CanonicalSvgDocument.mjs +102 -0
- package/src/core/rendering/PcbRenderPlan.mjs +429 -0
- package/src/core/rendering/SchematicSheetSelector.mjs +335 -0
- package/src/core/scene3d/PcbScene3dBuilder.mjs +906 -0
- package/src/core/scene3d/PcbScene3dPreparator.mjs +47 -0
- package/src/core/scene3d/Scene3dAssetIndex.mjs +284 -0
- package/src/core/scene3d/Scene3dBoardModel.mjs +596 -0
- package/src/core/scene3d/Scene3dDocumentMetadata.mjs +167 -0
- package/src/core/scene3d/Scene3dFreeze.mjs +37 -0
- package/src/core/scene3d/Scene3dIdRegistry.mjs +34 -0
- package/src/core/scene3d/Scene3dInputPreflight.mjs +193 -0
- package/src/core/scene3d/Scene3dMaterials.mjs +58 -0
- package/src/core/scene3d/Scene3dModelReference.mjs +134 -0
- package/src/core/scene3d/Scene3dOptions.mjs +363 -0
- package/src/core/scene3d/SceneAssetResolver.mjs +441 -0
- package/src/core/simulation/SimulationParameterCloner.mjs +543 -0
- package/src/core/worker/ParserWorkerClient.mjs +997 -0
- package/src/core/worker/ToolkitWorkerProtocol.mjs +412 -0
- package/src/core/worker/WorkerRequestData.mjs +623 -0
- package/src/core/worker/WorkerResponseData.mjs +905 -0
- package/src/extensions.mjs +37 -0
- package/src/index.mjs +14 -9
- package/src/interaction.mjs +2 -0
- package/src/manufacturing.mjs +1 -0
- package/src/parser.mjs +12 -2
- package/src/project.mjs +5 -0
- package/src/query.mjs +1 -0
- package/src/renderers.mjs +3 -29
- package/src/scene3d.mjs +3 -0
- package/src/simulation.mjs +1 -0
- package/src/styles/renderers.css +24 -0
- package/src/testing/ToolkitContractFixtures.mjs +124 -0
- package/src/testing/ToolkitLoopbackWorker.mjs +174 -0
- package/src/testing/runToolkitContract.mjs +705 -0
- package/src/testing.mjs +3 -0
- package/src/ui/BomTableRenderer.mjs +304 -0
- package/src/ui/CircuitJsonPcbBoardSvgRenderer.mjs +80 -0
- package/src/ui/CircuitJsonPcbPrimitiveAttributeRenderer.mjs +3 -5
- package/src/ui/CircuitJsonPcbSvgRenderer.mjs +63 -43
- package/src/ui/CircuitJsonPcbViaSvgRenderer.mjs +3 -5
- package/src/ui/CircuitJsonSchematicDebugRenderer.mjs +164 -0
- package/src/ui/CircuitJsonSchematicImageSvgRenderer.mjs +210 -0
- package/src/ui/CircuitJsonSchematicLineRenderer.mjs +86 -0
- package/src/ui/CircuitJsonSchematicSheetSymbolSvgRenderer.mjs +98 -0
- package/src/ui/CircuitJsonSchematicSvgArcPath.mjs +117 -17
- package/src/ui/CircuitJsonSchematicSvgPortMetadata.mjs +67 -20
- package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +45 -9
- package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +151 -148
- package/src/ui/CircuitJsonSchematicTableSvgRenderer.mjs +4 -292
- package/src/ui/PcbSvgRenderer.mjs +41 -0
- package/src/ui/SafeSvgPaint.mjs +26 -0
- package/src/ui/SafeXmlText.mjs +60 -0
- package/src/ui/SchematicGeometryBounds.mjs +540 -0
- package/src/ui/SchematicSvgRenderer.mjs +110 -0
- package/src/ui/SchematicTableGeometry.mjs +319 -0
- package/src/ui/SchematicTextAnchor.mjs +55 -0
- package/src/ui/SchematicTextBounds.mjs +98 -0
- 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)
|