altium-toolkit 1.1.22 → 1.1.23

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 (99) hide show
  1. package/README.md +34 -5
  2. package/docs/api.md +134 -23
  3. package/docs/model-format.md +174 -20
  4. package/docs/schemas/altium_toolkit/embedded_assets_a1.schema.json +56 -0
  5. package/docs/schemas/altium_toolkit/fixture_coverage_matrix_a1.schema.json +89 -0
  6. package/docs/schemas/altium_toolkit/geometry_bounds_a1.schema.json +86 -0
  7. package/docs/schemas/altium_toolkit/library_catalog_a1.schema.json +65 -0
  8. package/docs/schemas/altium_toolkit/library_diff_a1.schema.json +54 -0
  9. package/docs/schemas/altium_toolkit/library_inspection_a1.schema.json +94 -0
  10. package/docs/schemas/altium_toolkit/library_qa_a1.schema.json +4 -0
  11. package/docs/schemas/altium_toolkit/native_stream_inventory_a1.schema.json +66 -0
  12. package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +511 -1
  13. package/docs/schemas/altium_toolkit/parameter_record_inventory_a1.schema.json +84 -0
  14. package/docs/schemas/altium_toolkit/parser_diagnostics_a1.schema.json +63 -0
  15. package/docs/schemas/altium_toolkit/parser_value_verification_a1.schema.json +74 -0
  16. package/docs/schemas/altium_toolkit/pcb_class_report_a1.schema.json +79 -0
  17. package/docs/schemas/altium_toolkit/pcb_inspection_a1.schema.json +65 -0
  18. package/docs/schemas/altium_toolkit/pcb_net_membership_a1.schema.json +98 -0
  19. package/docs/schemas/altium_toolkit/project_bundle_a1.schema.json +3 -0
  20. package/docs/schemas/altium_toolkit/project_hierarchy_a1.schema.json +79 -0
  21. package/docs/schemas/altium_toolkit/unsupported_features_a1.schema.json +212 -0
  22. package/docs/testing.md +2 -0
  23. package/examples/README.md +21 -0
  24. package/examples/cli-utils.mjs +148 -0
  25. package/examples/corpus-smoke.mjs +523 -0
  26. package/examples/extract-bom.mjs +47 -0
  27. package/examples/generate-pnp.mjs +59 -0
  28. package/examples/inspect-board.mjs +70 -0
  29. package/examples/inspect-schematic.mjs +406 -0
  30. package/examples/library-catalog.mjs +115 -0
  31. package/examples/net-report.mjs +61 -0
  32. package/examples/validate-library.mjs +59 -0
  33. package/package.json +1 -1
  34. package/src/core/BinaryReader.mjs +213 -2
  35. package/src/core/altium/AltiumParser.mjs +352 -14
  36. package/src/core/altium/AltiumUnits.mjs +205 -0
  37. package/src/core/altium/AsciiRecordParser.mjs +9 -0
  38. package/src/core/altium/EmbeddedAssetReportBuilder.mjs +383 -0
  39. package/src/core/altium/FixtureCoverageMatrixBuilder.mjs +304 -0
  40. package/src/core/altium/GeometryBoundsReportBuilder.mjs +935 -0
  41. package/src/core/altium/LibraryCatalogArtifactBuilder.mjs +296 -0
  42. package/src/core/altium/LibraryDiffReportBuilder.mjs +260 -0
  43. package/src/core/altium/LibraryInspectionReportBuilder.mjs +156 -0
  44. package/src/core/altium/LibraryQaReportBuilder.mjs +374 -1
  45. package/src/core/altium/NativeStreamInventoryBuilder.mjs +177 -0
  46. package/src/core/altium/NormalizedModelSchema.mjs +3 -31
  47. package/src/core/altium/ParameterCollection.mjs +431 -0
  48. package/src/core/altium/ParameterRecordInventoryBuilder.mjs +274 -0
  49. package/src/core/altium/ParserCompatibilityFuzzer.mjs +106 -2
  50. package/src/core/altium/ParserDiagnosticNormalizer.mjs +213 -0
  51. package/src/core/altium/ParserErrors.mjs +90 -0
  52. package/src/core/altium/ParserFieldCoverageReportBuilder.mjs +656 -0
  53. package/src/core/altium/ParserUtils.mjs +24 -0
  54. package/src/core/altium/ParserValueVerificationReportBuilder.mjs +323 -0
  55. package/src/core/altium/PcbClassReportBuilder.mjs +366 -0
  56. package/src/core/altium/PcbInspectionReportBuilder.mjs +313 -0
  57. package/src/core/altium/PcbLayerGroups.mjs +308 -0
  58. package/src/core/altium/PcbLayerStackCustomDataParser.mjs +183 -0
  59. package/src/core/altium/PcbLayerStackInterchangeParser.mjs +473 -4
  60. package/src/core/altium/PcbLayerStackReadModelBuilder.mjs +83 -15
  61. package/src/core/altium/PcbLayerStackSourceMetadataParser.mjs +74 -4
  62. package/src/core/altium/PcbLibModelParser.mjs +20 -4
  63. package/src/core/altium/PcbLibStreamExtractor.mjs +49 -6
  64. package/src/core/altium/PcbModelParser.mjs +223 -4
  65. package/src/core/altium/PcbNetMembershipReportBuilder.mjs +270 -0
  66. package/src/core/altium/PcbStreamExtractor.mjs +130 -6
  67. package/src/core/altium/PcbTrackPrimitiveParser.mjs +66 -2
  68. package/src/core/altium/ProjectDesignBundleBuilder.mjs +15 -0
  69. package/src/core/altium/ProjectHierarchyReportBuilder.mjs +660 -0
  70. package/src/core/altium/ProjectNetlistExporter.mjs +2 -0
  71. package/src/core/altium/RawDataPreservationReportBuilder.mjs +348 -0
  72. package/src/core/altium/SchLibModelParser.mjs +840 -0
  73. package/src/core/altium/SchLibStreamExtractor.mjs +586 -0
  74. package/src/core/altium/SchematicBusEntryParser.mjs +3 -2
  75. package/src/core/altium/SchematicCodeSymbolParser.mjs +663 -0
  76. package/src/core/altium/SchematicConnectivityQaBuilder.mjs +177 -2
  77. package/src/core/altium/SchematicDisplayModeCatalogParser.mjs +10 -1
  78. package/src/core/altium/SchematicFieldCoverageReportBuilder.mjs +549 -0
  79. package/src/core/altium/SchematicHarnessParser.mjs +9 -3
  80. package/src/core/altium/SchematicHyperlinkParser.mjs +122 -0
  81. package/src/core/altium/SchematicNetlistBuilder.mjs +271 -8
  82. package/src/core/altium/SchematicOwnershipGraphParser.mjs +102 -3
  83. package/src/core/altium/SchematicPinParser.mjs +12 -45
  84. package/src/core/altium/SchematicPrimitiveParser.mjs +9 -14
  85. package/src/core/altium/SchematicQaReportBuilder.mjs +2 -0
  86. package/src/core/altium/SchematicRecordStreamParser.mjs +183 -0
  87. package/src/core/altium/SchematicRecordTypeRegistry.mjs +6 -1
  88. package/src/core/altium/SchematicSheetParser.mjs +8 -2
  89. package/src/core/altium/SchematicStreamExtractor.mjs +64 -25
  90. package/src/core/altium/SchematicTextOrientationResolver.mjs +76 -0
  91. package/src/core/altium/SchematicTextParser.mjs +28 -12
  92. package/src/core/altium/SchematicTextRunParser.mjs +81 -0
  93. package/src/core/altium/SchematicThumbnailParser.mjs +425 -0
  94. package/src/core/altium/UnsupportedFeatureReportBuilder.mjs +380 -0
  95. package/src/parser.mjs +35 -1
  96. package/src/renderers.mjs +1 -0
  97. package/src/ui/SchematicShapeRenderer.mjs +49 -6
  98. package/src/ui/SchematicSvgRenderer.mjs +37 -8
  99. package/src/ui/SchematicTypography.mjs +4 -3
