@vessel-dsp/core 0.5.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 (241) hide show
  1. package/README.md +10 -0
  2. package/dist/editor/commands.d.ts +48 -0
  3. package/dist/editor/commands.d.ts.map +1 -0
  4. package/{src/editor/commands.ts → dist/editor/commands.js} +44 -91
  5. package/dist/editor/commands.js.map +1 -0
  6. package/dist/editor/factory.d.ts +10 -0
  7. package/dist/editor/factory.d.ts.map +1 -0
  8. package/{src/editor/factory.ts → dist/editor/factory.js} +11 -27
  9. package/dist/editor/factory.js.map +1 -0
  10. package/dist/editor/history.d.ts +29 -0
  11. package/dist/editor/history.d.ts.map +1 -0
  12. package/{src/editor/history.ts → dist/editor/history.js} +12 -42
  13. package/dist/editor/history.js.map +1 -0
  14. package/{src/editor/index.ts → dist/editor/index.d.ts} +1 -3
  15. package/dist/editor/index.d.ts.map +1 -0
  16. package/dist/editor/index.js +5 -0
  17. package/dist/editor/index.js.map +1 -0
  18. package/dist/editor/layout.d.ts +8 -0
  19. package/dist/editor/layout.d.ts.map +1 -0
  20. package/{src/editor/layout.ts → dist/editor/layout.js} +36 -90
  21. package/dist/editor/layout.js.map +1 -0
  22. package/dist/formats/circuit-json/serializer.d.ts +86 -0
  23. package/dist/formats/circuit-json/serializer.d.ts.map +1 -0
  24. package/{src/formats/circuit-json/serializer.ts → dist/formats/circuit-json/serializer.js} +114 -414
  25. package/dist/formats/circuit-json/serializer.js.map +1 -0
  26. package/dist/formats/document.d.ts +64 -0
  27. package/dist/formats/document.d.ts.map +1 -0
  28. package/dist/formats/document.js +300 -0
  29. package/dist/formats/document.js.map +1 -0
  30. package/dist/formats/interchange/parser.d.ts +3 -0
  31. package/dist/formats/interchange/parser.d.ts.map +1 -0
  32. package/{src/formats/interchange/parser.ts → dist/formats/interchange/parser.js} +651 -299
  33. package/dist/formats/interchange/parser.js.map +1 -0
  34. package/dist/formats/interchange/serializer.d.ts +9 -0
  35. package/dist/formats/interchange/serializer.d.ts.map +1 -0
  36. package/{src/formats/interchange/serializer.ts → dist/formats/interchange/serializer.js} +151 -158
  37. package/dist/formats/interchange/serializer.js.map +1 -0
  38. package/dist/formats/ltspice/catalog.d.ts +19 -0
  39. package/dist/formats/ltspice/catalog.d.ts.map +1 -0
  40. package/{src/formats/ltspice/catalog.ts → dist/formats/ltspice/catalog.js} +18 -52
  41. package/dist/formats/ltspice/catalog.js.map +1 -0
  42. package/dist/formats/ltspice/encoding.d.ts +2 -0
  43. package/dist/formats/ltspice/encoding.d.ts.map +1 -0
  44. package/{src/formats/ltspice/encoding.ts → dist/formats/ltspice/encoding.js} +17 -41
  45. package/dist/formats/ltspice/encoding.js.map +1 -0
  46. package/dist/formats/ltspice/parser.d.ts +3 -0
  47. package/dist/formats/ltspice/parser.d.ts.map +1 -0
  48. package/{src/formats/ltspice/parser.ts → dist/formats/ltspice/parser.js} +39 -141
  49. package/dist/formats/ltspice/parser.js.map +1 -0
  50. package/dist/formats/ltspice/serializer.d.ts +7 -0
  51. package/dist/formats/ltspice/serializer.d.ts.map +1 -0
  52. package/{src/formats/ltspice/serializer.ts → dist/formats/ltspice/serializer.js} +18 -45
  53. package/dist/formats/ltspice/serializer.js.map +1 -0
  54. package/dist/formats/schx/catalog.d.ts +19 -0
  55. package/dist/formats/schx/catalog.d.ts.map +1 -0
  56. package/{src/formats/schx/catalog.ts → dist/formats/schx/catalog.js} +48 -101
  57. package/dist/formats/schx/catalog.js.map +1 -0
  58. package/dist/formats/schx/parser.d.ts +3 -0
  59. package/dist/formats/schx/parser.d.ts.map +1 -0
  60. package/{src/formats/schx/parser.ts → dist/formats/schx/parser.js} +31 -86
  61. package/dist/formats/schx/parser.js.map +1 -0
  62. package/dist/formats/schx/runtime-descriptors.d.ts +3 -0
  63. package/dist/formats/schx/runtime-descriptors.d.ts.map +1 -0
  64. package/{src/formats/schx/runtime-descriptors.ts → dist/formats/schx/runtime-descriptors.js} +36 -123
  65. package/dist/formats/schx/runtime-descriptors.js.map +1 -0
  66. package/dist/formats/schx/serializer.d.ts +5 -0
  67. package/dist/formats/schx/serializer.d.ts.map +1 -0
  68. package/{src/formats/schx/serializer.ts → dist/formats/schx/serializer.js} +17 -42
  69. package/dist/formats/schx/serializer.js.map +1 -0
  70. package/dist/formats/schx/transforms.d.ts +4 -0
  71. package/dist/formats/schx/transforms.d.ts.map +1 -0
  72. package/{src/formats/schx/transforms.ts → dist/formats/schx/transforms.js} +6 -10
  73. package/dist/formats/schx/transforms.js.map +1 -0
  74. package/dist/formats/spice/parser.d.ts +3 -0
  75. package/dist/formats/spice/parser.d.ts.map +1 -0
  76. package/{src/formats/spice/parser.ts → dist/formats/spice/parser.js} +50 -96
  77. package/dist/formats/spice/parser.js.map +1 -0
  78. package/dist/formats/spice/serializer.d.ts +3 -0
  79. package/dist/formats/spice/serializer.d.ts.map +1 -0
  80. package/{src/formats/spice/serializer.ts → dist/formats/spice/serializer.js} +8 -13
  81. package/dist/formats/spice/serializer.js.map +1 -0
  82. package/dist/index.d.ts +47 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +32 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/model/connectivity.d.ts +16 -0
  87. package/dist/model/connectivity.d.ts.map +1 -0
  88. package/{src/model/connectivity.ts → dist/model/connectivity.js} +28 -63
  89. package/dist/model/connectivity.js.map +1 -0
  90. package/dist/model/netlist.d.ts +24 -0
  91. package/dist/model/netlist.d.ts.map +1 -0
  92. package/{src/model/netlist.ts → dist/model/netlist.js} +42 -110
  93. package/dist/model/netlist.js.map +1 -0
  94. package/dist/model/properties.d.ts +9 -0
  95. package/dist/model/properties.d.ts.map +1 -0
  96. package/{src/model/properties.ts → dist/model/properties.js} +10 -18
  97. package/dist/model/properties.js.map +1 -0
  98. package/dist/model/quantity.d.ts +3 -0
  99. package/dist/model/quantity.d.ts.map +1 -0
  100. package/{src/model/quantity.ts → dist/model/quantity.js} +7 -30
  101. package/dist/model/quantity.js.map +1 -0
  102. package/dist/model/types.d.ts +431 -0
  103. package/dist/model/types.d.ts.map +1 -0
  104. package/dist/model/types.js +10 -0
  105. package/dist/model/types.js.map +1 -0
  106. package/dist/model/validation.d.ts +32 -0
  107. package/dist/model/validation.d.ts.map +1 -0
  108. package/{src/model/validation.ts → dist/model/validation.js} +420 -323
  109. package/dist/model/validation.js.map +1 -0
  110. package/dist/model/wires.d.ts +3 -0
  111. package/dist/model/wires.d.ts.map +1 -0
  112. package/{src/model/wires.ts → dist/model/wires.js} +10 -16
  113. package/dist/model/wires.js.map +1 -0
  114. package/dist/panel/extract.d.ts +5 -0
  115. package/dist/panel/extract.d.ts.map +1 -0
  116. package/{src/panel/extract.ts → dist/panel/extract.js} +146 -235
  117. package/dist/panel/extract.js.map +1 -0
  118. package/dist/panel/index.d.ts +6 -0
  119. package/dist/panel/index.d.ts.map +1 -0
  120. package/dist/panel/index.js +5 -0
  121. package/dist/panel/index.js.map +1 -0
  122. package/dist/panel/knobs.d.ts +7 -0
  123. package/dist/panel/knobs.d.ts.map +1 -0
  124. package/{src/panel/knobs.ts → dist/panel/knobs.js} +7 -18
  125. package/dist/panel/knobs.js.map +1 -0
  126. package/dist/panel/protocol.d.ts +9 -0
  127. package/dist/panel/protocol.d.ts.map +1 -0
  128. package/{src/panel/protocol.ts → dist/panel/protocol.js} +10 -26
  129. package/dist/panel/protocol.js.map +1 -0
  130. package/{src/panel/types.ts → dist/panel/types.d.ts} +50 -89
  131. package/dist/panel/types.d.ts.map +1 -0
  132. package/dist/panel/types.js +2 -0
  133. package/dist/panel/types.js.map +1 -0
  134. package/dist/preview/bounds.d.ts +12 -0
  135. package/dist/preview/bounds.d.ts.map +1 -0
  136. package/{src/preview/bounds.ts → dist/preview/bounds.js} +15 -29
  137. package/dist/preview/bounds.js.map +1 -0
  138. package/dist/preview/box-layout.d.ts +4 -0
  139. package/dist/preview/box-layout.d.ts.map +1 -0
  140. package/{src/preview/box-layout.ts → dist/preview/box-layout.js} +2 -6
  141. package/dist/preview/box-layout.js.map +1 -0
  142. package/dist/preview/colors.d.ts +3 -0
  143. package/dist/preview/colors.d.ts.map +1 -0
  144. package/{src/preview/colors.ts → dist/preview/colors.js} +3 -5
  145. package/dist/preview/colors.js.map +1 -0
  146. package/dist/preview/hanging.d.ts +8 -0
  147. package/dist/preview/hanging.d.ts.map +1 -0
  148. package/{src/preview/hanging.ts → dist/preview/hanging.js} +9 -28
  149. package/dist/preview/hanging.js.map +1 -0
  150. package/dist/preview/junctions.d.ts +3 -0
  151. package/dist/preview/junctions.d.ts.map +1 -0
  152. package/{src/preview/junctions.ts → dist/preview/junctions.js} +9 -24
  153. package/dist/preview/junctions.js.map +1 -0
  154. package/dist/preview/label-layout.d.ts +12 -0
  155. package/dist/preview/label-layout.d.ts.map +1 -0
  156. package/{src/preview/label-layout.ts → dist/preview/label-layout.js} +15 -36
  157. package/dist/preview/label-layout.js.map +1 -0
  158. package/dist/preview/ports.d.ts +17 -0
  159. package/dist/preview/ports.d.ts.map +1 -0
  160. package/{src/preview/ports.ts → dist/preview/ports.js} +10 -37
  161. package/dist/preview/ports.js.map +1 -0
  162. package/dist/preview/renderable-wires.d.ts +3 -0
  163. package/dist/preview/renderable-wires.d.ts.map +1 -0
  164. package/{src/preview/renderable-wires.ts → dist/preview/renderable-wires.js} +12 -29
  165. package/dist/preview/renderable-wires.js.map +1 -0
  166. package/dist/preview/routing.d.ts +4 -0
  167. package/dist/preview/routing.d.ts.map +1 -0
  168. package/dist/preview/routing.js +13 -0
  169. package/dist/preview/routing.js.map +1 -0
  170. package/dist/preview/snap.d.ts +9 -0
  171. package/dist/preview/snap.d.ts.map +1 -0
  172. package/{src/preview/snap.ts → dist/preview/snap.js} +9 -31
  173. package/dist/preview/snap.js.map +1 -0
  174. package/dist/preview/symbols/svg-content.d.ts +7 -0
  175. package/dist/preview/symbols/svg-content.d.ts.map +1 -0
  176. package/{src/preview/symbols/svg-content.ts → dist/preview/symbols/svg-content.js} +3 -6
  177. package/dist/preview/symbols/svg-content.js.map +1 -0
  178. package/dist/preview/symbols.d.ts +7 -0
  179. package/dist/preview/symbols.d.ts.map +1 -0
  180. package/{src/preview/symbols.ts → dist/preview/symbols.js} +18 -43
  181. package/dist/preview/symbols.js.map +1 -0
  182. package/dist/preview/wire-chains.d.ts +4 -0
  183. package/dist/preview/wire-chains.d.ts.map +1 -0
  184. package/{src/preview/wire-chains.ts → dist/preview/wire-chains.js} +37 -37
  185. package/dist/preview/wire-chains.js.map +1 -0
  186. package/package.json +3 -3
  187. package/src/formats/document.ts +0 -274
  188. package/src/index.ts +0 -205
  189. package/src/model/types.ts +0 -309
  190. package/src/panel/index.ts +0 -39
  191. package/src/preview/routing.ts +0 -15
  192. package/src/preview/symbols/analog-switch.svg +0 -17
  193. package/src/preview/symbols/battery.svg +0 -16
  194. package/src/preview/symbols/bbd.svg +0 -21
  195. package/src/preview/symbols/bjt-npn.svg +0 -16
  196. package/src/preview/symbols/bjt-pnp.svg +0 -17
  197. package/src/preview/symbols/capacitor-electrolytic.svg +0 -13
  198. package/src/preview/symbols/capacitor.svg +0 -12
  199. package/src/preview/symbols/current-source.svg +0 -14
  200. package/src/preview/symbols/delay-ic.svg +0 -22
  201. package/src/preview/symbols/diode-schottky.svg +0 -12
  202. package/src/preview/symbols/diode-zener.svg +0 -12
  203. package/src/preview/symbols/diode.svg +0 -13
  204. package/src/preview/symbols/flipflop.svg +0 -20
  205. package/src/preview/symbols/ground.svg +0 -12
  206. package/src/preview/symbols/ic-block.svg +0 -20
  207. package/src/preview/symbols/ic.svg +0 -19
  208. package/src/preview/symbols/inductor.svg +0 -11
  209. package/src/preview/symbols/jack-input.svg +0 -16
  210. package/src/preview/symbols/jack-output.svg +0 -16
  211. package/src/preview/symbols/jfet-junction-n.svg +0 -17
  212. package/src/preview/symbols/jfet-n.svg +0 -17
  213. package/src/preview/symbols/jfet-p.svg +0 -17
  214. package/src/preview/symbols/label.svg +0 -8
  215. package/src/preview/symbols/led.svg +0 -18
  216. package/src/preview/symbols/mosfet-n.svg +0 -21
  217. package/src/preview/symbols/mosfet-p.svg +0 -21
  218. package/src/preview/symbols/named-wire.svg +0 -11
  219. package/src/preview/symbols/opamp.svg +0 -21
  220. package/src/preview/symbols/optocoupler.svg +0 -30
  221. package/src/preview/symbols/ota.svg +0 -20
  222. package/src/preview/symbols/pentode.svg +0 -25
  223. package/src/preview/symbols/photoresistor.svg +0 -19
  224. package/src/preview/symbols/port.svg +0 -8
  225. package/src/preview/symbols/potentiometer.svg +0 -15
  226. package/src/preview/symbols/power-amp.svg +0 -20
  227. package/src/preview/symbols/rail.svg +0 -11
  228. package/src/preview/symbols/regulator.svg +0 -13
  229. package/src/preview/symbols/relay.svg +0 -20
  230. package/src/preview/symbols/resistor.svg +0 -11
  231. package/src/preview/symbols/switch-3pdt.svg +0 -32
  232. package/src/preview/symbols/switch-rotary.svg +0 -23
  233. package/src/preview/symbols/switch-spdt.svg +0 -16
  234. package/src/preview/symbols/switch-spst.svg +0 -14
  235. package/src/preview/symbols/switch-toggle.svg +0 -14
  236. package/src/preview/symbols/transformer.svg +0 -17
  237. package/src/preview/symbols/triode.svg +0 -17
  238. package/src/preview/symbols/tube-diode.svg +0 -13
  239. package/src/preview/symbols/unsupported.svg +0 -8
  240. package/src/preview/symbols/variable-resistor.svg +0 -13
  241. package/src/preview/symbols/voltage-source.svg +0 -15
