@vessel-dsp/core 0.6.0 → 0.6.1

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 (238) hide show
  1. package/dist/editor/commands.d.ts +48 -0
  2. package/dist/editor/commands.d.ts.map +1 -0
  3. package/{src/editor/commands.ts → dist/editor/commands.js} +44 -91
  4. package/dist/editor/commands.js.map +1 -0
  5. package/dist/editor/factory.d.ts +10 -0
  6. package/dist/editor/factory.d.ts.map +1 -0
  7. package/{src/editor/factory.ts → dist/editor/factory.js} +11 -27
  8. package/dist/editor/factory.js.map +1 -0
  9. package/dist/editor/history.d.ts +29 -0
  10. package/dist/editor/history.d.ts.map +1 -0
  11. package/{src/editor/history.ts → dist/editor/history.js} +12 -42
  12. package/dist/editor/history.js.map +1 -0
  13. package/{src/editor/index.ts → dist/editor/index.d.ts} +1 -3
  14. package/dist/editor/index.d.ts.map +1 -0
  15. package/dist/editor/index.js +5 -0
  16. package/dist/editor/index.js.map +1 -0
  17. package/dist/editor/layout.d.ts +8 -0
  18. package/dist/editor/layout.d.ts.map +1 -0
  19. package/{src/editor/layout.ts → dist/editor/layout.js} +36 -90
  20. package/dist/editor/layout.js.map +1 -0
  21. package/dist/formats/circuit-json/serializer.d.ts +86 -0
  22. package/dist/formats/circuit-json/serializer.d.ts.map +1 -0
  23. package/{src/formats/circuit-json/serializer.ts → dist/formats/circuit-json/serializer.js} +114 -414
  24. package/dist/formats/circuit-json/serializer.js.map +1 -0
  25. package/dist/formats/document.d.ts +64 -0
  26. package/dist/formats/document.d.ts.map +1 -0
  27. package/{src/formats/document.ts → dist/formats/document.js} +43 -159
  28. package/dist/formats/document.js.map +1 -0
  29. package/dist/formats/interchange/parser.d.ts +3 -0
  30. package/dist/formats/interchange/parser.d.ts.map +1 -0
  31. package/{src/formats/interchange/parser.ts → dist/formats/interchange/parser.js} +157 -463
  32. package/dist/formats/interchange/parser.js.map +1 -0
  33. package/dist/formats/interchange/serializer.d.ts +9 -0
  34. package/dist/formats/interchange/serializer.d.ts.map +1 -0
  35. package/{src/formats/interchange/serializer.ts → dist/formats/interchange/serializer.js} +84 -186
  36. package/dist/formats/interchange/serializer.js.map +1 -0
  37. package/dist/formats/ltspice/catalog.d.ts +19 -0
  38. package/dist/formats/ltspice/catalog.d.ts.map +1 -0
  39. package/{src/formats/ltspice/catalog.ts → dist/formats/ltspice/catalog.js} +18 -52
  40. package/dist/formats/ltspice/catalog.js.map +1 -0
  41. package/dist/formats/ltspice/encoding.d.ts +2 -0
  42. package/dist/formats/ltspice/encoding.d.ts.map +1 -0
  43. package/{src/formats/ltspice/encoding.ts → dist/formats/ltspice/encoding.js} +17 -41
  44. package/dist/formats/ltspice/encoding.js.map +1 -0
  45. package/dist/formats/ltspice/parser.d.ts +3 -0
  46. package/dist/formats/ltspice/parser.d.ts.map +1 -0
  47. package/{src/formats/ltspice/parser.ts → dist/formats/ltspice/parser.js} +39 -141
  48. package/dist/formats/ltspice/parser.js.map +1 -0
  49. package/dist/formats/ltspice/serializer.d.ts +7 -0
  50. package/dist/formats/ltspice/serializer.d.ts.map +1 -0
  51. package/{src/formats/ltspice/serializer.ts → dist/formats/ltspice/serializer.js} +18 -45
  52. package/dist/formats/ltspice/serializer.js.map +1 -0
  53. package/dist/formats/schx/catalog.d.ts +19 -0
  54. package/dist/formats/schx/catalog.d.ts.map +1 -0
  55. package/{src/formats/schx/catalog.ts → dist/formats/schx/catalog.js} +48 -101
  56. package/dist/formats/schx/catalog.js.map +1 -0
  57. package/dist/formats/schx/parser.d.ts +3 -0
  58. package/dist/formats/schx/parser.d.ts.map +1 -0
  59. package/{src/formats/schx/parser.ts → dist/formats/schx/parser.js} +31 -86
  60. package/dist/formats/schx/parser.js.map +1 -0
  61. package/dist/formats/schx/runtime-descriptors.d.ts +3 -0
  62. package/dist/formats/schx/runtime-descriptors.d.ts.map +1 -0
  63. package/{src/formats/schx/runtime-descriptors.ts → dist/formats/schx/runtime-descriptors.js} +36 -123
  64. package/dist/formats/schx/runtime-descriptors.js.map +1 -0
  65. package/dist/formats/schx/serializer.d.ts +5 -0
  66. package/dist/formats/schx/serializer.d.ts.map +1 -0
  67. package/{src/formats/schx/serializer.ts → dist/formats/schx/serializer.js} +17 -42
  68. package/dist/formats/schx/serializer.js.map +1 -0
  69. package/dist/formats/schx/transforms.d.ts +4 -0
  70. package/dist/formats/schx/transforms.d.ts.map +1 -0
  71. package/{src/formats/schx/transforms.ts → dist/formats/schx/transforms.js} +6 -10
  72. package/dist/formats/schx/transforms.js.map +1 -0
  73. package/dist/formats/spice/parser.d.ts +3 -0
  74. package/dist/formats/spice/parser.d.ts.map +1 -0
  75. package/{src/formats/spice/parser.ts → dist/formats/spice/parser.js} +50 -96
  76. package/dist/formats/spice/parser.js.map +1 -0
  77. package/dist/formats/spice/serializer.d.ts +3 -0
  78. package/dist/formats/spice/serializer.d.ts.map +1 -0
  79. package/{src/formats/spice/serializer.ts → dist/formats/spice/serializer.js} +8 -13
  80. package/dist/formats/spice/serializer.js.map +1 -0
  81. package/dist/index.d.ts +47 -0
  82. package/dist/index.d.ts.map +1 -0
  83. package/dist/index.js +32 -0
  84. package/dist/index.js.map +1 -0
  85. package/dist/model/connectivity.d.ts +16 -0
  86. package/dist/model/connectivity.d.ts.map +1 -0
  87. package/{src/model/connectivity.ts → dist/model/connectivity.js} +28 -63
  88. package/dist/model/connectivity.js.map +1 -0
  89. package/dist/model/netlist.d.ts +24 -0
  90. package/dist/model/netlist.d.ts.map +1 -0
  91. package/{src/model/netlist.ts → dist/model/netlist.js} +42 -110
  92. package/dist/model/netlist.js.map +1 -0
  93. package/dist/model/properties.d.ts +9 -0
  94. package/dist/model/properties.d.ts.map +1 -0
  95. package/{src/model/properties.ts → dist/model/properties.js} +10 -18
  96. package/dist/model/properties.js.map +1 -0
  97. package/dist/model/quantity.d.ts +3 -0
  98. package/dist/model/quantity.d.ts.map +1 -0
  99. package/{src/model/quantity.ts → dist/model/quantity.js} +7 -30
  100. package/dist/model/quantity.js.map +1 -0
  101. package/{src/model/types.ts → dist/model/types.d.ts} +17 -196
  102. package/dist/model/types.d.ts.map +1 -0
  103. package/dist/model/types.js +10 -0
  104. package/dist/model/types.js.map +1 -0
  105. package/dist/model/validation.d.ts +32 -0
  106. package/dist/model/validation.d.ts.map +1 -0
  107. package/{src/model/validation.ts → dist/model/validation.js} +172 -653
  108. package/dist/model/validation.js.map +1 -0
  109. package/dist/model/wires.d.ts +3 -0
  110. package/dist/model/wires.d.ts.map +1 -0
  111. package/{src/model/wires.ts → dist/model/wires.js} +10 -16
  112. package/dist/model/wires.js.map +1 -0
  113. package/dist/panel/extract.d.ts +5 -0
  114. package/dist/panel/extract.d.ts.map +1 -0
  115. package/{src/panel/extract.ts → dist/panel/extract.js} +146 -235
  116. package/dist/panel/extract.js.map +1 -0
  117. package/dist/panel/index.d.ts +6 -0
  118. package/dist/panel/index.d.ts.map +1 -0
  119. package/dist/panel/index.js +5 -0
  120. package/dist/panel/index.js.map +1 -0
  121. package/dist/panel/knobs.d.ts +7 -0
  122. package/dist/panel/knobs.d.ts.map +1 -0
  123. package/{src/panel/knobs.ts → dist/panel/knobs.js} +7 -18
  124. package/dist/panel/knobs.js.map +1 -0
  125. package/dist/panel/protocol.d.ts +9 -0
  126. package/dist/panel/protocol.d.ts.map +1 -0
  127. package/{src/panel/protocol.ts → dist/panel/protocol.js} +10 -26
  128. package/dist/panel/protocol.js.map +1 -0
  129. package/{src/panel/types.ts → dist/panel/types.d.ts} +50 -89
  130. package/dist/panel/types.d.ts.map +1 -0
  131. package/dist/panel/types.js +2 -0
  132. package/dist/panel/types.js.map +1 -0
  133. package/dist/preview/bounds.d.ts +12 -0
  134. package/dist/preview/bounds.d.ts.map +1 -0
  135. package/{src/preview/bounds.ts → dist/preview/bounds.js} +15 -29
  136. package/dist/preview/bounds.js.map +1 -0
  137. package/dist/preview/box-layout.d.ts +4 -0
  138. package/dist/preview/box-layout.d.ts.map +1 -0
  139. package/{src/preview/box-layout.ts → dist/preview/box-layout.js} +2 -6
  140. package/dist/preview/box-layout.js.map +1 -0
  141. package/dist/preview/colors.d.ts +3 -0
  142. package/dist/preview/colors.d.ts.map +1 -0
  143. package/{src/preview/colors.ts → dist/preview/colors.js} +3 -5
  144. package/dist/preview/colors.js.map +1 -0
  145. package/dist/preview/hanging.d.ts +8 -0
  146. package/dist/preview/hanging.d.ts.map +1 -0
  147. package/{src/preview/hanging.ts → dist/preview/hanging.js} +9 -28
  148. package/dist/preview/hanging.js.map +1 -0
  149. package/dist/preview/junctions.d.ts +3 -0
  150. package/dist/preview/junctions.d.ts.map +1 -0
  151. package/{src/preview/junctions.ts → dist/preview/junctions.js} +9 -24
  152. package/dist/preview/junctions.js.map +1 -0
  153. package/dist/preview/label-layout.d.ts +12 -0
  154. package/dist/preview/label-layout.d.ts.map +1 -0
  155. package/{src/preview/label-layout.ts → dist/preview/label-layout.js} +15 -36
  156. package/dist/preview/label-layout.js.map +1 -0
  157. package/dist/preview/ports.d.ts +17 -0
  158. package/dist/preview/ports.d.ts.map +1 -0
  159. package/{src/preview/ports.ts → dist/preview/ports.js} +10 -37
  160. package/dist/preview/ports.js.map +1 -0
  161. package/dist/preview/renderable-wires.d.ts +3 -0
  162. package/dist/preview/renderable-wires.d.ts.map +1 -0
  163. package/{src/preview/renderable-wires.ts → dist/preview/renderable-wires.js} +12 -29
  164. package/dist/preview/renderable-wires.js.map +1 -0
  165. package/dist/preview/routing.d.ts +4 -0
  166. package/dist/preview/routing.d.ts.map +1 -0
  167. package/dist/preview/routing.js +13 -0
  168. package/dist/preview/routing.js.map +1 -0
  169. package/dist/preview/snap.d.ts +9 -0
  170. package/dist/preview/snap.d.ts.map +1 -0
  171. package/{src/preview/snap.ts → dist/preview/snap.js} +9 -31
  172. package/dist/preview/snap.js.map +1 -0
  173. package/dist/preview/symbols/svg-content.d.ts +7 -0
  174. package/dist/preview/symbols/svg-content.d.ts.map +1 -0
  175. package/{src/preview/symbols/svg-content.ts → dist/preview/symbols/svg-content.js} +3 -6
  176. package/dist/preview/symbols/svg-content.js.map +1 -0
  177. package/dist/preview/symbols.d.ts +7 -0
  178. package/dist/preview/symbols.d.ts.map +1 -0
  179. package/{src/preview/symbols.ts → dist/preview/symbols.js} +18 -43
  180. package/dist/preview/symbols.js.map +1 -0
  181. package/dist/preview/wire-chains.d.ts +4 -0
  182. package/dist/preview/wire-chains.d.ts.map +1 -0
  183. package/{src/preview/wire-chains.ts → dist/preview/wire-chains.js} +37 -37
  184. package/dist/preview/wire-chains.js.map +1 -0
  185. package/package.json +3 -3
  186. package/src/index.ts +0 -255
  187. package/src/panel/index.ts +0 -39
  188. package/src/preview/routing.ts +0 -15
  189. package/src/preview/symbols/analog-switch.svg +0 -17
  190. package/src/preview/symbols/battery.svg +0 -16
  191. package/src/preview/symbols/bbd.svg +0 -21
  192. package/src/preview/symbols/bjt-npn.svg +0 -16
  193. package/src/preview/symbols/bjt-pnp.svg +0 -17
  194. package/src/preview/symbols/capacitor-electrolytic.svg +0 -13
  195. package/src/preview/symbols/capacitor.svg +0 -12
  196. package/src/preview/symbols/current-source.svg +0 -14
  197. package/src/preview/symbols/delay-ic.svg +0 -22
  198. package/src/preview/symbols/diode-schottky.svg +0 -12
  199. package/src/preview/symbols/diode-zener.svg +0 -12
  200. package/src/preview/symbols/diode.svg +0 -13
  201. package/src/preview/symbols/flipflop.svg +0 -20
  202. package/src/preview/symbols/ground.svg +0 -12
  203. package/src/preview/symbols/ic-block.svg +0 -20
  204. package/src/preview/symbols/ic.svg +0 -19
  205. package/src/preview/symbols/inductor.svg +0 -11
  206. package/src/preview/symbols/jack-input.svg +0 -16
  207. package/src/preview/symbols/jack-output.svg +0 -16
  208. package/src/preview/symbols/jfet-junction-n.svg +0 -17
  209. package/src/preview/symbols/jfet-n.svg +0 -17
  210. package/src/preview/symbols/jfet-p.svg +0 -17
  211. package/src/preview/symbols/label.svg +0 -8
  212. package/src/preview/symbols/led.svg +0 -18
  213. package/src/preview/symbols/mosfet-n.svg +0 -21
  214. package/src/preview/symbols/mosfet-p.svg +0 -21
  215. package/src/preview/symbols/named-wire.svg +0 -11
  216. package/src/preview/symbols/opamp.svg +0 -21
  217. package/src/preview/symbols/optocoupler.svg +0 -30
  218. package/src/preview/symbols/ota.svg +0 -20
  219. package/src/preview/symbols/pentode.svg +0 -25
  220. package/src/preview/symbols/photoresistor.svg +0 -19
  221. package/src/preview/symbols/port.svg +0 -8
  222. package/src/preview/symbols/potentiometer.svg +0 -15
  223. package/src/preview/symbols/power-amp.svg +0 -20
  224. package/src/preview/symbols/rail.svg +0 -11
  225. package/src/preview/symbols/regulator.svg +0 -13
  226. package/src/preview/symbols/relay.svg +0 -20
  227. package/src/preview/symbols/resistor.svg +0 -11
  228. package/src/preview/symbols/switch-3pdt.svg +0 -32
  229. package/src/preview/symbols/switch-rotary.svg +0 -23
  230. package/src/preview/symbols/switch-spdt.svg +0 -16
  231. package/src/preview/symbols/switch-spst.svg +0 -14
  232. package/src/preview/symbols/switch-toggle.svg +0 -14
  233. package/src/preview/symbols/transformer.svg +0 -17
  234. package/src/preview/symbols/triode.svg +0 -17
  235. package/src/preview/symbols/tube-diode.svg +0 -13
  236. package/src/preview/symbols/unsupported.svg +0 -8
  237. package/src/preview/symbols/variable-resistor.svg +0 -13
  238. package/src/preview/symbols/voltage-source.svg +0 -15
