circuitjson-toolkit 1.0.3 → 1.0.16

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 (54) hide show
  1. package/AGENTS.md +5 -3
  2. package/README.md +21 -2
  3. package/docs/api.md +50 -4
  4. package/docs/model-format.md +21 -3
  5. package/package.json +3 -2
  6. package/spec/library-scope.md +4 -1
  7. package/src/core/CircuitJsonBomBuilder.mjs +143 -0
  8. package/src/core/CircuitJsonDocument.mjs +46 -13
  9. package/src/core/CircuitJsonElementValidator.mjs +990 -0
  10. package/src/core/CircuitJsonIndexer.mjs +773 -4
  11. package/src/core/CircuitJsonManufacturingBuilder.mjs +898 -0
  12. package/src/core/CircuitJsonManufacturingDownloadBuilder.mjs +196 -0
  13. package/src/core/CircuitJsonParser.mjs +22 -6
  14. package/src/core/CircuitJsonPcbClearanceDiagnostics.mjs +329 -0
  15. package/src/core/CircuitJsonPcbCopperGeometry.mjs +503 -0
  16. package/src/core/CircuitJsonPcbDrawingStyle.mjs +88 -0
  17. package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +172 -0
  18. package/src/core/CircuitJsonPcbNetMetadata.mjs +247 -0
  19. package/src/core/CircuitJsonPcbPadPrimitiveModel.mjs +70 -0
  20. package/src/core/CircuitJsonPcbPrimitiveArtwork.mjs +992 -0
  21. package/src/core/CircuitJsonPcbPrimitiveBuilder.mjs +872 -0
  22. package/src/core/CircuitJsonPcbPrimitiveFields.mjs +233 -0
  23. package/src/core/CircuitJsonPcbPrimitiveGeometry.mjs +142 -0
  24. package/src/core/CircuitJsonPcbPrimitiveGroups.mjs +305 -0
  25. package/src/core/CircuitJsonPcbPrimitiveIndex.mjs +65 -0
  26. package/src/core/CircuitJsonPcbPrimitiveOverlays.mjs +895 -0
  27. package/src/core/CircuitJsonPcbTraceLengthModel.mjs +257 -0
  28. package/src/core/CircuitJsonPcbZonePrimitiveBuilder.mjs +683 -0
  29. package/src/core/CircuitJsonSourceMetadata.mjs +233 -0
  30. package/src/core/CircuitJsonSupportMatrixBuilder.mjs +481 -0
  31. package/src/core/CircuitJsonUnits.mjs +133 -8
  32. package/src/core/PcbBoundsSelectionModel.mjs +250 -0
  33. package/src/core/PcbCandidateSelectionModel.mjs +77 -0
  34. package/src/core/PcbDiagnosticFocusModel.mjs +423 -0
  35. package/src/core/PcbInteractionPrimitiveModel.mjs +560 -0
  36. package/src/core/SelectedPartCircuitJsonExportAdapter.mjs +335 -0
  37. package/src/core/spice/SpiceCompatibilityPreprocessor.mjs +139 -0
  38. package/src/core/spice/SpiceDirectiveParser.mjs +231 -0
  39. package/src/core/spice/SpiceFallbackSimulationEngine.mjs +168 -0
  40. package/src/core/spice/SpiceSimulationDiagnostics.mjs +234 -0
  41. package/src/core/spice/SpiceSimulationGraphBuilder.mjs +421 -0
  42. package/src/core/spice/SpiceSimulationGraphSummary.mjs +90 -0
  43. package/src/core/spice/SpiceSimulationService.mjs +92 -0
  44. package/src/core/spice/SpiceTimeSeriesNormalizer.mjs +132 -0
  45. package/src/index.mjs +8 -0
  46. package/src/renderers.mjs +29 -0
  47. package/src/ui/CircuitJsonPcbPrimitiveAttributeRenderer.mjs +128 -0
  48. package/src/ui/CircuitJsonPcbSvgRenderer.mjs +964 -0
  49. package/src/ui/CircuitJsonPcbViaSvgRenderer.mjs +168 -0
  50. package/src/ui/CircuitJsonSchematicSvgArcPath.mjs +138 -0
  51. package/src/ui/CircuitJsonSchematicSvgPortMetadata.mjs +114 -0
  52. package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +130 -0
  53. package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +994 -0
  54. package/src/ui/CircuitJsonSchematicTableSvgRenderer.mjs +439 -0