@@ -1,84 +1,53 @@
1
- import { isParsedQuantity } from '../../model/properties';
2
- import type {
3
- CircuitDocument,
4
- CircuitDocumentDevice,
5
- CircuitDocumentDeviceKind,
6
- Component,
7
- ComponentKind,
8
- ControlApplicabilityPredicate,
9
- ControlContext,
10
- ControlGroup,
11
- DeviceInterface,
12
- DeviceInterfaceBinding,
13
- DeviceInterfaceControlKind,
14
- ControlInterface,
15
- ControlInterfaceAssignmentHint,
16
- ControlInterfaceConnector,
17
- ControlInterfacePolarity,
18
- ControlInterfaceRole,
19
- ControlOutput,
20
- ControlOutputSwitchMode,
21
- DocumentSource,
22
- PanelColumnOrder,
23
- PanelControlKind,
24
- PanelElementBinding,
25
- PanelGridLayout,
26
- PanelGridIndexing,
27
- PanelGridPosition,
28
- PanelPlacementMetadata,
29
- PanelRowOrder,
30
- ParsedQuantity,
31
- Point,
32
- PropertyValue,
33
- Rotation,
34
- Terminal,
35
- Warning,
36
- Wire,
37
- } from '../../model/types';
38
-
39
- type YamlScalar = string | number | boolean | null;
40
- type YamlValue = YamlScalar | readonly YamlValue[] | YamlObject;
41
- type YamlObject = { [key: string]: YamlValue };
42
-
43
- type YamlLine = Readonly<{
44
- indent: number;
45
- text: string;
46
- lineNumber: number;
47
- }>;
48
-
49
- type Cursor = {
50
- index: number;
51
- };
52
-
53
- type ParsedPair = Readonly<{
54
- key: string;
55
- rest: string;
56
- }>;
57
-
58
- const INTERCHANGE_SCHEMA = 'circuit-interchange/v2';
59
-
60
- export function parseInterchangeYaml(source: string): CircuitDocument {
1
+ import { isParsedQuantity } from '../../model/properties.js';
2
+ const INTERCHANGE_SCHEMA_V2 = 'circuit-interchange/v2';
3
+ const INTERCHANGE_SCHEMA_V3 = 'circuit-interchange/v3';
4
+ const V3_ONLY_TOP_LEVEL_FIELDS = [
5
+ 'mechanical',
6
+ 'build',
7
+ 'bom',
8
+ 'partProfiles',
9
+ 'footprints',
10
+ 'offBoardWiring',
11
+ 'boards',
12
+ ];
13
+ export function parseInterchangeYaml(source) {
61
14
  const value = parseYamlSubset(source);
62
15
  const root = expectObject(value, 'root');
63
16
  const schema = expectString(root.schema, 'schema');
64
- if (schema !== INTERCHANGE_SCHEMA) {
17
+ if (schema !== INTERCHANGE_SCHEMA_V2 && schema !== INTERCHANGE_SCHEMA_V3) {
65
18
  throw new Error(`unsupported interchange schema: ${schema}`);
66
19
  }
67
-
68
- const panel = parsePanel(root.panel);
20
+ const isV3 = schema === INTERCHANGE_SCHEMA_V3;
21
+ if (!isV3) {
22
+ rejectV3OnlyTopLevelFields(root);
23
+ }
24
+ const panel = parsePanel(root.panel, isV3);
69
25
  const controlInterfaces = parseControlInterfaces(root.controlInterfaces);
70
26
  const device = parseDevice(root.device);
71
27
  const controlOutputs = parseControlOutputs(root.controlOutputs);
72
28
  const controlGroups = parseControlGroups(root.controlGroups);
73
29
  const controlContexts = parseControlContexts(root.controlContexts);
74
30
  const deviceInterface = parseDeviceInterface(root.deviceInterface);
75
-
31
+ const mechanical = isV3 ? parseMechanical(root.mechanical) : undefined;
32
+ const build = isV3 ? parseBuild(root.build) : undefined;
33
+ const bom = isV3 ? parseBom(root.bom) : undefined;
34
+ const partProfiles = isV3 ? parsePartProfiles(root.partProfiles) : undefined;
35
+ const footprints = isV3 ? parseFootprints(root.footprints) : undefined;
36
+ const offBoardWiring = isV3 ? parseOffBoardWiring(root.offBoardWiring) : undefined;
37
+ const boards = isV3 ? parseBoards(root.boards) : undefined;
76
38
  return {
77
39
  metadata: parseMetadata(root.metadata),
78
40
  source: parseSource(root.source),
79
41
  ...(device === undefined ? {} : { device }),
80
42
  ...(controlGroups === undefined ? {} : { controlGroups }),
81
43
  ...(controlContexts === undefined ? {} : { controlContexts }),
44
+ ...(mechanical === undefined ? {} : { mechanical }),
45
+ ...(build === undefined ? {} : { build }),
46
+ ...(bom === undefined ? {} : { bom }),
47
+ ...(partProfiles === undefined ? {} : { partProfiles }),
48
+ ...(footprints === undefined ? {} : { footprints }),
49
+ ...(offBoardWiring === undefined ? {} : { offBoardWiring }),
50
+ ...(boards === undefined ? {} : { boards }),
82
51
  ...(deviceInterface === undefined ? {} : { deviceInterface }),
83
52
  ...(panel === undefined ? {} : { panel }),
84
53
  ...(controlInterfaces === undefined ? {} : { controlInterfaces }),
@@ -90,8 +59,463 @@ export function parseInterchangeYaml(source: string): CircuitDocument {
90
59
  rawAttributes: parseStringRecord(root.rawAttributes, 'rawAttributes'),
91
60
  };
92
61
  }
93
-
94
- function parseControlGroups(value: YamlValue | undefined): readonly ControlGroup[] | undefined {
62
+ function rejectV3OnlyTopLevelFields(root) {
63
+ for (const field of V3_ONLY_TOP_LEVEL_FIELDS) {
64
+ if (root[field] !== undefined) {
65
+ throw new Error(`${field}: requires schema ${INTERCHANGE_SCHEMA_V3}`);
66
+ }
67
+ }
68
+ }
69
+ function parseMechanical(value) {
70
+ if (value === undefined) {
71
+ return undefined;
72
+ }
73
+ const mechanical = expectObject(value, 'mechanical');
74
+ return {
75
+ ...parseBuildDataObject(mechanical, 'mechanical'),
76
+ ...(mechanical.schema === undefined ? {} : { schema: expectString(mechanical.schema, 'mechanical.schema') }),
77
+ ...(mechanical.units === undefined ? {} : { units: expectString(mechanical.units, 'mechanical.units') }),
78
+ };
79
+ }
80
+ function parseBuild(value) {
81
+ if (value === undefined) {
82
+ return undefined;
83
+ }
84
+ const build = expectObject(value, 'build');
85
+ return {
86
+ ...parseBuildDataObject(build, 'build'),
87
+ schema: parseLiteralString(build.schema, 'build.schema', 'build-scope/v1'),
88
+ ...(build.intent === undefined ? {} : { intent: parseBuildIntent(build.intent, 'build.intent') }),
89
+ ...(build.completeness === undefined
90
+ ? {}
91
+ : { completeness: parseBuildCompleteness(build.completeness, 'build.completeness') }),
92
+ ...(build.selectedBoardId === undefined
93
+ ? {}
94
+ : { selectedBoardId: expectString(build.selectedBoardId, 'build.selectedBoardId') }),
95
+ ...(build.selectedOffBoardWiringHarnessIds === undefined
96
+ ? {}
97
+ : {
98
+ selectedOffBoardWiringHarnessIds: parseOptionalStringArray(build.selectedOffBoardWiringHarnessIds, 'build.selectedOffBoardWiringHarnessIds') ?? [],
99
+ }),
100
+ ...(build.alternateBoardIds === undefined
101
+ ? {}
102
+ : { alternateBoardIds: parseOptionalStringArray(build.alternateBoardIds, 'build.alternateBoardIds') ?? [] }),
103
+ ...(build.bomScope === undefined ? {} : { bomScope: expectString(build.bomScope, 'build.bomScope') }),
104
+ };
105
+ }
106
+ function parseBuildIntent(value, path) {
107
+ const intent = expectString(value, path);
108
+ if (intent === 'diy-build-artifact' || intent === 'schema-review-sample') {
109
+ return intent;
110
+ }
111
+ throw new Error(`${path}: expected diy-build-artifact or schema-review-sample`);
112
+ }
113
+ function parseBuildCompleteness(value, path) {
114
+ const completeness = expectString(value, path);
115
+ if (completeness === 'complete-selected-build' || completeness === 'partial-offboard-wiring') {
116
+ return completeness;
117
+ }
118
+ throw new Error(`${path}: expected complete-selected-build or partial-offboard-wiring`);
119
+ }
120
+ function parseBom(value) {
121
+ if (value === undefined) {
122
+ return undefined;
123
+ }
124
+ const bom = expectObject(value, 'bom');
125
+ return {
126
+ ...parseBuildDataObject(bom, 'bom'),
127
+ schema: parseLiteralString(bom.schema, 'bom.schema', 'build-bom/v1'),
128
+ items: optionalArray(bom.items, 'bom.items').map(parseBomItem),
129
+ };
130
+ }
131
+ function parseBomItem(value, index) {
132
+ const path = `bom.items[${index}]`;
133
+ const item = expectObject(value, path);
134
+ return {
135
+ ...parseBuildDataObject(item, path),
136
+ id: expectString(item.id, `${path}.id`),
137
+ refs: optionalArray(item.refs, `${path}.refs`).map((ref, refIndex) => parseBomRef(ref, `${path}.refs[${refIndex}]`)),
138
+ quantity: expectNumber(item.quantity, `${path}.quantity`),
139
+ ...(item.value === undefined ? {} : { value: expectString(item.value, `${path}.value`) }),
140
+ ...(item.partProfileId === undefined
141
+ ? {}
142
+ : { partProfileId: expectString(item.partProfileId, `${path}.partProfileId`) }),
143
+ ...(item.category === undefined ? {} : { category: expectString(item.category, `${path}.category`) }),
144
+ ...(item.sku === undefined ? {} : { sku: expectString(item.sku, `${path}.sku`) }),
145
+ };
146
+ }
147
+ function parseBomRef(value, path) {
148
+ const ref = expectObject(value, path);
149
+ const kind = expectString(ref.kind, `${path}.kind`);
150
+ switch (kind) {
151
+ case 'component':
152
+ case 'device-interface-control':
153
+ case 'panel-element':
154
+ case 'board':
155
+ case 'freeform-build-item':
156
+ return {
157
+ ...parseBuildDataObject(ref, path),
158
+ kind,
159
+ ...(ref.componentId === undefined ? {} : { componentId: expectString(ref.componentId, `${path}.componentId`) }),
160
+ ...(ref.controlId === undefined ? {} : { controlId: expectString(ref.controlId, `${path}.controlId`) }),
161
+ ...(ref.panelElementId === undefined
162
+ ? {}
163
+ : { panelElementId: expectString(ref.panelElementId, `${path}.panelElementId`) }),
164
+ ...(ref.boardId === undefined ? {} : { boardId: expectString(ref.boardId, `${path}.boardId`) }),
165
+ ...(ref.label === undefined ? {} : { label: expectString(ref.label, `${path}.label`) }),
166
+ };
167
+ default:
168
+ throw new Error(`${path}.kind: expected component, device-interface-control, panel-element, board, or freeform-build-item`);
169
+ }
170
+ }
171
+ function parsePartProfiles(value) {
172
+ if (value === undefined) {
173
+ return undefined;
174
+ }
175
+ const catalog = expectObject(value, 'partProfiles');
176
+ return {
177
+ ...parseBuildDataObject(catalog, 'partProfiles'),
178
+ schema: parseLiteralString(catalog.schema, 'partProfiles.schema', 'part-profile-catalog/v1'),
179
+ ...(catalog.resolution === undefined
180
+ ? {}
181
+ : { resolution: expectString(catalog.resolution, 'partProfiles.resolution') }),
182
+ ...(catalog.units === undefined ? {} : { units: expectString(catalog.units, 'partProfiles.units') }),
183
+ profiles: optionalArray(catalog.profiles, 'partProfiles.profiles').map((profile, index) => parsePartProfile(profile, index)),
184
+ };
185
+ }
186
+ function parsePartProfile(value, index) {
187
+ const path = `partProfiles.profiles[${index}]`;
188
+ const profile = expectObject(value, path);
189
+ return {
190
+ ...parseBuildDataObject(profile, path),
191
+ id: expectString(profile.id, `${path}.id`),
192
+ ...(profile.kind === undefined ? {} : { kind: expectString(profile.kind, `${path}.kind`) }),
193
+ };
194
+ }
195
+ function parseFootprints(value) {
196
+ if (value === undefined) {
197
+ return undefined;
198
+ }
199
+ const catalog = expectObject(value, 'footprints');
200
+ return {
201
+ ...parseBuildDataObject(catalog, 'footprints'),
202
+ schema: parseLiteralString(catalog.schema, 'footprints.schema', 'board-footprint-catalog/v1'),
203
+ ...(catalog.resolution === undefined ? {} : { resolution: expectString(catalog.resolution, 'footprints.resolution') }),
204
+ ...(catalog.units === undefined ? {} : { units: expectString(catalog.units, 'footprints.units') }),
205
+ footprints: optionalArray(catalog.footprints, 'footprints.footprints').map((footprint, index) => parseFootprint(footprint, index)),
206
+ };
207
+ }
208
+ function parseFootprint(value, index) {
209
+ const path = `footprints.footprints[${index}]`;
210
+ const footprint = expectObject(value, path);
211
+ return {
212
+ ...parseBuildDataObject(footprint, path),
213
+ id: expectString(footprint.id, `${path}.id`),
214
+ ...(footprint.boardApplicability === undefined
215
+ ? {}
216
+ : { boardApplicability: parseBoardApplicability(footprint.boardApplicability, `${path}.boardApplicability`) }),
217
+ };
218
+ }
219
+ function parseBoardApplicability(value, path) {
220
+ const applicability = expectObject(value, path);
221
+ return {
222
+ ...parseBuildDataObject(applicability, path),
223
+ family: parseBoardFamily(applicability.family, `${path}.family`),
224
+ kind: parseBoardKind(applicability.kind, `${path}.kind`),
225
+ ...(applicability.subtype === undefined
226
+ ? {}
227
+ : { subtype: parseBoardSubtype(applicability.subtype, `${path}.subtype`) }),
228
+ };
229
+ }
230
+ function parseOffBoardWiring(value) {
231
+ if (value === undefined) {
232
+ return undefined;
233
+ }
234
+ const plan = expectObject(value, 'offBoardWiring');
235
+ return {
236
+ ...parseBuildDataObject(plan, 'offBoardWiring'),
237
+ schema: parseLiteralString(plan.schema, 'offBoardWiring.schema', 'offboard-wiring/v1'),
238
+ ...(plan.source === undefined ? {} : { source: expectString(plan.source, 'offBoardWiring.source') }),
239
+ ...(plan.coverage === undefined
240
+ ? {}
241
+ : { coverage: parseOffBoardWiringCoverage(plan.coverage, 'offBoardWiring.coverage') }),
242
+ harnesses: optionalArray(plan.harnesses, 'offBoardWiring.harnesses').map(parseOffBoardWiringHarness),
243
+ };
244
+ }
245
+ function parseOffBoardWiringCoverage(value, path) {
246
+ const coverage = expectString(value, path);
247
+ if (coverage === 'selected-build-complete' || coverage === 'representative-selected-build-endpoints') {
248
+ return coverage;
249
+ }
250
+ throw new Error(`${path}: expected selected-build-complete or representative-selected-build-endpoints`);
251
+ }
252
+ function parseOffBoardWiringHarness(value, index) {
253
+ const path = `offBoardWiring.harnesses[${index}]`;
254
+ const harness = expectObject(value, path);
255
+ return {
256
+ ...parseBuildDataObject(harness, path),
257
+ id: expectString(harness.id, `${path}.id`),
258
+ ...(harness.status === undefined
259
+ ? {}
260
+ : { status: parseOffBoardWiringHarnessStatus(harness.status, `${path}.status`) }),
261
+ ...(harness.notes === undefined ? {} : { notes: expectString(harness.notes, `${path}.notes`) }),
262
+ endpoints: optionalArray(harness.endpoints, `${path}.endpoints`).map((endpoint, endpointIndex) => parseOffBoardWiringEndpoint(endpoint, `${path}.endpoints[${endpointIndex}]`)),
263
+ connections: optionalArray(harness.connections, `${path}.connections`).map((connection, connectionIndex) => parseOffBoardWiringConnection(connection, `${path}.connections[${connectionIndex}]`)),
264
+ };
265
+ }
266
+ function parseOffBoardWiringHarnessStatus(value, path) {
267
+ const status = expectString(value, path);
268
+ if (status === 'complete' || status === 'partial' || status === 'candidate') {
269
+ return status;
270
+ }
271
+ throw new Error(`${path}: expected complete, partial, or candidate`);
272
+ }
273
+ function parseOffBoardWiringEndpoint(value, path) {
274
+ const endpoint = expectObject(value, path);
275
+ const kind = expectString(endpoint.kind, `${path}.kind`);
276
+ switch (kind) {
277
+ case 'panel-component-terminal':
278
+ case 'board-terminal':
279
+ case 'power-terminal':
280
+ case 'footswitch-terminal':
281
+ case 'free-wire-label':
282
+ return {
283
+ ...parseBuildDataObject(endpoint, path),
284
+ id: expectString(endpoint.id, `${path}.id`),
285
+ kind,
286
+ ...(endpoint.componentId === undefined
287
+ ? {}
288
+ : { componentId: expectString(endpoint.componentId, `${path}.componentId`) }),
289
+ ...(endpoint.terminalName === undefined
290
+ ? {}
291
+ : { terminalName: expectString(endpoint.terminalName, `${path}.terminalName`) }),
292
+ ...(endpoint.panelElementId === undefined
293
+ ? {}
294
+ : { panelElementId: expectString(endpoint.panelElementId, `${path}.panelElementId`) }),
295
+ ...(endpoint.boardId === undefined ? {} : { boardId: expectString(endpoint.boardId, `${path}.boardId`) }),
296
+ ...(endpoint.terminalId === undefined
297
+ ? {}
298
+ : { terminalId: expectString(endpoint.terminalId, `${path}.terminalId`) }),
299
+ ...(endpoint.label === undefined ? {} : { label: expectString(endpoint.label, `${path}.label`) }),
300
+ };
301
+ default:
302
+ throw new Error(`${path}.kind: expected a supported off-board wiring endpoint kind`);
303
+ }
304
+ }
305
+ function parseOffBoardWiringConnection(value, path) {
306
+ const connection = expectObject(value, path);
307
+ return {
308
+ ...parseBuildDataObject(connection, path),
309
+ id: expectString(connection.id, `${path}.id`),
310
+ fromEndpointId: expectString(connection.fromEndpointId, `${path}.fromEndpointId`),
311
+ toEndpointId: expectString(connection.toEndpointId, `${path}.toEndpointId`),
312
+ ...(connection.signalRef === undefined
313
+ ? {}
314
+ : { signalRef: parseBuildDataObject(connection.signalRef, `${path}.signalRef`) }),
315
+ ...(connection.wire === undefined ? {} : { wire: parseBuildDataObject(connection.wire, `${path}.wire`) }),
316
+ };
317
+ }
318
+ function parseBoards(value) {
319
+ if (value === undefined) {
320
+ return undefined;
321
+ }
322
+ return optionalArray(value, 'boards').map(parseBoard);
323
+ }
324
+ function parseBoard(value, index) {
325
+ const path = `boards[${index}]`;
326
+ const board = expectObject(value, path);
327
+ const sourceCircuit = board.sourceCircuit === undefined
328
+ ? undefined
329
+ : parseBoardSourceCircuit(board.sourceCircuit, `${path}.sourceCircuit`);
330
+ return {
331
+ ...parseBuildDataObject(board, path),
332
+ id: expectString(board.id, `${path}.id`),
333
+ schema: parseLiteralString(board.schema, `${path}.schema`, 'circuit-board/v1'),
334
+ family: parseBoardFamily(board.family, `${path}.family`),
335
+ kind: parseBoardKind(board.kind, `${path}.kind`),
336
+ ...(board.subtype === undefined ? {} : { subtype: parseBoardSubtype(board.subtype, `${path}.subtype`) }),
337
+ ...(board.source === undefined ? {} : { source: expectString(board.source, `${path}.source`) }),
338
+ ...(board.units === undefined ? {} : { units: expectString(board.units, `${path}.units`) }),
339
+ ...(board.locked === undefined ? {} : { locked: expectBoolean(board.locked, `${path}.locked`) }),
340
+ ...(sourceCircuit === undefined ? {} : { sourceCircuit }),
341
+ edgeTerminals: optionalArray(board.edgeTerminals, `${path}.edgeTerminals`).map((terminal, terminalIndex) => parseBoardEdgeTerminal(terminal, `${path}.edgeTerminals[${terminalIndex}]`)),
342
+ footprintPlacements: optionalArray(board.footprintPlacements, `${path}.footprintPlacements`).map((placement, placementIndex) => parseBoardFootprintPlacement(placement, `${path}.footprintPlacements[${placementIndex}]`)),
343
+ ...(board.netlist === undefined ? {} : { netlist: parseBoardNetlist(board.netlist, `${path}.netlist`) }),
344
+ routes: optionalArray(board.routes, `${path}.routes`).map((route, routeIndex) => parseBoardRoute(route, `${path}.routes[${routeIndex}]`)),
345
+ ...(board.zones === undefined ? {} : { zones: parseBuildDataObjectArray(board.zones, `${path}.zones`) }),
346
+ ...(board.drills === undefined ? {} : { drills: parseBuildDataObjectArray(board.drills, `${path}.drills`) }),
347
+ ...(board.review === undefined ? {} : { review: parseBuildDataObject(board.review, `${path}.review`) }),
348
+ };
349
+ }
350
+ function parseBoardSourceCircuit(value, path) {
351
+ const sourceCircuit = expectObject(value, path);
352
+ const hash = expectString(sourceCircuit.hash, `${path}.hash`);
353
+ if (!/^sha256:[0-9a-f]{64}$/.test(hash)) {
354
+ throw new Error(`${path}.hash: expected sha256:<64 lowercase hex characters>`);
355
+ }
356
+ return {
357
+ ...parseBuildDataObject(sourceCircuit, path),
358
+ schema: parseLiteralString(sourceCircuit.schema, `${path}.schema`, 'canonical-circuit-facts-hash/v1'),
359
+ hashAlgorithm: parseLiteralString(sourceCircuit.hashAlgorithm, `${path}.hashAlgorithm`, 'sha256'),
360
+ hash,
361
+ };
362
+ }
363
+ function parseBoardEdgeTerminal(value, path) {
364
+ const terminal = expectObject(value, path);
365
+ return {
366
+ ...parseBuildDataObject(terminal, path),
367
+ id: expectString(terminal.id, `${path}.id`),
368
+ ...(terminal.role === undefined ? {} : { role: expectString(terminal.role, `${path}.role`) }),
369
+ ...(terminal.terminalRef === undefined
370
+ ? {}
371
+ : { terminalRef: parseComponentTerminalRef(terminal.terminalRef, `${path}.terminalRef`) }),
372
+ ...(terminal.hole === undefined ? {} : { hole: parseBoardHole(terminal.hole, `${path}.hole`) }),
373
+ };
374
+ }
375
+ function parseBoardFootprintPlacement(value, path) {
376
+ const placement = expectObject(value, path);
377
+ return {
378
+ ...parseBuildDataObject(placement, path),
379
+ componentId: expectString(placement.componentId, `${path}.componentId`),
380
+ footprintId: expectString(placement.footprintId, `${path}.footprintId`),
381
+ ...(placement.atGrid === undefined ? {} : { atGrid: parseBoardHole(placement.atGrid, `${path}.atGrid`) }),
382
+ ...(placement.atMm === undefined ? {} : { atMm: parsePoint(placement.atMm, `${path}.atMm`) }),
383
+ ...(placement.rotationDeg === undefined ? {} : { rotationDeg: expectNumber(placement.rotationDeg, `${path}.rotationDeg`) }),
384
+ pads: optionalArray(placement.pads, `${path}.pads`).map((pad, padIndex) => parseBoardPlacedPad(pad, `${path}.pads[${padIndex}]`)),
385
+ };
386
+ }
387
+ function parseBoardPlacedPad(value, path) {
388
+ const pad = expectObject(value, path);
389
+ return {
390
+ ...parseBuildDataObject(pad, path),
391
+ padId: expectString(pad.padId, `${path}.padId`),
392
+ ...(pad.terminalName === undefined ? {} : { terminalName: expectString(pad.terminalName, `${path}.terminalName`) }),
393
+ ...(pad.hole === undefined ? {} : { hole: parseBoardHole(pad.hole, `${path}.hole`) }),
394
+ ...(pad.positionMm === undefined ? {} : { positionMm: parsePoint(pad.positionMm, `${path}.positionMm`) }),
395
+ };
396
+ }
397
+ function parseBoardNetlist(value, path) {
398
+ const netlist = expectObject(value, path);
399
+ return {
400
+ ...parseBuildDataObject(netlist, path),
401
+ ...(netlist.source === undefined ? {} : { source: expectString(netlist.source, `${path}.source`) }),
402
+ nets: optionalArray(netlist.nets, `${path}.nets`).map((net, netIndex) => parseBoardNet(net, `${path}.nets[${netIndex}]`)),
403
+ };
404
+ }
405
+ function parseBoardNet(value, path) {
406
+ const net = expectObject(value, path);
407
+ return {
408
+ ...parseBuildDataObject(net, path),
409
+ id: expectString(net.id, `${path}.id`),
410
+ ...(net.name === undefined ? {} : { name: expectString(net.name, `${path}.name`) }),
411
+ members: optionalArray(net.members, `${path}.members`).map((member, memberIndex) => parseBoardNetMember(member, `${path}.members[${memberIndex}]`)),
412
+ };
413
+ }
414
+ function parseBoardNetMember(value, path) {
415
+ const member = expectObject(value, path);
416
+ return {
417
+ ...parseBuildDataObject(member, path),
418
+ componentId: expectString(member.componentId, `${path}.componentId`),
419
+ terminalName: expectString(member.terminalName, `${path}.terminalName`),
420
+ ...(member.padId === undefined ? {} : { padId: expectString(member.padId, `${path}.padId`) }),
421
+ ...(member.terminalId === undefined ? {} : { terminalId: expectString(member.terminalId, `${path}.terminalId`) }),
422
+ };
423
+ }
424
+ function parseBoardRoute(value, path) {
425
+ const route = expectObject(value, path);
426
+ return {
427
+ ...parseBuildDataObject(route, path),
428
+ id: expectString(route.id, `${path}.id`),
429
+ ...(route.netRef === undefined ? {} : { netRef: parseBuildDataObject(route.netRef, `${path}.netRef`) }),
430
+ ...(route.locked === undefined ? {} : { locked: expectBoolean(route.locked, `${path}.locked`) }),
431
+ ...(route.conductors === undefined
432
+ ? {}
433
+ : { conductors: parseBuildDataObjectArray(route.conductors, `${path}.conductors`) }),
434
+ ...(route.copper === undefined ? {} : { copper: parseBuildDataObjectArray(route.copper, `${path}.copper`) }),
435
+ ...(route.vias === undefined ? {} : { vias: parseBuildDataObjectArray(route.vias, `${path}.vias`) }),
436
+ ...(route.zones === undefined ? {} : { zones: parseBuildDataObjectArray(route.zones, `${path}.zones`) }),
437
+ ...(route.drills === undefined ? {} : { drills: parseBuildDataObjectArray(route.drills, `${path}.drills`) }),
438
+ };
439
+ }
440
+ function parseComponentTerminalRef(value, path) {
441
+ const ref = expectObject(value, path);
442
+ return {
443
+ ...parseBuildDataObject(ref, path),
444
+ componentId: expectString(ref.componentId, `${path}.componentId`),
445
+ terminalName: expectString(ref.terminalName, `${path}.terminalName`),
446
+ };
447
+ }
448
+ function parseBoardHole(value, path) {
449
+ const hole = expectObject(value, path);
450
+ return {
451
+ ...parseBuildDataObject(hole, path),
452
+ row: expectPositiveInteger(hole.row, `${path}.row`),
453
+ column: expectPositiveInteger(hole.column, `${path}.column`),
454
+ };
455
+ }
456
+ function parseBoardFamily(value, path) {
457
+ const family = expectString(value, path);
458
+ if (family === 'prototype-board' || family === 'fabricated-board') {
459
+ return family;
460
+ }
461
+ throw new Error(`${path}: expected prototype-board or fabricated-board`);
462
+ }
463
+ function parseBoardKind(value, path) {
464
+ const kind = expectString(value, path);
465
+ switch (kind) {
466
+ case 'stripboard':
467
+ case 'perfboard':
468
+ case 'breadboard-pattern':
469
+ case 'pcb':
470
+ return kind;
471
+ default:
472
+ throw new Error(`${path}: expected stripboard, perfboard, breadboard-pattern, or pcb`);
473
+ }
474
+ }
475
+ function parseBoardSubtype(value, path) {
476
+ const subtype = expectString(value, path);
477
+ switch (subtype) {
478
+ case 'veroboard':
479
+ case 'isolated-pad':
480
+ case 'solderable-half-breadboard':
481
+ case 'single-sided-through-hole':
482
+ case 'two-layer-through-hole':
483
+ return subtype;
484
+ default:
485
+ throw new Error(`${path}: expected a supported board subtype`);
486
+ }
487
+ }
488
+ function parseLiteralString(value, path, expected) {
489
+ const actual = expectString(value, path);
490
+ if (actual === expected) {
491
+ return expected;
492
+ }
493
+ throw new Error(`${path}: expected ${expected}`);
494
+ }
495
+ function parseBuildDataObjectArray(value, path) {
496
+ return optionalArray(value, path).map((item, index) => parseBuildDataObject(item, `${path}[${index}]`));
497
+ }
498
+ function parseBuildDataObject(value, path) {
499
+ const object = expectObject(value, path);
500
+ const out = {};
501
+ for (const [key, child] of Object.entries(object)) {
502
+ out[key] = parseBuildDataValue(child, `${path}.${key}`);
503
+ }
504
+ return out;
505
+ }
506
+ function parseBuildDataValue(value, path) {
507
+ if (isScalar(value)) {
508
+ return value;
509
+ }
510
+ if (Array.isArray(value)) {
511
+ return value.map((item, index) => parseBuildDataValue(item, `${path}[${index}]`));
512
+ }
513
+ if (isYamlObject(value)) {
514
+ return parseBuildDataObject(value, path);
515
+ }
516
+ throw new Error(`${path}: expected v3 build data value`);
517
+ }
518
+ function parseControlGroups(value) {
95
519
  if (value === undefined) {
96
520
  return undefined;
97
521
  }
@@ -109,8 +533,7 @@ function parseControlGroups(value: YamlValue | undefined): readonly ControlGroup
109
533
  };
110
534
  });
111
535
  }
112
-
113
- function parseControlContexts(value: YamlValue | undefined): readonly ControlContext[] | undefined {
536
+ function parseControlContexts(value) {
114
537
  if (value === undefined) {
115
538
  return undefined;
116
539
  }
@@ -126,8 +549,7 @@ function parseControlContexts(value: YamlValue | undefined): readonly ControlCon
126
549
  };
127
550
  });
