circuitjson-toolkit 1.0.16 → 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/AGENTS.md +15 -0
- 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
|
@@ -1,289 +1,67 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
'pcb_courtyard_line',
|
|
20
|
-
'pcb_courtyard_outline',
|
|
21
|
-
'pcb_courtyard_path',
|
|
22
|
-
'pcb_courtyard_overlap_error',
|
|
23
|
-
'pcb_courtyard_pill',
|
|
24
|
-
'pcb_courtyard_polygon',
|
|
25
|
-
'pcb_courtyard_rect',
|
|
26
|
-
'pcb_cutout',
|
|
27
|
-
'pcb_fabrication_note_dimension',
|
|
28
|
-
'pcb_fabrication_note_path',
|
|
29
|
-
'pcb_fabrication_note_rect',
|
|
30
|
-
'pcb_fabrication_note_text',
|
|
31
|
-
'pcb_footprint_overlap_error',
|
|
32
|
-
'pcb_ground_plane',
|
|
33
|
-
'pcb_ground_plane_region',
|
|
34
|
-
'pcb_group',
|
|
35
|
-
'pcb_hole',
|
|
36
|
-
'pcb_keepout',
|
|
37
|
-
'pcb_manual_edit_conflict_warning',
|
|
38
|
-
'pcb_missing_footprint_error',
|
|
39
|
-
'pcb_net',
|
|
40
|
-
'pcb_note_dimension',
|
|
41
|
-
'pcb_note_line',
|
|
42
|
-
'pcb_note_path',
|
|
43
|
-
'pcb_note_rect',
|
|
44
|
-
'pcb_note_text',
|
|
45
|
-
'pcb_pad_pad_clearance_error',
|
|
46
|
-
'pcb_pad_trace_clearance_error',
|
|
47
|
-
'pcb_panel',
|
|
48
|
-
'pcb_panelization_placement_error',
|
|
49
|
-
'pcb_placement_error',
|
|
50
|
-
'pcb_plated_hole',
|
|
51
|
-
'pcb_port',
|
|
52
|
-
'pcb_port_not_connected_error',
|
|
53
|
-
'pcb_port_not_matched_error',
|
|
54
|
-
'pcb_silkscreen_circle',
|
|
55
|
-
'pcb_silkscreen_graphic',
|
|
56
|
-
'pcb_silkscreen_line',
|
|
57
|
-
'pcb_silkscreen_oval',
|
|
58
|
-
'pcb_silkscreen_path',
|
|
59
|
-
'pcb_silkscreen_pill',
|
|
60
|
-
'pcb_silkscreen_rect',
|
|
61
|
-
'pcb_silkscreen_text',
|
|
62
|
-
'pcb_smtpad',
|
|
63
|
-
'pcb_solder_paste',
|
|
64
|
-
'pcb_text',
|
|
65
|
-
'pcb_thermal_spoke',
|
|
66
|
-
'pcb_trace',
|
|
67
|
-
'pcb_trace_error',
|
|
68
|
-
'pcb_trace_hint',
|
|
69
|
-
'pcb_trace_missing_error',
|
|
70
|
-
'pcb_trace_warning',
|
|
71
|
-
'pcb_via',
|
|
72
|
-
'pcb_via_clearance_error',
|
|
73
|
-
'pcb_via_trace_clearance_error',
|
|
74
|
-
'schematic_arc',
|
|
75
|
-
'schematic_box',
|
|
76
|
-
'schematic_circle',
|
|
77
|
-
'schematic_component',
|
|
78
|
-
'schematic_debug_object',
|
|
79
|
-
'schematic_error',
|
|
80
|
-
'schematic_group',
|
|
81
|
-
'schematic_layout_error',
|
|
82
|
-
'schematic_line',
|
|
83
|
-
'schematic_manual_edit_conflict_warning',
|
|
84
|
-
'schematic_net_label',
|
|
85
|
-
'schematic_path',
|
|
86
|
-
'schematic_port',
|
|
87
|
-
'schematic_rect',
|
|
88
|
-
'schematic_sheet',
|
|
89
|
-
'schematic_symbol',
|
|
90
|
-
'schematic_table',
|
|
91
|
-
'schematic_table_cell',
|
|
92
|
-
'schematic_text',
|
|
93
|
-
'schematic_trace',
|
|
94
|
-
'schematic_voltage_probe',
|
|
95
|
-
'simulation_current_probe',
|
|
96
|
-
'simulation_current_source',
|
|
97
|
-
'simulation_experiment',
|
|
98
|
-
'simulation_op_amp',
|
|
99
|
-
'simulation_oscilloscope_trace',
|
|
100
|
-
'simulation_spice_subcircuit',
|
|
101
|
-
'simulation_switch',
|
|
102
|
-
'simulation_transient_current_graph',
|
|
103
|
-
'simulation_transient_voltage_graph',
|
|
104
|
-
'simulation_unknown_experiment_error',
|
|
105
|
-
'simulation_voltage_probe',
|
|
106
|
-
'simulation_voltage_source',
|
|
107
|
-
'source_ambiguous_port_reference',
|
|
108
|
-
'source_board',
|
|
109
|
-
'source_component',
|
|
110
|
-
'source_component_internal_connection',
|
|
111
|
-
'source_component_misconfigured_error',
|
|
112
|
-
'source_component_pins_underspecified_warning',
|
|
113
|
-
'source_failed_to_create_component_error',
|
|
114
|
-
'source_group',
|
|
115
|
-
'source_i2c_misconfigured_error',
|
|
116
|
-
'source_invalid_component_property_error',
|
|
117
|
-
'source_manually_placed_via',
|
|
118
|
-
'source_missing_manufacturer_part_number_warning',
|
|
119
|
-
'source_missing_property_error',
|
|
120
|
-
'source_net',
|
|
121
|
-
'source_no_ground_pin_defined_warning',
|
|
122
|
-
'source_no_power_pin_defined_warning',
|
|
123
|
-
'source_pcb_ground_plane',
|
|
124
|
-
'source_pin_missing_trace_warning',
|
|
125
|
-
'source_pin_must_be_connected_error',
|
|
126
|
-
'source_port',
|
|
127
|
-
'source_project_metadata',
|
|
128
|
-
'source_property_ignored_warning',
|
|
129
|
-
'source_trace',
|
|
130
|
-
'source_trace_not_connected_error',
|
|
131
|
-
'supplier_footprint_mismatch_warning',
|
|
132
|
-
'unknown_error_finding_part'
|
|
133
|
-
])
|
|
134
|
-
|
|
135
|
-
const ID_FIELD_EXCEPTIONS = new Set([
|
|
136
|
-
'pcb_autorouting_error',
|
|
137
|
-
'pcb_courtyard_overlap_error',
|
|
138
|
-
'pcb_footprint_overlap_error',
|
|
139
|
-
'pcb_port_not_matched_error',
|
|
140
|
-
'pcb_via_clearance_error',
|
|
141
|
-
'schematic_box',
|
|
142
|
-
'schematic_debug_object',
|
|
143
|
-
'source_project_metadata'
|
|
144
|
-
])
|
|
145
|
-
|
|
146
|
-
const SOURCE_COMPONENT_FTYPES = new Set([
|
|
147
|
-
'interconnect',
|
|
148
|
-
'simple_ammeter',
|
|
149
|
-
'simple_battery',
|
|
150
|
-
'simple_capacitor',
|
|
151
|
-
'simple_chip',
|
|
152
|
-
'simple_connector',
|
|
153
|
-
'simple_crystal',
|
|
154
|
-
'simple_current_source',
|
|
155
|
-
'simple_diode',
|
|
156
|
-
'simple_fiducial',
|
|
157
|
-
'simple_fuse',
|
|
158
|
-
'simple_ground',
|
|
159
|
-
'simple_inductor',
|
|
160
|
-
'simple_led',
|
|
161
|
-
'simple_mosfet',
|
|
162
|
-
'simple_op_amp',
|
|
163
|
-
'simple_pin_header',
|
|
164
|
-
'simple_pinout',
|
|
165
|
-
'simple_potentiometer',
|
|
166
|
-
'simple_power_source',
|
|
167
|
-
'simple_push_button',
|
|
168
|
-
'simple_resistor',
|
|
169
|
-
'simple_resonator',
|
|
170
|
-
'simple_switch',
|
|
171
|
-
'simple_test_point',
|
|
172
|
-
'simple_transistor',
|
|
173
|
-
'simple_voltage_probe',
|
|
174
|
-
'simple_voltage_source'
|
|
175
|
-
])
|
|
176
|
-
|
|
177
|
-
const LAYERS = new Set(
|
|
178
|
-
'top bottom inner1 inner2 inner3 inner4 inner5 inner6'.split(' ')
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
const SMT_PAD_SHAPES = new Set([
|
|
182
|
-
'circle',
|
|
183
|
-
'rect',
|
|
184
|
-
'rotated_rect',
|
|
185
|
-
'rotated_pill',
|
|
186
|
-
'pill',
|
|
187
|
-
'polygon',
|
|
188
|
-
'rounded_rect'
|
|
189
|
-
])
|
|
190
|
-
|
|
191
|
-
const PCB_HOLE_SHAPES = new Set([
|
|
192
|
-
'circle',
|
|
193
|
-
'circle_or_square',
|
|
194
|
-
'oval',
|
|
195
|
-
'pill',
|
|
196
|
-
'rect',
|
|
197
|
-
'rotated_pill',
|
|
198
|
-
'round'
|
|
199
|
-
])
|
|
200
|
-
|
|
201
|
-
const PCB_PLATED_HOLE_SHAPES = new Set([
|
|
202
|
-
'circle',
|
|
203
|
-
'circular_hole_with_rect_pad',
|
|
204
|
-
'hole_with_polygon_pad',
|
|
205
|
-
'oval',
|
|
206
|
-
'pill',
|
|
207
|
-
'pill_hole_with_rect_pad',
|
|
208
|
-
'rotated_pill_hole_with_rect_pad'
|
|
209
|
-
])
|
|
210
|
-
|
|
211
|
-
const PCB_PLATED_HOLE_HOLE_SHAPES = new Set([
|
|
212
|
-
'circle',
|
|
213
|
-
'oval',
|
|
214
|
-
'pill',
|
|
215
|
-
'rotated_pill'
|
|
216
|
-
])
|
|
217
|
-
|
|
218
|
-
const PCB_SOLDER_PASTE_SHAPES = new Set([
|
|
219
|
-
'circle',
|
|
220
|
-
'oval',
|
|
221
|
-
'pill',
|
|
222
|
-
'rect',
|
|
223
|
-
'rotated_rect'
|
|
224
|
-
])
|
|
225
|
-
|
|
226
|
-
const PCB_BOARD_SHAPES = new Set(['polygon', 'rect'])
|
|
227
|
-
|
|
228
|
-
const PCB_CUTOUT_SHAPES = new Set(['circle', 'path', 'polygon', 'rect'])
|
|
229
|
-
|
|
230
|
-
const PCB_COPPER_POUR_SHAPES = new Set(['brep', 'polygon', 'rect'])
|
|
231
|
-
|
|
232
|
-
const SIMULATION_SOURCE_KINDS = new Set(['ac', 'dc'])
|
|
233
|
-
|
|
234
|
-
const SIMULATION_WAVE_SHAPES = new Set(
|
|
235
|
-
'sawtooth sinewave square triangle'.split(' ')
|
|
1
|
+
import { CIRCUIT_JSON_ELEMENT_TYPES } from './CircuitJsonElementTypes.mjs'
|
|
2
|
+
import {
|
|
3
|
+
CIRCUIT_JSON_UPSTREAM_ELEMENT_TYPES,
|
|
4
|
+
CIRCUIT_JSON_UPSTREAM_ID_FIELD_EXCEPTIONS,
|
|
5
|
+
CIRCUIT_JSON_UPSTREAM_VARIANT_SETS
|
|
6
|
+
} from './CircuitJsonUpstreamSchema.mjs'
|
|
7
|
+
import { CircuitJsonToolkitElementSchema } from './CircuitJsonToolkitElementSchema.mjs'
|
|
8
|
+
import { CircuitJsonUpstreamValidator } from './CircuitJsonUpstreamValidator.mjs'
|
|
9
|
+
import { CircuitJsonModelFreezeTraversal } from './context/CircuitJsonModelFreezeTraversal.mjs'
|
|
10
|
+
|
|
11
|
+
const KNOWN_ELEMENT_TYPES = CIRCUIT_JSON_ELEMENT_TYPES
|
|
12
|
+
const UPSTREAM_ELEMENT_TYPES = new Set(CIRCUIT_JSON_UPSTREAM_ELEMENT_TYPES)
|
|
13
|
+
const ID_FIELD_EXCEPTIONS = new Set(CIRCUIT_JSON_UPSTREAM_ID_FIELD_EXCEPTIONS)
|
|
14
|
+
const VARIANT_SETS = Object.fromEntries(
|
|
15
|
+
Object.entries(CIRCUIT_JSON_UPSTREAM_VARIANT_SETS).map(([name, values]) => [
|
|
16
|
+
name,
|
|
17
|
+
new Set(values)
|
|
18
|
+
])
|
|
236
19
|
)
|
|
237
|
-
|
|
238
|
-
const SIMULATION_EXPERIMENT_METHODS = new Set(['gear', 'trap'])
|
|
239
|
-
|
|
240
|
-
const VARIANT_SETS = {
|
|
241
|
-
sourceComponentFtypes: SOURCE_COMPONENT_FTYPES,
|
|
242
|
-
pcbBoardShapes: PCB_BOARD_SHAPES,
|
|
243
|
-
pcbSmtPadShapes: SMT_PAD_SHAPES,
|
|
244
|
-
pcbHoleShapes: PCB_HOLE_SHAPES,
|
|
245
|
-
pcbPlatedHoleShapes: PCB_PLATED_HOLE_SHAPES,
|
|
246
|
-
pcbPlatedHoleHoleShapes: PCB_PLATED_HOLE_HOLE_SHAPES,
|
|
247
|
-
pcbSolderPasteShapes: PCB_SOLDER_PASTE_SHAPES,
|
|
248
|
-
pcbCutoutShapes: PCB_CUTOUT_SHAPES,
|
|
249
|
-
pcbCopperPourShapes: PCB_COPPER_POUR_SHAPES,
|
|
250
|
-
simulationSourceKinds: SIMULATION_SOURCE_KINDS,
|
|
251
|
-
simulationWaveShapes: SIMULATION_WAVE_SHAPES,
|
|
252
|
-
simulationExperimentMethods: SIMULATION_EXPERIMENT_METHODS
|
|
253
|
-
}
|
|
254
|
-
|
|
255
20
|
const SORTED_VARIANT_SET_NAMES = Object.keys(VARIANT_SETS).sort()
|
|
256
21
|
|
|
257
|
-
/**
|
|
258
|
-
|
|
259
|
-
*/
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* @typedef {{ elementTypes: string[], idFieldExceptions: string[], variantSets: Record<string, string[]> }} CircuitJsonSchemaSnapshot
|
|
263
|
-
*/
|
|
22
|
+
/** @typedef {{ set: string, value: string }} CircuitJsonVariantDiff */
|
|
23
|
+
/** @typedef {{ elementTypes: string[], idFieldExceptions: string[], variantSets: Record<string, string[]> }} CircuitJsonSchemaSnapshot */
|
|
24
|
+
/** @typedef {{ matches: boolean, missingElementTypes: string[], unexpectedElementTypes: string[], missingIdFieldExceptions: string[], unexpectedIdFieldExceptions: string[], missingVariants: CircuitJsonVariantDiff[], unexpectedVariants: CircuitJsonVariantDiff[] }} CircuitJsonSchemaSnapshotComparison */
|
|
264
25
|
|
|
265
26
|
/**
|
|
266
|
-
*
|
|
267
|
-
*/
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Validates serialized CircuitJSON element objects without external runtime
|
|
271
|
-
* dependencies.
|
|
27
|
+
* Validates serialized CircuitJSON against the generated pinned upstream union.
|
|
272
28
|
*/
|
|
273
29
|
export class CircuitJsonElementValidator {
|
|
274
30
|
/**
|
|
275
31
|
* Returns validation errors for a candidate model.
|
|
276
32
|
* @param {unknown} value Candidate model.
|
|
33
|
+
* @param {{ freeze?: boolean }} [options] Validation options.
|
|
277
34
|
* @returns {string[]}
|
|
278
35
|
*/
|
|
279
|
-
static validateModel(value) {
|
|
36
|
+
static validateModel(value, options = {}) {
|
|
280
37
|
if (!Array.isArray(value)) {
|
|
281
38
|
return ['Expected a CircuitJSON element array.']
|
|
282
39
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
)
|
|
40
|
+
const traversal = new CircuitJsonModelFreezeTraversal(
|
|
41
|
+
value,
|
|
42
|
+
options.freeze === true
|
|
43
|
+
)
|
|
44
|
+
const errors = traversal.errors()
|
|
45
|
+
if (errors.length === 0) {
|
|
46
|
+
const length = Object.getOwnPropertyDescriptor(
|
|
47
|
+
value,
|
|
48
|
+
'length'
|
|
49
|
+
).value
|
|
50
|
+
for (let index = 0; index < length; index += 1) {
|
|
51
|
+
const element = Object.getOwnPropertyDescriptor(
|
|
52
|
+
value,
|
|
53
|
+
String(index)
|
|
54
|
+
).value
|
|
55
|
+
errors.push(
|
|
56
|
+
...CircuitJsonElementValidator.#validateElementData(
|
|
57
|
+
element,
|
|
58
|
+
index
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
traversal.commit(errors.length === 0)
|
|
64
|
+
return errors
|
|
287
65
|
}
|
|
288
66
|
|
|
289
67
|
/**
|
|
@@ -293,36 +71,39 @@ export class CircuitJsonElementValidator {
|
|
|
293
71
|
* @returns {string[]}
|
|
294
72
|
*/
|
|
295
73
|
static validateElement(value, index = -1) {
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
const type = String(value.type || '').trim()
|
|
302
|
-
if (!type) {
|
|
303
|
-
return ['CircuitJSON element type is required' + location + '.']
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (!KNOWN_ELEMENT_TYPES.has(type)) {
|
|
307
|
-
return ['Unsupported CircuitJSON element type: ' + type + '.']
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const errors = []
|
|
311
|
-
CircuitJsonElementValidator.#validateId(value, type, errors)
|
|
312
|
-
CircuitJsonElementValidator.#validateCoreShape(value, type, errors)
|
|
313
|
-
return errors
|
|
74
|
+
const traversal = new CircuitJsonModelFreezeTraversal([value], false)
|
|
75
|
+
const shapeErrors = traversal.errors()
|
|
76
|
+
return shapeErrors.length
|
|
77
|
+
? shapeErrors
|
|
78
|
+
: CircuitJsonElementValidator.#validateElementData(value, index)
|
|
314
79
|
}
|
|
315
80
|
|
|
316
81
|
/**
|
|
317
|
-
* Returns all
|
|
82
|
+
* Returns all exact pinned upstream element type names.
|
|
318
83
|
* @returns {string[]}
|
|
319
84
|
*/
|
|
320
85
|
static knownElementTypes() {
|
|
86
|
+
return [...UPSTREAM_ELEMENT_TYPES]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Returns every accepted canonical type, including toolkit-owned additions.
|
|
91
|
+
* @returns {string[]}
|
|
92
|
+
*/
|
|
93
|
+
static canonicalElementTypes() {
|
|
321
94
|
return [...KNOWN_ELEMENT_TYPES]
|
|
322
95
|
}
|
|
323
96
|
|
|
324
97
|
/**
|
|
325
|
-
* Returns
|
|
98
|
+
* Returns canonical toolkit types newer than the pinned upstream union.
|
|
99
|
+
* @returns {string[]}
|
|
100
|
+
*/
|
|
101
|
+
static extensionElementTypes() {
|
|
102
|
+
return CircuitJsonToolkitElementSchema.elementTypes()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Returns types without an unconditionally required conventional id field.
|
|
326
107
|
* @returns {string[]}
|
|
327
108
|
*/
|
|
328
109
|
static idFieldExceptions() {
|
|
@@ -330,7 +111,7 @@ export class CircuitJsonElementValidator {
|
|
|
330
111
|
}
|
|
331
112
|
|
|
332
113
|
/**
|
|
333
|
-
* Returns schema metadata suitable for drift snapshots.
|
|
114
|
+
* Returns upstream-derived schema metadata suitable for drift snapshots.
|
|
334
115
|
* @returns {CircuitJsonSchemaSnapshot}
|
|
335
116
|
*/
|
|
336
117
|
static schemaSnapshot() {
|
|
@@ -342,7 +123,7 @@ export class CircuitJsonElementValidator {
|
|
|
342
123
|
}
|
|
343
124
|
|
|
344
125
|
/**
|
|
345
|
-
* Returns
|
|
126
|
+
* Returns upstream-derived discriminant sets.
|
|
346
127
|
* @returns {Record<string, string[]>}
|
|
347
128
|
*/
|
|
348
129
|
static variantSets() {
|
|
@@ -372,7 +153,6 @@ export class CircuitJsonElementValidator {
|
|
|
372
153
|
CircuitJsonElementValidator.#compareVariantSets(
|
|
373
154
|
snapshot.variantSets || {}
|
|
374
155
|
)
|
|
375
|
-
|
|
376
156
|
return {
|
|
377
157
|
matches:
|
|
378
158
|
elementComparison.missing.length === 0 &&
|
|
@@ -391,549 +171,22 @@ export class CircuitJsonElementValidator {
|
|
|
391
171
|
}
|
|
392
172
|
|
|
393
173
|
/**
|
|
394
|
-
* Validates
|
|
395
|
-
* @param {
|
|
396
|
-
* @param {
|
|
397
|
-
* @
|
|
398
|
-
* @returns {void}
|
|
399
|
-
*/
|
|
400
|
-
static #validateId(element, type, errors) {
|
|
401
|
-
if (ID_FIELD_EXCEPTIONS.has(type)) {
|
|
402
|
-
return
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const idField = type + '_id'
|
|
406
|
-
if (!CircuitJsonElementValidator.#isNonEmptyString(element[idField])) {
|
|
407
|
-
errors.push(type + ' ' + idField + ' is required.')
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Validates type-specific fields used by core consumers.
|
|
413
|
-
* @param {Record<string, unknown>} element Element.
|
|
414
|
-
* @param {string} type Element type.
|
|
415
|
-
* @param {string[]} errors Error sink.
|
|
416
|
-
* @returns {void}
|
|
417
|
-
*/
|
|
418
|
-
static #validateCoreShape(element, type, errors) {
|
|
419
|
-
if (type === 'source_component') {
|
|
420
|
-
CircuitJsonElementValidator.#validateSourceComponent(
|
|
421
|
-
element,
|
|
422
|
-
errors
|
|
423
|
-
)
|
|
424
|
-
return
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (type === 'source_port') {
|
|
428
|
-
CircuitJsonElementValidator.#validateSourcePort(element, errors)
|
|
429
|
-
return
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
if (type === 'schematic_component') {
|
|
433
|
-
CircuitJsonElementValidator.#validateSchematicComponent(
|
|
434
|
-
element,
|
|
435
|
-
errors
|
|
436
|
-
)
|
|
437
|
-
return
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
if (type === 'pcb_board') {
|
|
441
|
-
CircuitJsonElementValidator.#validatePcbBoard(element, errors)
|
|
442
|
-
return
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
if (type === 'pcb_component') {
|
|
446
|
-
CircuitJsonElementValidator.#validatePcbComponent(element, errors)
|
|
447
|
-
return
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
if (type === 'pcb_smtpad') {
|
|
451
|
-
CircuitJsonElementValidator.#validatePcbSmtPad(element, errors)
|
|
452
|
-
return
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (type === 'simulation_oscilloscope_trace') {
|
|
456
|
-
CircuitJsonElementValidator.#validateSimulationOscilloscopeTrace(
|
|
457
|
-
element,
|
|
458
|
-
errors
|
|
459
|
-
)
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Validates a source component.
|
|
465
|
-
* @param {Record<string, unknown>} element Element.
|
|
466
|
-
* @param {string[]} errors Error sink.
|
|
467
|
-
* @returns {void}
|
|
468
|
-
*/
|
|
469
|
-
static #validateSourceComponent(element, errors) {
|
|
470
|
-
CircuitJsonElementValidator.#requireString(
|
|
471
|
-
element,
|
|
472
|
-
'source_component',
|
|
473
|
-
'name',
|
|
474
|
-
errors
|
|
475
|
-
)
|
|
476
|
-
|
|
477
|
-
if (
|
|
478
|
-
Object.hasOwn(element, 'supplier_part_numbers') &&
|
|
479
|
-
!CircuitJsonElementValidator.#isPlainObject(
|
|
480
|
-
element.supplier_part_numbers
|
|
481
|
-
)
|
|
482
|
-
) {
|
|
483
|
-
errors.push(
|
|
484
|
-
'source_component supplier_part_numbers must be an object.'
|
|
485
|
-
)
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (
|
|
489
|
-
Object.hasOwn(element, 'ftype') &&
|
|
490
|
-
!SOURCE_COMPONENT_FTYPES.has(String(element.ftype || ''))
|
|
491
|
-
) {
|
|
492
|
-
errors.push('source_component ftype is not supported.')
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
* Validates a source port.
|
|
498
|
-
* @param {Record<string, unknown>} element Element.
|
|
499
|
-
* @param {string[]} errors Error sink.
|
|
500
|
-
* @returns {void}
|
|
501
|
-
*/
|
|
502
|
-
static #validateSourcePort(element, errors) {
|
|
503
|
-
if (
|
|
504
|
-
Object.hasOwn(element, 'pin_number') &&
|
|
505
|
-
!Number.isFinite(element.pin_number)
|
|
506
|
-
) {
|
|
507
|
-
errors.push('source_port pin_number must be a number.')
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
/**
|
|
512
|
-
* Validates a schematic component.
|
|
513
|
-
* @param {Record<string, unknown>} element Element.
|
|
514
|
-
* @param {string[]} errors Error sink.
|
|
515
|
-
* @returns {void}
|
|
516
|
-
*/
|
|
517
|
-
static #validateSchematicComponent(element, errors) {
|
|
518
|
-
CircuitJsonElementValidator.#requirePoint(
|
|
519
|
-
element,
|
|
520
|
-
'schematic_component',
|
|
521
|
-
'center',
|
|
522
|
-
errors
|
|
523
|
-
)
|
|
524
|
-
CircuitJsonElementValidator.#requireSize(
|
|
525
|
-
element,
|
|
526
|
-
'schematic_component',
|
|
527
|
-
'size',
|
|
528
|
-
errors
|
|
529
|
-
)
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Validates a PCB board.
|
|
534
|
-
* @param {Record<string, unknown>} element Element.
|
|
535
|
-
* @param {string[]} errors Error sink.
|
|
536
|
-
* @returns {void}
|
|
537
|
-
*/
|
|
538
|
-
static #validatePcbBoard(element, errors) {
|
|
539
|
-
CircuitJsonElementValidator.#requirePoint(
|
|
540
|
-
element,
|
|
541
|
-
'pcb_board',
|
|
542
|
-
'center',
|
|
543
|
-
errors
|
|
544
|
-
)
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Validates a PCB component.
|
|
549
|
-
* @param {Record<string, unknown>} element Element.
|
|
550
|
-
* @param {string[]} errors Error sink.
|
|
551
|
-
* @returns {void}
|
|
552
|
-
*/
|
|
553
|
-
static #validatePcbComponent(element, errors) {
|
|
554
|
-
CircuitJsonElementValidator.#requireString(
|
|
555
|
-
element,
|
|
556
|
-
'pcb_component',
|
|
557
|
-
'source_component_id',
|
|
558
|
-
errors
|
|
559
|
-
)
|
|
560
|
-
CircuitJsonElementValidator.#requirePoint(
|
|
561
|
-
element,
|
|
562
|
-
'pcb_component',
|
|
563
|
-
'center',
|
|
564
|
-
errors
|
|
565
|
-
)
|
|
566
|
-
CircuitJsonElementValidator.#requireLayer(
|
|
567
|
-
element,
|
|
568
|
-
'pcb_component',
|
|
569
|
-
'layer',
|
|
570
|
-
errors
|
|
571
|
-
)
|
|
572
|
-
CircuitJsonElementValidator.#optionalAngle(
|
|
573
|
-
element,
|
|
574
|
-
'pcb_component',
|
|
575
|
-
'rotation',
|
|
576
|
-
errors
|
|
577
|
-
)
|
|
578
|
-
CircuitJsonElementValidator.#optionalLength(
|
|
579
|
-
element,
|
|
580
|
-
'pcb_component',
|
|
581
|
-
'width',
|
|
582
|
-
errors
|
|
583
|
-
)
|
|
584
|
-
CircuitJsonElementValidator.#optionalLength(
|
|
585
|
-
element,
|
|
586
|
-
'pcb_component',
|
|
587
|
-
'height',
|
|
588
|
-
errors
|
|
589
|
-
)
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
/**
|
|
593
|
-
* Validates an SMT pad.
|
|
594
|
-
* @param {Record<string, unknown>} element Element.
|
|
595
|
-
* @param {string[]} errors Error sink.
|
|
596
|
-
* @returns {void}
|
|
597
|
-
*/
|
|
598
|
-
static #validatePcbSmtPad(element, errors) {
|
|
599
|
-
const shape = String(element.shape || '')
|
|
600
|
-
if (!SMT_PAD_SHAPES.has(shape)) {
|
|
601
|
-
errors.push(
|
|
602
|
-
'pcb_smtpad shape must be one of: ' +
|
|
603
|
-
[...SMT_PAD_SHAPES].join(', ') +
|
|
604
|
-
'.'
|
|
605
|
-
)
|
|
606
|
-
return
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
CircuitJsonElementValidator.#requireLayer(
|
|
610
|
-
element,
|
|
611
|
-
'pcb_smtpad',
|
|
612
|
-
'layer',
|
|
613
|
-
errors
|
|
614
|
-
)
|
|
615
|
-
|
|
616
|
-
if (shape === 'polygon') {
|
|
617
|
-
if (!Array.isArray(element.points)) {
|
|
618
|
-
errors.push('pcb_smtpad points is required.')
|
|
619
|
-
}
|
|
620
|
-
return
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
CircuitJsonElementValidator.#requireLength(
|
|
624
|
-
element,
|
|
625
|
-
'pcb_smtpad',
|
|
626
|
-
'x',
|
|
627
|
-
errors
|
|
628
|
-
)
|
|
629
|
-
CircuitJsonElementValidator.#requireLength(
|
|
630
|
-
element,
|
|
631
|
-
'pcb_smtpad',
|
|
632
|
-
'y',
|
|
633
|
-
errors
|
|
634
|
-
)
|
|
635
|
-
|
|
636
|
-
if (shape === 'circle') {
|
|
637
|
-
if (
|
|
638
|
-
!CircuitJsonElementValidator.#hasLength(element, 'radius') &&
|
|
639
|
-
!CircuitJsonElementValidator.#hasLength(element, 'diameter') &&
|
|
640
|
-
!(
|
|
641
|
-
CircuitJsonElementValidator.#hasLength(element, 'width') &&
|
|
642
|
-
CircuitJsonElementValidator.#hasLength(element, 'height')
|
|
643
|
-
)
|
|
644
|
-
) {
|
|
645
|
-
errors.push(
|
|
646
|
-
'pcb_smtpad radius, diameter, or width and height is required.'
|
|
647
|
-
)
|
|
648
|
-
}
|
|
649
|
-
return
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
CircuitJsonElementValidator.#requireLength(
|
|
653
|
-
element,
|
|
654
|
-
'pcb_smtpad',
|
|
655
|
-
'width',
|
|
656
|
-
errors
|
|
657
|
-
)
|
|
658
|
-
CircuitJsonElementValidator.#requireLength(
|
|
659
|
-
element,
|
|
660
|
-
'pcb_smtpad',
|
|
661
|
-
'height',
|
|
662
|
-
errors
|
|
663
|
-
)
|
|
664
|
-
|
|
665
|
-
if (shape.startsWith('rotated_')) {
|
|
666
|
-
CircuitJsonElementValidator.#requireAngle(
|
|
667
|
-
element,
|
|
668
|
-
'pcb_smtpad',
|
|
669
|
-
'ccw_rotation',
|
|
670
|
-
errors
|
|
671
|
-
)
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
if (shape.endsWith('pill')) {
|
|
675
|
-
CircuitJsonElementValidator.#optionalLength(
|
|
676
|
-
element,
|
|
677
|
-
'pcb_smtpad',
|
|
678
|
-
'radius',
|
|
679
|
-
errors
|
|
680
|
-
)
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
/**
|
|
685
|
-
* Validates oscilloscope trace reference and unit constraints.
|
|
686
|
-
* @param {Record<string, unknown>} element Element.
|
|
687
|
-
* @param {string[]} errors Error sink.
|
|
688
|
-
* @returns {void}
|
|
689
|
-
*/
|
|
690
|
-
static #validateSimulationOscilloscopeTrace(element, errors) {
|
|
691
|
-
const voltageReferences = [
|
|
692
|
-
element.simulation_transient_voltage_graph_id,
|
|
693
|
-
element.simulation_voltage_probe_id
|
|
694
|
-
].filter((value) =>
|
|
695
|
-
CircuitJsonElementValidator.#isNonEmptyString(value)
|
|
696
|
-
).length
|
|
697
|
-
const currentReferences = [
|
|
698
|
-
element.simulation_transient_current_graph_id,
|
|
699
|
-
element.simulation_current_probe_id
|
|
700
|
-
].filter((value) =>
|
|
701
|
-
CircuitJsonElementValidator.#isNonEmptyString(value)
|
|
702
|
-
).length
|
|
703
|
-
|
|
704
|
-
if (voltageReferences + currentReferences !== 1) {
|
|
705
|
-
errors.push(
|
|
706
|
-
'simulation_oscilloscope_trace must reference exactly one graph or probe.'
|
|
707
|
-
)
|
|
708
|
-
}
|
|
709
|
-
if (voltageReferences > 0 && Object.hasOwn(element, 'amps_per_div')) {
|
|
710
|
-
errors.push(
|
|
711
|
-
'voltage oscilloscope traces must use volts_per_div, not amps_per_div.'
|
|
712
|
-
)
|
|
713
|
-
}
|
|
714
|
-
if (currentReferences > 0 && Object.hasOwn(element, 'volts_per_div')) {
|
|
715
|
-
errors.push(
|
|
716
|
-
'current oscilloscope traces must use amps_per_div, not volts_per_div.'
|
|
717
|
-
)
|
|
718
|
-
}
|
|
719
|
-
CircuitJsonElementValidator.#optionalPositiveNumber(
|
|
720
|
-
element,
|
|
721
|
-
'simulation_oscilloscope_trace',
|
|
722
|
-
'volts_per_div',
|
|
723
|
-
errors
|
|
724
|
-
)
|
|
725
|
-
CircuitJsonElementValidator.#optionalPositiveNumber(
|
|
726
|
-
element,
|
|
727
|
-
'simulation_oscilloscope_trace',
|
|
728
|
-
'amps_per_div',
|
|
729
|
-
errors
|
|
730
|
-
)
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
/**
|
|
734
|
-
* Requires a non-empty string field.
|
|
735
|
-
* @param {Record<string, unknown>} element Element.
|
|
736
|
-
* @param {string} type Element type.
|
|
737
|
-
* @param {string} field Field name.
|
|
738
|
-
* @param {string[]} errors Error sink.
|
|
739
|
-
* @returns {void}
|
|
740
|
-
*/
|
|
741
|
-
static #requireString(element, type, field, errors) {
|
|
742
|
-
if (!CircuitJsonElementValidator.#isNonEmptyString(element[field])) {
|
|
743
|
-
errors.push(type + ' ' + field + ' is required.')
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
/**
|
|
748
|
-
* Requires a finite number field.
|
|
749
|
-
* @param {Record<string, unknown>} element Element.
|
|
750
|
-
* @param {string} type Element type.
|
|
751
|
-
* @param {string} field Field name.
|
|
752
|
-
* @param {string[]} errors Error sink.
|
|
753
|
-
* @returns {void}
|
|
754
|
-
*/
|
|
755
|
-
static #requireNumber(element, type, field, errors) {
|
|
756
|
-
if (!Number.isFinite(element[field])) {
|
|
757
|
-
errors.push(type + ' ' + field + ' is required.')
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
/**
|
|
762
|
-
* Requires a finite length field.
|
|
763
|
-
* @param {Record<string, unknown>} element Element.
|
|
764
|
-
* @param {string} type Element type.
|
|
765
|
-
* @param {string} field Field name.
|
|
766
|
-
* @param {string[]} errors Error sink.
|
|
767
|
-
* @returns {void}
|
|
768
|
-
*/
|
|
769
|
-
static #requireLength(element, type, field, errors) {
|
|
770
|
-
if (CircuitJsonUnits.optionalLength(element[field]) === null) {
|
|
771
|
-
errors.push(type + ' ' + field + ' is required.')
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
/**
|
|
776
|
-
* Returns true when a field is a finite length.
|
|
777
|
-
* @param {Record<string, unknown>} element Element.
|
|
778
|
-
* @param {string} field Field name.
|
|
779
|
-
* @returns {boolean}
|
|
780
|
-
*/
|
|
781
|
-
static #hasLength(element, field) {
|
|
782
|
-
return CircuitJsonUnits.optionalLength(element[field]) !== null
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
/**
|
|
786
|
-
* Requires a finite angle field.
|
|
787
|
-
* @param {Record<string, unknown>} element Element.
|
|
788
|
-
* @param {string} type Element type.
|
|
789
|
-
* @param {string} field Field name.
|
|
790
|
-
* @param {string[]} errors Error sink.
|
|
791
|
-
* @returns {void}
|
|
792
|
-
*/
|
|
793
|
-
static #requireAngle(element, type, field, errors) {
|
|
794
|
-
if (CircuitJsonUnits.optionalAngle(element[field]) === null) {
|
|
795
|
-
errors.push(type + ' ' + field + ' is required.')
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
/**
|
|
800
|
-
* Validates an optional finite length field.
|
|
801
|
-
* @param {Record<string, unknown>} element Element.
|
|
802
|
-
* @param {string} type Element type.
|
|
803
|
-
* @param {string} field Field name.
|
|
804
|
-
* @param {string[]} errors Error sink.
|
|
805
|
-
* @returns {void}
|
|
806
|
-
*/
|
|
807
|
-
static #optionalLength(element, type, field, errors) {
|
|
808
|
-
if (
|
|
809
|
-
Object.hasOwn(element, field) &&
|
|
810
|
-
CircuitJsonUnits.optionalLength(element[field]) === null
|
|
811
|
-
) {
|
|
812
|
-
errors.push(type + ' ' + field + ' must be a finite length.')
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
/**
|
|
817
|
-
* Validates an optional finite angle field.
|
|
818
|
-
* @param {Record<string, unknown>} element Element.
|
|
819
|
-
* @param {string} type Element type.
|
|
820
|
-
* @param {string} field Field name.
|
|
821
|
-
* @param {string[]} errors Error sink.
|
|
822
|
-
* @returns {void}
|
|
823
|
-
*/
|
|
824
|
-
static #optionalAngle(element, type, field, errors) {
|
|
825
|
-
if (
|
|
826
|
-
Object.hasOwn(element, field) &&
|
|
827
|
-
CircuitJsonUnits.optionalAngle(element[field]) === null
|
|
828
|
-
) {
|
|
829
|
-
errors.push(type + ' ' + field + ' must be a finite angle.')
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* Validates an optional positive finite number field.
|
|
835
|
-
* @param {Record<string, unknown>} element Element.
|
|
836
|
-
* @param {string} type Element type.
|
|
837
|
-
* @param {string} field Field name.
|
|
838
|
-
* @param {string[]} errors Error sink.
|
|
839
|
-
* @returns {void}
|
|
840
|
-
*/
|
|
841
|
-
static #optionalPositiveNumber(element, type, field, errors) {
|
|
842
|
-
if (!Object.hasOwn(element, field)) {
|
|
843
|
-
return
|
|
844
|
-
}
|
|
845
|
-
const value = Number(element[field])
|
|
846
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
847
|
-
errors.push(type + ' ' + field + ' must be a positive number.')
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
/**
|
|
852
|
-
* Requires a finite point object field.
|
|
853
|
-
* @param {Record<string, unknown>} element Element.
|
|
854
|
-
* @param {string} type Element type.
|
|
855
|
-
* @param {string} field Field name.
|
|
856
|
-
* @param {string[]} errors Error sink.
|
|
857
|
-
* @returns {void}
|
|
858
|
-
*/
|
|
859
|
-
static #requirePoint(element, type, field, errors) {
|
|
860
|
-
const point = element[field]
|
|
861
|
-
if (!CircuitJsonElementValidator.#isObject(point)) {
|
|
862
|
-
errors.push(type + ' ' + field + ' is required.')
|
|
863
|
-
return
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
if (CircuitJsonUnits.optionalPoint(point) === null) {
|
|
867
|
-
errors.push(type + ' ' + field + ' is required.')
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Requires a finite size object field.
|
|
873
|
-
* @param {Record<string, unknown>} element Element.
|
|
874
|
-
* @param {string} type Element type.
|
|
875
|
-
* @param {string} field Field name.
|
|
876
|
-
* @param {string[]} errors Error sink.
|
|
877
|
-
* @returns {void}
|
|
174
|
+
* Validates one element after descriptor-safety traversal.
|
|
175
|
+
* @param {unknown} value Candidate element.
|
|
176
|
+
* @param {number} index Element index.
|
|
177
|
+
* @returns {string[]} Validation errors.
|
|
878
178
|
*/
|
|
879
|
-
static #
|
|
880
|
-
const
|
|
881
|
-
if (!
|
|
882
|
-
|
|
883
|
-
return
|
|
179
|
+
static #validateElementData(value, index) {
|
|
180
|
+
const location = index >= 0 ? ` at index ${index}` : ''
|
|
181
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
182
|
+
return [`Expected a CircuitJSON element object${location}.`]
|
|
884
183
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
/**
|
|
892
|
-
* Requires a known layer field.
|
|
893
|
-
* @param {Record<string, unknown>} element Element.
|
|
894
|
-
* @param {string} type Element type.
|
|
895
|
-
* @param {string} field Field name.
|
|
896
|
-
* @param {string[]} errors Error sink.
|
|
897
|
-
* @returns {void}
|
|
898
|
-
*/
|
|
899
|
-
static #requireLayer(element, type, field, errors) {
|
|
900
|
-
const layer =
|
|
901
|
-
typeof element[field] === 'object' && element[field] !== null
|
|
902
|
-
? element[field].name
|
|
903
|
-
: element[field]
|
|
904
|
-
if (!LAYERS.has(String(layer || ''))) {
|
|
905
|
-
errors.push(type + ' ' + field + ' is required.')
|
|
184
|
+
const typeValue = value.type
|
|
185
|
+
const type = typeof typeValue === 'string' ? typeValue.trim() : ''
|
|
186
|
+
if (!type) {
|
|
187
|
+
return [`CircuitJSON element type is required${location}.`]
|
|
906
188
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
/**
|
|
910
|
-
* Returns true for non-null objects.
|
|
911
|
-
* @param {unknown} value Candidate.
|
|
912
|
-
* @returns {boolean}
|
|
913
|
-
*/
|
|
914
|
-
static #isObject(value) {
|
|
915
|
-
return Boolean(value) && typeof value === 'object'
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
/**
|
|
919
|
-
* Returns true for plain object values.
|
|
920
|
-
* @param {unknown} value Candidate.
|
|
921
|
-
* @returns {boolean}
|
|
922
|
-
*/
|
|
923
|
-
static #isPlainObject(value) {
|
|
924
|
-
return (
|
|
925
|
-
CircuitJsonElementValidator.#isObject(value) &&
|
|
926
|
-
!Array.isArray(value)
|
|
927
|
-
)
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
/**
|
|
931
|
-
* Returns true for non-empty strings.
|
|
932
|
-
* @param {unknown} value Candidate.
|
|
933
|
-
* @returns {boolean}
|
|
934
|
-
*/
|
|
935
|
-
static #isNonEmptyString(value) {
|
|
936
|
-
return typeof value === 'string' && value.trim().length > 0
|
|
189
|
+
return CircuitJsonUpstreamValidator.validate(value, type, location)
|
|
937
190
|
}
|
|
938
191
|
|
|
939
192
|
/**
|
|
@@ -954,37 +207,35 @@ export class CircuitJsonElementValidator {
|
|
|
954
207
|
}
|
|
955
208
|
|
|
956
209
|
/**
|
|
957
|
-
* Compares expected and actual variant
|
|
210
|
+
* Compares expected and actual upstream variant sets.
|
|
958
211
|
* @param {Record<string, string[]>} snapshotSets Snapshot variant sets.
|
|
959
212
|
* @returns {{ missing: CircuitJsonVariantDiff[], unexpected: CircuitJsonVariantDiff[] }}
|
|
960
213
|
*/
|
|
961
214
|
static #compareVariantSets(snapshotSets) {
|
|
962
215
|
const missing = []
|
|
963
216
|
const unexpected = []
|
|
964
|
-
const
|
|
217
|
+
const names = new Set([
|
|
965
218
|
...SORTED_VARIANT_SET_NAMES,
|
|
966
|
-
...Object.keys(snapshotSets
|
|
219
|
+
...Object.keys(snapshotSets)
|
|
967
220
|
])
|
|
968
|
-
|
|
969
|
-
for (const setName of [...setNames].sort()) {
|
|
221
|
+
for (const name of [...names].sort()) {
|
|
970
222
|
const comparison = CircuitJsonElementValidator.#compareSets(
|
|
971
|
-
new Set((snapshotSets
|
|
972
|
-
new Set(VARIANT_SETS[
|
|
223
|
+
new Set((snapshotSets[name] || []).map(String)),
|
|
224
|
+
new Set(VARIANT_SETS[name] || [])
|
|
973
225
|
)
|
|
974
226
|
missing.push(
|
|
975
|
-
...comparison.missing.map((value) => ({
|
|
976
|
-
set: setName,
|
|
977
|
-
value
|
|
978
|
-
}))
|
|
227
|
+
...comparison.missing.map((value) => ({ set: name, value }))
|
|
979
228
|
)
|
|
980
229
|
unexpected.push(
|
|
981
230
|
...comparison.unexpected.map((value) => ({
|
|
982
|
-
set:
|
|
231
|
+
set: name,
|
|
983
232
|
value
|
|
984
233
|
}))
|
|
985
234
|
)
|
|
986
235
|
}
|
|
987
|
-
|
|
988
236
|
return { missing, unexpected }
|
|
989
237
|
}
|
|
990
238
|
}
|
|
239
|
+
|
|
240
|
+
Object.freeze(CircuitJsonElementValidator.prototype)
|
|
241
|
+
Object.freeze(CircuitJsonElementValidator)
|