@@ -1,106 +1,17 @@
1
- import { propertyQuantityValue, propertyStringValue } from './properties';
2
- import { extractPanel } from '../panel/extract';
3
- import type {
4
- BoardNet,
5
- BoardRealization,
6
- BoardRoute,
7
- BuildBomRef,
8
- CircuitDocument,
9
- Component,
10
- ComponentKind,
11
- DeviceInterfaceBinding,
12
- DeviceInterfaceControl,
13
- OffBoardSignalRef,
14
- OffBoardWiringEndpoint,
15
- PanelControlKind,
16
- PanelElementPlacement,
17
- PanelFace,
18
- ParsedQuantity,
19
- PropertyValue,
20
- VdspBuildDataObject,
21
- } from './types';
22
-
23
- export type ValidationSeverity = 'error' | 'warning';
24
-
25
- export type ValidationCode =
26
- | 'value-required'
27
- | 'model-required'
28
- | 'value-unparseable'
29
- | 'value-out-of-range'
30
- | 'unit-mismatch'
31
- | 'unsupported-component'
32
- | 'invalid-jack-role'
33
- | 'invalid-jack-interface'
34
- | 'invalid-jack-audio-role'
35
- | 'descriptor-control-empty'
36
- | 'descriptor-mode-label-mismatch'
37
- | 'duplicate-device-interface-control-id'
38
- | 'invalid-device-interface-token'
39
- | 'control-group-context-unresolved'
40
- | 'device-interface-group-unresolved'
41
- | 'device-interface-context-unresolved'
42
- | 'device-interface-binding-unresolved'
43
- | 'device-interface-duplicate-role'
44
- | 'panel-interface-control-unresolved'
45
- | 'panel-binding-unresolved'
46
- | 'panel-control-unresolved'
47
- | 'panel-kind-mismatch'
48
- | 'panel-cell-collision'
49
- | 'build-board-unresolved'
50
- | 'build-harness-unresolved'
51
- | 'bom-ref-unresolved'
52
- | 'offboard-endpoint-unresolved'
53
- | 'offboard-signal-unresolved'
54
- | 'board-source-hash-invalid'
55
- | 'board-terminal-unresolved'
56
- | 'board-route-feature-invalid'
57
- | 'board-net-unrouted'
58
- | 'duplicate-id'
59
- | 'degenerate-wire';
60
-
61
- export type ValidationIssue = Readonly<{
62
- code: ValidationCode;
63
- severity: ValidationSeverity;
64
- message: string;
65
- componentId?: string;
66
- property?: string;
67
- wireId?: string;
68
- }>;
69
-
70
- export type QuantityRule = Readonly<{
71
- kind: 'quantity';
72
- name: string;
73
- required: boolean;
74
- aliases?: readonly string[];
75
- unit?: string;
76
- min?: number;
77
- max?: number;
78
- }>;
79
-
80
- export type StringRule = Readonly<{
81
- kind: 'string';
82
- name: string;
83
- required: boolean;
84
- aliases?: readonly string[];
85
- }>;
86
-
87
- export type PropertyRule = QuantityRule | StringRule;
88
-
89
- const MODEL_ALIASES = ['Model', 'Type', 'partNumber', 'PartNumber'] as const;
90
-
1
+ import { propertyQuantityValue, propertyStringValue } from './properties.js';
2
+ import { extractPanel } from '../panel/extract.js';
3
+ const MODEL_ALIASES = ['Model', 'Type', 'partNumber', 'PartNumber'];
91
4
  // Short source-type names (last dotted segment) that represent an "ideal" component variant —
92
5
  // no model name is required because the component is a mathematical abstraction.
93
- const IDEAL_SOURCE_TYPES: ReadonlySet<string> = new Set(['IdealOpAmp']);
94
-
6
+ const IDEAL_SOURCE_TYPES = new Set(['IdealOpAmp']);
95
7
  // Per-kind property names that, if present, satisfy the "needs a model" requirement.
96
8
  // LiveSPICE stores tube Koren parameters and opamp small-signal parameters inline; when those
97
9
  // are present, the parameters ARE the model definition and no separate model name is needed.