@@ -0,0 +1,304 @@
1
+ // SPDX-FileCopyrightText: 2026 André Fiedler
2
+ //
3
+ // SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ /**
6
+ * Builds fixture coverage matrices from synthetic fixture manifests.
7
+ */
8
+ export class FixtureCoverageMatrixBuilder {
9
+ static SCHEMA = 'altium-toolkit.fixture-coverage-matrix.a1'
10
+
11
+ /**
12
+ * Builds a fixture coverage matrix report.
13
+ * @param {{ manifest?: object, requiredCoverage?: string[], requiredContracts?: Record<string, string[]> } | object} [input]
14
+ * @returns {object}
15
+ */
16
+ static build(input = {}) {
17
+ const manifest = input.manifest || input
18
+ const fixtures = Array.isArray(manifest.fixtures)
19
+ ? manifest.fixtures
20
+ : []
21
+ const requiredCoverage = FixtureCoverageMatrixBuilder.#stringList(
22
+ input.requiredCoverage
23
+ )
24
+ const requiredContracts =
25
+ FixtureCoverageMatrixBuilder.#requiredContracts(
26
+ input.requiredContracts
27
+ )
28
+ const coverage = FixtureCoverageMatrixBuilder.#coverageRows(
29
+ fixtures,
30
+ requiredCoverage
31
+ )
32
+ const contracts = FixtureCoverageMatrixBuilder.#contractRows(
33
+ fixtures,
34
+ requiredContracts
35
+ )
36
+ const missingCoverage = coverage
37
+ .filter((entry) => entry.required && !entry.covered)
38
+ .map((entry) => entry.tag)
39
+ const missingContracts = contracts
40
+ .filter((entry) => entry.required && !entry.covered)
41
+ .map((entry) => ({
42
+ group: entry.group,
43
+ contract: entry.contract
44
+ }))
45
+ const policy = FixtureCoverageMatrixBuilder.#policy(manifest, fixtures)
46
+
47
+ return {
48
+ schema: FixtureCoverageMatrixBuilder.SCHEMA,
49
+ summary: {
50
+ fixtureCount: fixtures.length,
51
+ coverageTagCount: coverage.length,
52
+ contractCount: contracts.length,
53
+ requiredCoverageCount: requiredCoverage.length,
54
+ requiredContractCount: requiredContracts.length,
55
+ missingCoverageCount: missingCoverage.length,
56
+ missingContractCount: missingContracts.length,
57
+ nativeAssetCount: policy.nativeAssetCount,
58
+ status:
59
+ missingCoverage.length || missingContracts.length
60
+ ? 'gap'
61
+ : 'pass'
62
+ },
63
+ policy,
64
+ coverage,
65
+ contracts,
66
+ missingCoverage,
67
+ missingContracts
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Builds coverage rows.
73
+ * @param {object[]} fixtures Fixture rows.
74
+ * @param {string[]} requiredCoverage Required coverage tags.
75
+ * @returns {object[]}
76
+ */
77
+ static #coverageRows(fixtures, requiredCoverage) {
78
+ const required = new Set(requiredCoverage)
79
+ const fixtureKeysByTag = new Map()
80
+
81
+ for (const fixture of fixtures) {
82
+ for (const tag of FixtureCoverageMatrixBuilder.#stringList(
83
+ fixture.coverage
84
+ )) {
85
+ FixtureCoverageMatrixBuilder.#appendKey(
86
+ fixtureKeysByTag,
87
+ tag,
88
+ FixtureCoverageMatrixBuilder.#fixtureKey(fixture)
89
+ )
90
+ }
91
+ }
92
+
93
+ return FixtureCoverageMatrixBuilder.#sortedUnion(
94
+ [...fixtureKeysByTag.keys()],
95
+ requiredCoverage
96
+ ).map((tag) =>
97
+ FixtureCoverageMatrixBuilder.#coverageRow(
98
+ tag,
99
+ fixtureKeysByTag.get(tag) || new Set(),
100
+ required.has(tag)
101
+ )
102
+ )
103
+ }
104
+
105
+ /**
106
+ * Builds one coverage row.
107
+ * @param {string} tag Coverage tag.
108
+ * @param {Set<string>} fixtureKeys Fixture keys.
109
+ * @param {boolean} required Required flag.
110
+ * @returns {object}
111
+ */
112
+ static #coverageRow(tag, fixtureKeys, required) {
113
+ const keys = [...fixtureKeys].sort()
114
+ return {
115
+ tag,
116
+ fixtureKeys: keys,
117
+ count: keys.length,
118
+ required,
119
+ covered: keys.length > 0
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Builds contract rows.
125
+ * @param {object[]} fixtures Fixture rows.
126
+ * @param {{ group: string, contract: string }[]} requiredContracts Required contracts.
127
+ * @returns {object[]}
128
+ */
129
+ static #contractRows(fixtures, requiredContracts) {
130
+ const required = new Set(
131
+ requiredContracts.map((entry) =>
132
+ FixtureCoverageMatrixBuilder.#contractKey(
133
+ entry.group,
134
+ entry.contract
135
+ )
136
+ )
137
+ )
138
+ const fixtureKeysByContract = new Map()
139
+
140
+ for (const fixture of fixtures) {
141
+ const contracts = fixture.contracts || {}
142
+ for (const [group, groupContracts] of Object.entries(contracts)) {
143
+ for (const contract of FixtureCoverageMatrixBuilder.#stringList(
144
+ groupContracts
145
+ )) {
146
+ FixtureCoverageMatrixBuilder.#appendKey(
147
+ fixtureKeysByContract,
148
+ FixtureCoverageMatrixBuilder.#contractKey(
149
+ group,
150
+ contract
151
+ ),
152
+ FixtureCoverageMatrixBuilder.#fixtureKey(fixture)
153
+ )
154
+ }
155
+ }
156
+ }
157
+
158
+ const allKeys = FixtureCoverageMatrixBuilder.#sortedUnion(
159
+ [...fixtureKeysByContract.keys()],
160
+ requiredContracts.map((entry) =>
161
+ FixtureCoverageMatrixBuilder.#contractKey(
162
+ entry.group,
163
+ entry.contract
164
+ )
165
+ )
166
+ )
167
+
168
+ return allKeys.map((key) => {
169
+ const [group, contract] =
170
+ FixtureCoverageMatrixBuilder.#splitContractKey(key)
171
+ const fixtureKeys = fixtureKeysByContract.get(key) || new Set()
172
+ return FixtureCoverageMatrixBuilder.#contractRow(
173
+ group,
174
+ contract,
175
+ fixtureKeys,
176
+ required.has(key)
177
+ )
178
+ })
179
+ }
180
+
181
+ /**
182
+ * Builds one contract row.
183
+ * @param {string} group Contract group.
184
+ * @param {string} contract Contract name.
185
+ * @param {Set<string>} fixtureKeys Fixture keys.
186
+ * @param {boolean} required Required flag.
187
+ * @returns {object}
188
+ */
189
+ static #contractRow(group, contract, fixtureKeys, required) {
190
+ const keys = [...fixtureKeys].sort()
191
+ return {
192
+ group,
193
+ contract,
194
+ fixtureKeys: keys,
195
+ count: keys.length,
196
+ required,
197
+ covered: keys.length > 0
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Normalizes required contract input.
203
+ * @param {Record<string, string[]> | undefined} requiredContracts Required contract groups.
204
+ * @returns {{ group: string, contract: string }[]}
205
+ */
206
+ static #requiredContracts(requiredContracts) {
207
+ return Object.entries(requiredContracts || {}).flatMap(
208
+ ([group, contracts]) =>
209
+ FixtureCoverageMatrixBuilder.#stringList(contracts).map(
210
+ (contract) => ({ group, contract })
211
+ )
212
+ )
213
+ }
214
+
215
+ /**
216
+ * Builds fixture policy metadata.
217
+ * @param {object} manifest Fixture manifest.
218
+ * @param {object[]} fixtures Fixture rows.
219
+ * @returns {{ assetPolicy: string, nativeAssetCount: number, compliant: boolean }}
220
+ */
221
+ static #policy(manifest, fixtures) {
222
+ const assetPolicy = String(manifest.assetPolicy || '')
223
+ const nativeAssetCount = fixtures.filter(
224
+ (fixture) => fixture.nativeAsset
225
+ ).length
226
+ const compliant =
227
+ nativeAssetCount === 0 &&
228
+ fixtures.every(
229
+ (fixture) =>
230
+ !fixture.nativeAsset &&
231
+ (!assetPolicy || fixture.assetPolicy === assetPolicy)
232
+ )
233
+
234
+ return {
235
+ assetPolicy,
236
+ nativeAssetCount,
237
+ compliant
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Appends one fixture key to a map of sets.
243
+ * @param {Map<string, Set<string>>} map Destination map.
244
+ * @param {string} tag Map key.
245
+ * @param {string} fixtureKey Fixture key.
246
+ * @returns {void}
247
+ */
248
+ static #appendKey(map, tag, fixtureKey) {
249
+ if (!tag) return
250
+ map.set(tag, new Set([...(map.get(tag) || []), fixtureKey]))
251
+ }
252
+
253
+ /**
254
+ * Returns a fixture key.
255
+ * @param {object} fixture Fixture row.
256
+ * @returns {string}
257
+ */
258
+ static #fixtureKey(fixture) {
259
+ return String(fixture.key || fixture.id || fixture.name || '')
260
+ }
261
+
262
+ /**
263
+ * Builds a stable contract key.
264
+ * @param {string} group Contract group.
265
+ * @param {string} contract Contract name.
266
+ * @returns {string}
267
+ */
268
+ static #contractKey(group, contract) {
269
+ return String(group || '') + '\u0000' + String(contract || '')
270
+ }
271
+
272
+ /**
273
+ * Splits one stable contract key.
274
+ * @param {string} key Contract key.
275
+ * @returns {[string, string]}
276
+ */
277
+ static #splitContractKey(key) {
278
+ const [group, contract] = String(key).split('\u0000')
279
+ return [group || '', contract || '']
280
+ }
281
+
282
+ /**
283
+ * Builds a sorted unique array from two lists.
284
+ * @param {string[]} first First values.
285
+ * @param {string[]} second Second values.
286
+ * @returns {string[]}
287
+ */
288
+ static #sortedUnion(first, second) {
289
+ return [...new Set([...first, ...second])]
290
+ .filter(Boolean)
291
+ .sort((left, right) => left.localeCompare(right))
292
+ }
293
+
294
+ /**
295
+ * Normalizes string-list input.
296
+ * @param {unknown} value Source value.
297
+ * @returns {string[]}
298
+ */
299
+ static #stringList(value) {
300
+ return Array.isArray(value)
301
+ ? value.map((entry) => String(entry)).filter(Boolean)
302
+ : []
303
+ }
304
+ }