128
551
  }
129
-
130
- function parseDeviceInterface(value: YamlValue | undefined): DeviceInterface | undefined {
552
+ function parseDeviceInterface(value) {
131
553
  if (value === undefined) {
132
554
  return undefined;
133
555
  }
@@ -155,11 +577,7 @@ function parseDeviceInterface(value: YamlValue | undefined): DeviceInterface | u
155
577
  }),
156
578
  };
157
579
  }
158
-
159
- function parseDeviceInterfaceControlKind(
160
- value: YamlValue | undefined,
161
- path: string,
162
- ): DeviceInterfaceControlKind {
580
+ function parseDeviceInterfaceControlKind(value, path) {
163
581
  const kind = expectString(value, path);
164
582
  switch (kind) {
165
583
  case 'knob':
@@ -174,11 +592,7 @@ function parseDeviceInterfaceControlKind(
174
592
  throw new Error(`${path}: expected knob, slider, switch, selector, footswitch, led, or jack`);
175
593
  }
176
594
  }
177
-
178
- function parseOptionalDeviceInterfaceBinding(
179
- value: YamlValue | undefined,
180
- path: string,
181
- ): DeviceInterfaceBinding | undefined {
595
+ function parseOptionalDeviceInterfaceBinding(value, path) {
182
596
  if (value === undefined) {
183
597
  return undefined;
184
598
  }
@@ -195,11 +609,7 @@ function parseOptionalDeviceInterfaceBinding(
195
609
  ...(externalInterfaceId === undefined ? {} : { externalInterfaceId }),
196
610
  };
197
611
  }
198
-
199
- function parseOptionalApplicabilityPredicate(
200
- value: YamlValue | undefined,
201
- path: string,
202
- ): ControlApplicabilityPredicate | undefined {
612
+ function parseOptionalApplicabilityPredicate(value, path) {
203
613
  if (value === undefined) {
204
614
  return undefined;
205
615
  }
@@ -211,8 +621,7 @@ function parseOptionalApplicabilityPredicate(
211
621
  ...(anyOf === undefined ? {} : { anyOf }),
212
622
  };
213
623
  }
214
-
215
- function parseDevice(value: YamlValue | undefined): CircuitDocumentDevice | undefined {
624
+ function parseDevice(value) {
216
625
  if (value === undefined) {
217
626
  return undefined;
218
627
  }
@@ -231,8 +640,7 @@ function parseDevice(value: YamlValue | undefined): CircuitDocumentDevice | unde
231
640
  ...(audioProcessing === undefined ? {} : { audioProcessing }),
232
641
  };
233
642
  }
234
-
235
- function parseCircuitDocumentDeviceKind(value: YamlValue | undefined, path: string): CircuitDocumentDeviceKind {
643
+ function parseCircuitDocumentDeviceKind(value, path) {
236
644
  const kind = expectString(value, path);
237
645
  switch (kind) {
238
646
  case 'audio-pedal':
@@ -244,8 +652,7 @@ function parseCircuitDocumentDeviceKind(value: YamlValue | undefined, path: stri
244
652
  throw new Error(`${path}: expected audio-pedal, control-accessory, utility, or unknown`);
245
653
  }
246
654
  }
247
-
248
- function parseControlOutputs(value: YamlValue | undefined): readonly ControlOutput[] | undefined {
655
+ function parseControlOutputs(value) {
249
656
  if (value === undefined) {
250
657
  return undefined;
251
658
  }
@@ -273,11 +680,7 @@ function parseControlOutputs(value: YamlValue | undefined): readonly ControlOutp
273
680
  };
274
681
  });
275
682
  }
276
-
277
- function parseOptionalControlOutputSwitchMode(
278
- value: YamlValue | undefined,
279
- path: string,
280
- ): ControlOutputSwitchMode | undefined {
683
+ function parseOptionalControlOutputSwitchMode(value, path) {
281
684
  if (value === undefined) {
282
685
  return undefined;
283
686
  }
@@ -290,8 +693,7 @@ function parseOptionalControlOutputSwitchMode(
290
693
  throw new Error(`${path}: expected momentary or latching`);
291
694
  }
292
695
  }
293
-
294
- function parseControlInterfaces(value: YamlValue | undefined): readonly ControlInterface[] | undefined {
696
+ function parseControlInterfaces(value) {
295
697
  if (value === undefined) {
296
698
  return undefined;
297
699
  }
@@ -302,10 +704,7 @@ function parseControlInterfaces(value: YamlValue | undefined): readonly ControlI
302
704
  const controlRole = parseOptionalString(controlInterface.controlRole, `${path}.controlRole`);
303
705
  const interfaceName = parseOptionalString(controlInterface.interface, `${path}.interface`);
304
706
  const connector = parseOptionalControlInterfaceConnector(controlInterface.connector, `${path}.connector`);
305
- const assignmentHint = parseOptionalControlInterfaceAssignmentHint(
306
- controlInterface.assignmentHint,
307
- `${path}.assignmentHint`,
308
- );
707
+ const assignmentHint = parseOptionalControlInterfaceAssignmentHint(controlInterface.assignmentHint, `${path}.assignmentHint`);
309
708
  const polarity = parseOptionalControlInterfacePolarity(controlInterface.polarity, `${path}.polarity`);
310
709
  const binding = parseOptionalControlInterfaceBinding(controlInterface.binding, `${path}.binding`);
311
710
  const description = parseOptionalString(controlInterface.description, `${path}.description`);
@@ -324,11 +723,7 @@ function parseControlInterfaces(value: YamlValue | undefined): readonly ControlI
324
723
  };
325
724
  });
326
725
  }
327
-
328
- function parseOptionalControlInterfaceBinding(
329
- value: YamlValue | undefined,
330
- path: string,
331
- ): ControlInterface['binding'] | undefined {
726
+ function parseOptionalControlInterfaceBinding(value, path) {
332
727
  if (value === undefined) {
333
728
  return undefined;
334
729
  }
@@ -344,8 +739,7 @@ function parseOptionalControlInterfaceBinding(
344
739
  ...(property === undefined ? {} : { property }),
345
740
  };
346
741
  }
347
-
348
- function parseControlInterfaceRole(value: YamlValue | undefined, path: string): ControlInterfaceRole {
742
+ function parseControlInterfaceRole(value, path) {
349
743
  const role = expectString(value, path);
350
744
  switch (role) {
351
745
  case 'external-control':
@@ -360,11 +754,7 @@ function parseControlInterfaceRole(value: YamlValue | undefined, path: string):
360
754
  throw new Error(`${path}: expected external-control, tempo-tap, trigger, reset, sampler-trigger, expression, or unknown`);
361
755
  }
362
756
  }
363
-
364
- function parseOptionalControlInterfaceConnector(
365
- value: YamlValue | undefined,
366
- path: string,
367
- ): ControlInterfaceConnector | undefined {
757
+ function parseOptionalControlInterfaceConnector(value, path) {
368
758
  if (value === undefined) {
369
759
  return undefined;
370
760
  }
@@ -381,11 +771,7 @@ function parseOptionalControlInterfaceConnector(
381
771
  throw new Error(`${path}: expected a supported connector kind`);
382
772
  }
383
773
  }
384
-
385
- function parseOptionalControlInterfaceAssignmentHint(
386
- value: YamlValue | undefined,
387
- path: string,
388
- ): ControlInterfaceAssignmentHint | undefined {
774
+ function parseOptionalControlInterfaceAssignmentHint(value, path) {
389
775
  if (value === undefined) {
390
776
  return undefined;
391
777
  }
@@ -400,11 +786,7 @@ function parseOptionalControlInterfaceAssignmentHint(
400
786
  throw new Error(`${path}: expected momentary, latching, momentary-or-latching, or continuous`);
401
787
  }
402
788
  }
403
-
404
- function parseOptionalControlInterfacePolarity(
405
- value: YamlValue | undefined,
406
- path: string,
407
- ): ControlInterfacePolarity | undefined {
789
+ function parseOptionalControlInterfacePolarity(value, path) {
408
790
  if (value === undefined) {
409
791
  return undefined;
410
792
  }
@@ -419,13 +801,12 @@ function parseOptionalControlInterfacePolarity(
419
801
  throw new Error(`${path}: expected normally-open, normally-closed, expression, or unknown`);
420
802
  }
421
803
  }
422
-
423
- function parseYamlSubset(source: string): YamlValue {
804
+ function parseYamlSubset(source) {
424
805
  const lines = tokenize(source);
425
806
  if (lines.length === 0) {
426
807
  throw new Error('interchange YAML is empty');
427
808
  }
428
- const cursor: Cursor = { index: 0 };
809
+ const cursor = { index: 0 };
429
810
  const first = lines[0];
430
811
  if (first === undefined) {
431
812
  throw new Error('interchange YAML is empty');
@@ -437,9 +818,8 @@ function parseYamlSubset(source: string): YamlValue {
437
818
  }
438
819
  return value;
439
820
  }
440
-
441
- function tokenize(source: string): readonly YamlLine[] {
442
- const lines: YamlLine[] = [];
821
+ function tokenize(source) {
822
+ const lines = [];
443
823
  const rawLines = source.replace(/^/, '').split(/\r?\n/);
444
824
  rawLines.forEach((rawLine, index) => {
445
825
  if (rawLine.trim().length === 0) {
@@ -457,8 +837,7 @@ function tokenize(source: string): readonly YamlLine[] {
457
837
  });
458
838
  return lines;
459
839
  }
460
-
461
- function parseBlock(lines: readonly YamlLine[], cursor: Cursor, indent: number): YamlValue {
840
+ function parseBlock(lines, cursor, indent) {
462
841
  const line = lines[cursor.index];
463
842
  if (line === undefined) {
464
843
  return {};
@@ -471,9 +850,8 @@ function parseBlock(lines: readonly YamlLine[], cursor: Cursor, indent: number):
471
850
  }
472
851
  return parseObject(lines, cursor, indent);
473
852
  }
474
-
475
- function parseObject(lines: readonly YamlLine[], cursor: Cursor, indent: number): YamlObject {
476
- const out: YamlObject = {};
853
+ function parseObject(lines, cursor, indent) {
854
+ const out = {};
477
855
  while (cursor.index < lines.length) {
478
856
  const line = lines[cursor.index];
479
857
  if (line === undefined || line.indent < indent) {
@@ -485,7 +863,6 @@ function parseObject(lines: readonly YamlLine[], cursor: Cursor, indent: number)
485
863
  if (line.text === '-' || line.text.startsWith('- ')) {
486
864
  break;
487
865
  }
488
-
489
866
  const pair = parsePair(line.text, line.lineNumber);
490
867
  cursor.index += 1;
491
868
  out[pair.key] = pair.rest.length > 0
@@ -494,9 +871,8 @@ function parseObject(lines: readonly YamlLine[], cursor: Cursor, indent: number)
494
871
  }
495
872
  return out;
496
873
  }
497
-
498
- function parseArray(lines: readonly YamlLine[], cursor: Cursor, indent: number): readonly YamlValue[] {
499
- const out: YamlValue[] = [];
874
+ function parseArray(lines, cursor, indent) {
875
+ const out = [];
500
876
  while (cursor.index < lines.length) {
501
877
  const line = lines[cursor.index];
502
878
  if (line === undefined || line.indent < indent) {
@@ -508,33 +884,26 @@ function parseArray(lines: readonly YamlLine[], cursor: Cursor, indent: number):
508
884
  if (line.text !== '-' && !line.text.startsWith('- ')) {
509
885
  break;
510
886
  }
511
-
512
887
  const rest = line.text === '-' ? '' : line.text.slice(2);
513
888
  cursor.index += 1;
514
889
  if (rest.length === 0) {
515
890
  out.push(parseNestedValue(lines, cursor, indent, line.lineNumber));
516
- } else if (looksLikePair(rest)) {
891
+ }
892
+ else if (looksLikePair(rest)) {
517
893
  out.push(parseObjectItem(rest, lines, cursor, indent + 2, line.lineNumber));
518
- } else {
894
+ }
895
+ else {
519
896
  out.push(parseInlineValue(rest, line.lineNumber));
520
897
  }
521
898
  }
522
899
  return out;
523
900
  }
524
-
525
- function parseObjectItem(
526
- firstPairText: string,
527
- lines: readonly YamlLine[],
528
- cursor: Cursor,
529
- indent: number,
530
- lineNumber: number,
531
- ): YamlObject {
532
- const out: YamlObject = {};
901
+ function parseObjectItem(firstPairText, lines, cursor, indent, lineNumber) {
902
+ const out = {};
533
903
  const firstPair = parsePair(firstPairText, lineNumber);
534
904
  out[firstPair.key] = firstPair.rest.length > 0
535
905
  ? parseInlineValue(firstPair.rest, lineNumber)
536
906
  : parseNestedValue(lines, cursor, indent, lineNumber);
537
-
538
907
  while (cursor.index < lines.length) {
539
908
  const line = lines[cursor.index];
540
909
  if (line === undefined || line.indent < indent) {
@@ -546,23 +915,15 @@ function parseObjectItem(
546
915
  if (line.text === '-' || line.text.startsWith('- ')) {
547
916
  break;
548
917
  }
549
-
550
918
  const pair = parsePair(line.text, line.lineNumber);
551
919
  cursor.index += 1;
552
920
  out[pair.key] = pair.rest.length > 0
553
921
  ? parseInlineValue(pair.rest, line.lineNumber)
554
922
  : parseNestedValue(lines, cursor, indent, line.lineNumber);
555
923
  }
556
-
557
924
  return out;
558
925
  }
559
-
560
- function parseNestedValue(
561
- lines: readonly YamlLine[],
562
- cursor: Cursor,
563
- parentIndent: number,
564
- lineNumber: number,
565
- ): YamlValue {
926
+ function parseNestedValue(lines, cursor, parentIndent, lineNumber) {
566
927
  const next = lines[cursor.index];
567
928
  if (next === undefined || next.indent <= parentIndent) {
568
929
  return {};
@@ -572,8 +933,7 @@ function parseNestedValue(
572
933
  }
573
934
  return parseBlock(lines, cursor, next.indent);
574
935
  }
575
-
576
- function parsePair(text: string, lineNumber: number): ParsedPair {
936
+ function parsePair(text, lineNumber) {
577
937
  const colonIndex = findPairColon(text);
578
938
  if (colonIndex <= 0) {
579
939
  throw new Error(`line ${lineNumber}: expected key/value pair`);
@@ -585,20 +945,17 @@ function parsePair(text: string, lineNumber: number): ParsedPair {
585
945
  rest: restText.startsWith(' ') ? restText.slice(1) : restText,
586
946
  };
587
947
  }
588
-
589
- function looksLikePair(text: string): boolean {
948
+ function looksLikePair(text) {
590
949
  return findPairColon(text) > 0;
591
950
  }
592
-
593
- function findPairColon(text: string): number {
951
+ function findPairColon(text) {
594
952
  if (text.startsWith('"')) {
595
953
  const end = findJsonStringEnd(text);
596
954
  return end >= 0 && text[end + 1] === ':' ? end + 1 : -1;
597
955
  }
598
956
  return text.indexOf(':');
599
957
  }
600
-
601
- function findJsonStringEnd(text: string): number {
958
+ function findJsonStringEnd(text) {
602
959
  let escaped = false;
603
960
  for (let index = 1; index < text.length; index += 1) {
604
961
  const char = text[index];
@@ -616,8 +973,7 @@ function findJsonStringEnd(text: string): number {
616
973
  }
617
974
  return -1;
618
975
  }
619
-
620
- function parseKey(text: string, lineNumber: number): string {
976
+ function parseKey(text, lineNumber) {
621
977
  if (!text.startsWith('"')) {
622
978
  return text;
623
979
  }
@@ -626,13 +982,13 @@ function parseKey(text: string, lineNumber: number): string {
626
982
  if (typeof parsed === 'string') {
627
983
  return parsed;
628
984
  }
629
- } catch {
985
+ }
986
+ catch {
630
987
  // Fall through to the consistent parser error below.
631
988
  }
632
989
  throw new Error(`line ${lineNumber}: invalid quoted key`);
633
990
  }
634
-
635
- function parseInlineValue(text: string, lineNumber: number): YamlValue {
991
+ function parseInlineValue(text, lineNumber) {
636
992
  if (text === '[]') {
637
993
  return [];
638
994
  }
@@ -654,7 +1010,8 @@ function parseInlineValue(text: string, lineNumber: number): YamlValue {
654
1010
  if (typeof parsed === 'string') {
655
1011
  return parsed;
656
1012
  }
657
- } catch {
1013
+ }
1014
+ catch {
658
1015
  // Fall through to the consistent parser error below.
659
1016
  }
660
1017
  throw new Error(`line ${lineNumber}: invalid quoted scalar`);
@@ -664,8 +1021,7 @@ function parseInlineValue(text: string, lineNumber: number): YamlValue {
664
1021
  }
665
1022
  return text;
666
1023
  }
667
-
668
- function parseMetadata(value: YamlValue | undefined): CircuitDocument['metadata'] {
1024
+ function parseMetadata(value) {
669
1025
  const metadata = optionalObject(value, 'metadata');
670
1026
  return {
671
1027
  name: scalarText(metadata.name),
@@ -673,53 +1029,51 @@ function parseMetadata(value: YamlValue | undefined): CircuitDocument['metadata'
673
1029
  partNumber: scalarText(metadata.partNumber),
674
1030
  };
675
1031
  }
676
-
677
- function parseSource(value: YamlValue | undefined): DocumentSource {
1032
+ function parseSource(value) {
678
1033
  return parseStringRecord(value, 'source');
679
1034
  }
680
-
681
- function parsePanel(value: YamlValue | undefined): PanelPlacementMetadata | undefined {
1035
+ function parsePanel(value, allowV3PhysicalFields) {
682
1036
  if (value === undefined) {
683
1037
  return undefined;
684
1038
  }
685
1039
  const panel = expectObject(value, 'panel');
686
-
687
1040
  if (panel.faces !== undefined) {
688
1041
  return {
689
- faces: optionalArray(panel.faces, 'panel.faces').map((item, index) => parsePanelFace(item, index)),
1042
+ faces: optionalArray(panel.faces, 'panel.faces').map((item, index) => parsePanelFace(item, index, allowV3PhysicalFields)),
690
1043
  };
691
1044
  }
692
-
693
1045
  if (panel.layout === undefined) {
694
1046
  return undefined;
695
1047
  }
696
-
697
1048
  const layout = parsePanelLayout(panel.layout, 'panel.layout');
698
1049
  const elementsValue = panel.controls ?? panel.elements;
699
1050
  const elementsPath = panel.controls === undefined ? 'panel.elements' : 'panel.controls';
700
1051
  return {
701
1052
  faces: [{
702
- id: 'top',
703
- layout,
704
- elements: parsePanelElements(elementsValue, layout, elementsPath),
705
- }],
1053
+ id: 'top',
1054
+ layout,
1055
+ elements: parsePanelElements(elementsValue, layout, elementsPath, allowV3PhysicalFields),
1056
+ }],
706
1057
  };
707
1058
  }
708
-
709
- function parsePanelFace(value: YamlValue, index: number): PanelPlacementMetadata['faces'][number] {
1059
+ function parsePanelFace(value, index, allowV3PhysicalFields) {
710
1060
  const path = `panel.faces[${index}]`;
711
1061
  const face = expectObject(value, path);
712
1062
  const label = parseOptionalString(face.label, `${path}.label`);
713
1063
  const layout = parsePanelLayout(face.layout, `${path}.layout`);
1064
+ if (!allowV3PhysicalFields && face.geometry !== undefined) {
1065
+ throw new Error(`${path}.geometry: requires schema ${INTERCHANGE_SCHEMA_V3}`);
1066
+ }
1067
+ const geometry = allowV3PhysicalFields ? parseOptionalPanelFaceGeometry(face.geometry, `${path}.geometry`) : undefined;
714
1068
  return {
715
1069
  id: expectString(face.id, `${path}.id`),
716
1070
  ...(label === undefined ? {} : { label }),
717
1071
  layout,
718
- elements: parsePanelElements(face.elements, layout, `${path}.elements`),
1072
+ ...(geometry === undefined ? {} : { geometry }),
1073
+ elements: parsePanelElements(face.elements, layout, `${path}.elements`, allowV3PhysicalFields),
719
1074
  };
720
1075
  }
721
-
722
- function parsePanelLayout(value: YamlValue | undefined, path: string): PanelGridLayout {
1076
+ function parsePanelLayout(value, path) {
723
1077
  const layout = expectObject(value, path);
724
1078
  const rowOrder = parseOptionalPanelRowOrder(layout.rowOrder, `${path}.rowOrder`);
725
1079
  const columnOrder = parseOptionalPanelColumnOrder(layout.columnOrder, `${path}.columnOrder`);
@@ -732,33 +1086,77 @@ function parsePanelLayout(value: YamlValue | undefined, path: string): PanelGrid
732
1086
  ...(columnOrder === undefined ? {} : { columnOrder }),
733
1087
  };
734
1088
  }
735
-
736
- function parsePanelElements(
737
- value: YamlValue | undefined,
738
- layout: PanelGridLayout,
739
- path: string,
740
- ): PanelPlacementMetadata['faces'][number]['elements'] {
1089
+ function parsePanelElements(value, layout, path, allowV3PhysicalFields) {
741
1090
  return optionalArray(value, path).map((item, index) => {
742
1091
  const elementPath = `${path}[${index}]`;
743
1092
  const element = expectObject(item, elementPath);
744
1093
  const label = parseOptionalString(element.label, `${elementPath}.label`);
745
1094
  const interfaceControlId = parseOptionalString(element.interfaceControlId, `${elementPath}.interfaceControlId`);
1095
+ if (!allowV3PhysicalFields && element.id !== undefined) {
1096
+ throw new Error(`${elementPath}.id: requires schema ${INTERCHANGE_SCHEMA_V3}`);
1097
+ }
1098
+ if (!allowV3PhysicalFields && element.physical !== undefined) {
1099
+ throw new Error(`${elementPath}.physical: requires schema ${INTERCHANGE_SCHEMA_V3}`);
1100
+ }
1101
+ const id = allowV3PhysicalFields ? parseOptionalString(element.id, `${elementPath}.id`) : undefined;
1102
+ const physical = allowV3PhysicalFields
1103
+ ? parseOptionalPanelElementPhysical(element.physical, `${elementPath}.physical`)
1104
+ : undefined;
746
1105
  return {
1106
+ ...(id === undefined ? {} : { id }),
747
1107
  bind: parsePanelElementBinding(element, elementPath),
748
- kind: parsePanelControlKind(
749
- element.kind ?? element.controlKind,
750
- element.kind === undefined && element.controlKind !== undefined
751
- ? `${elementPath}.controlKind`
752
- : `${elementPath}.kind`,
753
- ),
1108
+ kind: parsePanelControlKind(element.kind ?? element.controlKind, element.kind === undefined && element.controlKind !== undefined
1109
+ ? `${elementPath}.controlKind`
1110
+ : `${elementPath}.kind`),
754
1111
  grid: parsePanelGridPosition(element.grid, `${elementPath}.grid`, layout),
755
1112
  ...(label === undefined ? {} : { label }),
756
1113
  ...(interfaceControlId === undefined ? {} : { interfaceControlId }),
1114
+ ...(physical === undefined ? {} : { physical }),
757
1115
  };
758
1116
  });
759
1117
  }
760
-
761
- function parsePanelElementBinding(element: YamlObject, path: string): PanelElementBinding {
1118
+ function parseOptionalPanelFaceGeometry(value, path) {
1119
+ if (value === undefined) {
1120
+ return undefined;
1121
+ }
1122
+ const geometry = expectObject(value, path);
1123
+ return {
1124
+ ...parseBuildDataObject(geometry, path),
1125
+ ...(geometry.units === undefined ? {} : { units: expectString(geometry.units, `${path}.units`) }),
1126
+ ...(geometry.surface === undefined ? {} : { surface: expectString(geometry.surface, `${path}.surface`) }),
1127
+ ...(geometry.usableRectMm === undefined
1128
+ ? {}
1129
+ : { usableRectMm: parseMillimeterRect(geometry.usableRectMm, `${path}.usableRectMm`) }),
1130
+ };
1131
+ }
1132
+ function parseOptionalPanelElementPhysical(value, path) {
1133
+ if (value === undefined) {
1134
+ return undefined;
1135
+ }
1136
+ const physical = expectObject(value, path);
1137
+ return {
1138
+ ...parseBuildDataObject(physical, path),
1139
+ ...(physical.units === undefined ? {} : { units: expectString(physical.units, `${path}.units`) }),
1140
+ ...(physical.centerMm === undefined ? {} : { centerMm: parsePoint(physical.centerMm, `${path}.centerMm`) }),
1141
+ ...(physical.drillDiameterMm === undefined
1142
+ ? {}
1143
+ : { drillDiameterMm: expectNumber(physical.drillDiameterMm, `${path}.drillDiameterMm`) }),
1144
+ ...(physical.partProfileId === undefined
1145
+ ? {}
1146
+ : { partProfileId: expectString(physical.partProfileId, `${path}.partProfileId`) }),
1147
+ ...(physical.locked === undefined ? {} : { locked: expectBoolean(physical.locked, `${path}.locked`) }),
1148
+ };
1149
+ }
1150
+ function parseMillimeterRect(value, path) {
1151
+ const rect = expectObject(value, path);
1152
+ return {
1153
+ x: expectNumber(rect.x, `${path}.x`),
1154
+ y: expectNumber(rect.y, `${path}.y`),
1155
+ width: expectNumber(rect.width, `${path}.width`),
1156
+ height: expectNumber(rect.height, `${path}.height`),
1157
+ };
1158
+ }
1159
+ function parsePanelElementBinding(element, path) {
762
1160
  if (element.bind !== undefined) {
763
1161
  const bind = expectObject(element.bind, `${path}.bind`);
764
1162
  const controlId = parseOptionalString(bind.controlId, `${path}.bind.controlId`);
@@ -771,17 +1169,11 @@ function parsePanelElementBinding(element: YamlObject, path: string): PanelEleme
771
1169
  ...(property === undefined ? {} : { property }),
772
1170
  };
773
1171
  }
774
-
775
1172
  return {
776
1173
  componentId: expectString(element.componentId, `${path}.componentId`),
777
1174
  };
778
1175
  }
779
-
780
- function parsePanelGridPosition(
781
- value: YamlValue | undefined,
782
- path: string,
783
- layout: PanelGridLayout,
784
- ): PanelGridPosition {
1176
+ function parsePanelGridPosition(value, path, layout) {
785
1177
  const grid = expectObject(value, path);
786
1178
  const rowSpan = parseOptionalPositiveInteger(grid.rowSpan, `${path}.rowSpan`);
787
1179
  const columnSpan = parseOptionalPositiveInteger(grid.columnSpan, `${path}.columnSpan`);
@@ -796,15 +1188,7 @@ function parsePanelGridPosition(
796
1188
  ...(columnSpan === undefined ? {} : { columnSpan }),
797
1189
  };
798
1190
  }
799
-
800
- function validateGridAxis(
801
- value: number,
802
- span: number,
803
- size: number,
804
- indexing: PanelGridIndexing,
805
- path: string,
806
- axisName: 'row' | 'column',
807
- ): void {
1191
+ function validateGridAxis(value, span, size, indexing, path, axisName) {
808
1192
  const min = indexing === 'one-based' ? 1 : 0;
809
1193
  const occupiedEnd = indexing === 'one-based' ? value + span - 1 : value + span;
810
1194
  if (value < min || occupiedEnd > size) {
@@ -812,24 +1196,21 @@ function validateGridAxis(
812
1196
  throw new Error(`${path}: expected ${indexing} ${axisName} coordinate within ${min}..${maxLabel}`);
813
1197
  }
814
1198
  }
815
-
816
- function parsePanelLayoutKind(value: YamlValue | undefined, path: string): 'stompbox-grid' {
1199
+ function parsePanelLayoutKind(value, path) {
817
1200
  const kind = expectString(value, path);
818
1201
  if (kind === 'stompbox-grid') {
819
1202
  return kind;
820
1203
  }
821
1204
  throw new Error(`${path}: expected stompbox-grid`);
822
1205
  }
823
-
824
- function parsePanelGridIndexing(value: YamlValue | undefined, path: string): PanelGridIndexing {
1206
+ function parsePanelGridIndexing(value, path) {
825
1207
  const indexing = expectString(value, path);
826
1208
  if (indexing === 'one-based' || indexing === 'zero-based') {
827
1209
  return indexing;
828
1210
  }
829
1211
  throw new Error(`${path}: expected one-based or zero-based`);
830
1212
  }
831
-
832
- function parseOptionalPanelRowOrder(value: YamlValue | undefined, path: string): PanelRowOrder | undefined {
1213
+ function parseOptionalPanelRowOrder(value, path) {
833
1214
  if (value === undefined) {
834
1215
  return undefined;
835
1216
  }
@@ -839,8 +1220,7 @@ function parseOptionalPanelRowOrder(value: YamlValue | undefined, path: string):
839
1220
  }
840
1221
  throw new Error(`${path}: expected top-to-bottom or bottom-to-top`);
841
1222
  }
842
-
843
- function parseOptionalPanelColumnOrder(value: YamlValue | undefined, path: string): PanelColumnOrder | undefined {
1223
+ function parseOptionalPanelColumnOrder(value, path) {
844
1224
  if (value === undefined) {
845
1225
  return undefined;
846
1226
  }
@@ -850,22 +1230,22 @@ function parseOptionalPanelColumnOrder(value: YamlValue | undefined, path: strin
850
1230
  }
851
1231
  throw new Error(`${path}: expected left-to-right or right-to-left`);
852
1232
  }
853
-
854
- function parsePanelControlKind(value: YamlValue | undefined, path: string): PanelControlKind {
1233
+ function parsePanelControlKind(value, path) {
855
1234
  const kind = expectString(value, path);
856
1235
  switch (kind) {
857
1236
  case 'knob':
858
1237
  case 'slider':
859
1238
  case 'switch':
1239
+ case 'selector':
1240
+ case 'footswitch':
860
1241
  case 'led':
861
1242
  case 'jack':
862
1243
  return kind;
863
1244
  default:
864
- throw new Error(`${path}: expected knob, slider, switch, led, or jack`);
1245
+ throw new Error(`${path}: expected knob, slider, switch, selector, footswitch, led, or jack`);
865
1246
  }
866
1247
  }
867
-
868
- function parseComponents(value: YamlValue | undefined): readonly Component[] {
1248
+ function parseComponents(value) {
869
1249
  return optionalArray(value, 'components').map((item, index) => {
870
1250
  const path = `components[${index}]`;
871
1251
  const component = expectObject(item, path);
@@ -882,8 +1262,7 @@ function parseComponents(value: YamlValue | undefined): readonly Component[] {
882
1262
  };
883
1263
  });
884
1264
  }
885
-
886
- function parseTerminals(value: YamlValue | undefined, path: string): readonly Terminal[] {
1265
+ function parseTerminals(value, path) {
887
1266
  return optionalArray(value, path).map((item, index) => {
888
1267
  const terminalPath = `${path}[${index}]`;
889
1268
  const terminal = expectObject(item, terminalPath);
@@ -893,17 +1272,15 @@ function parseTerminals(value: YamlValue | undefined, path: string): readonly Te
893
1272
  };
894
1273
  });
895
1274
  }
896
-
897
- function parseProperties(value: YamlValue | undefined, path: string): Readonly<Record<string, PropertyValue>> {
1275
+ function parseProperties(value, path) {
898
1276
  const properties = optionalObject(value, path);
899
- const out: Record<string, PropertyValue> = {};
1277
+ const out = {};
900
1278
  for (const [key, child] of Object.entries(properties)) {
901
1279
  out[key] = parsePropertyValue(child, `${path}.${key}`);
902
1280
  }
903
1281
  return out;
904
1282
  }
905
-
906
- function parsePropertyValue(value: YamlValue, path: string): PropertyValue {
1283
+ function parsePropertyValue(value, path) {
907
1284
  if (isParsedQuantityValue(value)) {
908
1285
  return {
909
1286
  raw: expectString(value.raw, `${path}.raw`),
@@ -915,7 +1292,7 @@ function parsePropertyValue(value: YamlValue, path: string): PropertyValue {
915
1292
  return value.map((item, index) => parsePropertyValue(item, `${path}[${index}]`));
916
1293
  }
917
1294
  if (isYamlObject(value)) {
918
- const out: Record<string, PropertyValue> = {};
1295
+ const out = {};
919
1296
  for (const [key, child] of Object.entries(value)) {
920
1297
  out[key] = parsePropertyValue(child, `${path}.${key}`);
921
1298
  }
@@ -926,12 +1303,10 @@ function parsePropertyValue(value: YamlValue, path: string): PropertyValue {
926
1303
  }
927
1304
  throw new Error(`${path}: expected scalar property value or parsed quantity`);
928
1305
  }
929
-
930
- function isParsedQuantityValue(value: YamlValue): value is ParsedQuantity {
1306
+ function isParsedQuantityValue(value) {
931
1307
  return isParsedQuantity(value);
932
1308
  }
933
-
934
- function parseWires(value: YamlValue | undefined): readonly Wire[] {
1309
+ function parseWires(value) {
935
1310
  return optionalArray(value, 'wires').map((item, index) => {
936
1311
  const path = `wires[${index}]`;
937
1312
  const wire = expectObject(item, path);
@@ -947,12 +1322,11 @@ function parseWires(value: YamlValue | undefined): readonly Wire[] {
947
1322
  };
948
1323
  });
949
1324
  }
950
-
951
- function parseWarnings(value: YamlValue | undefined): readonly Warning[] {
1325
+ function parseWarnings(value) {
952
1326
  return optionalArray(value, 'diagnostics').map((item, index) => {
953
1327
  const path = `diagnostics[${index}]`;
954
1328
  const warning = expectObject(item, path);
955
- const out: Warning = {
1329
+ const out = {
956
1330
  code: expectString(warning.code, `${path}.code`),
957
1331
  message: expectString(warning.message, `${path}.message`),
958
1332
  ...(warning.componentId === undefined
@@ -965,44 +1339,38 @@ function parseWarnings(value: YamlValue | undefined): readonly Warning[] {
965
1339
  return out;
966
1340
  });
967
1341
  }
968
-
969
- function parseStringArray(value: YamlValue | undefined, path: string): readonly string[] {
1342
+ function parseStringArray(value, path) {
970
1343
  return optionalArray(value, path).map((item, index) => scalarText(item, `${path}[${index}]`));
971
1344
  }
972
-
973
- function parseStringRecord(value: YamlValue | undefined, path: string): Readonly<Record<string, string>> {
1345
+ function parseStringRecord(value, path) {
974
1346
  const record = optionalObject(value, path);
975
- const out: Record<string, string> = {};
1347
+ const out = {};
976
1348
  for (const [key, child] of Object.entries(record)) {
977
1349
  out[key] = scalarText(child, `${path}.${key}`);
978
1350
  }
979
1351
  return out;
980
1352
  }
981
-
982
- function parsePoint(value: YamlValue | undefined, path: string): Point {
1353
+ function parsePoint(value, path) {
983
1354
  const point = expectObject(value, path);
984
1355
  return {
985
1356
  x: expectNumber(point.x, `${path}.x`),
986
1357
  y: expectNumber(point.y, `${path}.y`),
987
1358
  };
988
1359
  }
989
-
990
- function parseRotation(value: YamlValue | undefined, path: string): Rotation {
1360
+ function parseRotation(value, path) {
991
1361
  const rotation = expectNumber(value, path);
992
1362
  if (rotation === 0 || rotation === 1 || rotation === 2 || rotation === 3) {
993
1363
  return rotation;
994
1364
  }
995
1365
  throw new Error(`${path}: expected rotation 0, 1, 2, or 3`);
996
1366
  }
997
-
998
- function parseNullableString(value: YamlValue | undefined, path: string): string | null {
1367
+ function parseNullableString(value, path) {
999
1368
  if (value === null || value === undefined) {
1000
1369
  return null;
1001
1370
  }
1002
1371
  return expectString(value, path);
1003
1372
  }
1004
-
1005
- function parseComponentKind(value: YamlValue | undefined, path: string): ComponentKind {
1373
+ function parseComponentKind(value, path) {
1006
1374
  const kind = expectString(value, path);
1007
1375
  switch (kind) {
1008
1376
  case 'resistor':
@@ -1045,108 +1413,93 @@ function parseComponentKind(value: YamlValue | undefined, path: string): Compone
1045
1413
  throw new Error(`${path}: unsupported component kind "${kind}"`);
1046
1414
  }
1047
1415
  }
1048
-
1049
- function optionalObject(value: YamlValue | undefined, path: string): YamlObject {
1416
+ function optionalObject(value, path) {
1050
1417
  if (value === undefined) {
1051
1418
  return {};
1052
1419
  }
1053
1420
  return expectObject(value, path);
1054
1421
  }
1055
-
1056
- function optionalArray(value: YamlValue | undefined, path: string): readonly YamlValue[] {
1422
+ function optionalArray(value, path) {
1057
1423
  if (value === undefined) {
1058
1424
  return [];
1059
1425
  }
1060
1426
  return expectArray(value, path);
1061
1427
  }
1062
-
1063
- function expectObject(value: YamlValue | undefined, path: string): YamlObject {
1428
+ function expectObject(value, path) {
1064
1429
  if (isYamlObject(value)) {
1065
1430
  return value;
1066
1431
  }
1067
1432
  throw new Error(`${path}: expected object`);
1068
1433
  }
1069
-
1070
- function expectArray(value: YamlValue | undefined, path: string): readonly YamlValue[] {
1434
+ function expectArray(value, path) {
1071
1435
  if (Array.isArray(value)) {
1072
1436
  return value;
1073
1437
  }
1074
1438
  throw new Error(`${path}: expected array`);
1075
1439
  }
1076
-
1077
- function expectString(value: YamlValue | undefined, path: string): string {
1440
+ function expectString(value, path) {
1078
1441
  if (typeof value === 'string') {
1079
1442
  return value;
1080
1443
  }
1081
1444
  throw new Error(`${path}: expected string`);
1082
1445
  }
1083
-
1084
- function expectNumber(value: YamlValue | undefined, path: string): number {
1446
+ function expectNumber(value, path) {
1085
1447
  if (typeof value === 'number' && Number.isFinite(value)) {
1086
1448
  return value;
1087
1449
  }
1088
1450
  throw new Error(`${path}: expected number`);
1089
1451
  }
1090
-
1091
- function expectPositiveInteger(value: YamlValue | undefined, path: string): number {
1452
+ function expectPositiveInteger(value, path) {
1092
1453
  const number = expectNumber(value, path);
1093
1454
  if (Number.isInteger(number) && number > 0) {
1094
1455
  return number;
1095
1456
  }
1096
1457
  throw new Error(`${path}: expected positive integer`);
1097
1458
  }
1098
-
1099
- function expectNonNegativeInteger(value: YamlValue | undefined, path: string): number {
1459
+ function expectNonNegativeInteger(value, path) {
1100
1460
  const number = expectNumber(value, path);
1101
1461
  if (Number.isInteger(number) && number >= 0) {
1102
1462
  return number;
1103
1463
  }
1104
1464
  throw new Error(`${path}: expected non-negative integer`);
1105
1465
  }
1106
-
1107
- function parseOptionalPositiveInteger(value: YamlValue | undefined, path: string): number | undefined {
1466
+ function parseOptionalPositiveInteger(value, path) {
1108
1467
  if (value === undefined) {
1109
1468
  return undefined;
1110
1469
  }
1111
1470
  return expectPositiveInteger(value, path);
1112
1471
  }
1113
-
1114
- function parseOptionalNumber(value: YamlValue | undefined, path: string): number | undefined {
1472
+ function parseOptionalNumber(value, path) {
1115
1473
  if (value === undefined) {
1116
1474
  return undefined;
1117
1475
  }
1118
1476
  return expectNumber(value, path);
1119
1477
  }
1120
-
1121
- function parseOptionalString(value: YamlValue | undefined, path: string): string | undefined {
1478
+ function parseOptionalString(value, path) {
1122
1479
  if (value === undefined) {
1123
1480
  return undefined;
1124
1481
  }
1125
1482
  return expectString(value, path);
1126
1483
  }
1127
-
1128
- function parseOptionalStringArray(value: YamlValue | undefined, path: string): readonly string[] | undefined {
1484
+ function parseOptionalStringArray(value, path) {
1129
1485
  if (value === undefined) {
1130
1486
  return undefined;
1131
1487
  }
1132
1488
  return expectArray(value, path).map((item, index) => expectString(item, `${path}[${index}]`));
1133
1489
  }
1134
-
1135
- function parseOptionalBoolean(value: YamlValue | undefined, path: string): boolean | undefined {
1490
+ function parseOptionalBoolean(value, path) {
1136
1491
  if (value === undefined) {
1137
1492
  return undefined;
1138
1493
  }
1139
1494
  return expectBoolean(value, path);
1140
1495
  }
1141
-
1142
- function expectBoolean(value: YamlValue | undefined, path: string): boolean {
1496
+ function expectBoolean(value, path) {
1143
1497
  if (typeof value === 'boolean') {
1144
1498
  return value;
1145
1499
  }
1146
1500
  throw new Error(`${path}: expected boolean`);
1147
1501
  }
1148
-
1149
- function scalarText(value: YamlValue | undefined, path = 'value'): string {
1502
+ function scalarText(value, path = 'value') {
1150
1503
  if (value === undefined || value === null) {
1151
1504
  return '';
1152
1505
  }
@@ -1155,11 +1508,10 @@ function scalarText(value: YamlValue | undefined, path = 'value'): string {
1155
1508
  }
1156
1509
  throw new Error(`${path}: expected scalar`);
1157
1510
  }
1158
-
1159
- function isScalar(value: YamlValue): value is YamlScalar {
1511
+ function isScalar(value) {
1160
1512
  return value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
1161
1513
  }
1162
-
1163
- function isYamlObject(value: YamlValue | undefined): value is YamlObject {
1514
+ function isYamlObject(value) {
1164
1515
  return value !== undefined && value !== null && typeof value === 'object' && !Array.isArray(value);
1165
1516
  }
1517
+ //# sourceMappingURL=parser.js.map