98
- const INLINE_MODEL_PARAMETERS: Partial<Record<ComponentKind, readonly string[]>> = {
10
+ const INLINE_MODEL_PARAMETERS = {
99
11
  opamp: ['Rin', 'Rout', 'Aol', 'GBP', 'SupplyVoltage'],
100
12
  triode: ['Mu', 'K', 'Kp', 'Kvb', 'Ex', 'Kg'],
101
13
  pentode: ['Mu', 'K', 'Kp', 'Kvb', 'Ex', 'Kg', 'Kg1', 'Kg2'],
102
14
  };
103
-
104
15
  const RUNTIME_DESCRIPTOR_CONTROL_PROPERTIES = [
105
16
  'TimeControl',
106
17
  'FeedbackControl',
@@ -117,23 +28,16 @@ const RUNTIME_DESCRIPTOR_CONTROL_PROPERTIES = [
117
28
  'DirectOutJack',
118
29
  'DirectOutputControl',
119
30
  'DirectOutControl',
120
- ] as const;
121
-
122
- type ResolvedPanelElement = Readonly<{
123
- id: string;
124
- componentId: string;
125
- kind: PanelControlKind;
126
- }>;
127
-
128
- const KIND_RULES: Partial<Record<ComponentKind, readonly PropertyRule[]>> = {
31
+ ];
32
+ const KIND_RULES = {
129
33
  resistor: [{
130
- kind: 'quantity', name: 'R', required: true, unit: 'Ω',
131
- min: 1e-9, max: 1e9, aliases: ['Resistance', 'resistance', 'r'],
132
- }],
34
+ kind: 'quantity', name: 'R', required: true, unit: 'Ω',
35
+ min: 1e-9, max: 1e9, aliases: ['Resistance', 'resistance', 'r'],
36
+ }],
133
37
  'variable-resistor': [{
134
- kind: 'quantity', name: 'R', required: true, unit: 'Ω',
135
- min: 1e-9, max: 1e9, aliases: ['Resistance', 'resistance', 'r'],
136
- }],
38
+ kind: 'quantity', name: 'R', required: true, unit: 'Ω',
39
+ min: 1e-9, max: 1e9, aliases: ['Resistance', 'resistance', 'r'],
40
+ }],
137
41
  potentiometer: [
138
42
  {
139
43
  kind: 'quantity', name: 'R', required: true, unit: 'Ω',
@@ -142,29 +46,29 @@ const KIND_RULES: Partial<Record<ComponentKind, readonly PropertyRule[]>> = {
142
46
  { kind: 'string', name: 'taper', required: false, aliases: ['Taper'] },
143
47
  ],
144
48
  capacitor: [{
145
- kind: 'quantity', name: 'C', required: true, unit: 'F',
146
- min: 1e-15, max: 1, aliases: ['Capacitance', 'capacitance', 'c'],
147
- }],
49
+ kind: 'quantity', name: 'C', required: true, unit: 'F',
50
+ min: 1e-15, max: 1, aliases: ['Capacitance', 'capacitance', 'c'],
51
+ }],
148
52
  inductor: [{
149
- kind: 'quantity', name: 'L', required: true, unit: 'H',
150
- min: 1e-12, max: 100, aliases: ['Inductance', 'inductance', 'l'],
151
- }],
53
+ kind: 'quantity', name: 'L', required: true, unit: 'H',
54
+ min: 1e-12, max: 100, aliases: ['Inductance', 'inductance', 'l'],
55
+ }],
152
56
  'voltage-source': [{
153
- kind: 'quantity', name: 'V', required: true, unit: 'V',
154
- aliases: ['Voltage', 'voltage', 'v'],
155
- }],
57
+ kind: 'quantity', name: 'V', required: true, unit: 'V',
58
+ aliases: ['Voltage', 'voltage', 'v'],
59
+ }],
156
60
  'current-source': [{
157
- kind: 'quantity', name: 'I', required: true, unit: 'A',
158
- aliases: ['Current', 'current', 'i'],
159
- }],
61
+ kind: 'quantity', name: 'I', required: true, unit: 'A',
62
+ aliases: ['Current', 'current', 'i'],
63
+ }],
160
64
  battery: [{
161
- kind: 'quantity', name: 'V', required: true, unit: 'V',
162
- aliases: ['Voltage', 'voltage', 'v'],
163
- }],
65
+ kind: 'quantity', name: 'V', required: true, unit: 'V',
66
+ aliases: ['Voltage', 'voltage', 'v'],
67
+ }],
164
68
  rail: [{
165
- kind: 'quantity', name: 'V', required: true, unit: 'V',
166
- aliases: ['Voltage', 'voltage', 'v'],
167
- }],
69
+ kind: 'quantity', name: 'V', required: true, unit: 'V',
70
+ aliases: ['Voltage', 'voltage', 'v'],
71
+ }],
168
72
  diode: [{ kind: 'string', name: 'model', required: true, aliases: [...MODEL_ALIASES] }],
169
73
  led: [{ kind: 'string', name: 'model', required: true, aliases: [...MODEL_ALIASES] }],
170
74
  bjt: [{ kind: 'string', name: 'model', required: true, aliases: [...MODEL_ALIASES] }],
@@ -185,27 +89,19 @@ const KIND_RULES: Partial<Record<ComponentKind, readonly PropertyRule[]>> = {
185
89
  flipflop: [{ kind: 'string', name: 'model', required: true, aliases: [...MODEL_ALIASES] }],
186
90
  ic: [{ kind: 'string', name: 'model', required: true, aliases: [...MODEL_ALIASES] }],
187
91
  };
