@vessel-dsp/core 0.6.0 → 0.6.2

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 +101 -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} +352 -418
  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} +20 -16
  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,170 +1,15 @@
1
- import {
2
- any_circuit_element,
3
- type AnyCircuitElement,
4
- type AnyCircuitElementInput,
5
- type CircuitJson as OfficialCircuitJson,
6
- } from 'circuit-json';
7
- import { getPinNode, resolveConnectivity, type Connectivity, type NodeId } from '../../model/connectivity';
8
- import { propertyQuantityValue, propertyStringValue } from '../../model/properties';
9
- import { parseQuantity } from '../../model/quantity';
10
- import type {
11
- CircuitDocument,
12
- Component,
13
- ComponentKind,
14
- ParsedQuantity,
15
- Point,
16
- PropertyValue,
17
- Rotation,
18
- Terminal,
19
- Warning,
20
- Wire,
21
- } from '../../model/types';
22
-
23
- export type CircuitJsonExportTarget = 'tscircuit';
24
-
25
- export type CircuitJsonExportOptions = Readonly<{
26
- target?: CircuitJsonExportTarget;
27
- }>;
28
-
29
- export type CircuitJsonSourceNet = Readonly<{
30
- type: 'source_net';
31
- source_net_id: string;
32
- name: string;
33
- member_source_group_ids: string[];
34
- is_power?: boolean;
35
- is_ground?: boolean;
36
- is_digital_signal?: boolean;
37
- is_analog_signal?: boolean;
38
- is_positive_voltage_source?: boolean;
39
- }>;
40
-
41
- export type CircuitJsonSourceComponent = Readonly<{
42
- type: 'source_component';
43
- source_component_id: string;
44
- name: string;
45
- ftype?: string;
46
- display_name?: string;
47
- display_value?: string;
48
- resistance?: number;
49
- display_resistance?: string;
50
- capacitance?: number;
51
- display_capacitance?: string;
52
- inductance?: number;
53
- display_inductance?: string;
54
- voltage?: number;
55
- current?: number;
56
- wave_shape?: 'dc' | 'sine' | 'square' | 'triangle' | 'sawtooth' | 'sinewave';
57
- transistor_type?: 'npn' | 'pnp';
58
- channel_type?: 'n' | 'p';
59
- mosfet_mode?: 'enhancement' | 'depletion';
60
- max_resistance?: number;
61
- display_max_resistance?: string;
62
- manufacturer_part_number?: string;
63
- }>;
64
-
65
- export type CircuitJsonSourcePort = Readonly<{
66
- type: 'source_port';
67
- source_port_id: string;
68
- source_component_id: string;
69
- name: string;
70
- port_hints: string[];
71
- provides_ground?: boolean;
72
- requires_ground?: boolean;
73
- provides_power?: boolean;
74
- requires_power?: boolean;
75
- provides_voltage?: number;
76
- }>;
77
-
78
- export type CircuitJsonSourceTrace = Readonly<{
79
- type: 'source_trace';
80
- source_trace_id: string;
81
- connected_source_port_ids: string[];
82
- connected_source_net_ids: string[];
83
- display_name?: string;
84
- }>;
85
-
86
- export type CircuitJsonElement =
87
- AnyCircuitElement;
88
-
89
- export type CircuitJson = OfficialCircuitJson;
90
- export type { AnyCircuitElement, AnyCircuitElementInput };
91
-
92
- export type CircuitJsonExport = Readonly<{
93
- elements: CircuitJson;
94
- warnings: readonly string[];
95
- }>;
96
-
97
- export type CircuitJsonSchemaValidationIssue = Readonly<{
98
- code: 'circuit-json-schema-invalid';
99
- message: string;
100
- path?: string;
101
- }>;
102
-
103
- export type CircuitJsonSchemaValidationResult =
104
- | Readonly<{
105
- valid: true;
106
- elements: CircuitJson;
107
- errors: readonly [];
108
- }>
109
- | Readonly<{
110
- valid: false;
111
- errors: readonly CircuitJsonSchemaValidationIssue[];
112
- }>;
113
-
114
- export type ParseCircuitJsonDocumentOptions = Readonly<{
115
- filename?: string;
116
- }>;
117
-
118
- type JsonRecord = Readonly<Record<string, unknown>>;
119
-
120
- type SourceComponentRecord = Readonly<{
121
- sourceComponentId: string;
122
- componentId: string;
123
- name: string;
124
- ftype: string | null;
125
- record: JsonRecord;
126
- }>;
127
-
128
- type SourcePortRecord = Readonly<{
129
- sourcePortId: string;
130
- componentSourceId: string;
131
- terminalName: string;
132
- record: JsonRecord;
133
- }>;
134
-
135
- type SchematicComponentRecord = Readonly<{
136
- sourceComponentId: string;
137
- center: Point;
138
- }>;
139
-
140
- type SchematicPortRecord = Readonly<{
141
- sourcePortId: string;
142
- center: Point;
143
- }>;
144
-
145
- type MutableComponentBuild = {
146
- readonly sourceComponent: SourceComponentRecord;
147
- readonly ports: readonly SourcePortRecord[];
148
- readonly origin: Point;
149
- readonly terminals: readonly Terminal[];
150
- };
151
-
152
- type QuantityLookup = Readonly<{
153
- value: ParsedQuantity;
154
- }>;
155
-
156
- type QuantityKey = 'resistance' | 'capacitance' | 'inductance' | 'voltage' | 'current';
157
- type CircuitJsonSchematicPortDirection = 'up' | 'down' | 'left' | 'right';
158
-
159
- const VALUE_PROPERTY_NAMES: Readonly<Record<QuantityKey, readonly string[]>> = {
1
+ import { any_circuit_element, } from 'circuit-json';
2
+ import { getPinNode, resolveConnectivity } from '../../model/connectivity.js';
3
+ import { propertyQuantityValue, propertyStringValue } from '../../model/properties.js';
4
+ import { parseQuantity } from '../../model/quantity.js';
5
+ const VALUE_PROPERTY_NAMES = {
160
6
  resistance: ['R', 'Resistance', 'resistance', 'value', 'Value'],
161
7
  capacitance: ['C', 'Capacitance', 'capacitance', 'value', 'Value'],
162
8
  inductance: ['L', 'Inductance', 'inductance', 'value', 'Value'],
163
9
  voltage: ['V', 'Voltage', 'voltage', 'value', 'Value'],
164
10
  current: ['I', 'Current', 'current', 'value', 'Value'],
165
11
  };
166
-
167
- const MODEL_PROPERTY_NAMES: readonly string[] = [
12
+ const MODEL_PROPERTY_NAMES = [
168
13
  'manufacturerPartNumber',
169
14
  'ManufacturerPartNumber',
170
15
  'manufacturer_part_number',
@@ -175,8 +20,7 @@ const MODEL_PROPERTY_NAMES: readonly string[] = [
175
20
  'modelName',
176
21
  'ModelName',
177
22
  ];
178
-
179
- const DIRECT_EXPORT_KINDS: ReadonlySet<ComponentKind> = new Set<ComponentKind>([
23
+ const DIRECT_EXPORT_KINDS = new Set([
180
24
  'resistor',
181
25
  'variable-resistor',
182
26
  'capacitor',
@@ -198,30 +42,26 @@ const DIRECT_EXPORT_KINDS: ReadonlySet<ComponentKind> = new Set<ComponentKind>([
198
42
  'port',
199
43
  'ic',
200
44
  ]);
201
-
202
- const SOURCE_ONLY_NET_NAME_KINDS: ReadonlySet<ComponentKind> = new Set<ComponentKind>([
45
+ const SOURCE_ONLY_NET_NAME_KINDS = new Set([
203
46
  'label',
204
47
  'named-wire',
205
48
  ]);
206
-
49
+ const VESSEL_CAN_CAP_SOURCE_GROUP_PREFIX = 'source_group:vessel-dsp-can-cap:';
50
+ const VESSEL_DSP_PROPERTY_JSON_PREFIX = 'vessel-dsp-property-json:';
51
+ const VESSEL_DSP_PROPERTY_SIDECAR_ID_PREFIX = 'vessel_dsp_property:';
207
52
  const TSCIRCUIT_SCHEMATIC_COORD_SCALE = 0.02;
208
53
  const DEFAULT_SCHEMATIC_COMPONENT_SIZE = { width: 1.2, height: 0.8 };
209
-
210
- export function serializeCircuitJsonDocument(
211
- doc: CircuitDocument,
212
- _options: CircuitJsonExportOptions = {},
213
- ): CircuitJsonExport {
54
+ export function serializeCircuitJsonDocument(doc, _options = {}) {
214
55
  const connectivity = resolveConnectivity(doc);
215
- const warnings: string[] = [];
216
- const exportedComponentIds = new Set<string>();
217
- const sourcePortIdsByNode = new Map<NodeId, string[]>();
56
+ const warnings = [];
57
+ const exportedComponentIds = new Set();
58
+ const sourcePortIdsByNode = new Map();
218
59
  const names = netNames(doc, connectivity);
219
60
  const powerNodes = railNodeIds(doc, connectivity);
220
-
221
61
  const nets = sourceNetElements(connectivity, names, powerNodes);
222
- const components: CircuitJsonSourceComponent[] = [];
223
- const ports: CircuitJsonSourcePort[] = [];
224
-
62
+ const groups = sourceGroupElements(doc);
63
+ const components = [];
64
+ const ports = [];
225
65
  for (const component of doc.components) {
226
66
  const sourceComponent = sourceComponentElement(component, warnings);
227
67
  if (sourceComponent === null) {
@@ -229,7 +69,6 @@ export function serializeCircuitJsonDocument(
229
69
  }
230
70
  exportedComponentIds.add(component.id);
231
71
  components.push(sourceComponent);
232
-
233
72
  for (const terminal of component.terminals) {
234
73
  const nodeId = getPinNode(connectivity, {
235
74
  componentId: component.id,
@@ -242,30 +81,27 @@ export function serializeCircuitJsonDocument(
242
81
  }
243
82
  }
244
83
  }
245
-
246
84
  const traces = sourceTraceElements(connectivity, names, sourcePortIdsByNode, warnings);
247
85
  warnings.push(...sourceOnlyWarnings(doc, exportedComponentIds));
248
-
249
- const sourceElements = [...sourceProjectMetadataElements(doc), ...nets, ...components, ...ports, ...traces];
86
+ const sidecars = sourcePropertySidecarElements(doc, exportedComponentIds);
87
+ const sourceElements = [...sourceProjectMetadataElements(doc), ...nets, ...groups, ...components, ...ports, ...traces, ...sidecars];
250
88
  return {
251
89
  elements: normalizeCircuitJsonElements([...sourceElements, ...schematicElements(doc, traces)]),
252
90
  warnings,
253
91
  };
254
92
  }
255
-
256
- export function validateCircuitJsonDocument(source: unknown): CircuitJsonSchemaValidationResult {
93
+ export function validateCircuitJsonDocument(source) {
257
94
  if (!Array.isArray(source)) {
258
95
  return {
259
96
  valid: false,
260
97
  errors: [{
261
- code: 'circuit-json-schema-invalid',
262
- message: 'Circuit JSON document must be an array of elements',
263
- }],
98
+ code: 'circuit-json-schema-invalid',
99
+ message: 'Circuit JSON document must be an array of elements',
100
+ }],
264
101
  };
265
102
  }
266
-
267
- const elements: AnyCircuitElement[] = [];
268
- const errors: CircuitJsonSchemaValidationIssue[] = [];
103
+ const elements = [];
104
+ const errors = [];
269
105
  for (const [index, element] of source.entries()) {
270
106
  const shallowIssue = shallowSchemaIssue(element);
271
107
  if (shallowIssue !== null) {
@@ -287,32 +123,27 @@ export function validateCircuitJsonDocument(source: unknown): CircuitJsonSchemaV
287
123
  path: `[${index}]`,
288
124
  });
289
125
  }
290
-
291
126
  if (errors.length > 0) {
292
127
  return { valid: false, errors };
293
128
  }
294
129
  return { valid: true, elements, errors: [] };
295
130
  }
296
-
297
- export function parseCircuitJsonDocument(
298
- source: unknown,
299
- options: ParseCircuitJsonDocumentOptions = {},
300
- ): CircuitDocument {
131
+ export function parseCircuitJsonDocument(source, options = {}) {
301
132
  const result = validateCircuitJsonDocument(source);
302
133
  if (!result.valid) {
303
134
  throw new Error(result.errors.map((error) => `${error.path ?? '<root>'}: ${error.message}`).join('; '));
304
135
  }
305
-
306
- const sourceComponents = new Map<string, SourceComponentRecord>();
307
- const sourcePorts = new Map<string, SourcePortRecord>();
308
- const schematicComponents = new Map<string, SchematicComponentRecord>();
309
- const schematicPorts = new Map<string, SchematicPortRecord>();
310
- const sourceTraces: JsonRecord[] = [];
311
- const sourceNets = new Map<string, JsonRecord>();
312
- const warnings: Warning[] = [];
313
- const directives: string[] = [];
314
- let metadataName: string | null = null;
315
-
136
+ const sourceComponents = new Map();
137
+ const sourcePorts = new Map();
138
+ const sourceGroups = new Map();
139
+ const sourcePropertySidecars = new Map();
140
+ const schematicComponents = new Map();
141
+ const schematicPorts = new Map();
142
+ const sourceTraces = [];
143
+ const sourceNets = new Map();
144
+ const warnings = [];
145
+ const directives = [];
146
+ let metadataName = null;
316
147
  for (const element of result.elements) {
317
148
  const record = checkedRecord(element);
318
149
  const type = stringField(record, 'type');
@@ -324,10 +155,37 @@ export function parseCircuitJsonDocument(
324
155
  componentId: stripKnownPrefix(sourceComponentId, 'source_component:') ?? sanitizeId(stringField(record, 'name') ?? sourceComponentId),
325
156
  name: stringField(record, 'name') ?? stripKnownPrefix(sourceComponentId, 'source_component:') ?? sourceComponentId,
326
157
  ftype: stringField(record, 'ftype'),
158
+ sourceGroupId: stringField(record, 'source_group_id'),
327
159
  record,
328
160
  });
329
161
  break;
330
162
  }
163
+ case 'source_group': {
164
+ const sourceGroupId = requiredStringField(record, 'source_group_id');
165
+ sourceGroups.set(sourceGroupId, {
166
+ sourceGroupId,
167
+ name: stringField(record, 'name'),
168
+ });
169
+ break;
170
+ }
171
+ case 'source_property_ignored_warning': {
172
+ const sidecar = vesselDspPropertySidecar(record);
173
+ if (sidecar === null) {
174
+ warnings.push({
175
+ code: 'circuit-json-source-property-ignored',
176
+ message: `Circuit JSON source property "${stringField(record, 'property_name') ?? ''}" was ignored`,
177
+ });
178
+ break;
179
+ }
180
+ const existing = sourcePropertySidecars.get(sidecar.sourceComponentId);
181
+ if (existing === undefined) {
182
+ sourcePropertySidecars.set(sidecar.sourceComponentId, [sidecar]);
183
+ }
184
+ else {
185
+ existing.push(sidecar);
186
+ }
187
+ break;
188
+ }
331
189
  case 'source_port': {
332
190
  const sourcePortId = requiredStringField(record, 'source_port_id');
333
191
  const componentSourceId = requiredStringField(record, 'source_component_id');
@@ -382,7 +240,6 @@ export function parseCircuitJsonDocument(
382
240
  break;
383
241
  }
384
242
  }
385
-
386
243
  const portsByComponent = groupPortsByComponent(sourcePorts);
387
244
  const hasSchematicGeometry = schematicComponents.size > 0 || schematicPorts.size > 0;
388
245
  if (!hasSchematicGeometry && sourceComponents.size > 0) {
@@ -391,16 +248,15 @@ export function parseCircuitJsonDocument(
391
248
  message: 'Circuit JSON source elements did not include schematic geometry; generated deterministic component and terminal positions',
392
249
  });
393
250
  }
394
-
395
251
  const componentBuilds = Array.from(sourceComponents.values()).map((sourceComponent, index) => {
396
252
  const ports = portsByComponent.get(sourceComponent.sourceComponentId) ?? [];
397
- return buildComponentFromCircuitJson(sourceComponent, ports, schematicComponents, schematicPorts, index);
253
+ const sidecars = sourcePropertySidecars.get(sourceComponent.sourceComponentId) ?? [];
254
+ return buildComponentFromCircuitJson(sourceComponent, ports, schematicComponents, schematicPorts, sourceGroups, sidecars, index);
398
255
  });
399
256
  const components = componentBuilds.map((build) => build.component);
400
257
  const terminalPositions = terminalPositionMap(componentBuilds);
401
258
  const wires = wireElementsFromSourceTraces(sourceTraces, terminalPositions);
402
259
  const netWarnings = warningsForUnconnectedNets(sourceNets, sourceTraces);
403
-
404
260
  return {
405
261
  metadata: {
406
262
  name: metadataName ?? filenameWithoutCircuitJsonExtension(options.filename ?? 'Circuit JSON Import'),
@@ -418,25 +274,18 @@ export function parseCircuitJsonDocument(
418
274
  rawAttributes: { format: 'circuit-json' },
419
275
  };
420
276
  }
421
-
422
- function sourceProjectMetadataElements(doc: CircuitDocument): AnyCircuitElement[] {
277
+ function sourceProjectMetadataElements(doc) {
423
278
  if (doc.metadata.name.trim().length === 0) {
424
279
  return [];
425
280
  }
426
281
  return [{
427
- type: 'source_project_metadata',
428
- name: doc.metadata.name,
429
- software_used_string: '@vessel-dsp/core',
430
- }];
431
- }
432
-
433
- function sourceNetElements(
434
- connectivity: Connectivity,
435
- names: ReadonlyMap<NodeId, string>,
436
- powerNodes: ReadonlySet<NodeId>,
437
- ): readonly CircuitJsonSourceNet[] {
438
- const elements: CircuitJsonSourceNet[] = [];
439
-
282
+ type: 'source_project_metadata',
283
+ name: doc.metadata.name,
284
+ software_used_string: '@vessel-dsp/core',
285
+ }];
286
+ }
287
+ function sourceNetElements(connectivity, names, powerNodes) {
288
+ const elements = [];
440
289
  for (let nodeId = 0; nodeId < connectivity.nodeCount; nodeId += 1) {
441
290
  const isGround = connectivity.groundNodeId === nodeId;
442
291
  const isPower = powerNodes.has(nodeId);
@@ -451,18 +300,28 @@ function sourceNetElements(
451
300
  is_analog_signal: true,
452
301
  });
453
302
  }
454
-
455
303
  return elements;
456
304
  }
457
-
458
- function sourceComponentElement(
459
- component: Component,
460
- warnings: string[],
461
- ): CircuitJsonSourceComponent | null {
305
+ function sourceGroupElements(doc) {
306
+ const byGroupId = new Map();
307
+ for (const component of doc.components) {
308
+ const groupId = canCapGroupProperty(component);
309
+ if (groupId === null || byGroupId.has(groupId)) {
310
+ continue;
311
+ }
312
+ const packageName = firstStringProperty(component, ['CanCapPackageName', 'PackageName']);
313
+ byGroupId.set(groupId, {
314
+ type: 'source_group',
315
+ source_group_id: canCapSourceGroupId(groupId),
316
+ show_as_schematic_box: true,
317
+ name: packageName ?? groupId,
318
+ });
319
+ }
320
+ return Array.from(byGroupId.values());
321
+ }
322
+ function sourceComponentElement(component, warnings) {
462
323
  if (component.kind === 'unsupported') {
463
- warnings.push(
464
- `${component.id} (unsupported): unsupported source type ${component.sourceTypeName ?? 'unknown'} skipped from Circuit JSON export`,
465
- );
324
+ warnings.push(`${component.id} (unsupported): unsupported source type ${component.sourceTypeName ?? 'unknown'} skipped from Circuit JSON export`);
466
325
  return null;
467
326
  }
468
327
  if (SOURCE_ONLY_NET_NAME_KINDS.has(component.kind)) {
@@ -472,10 +331,8 @@ function sourceComponentElement(
472
331
  warnings.push(`${component.id} (${component.kind}): no Circuit JSON source-component mapping; skipped`);
473
332
  return null;
474
333
  }
475
-
476
334
  const base = sourceComponentBase(component);
477
335
  const manufacturerPartNumber = firstStringProperty(component, MODEL_PROPERTY_NAMES);
478
-
479
336
  switch (component.kind) {
480
337
  case 'resistor':
481
338
  case 'variable-resistor': {
@@ -540,9 +397,7 @@ function sourceComponentElement(
540
397
  ...(manufacturerPartNumber !== null ? { manufacturer_part_number: manufacturerPartNumber } : {}),
541
398
  };
542
399
  case 'jfet':
543
- warnings.push(
544
- `${component.id} (jfet): Circuit JSON has no simple_jfet ftype; emitted simple_mosfet depletion-mode source metadata`,
545
- );
400
+ warnings.push(`${component.id} (jfet): Circuit JSON has no simple_jfet ftype; emitted simple_mosfet depletion-mode source metadata`);
546
401
  return {
547
402
  ...base,
548
403
  ftype: 'simple_mosfet',
@@ -648,17 +503,96 @@ function sourceComponentElement(
648
503
  return null;
649
504
  }
650
505
  }
651
-
652
- function sourceComponentBase(component: Component): CircuitJsonSourceComponent {
506
+ function sourceComponentBase(component) {
507
+ const groupId = canCapGroupProperty(component);
653
508
  return {
654
509
  type: 'source_component',
655
510
  source_component_id: sourceComponentId(component.id),
656
511
  name: component.name,
657
512
  display_name: component.name,
513
+ ...(groupId === null ? {} : { source_group_id: canCapSourceGroupId(groupId) }),
658
514
  };
659
515
  }
660
-
661
- function sourcePortElement(component: Component, terminalName: string): CircuitJsonSourcePort {
516
+ function canCapGroupProperty(component) {
517
+ if (component.kind !== 'capacitor') {
518
+ return null;
519
+ }
520
+ const value = propertyStringValue(component.properties.CanCapGroupId);
521
+ return value === null || value.trim().length === 0 ? null : value.trim();
522
+ }
523
+ function canCapSourceGroupId(groupId) {
524
+ return `${VESSEL_CAN_CAP_SOURCE_GROUP_PREFIX}${groupId}`;
525
+ }
526
+ function canCapGroupIdFromSourceGroupId(sourceGroupId) {
527
+ return stripKnownPrefix(sourceGroupId, VESSEL_CAN_CAP_SOURCE_GROUP_PREFIX);
528
+ }
529
+ function sourcePropertySidecarElements(doc, exportedComponentIds) {
530
+ const sidecars = [];
531
+ for (const component of doc.components) {
532
+ if (!exportedComponentIds.has(component.id)) {
533
+ continue;
534
+ }
535
+ const consumed = circuitJsonConsumedPropertyKeys(component);
536
+ for (const [propertyName, value] of Object.entries(component.properties)) {
537
+ if (consumed.has(propertyName)) {
538
+ continue;
539
+ }
540
+ const sourceComponentIdValue = sourceComponentId(component.id);
541
+ sidecars.push({
542
+ type: 'source_property_ignored_warning',
543
+ source_property_ignored_warning_id: `${VESSEL_DSP_PROPERTY_SIDECAR_ID_PREFIX}${sourceComponentIdValue}:${propertyName}`,
544
+ source_component_id: sourceComponentIdValue,
545
+ property_name: propertyName,
546
+ error_type: 'source_property_ignored_warning',
547
+ message: `${VESSEL_DSP_PROPERTY_JSON_PREFIX}${stableJsonStringify({
548
+ property: propertyName,
549
+ value,
550
+ })}`,
551
+ });
552
+ }
553
+ }
554
+ return sidecars;
555
+ }
556
+ function circuitJsonConsumedPropertyKeys(component) {
557
+ const consumed = new Set();
558
+ const quantityKey = quantityKeyForComponent(component.kind);
559
+ if (quantityKey !== null) {
560
+ for (const name of VALUE_PROPERTY_NAMES[quantityKey]) {
561
+ consumed.add(name);
562
+ }
563
+ }
564
+ for (const name of MODEL_PROPERTY_NAMES) {
565
+ consumed.add(name);
566
+ }
567
+ consumed.add('ftype');
568
+ if (canCapGroupProperty(component) !== null) {
569
+ consumed.add('CanCapGroupId');
570
+ consumed.add('CanCapPackageName');
571
+ consumed.add('PackageName');
572
+ }
573
+ return consumed;
574
+ }
575
+ function quantityKeyForComponent(kind) {
576
+ switch (kind) {
577
+ case 'resistor':
578
+ case 'variable-resistor':
579
+ case 'potentiometer':
580
+ return 'resistance';
581
+ case 'capacitor':
582
+ return 'capacitance';
583
+ case 'inductor':
584
+ return 'inductance';
585
+ case 'voltage-source':
586
+ case 'battery':
587
+ case 'rail':
588
+ return 'voltage';
589
+ case 'current-source':
590
+ return 'current';
591
+ default:
592
+ return null;
593
+ }
594
+ }
595
+ function sourcePortElement(component, terminalName) {
662
596
  const voltage = component.kind === 'rail' ? quantity(component, 'voltage') : null;
663
597
  return {
664
598
  type: 'source_port',
@@ -671,15 +605,8 @@ function sourcePortElement(component: Component, terminalName: string): CircuitJ
671
605
  ...(voltage !== null ? { provides_voltage: voltage.value.value } : {}),
672
606
  };
673
607
  }
674
-
675
- function sourceTraceElements(
676
- connectivity: Connectivity,
677
- names: ReadonlyMap<NodeId, string>,
678
- sourcePortIdsByNode: ReadonlyMap<NodeId, readonly string[]>,
679
- warnings: string[],
680
- ): readonly CircuitJsonSourceTrace[] {
681
- const traces: CircuitJsonSourceTrace[] = [];
682
-
608
+ function sourceTraceElements(connectivity, names, sourcePortIdsByNode, warnings) {
609
+ const traces = [];
683
610
  for (let nodeId = 0; nodeId < connectivity.nodeCount; nodeId += 1) {
684
611
  const sourcePortIds = sourcePortIdsByNode.get(nodeId) ?? [];
685
612
  if (sourcePortIds.length === 0) {
@@ -694,17 +621,11 @@ function sourceTraceElements(
694
621
  display_name: names.get(nodeId) ?? (connectivity.groundNodeId === nodeId ? 'GND' : `N${nodeId}`),
695
622
  });
696
623
  }
697
-
698
624
  return traces;
699
625
  }
700
-
701
- function schematicElements(
702
- doc: CircuitDocument,
703
- sourceTraces: readonly CircuitJsonSourceTrace[],
704
- ): AnyCircuitElement[] {
705
- const elements: AnyCircuitElement[] = [];
706
- const schematicPortBySourcePortId = new Map<string, string>();
707
-
626
+ function schematicElements(doc, sourceTraces) {
627
+ const elements = [];
628
+ const schematicPortBySourcePortId = new Map();
708
629
  for (const component of doc.components) {
709
630
  if (SOURCE_ONLY_NET_NAME_KINDS.has(component.kind)) {
710
631
  continue;
@@ -738,7 +659,6 @@ function schematicElements(
738
659
  });
739
660
  }
740
661
  }
741
-
742
662
  for (const trace of sourceTraces) {
743
663
  const sourcePortIds = trace.connected_source_port_ids;
744
664
  if (sourcePortIds.length < 2) {
@@ -762,7 +682,6 @@ function schematicElements(
762
682
  edges,
763
683
  });
764
684
  }
765
-
766
685
  for (const [index, directive] of doc.directives.entries()) {
767
686
  elements.push({
768
687
  type: 'schematic_text',
@@ -775,19 +694,16 @@ function schematicElements(
775
694
  anchor: 'left',
776
695
  });
777
696
  }
778
-
779
697
  return elements;
780
698
  }
781
-
782
- function normalizeCircuitJsonElements(elements: readonly unknown[]): CircuitJson {
699
+ function normalizeCircuitJsonElements(elements) {
783
700
  const result = validateCircuitJsonDocument(elements);
784
701
  if (!result.valid) {
785
702
  throw new Error(`generated invalid Circuit JSON: ${result.errors.map((error) => `${error.path ?? '<root>'}: ${error.message}`).join('; ')}`);
786
703
  }
787
704
  return result.elements;
788
705
  }
789
-
790
- function schematicSymbolName(component: Component): string {
706
+ function schematicSymbolName(component) {
791
707
  switch (component.kind) {
792
708
  case 'resistor':
793
709
  case 'variable-resistor':
@@ -843,8 +759,7 @@ function schematicSymbolName(component: Component): string {
843
759
  return 'testpoint_right';
844
760
  }
845
761
  }
846
-
847
- function schematicComponentSize(component: Component): { readonly width: number; readonly height: number } {
762
+ function schematicComponentSize(component) {
848
763
  switch (component.kind) {
849
764
  case 'resistor':
850
765
  case 'variable-resistor':
@@ -872,13 +787,11 @@ function schematicComponentSize(component: Component): { readonly width: number;
872
787
  return DEFAULT_SCHEMATIC_COMPONENT_SIZE;
873
788
  }
874
789
  }
875
-
876
- function schematicDisplayValue(component: Component): string | undefined {
790
+ function schematicDisplayValue(component) {
877
791
  const value = quantity(component, 'resistance') ?? quantity(component, 'capacitance') ?? quantity(component, 'inductance') ?? quantity(component, 'voltage') ?? quantity(component, 'current');
878
792
  return value?.value.raw;
879
793
  }
880
-
881
- function schematicPortFacingDirection(origin: Point, terminalPosition: Point): CircuitJsonSchematicPortDirection {
794
+ function schematicPortFacingDirection(origin, terminalPosition) {
882
795
  const dx = terminalPosition.x - origin.x;
883
796
  const dy = terminalPosition.y - origin.y;
884
797
  if (Math.abs(dx) >= Math.abs(dy)) {
@@ -886,13 +799,11 @@ function schematicPortFacingDirection(origin: Point, terminalPosition: Point): C
886
799
  }
887
800
  return dy < 0 ? 'up' : 'down';
888
801
  }
889
-
890
- function isPolarizedCapacitor(component: Component): boolean {
802
+ function isPolarizedCapacitor(component) {
891
803
  const text = searchablePropertyText(component);
892
804
  return text.includes('electrolytic') || text.includes('polar');
893
805
  }
894
-
895
- function diodeSymbolName(component: Component): string {
806
+ function diodeSymbolName(component) {
896
807
  const text = searchablePropertyText(component);
897
808
  if (text.includes('zener')) {
898
809
  return 'zener_diode_horz';
@@ -902,8 +813,7 @@ function diodeSymbolName(component: Component): string {
902
813
  }
903
814
  return 'diode_right';
904
815
  }
905
-
906
- function mosfetSymbolName(component: Component): string {
816
+ function mosfetSymbolName(component) {
907
817
  const channel = inferMosfetChannel(component);
908
818
  const mode = inferMosfetMode(component);
909
819
  if (channel === 'p') {
@@ -911,9 +821,8 @@ function mosfetSymbolName(component: Component): string {
911
821
  }
912
822
  return mode === 'depletion' ? 'n_channel_d_mosfet_transistor_horz' : 'n_channel_e_mosfet_transistor_horz';
913
823
  }
914
-
915
- function netNames(doc: CircuitDocument, connectivity: Connectivity): ReadonlyMap<NodeId, string> {
916
- const names = new Map<NodeId, string>();
824
+ function netNames(doc, connectivity) {
825
+ const names = new Map();
917
826
  for (const component of doc.components) {
918
827
  if (component.terminals.length === 0) {
919
828
  continue;
@@ -938,9 +847,8 @@ function netNames(doc: CircuitDocument, connectivity: Connectivity): ReadonlyMap
938
847
  }
939
848
  return names;
940
849
  }
941
-
942
- function railNodeIds(doc: CircuitDocument, connectivity: Connectivity): ReadonlySet<NodeId> {
943
- const ids = new Set<NodeId>();
850
+ function railNodeIds(doc, connectivity) {
851
+ const ids = new Set();
944
852
  for (const component of doc.components) {
945
853
  if (component.kind !== 'rail') {
946
854
  continue;
@@ -957,12 +865,8 @@ function railNodeIds(doc: CircuitDocument, connectivity: Connectivity): Readonly
957
865
  }
958
866
  return ids;
959
867
  }
960
-
961
- function sourceOnlyWarnings(
962
- doc: CircuitDocument,
963
- exportedComponentIds: ReadonlySet<string>,
964
- ): readonly string[] {
965
- const warnings: string[] = [];
868
+ function sourceOnlyWarnings(doc, exportedComponentIds) {
869
+ const warnings = [];
966
870
  for (const component of doc.components) {
967
871
  if (exportedComponentIds.has(component.id)) {
968
872
  continue;
@@ -973,8 +877,7 @@ function sourceOnlyWarnings(
973
877
  }
974
878
  return warnings;
975
879
  }
976
-
977
- function appendSourcePort(map: Map<NodeId, string[]>, nodeId: NodeId, sourcePortIdValue: string): void {
880
+ function appendSourcePort(map, nodeId, sourcePortIdValue) {
978
881
  const existing = map.get(nodeId);
979
882
  if (existing === undefined) {
980
883
  map.set(nodeId, [sourcePortIdValue]);
@@ -982,8 +885,7 @@ function appendSourcePort(map: Map<NodeId, string[]>, nodeId: NodeId, sourcePort
982
885
  }
983
886
  existing.push(sourcePortIdValue);
984
887
  }
985
-
986
- function quantity(component: Component, key: QuantityKey): QuantityLookup | null {
888
+ function quantity(component, key) {
987
889
  const names = VALUE_PROPERTY_NAMES[key];
988
890
  for (const name of names) {
989
891
  const value = component.properties[name];
@@ -994,17 +896,10 @@ function quantity(component: Component, key: QuantityKey): QuantityLookup | null
994
896
  }
995
897
  return null;
996
898
  }
997
-
998
- function propertyQuantity(value: PropertyValue | undefined): ParsedQuantity | null {
899
+ function propertyQuantity(value) {
999
900
  return propertyQuantityValue(value);
1000
901
  }
1001
-
1002
- function missingQuantityComponent(
1003
- component: Component,
1004
- quantityName: string,
1005
- warnings: string[],
1006
- manufacturerPartNumber: string | null,
1007
- ): CircuitJsonSourceComponent {
902
+ function missingQuantityComponent(component, quantityName, warnings, manufacturerPartNumber) {
1008
903
  warnings.push(`${component.id} (${component.kind}): missing ${quantityName}; emitted opaque simple_chip source component metadata only`);
1009
904
  return {
1010
905
  ...sourceComponentBase(component),
@@ -1012,8 +907,7 @@ function missingQuantityComponent(
1012
907
  ...(manufacturerPartNumber !== null ? { manufacturer_part_number: manufacturerPartNumber } : {}),
1013
908
  };
1014
909
  }
1015
-
1016
- function firstStringProperty(component: Component, names: readonly string[]): string | null {
910
+ function firstStringProperty(component, names) {
1017
911
  for (const name of names) {
1018
912
  const value = component.properties[name];
1019
913
  const text = propertyStringValue(value);
@@ -1023,29 +917,24 @@ function firstStringProperty(component: Component, names: readonly string[]): st
1023
917
  }
1024
918
  return null;
1025
919
  }
1026
-
1027
- function inferTransistorType(component: Component): 'npn' | 'pnp' {
920
+ function inferTransistorType(component) {
1028
921
  const searchable = searchablePropertyText(component);
1029
922
  return searchable.includes('pnp') ? 'pnp' : 'npn';
1030
923
  }
1031
-
1032
- function inferMosfetChannel(component: Component): 'n' | 'p' {
924
+ function inferMosfetChannel(component) {
1033
925
  const searchable = searchablePropertyText(component);
1034
926
  return searchable.includes('pmos') || searchable.includes('p-channel') || searchable.includes('p channel') ? 'p' : 'n';
1035
927
  }
1036
-
1037
- function inferJfetChannel(component: Component): 'n' | 'p' {
928
+ function inferJfetChannel(component) {
1038
929
  const searchable = searchablePropertyText(component);
1039
930
  return searchable.includes('pjf') || searchable.includes('p-channel') || searchable.includes('p channel') ? 'p' : 'n';
1040
931
  }
1041
-
1042
- function inferMosfetMode(component: Component): 'enhancement' | 'depletion' {
932
+ function inferMosfetMode(component) {
1043
933
  const searchable = searchablePropertyText(component);
1044
934
  return searchable.includes('depletion') ? 'depletion' : 'enhancement';
1045
935
  }
1046
-
1047
- function searchablePropertyText(component: Component): string {
1048
- const values: string[] = [component.name, component.sourceTypeName ?? ''];
936
+ function searchablePropertyText(component) {
937
+ const values = [component.name, component.sourceTypeName ?? ''];
1049
938
  for (const value of Object.values(component.properties)) {
1050
939
  const text = propertyStringValue(value);
1051
940
  if (text !== null) {
@@ -1054,24 +943,19 @@ function searchablePropertyText(component: Component): string {
1054
943
  }
1055
944
  return values.join(' ').toLowerCase();
1056
945
  }
1057
-
1058
- function sourceComponentId(componentId: string): string {
946
+ function sourceComponentId(componentId) {
1059
947
  return `source_component:${componentId}`;
1060
948
  }
1061
-
1062
- function sourcePortId(componentId: string, terminalName: string): string {
949
+ function sourcePortId(componentId, terminalName) {
1063
950
  return `source_port:${componentId}:${terminalName}`;
1064
951
  }
1065
-
1066
- function sourceNetId(nodeId: NodeId): string {
952
+ function sourceNetId(nodeId) {
1067
953
  return `source_net:${nodeId}`;
1068
954
  }
1069
-
1070
- function sourceTraceId(nodeId: NodeId): string {
955
+ function sourceTraceId(nodeId) {
1071
956
  return `source_trace:${nodeId}`;
1072
957
  }
1073
-
1074
- function sourcePortPosition(doc: CircuitDocument, sourcePortIdValue: string): Point {
958
+ function sourcePortPosition(doc, sourcePortIdValue) {
1075
959
  const parsed = parseSourcePortId(sourcePortIdValue);
1076
960
  if (parsed === null) {
1077
961
  return { x: 0, y: 0 };
@@ -1080,19 +964,16 @@ function sourcePortPosition(doc: CircuitDocument, sourcePortIdValue: string): Po
1080
964
  const terminal = component?.terminals.find((candidate) => candidate.name === parsed.terminalName);
1081
965
  return toTscircuitSchematicPoint(terminal?.position ?? component?.origin ?? { x: 0, y: 0 });
1082
966
  }
1083
-
1084
- function toTscircuitSchematicPoint(point: Point): Point {
967
+ function toTscircuitSchematicPoint(point) {
1085
968
  return {
1086
969
  x: roundTscircuitSchematicCoordinate(point.x * TSCIRCUIT_SCHEMATIC_COORD_SCALE),
1087
970
  y: roundTscircuitSchematicCoordinate(point.y * TSCIRCUIT_SCHEMATIC_COORD_SCALE),
1088
971
  };
1089
972
  }
1090
-
1091
- function roundTscircuitSchematicCoordinate(value: number): number {
973
+ function roundTscircuitSchematicCoordinate(value) {
1092
974
  return Math.round(value * 1000) / 1000;
1093
975
  }
1094
-
1095
- function parseSourcePortId(sourcePortIdValue: string): Readonly<{ componentId: string; terminalName: string }> | null {
976
+ function parseSourcePortId(sourcePortIdValue) {
1096
977
  const prefix = 'source_port:';
1097
978
  if (!sourcePortIdValue.startsWith(prefix)) {
1098
979
  return null;
@@ -1107,23 +988,16 @@ function parseSourcePortId(sourcePortIdValue: string): Readonly<{ componentId: s
1107
988
  terminalName: rest.slice(separator + 1),
1108
989
  };
1109
990
  }
1110
-
1111
- function summarizeSchemaIssues(
1112
- issues: readonly {
1113
- readonly path: readonly (string | number)[];
1114
- readonly message: string;
1115
- }[],
1116
- ): string {
991
+ function summarizeSchemaIssues(issues) {
1117
992
  return issues
1118
993
  .slice(0, 3)
1119
994
  .map((issue) => {
1120
- const path = issue.path.length === 0 ? '<root>' : issue.path.join('.');
1121
- return `${path}: ${issue.message}`;
1122
- })
995
+ const path = issue.path.length === 0 ? '<root>' : issue.path.join('.');
996
+ return `${path}: ${issue.message}`;
997
+ })
1123
998
  .join('; ');
1124
999
  }
1125
-
1126
- function shallowSchemaIssue(value: unknown): string | null {
1000
+ function shallowSchemaIssue(value) {
1127
1001
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
1128
1002
  return 'Circuit JSON element must be an object';
1129
1003
  }
@@ -1134,33 +1008,61 @@ function shallowSchemaIssue(value: unknown): string | null {
1134
1008
  }
1135
1009
  return null;
1136
1010
  }
1137
-
1138
- function checkedRecord(value: unknown): JsonRecord {
1011
+ function checkedRecord(value) {
1139
1012
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
1140
1013
  throw new Error('expected Circuit JSON element object');
1141
1014
  }
1142
1015
  return Object.fromEntries(Object.entries(value));
1143
1016
  }
1144
-
1145
- function stringField(record: JsonRecord, key: string): string | null {
1017
+ function vesselDspPropertySidecar(record) {
1018
+ const sourceComponentIdValue = stringField(record, 'source_component_id');
1019
+ const propertyName = stringField(record, 'property_name');
1020
+ const message = stringField(record, 'message');
1021
+ if (sourceComponentIdValue === null || propertyName === null || message === null) {
1022
+ return null;
1023
+ }
1024
+ if (!message.startsWith(VESSEL_DSP_PROPERTY_JSON_PREFIX)) {
1025
+ return null;
1026
+ }
1027
+ const payload = parseJsonRecord(message.slice(VESSEL_DSP_PROPERTY_JSON_PREFIX.length));
1028
+ if (payload === null || payload.property !== propertyName) {
1029
+ return null;
1030
+ }
1031
+ const value = payload.value;
1032
+ if (!isPropertyValue(value)) {
1033
+ return null;
1034
+ }
1035
+ return {
1036
+ sourceComponentId: sourceComponentIdValue,
1037
+ propertyName,
1038
+ value,
1039
+ };
1040
+ }
1041
+ function parseJsonRecord(source) {
1042
+ try {
1043
+ const parsed = JSON.parse(source);
1044
+ return isJsonRecord(parsed) ? parsed : null;
1045
+ }
1046
+ catch {
1047
+ return null;
1048
+ }
1049
+ }
1050
+ function stringField(record, key) {
1146
1051
  const value = record[key];
1147
1052
  return typeof value === 'string' ? value : null;
1148
1053
  }
1149
-
1150
- function requiredStringField(record: JsonRecord, key: string): string {
1054
+ function requiredStringField(record, key) {
1151
1055
  const value = stringField(record, key);
1152
1056
  if (value === null) {
1153
1057
  throw new Error(`Circuit JSON element is missing string field ${key}`);
1154
1058
  }
1155
1059
  return value;
1156
1060
  }
1157
-
1158
- function numericField(record: JsonRecord, key: string): number | null {
1061
+ function numericField(record, key) {
1159
1062
  const value = record[key];
1160
1063
  return typeof value === 'number' && Number.isFinite(value) ? value : null;
1161
1064
  }
1162
-
1163
- function pointField(record: JsonRecord, key: string): Point | null {
1065
+ function pointField(record, key) {
1164
1066
  const value = record[key];
1165
1067
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
1166
1068
  return null;
@@ -1170,30 +1072,57 @@ function pointField(record: JsonRecord, key: string): Point | null {
1170
1072
  const y = pointRecord.y;
1171
1073
  return typeof x === 'number' && typeof y === 'number' ? { x, y } : null;
1172
1074
  }
1173
-
1174
- function stringArrayField(record: JsonRecord, key: string): readonly string[] {
1075
+ function stringArrayField(record, key) {
1175
1076
  const value = record[key];
1176
- return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : [];
1077
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
1177
1078
  }
1178
-
1179
- function stripKnownPrefix(value: string, prefix: string): string | null {
1079
+ function stripKnownPrefix(value, prefix) {
1180
1080
  return value.startsWith(prefix) ? value.slice(prefix.length) : null;
1181
1081
  }
1182
-
1183
- function sanitizeId(value: string): string {
1082
+ function sanitizeId(value) {
1184
1083
  const sanitized = value.replace(/\s+/g, '-').replace(/[^A-Za-z0-9_-]/g, '');
1185
1084
  return sanitized.length > 0 ? sanitized : 'component';
1186
1085
  }
1187
-
1188
- function terminalNameFromSourcePortId(sourcePortIdValue: string): string {
1086
+ function stableJsonStringify(value) {
1087
+ return JSON.stringify(stableJsonValue(value)) ?? 'null';
1088
+ }
1089
+ function stableJsonValue(value) {
1090
+ if (Array.isArray(value)) {
1091
+ return value.map(stableJsonValue);
1092
+ }
1093
+ if (!isJsonRecord(value)) {
1094
+ return value;
1095
+ }
1096
+ const out = {};
1097
+ for (const key of Object.keys(value).sort()) {
1098
+ out[key] = stableJsonValue(value[key]);
1099
+ }
1100
+ return out;
1101
+ }
1102
+ function isPropertyValue(value) {
1103
+ if (value === null ||
1104
+ typeof value === 'string' ||
1105
+ typeof value === 'boolean' ||
1106
+ (typeof value === 'number' && Number.isFinite(value))) {
1107
+ return true;
1108
+ }
1109
+ if (Array.isArray(value)) {
1110
+ return value.every(isPropertyValue);
1111
+ }
1112
+ if (!isJsonRecord(value)) {
1113
+ return false;
1114
+ }
1115
+ return Object.values(value).every(isPropertyValue);
1116
+ }
1117
+ function isJsonRecord(value) {
1118
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
1119
+ }
1120
+ function terminalNameFromSourcePortId(sourcePortIdValue) {
1189
1121
  const parsed = parseSourcePortId(sourcePortIdValue);
1190
1122
  return parsed?.terminalName ?? sanitizeId(sourcePortIdValue);
1191
1123
  }
1192
-
1193
- function groupPortsByComponent(
1194
- sourcePorts: ReadonlyMap<string, SourcePortRecord>,
1195
- ): ReadonlyMap<string, readonly SourcePortRecord[]> {
1196
- const map = new Map<string, SourcePortRecord[]>();
1124
+ function groupPortsByComponent(sourcePorts) {
1125
+ const map = new Map();
1197
1126
  for (const port of sourcePorts.values()) {
1198
1127
  const existing = map.get(port.componentSourceId);
1199
1128
  if (existing === undefined) {
@@ -1204,20 +1133,15 @@ function groupPortsByComponent(
1204
1133
  }
1205
1134
  return map;
1206
1135
  }
1207
-
1208
- function buildComponentFromCircuitJson(
1209
- sourceComponent: SourceComponentRecord,
1210
- ports: readonly SourcePortRecord[],
1211
- schematicComponents: ReadonlyMap<string, SchematicComponentRecord>,
1212
- schematicPorts: ReadonlyMap<string, SchematicPortRecord>,
1213
- index: number,
1214
- ): MutableComponentBuild & { readonly component: Component } {
1136
+ function buildComponentFromCircuitJson(sourceComponent, ports, schematicComponents, schematicPorts, sourceGroups, sidecars, index) {
1215
1137
  const origin = schematicComponents.get(sourceComponent.sourceComponentId)?.center ?? synthesizedOrigin(index);
1216
1138
  const terminals = ports.map((port, portIndex) => ({
1217
1139
  name: port.terminalName,
1218
1140
  position: schematicPorts.get(port.sourcePortId)?.center ?? synthesizedTerminalPosition(origin, ports.length, portIndex),
1219
1141
  }));
1220
- const component: Component = {
1142
+ const sidecarProperties = propertiesFromSidecars(sidecars);
1143
+ const groupedProperties = canCapPropertiesFromSourceGroup(sourceComponent, sourceGroups, sidecarProperties);
1144
+ const component = {
1221
1145
  id: sourceComponent.componentId,
1222
1146
  kind: kindFromCircuitJsonFtype(sourceComponent.ftype),
1223
1147
  name: sourceComponent.name,
@@ -1225,19 +1149,49 @@ function buildComponentFromCircuitJson(
1225
1149
  rotation: 0,
1226
1150
  flipped: false,
1227
1151
  terminals,
1228
- properties: propertiesFromCircuitJsonComponent(sourceComponent.record),
1152
+ properties: {
1153
+ ...propertiesFromCircuitJsonComponent(sourceComponent.record),
1154
+ ...sidecarProperties,
1155
+ ...groupedProperties,
1156
+ },
1229
1157
  sourceTypeName: sourceComponent.ftype === null ? 'circuit-json:source_component' : `circuit-json:${sourceComponent.ftype}`,
1230
1158
  };
1231
1159
  return { sourceComponent, ports, origin, terminals, component };
1232
1160
  }
1233
-
1234
- function synthesizedOrigin(index: number): Point {
1161
+ function propertiesFromSidecars(sidecars) {
1162
+ const properties = {};
1163
+ for (const sidecar of sidecars) {
1164
+ properties[sidecar.propertyName] = sidecar.value;
1165
+ }
1166
+ return properties;
1167
+ }
1168
+ function canCapPropertiesFromSourceGroup(sourceComponent, sourceGroups, sidecarProperties) {
1169
+ const sourceGroupId = sourceComponent.sourceGroupId;
1170
+ if (sourceGroupId === null) {
1171
+ return {};
1172
+ }
1173
+ if (kindFromCircuitJsonFtype(sourceComponent.ftype) !== 'capacitor') {
1174
+ return {};
1175
+ }
1176
+ const discriminatorGroupId = canCapGroupIdFromSourceGroupId(sourceGroupId);
1177
+ const hasCanCapSection = sidecarProperties.CanCapSection !== undefined;
1178
+ if (discriminatorGroupId === null && !hasCanCapSection) {
1179
+ return {};
1180
+ }
1181
+ const group = sourceGroups.get(sourceGroupId);
1182
+ const fallbackGroupId = stripKnownPrefix(sourceGroupId, 'source_group:') ?? sourceGroupId;
1183
+ const canCapGroupId = discriminatorGroupId ?? fallbackGroupId;
1184
+ return {
1185
+ CanCapGroupId: canCapGroupId,
1186
+ ...(group?.name === null || group?.name === undefined ? {} : { CanCapPackageName: group.name }),
1187
+ };
1188
+ }
1189
+ function synthesizedOrigin(index) {
1235
1190
  const column = index % 4;
1236
1191
  const row = Math.floor(index / 4);
1237
1192
  return { x: column * 80, y: row * 60 };
1238
1193
  }
1239
-
1240
- function synthesizedTerminalPosition(origin: Point, terminalCount: number, index: number): Point {
1194
+ function synthesizedTerminalPosition(origin, terminalCount, index) {
1241
1195
  if (terminalCount <= 1) {
1242
1196
  return origin;
1243
1197
  }
@@ -1249,8 +1203,7 @@ function synthesizedTerminalPosition(origin: Point, terminalCount: number, index
1249
1203
  const step = terminalCount === 1 ? 0 : 40 / (terminalCount - 1);
1250
1204
  return { x: origin.x - 20, y: origin.y - 20 + step * index };
1251
1205
  }
1252
-
1253
- function kindFromCircuitJsonFtype(ftype: string | null): ComponentKind {
1206
+ function kindFromCircuitJsonFtype(ftype) {
1254
1207
  switch (ftype) {
1255
1208
  case 'simple_resistor':
1256
1209
  return 'resistor';
@@ -1293,9 +1246,8 @@ function kindFromCircuitJsonFtype(ftype: string | null): ComponentKind {
1293
1246
  return 'unsupported';
1294
1247
  }
1295
1248
  }
1296
-
1297
- function propertiesFromCircuitJsonComponent(record: JsonRecord): Readonly<Record<string, PropertyValue>> {
1298
- const properties: Record<string, PropertyValue> = {};
1249
+ function propertiesFromCircuitJsonComponent(record) {
1250
+ const properties = {};
1299
1251
  const ftype = stringField(record, 'ftype');
1300
1252
  const displayValue = stringField(record, 'display_value');
1301
1253
  if (displayValue !== null) {
@@ -1316,14 +1268,7 @@ function propertiesFromCircuitJsonComponent(record: JsonRecord): Readonly<Record
1316
1268
  }
1317
1269
  return properties;
1318
1270
  }
1319
-
1320
- function addQuantityProperty(
1321
- properties: Record<string, PropertyValue>,
1322
- propertyName: string,
1323
- record: JsonRecord,
1324
- displayKey: string | null,
1325
- numericKey: string,
1326
- ): void {
1271
+ function addQuantityProperty(properties, propertyName, record, displayKey, numericKey) {
1327
1272
  const display = displayKey === null ? null : stringField(record, displayKey);
1328
1273
  if (display !== null) {
1329
1274
  properties[propertyName] = parseQuantity(display) ?? display;
@@ -1334,11 +1279,8 @@ function addQuantityProperty(
1334
1279
  properties[propertyName] = numeric;
1335
1280
  }
1336
1281
  }
1337
-
1338
- function terminalPositionMap(
1339
- builds: readonly (MutableComponentBuild & { readonly component: Component })[],
1340
- ): ReadonlyMap<string, Point> {
1341
- const map = new Map<string, Point>();
1282
+ function terminalPositionMap(builds) {
1283
+ const map = new Map();
1342
1284
  for (const build of builds) {
1343
1285
  for (const port of build.ports) {
1344
1286
  const terminal = build.component.terminals.find((candidate) => candidate.name === port.terminalName);
@@ -1349,12 +1291,8 @@ function terminalPositionMap(
1349
1291
  }
1350
1292
  return map;
1351
1293
  }
1352
-
1353
- function wireElementsFromSourceTraces(
1354
- sourceTraces: readonly JsonRecord[],
1355
- terminalPositions: ReadonlyMap<string, Point>,
1356
- ): readonly Wire[] {
1357
- const wires: Wire[] = [];
1294
+ function wireElementsFromSourceTraces(sourceTraces, terminalPositions) {
1295
+ const wires = [];
1358
1296
  for (const trace of sourceTraces) {
1359
1297
  const ports = stringArrayField(trace, 'connected_source_port_ids');
1360
1298
  if (ports.length < 2) {
@@ -1378,18 +1316,14 @@ function wireElementsFromSourceTraces(
1378
1316
  }
1379
1317
  return wires;
1380
1318
  }
1381
-
1382
- function warningsForUnconnectedNets(
1383
- sourceNets: ReadonlyMap<string, JsonRecord>,
1384
- sourceTraces: readonly JsonRecord[],
1385
- ): readonly Warning[] {
1386
- const referencedNetIds = new Set<string>();
1319
+ function warningsForUnconnectedNets(sourceNets, sourceTraces) {
1320
+ const referencedNetIds = new Set();
1387
1321
  for (const trace of sourceTraces) {
1388
1322
  for (const netId of stringArrayField(trace, 'connected_source_net_ids')) {
1389
1323
  referencedNetIds.add(netId);
1390
1324
  }
1391
1325
  }
1392
- const warnings: Warning[] = [];
1326
+ const warnings = [];
1393
1327
  for (const [netId, net] of sourceNets) {
1394
1328
  if (!referencedNetIds.has(netId)) {
1395
1329
  warnings.push({
@@ -1400,11 +1334,11 @@ function warningsForUnconnectedNets(
1400
1334
  }
1401
1335
  return warnings;
1402
1336
  }
1403
-
1404
- function filenameWithoutCircuitJsonExtension(filename: string): string {
1337
+ function filenameWithoutCircuitJsonExtension(filename) {
1405
1338
  return filename
1406
1339
  .replace(/\.circuit\.json$/i, '')
1407
1340
  .replace(/\.json$/i, '')
1408
1341
  .replace(/[-_]+/g, ' ')
1409
1342
  .trim();
1410
1343
  }
1344
+ //# sourceMappingURL=serializer.js.map