circuitjson-toolkit 1.0.16 → 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/AGENTS.md +15 -0
- package/NOTICE.md +31 -0
- package/README.md +267 -107
- package/docs/api.md +501 -96
- package/docs/capabilities.md +70 -0
- package/docs/migration/behaviors.md +45 -0
- package/docs/migration/parser.md +60 -0
- package/docs/migration/renderers.md +515 -0
- package/docs/migration/root.md +740 -0
- package/docs/migration.md +120 -0
- package/docs/model-format.md +175 -57
- package/docs/provenance.md +206 -0
- package/docs/release-notes-v1.1.0.md +154 -0
- package/docs/testing.md +117 -7
- package/package.json +31 -5
- package/spec/api-baseline-v1.0.17.json +1 -0
- package/spec/baseline-provenance-v1.0.17.json +7 -0
- package/spec/circuitjson-schema-snapshot.json +321 -0
- package/spec/circuitjson-schema-source.json +28 -0
- package/spec/feature-preservation.json +1 -0
- package/spec/library-scope.md +27 -20
- package/src/capabilities.mjs +1 -0
- package/src/core/ArchiveEntryPath.mjs +93 -0
- package/src/core/ArchiveLimits.mjs +31 -0
- package/src/core/ArchiveLimitsValidator.mjs +107 -0
- package/src/core/AsyncInputOwnership.mjs +56 -0
- package/src/core/AttachedValueLimits.mjs +67 -0
- package/src/core/CircuitJsonDiagnosticIndexer.mjs +184 -0
- package/src/core/CircuitJsonDocument.mjs +19 -61
- package/src/core/CircuitJsonElementTypes.mjs +10 -0
- package/src/core/CircuitJsonElementValidator.mjs +98 -847
- package/src/core/CircuitJsonIndexer.mjs +274 -194
- package/src/core/CircuitJsonManufacturingBuilder.mjs +167 -164
- package/src/core/CircuitJsonParser.mjs +75 -13
- package/src/core/CircuitJsonPcbClearanceDiagnostics.mjs +12 -6
- package/src/core/CircuitJsonPcbHolePrimitiveModel.mjs +108 -10
- package/src/core/CircuitJsonPcbPadPrimitiveModel.mjs +1 -1
- package/src/core/CircuitJsonPcbPrimitiveArtwork.mjs +44 -38
- package/src/core/CircuitJsonPcbPrimitiveBuilder.mjs +146 -28
- package/src/core/CircuitJsonPcbPrimitiveFields.mjs +70 -4
- package/src/core/CircuitJsonPcbPrimitiveIndex.mjs +18 -2
- package/src/core/CircuitJsonPcbPrimitiveOverlays.mjs +26 -9
- package/src/core/CircuitJsonPcbZonePrimitiveBuilder.mjs +7 -6
- package/src/core/CircuitJsonSerializedInputAudit.mjs +87 -0
- package/src/core/CircuitJsonSourceMetadata.mjs +5 -1
- package/src/core/CircuitJsonSupportMatrixBuilder.mjs +3 -1
- package/src/core/CircuitJsonToolkitElementSchema.mjs +218 -0
- package/src/core/CircuitJsonUnitParsers.mjs +101 -0
- package/src/core/CircuitJsonUnits.mjs +13 -87
- package/src/core/CircuitJsonUpstreamSchema.mjs +9 -0
- package/src/core/CircuitJsonUpstreamValidator.mjs +418 -0
- package/src/core/CircuitJsonValidationUnits.mjs +6 -0
- package/src/core/ManufacturingService.mjs +323 -0
- package/src/core/Parser.mjs +343 -0
- package/src/core/ParserOptions.mjs +333 -0
- package/src/core/PcbBoundsSelectionModel.mjs +55 -19
- package/src/core/PcbDiagnosticFocusModel.mjs +42 -11
- package/src/core/PcbInteractionIndex.mjs +368 -0
- package/src/core/PcbInteractionPrimitiveModel.mjs +393 -62
- package/src/core/ProjectAsyncInputOwner.mjs +70 -0
- package/src/core/ProjectLoader.mjs +975 -0
- package/src/core/SimulationService.mjs +790 -0
- package/src/core/ToolkitCapabilities.mjs +130 -0
- package/src/core/ZipArchiveInspector.mjs +649 -0
- package/src/core/context/BinaryDataSnapshot.mjs +217 -0
- package/src/core/context/CircuitJsonContextIndexes.mjs +96 -0
- package/src/core/context/CircuitJsonDerivedCache.mjs +114 -0
- package/src/core/context/CircuitJsonDocumentContext.mjs +353 -0
- package/src/core/context/CircuitJsonLegacyModel.mjs +147 -0
- package/src/core/context/CircuitJsonLegacyNormalizer.mjs +847 -0
- package/src/core/context/CircuitJsonMetadataBoundary.mjs +76 -0
- package/src/core/context/CircuitJsonModelFreezeTraversal.mjs +179 -0
- package/src/core/context/CircuitJsonReadOnlyDocument.mjs +920 -0
- package/src/core/context/CircuitJsonSchematicTableNormalizer.mjs +314 -0
- package/src/core/context/CircuitJsonValidationAuthority.mjs +39 -0
- package/src/core/context/CircuitJsonValidationProof.mjs +217 -0
- package/src/core/context/PcbPrimitivePreparation.mjs +198 -0
- package/src/core/context/PcbSpatialIndex.mjs +701 -0
- package/src/core/context/ProtectedExtensionBinaryBoundary.mjs +128 -0
- package/src/core/context/StructuredDataSnapshot.mjs +683 -0
- package/src/core/contracts/DocumentResult.mjs +198 -0
- package/src/core/contracts/ProjectResult.mjs +96 -0
- package/src/core/contracts/RuntimeProxyBoundary.mjs +48 -0
- package/src/core/contracts/ToolkitAsset.mjs +493 -0
- package/src/core/contracts/ToolkitDiagnostic.mjs +38 -0
- package/src/core/contracts/ToolkitError.mjs +176 -0
- package/src/core/contracts/ToolkitProgress.mjs +89 -0
- package/src/core/interaction/CanonicalInteractionOptions.mjs +246 -0
- package/src/core/interaction/PcbInteractionBounds.mjs +167 -0
- package/src/core/query/CircuitTraversal.mjs +343 -0
- package/src/core/query/ComponentGrouping.mjs +275 -0
- package/src/core/query/QueryNetlistBuilder.mjs +306 -0
- package/src/core/query/QueryService.mjs +435 -0
- package/src/core/query/RegexPattern.mjs +75 -0
- package/src/core/rendering/CanonicalBomOrder.mjs +81 -0
- package/src/core/rendering/CanonicalBomRows.mjs +92 -0
- package/src/core/rendering/CanonicalRenderOptions.mjs +498 -0
- package/src/core/rendering/CanonicalSvgDocument.mjs +102 -0
- package/src/core/rendering/PcbRenderPlan.mjs +429 -0
- package/src/core/rendering/SchematicSheetSelector.mjs +335 -0
- package/src/core/scene3d/PcbScene3dBuilder.mjs +906 -0
- package/src/core/scene3d/PcbScene3dPreparator.mjs +47 -0
- package/src/core/scene3d/Scene3dAssetIndex.mjs +284 -0
- package/src/core/scene3d/Scene3dBoardModel.mjs +596 -0
- package/src/core/scene3d/Scene3dDocumentMetadata.mjs +167 -0
- package/src/core/scene3d/Scene3dFreeze.mjs +37 -0
- package/src/core/scene3d/Scene3dIdRegistry.mjs +34 -0
- package/src/core/scene3d/Scene3dInputPreflight.mjs +193 -0
- package/src/core/scene3d/Scene3dMaterials.mjs +58 -0
- package/src/core/scene3d/Scene3dModelReference.mjs +134 -0
- package/src/core/scene3d/Scene3dOptions.mjs +363 -0
- package/src/core/scene3d/SceneAssetResolver.mjs +441 -0
- package/src/core/simulation/SimulationParameterCloner.mjs +543 -0
- package/src/core/worker/ParserWorkerClient.mjs +997 -0
- package/src/core/worker/ToolkitWorkerProtocol.mjs +412 -0
- package/src/core/worker/WorkerRequestData.mjs +623 -0
- package/src/core/worker/WorkerResponseData.mjs +905 -0
- package/src/extensions.mjs +37 -0
- package/src/index.mjs +14 -9
- package/src/interaction.mjs +2 -0
- package/src/manufacturing.mjs +1 -0
- package/src/parser.mjs +12 -2
- package/src/project.mjs +5 -0
- package/src/query.mjs +1 -0
- package/src/renderers.mjs +3 -29
- package/src/scene3d.mjs +3 -0
- package/src/simulation.mjs +1 -0
- package/src/styles/renderers.css +24 -0
- package/src/testing/ToolkitContractFixtures.mjs +124 -0
- package/src/testing/ToolkitLoopbackWorker.mjs +174 -0
- package/src/testing/runToolkitContract.mjs +705 -0
- package/src/testing.mjs +3 -0
- package/src/ui/BomTableRenderer.mjs +304 -0
- package/src/ui/CircuitJsonPcbBoardSvgRenderer.mjs +80 -0
- package/src/ui/CircuitJsonPcbPrimitiveAttributeRenderer.mjs +3 -5
- package/src/ui/CircuitJsonPcbSvgRenderer.mjs +63 -43
- package/src/ui/CircuitJsonPcbViaSvgRenderer.mjs +3 -5
- package/src/ui/CircuitJsonSchematicDebugRenderer.mjs +164 -0
- package/src/ui/CircuitJsonSchematicImageSvgRenderer.mjs +210 -0
- package/src/ui/CircuitJsonSchematicLineRenderer.mjs +86 -0
- package/src/ui/CircuitJsonSchematicSheetSymbolSvgRenderer.mjs +98 -0
- package/src/ui/CircuitJsonSchematicSvgArcPath.mjs +117 -17
- package/src/ui/CircuitJsonSchematicSvgPortMetadata.mjs +67 -20
- package/src/ui/CircuitJsonSchematicSvgPrimitiveAttributes.mjs +45 -9
- package/src/ui/CircuitJsonSchematicSvgRenderer.mjs +151 -148
- package/src/ui/CircuitJsonSchematicTableSvgRenderer.mjs +4 -292
- package/src/ui/PcbSvgRenderer.mjs +41 -0
- package/src/ui/SafeSvgPaint.mjs +26 -0
- package/src/ui/SafeXmlText.mjs +60 -0
- package/src/ui/SchematicGeometryBounds.mjs +540 -0
- package/src/ui/SchematicSvgRenderer.mjs +110 -0
- package/src/ui/SchematicTableGeometry.mjs +319 -0
- package/src/ui/SchematicTextAnchor.mjs +55 -0
- package/src/ui/SchematicTextBounds.mjs +98 -0
- package/src/workers/parser.worker.mjs +59 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { ToolkitDiagnostic } from '../contracts/ToolkitDiagnostic.mjs'
|
|
2
|
+
import { ToolkitError } from '../contracts/ToolkitError.mjs'
|
|
3
|
+
import { ComponentGrouping } from './ComponentGrouping.mjs'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Builds one clone-safe query netlist from prepared CircuitJSON indexes.
|
|
7
|
+
*/
|
|
8
|
+
export class QueryNetlistBuilder {
|
|
9
|
+
/**
|
|
10
|
+
* Builds the canonical query netlist for one document context.
|
|
11
|
+
* @param {import('../context/CircuitJsonDocumentContext.mjs').CircuitJsonDocumentContext} context Prepared document context.
|
|
12
|
+
* @returns {Record<string, any>} Canonical query netlist.
|
|
13
|
+
*/
|
|
14
|
+
static build(context) {
|
|
15
|
+
const elements = context.getIndex('elements')
|
|
16
|
+
const relations = context.getIndex('relations')
|
|
17
|
+
const connectivity = context.getIndex('connectivity')
|
|
18
|
+
const portsById = new Map(
|
|
19
|
+
(elements.elementsByType.get('source_port') || []).map((port) => [
|
|
20
|
+
String(port.source_port_id || '').trim(),
|
|
21
|
+
port
|
|
22
|
+
])
|
|
23
|
+
)
|
|
24
|
+
const internalConnections =
|
|
25
|
+
QueryNetlistBuilder.#internalConnections(elements)
|
|
26
|
+
const internalConnectionIdsByPort =
|
|
27
|
+
QueryNetlistBuilder.#internalConnectionIdsByPort(
|
|
28
|
+
internalConnections
|
|
29
|
+
)
|
|
30
|
+
const traces = [...connectivity.sourceTraceConnectivity.values()]
|
|
31
|
+
.map((trace) =>
|
|
32
|
+
QueryNetlistBuilder.#trace(
|
|
33
|
+
trace,
|
|
34
|
+
portsById,
|
|
35
|
+
internalConnectionIdsByPort
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
.filter((trace) => trace.id)
|
|
39
|
+
.sort((left, right) =>
|
|
40
|
+
ComponentGrouping.compareIds(left.id, right.id)
|
|
41
|
+
)
|
|
42
|
+
const components = QueryNetlistBuilder.#connectComponents(
|
|
43
|
+
ComponentGrouping.components(elements, relations),
|
|
44
|
+
traces
|
|
45
|
+
)
|
|
46
|
+
const nets = ComponentGrouping.nets(elements, traces)
|
|
47
|
+
const diagnostics = (connectivity.diagnostics || []).map((row) =>
|
|
48
|
+
ToolkitDiagnostic.create({
|
|
49
|
+
code: row.code || row.type || 'QUERY_CONNECTIVITY_DIAGNOSTIC',
|
|
50
|
+
severity: row.severity || 'warning',
|
|
51
|
+
message: row.message || '',
|
|
52
|
+
source: context.source.fileName || '',
|
|
53
|
+
details: row
|
|
54
|
+
})
|
|
55
|
+
)
|
|
56
|
+
return {
|
|
57
|
+
schema: 'ecad-toolkit.netlist.v1',
|
|
58
|
+
components,
|
|
59
|
+
nets,
|
|
60
|
+
traces,
|
|
61
|
+
internalConnections,
|
|
62
|
+
diagnostics,
|
|
63
|
+
statistics: {
|
|
64
|
+
componentCount: components.length,
|
|
65
|
+
netCount: nets.length,
|
|
66
|
+
traceCount: traces.length,
|
|
67
|
+
internalConnectionCount: internalConnections.length
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Builds explicit electrical bridges between source-component ports.
|
|
74
|
+
* @param {Record<string, any>} elements Prepared elements index.
|
|
75
|
+
* @returns {object[]} Stable internal-connection records.
|
|
76
|
+
*/
|
|
77
|
+
static #internalConnections(elements) {
|
|
78
|
+
const recordsById = new Map()
|
|
79
|
+
for (const component of elements.elementsByType.get(
|
|
80
|
+
'source_component'
|
|
81
|
+
) || []) {
|
|
82
|
+
const sourceComponentId = String(
|
|
83
|
+
component.source_component_id || ''
|
|
84
|
+
).trim()
|
|
85
|
+
const groups = Array.isArray(
|
|
86
|
+
component.internally_connected_source_port_ids
|
|
87
|
+
)
|
|
88
|
+
? component.internally_connected_source_port_ids
|
|
89
|
+
: []
|
|
90
|
+
groups.forEach((sourcePortIds) => {
|
|
91
|
+
const normalizedPortIds =
|
|
92
|
+
QueryNetlistBuilder.#ids(sourcePortIds)
|
|
93
|
+
if (!sourceComponentId || normalizedPortIds.length < 2) return
|
|
94
|
+
QueryNetlistBuilder.#addInternalConnection(recordsById, {
|
|
95
|
+
id: QueryNetlistBuilder.#inlineConnectionId(
|
|
96
|
+
sourceComponentId,
|
|
97
|
+
normalizedPortIds
|
|
98
|
+
),
|
|
99
|
+
sourceComponentId,
|
|
100
|
+
sourcePortIds: normalizedPortIds
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
for (const connection of elements.elementsByType.get(
|
|
105
|
+
'source_component_internal_connection'
|
|
106
|
+
) || []) {
|
|
107
|
+
const id = String(
|
|
108
|
+
connection.source_component_internal_connection_id || ''
|
|
109
|
+
).trim()
|
|
110
|
+
const sourceComponentId = String(
|
|
111
|
+
connection.source_component_id || ''
|
|
112
|
+
).trim()
|
|
113
|
+
const sourcePortIds = QueryNetlistBuilder.#ids(
|
|
114
|
+
connection.source_port_ids
|
|
115
|
+
)
|
|
116
|
+
if (!id || !sourceComponentId || sourcePortIds.length < 2) continue
|
|
117
|
+
QueryNetlistBuilder.#addInternalConnection(recordsById, {
|
|
118
|
+
id,
|
|
119
|
+
sourceComponentId,
|
|
120
|
+
sourcePortIds
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
return [...recordsById.values()].sort((left, right) =>
|
|
124
|
+
ComponentGrouping.compareIds(left.id, right.id)
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Adds one unique connection or rejects a conflicting canonical id.
|
|
130
|
+
* @param {Map<string, object>} recordsById Connection records by id.
|
|
131
|
+
* @param {{ id: string, sourceComponentId: string, sourcePortIds: string[] }} record Connection record.
|
|
132
|
+
* @returns {void}
|
|
133
|
+
*/
|
|
134
|
+
static #addInternalConnection(recordsById, record) {
|
|
135
|
+
const existing = recordsById.get(record.id)
|
|
136
|
+
if (!existing) {
|
|
137
|
+
recordsById.set(record.id, record)
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
if (QueryNetlistBuilder.#sameInternalConnection(existing, record)) {
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
throw new ToolkitError(
|
|
144
|
+
'Conflicting source internal connections use the same canonical id.',
|
|
145
|
+
{
|
|
146
|
+
code: 'ERR_QUERY_CONNECTIVITY_ID',
|
|
147
|
+
category: 'validation',
|
|
148
|
+
format: 'circuitjson',
|
|
149
|
+
details: {
|
|
150
|
+
id: record.id,
|
|
151
|
+
existing,
|
|
152
|
+
conflicting: record
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Tests whether two normalized internal-connection records are identical.
|
|
160
|
+
* @param {object} left Existing connection.
|
|
161
|
+
* @param {object} right Candidate connection.
|
|
162
|
+
* @returns {boolean} Whether both records describe the same connection.
|
|
163
|
+
*/
|
|
164
|
+
static #sameInternalConnection(left, right) {
|
|
165
|
+
return (
|
|
166
|
+
left.sourceComponentId === right.sourceComponentId &&
|
|
167
|
+
left.sourcePortIds.length === right.sourcePortIds.length &&
|
|
168
|
+
left.sourcePortIds.every(
|
|
169
|
+
(sourcePortId, index) =>
|
|
170
|
+
sourcePortId === right.sourcePortIds[index]
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Derives an insertion-independent id from one inline port membership.
|
|
177
|
+
* @param {string} sourceComponentId Source-component id.
|
|
178
|
+
* @param {string[]} sourcePortIds Sorted source-port ids.
|
|
179
|
+
* @returns {string} Stable internal-connection id.
|
|
180
|
+
*/
|
|
181
|
+
static #inlineConnectionId(sourceComponentId, sourcePortIds) {
|
|
182
|
+
const membership = sourcePortIds.map(encodeURIComponent).join('+')
|
|
183
|
+
return `${sourceComponentId}:internal:${membership}`
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Indexes internal-connection ids by every participating source port.
|
|
188
|
+
* @param {object[]} internalConnections Internal-connection records.
|
|
189
|
+
* @returns {Map<string, string[]>} Stable ids by source-port id.
|
|
190
|
+
*/
|
|
191
|
+
static #internalConnectionIdsByPort(internalConnections) {
|
|
192
|
+
const result = new Map()
|
|
193
|
+
for (const connection of internalConnections) {
|
|
194
|
+
for (const sourcePortId of connection.sourcePortIds) {
|
|
195
|
+
if (!result.has(sourcePortId)) result.set(sourcePortId, [])
|
|
196
|
+
result.get(sourcePortId).push(connection.id)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
for (const ids of result.values()) {
|
|
200
|
+
ids.sort(ComponentGrouping.compareIds)
|
|
201
|
+
}
|
|
202
|
+
return result
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Adds trace-derived net ids to detached component pin records.
|
|
207
|
+
* @param {object[]} components Component records.
|
|
208
|
+
* @param {object[]} traces Trace records.
|
|
209
|
+
* @returns {object[]} Connected component records.
|
|
210
|
+
*/
|
|
211
|
+
static #connectComponents(components, traces) {
|
|
212
|
+
const netIdsByPort = new Map()
|
|
213
|
+
for (const trace of traces) {
|
|
214
|
+
for (const portId of trace.sourcePortIds) {
|
|
215
|
+
if (!netIdsByPort.has(portId)) {
|
|
216
|
+
netIdsByPort.set(portId, new Set())
|
|
217
|
+
}
|
|
218
|
+
const netIds = netIdsByPort.get(portId)
|
|
219
|
+
for (const netId of trace.sourceNetIds) netIds.add(netId)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return components.map((component) => ({
|
|
223
|
+
...component,
|
|
224
|
+
pins: component.pins.map((pin) => ({
|
|
225
|
+
...pin,
|
|
226
|
+
netIds: [
|
|
227
|
+
...new Set([
|
|
228
|
+
...(pin.netIds || []),
|
|
229
|
+
...(netIdsByPort.get(pin.id) || [])
|
|
230
|
+
])
|
|
231
|
+
].sort(ComponentGrouping.compareIds)
|
|
232
|
+
}))
|
|
233
|
+
}))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Builds one canonical trace record and its stable endpoints.
|
|
238
|
+
* @param {Record<string, any>} trace Connectivity index row.
|
|
239
|
+
* @param {Map<string, object>} portsById Source ports by id.
|
|
240
|
+
* @param {Map<string, string[]>} internalConnectionIdsByPort Internal connections by port.
|
|
241
|
+
* @returns {Record<string, any>} Trace record.
|
|
242
|
+
*/
|
|
243
|
+
static #trace(trace, portsById, internalConnectionIdsByPort) {
|
|
244
|
+
const sourcePortIds = QueryNetlistBuilder.#ids(
|
|
245
|
+
trace.connectedSourcePortIds
|
|
246
|
+
)
|
|
247
|
+
const sourceNetIds = QueryNetlistBuilder.#ids(
|
|
248
|
+
trace.connectedSourceNetIds
|
|
249
|
+
)
|
|
250
|
+
const sourceComponentIds = [
|
|
251
|
+
...new Set(
|
|
252
|
+
sourcePortIds
|
|
253
|
+
.map((id) => portsById.get(id))
|
|
254
|
+
.map((port) =>
|
|
255
|
+
String(port?.source_component_id || '').trim()
|
|
256
|
+
)
|
|
257
|
+
.filter(Boolean)
|
|
258
|
+
)
|
|
259
|
+
].sort(ComponentGrouping.compareIds)
|
|
260
|
+
const internalConnectionIds = [
|
|
261
|
+
...new Set(
|
|
262
|
+
sourcePortIds.flatMap(
|
|
263
|
+
(id) => internalConnectionIdsByPort.get(id) || []
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
].sort(ComponentGrouping.compareIds)
|
|
267
|
+
const endpoints = [
|
|
268
|
+
...sourcePortIds.map((id) => ({
|
|
269
|
+
id,
|
|
270
|
+
kind: 'port',
|
|
271
|
+
componentId: String(
|
|
272
|
+
portsById.get(id)?.source_component_id || ''
|
|
273
|
+
).trim()
|
|
274
|
+
})),
|
|
275
|
+
...sourceNetIds.map((id) => ({ id, kind: 'net' }))
|
|
276
|
+
].sort((left, right) => {
|
|
277
|
+
const kindOrder = ComponentGrouping.compareIds(
|
|
278
|
+
left.kind,
|
|
279
|
+
right.kind
|
|
280
|
+
)
|
|
281
|
+
return kindOrder || ComponentGrouping.compareIds(left.id, right.id)
|
|
282
|
+
})
|
|
283
|
+
return {
|
|
284
|
+
id: String(trace.sourceTraceId || '').trim(),
|
|
285
|
+
sourcePortIds,
|
|
286
|
+
sourceNetIds,
|
|
287
|
+
sourceComponentIds,
|
|
288
|
+
internalConnectionIds,
|
|
289
|
+
endpoints
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Normalizes stable relation ids.
|
|
295
|
+
* @param {unknown} values Relation values.
|
|
296
|
+
* @returns {string[]} Unique sorted ids.
|
|
297
|
+
*/
|
|
298
|
+
static #ids(values) {
|
|
299
|
+
const candidates = Array.isArray(values) ? values : [values]
|
|
300
|
+
return [
|
|
301
|
+
...new Set(candidates.map((value) => String(value || '').trim()))
|
|
302
|
+
]
|
|
303
|
+
.filter(Boolean)
|
|
304
|
+
.sort(ComponentGrouping.compareIds)
|
|
305
|
+
}
|
|
306
|
+
}
|