188
-
189
- export function getRulesForKind(kind: ComponentKind): readonly PropertyRule[] {
92
+ export function getRulesForKind(kind) {
190
93
  return KIND_RULES[kind] ?? [];
191
94
  }
192
-
193
- export function validateComponent(
194
- component: Component,
195
- rules: readonly PropertyRule[] = getRulesForKind(component.kind),
196
- ): readonly ValidationIssue[] {
197
- const issues: ValidationIssue[] = [];
198
-
95
+ export function validateComponent(component, rules = getRulesForKind(component.kind)) {
96
+ const issues = [];
199
97
  for (const rule of rules) {
200
98
  const value = findProperty(component, rule);
201
-
202
99
  if (value === undefined) {
203
100
  if (rule.required && !isRequirementWaived(component, rule)) {
204
101
  issues.push(missingPropertyIssue(component, rule));
205
102
  }
206
103
  continue;
207
104
  }
208
-
209
105
  if (rule.kind === 'string') {
210
106
  if (typeof value !== 'string' || value.trim().length === 0) {
211
107
  if (rule.required && !isRequirementWaived(component, rule)) {
@@ -214,7 +110,6 @@ export function validateComponent(
214
110
  }
215
111
  continue;
216
112
  }
217
-
218
113
  const quantity = coerceQuantity(value);
219
114
  if (quantity === null) {
220
115
  if (typeof value === 'string' && isRawQuantityExpression(value)) {
@@ -229,7 +124,6 @@ export function validateComponent(
229
124
  });
230
125
  continue;
231
126
  }
232
-
233
127
  if (rule.unit !== undefined && rule.unit.length > 0 && quantity.unit.length > 0 && quantity.unit !== rule.unit) {
234
128
  issues.push({
235
129
  code: 'unit-mismatch',
@@ -239,7 +133,6 @@ export function validateComponent(
239
133
  property: rule.name,
240
134
  });
241
135
  }
242
-
243
136
  if (rule.min !== undefined && quantity.value < rule.min) {
244
137
  issues.push({
245
138
  code: 'value-out-of-range',
@@ -259,14 +152,11 @@ export function validateComponent(
259
152
  });
260
153
  }
261
154
  }
262
-
263
155
  return issues;
264
156
  }
265
-
266
- export function validateDocument(doc: CircuitDocument): readonly ValidationIssue[] {
267
- const issues: ValidationIssue[] = [];
268
- const seen = new Set<string>();
269
-
157
+ export function validateDocument(doc) {
158
+ const issues = [];
159
+ const seen = new Set();
270
160
  for (const component of doc.components) {
271
161
  if (seen.has(component.id)) {
272
162
  issues.push({
@@ -277,7 +167,6 @@ export function validateDocument(doc: CircuitDocument): readonly ValidationIssue
277
167
  });
278
168
  }
279
169
  seen.add(component.id);
280
-
281
170
  if (component.kind === 'unsupported') {
282
171
  issues.push({
283
172
  code: 'unsupported-component',
@@ -287,16 +176,13 @@ export function validateDocument(doc: CircuitDocument): readonly ValidationIssue
287
176
  });
288
177
  continue;
289
178
  }
290
-
291
179
  for (const issue of validateComponent(component)) {
292
180
  issues.push(issue);
293
181
  }
294
-
295
182
  for (const issue of validateSemanticMetadata(component)) {
296
183
  issues.push(issue);
297
184
  }
298
185
  }
299
-
300
186
  for (const wire of doc.wires) {
301
187
  const [a, b] = wire.endpoints;
302
188
  if (a.x === b.x && a.y === b.y) {
@@ -308,31 +194,24 @@ export function validateDocument(doc: CircuitDocument): readonly ValidationIssue
308
194
  });
309
195
  }
310
196
  }
311
-
312
197
  for (const issue of validateDeviceInterface(doc, seen)) {
313
198
  issues.push(issue);
314
199
  }
315
-
316
200
  for (const issue of validatePanel(doc, seen, new Set(doc.deviceInterface?.controls.map((control) => control.id) ?? []))) {
317
201
  issues.push(issue);
318
202
  }
319
-
320
203
  for (const issue of validateV3BuildMetadata(doc, seen)) {
321
204
  issues.push(issue);
322
205
  }
323
-
324
206
  return issues;
325
207
  }
326
-
327
- export function hasErrors(issues: readonly ValidationIssue[]): boolean {
208
+ export function hasErrors(issues) {
328
209
  return issues.some((issue) => issue.severity === 'error');
329
210
  }
330
-
331
- function isRequirementWaived(component: Component, rule: PropertyRule): boolean {
211
+ function isRequirementWaived(component, rule) {
332
212
  if (isInterfaceOnlyComponent(component)) {
333
213
  return true;
334
214
  }
335
-
336
215
  // Only the "model" string requirement has a waiver path today.
337
216
  if (rule.kind !== 'string' || rule.name !== 'model') {
338
217
  return false;
@@ -347,8 +226,7 @@ function isRequirementWaived(component: Component, rule: PropertyRule): boolean
347
226
  const inline = INLINE_MODEL_PARAMETERS[component.kind] ?? [];
348
227
  return inline.some((name) => component.properties[name] !== undefined);
349
228
  }
350
-
351
- function isInterfaceOnlyComponent(component: Component): boolean {
229
+ function isInterfaceOnlyComponent(component) {
352
230
  const interfaceOnly = component.properties.InterfaceOnly;
353
231
  if (interfaceOnly === true) {
354
232
  return true;
@@ -359,25 +237,19 @@ function isInterfaceOnlyComponent(component: Component): boolean {
359
237
  const support = component.properties.Support;
360
238
  return typeof support === 'string' && normalizeToken(support) === 'view-only';
361
239
  }
362
-
363
- function validateSemanticMetadata(component: Component): readonly ValidationIssue[] {
364
- const issues: ValidationIssue[] = [];
365
-
240
+ function validateSemanticMetadata(component) {
241
+ const issues = [];
366
242
  if (component.kind === 'jack') {
367
243
  issues.push(...validateJackSemanticMetadata(component));
368
244
  }
369
-
370
245
  if (component.kind === 'ic' && component.properties.RuntimeDescriptor === 'true') {
371
246
  issues.push(...validateRuntimeDescriptorMetadata(component));
372
247
  }
373
-
374
248
  return issues;
375
249
  }
376
-
377
- function validateJackSemanticMetadata(component: Component): readonly ValidationIssue[] {
378
- const issues: ValidationIssue[] = [];
379
-
380
- for (const property of ['Role', 'ControlRole'] as const) {
250
+ function validateJackSemanticMetadata(component) {
251
+ const issues = [];
252
+ for (const property of ['Role', 'ControlRole']) {
381
253
  const value = propertyString(component, property);
382
254
  if (value !== null && value.trim().length > 0 && !isRecognizedJackRole(value)) {
383
255
  issues.push({
@@ -389,7 +261,6 @@ function validateJackSemanticMetadata(component: Component): readonly Validation
389
261
  });
390
262
  }
391
263
  }
392
-
393
264
  const interfaceName = propertyString(component, 'Interface');
394
265
  if (interfaceName !== null && interfaceName.trim().length > 0 && !isRecognizedJackInterface(interfaceName)) {
395
266
  issues.push({
@@ -400,7 +271,6 @@ function validateJackSemanticMetadata(component: Component): readonly Validation
400
271
  property: 'Interface',
401
272
  });
402
273
  }
403
-
404
274
  const audioRole = propertyString(component, 'AudioRole');
405
275
  if (audioRole !== null && !isValidJackAudioRole(audioRole)) {
406
276
  issues.push({
@@ -411,13 +281,10 @@ function validateJackSemanticMetadata(component: Component): readonly Validation
411
281
  property: 'AudioRole',
412
282
  });
413
283
  }
414
-
415
284
  return issues;
416
285
  }
417
-
418
- function validateRuntimeDescriptorMetadata(component: Component): readonly ValidationIssue[] {
419
- const issues: ValidationIssue[] = [];
420
-
286
+ function validateRuntimeDescriptorMetadata(component) {
287
+ const issues = [];
421
288
  for (const property of RUNTIME_DESCRIPTOR_CONTROL_PROPERTIES) {
422
289
  const value = propertyString(component, property);
423
290
  if (value !== null && value.trim().length === 0) {
@@ -430,7 +297,6 @@ function validateRuntimeDescriptorMetadata(component: Component): readonly Valid
430
297
  });
431
298
  }
432
299
  }
433
-
434
300
  const labels = parseStringList(propertyStringAny(component, ['ModeLabels', 'ModeOptions']));
435
301
  const stepCount = parsePositiveInteger(propertyStringAny(component, ['ModeStepCount', 'ModeSteps', 'ModeCount']));
436
302
  if (labels.length > 0 && stepCount !== undefined && labels.length !== stepCount) {
@@ -442,11 +308,9 @@ function validateRuntimeDescriptorMetadata(component: Component): readonly Valid
442
308
  property: 'ModeLabels',
443
309
  });
444
310
  }
445
-
446
311
  return issues;
447
312
  }
448
-
449
- function shortSourceType(sourceTypeName: string | null): string | null {
313
+ function shortSourceType(sourceTypeName) {
450
314
  if (sourceTypeName === null) {
451
315
  return null;
452
316
  }
@@ -457,8 +321,7 @@ function shortSourceType(sourceTypeName: string | null): string | null {
457
321
  const lastDot = head.lastIndexOf('.');
458
322
  return lastDot >= 0 ? head.slice(lastDot + 1) : head;
459
323
  }
460
-
461
- function findProperty(component: Component, rule: PropertyRule): PropertyValue | undefined {
324
+ function findProperty(component, rule) {
462
325
  const candidates = [rule.name, ...(rule.aliases ?? [])];
463
326
  for (const name of candidates) {
464
327
  const value = component.properties[name];
@@ -468,12 +331,10 @@ function findProperty(component: Component, rule: PropertyRule): PropertyValue |
468
331
  }
469
332
  return undefined;
470
333
  }
471
-
472
- function propertyString(component: Component, name: string): string | null {
334
+ function propertyString(component, name) {
473
335
  return propertyStringValue(component.properties[name]);
474
336
  }
475
-
476
- function propertyStringAny(component: Component, names: readonly string[]): string | null {
337
+ function propertyStringAny(component, names) {
477
338
  for (const name of names) {
478
339
  const value = propertyString(component, name);
479
340
  if (value !== null) {
@@ -482,12 +343,10 @@ function propertyStringAny(component: Component, names: readonly string[]): stri
482
343
  }
483
344
  return null;
484
345
  }
485
-
486
- function coerceQuantity(value: PropertyValue): ParsedQuantity | null {
346
+ function coerceQuantity(value) {
487
347
  return propertyQuantityValue(value);
488
348
  }
489
-
490
- function isRawQuantityExpression(value: string): boolean {
349
+ function isRawQuantityExpression(value) {
491
350
  const trimmed = value.trim();
492
351
  if (trimmed.length === 0) {
493
352
  return false;
@@ -498,8 +357,7 @@ function isRawQuantityExpression(value: string): boolean {
498
357
  return /^(AC|DC)\b/i.test(trimmed) ||
499
358
  /^(SINE|PULSE|PWL|EXP|SFFM|AM|WAVEFILE)\s*\(/i.test(trimmed);
500
359
  }
501
-
502
- function isRecognizedJackRole(value: string): boolean {
360
+ function isRecognizedJackRole(value) {
503
361
  const normalized = normalizeToken(value);
504
362
  return [
505
363
  'input',
@@ -531,8 +389,7 @@ function isRecognizedJackRole(value: string): boolean {
531
389
  'reset',
532
390
  ].includes(normalized);
533
391
  }
534
-
535
- function isRecognizedJackInterface(value: string): boolean {
392
+ function isRecognizedJackInterface(value) {
536
393
  const normalized = normalizeToken(value);
537
394
  return isRecognizedJackRole(value) ||
538
395
  [
@@ -547,12 +404,10 @@ function isRecognizedJackInterface(value: string): boolean {
547
404
  'tap-tempo-input',
548
405
  ].includes(normalized);
549
406
  }
550
-
551
- function isValidJackAudioRole(value: string): boolean {
407
+ function isValidJackAudioRole(value) {
552
408
  return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(value);
553
409
  }
554
-
555
- function parseStringList(value: string | null): readonly string[] {
410
+ function parseStringList(value) {
556
411
  if (value === null) {
557
412
  return [];
558
413
  }
@@ -561,8 +416,7 @@ function parseStringList(value: string | null): readonly string[] {
561
416
  .map((part) => part.trim())
562
417
  .filter((part) => part.length > 0);
563
418
  }
564
-
565
- function parsePositiveInteger(value: string | null): number | undefined {
419
+ function parsePositiveInteger(value) {
566
420
  if (value === null) {
567
421
  return undefined;
568
422
  }
@@ -573,23 +427,17 @@ function parsePositiveInteger(value: string | null): number | undefined {
573
427
  const count = Number(trimmed);
574
428
  return Number.isInteger(count) && count > 0 ? count : undefined;
575
429
  }
576
-
577
- function normalizeToken(value: string): string {
430
+ function normalizeToken(value) {
578
431
  return value.trim().toLowerCase().replace(/[\s_]+/g, '-');
579
432
  }
580
-
581
- function validateDeviceInterface(
582
- doc: CircuitDocument,
583
- componentIds: ReadonlySet<string>,
584
- ): readonly ValidationIssue[] {
585
- const issues: ValidationIssue[] = [];
433
+ function validateDeviceInterface(doc, componentIds) {
434
+ const issues = [];
586
435
  const groupIds = new Set(doc.controlGroups?.map((group) => group.id) ?? []);
587
436
  const contextIds = new Set(doc.controlContexts?.map((context) => context.id) ?? []);
588
- const semanticControlIds = new Set<string>();
437
+ const semanticControlIds = new Set();
589
438
  const externalInterfaceIds = new Set(doc.controlInterfaces?.map((controlInterface) => controlInterface.id) ?? []);
590
439
  const componentsById = new Map(doc.components.map((component) => [component.id, component]));
591
440
  const resolvedPanelElements = resolvePanelElements(doc);
592
-
593
441
  for (const group of doc.controlGroups ?? []) {
594
442
  issues.push(...validateOpenToken(group.role, group.id, 'role'));
595
443
  for (const contextId of group.contextIds ?? []) {
@@ -604,11 +452,9 @@ function validateDeviceInterface(
604
452
  }
605
453
  }
606
454
  }
607
-
608
455
  for (const context of doc.controlContexts ?? []) {
609
456
  issues.push(...validateOpenToken(context.role, context.id, 'role'));
610
457
  }
611
-
612
458
  for (const control of doc.deviceInterface?.controls ?? []) {
613
459
  if (semanticControlIds.has(control.id)) {
614
460
  issues.push({
@@ -619,9 +465,7 @@ function validateDeviceInterface(
619
465
  });
620
466
  }
621
467
  semanticControlIds.add(control.id);
622
-
623
468
  issues.push(...validateOpenToken(control.role, control.id, 'role'));
624
-
625
469
  if (control.groupId !== undefined && !groupIds.has(control.groupId)) {
626
470
  issues.push({
627
471
  code: 'device-interface-group-unresolved',
@@ -631,85 +475,54 @@ function validateDeviceInterface(
631
475
  property: 'groupId',
632
476
  });
633
477
  }
634
-
635
478
  issues.push(...validateApplicability(control, contextIds));
636
-
637
479
  if (control.binding !== undefined) {
638
- issues.push(...validateDeviceInterfaceBinding(
639
- control,
640
- control.binding,
641
- componentIds,
642
- externalInterfaceIds,
643
- componentsById,
644
- resolvedPanelElements,
645
- ));
480
+ issues.push(...validateDeviceInterfaceBinding(control, control.binding, componentIds, externalInterfaceIds, componentsById, resolvedPanelElements));
646
481
  }
647
482
  }
648
-
649
483
  issues.push(...validateDuplicateDeviceInterfaceRoles(doc.deviceInterface?.controls ?? []));
650
-
651
484
  return issues;
652
485
  }
653
-
654
- function validateOpenToken(value: string, componentId: string, property: string): readonly ValidationIssue[] {
486
+ function validateOpenToken(value, componentId, property) {
655
487
  if (/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(value)) {
656
488
  return [];
657
489
  }
658
490
  return [{
659
- code: 'invalid-device-interface-token',
660
- severity: 'warning',
661
- message: `${componentId}: ${property} "${value}" must be a lower-kebab token`,
662
- componentId,
663
- property,
664
- }];
491
+ code: 'invalid-device-interface-token',
492
+ severity: 'warning',
493
+ message: `${componentId}: ${property} "${value}" must be a lower-kebab token`,
494
+ componentId,
495
+ property,
496
+ }];
665
497
  }
666
-
667
- function validateApplicability(
668
- control: DeviceInterfaceControl,
669
- contextIds: ReadonlySet<string>,
670
- ): readonly ValidationIssue[] {
671
- const issues: ValidationIssue[] = [];
498
+ function validateApplicability(control, contextIds) {
499
+ const issues = [];
672
500
  if (control.appliesWhen === undefined) {
673
501
  return issues;
674
502
  }
675
-
676
503
  issues.push(...validateContextList(control.id, 'appliesWhen.allOf', control.appliesWhen.allOf, contextIds));
677
504
  issues.push(...validateContextList(control.id, 'appliesWhen.anyOf', control.appliesWhen.anyOf, contextIds));
678
-
679
- if (
680
- control.appliesWhen.allOf !== undefined
505
+ if (control.appliesWhen.allOf !== undefined
681
506
  && control.appliesWhen.allOf.length === 0
682
- && control.appliesWhen.anyOf === undefined
683
- ) {
507
+ && control.appliesWhen.anyOf === undefined) {
684
508
  issues.push(emptyApplicabilityIssue(control.id, 'appliesWhen.allOf'));
685
509
  }
686
- if (
687
- control.appliesWhen.anyOf !== undefined
510
+ if (control.appliesWhen.anyOf !== undefined
688
511
  && control.appliesWhen.anyOf.length === 0
689
- && control.appliesWhen.allOf === undefined
690
- ) {
512
+ && control.appliesWhen.allOf === undefined) {
691
513
  issues.push(emptyApplicabilityIssue(control.id, 'appliesWhen.anyOf'));
692
514
  }
693
-
694
515
  return issues;
695
516
  }
696
-
697
- function validateContextList(
698
- controlId: string,
699
- property: string,
700
- values: readonly string[] | undefined,
701
- contextIds: ReadonlySet<string>,
702
- ): readonly ValidationIssue[] {
517
+ function validateContextList(controlId, property, values, contextIds) {
703
518
  if (values === undefined) {
704
519
  return [];
705
520
  }
706
-
707
- const issues: ValidationIssue[] = [];
708
- const seen = new Set<string>();
521
+ const issues = [];
522
+ const seen = new Set();
709
523
  if (values.length === 0) {
710
524
  issues.push(emptyApplicabilityIssue(controlId, property));
711
525
  }
712
-
713
526
  for (const contextId of values) {
714
527
  if (seen.has(contextId)) {
715
528
  issues.push({
@@ -721,7 +534,6 @@ function validateContextList(
721
534
  });
722
535
  }
723
536
  seen.add(contextId);
724
-
725
537
  if (!contextIds.has(contextId)) {
726
538
  issues.push({
727
539
  code: 'device-interface-context-unresolved',
@@ -732,11 +544,9 @@ function validateContextList(
732
544
  });
733
545
  }
734
546
  }
735
-
736
547
  return issues;
737
548
  }
738
-
739
- function emptyApplicabilityIssue(controlId: string, property: string): ValidationIssue {
549
+ function emptyApplicabilityIssue(controlId, property) {
740
550
  return {
741
551
  code: 'device-interface-context-unresolved',
742
552
  severity: 'warning',
@@ -745,16 +555,8 @@ function emptyApplicabilityIssue(controlId: string, property: string): Validatio
745
555
  property,
746
556
  };
747
557
  }
748
-
749
- function validateDeviceInterfaceBinding(
750
- control: DeviceInterfaceControl,
751
- binding: DeviceInterfaceBinding,
752
- componentIds: ReadonlySet<string>,
753
- externalInterfaceIds: ReadonlySet<string>,
754
- componentsById: ReadonlyMap<string, Component>,
755
- resolvedPanelElements: readonly ResolvedPanelElement[],
756
- ): readonly ValidationIssue[] {
757
- const issues: ValidationIssue[] = [];
558
+ function validateDeviceInterfaceBinding(control, binding, componentIds, externalInterfaceIds, componentsById, resolvedPanelElements) {
559
+ const issues = [];
758
560
  if (binding.externalInterfaceId !== undefined && !externalInterfaceIds.has(binding.externalInterfaceId)) {
759
561
  issues.push({
760
562
  code: 'device-interface-binding-unresolved',
@@ -764,7 +566,6 @@ function validateDeviceInterfaceBinding(
764
566
  property: 'binding.externalInterfaceId',
765
567
  });
766
568
  }
767
-
768
569
  if (!componentIds.has(binding.componentId)) {
769
570
  issues.push({
770
571
  code: 'device-interface-binding-unresolved',
@@ -775,13 +576,8 @@ function validateDeviceInterfaceBinding(
775
576
  });
776
577
  return issues;
777
578
  }
778
-
779
- if (
780
- binding.controlId !== undefined
781
- && !resolvedPanelElements.some((resolved) =>
782
- resolved.componentId === binding.componentId && resolved.id === binding.controlId
783
- )
784
- ) {
579
+ if (binding.controlId !== undefined
580
+ && !resolvedPanelElements.some((resolved) => resolved.componentId === binding.componentId && resolved.id === binding.controlId)) {
785
581
  issues.push({
786
582
  code: 'device-interface-binding-unresolved',
787
583
  severity: 'warning',
@@ -790,7 +586,6 @@ function validateDeviceInterfaceBinding(
790
586
  property: 'binding.controlId',
791
587
  });
792
588
  }
793
-
794
589
  const component = componentsById.get(binding.componentId);
795
590
  if (binding.property !== undefined && component?.properties[binding.property] === undefined) {
796
591
  issues.push({
@@ -801,15 +596,11 @@ function validateDeviceInterfaceBinding(
801
596
  property: 'binding.property',
802
597
  });
803
598
  }
804
-
805
599
  return issues;
806
600
  }
807
-
808
- function validateDuplicateDeviceInterfaceRoles(
809
- controls: readonly DeviceInterfaceControl[],
810
- ): readonly ValidationIssue[] {
811
- const issues: ValidationIssue[] = [];
812
- const seen = new Map<string, DeviceInterfaceControl>();
601
+ function validateDuplicateDeviceInterfaceRoles(controls) {
602
+ const issues = [];
603
+ const seen = new Map();
813
604
  for (const control of controls) {
814
605
  const key = `${control.groupId ?? ''}:${control.role}`;
815
606
  const existing = seen.get(key);
@@ -828,8 +619,7 @@ function validateDuplicateDeviceInterfaceRoles(
828
619
  }
829
620
  return issues;
830
621
  }
831
-
832
- function deviceInterfaceBindingSignature(binding: DeviceInterfaceBinding | undefined): string {
622
+ function deviceInterfaceBindingSignature(binding) {
833
623
  if (binding === undefined) {
834
624
  return '';
835
625
  }
@@ -841,19 +631,12 @@ function deviceInterfaceBindingSignature(binding: DeviceInterfaceBinding | undef
841
631
  binding.externalInterfaceId ?? '',
842
632
  ].join(':');
843
633
  }
844
-
845
- function validatePanel(
846
- doc: CircuitDocument,
847
- componentIds: ReadonlySet<string>,
848
- semanticControlIds: ReadonlySet<string>,
849
- ): readonly ValidationIssue[] {
634
+ function validatePanel(doc, componentIds, semanticControlIds) {
850
635
  if (doc.panel === undefined) {
851
636
  return [];
852
637
  }
853
-
854
- const issues: ValidationIssue[] = [];
638
+ const issues = [];
855
639
  const resolvedElements = resolvePanelElements(doc);
856
-
857
640
  for (const face of doc.panel.faces) {
858
641
  for (const element of face.elements) {
859
642
  const componentId = element.bind.componentId;
@@ -875,7 +658,6 @@ function validatePanel(
875
658
  });
876
659
  continue;
877
660
  }
878
-
879
661
  const resolved = resolvePanelElement(resolvedElements, element);
880
662
  if (element.bind.controlId !== undefined && resolved === undefined) {
881
663
  issues.push({
@@ -887,7 +669,6 @@ function validatePanel(
887
669
  });
888
670
  continue;
889
671
  }
890
-
891
672
  if (resolved !== undefined && !panelKindsCompatible(element.kind, resolved.kind)) {
892
673
  issues.push({
893
674
  code: 'panel-kind-mismatch',
@@ -897,26 +678,21 @@ function validatePanel(
897
678
  });
898
679
  }
899
680
  }
900
-
901
681
  for (const issue of validatePanelCellCollisions(face)) {
902
682
  issues.push(issue);
903
683
  }
904
684
  }
905
-
906
685
  return issues;
907
686
  }
908
-
909
- function panelKindsCompatible(declared: PanelControlKind, resolved: PanelControlKind): boolean {
687
+ function panelKindsCompatible(declared, resolved) {
910
688
  if (declared === resolved) {
911
689
  return true;
912
690
  }
913
691
  return resolved === 'switch' && (declared === 'selector' || declared === 'footswitch');
914
692
  }
915
-
916
- function resolvePanelElements(doc: CircuitDocument): readonly ResolvedPanelElement[] {
693
+ function resolvePanelElements(doc) {
917
694
  const panel = extractPanel(doc);
918
- const resolved: ResolvedPanelElement[] = [];
919
-
695
+ const resolved = [];
920
696
  for (const knob of panel.knobs) {
921
697
  resolved.push({
922
698
  id: knob.id,
@@ -952,34 +728,21 @@ function resolvePanelElements(doc: CircuitDocument): readonly ResolvedPanelEleme
952
728
  kind: 'jack',
953
729
  });
954
730
  }
955
-
956
731
  return resolved;
957
732
  }
958
-
959
- function resolvePanelElement(
960
- resolvedElements: readonly ResolvedPanelElement[],
961
- element: PanelElementPlacement,
962
- ): ResolvedPanelElement | undefined {
733
+ function resolvePanelElement(resolvedElements, element) {
963
734
  if (element.bind.controlId !== undefined) {
964
- return resolvedElements.find((resolved) =>
965
- resolved.componentId === element.bind.componentId && resolved.id === element.bind.controlId,
966
- );
967
- }
968
-
969
- return resolvedElements.find((resolved) =>
970
- resolved.componentId === element.bind.componentId && resolved.id === element.bind.componentId,
971
- );
735
+ return resolvedElements.find((resolved) => resolved.componentId === element.bind.componentId && resolved.id === element.bind.controlId);
736
+ }
737
+ return resolvedElements.find((resolved) => resolved.componentId === element.bind.componentId && resolved.id === element.bind.componentId);
972
738
  }
973
-
974
- function componentIdFromPanelElementId(id: string): string {
739
+ function componentIdFromPanelElementId(id) {
975
740
  const separator = id.indexOf(':');
976
741
  return separator <= 0 ? id : id.slice(0, separator);
977
742
  }
978
-
979
- function validatePanelCellCollisions(face: PanelFace): readonly ValidationIssue[] {
980
- const issues: ValidationIssue[] = [];
981
- const occupied = new Map<string, PanelElementPlacement>();
982
-
743
+ function validatePanelCellCollisions(face) {
744
+ const issues = [];
745
+ const occupied = new Map();
983
746
  for (const element of face.elements) {
984
747
  const rowSpan = element.grid.rowSpan ?? 1;
985
748
  const columnSpan = element.grid.columnSpan ?? 1;
@@ -1001,19 +764,13 @@ function validatePanelCellCollisions(face: PanelFace): readonly ValidationIssue[
1001
764
  }
1002
765
  }
1003
766
  }
1004
-
1005
767
  return issues;
1006
768
  }
1007
-
1008
- function validateV3BuildMetadata(
1009
- doc: CircuitDocument,
1010
- componentIds: ReadonlySet<string>,
1011
- ): readonly ValidationIssue[] {
769
+ function validateV3BuildMetadata(doc, componentIds) {
1012
770
  if (!hasV3BuildMetadata(doc)) {
1013
771
  return [];
1014
772
  }
1015
-
1016
- const issues: ValidationIssue[] = [];
773
+ const issues = [];
1017
774
  const boards = doc.boards ?? [];
1018
775
  const boardsById = new Map(boards.map((board) => [board.id, board]));
1019
776
  const componentsById = new Map(doc.components.map((component) => [component.id, component]));
@@ -1027,54 +784,25 @@ function validateV3BuildMetadata(
1027
784
  board.id,
1028
785
  new Set(board.edgeTerminals.map((terminal) => terminal.id)),
1029
786
  ]));
1030
-
1031
787
  const selectedBoardId = doc.build?.selectedBoardId;
1032
788
  if (selectedBoardId !== undefined && !boardsById.has(selectedBoardId)) {
1033
- issues.push(unresolvedIssue(
1034
- 'build-board-unresolved',
1035
- 'error',
1036
- `Build selectedBoardId references missing board "${selectedBoardId}"`,
1037
- selectedBoardId,
1038
- 'selectedBoardId',
1039
- ));
1040
- }
1041
-
789
+ issues.push(unresolvedIssue('build-board-unresolved', 'error', `Build selectedBoardId references missing board "${selectedBoardId}"`, selectedBoardId, 'selectedBoardId'));
790
+ }
1042
791
  for (const boardId of doc.build?.alternateBoardIds ?? []) {
1043
792
  if (!boardsById.has(boardId)) {
1044
- issues.push(unresolvedIssue(
1045
- 'build-board-unresolved',
1046
- 'warning',
1047
- `Build alternateBoardIds references missing board "${boardId}"`,
1048
- boardId,
1049
- 'alternateBoardIds',
1050
- ));
793
+ issues.push(unresolvedIssue('build-board-unresolved', 'warning', `Build alternateBoardIds references missing board "${boardId}"`, boardId, 'alternateBoardIds'));
1051
794
  }
1052
795
  }
1053
-
1054
796
  const preferredBoardId = dataString(doc.mechanical?.internalBoard, 'preferredBoardId');
1055
797
  if (preferredBoardId !== undefined && !boardsById.has(preferredBoardId)) {
1056
- issues.push(unresolvedIssue(
1057
- 'build-board-unresolved',
1058
- 'warning',
1059
- `Mechanical internalBoard.preferredBoardId references missing board "${preferredBoardId}"`,
1060
- preferredBoardId,
1061
- 'mechanical.internalBoard.preferredBoardId',
1062
- ));
1063
- }
1064
-
798
+ issues.push(unresolvedIssue('build-board-unresolved', 'warning', `Mechanical internalBoard.preferredBoardId references missing board "${preferredBoardId}"`, preferredBoardId, 'mechanical.internalBoard.preferredBoardId'));
799
+ }
1065
800
  const harnessesById = new Map(doc.offBoardWiring?.harnesses.map((harness) => [harness.id, harness]) ?? []);
1066
801
  for (const harnessId of doc.build?.selectedOffBoardWiringHarnessIds ?? []) {
1067
802
  if (!harnessesById.has(harnessId)) {
1068
- issues.push(unresolvedIssue(
1069
- 'build-harness-unresolved',
1070
- 'error',
1071
- `Build selectedOffBoardWiringHarnessIds references missing harness "${harnessId}"`,
1072
- harnessId,
1073
- 'selectedOffBoardWiringHarnessIds',
1074
- ));
803
+ issues.push(unresolvedIssue('build-harness-unresolved', 'error', `Build selectedOffBoardWiringHarnessIds references missing harness "${harnessId}"`, harnessId, 'selectedOffBoardWiringHarnessIds'));
1075
804
  }
1076
805
  }
1077
-
1078
806
  for (const item of doc.bom?.items ?? []) {
1079
807
  for (const ref of item.refs) {
1080
808
  const issue = validateBomRef(ref, componentIds, controlIds, panelElementIds, boardsById, item.id);
@@ -1083,32 +811,21 @@ function validateV3BuildMetadata(
1083
811
  }
1084
812
  }
1085
813
  }
1086
-
1087
814
  for (const board of boards) {
1088
815
  issues.push(...validateBoardRealization(board, componentsById, boardNetsByBoardId));
1089
816
  }
1090
-
1091
817
  if (doc.offBoardWiring !== undefined) {
1092
- issues.push(...validateOffBoardWiring(
1093
- doc,
1094
- componentsById,
1095
- panelElementIds,
1096
- boardTerminalsByBoardId,
1097
- boardNetsByBoardId,
1098
- ));
1099
- }
1100
-
818
+ issues.push(...validateOffBoardWiring(doc, componentsById, panelElementIds, boardTerminalsByBoardId, boardNetsByBoardId));
819
+ }
1101
820
  if (doc.build?.completeness === 'complete-selected-build' && selectedBoardId !== undefined) {
1102
821
  const selectedBoard = boardsById.get(selectedBoardId);
1103
822
  if (selectedBoard !== undefined) {
1104
823
  issues.push(...validateCompleteSelectedBoardRoutes(selectedBoard));
1105
824
  }
1106
825
  }
1107
-
1108
826
  return issues;
1109
827
  }
1110
-
1111
- function hasV3BuildMetadata(doc: CircuitDocument): boolean {
828
+ function hasV3BuildMetadata(doc) {
1112
829
  return doc.mechanical !== undefined ||
1113
830
  doc.build !== undefined ||
1114
831
  doc.bom !== undefined ||
@@ -1116,69 +833,27 @@ function hasV3BuildMetadata(doc: CircuitDocument): boolean {
1116
833
  doc.footprints !== undefined ||
1117
834
  doc.offBoardWiring !== undefined ||
1118
835
  doc.boards !== undefined ||
1119
- doc.panel?.faces.some((face) =>
1120
- face.geometry !== undefined ||
1121
- face.elements.some((element) => element.id !== undefined || element.physical !== undefined)
1122
- ) === true;
836
+ doc.panel?.faces.some((face) => face.geometry !== undefined ||
837
+ face.elements.some((element) => element.id !== undefined || element.physical !== undefined)) === true;
1123
838
  }
1124
-
1125
- function validateBomRef(
1126
- ref: BuildBomRef,
1127
- componentIds: ReadonlySet<string>,
1128
- controlIds: ReadonlySet<string>,
1129
- panelElementIds: ReadonlySet<string>,
1130
- boardsById: ReadonlyMap<string, BoardRealization>,
1131
- itemId: string,
1132
- ): ValidationIssue | undefined {
839
+ function validateBomRef(ref, componentIds, controlIds, panelElementIds, boardsById, itemId) {
1133
840
  if (ref.kind === 'component' && (ref.componentId === undefined || !componentIds.has(ref.componentId))) {
1134
- return unresolvedIssue(
1135
- 'bom-ref-unresolved',
1136
- 'warning',
1137
- `BOM item "${itemId}" references missing component "${ref.componentId ?? ''}"`,
1138
- itemId,
1139
- 'refs.componentId',
1140
- );
1141
- }
1142
- if (
1143
- ref.kind === 'device-interface-control' &&
1144
- (ref.controlId === undefined || !controlIds.has(ref.controlId))
1145
- ) {
1146
- return unresolvedIssue(
1147
- 'bom-ref-unresolved',
1148
- 'warning',
1149
- `BOM item "${itemId}" references missing device interface control "${ref.controlId ?? ''}"`,
1150
- itemId,
1151
- 'refs.controlId',
1152
- );
841
+ return unresolvedIssue('bom-ref-unresolved', 'warning', `BOM item "${itemId}" references missing component "${ref.componentId ?? ''}"`, itemId, 'refs.componentId');
842
+ }
843
+ if (ref.kind === 'device-interface-control' &&
844
+ (ref.controlId === undefined || !controlIds.has(ref.controlId))) {
845
+ return unresolvedIssue('bom-ref-unresolved', 'warning', `BOM item "${itemId}" references missing device interface control "${ref.controlId ?? ''}"`, itemId, 'refs.controlId');
1153
846
  }
1154
847
  if (ref.kind === 'panel-element' && (ref.panelElementId === undefined || !panelElementIds.has(ref.panelElementId))) {
1155
- return unresolvedIssue(
1156
- 'bom-ref-unresolved',
1157
- 'warning',
1158
- `BOM item "${itemId}" references missing panel element "${ref.panelElementId ?? ''}"`,
1159
- itemId,
1160
- 'refs.panelElementId',
1161
- );
848
+ return unresolvedIssue('bom-ref-unresolved', 'warning', `BOM item "${itemId}" references missing panel element "${ref.panelElementId ?? ''}"`, itemId, 'refs.panelElementId');
1162
849
  }
1163
850
  if (ref.kind === 'board' && (ref.boardId === undefined || !boardsById.has(ref.boardId))) {
1164
- return unresolvedIssue(
1165
- 'bom-ref-unresolved',
1166
- 'warning',
1167
- `BOM item "${itemId}" references missing board "${ref.boardId ?? ''}"`,
1168
- itemId,
1169
- 'refs.boardId',
1170
- );
851
+ return unresolvedIssue('bom-ref-unresolved', 'warning', `BOM item "${itemId}" references missing board "${ref.boardId ?? ''}"`, itemId, 'refs.boardId');
1171
852
  }
1172
853
  return undefined;
1173
854
  }
1174
-
1175
- function validateBoardRealization(
1176
- board: BoardRealization,
1177
- componentsById: ReadonlyMap<string, Component>,
1178
- boardNetsByBoardId: ReadonlyMap<string, ReadonlySet<string>>,
1179
- ): readonly ValidationIssue[] {
1180
- const issues: ValidationIssue[] = [];
1181
-
855
+ function validateBoardRealization(board, componentsById, boardNetsByBoardId) {
856
+ const issues = [];
1182
857
  if (board.sourceCircuit !== undefined && !isDigestShapedSourceHash(board.sourceCircuit.hash)) {
1183
858
  issues.push({
1184
859
  code: 'board-source-hash-invalid',
@@ -1188,60 +863,30 @@ function validateBoardRealization(
1188
863
  property: 'sourceCircuit.hash',
1189
864
  });
1190
865
  }
1191
-
1192
866
  for (const terminal of board.edgeTerminals) {
1193
867
  if (terminal.terminalRef !== undefined && !componentTerminalExists(componentsById, terminal.terminalRef)) {
1194
- issues.push(unresolvedIssue(
1195
- 'board-terminal-unresolved',
1196
- 'warning',
1197
- `Board "${board.id}" edge terminal "${terminal.id}" references missing component terminal`,
1198
- board.id,
1199
- terminal.id,
1200
- ));
868
+ issues.push(unresolvedIssue('board-terminal-unresolved', 'warning', `Board "${board.id}" edge terminal "${terminal.id}" references missing component terminal`, board.id, terminal.id));
1201
869
  }
1202
870
  }
1203
-
1204
871
  for (const placement of board.footprintPlacements) {
1205
872
  if (!componentsById.has(placement.componentId)) {
1206
- issues.push(unresolvedIssue(
1207
- 'board-terminal-unresolved',
1208
- 'warning',
1209
- `Board "${board.id}" places missing component "${placement.componentId}"`,
1210
- board.id,
1211
- placement.componentId,
1212
- ));
873
+ issues.push(unresolvedIssue('board-terminal-unresolved', 'warning', `Board "${board.id}" places missing component "${placement.componentId}"`, board.id, placement.componentId));
1213
874
  continue;
1214
875
  }
1215
876
  for (const pad of placement.pads) {
1216
- if (
1217
- pad.terminalName !== undefined &&
1218
- !componentHasTerminal(componentsById, placement.componentId, pad.terminalName)
1219
- ) {
1220
- issues.push(unresolvedIssue(
1221
- 'board-terminal-unresolved',
1222
- 'warning',
1223
- `Board "${board.id}" pad "${pad.padId}" references missing terminal "${pad.terminalName}"`,
1224
- board.id,
1225
- pad.padId,
1226
- ));
877
+ if (pad.terminalName !== undefined &&
878
+ !componentHasTerminal(componentsById, placement.componentId, pad.terminalName)) {
879
+ issues.push(unresolvedIssue('board-terminal-unresolved', 'warning', `Board "${board.id}" pad "${pad.padId}" references missing terminal "${pad.terminalName}"`, board.id, pad.padId));
1227
880
  }
1228
881
  }
1229
882
  }
1230
-
1231
883
  for (const net of board.netlist?.nets ?? []) {
1232
884
  for (const member of net.members) {
1233
885
  if (!componentTerminalExists(componentsById, member)) {
1234
- issues.push(unresolvedIssue(
1235
- 'board-terminal-unresolved',
1236
- 'warning',
1237
- `Board "${board.id}" net "${net.id}" references missing component terminal`,
1238
- board.id,
1239
- net.id,
1240
- ));
886
+ issues.push(unresolvedIssue('board-terminal-unresolved', 'warning', `Board "${board.id}" net "${net.id}" references missing component terminal`, board.id, net.id));
1241
887
  }
1242
888
  }
1243
889
  }
1244
-
1245
890
  for (const route of board.routes) {
1246
891
  if (route.zones !== undefined || route.drills !== undefined) {
1247
892
  issues.push({
@@ -1253,31 +898,16 @@ function validateBoardRealization(
1253
898
  });
1254
899
  }
1255
900
  if (isBoardNetlistRef(route.netRef) && !boardNetRefExists(route.netRef, board.id, boardNetsByBoardId)) {
1256
- issues.push(unresolvedIssue(
1257
- 'offboard-signal-unresolved',
1258
- 'warning',
1259
- `Board "${board.id}" route "${route.id}" references missing board net "${route.netRef.netId}"`,
1260
- board.id,
1261
- route.id,
1262
- ));
901
+ issues.push(unresolvedIssue('offboard-signal-unresolved', 'warning', `Board "${board.id}" route "${route.id}" references missing board net "${route.netRef.netId}"`, board.id, route.id));
1263
902
  }
1264
903
  }
1265
-
1266
904
  return issues;
1267
905
  }
1268
-
1269
- function validateOffBoardWiring(
1270
- doc: CircuitDocument,
1271
- componentsById: ReadonlyMap<string, Component>,
1272
- panelElementIds: ReadonlySet<string>,
1273
- boardTerminalsByBoardId: ReadonlyMap<string, ReadonlySet<string>>,
1274
- boardNetsByBoardId: ReadonlyMap<string, ReadonlySet<string>>,
1275
- ): readonly ValidationIssue[] {
1276
- const issues: ValidationIssue[] = [];
1277
- const endpointIds = new Set<string>();
1278
-
906
+ function validateOffBoardWiring(doc, componentsById, panelElementIds, boardTerminalsByBoardId, boardNetsByBoardId) {
907
+ const issues = [];
908
+ const endpointIds = new Set();
1279
909
  for (const harness of doc.offBoardWiring?.harnesses ?? []) {
1280
- const localEndpointIds = new Set<string>();
910
+ const localEndpointIds = new Set();
1281
911
  for (const endpoint of harness.endpoints) {
1282
912
  endpointIds.add(endpoint.id);
1283
913
  localEndpointIds.add(endpoint.id);
@@ -1286,25 +916,12 @@ function validateOffBoardWiring(
1286
916
  issues.push(issue);
1287
917
  }
1288
918
  }
1289
-
1290
919
  for (const connection of harness.connections) {
1291
920
  if (!localEndpointIds.has(connection.fromEndpointId)) {
1292
- issues.push(unresolvedIssue(
1293
- 'offboard-endpoint-unresolved',
1294
- 'error',
1295
- `Harness "${harness.id}" connection "${connection.id}" references missing endpoint "${connection.fromEndpointId}"`,
1296
- harness.id,
1297
- connection.id,
1298
- ));
921
+ issues.push(unresolvedIssue('offboard-endpoint-unresolved', 'error', `Harness "${harness.id}" connection "${connection.id}" references missing endpoint "${connection.fromEndpointId}"`, harness.id, connection.id));
1299
922
  }
1300
923
  if (!localEndpointIds.has(connection.toEndpointId)) {
1301
- issues.push(unresolvedIssue(
1302
- 'offboard-endpoint-unresolved',
1303
- 'error',
1304
- `Harness "${harness.id}" connection "${connection.id}" references missing endpoint "${connection.toEndpointId}"`,
1305
- harness.id,
1306
- connection.id,
1307
- ));
924
+ issues.push(unresolvedIssue('offboard-endpoint-unresolved', 'error', `Harness "${harness.id}" connection "${connection.id}" references missing endpoint "${connection.toEndpointId}"`, harness.id, connection.id));
1308
925
  }
1309
926
  if (connection.signalRef !== undefined) {
1310
927
  const issue = validateOffBoardSignalRef(connection.signalRef, componentsById, boardNetsByBoardId, harness.id);
@@ -1314,7 +931,6 @@ function validateOffBoardWiring(
1314
931
  }
1315
932
  }
1316
933
  }
1317
-
1318
934
  for (const harnessId of doc.build?.selectedOffBoardWiringHarnessIds ?? []) {
1319
935
  const harness = doc.offBoardWiring?.harnesses.find((candidate) => candidate.id === harnessId);
1320
936
  if (harness === undefined) {
@@ -1322,122 +938,59 @@ function validateOffBoardWiring(
1322
938
  }
1323
939
  for (const connection of harness.connections) {
1324
940
  if (!endpointIds.has(connection.fromEndpointId) || !endpointIds.has(connection.toEndpointId)) {
1325
- issues.push(unresolvedIssue(
1326
- 'offboard-endpoint-unresolved',
1327
- 'error',
1328
- `Selected harness "${harnessId}" contains an unresolved connection endpoint`,
1329
- harnessId,
1330
- connection.id,
1331
- ));
941
+ issues.push(unresolvedIssue('offboard-endpoint-unresolved', 'error', `Selected harness "${harnessId}" contains an unresolved connection endpoint`, harnessId, connection.id));
1332
942
  }
1333
943
  }
1334
944
  }
1335
-
1336
945
  return issues;
1337
946
  }
1338
-
1339
- function validateOffBoardEndpoint(
1340
- endpoint: OffBoardWiringEndpoint,
1341
- componentsById: ReadonlyMap<string, Component>,
1342
- panelElementIds: ReadonlySet<string>,
1343
- boardTerminalsByBoardId: ReadonlyMap<string, ReadonlySet<string>>,
1344
- ): ValidationIssue | undefined {
947
+ function validateOffBoardEndpoint(endpoint, componentsById, panelElementIds, boardTerminalsByBoardId) {
1345
948
  if (endpoint.kind === 'board-terminal') {
1346
949
  const terminalIds = endpoint.boardId === undefined ? undefined : boardTerminalsByBoardId.get(endpoint.boardId);
1347
950
  if (terminalIds === undefined || endpoint.terminalId === undefined || !terminalIds.has(endpoint.terminalId)) {
1348
- return unresolvedIssue(
1349
- 'offboard-endpoint-unresolved',
1350
- 'error',
1351
- `Off-board endpoint "${endpoint.id}" references missing board terminal`,
1352
- endpoint.id,
1353
- 'terminalId',
1354
- );
951
+ return unresolvedIssue('offboard-endpoint-unresolved', 'error', `Off-board endpoint "${endpoint.id}" references missing board terminal`, endpoint.id, 'terminalId');
1355
952
  }
1356
953
  return undefined;
1357
954
  }
1358
-
1359
- if (
1360
- endpoint.kind === 'panel-component-terminal' ||
955
+ if (endpoint.kind === 'panel-component-terminal' ||
1361
956
  endpoint.kind === 'power-terminal' ||
1362
- endpoint.kind === 'footswitch-terminal'
1363
- ) {
1364
- if (
1365
- endpoint.componentId === undefined ||
957
+ endpoint.kind === 'footswitch-terminal') {
958
+ if (endpoint.componentId === undefined ||
1366
959
  endpoint.terminalName === undefined ||
1367
- !componentHasTerminal(componentsById, endpoint.componentId, endpoint.terminalName)
1368
- ) {
1369
- return unresolvedIssue(
1370
- 'offboard-endpoint-unresolved',
1371
- 'error',
1372
- `Off-board endpoint "${endpoint.id}" references missing component terminal`,
1373
- endpoint.id,
1374
- 'componentId',
1375
- );
960
+ !componentHasTerminal(componentsById, endpoint.componentId, endpoint.terminalName)) {
961
+ return unresolvedIssue('offboard-endpoint-unresolved', 'error', `Off-board endpoint "${endpoint.id}" references missing component terminal`, endpoint.id, 'componentId');
1376
962
  }
1377
- if (
1378
- endpoint.panelElementId !== undefined &&
963
+ if (endpoint.panelElementId !== undefined &&
1379
964
  endpoint.kind !== 'power-terminal' &&
1380
- !panelElementIds.has(endpoint.panelElementId)
1381
- ) {
1382
- return unresolvedIssue(
1383
- 'offboard-endpoint-unresolved',
1384
- 'warning',
1385
- `Off-board endpoint "${endpoint.id}" references missing panel element "${endpoint.panelElementId}"`,
1386
- endpoint.id,
1387
- 'panelElementId',
1388
- );
965
+ !panelElementIds.has(endpoint.panelElementId)) {
966
+ return unresolvedIssue('offboard-endpoint-unresolved', 'warning', `Off-board endpoint "${endpoint.id}" references missing panel element "${endpoint.panelElementId}"`, endpoint.id, 'panelElementId');
1389
967
  }
1390
968
  }
1391
-
1392
969
  return undefined;
1393
970
  }
1394
-
1395
- function validateOffBoardSignalRef(
1396
- signalRef: OffBoardSignalRef,
1397
- componentsById: ReadonlyMap<string, Component>,
1398
- boardNetsByBoardId: ReadonlyMap<string, ReadonlySet<string>>,
1399
- harnessId: string,
1400
- ): ValidationIssue | undefined {
971
+ function validateOffBoardSignalRef(signalRef, componentsById, boardNetsByBoardId, harnessId) {
1401
972
  if (isBoardNetlistRef(signalRef)) {
1402
973
  if (!boardNetRefExists(signalRef, signalRef.boardId, boardNetsByBoardId)) {
1403
- return unresolvedIssue(
1404
- 'offboard-signal-unresolved',
1405
- 'error',
1406
- `Harness "${harnessId}" references missing board net "${signalRef.netId}"`,
1407
- harnessId,
1408
- 'signalRef',
1409
- );
974
+ return unresolvedIssue('offboard-signal-unresolved', 'error', `Harness "${harnessId}" references missing board net "${signalRef.netId}"`, harnessId, 'signalRef');
1410
975
  }
1411
976
  return undefined;
1412
977
  }
1413
-
1414
978
  const member = dataObject(signalRef, 'member');
1415
979
  const componentId = dataString(member, 'componentId');
1416
980
  const terminalName = dataString(member, 'terminalName');
1417
981
  if (dataString(signalRef, 'source') === 'canonical-circuit' && componentId !== undefined && terminalName !== undefined) {
1418
982
  if (!componentHasTerminal(componentsById, componentId, terminalName)) {
1419
- return unresolvedIssue(
1420
- 'offboard-signal-unresolved',
1421
- 'error',
1422
- `Harness "${harnessId}" references missing canonical component terminal`,
1423
- harnessId,
1424
- 'signalRef',
1425
- );
983
+ return unresolvedIssue('offboard-signal-unresolved', 'error', `Harness "${harnessId}" references missing canonical component terminal`, harnessId, 'signalRef');
1426
984
  }
1427
985
  }
1428
-
1429
986
  return undefined;
1430
987
  }
1431
-
1432
- function validateCompleteSelectedBoardRoutes(board: BoardRealization): readonly ValidationIssue[] {
1433
- const issues: ValidationIssue[] = [];
1434
- const routedNetIds = new Set(
1435
- board.routes
1436
- .filter((route) => isRouteForBoardNet(route, board.id))
1437
- .map((route) => dataString(route.netRef, 'netId'))
1438
- .filter((netId): netId is string => netId !== undefined),
1439
- );
1440
-
988
+ function validateCompleteSelectedBoardRoutes(board) {
989
+ const issues = [];
990
+ const routedNetIds = new Set(board.routes
991
+ .filter((route) => isRouteForBoardNet(route, board.id))
992
+ .map((route) => dataString(route.netRef, 'netId'))
993
+ .filter((netId) => netId !== undefined));
1441
994
  for (const net of board.netlist?.nets ?? []) {
1442
995
  if (isSingleTerminalEdgeNet(net)) {
1443
996
  continue;
@@ -1452,43 +1005,29 @@ function validateCompleteSelectedBoardRoutes(board: BoardRealization): readonly
1452
1005
  });
1453
1006
  }
1454
1007
  }
1455
-
1456
1008
  return issues;
1457
1009
  }
1458
-
1459
- function isSingleTerminalEdgeNet(net: BoardNet): boolean {
1010
+ function isSingleTerminalEdgeNet(net) {
1460
1011
  return net.members.length <= 1;
1461
1012
  }
1462
-
1463
- function isRouteForBoardNet(route: BoardRoute, boardId: string): boolean {
1013
+ function isRouteForBoardNet(route, boardId) {
1464
1014
  if (!isBoardNetlistRef(route.netRef)) {
1465
1015
  return false;
1466
1016
  }
1467
1017
  return route.netRef.boardId === undefined || route.netRef.boardId === boardId;
1468
1018
  }
1469
-
1470
- function isBoardNetlistRef(value: VdspBuildDataObject | undefined): value is VdspBuildDataObject & {
1471
- source: 'board-netlist';
1472
- boardId?: string;
1473
- netId: string;
1474
- } {
1019
+ function isBoardNetlistRef(value) {
1475
1020
  return dataString(value, 'source') === 'board-netlist' && dataString(value, 'netId') !== undefined;
1476
1021
  }
1477
-
1478
- function boardNetRefExists(
1479
- ref: VdspBuildDataObject & { netId: string; boardId?: string },
1480
- fallbackBoardId: string | undefined,
1481
- boardNetsByBoardId: ReadonlyMap<string, ReadonlySet<string>>,
1482
- ): boolean {
1022
+ function boardNetRefExists(ref, fallbackBoardId, boardNetsByBoardId) {
1483
1023
  const boardId = ref.boardId ?? fallbackBoardId;
1484
1024
  if (boardId === undefined) {
1485
1025
  return Array.from(boardNetsByBoardId.values()).some((netIds) => netIds.has(ref.netId));
1486
1026
  }
1487
1027
  return boardNetsByBoardId.get(boardId)?.has(ref.netId) === true;
1488
1028
  }
1489
-
1490
- function collectPanelElementIds(doc: CircuitDocument): ReadonlySet<string> {
1491
- const ids = new Set<string>();
1029
+ function collectPanelElementIds(doc) {
1030
+ const ids = new Set();
1492
1031
  for (const face of doc.panel?.faces ?? []) {
1493
1032
  for (const element of face.elements) {
1494
1033
  if (element.id !== undefined) {
@@ -1502,47 +1041,27 @@ function collectPanelElementIds(doc: CircuitDocument): ReadonlySet<string> {
1502
1041
  }
1503
1042
  return ids;
1504
1043
  }
1505
-
1506
- function componentTerminalExists(
1507
- componentsById: ReadonlyMap<string, Component>,
1508
- ref: Readonly<{ componentId: string; terminalName: string }>,
1509
- ): boolean {
1044
+ function componentTerminalExists(componentsById, ref) {
1510
1045
  return componentHasTerminal(componentsById, ref.componentId, ref.terminalName);
1511
1046
  }
1512
-
1513
- function componentHasTerminal(
1514
- componentsById: ReadonlyMap<string, Component>,
1515
- componentId: string,
1516
- terminalName: string,
1517
- ): boolean {
1047
+ function componentHasTerminal(componentsById, componentId, terminalName) {
1518
1048
  return componentsById.get(componentId)?.terminals.some((terminal) => terminal.name === terminalName) === true;
1519
1049
  }
1520
-
1521
- function isDigestShapedSourceHash(hash: string): boolean {
1050
+ function isDigestShapedSourceHash(hash) {
1522
1051
  return /^sha256:[0-9a-f]{64}$/i.test(hash);
1523
1052
  }
1524
-
1525
- function dataString(object: VdspBuildDataObject | undefined, key: string): string | undefined {
1053
+ function dataString(object, key) {
1526
1054
  const value = object?.[key];
1527
1055
  return typeof value === 'string' ? value : undefined;
1528
1056
  }
1529
-
1530
- function dataObject(object: VdspBuildDataObject | undefined, key: string): VdspBuildDataObject | undefined {
1057
+ function dataObject(object, key) {
1531
1058
  const value = object?.[key];
1532
1059
  return isBuildDataObject(value) ? value : undefined;
1533
1060
  }
1534
-
1535
- function isBuildDataObject(value: unknown): value is VdspBuildDataObject {
1061
+ function isBuildDataObject(value) {
1536
1062
  return typeof value === 'object' && value !== null && !Array.isArray(value);
1537
1063
  }
1538
-
1539
- function unresolvedIssue(
1540
- code: ValidationCode,
1541
- severity: ValidationSeverity,
1542
- message: string,
1543
- componentId: string,
1544
- property: string,
1545
- ): ValidationIssue {
1064
+ function unresolvedIssue(code, severity, message, componentId, property) {
1546
1065
  return {
1547
1066
  code,
1548
1067
  severity,
@@ -1551,8 +1070,7 @@ function unresolvedIssue(
1551
1070
  property,
1552
1071
  };
1553
1072
  }
1554
-
1555
- function missingPropertyIssue(component: Component, rule: PropertyRule): ValidationIssue {
1073
+ function missingPropertyIssue(component, rule) {
1556
1074
  return {
1557
1075
  code: rule.kind === 'string' ? 'model-required' : 'value-required',
1558
1076
  severity: 'error',
@@ -1561,3 +1079,4 @@ function missingPropertyIssue(component: Component, rule: PropertyRule): Validat
1561
1079
  property: rule.name,
1562
1080
  };
1563
1081
  }
1082
+ //# sourceMappingURL=validation.js.map