@@ -0,0 +1,233 @@
1
+ const COMPONENT_ICON_BY_TYPE = {
2
+ battery: 'battery',
3
+ capacitor: 'capacitor',
4
+ chip: 'chip',
5
+ connector: 'connector',
6
+ crystal: 'crystal',
7
+ current_source: 'source',
8
+ diode: 'diode',
9
+ fiducial: 'fiducial',
10
+ fuse: 'fuse',
11
+ ground: 'ground',
12
+ inductor: 'inductor',
13
+ interconnect: 'connector',
14
+ led: 'led',
15
+ mosfet: 'transistor',
16
+ op_amp: 'op-amp',
17
+ pin_header: 'connector',
18
+ potentiometer: 'potentiometer',
19
+ push_button: 'switch',
20
+ resistor: 'resistor',
21
+ resonator: 'crystal',
22
+ switch: 'switch',
23
+ test_point: 'test-point',
24
+ transistor: 'transistor',
25
+ voltage_probe: 'probe',
26
+ voltage_source: 'source'
27
+ }
28
+
29
+ /**
30
+ * Normalizes generic source-level CircuitJSON metadata for stable consumers.
31
+ */
32
+ export class CircuitJsonSourceMetadata {
33
+ /**
34
+ * Builds a safe source net name from display-oriented text.
35
+ * @param {unknown} value Source net label.
36
+ * @param {{ fallback?: string, usedNames?: Set<string> }} [options]
37
+ * @returns {string}
38
+ */
39
+ static normalizeSourceNetName(value, options = {}) {
40
+ const fallback = String(options.fallback || 'net').trim() || 'net'
41
+ let name = String(value || '').trim() || fallback
42
+ name = name
43
+ .replaceAll('+', '_P')
44
+ .replaceAll('-', '_')
45
+ .replace(/[^A-Za-z0-9_]+/gu, '_')
46
+ .replace(/_+/gu, '_')
47
+ .replace(/_+$/u, '')
48
+ if (!name) {
49
+ name = fallback.replace(/[^A-Za-z0-9_]+/gu, '_') || 'net'
50
+ }
51
+ if (/^[0-9]/u.test(name)) {
52
+ name = 'net_' + name
53
+ }
54
+ return CircuitJsonSourceMetadata.#dedupeName(name, options.usedNames)
55
+ }
56
+
57
+ /**
58
+ * Builds a stable source port name from a pad or pin label.
59
+ * @param {unknown} value Source port label.
60
+ * @param {{ fallback?: string, usedNames?: Set<string> }} [options]
61
+ * @returns {string}
62
+ */
63
+ static normalizeSourcePortName(value, options = {}) {
64
+ const text = String(value ?? '').trim()
65
+ const fallback = String(options.fallback || 'pin').trim() || 'pin'
66
+ const name = /^[0-9]+$/u.test(text) ? 'pin' + Number(text) : text
67
+ return CircuitJsonSourceMetadata.#dedupeName(
68
+ name || fallback,
69
+ options.usedNames
70
+ )
71
+ }
72
+
73
+ /**
74
+ * Builds stable semantic metadata for one source component.
75
+ * @param {object} component Source component row.
76
+ * @returns {{ sourceFtype: string, componentType: string, componentIcon: string, supplierPartNumber: string, supplierPartNumbers: Record<string, string> }}
77
+ */
78
+ static normalizeSourceComponent(component) {
79
+ const sourceFtype =
80
+ CircuitJsonSourceMetadata.#sourceFtype(component) ||
81
+ CircuitJsonSourceMetadata.#inferSourceFtype(component)
82
+ const componentType = sourceFtype
83
+ .replace(/^simple_/u, '')
84
+ .replaceAll('_', '-')
85
+ const supplierPartNumbers =
86
+ CircuitJsonSourceMetadata.#supplierPartNumbers(component)
87
+ return {
88
+ sourceFtype,
89
+ componentType,
90
+ componentIcon:
91
+ COMPONENT_ICON_BY_TYPE[componentType.replaceAll('-', '_')] ||
92
+ componentType ||
93
+ 'component',
94
+ supplierPartNumber: String(
95
+ Object.values(supplierPartNumbers).find(Boolean) || ''
96
+ ).trim(),
97
+ supplierPartNumbers
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Returns a unique name against an optional used-name set.
103
+ * @param {string} name Base name.
104
+ * @param {Set<string> | undefined} usedNames Existing names.
105
+ * @returns {string}
106
+ */
107
+ static #dedupeName(name, usedNames) {
108
+ if (!(usedNames instanceof Set) || !usedNames.has(name)) return name
109
+ let index = 2
110
+ let candidate = name + '_' + index
111
+ while (usedNames.has(candidate)) {
112
+ index += 1
113
+ candidate = name + '_' + index
114
+ }
115
+ return candidate
116
+ }
117
+
118
+ /**
119
+ * Resolves an explicit source component function type.
120
+ * @param {object} component Source component row.
121
+ * @returns {string}
122
+ */
123
+ static #sourceFtype(component) {
124
+ return String(component?.ftype || component?.sourceFtype || '').trim()
125
+ }
126
+
127
+ /**
128
+ * Infers a source component function type from generic fields.
129
+ * @param {object} component Source component row.
130
+ * @returns {string}
131
+ */
132
+ static #inferSourceFtype(component) {
133
+ const reference = String(
134
+ component?.name ||
135
+ component?.reference ||
136
+ component?.designator ||
137
+ ''
138
+ ).trim()
139
+ const text = [
140
+ reference,
141
+ component?.footprint,
142
+ component?.package,
143
+ component?.package_name,
144
+ component?.value,
145
+ component?.description
146
+ ]
147
+ .map((value) => String(value || '').toLowerCase())
148
+ .filter(Boolean)
149
+ .join(' ')
150
+
151
+ if (text.includes('led')) return 'simple_led'
152
+ if (/^tp[0-9A-Z_-]*/iu.test(reference) || text.includes('test point')) {
153
+ return 'simple_test_point'
154
+ }
155
+ if (/^fid[0-9A-Z_-]*/iu.test(reference) || text.includes('fiducial')) {
156
+ return 'simple_fiducial'
157
+ }
158
+ if (/^sw[0-9A-Z_-]*/iu.test(reference) || text.includes('switch')) {
159
+ return 'simple_switch'
160
+ }
161
+ if (/^r[0-9A-Z_-]*/iu.test(reference) || component?.resistance) {
162
+ return 'simple_resistor'
163
+ }
164
+ if (/^c[0-9A-Z_-]*/iu.test(reference) || component?.capacitance) {
165
+ return 'simple_capacitor'
166
+ }
167
+ if (/^l[0-9A-Z_-]*/iu.test(reference) || component?.inductance) {
168
+ return 'simple_inductor'
169
+ }
170
+ if (text.includes('mosfet')) return 'simple_mosfet'
171
+ if (/^q[0-9A-Z_-]*/iu.test(reference) || text.includes('transistor')) {
172
+ return 'simple_transistor'
173
+ }
174
+ if (/^d[0-9A-Z_-]*/iu.test(reference)) return 'simple_diode'
175
+ if (/^(u|ic)[0-9A-Z_-]*/iu.test(reference) || text.includes('chip')) {
176
+ return 'simple_chip'
177
+ }
178
+ return ''
179
+ }
180
+
181
+ /**
182
+ * Normalizes supplier part fields into a stable keyed object.
183
+ * @param {object} component Source component row.
184
+ * @returns {Record<string, string>}
185
+ */
186
+ static #supplierPartNumbers(component) {
187
+ const numbers = {}
188
+ for (const field of ['supplier_part_numbers', 'supplierPartNumbers']) {
189
+ const value = component?.[field]
190
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
191
+ continue
192
+ }
193
+ for (const [key, entry] of Object.entries(value)) {
194
+ const normalized = String(entry || '').trim()
195
+ if (normalized) numbers[String(key || 'supplier')] = normalized
196
+ }
197
+ }
198
+ CircuitJsonSourceMetadata.#addSupplierNumber(
199
+ numbers,
200
+ 'supplier',
201
+ component?.supplier_part_number ?? component?.supplierPartNumber
202
+ )
203
+ CircuitJsonSourceMetadata.#addSupplierNumber(
204
+ numbers,
205
+ 'distributor',
206
+ component?.distributor_part_number ??
207
+ component?.distributorPartNumber
208
+ )
209
+ CircuitJsonSourceMetadata.#addSupplierNumber(
210
+ numbers,
211
+ 'assembly',
212
+ component?.assembly_part_number ?? component?.assemblyPartNumber
213
+ )
214
+ CircuitJsonSourceMetadata.#addSupplierNumber(
215
+ numbers,
216
+ 'catalog',
217
+ component?.catalog_part_number ?? component?.catalogPartNumber
218
+ )
219
+ return numbers
220
+ }
221
+
222
+ /**
223
+ * Adds one supplier part number when present.
224
+ * @param {Record<string, string>} numbers Supplier number map.
225
+ * @param {string} key Supplier key.
226
+ * @param {unknown} value Supplier part value.
227
+ * @returns {void}
228
+ */
229
+ static #addSupplierNumber(numbers, key, value) {
230
+ const normalized = String(value || '').trim()
231
+ if (normalized && !numbers[key]) numbers[key] = normalized
232
+ }
233
+ }