altium-toolkit 1.0.10 → 1.1.0
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.
- package/docs/api.md +6 -2
- package/docs/model-format.md +29 -4
- package/docs/schemas/altium_toolkit/ci_artifact_bundle_a1.schema.json +4 -0
- package/docs/schemas/altium_toolkit/contract_gate_a1.schema.json +34 -0
- package/docs/schemas/altium_toolkit/draftsman_board_view_cache_a1.schema.json +115 -0
- package/docs/schemas/altium_toolkit/draftsman_digest_a1.schema.json +132 -1
- package/docs/schemas/altium_toolkit/host_capabilities_a1.schema.json +39 -0
- package/docs/schemas/altium_toolkit/library_merge_plan_a1.schema.json +56 -0
- package/docs/schemas/altium_toolkit/library_qa_a1.schema.json +70 -0
- package/docs/schemas/altium_toolkit/normalized_model_a1.schema.json +692 -2
- package/docs/schemas/altium_toolkit/pcb_bom_profile_a1.schema.json +48 -0
- package/docs/schemas/altium_toolkit/pcb_layer_stack_a1.schema.json +98 -0
- package/docs/schemas/altium_toolkit/pcb_layer_stack_fidelity_a1.schema.json +66 -0
- package/docs/schemas/altium_toolkit/pcb_placed_footprint_extraction_a1.schema.json +31 -0
- package/docs/schemas/altium_toolkit/pcb_review_metadata_a1.schema.json +62 -0
- package/docs/schemas/altium_toolkit/pcb_rigid_flex_topology_a1.schema.json +52 -0
- package/docs/schemas/altium_toolkit/pcblib_parity_a1.schema.json +24 -0
- package/docs/schemas/altium_toolkit/project_bom_pnp_reconciliation_a1.schema.json +63 -0
- package/docs/schemas/altium_toolkit/project_outjob_digest_a1.schema.json +46 -0
- package/docs/schemas/altium_toolkit/project_script_a1.schema.json +50 -0
- package/docs/schemas/altium_toolkit/schematic_render_ops_a1.schema.json +55 -0
- package/docs/schemas/altium_toolkit/schematic_template_extraction_a1.schema.json +37 -0
- package/package.json +1 -1
- package/src/core/altium/AltiumParser.mjs +7 -2
- package/src/core/altium/CiArtifactBundleBuilder.mjs +16 -5
- package/src/core/altium/ContractGateReportBuilder.mjs +351 -0
- package/src/core/altium/DraftsmanBoardViewMetadataBuilder.mjs +653 -0
- package/src/core/altium/DraftsmanDigestParser.mjs +246 -7
- package/src/core/altium/DraftsmanImagePayloadManifestBuilder.mjs +178 -0
- package/src/core/altium/HostCapabilityDiagnosticsBuilder.mjs +271 -0
- package/src/core/altium/LibraryQaReportBuilder.mjs +504 -0
- package/src/core/altium/LibraryRenderManifestBuilder.mjs +172 -2
- package/src/core/altium/PcbBomProfileBuilder.mjs +263 -0
- package/src/core/altium/PcbComponentKindPolicy.mjs +146 -0
- package/src/core/altium/PcbLayerStackFidelityReportBuilder.mjs +141 -0
- package/src/core/altium/PcbLayerStackInterchangeParser.mjs +453 -0
- package/src/core/altium/PcbLayerStackQueryHelper.mjs +195 -0
- package/src/core/altium/PcbLayerStackReadModelBuilder.mjs +906 -0
- package/src/core/altium/PcbLayerStackSourceMetadataParser.mjs +488 -0
- package/src/core/altium/PcbLibModelParser.mjs +2 -0
- package/src/core/altium/PcbLibParityReportBuilder.mjs +242 -0
- package/src/core/altium/PcbModelParser.mjs +182 -18
- package/src/core/altium/PcbPickPlacePositionResolver.mjs +3 -0
- package/src/core/altium/PcbPlacedFootprintManifestBuilder.mjs +338 -0
- package/src/core/altium/PcbPolygonRecordParser.mjs +120 -0
- package/src/core/altium/PcbReviewDrillMetadataBuilder.mjs +301 -0
- package/src/core/altium/PcbReviewMetadataBuilder.mjs +373 -0
- package/src/core/altium/PcbReviewPolygonRealizationBuilder.mjs +269 -0
- package/src/core/altium/PcbReviewRouteHighlightProfileBuilder.mjs +298 -0
- package/src/core/altium/PcbRigidFlexTopologyBuilder.mjs +171 -0
- package/src/core/altium/PrintableTextDecoder.mjs +70 -6
- package/src/core/altium/PrjPcbModelParser.mjs +45 -0
- package/src/core/altium/PrjScrModelParser.mjs +386 -0
- package/src/core/altium/ProjectBomPnpReconciliationBuilder.mjs +237 -0
- package/src/core/altium/ProjectDesignBundleBuilder.mjs +61 -2
- package/src/core/altium/ProjectOutJobDigestBuilder.mjs +424 -13
- package/src/core/altium/SvgModelCrossLinkValidator.mjs +35 -2
- package/src/core/circuit-json/CircuitJsonModelAdapter.mjs +164 -0
- package/src/parser.mjs +15 -0
- package/src/ui/PcbFootprintPrimitiveSelector.mjs +13 -1
- package/src/ui/PcbScene3dBuilder.mjs +26 -4
- package/src/ui/SchematicRenderOpsSidecarBuilder.mjs +554 -0
- package/src/ui/SchematicSvgRenderer.mjs +48 -2
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 André Fiedler
|
|
2
|
+
//
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds deterministic host capability and fallback diagnostics.
|
|
7
|
+
*/
|
|
8
|
+
export class HostCapabilityDiagnosticsBuilder {
|
|
9
|
+
static SCHEMA = 'altium-toolkit.host-capabilities.a1'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Builds a host capability diagnostics report.
|
|
13
|
+
* @param {{ host?: object, capabilities?: Record<string, boolean>, fallbacks?: object[] }} options Diagnostics options.
|
|
14
|
+
* @returns {object}
|
|
15
|
+
*/
|
|
16
|
+
static build(options = {}) {
|
|
17
|
+
const capabilities = HostCapabilityDiagnosticsBuilder.#capabilityRows(
|
|
18
|
+
options.capabilities || {}
|
|
19
|
+
)
|
|
20
|
+
const diagnostics = [
|
|
21
|
+
...HostCapabilityDiagnosticsBuilder.#capabilityDiagnostics(
|
|
22
|
+
capabilities
|
|
23
|
+
),
|
|
24
|
+
...HostCapabilityDiagnosticsBuilder.#fallbackDiagnostics(
|
|
25
|
+
options.fallbacks || []
|
|
26
|
+
)
|
|
27
|
+
]
|
|
28
|
+
const readiness = HostCapabilityDiagnosticsBuilder.#readiness(
|
|
29
|
+
options.readinessCategories || [],
|
|
30
|
+
capabilities,
|
|
31
|
+
diagnostics
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return HostCapabilityDiagnosticsBuilder.#stripUndefined({
|
|
35
|
+
schema: HostCapabilityDiagnosticsBuilder.SCHEMA,
|
|
36
|
+
host: options.host || {},
|
|
37
|
+
summary: HostCapabilityDiagnosticsBuilder.#stripUndefined({
|
|
38
|
+
capabilityCount: capabilities.length,
|
|
39
|
+
unsupportedCapabilityCount: capabilities.filter(
|
|
40
|
+
(capability) => !capability.supported
|
|
41
|
+
).length,
|
|
42
|
+
fallbackCount: (options.fallbacks || []).length,
|
|
43
|
+
readinessStatus: readiness?.status,
|
|
44
|
+
readinessCategoryCount: readiness?.categories.length,
|
|
45
|
+
warningCount: diagnostics.filter(
|
|
46
|
+
(diagnostic) => diagnostic.severity === 'warning'
|
|
47
|
+
).length
|
|
48
|
+
}),
|
|
49
|
+
capabilities,
|
|
50
|
+
readiness,
|
|
51
|
+
diagnostics
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Builds sorted capability rows.
|
|
57
|
+
* @param {Record<string, boolean>} capabilities Capability map.
|
|
58
|
+
* @returns {object[]}
|
|
59
|
+
*/
|
|
60
|
+
static #capabilityRows(capabilities) {
|
|
61
|
+
return Object.keys(capabilities || {})
|
|
62
|
+
.sort((left, right) =>
|
|
63
|
+
left.localeCompare(right, undefined, { numeric: true })
|
|
64
|
+
)
|
|
65
|
+
.map((key) => {
|
|
66
|
+
const supported = capabilities[key] === true
|
|
67
|
+
return HostCapabilityDiagnosticsBuilder.#stripUndefined({
|
|
68
|
+
key,
|
|
69
|
+
supported,
|
|
70
|
+
diagnosticCode: supported
|
|
71
|
+
? undefined
|
|
72
|
+
: HostCapabilityDiagnosticsBuilder.#capabilityCode(key)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Builds diagnostics for unsupported capabilities.
|
|
79
|
+
* @param {object[]} capabilities Capability rows.
|
|
80
|
+
* @returns {object[]}
|
|
81
|
+
*/
|
|
82
|
+
static #capabilityDiagnostics(capabilities) {
|
|
83
|
+
return capabilities
|
|
84
|
+
.filter((capability) => !capability.supported)
|
|
85
|
+
.map((capability) => ({
|
|
86
|
+
code: capability.diagnosticCode,
|
|
87
|
+
severity: 'warning',
|
|
88
|
+
capability: capability.key,
|
|
89
|
+
message:
|
|
90
|
+
'Host capability ' + capability.key + ' is unavailable.'
|
|
91
|
+
}))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Builds diagnostics for caller-supplied fallback decisions.
|
|
96
|
+
* @param {object[]} fallbacks Fallback rows.
|
|
97
|
+
* @returns {object[]}
|
|
98
|
+
*/
|
|
99
|
+
static #fallbackDiagnostics(fallbacks) {
|
|
100
|
+
return (fallbacks || []).map((fallback) =>
|
|
101
|
+
HostCapabilityDiagnosticsBuilder.#stripUndefined({
|
|
102
|
+
...fallback,
|
|
103
|
+
code: fallback.code || 'host.fallback.used',
|
|
104
|
+
severity: fallback.severity || 'info',
|
|
105
|
+
message:
|
|
106
|
+
fallback.message ||
|
|
107
|
+
'Host fallback ' +
|
|
108
|
+
(fallback.code || 'host.fallback.used') +
|
|
109
|
+
' was used.'
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Builds host support readiness groups.
|
|
116
|
+
* @param {object[]} categories Readiness category descriptors.
|
|
117
|
+
* @param {object[]} capabilities Capability rows.
|
|
118
|
+
* @param {object[]} diagnostics Diagnostic rows.
|
|
119
|
+
* @returns {object | undefined}
|
|
120
|
+
*/
|
|
121
|
+
static #readiness(categories, capabilities, diagnostics) {
|
|
122
|
+
if (!categories.length) {
|
|
123
|
+
return undefined
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const capabilityByKey = new Map(
|
|
127
|
+
capabilities.map((capability) => [capability.key, capability])
|
|
128
|
+
)
|
|
129
|
+
const categoryRows = categories.map((category) =>
|
|
130
|
+
HostCapabilityDiagnosticsBuilder.#readinessCategory(
|
|
131
|
+
category,
|
|
132
|
+
capabilityByKey,
|
|
133
|
+
diagnostics
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
status: HostCapabilityDiagnosticsBuilder.#readinessStatus(
|
|
139
|
+
categoryRows
|
|
140
|
+
),
|
|
141
|
+
categories: categoryRows
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Builds one readiness category.
|
|
147
|
+
* @param {object} category Category descriptor.
|
|
148
|
+
* @param {Map<string, object>} capabilityByKey Capability lookup.
|
|
149
|
+
* @param {object[]} diagnostics Diagnostic rows.
|
|
150
|
+
* @returns {object}
|
|
151
|
+
*/
|
|
152
|
+
static #readinessCategory(category, capabilityByKey, diagnostics) {
|
|
153
|
+
const capabilityKeys = [...(category.capabilityKeys || [])]
|
|
154
|
+
const capabilityRows = capabilityKeys
|
|
155
|
+
.map((key) => capabilityByKey.get(key))
|
|
156
|
+
.filter(Boolean)
|
|
157
|
+
const unsupportedCapabilities = capabilityRows.filter(
|
|
158
|
+
(capability) => !capability.supported
|
|
159
|
+
)
|
|
160
|
+
const categoryDiagnostics =
|
|
161
|
+
HostCapabilityDiagnosticsBuilder.#categoryDiagnostics(
|
|
162
|
+
category.key,
|
|
163
|
+
unsupportedCapabilities,
|
|
164
|
+
diagnostics
|
|
165
|
+
)
|
|
166
|
+
const fallbackCount = categoryDiagnostics.filter(
|
|
167
|
+
(diagnostic) => diagnostic.category === category.key
|
|
168
|
+
).length
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
key: category.key,
|
|
172
|
+
displayName: category.displayName || category.key,
|
|
173
|
+
status: HostCapabilityDiagnosticsBuilder.#categoryStatus(
|
|
174
|
+
capabilityRows,
|
|
175
|
+
fallbackCount
|
|
176
|
+
),
|
|
177
|
+
capabilityKeys,
|
|
178
|
+
supportedCapabilityCount: capabilityRows.filter(
|
|
179
|
+
(capability) => capability.supported
|
|
180
|
+
).length,
|
|
181
|
+
unsupportedCapabilityCount: unsupportedCapabilities.length,
|
|
182
|
+
fallbackCount,
|
|
183
|
+
diagnosticCodes: categoryDiagnostics.map(
|
|
184
|
+
(diagnostic) => diagnostic.code
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Returns diagnostics related to one readiness category.
|
|
191
|
+
* @param {string} categoryKey Category key.
|
|
192
|
+
* @param {object[]} unsupportedCapabilities Unsupported capabilities.
|
|
193
|
+
* @param {object[]} diagnostics Diagnostic rows.
|
|
194
|
+
* @returns {object[]}
|
|
195
|
+
*/
|
|
196
|
+
static #categoryDiagnostics(
|
|
197
|
+
categoryKey,
|
|
198
|
+
unsupportedCapabilities,
|
|
199
|
+
diagnostics
|
|
200
|
+
) {
|
|
201
|
+
const unsupportedCodes = new Set(
|
|
202
|
+
unsupportedCapabilities.map(
|
|
203
|
+
(capability) => capability.diagnosticCode
|
|
204
|
+
)
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
return diagnostics.filter(
|
|
208
|
+
(diagnostic) =>
|
|
209
|
+
unsupportedCodes.has(diagnostic.code) ||
|
|
210
|
+
diagnostic.category === categoryKey
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Resolves one readiness category status.
|
|
216
|
+
* @param {object[]} capabilityRows Capability rows.
|
|
217
|
+
* @param {number} fallbackCount Fallback count.
|
|
218
|
+
* @returns {'supported' | 'limited' | 'unsupported'}
|
|
219
|
+
*/
|
|
220
|
+
static #categoryStatus(capabilityRows, fallbackCount) {
|
|
221
|
+
const supportedCount = capabilityRows.filter(
|
|
222
|
+
(capability) => capability.supported
|
|
223
|
+
).length
|
|
224
|
+
const unsupportedCount = capabilityRows.length - supportedCount
|
|
225
|
+
|
|
226
|
+
if (capabilityRows.length > 0 && supportedCount === 0) {
|
|
227
|
+
return 'unsupported'
|
|
228
|
+
}
|
|
229
|
+
if (unsupportedCount > 0 || fallbackCount > 0) {
|
|
230
|
+
return 'limited'
|
|
231
|
+
}
|
|
232
|
+
return 'supported'
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Resolves the aggregate readiness status.
|
|
237
|
+
* @param {object[]} categories Readiness category rows.
|
|
238
|
+
* @returns {'supported' | 'limited' | 'unsupported'}
|
|
239
|
+
*/
|
|
240
|
+
static #readinessStatus(categories) {
|
|
241
|
+
if (categories.every((category) => category.status === 'supported')) {
|
|
242
|
+
return 'supported'
|
|
243
|
+
}
|
|
244
|
+
if (categories.every((category) => category.status === 'unsupported')) {
|
|
245
|
+
return 'unsupported'
|
|
246
|
+
}
|
|
247
|
+
return 'limited'
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Builds the diagnostic code for an unsupported capability.
|
|
252
|
+
* @param {string} key Capability key.
|
|
253
|
+
* @returns {string}
|
|
254
|
+
*/
|
|
255
|
+
static #capabilityCode(key) {
|
|
256
|
+
return 'host.capability.' + key + '.unsupported'
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Removes undefined fields.
|
|
261
|
+
* @param {Record<string, unknown>} value Candidate object.
|
|
262
|
+
* @returns {Record<string, unknown>}
|
|
263
|
+
*/
|
|
264
|
+
static #stripUndefined(value) {
|
|
265
|
+
return Object.fromEntries(
|
|
266
|
+
Object.entries(value || {}).filter(
|
|
267
|
+
([, entryValue]) => entryValue !== undefined
|
|
268
|
+
)
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
}
|