@principal-ai/subsystems-react 0.49.0 → 0.51.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/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/pierre/PierreFileView.d.ts +1 -1
- package/dist/pierre/PierreFileView.d.ts.map +1 -1
- package/dist/pierre/PierreFileView.js +2 -1
- package/dist/pierre/PierreFileView.js.map +1 -1
- package/dist/pierre/PierreSnippetView.d.ts +1 -1
- package/dist/pierre/PierreSnippetView.d.ts.map +1 -1
- package/dist/pierre/PierreSnippetView.js +2 -1
- package/dist/pierre/PierreSnippetView.js.map +1 -1
- package/dist/pierre/PierreTrailCodeView.d.ts +7 -1
- package/dist/pierre/PierreTrailCodeView.d.ts.map +1 -1
- package/dist/pierre/PierreTrailCodeView.js +71 -18
- package/dist/pierre/PierreTrailCodeView.js.map +1 -1
- package/dist/pierre/pierreBackground.d.ts +36 -2
- package/dist/pierre/pierreBackground.d.ts.map +1 -1
- package/dist/pierre/pierreBackground.js +40 -13
- package/dist/pierre/pierreBackground.js.map +1 -1
- package/dist/pierre/visibleStep.d.ts +48 -0
- package/dist/pierre/visibleStep.d.ts.map +1 -0
- package/dist/pierre/visibleStep.js +53 -0
- package/dist/pierre/visibleStep.js.map +1 -0
- package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.d.ts +95 -0
- package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.d.ts.map +1 -0
- package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.js +340 -0
- package/dist/stories/Subsystem/C4Graph/c4Associations.fixture.js.map +1 -0
- package/dist/stories/Subsystem/C4Graph/c4Fixture.d.ts +4 -0
- package/dist/stories/Subsystem/C4Graph/c4Fixture.d.ts.map +1 -1
- package/dist/stories/Subsystem/C4Graph/c4Fixture.js +7334 -1
- package/dist/stories/Subsystem/C4Graph/c4Fixture.js.map +1 -1
- package/dist/stories/Subsystem/C4Graph/generateFixture.d.ts +2 -0
- package/dist/stories/Subsystem/C4Graph/generateFixture.d.ts.map +1 -0
- package/dist/stories/Subsystem/C4Graph/generateFixture.js +145 -0
- package/dist/stories/Subsystem/C4Graph/generateFixture.js.map +1 -0
- package/dist/subsystem/C4Graph.d.ts.map +1 -1
- package/dist/subsystem/C4Graph.js +16 -60
- package/dist/subsystem/C4Graph.js.map +1 -1
- package/dist/subsystem/C4NodeCard.d.ts +84 -0
- package/dist/subsystem/C4NodeCard.d.ts.map +1 -0
- package/dist/subsystem/C4NodeCard.js +229 -0
- package/dist/subsystem/C4NodeCard.js.map +1 -0
- package/dist/subsystem/DiagnosticToggle.d.ts +8 -1
- package/dist/subsystem/DiagnosticToggle.d.ts.map +1 -1
- package/dist/subsystem/DiagnosticToggle.js +40 -6
- package/dist/subsystem/DiagnosticToggle.js.map +1 -1
- package/dist/subsystem/FileDrawer.d.ts +10 -1
- package/dist/subsystem/FileDrawer.d.ts.map +1 -1
- package/dist/subsystem/FileDrawer.js +61 -40
- package/dist/subsystem/FileDrawer.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts +6 -0
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +21 -10
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/c4Associations.d.ts +186 -0
- package/dist/subsystem/c4Associations.d.ts.map +1 -0
- package/dist/subsystem/c4Associations.js +284 -0
- package/dist/subsystem/c4Associations.js.map +1 -0
- package/dist/subsystem/model.d.ts +25 -1
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +15 -0
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.d.ts.map +1 -1
- package/dist/subsystem/nodes.js +78 -7
- package/dist/subsystem/nodes.js.map +1 -1
- package/dist/subsystem/toC4.d.ts +119 -0
- package/dist/subsystem/toC4.d.ts.map +1 -1
- package/dist/subsystem/toC4.js +95 -5
- package/dist/subsystem/toC4.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +39 -0
- package/src/pierre/PierreFileView.tsx +2 -1
- package/src/pierre/PierreSnippetView.tsx +2 -1
- package/src/pierre/PierreTrailCodeView.tsx +81 -22
- package/src/pierre/pierreBackground.ts +62 -12
- package/src/pierre/visibleStep.test.ts +108 -0
- package/src/pierre/visibleStep.ts +76 -0
- package/src/stories/Subsystem/C4Graph/C4Associations.stories.tsx +273 -0
- package/src/stories/Subsystem/C4Graph/C4NodeGallery.stories.tsx +325 -0
- package/src/stories/Subsystem/C4Graph/c4Associations.fixture.ts +344 -0
- package/src/stories/Subsystem/C4Graph/c4Fixture.ts +7337 -4
- package/src/stories/Subsystem/C4Graph/generateFixture.ts +161 -0
- package/src/stories/Subsystem/ComponentGraph/Deprecated.stories.tsx +358 -0
- package/src/stories/Subsystem/ComponentGraph/Diagnostics.stories.tsx +27 -2
- package/src/stories/Subsystem/ComponentGraph/{Flows.stories.tsx → Trails.stories.tsx} +10 -4
- package/src/subsystem/C4Graph.tsx +51 -92
- package/src/subsystem/C4NodeCard.test.ts +196 -0
- package/src/subsystem/C4NodeCard.tsx +307 -0
- package/src/subsystem/DiagnosticToggle.tsx +58 -4
- package/src/subsystem/FileDrawer.tsx +112 -63
- package/src/subsystem/SubsystemComponentGraph.tsx +29 -5
- package/src/subsystem/c4Associations.test.ts +484 -0
- package/src/subsystem/c4Associations.ts +398 -0
- package/src/subsystem/model.ts +34 -1
- package/src/subsystem/nodes.tsx +124 -5
- package/src/subsystem/toC4.ts +210 -5
package/src/subsystem/toC4.ts
CHANGED
|
@@ -37,6 +37,12 @@ export interface C4Node {
|
|
|
37
37
|
parentId?: string;
|
|
38
38
|
/** Grouping key the node was derived from (process key, purl, alias). */
|
|
39
39
|
key?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Every derived key folded into this node, when one association claims more
|
|
42
|
+
* than one. Present only on a merged box — a node built from a single key
|
|
43
|
+
* leaves it undefined and `key` tells the whole story.
|
|
44
|
+
*/
|
|
45
|
+
sourceKeys?: string[];
|
|
40
46
|
/** Source component aliases rolled into this node. */
|
|
41
47
|
members: string[];
|
|
42
48
|
/** Distinct constructs among the members (for a quick breakdown). */
|
|
@@ -47,8 +53,58 @@ export interface C4Node {
|
|
|
47
53
|
component?: SubsystemComponent;
|
|
48
54
|
/** Source model ids that contributed members, when attribution is supplied. */
|
|
49
55
|
models?: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Confirmed C4 attributes, supplied by a decoration. Absent for the raw
|
|
58
|
+
* derivation — a bare `toC4(doc)` proposes nothing and confirms nothing.
|
|
59
|
+
*/
|
|
60
|
+
decoration?: C4Decoration;
|
|
50
61
|
}
|
|
51
62
|
|
|
63
|
+
/**
|
|
64
|
+
* The C4 attributes a reviewer (or an agent they approve) attaches to one
|
|
65
|
+
* derived box. This is the seam between "the models said X" and "a person
|
|
66
|
+
* agreed X is a container with technology Y".
|
|
67
|
+
*/
|
|
68
|
+
export interface C4Decoration {
|
|
69
|
+
/** Identity of the confirmed element. Replaces the derived node id, which is
|
|
70
|
+
* what makes two derived keys able to collapse into one box. */
|
|
71
|
+
id: string;
|
|
72
|
+
/** Display name. Defaults to the grouping key. */
|
|
73
|
+
label?: string;
|
|
74
|
+
/** C4 element type — required by the notation, absent from the document. */
|
|
75
|
+
type?: C4ElementType;
|
|
76
|
+
/** C4 "technology" — required on every container by the notation. */
|
|
77
|
+
technology?: string;
|
|
78
|
+
/** One-line responsibility. Derivable from member `purpose`. */
|
|
79
|
+
description?: string;
|
|
80
|
+
/** Has a person looked at this yet? */
|
|
81
|
+
state?: C4AssociationState;
|
|
82
|
+
/** Override the nesting parent (container id) for a component-level element. */
|
|
83
|
+
parentId?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* C4 element type — the shape of the box.
|
|
88
|
+
*
|
|
89
|
+
* The document has no field for this: `construct` says what a *declaration* is
|
|
90
|
+
* (function / store / external), not what an *element* is. c4model.com requires
|
|
91
|
+
* a type on every element, so this is something the projection has to supply
|
|
92
|
+
* or the reviewer has to confirm.
|
|
93
|
+
*/
|
|
94
|
+
export type C4ElementType =
|
|
95
|
+
/** a deployable unit — web app, server, CLI, desktop app */
|
|
96
|
+
| 'application'
|
|
97
|
+
/** a database, file store, or blob store */
|
|
98
|
+
| 'data-store'
|
|
99
|
+
/** a queue or topic */
|
|
100
|
+
| 'queue'
|
|
101
|
+
/** a JAR / assembly / npm package: organised *within* a container, not one */
|
|
102
|
+
| 'library'
|
|
103
|
+
/** another software system, outside our boundary */
|
|
104
|
+
| 'software-system'
|
|
105
|
+
/** a human */
|
|
106
|
+
| 'person';
|
|
107
|
+
|
|
52
108
|
/** A compound frame (the system, or a container holding components). */
|
|
53
109
|
export interface C4Group {
|
|
54
110
|
id: string;
|
|
@@ -78,6 +134,52 @@ export interface C4Model {
|
|
|
78
134
|
edges: C4Edge[];
|
|
79
135
|
}
|
|
80
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Has anyone signed off on this element yet?
|
|
139
|
+
*
|
|
140
|
+
* `proposed` is what an agent writes and a human has not looked at. `accepted`
|
|
141
|
+
* is a human decision. `rejected` means the derivation was wrong and the box
|
|
142
|
+
* should not be drawn at all — the one state that removes a node.
|
|
143
|
+
*/
|
|
144
|
+
export type C4AssociationState = 'proposed' | 'accepted' | 'rejected';
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* One confirmed C4 element: a container or component a person (or an agent a
|
|
148
|
+
* person approved) has said is a real element of the architecture.
|
|
149
|
+
*
|
|
150
|
+
* This lives OUTSIDE the subsystem model document on purpose. The document
|
|
151
|
+
* describes code — what a symbol is and where it runs. Whether two of those
|
|
152
|
+
* runs are one deployable unit is a different, revisable claim about the
|
|
153
|
+
* architecture, and writing it back would mean the next audit had to defend it.
|
|
154
|
+
*/
|
|
155
|
+
export interface C4Association {
|
|
156
|
+
/** Stable identity for this element: `container:<slug>`. Survives renames. */
|
|
157
|
+
id: string;
|
|
158
|
+
/** What C4 level this is. Determines where it is drawn. */
|
|
159
|
+
level: 'container' | 'component';
|
|
160
|
+
/**
|
|
161
|
+
* The derived keys this element absorbs. One key in the common case; more
|
|
162
|
+
* than one when several `process` values are really the same deployable.
|
|
163
|
+
* This is the whole dedup mechanism: merging is claiming several keys under
|
|
164
|
+
* one id, so the rollup below collapses them into a single box for free.
|
|
165
|
+
*/
|
|
166
|
+
sourceKeys: string[];
|
|
167
|
+
label: string;
|
|
168
|
+
/** C4 requires a type on every element. */
|
|
169
|
+
type: C4ElementType;
|
|
170
|
+
/** C4 requires technology on every container. */
|
|
171
|
+
technology?: string;
|
|
172
|
+
/** C4 requires a description on every element. */
|
|
173
|
+
description?: string;
|
|
174
|
+
state: C4AssociationState;
|
|
175
|
+
/** Why the association says what it says — shown in the confirm UI. */
|
|
176
|
+
rationale?: string;
|
|
177
|
+
/** Who or what authored this association. */
|
|
178
|
+
author?: string;
|
|
179
|
+
/** When the state last changed (ISO). */
|
|
180
|
+
decidedAt?: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
81
183
|
export interface ToC4Options {
|
|
82
184
|
view?: C4View;
|
|
83
185
|
/** Override the system title; defaults to the repo key's owner/name. */
|
|
@@ -86,6 +188,12 @@ export interface ToC4Options {
|
|
|
86
188
|
repoKey?: string;
|
|
87
189
|
/** Model-id attribution per component alias (e.g. from a merge sidecar). */
|
|
88
190
|
modelsByAlias?: Record<string, string[]>;
|
|
191
|
+
/**
|
|
192
|
+
* Confirmed elements. When supplied, a derived key claimed by an
|
|
193
|
+
* `accepted` association is drawn as that association's box instead of as a
|
|
194
|
+
* raw key, and a `rejected` one is not drawn at all.
|
|
195
|
+
*/
|
|
196
|
+
associations?: readonly C4Association[];
|
|
89
197
|
}
|
|
90
198
|
|
|
91
199
|
const EXTERNAL_PURL = 'external';
|
|
@@ -96,6 +204,48 @@ export function isGroundedComponent(c: SubsystemComponent): boolean {
|
|
|
96
204
|
return c.construct !== 'external' && c.construct !== 'custom_entity';
|
|
97
205
|
}
|
|
98
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Index confirmed associations by the derived keys they claim.
|
|
209
|
+
*
|
|
210
|
+
* First writer wins on a contested key so the result does not depend on array
|
|
211
|
+
* order, and the loser is reported so the conflict is visible rather than
|
|
212
|
+
* silently dropped.
|
|
213
|
+
*/
|
|
214
|
+
export function indexAssociations(
|
|
215
|
+
associations: readonly C4Association[] | undefined,
|
|
216
|
+
): {
|
|
217
|
+
byKey: Map<string, C4Association>;
|
|
218
|
+
contested: Array<{ key: string; kept: string; dropped: string }>;
|
|
219
|
+
} {
|
|
220
|
+
const byKey = new Map<string, C4Association>();
|
|
221
|
+
const contested: Array<{ key: string; kept: string; dropped: string }> = [];
|
|
222
|
+
if (!associations) return { byKey, contested };
|
|
223
|
+
|
|
224
|
+
// Stable order: accepted before proposed, so a confirmed decision outranks a
|
|
225
|
+
// speculative one for the same key.
|
|
226
|
+
const ordered = [...associations].sort((a, b) => {
|
|
227
|
+
const rank = (s: C4AssociationState) => (s === 'accepted' ? 0 : s === 'proposed' ? 1 : 2);
|
|
228
|
+
return rank(a.state) - rank(b.state) || a.id.localeCompare(b.id);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
for (const a of ordered) {
|
|
232
|
+
for (const key of a.sourceKeys) {
|
|
233
|
+
const existing = byKey.get(key);
|
|
234
|
+
if (existing && existing.id !== a.id) {
|
|
235
|
+
contested.push({ key, kept: existing.id, dropped: a.id });
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
byKey.set(key, a);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return { byKey, contested };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** True when a resolved association means this box should not be drawn. */
|
|
245
|
+
function isSuppressed(a: C4Association | undefined): boolean {
|
|
246
|
+
return a?.state === 'rejected';
|
|
247
|
+
}
|
|
248
|
+
|
|
99
249
|
/** `owner/name` from a purl, trimmed of scheme and fragment. */
|
|
100
250
|
export function labelFromPurl(purl: string): string {
|
|
101
251
|
const base = purl.replace(/^external:/, '').split('#')[0]?.trim() ?? '';
|
|
@@ -133,6 +283,11 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
|
|
|
133
283
|
const view: C4View = options.view ?? 'container';
|
|
134
284
|
const repoKey = options.repoKey ?? deriveRepoKey(doc) ?? 'system';
|
|
135
285
|
const systemId = `system:${repoKey}`;
|
|
286
|
+
const { byKey: assocByKey } = indexAssociations(options.associations);
|
|
287
|
+
// Id -> association, so a container frame can be labelled from its
|
|
288
|
+
// association even when no node exists for it (component view).
|
|
289
|
+
const assocById = new Map<string, C4Association>();
|
|
290
|
+
for (const a of options.associations ?? []) assocById.set(a.id, a);
|
|
136
291
|
|
|
137
292
|
const system: C4Node = {
|
|
138
293
|
id: systemId,
|
|
@@ -150,12 +305,33 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
|
|
|
150
305
|
const containerNodeId = (key: string) => `container:${key}`;
|
|
151
306
|
const componentNodeId = (alias: string) => `component:${alias}`;
|
|
152
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Resolve one derived container key into the box that should carry it.
|
|
310
|
+
*
|
|
311
|
+
* An accepted association wins: its id becomes the node id, so every key it
|
|
312
|
+
* claims rolls into one node (that is the dedup), and its label / type /
|
|
313
|
+
* technology / description become the node's presentation. A rejected one
|
|
314
|
+
* means the box is not drawn at all. Unclaimed keys keep the raw derived
|
|
315
|
+
* behaviour, so the projection is still useful with zero associations.
|
|
316
|
+
*/
|
|
317
|
+
const resolveContainer = (key: string): { id: string; label: string; assoc: C4Association | undefined } => {
|
|
318
|
+
const assoc = assocByKey.get(key);
|
|
319
|
+
if (assoc && assoc.level === 'container') {
|
|
320
|
+
return { id: assoc.id, label: assoc.label, assoc };
|
|
321
|
+
}
|
|
322
|
+
if (view === 'component') {
|
|
323
|
+
return { id: containerNodeId(key), label: key === UNASSIGNED ? 'Unassigned' : key, assoc: undefined };
|
|
324
|
+
}
|
|
325
|
+
return { id: containerNodeId(key), label: key === UNASSIGNED ? 'Unassigned' : key, assoc: undefined };
|
|
326
|
+
};
|
|
327
|
+
|
|
153
328
|
for (const c of doc.components) {
|
|
154
329
|
let id: string;
|
|
155
330
|
let kind: C4Kind;
|
|
156
331
|
let label: string;
|
|
157
332
|
let parentId: string | undefined;
|
|
158
333
|
let key: string | undefined;
|
|
334
|
+
let assoc: C4Association | undefined;
|
|
159
335
|
|
|
160
336
|
if (c.construct === 'external') {
|
|
161
337
|
key = externalKey(c);
|
|
@@ -169,16 +345,22 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
|
|
|
169
345
|
label = c.name;
|
|
170
346
|
} else if (view === 'component') {
|
|
171
347
|
key = containerKey(c);
|
|
348
|
+
const resolved = resolveContainer(key);
|
|
349
|
+
if (isSuppressed(resolved.assoc)) continue;
|
|
172
350
|
id = componentNodeId(c.alias);
|
|
173
351
|
kind = 'component';
|
|
174
352
|
label = c.name;
|
|
175
|
-
parentId =
|
|
353
|
+
parentId = resolved.id;
|
|
354
|
+
assoc = undefined; // the component itself has no association
|
|
176
355
|
} else {
|
|
177
356
|
key = containerKey(c);
|
|
178
|
-
|
|
357
|
+
const resolved = resolveContainer(key);
|
|
358
|
+
if (isSuppressed(resolved.assoc)) continue;
|
|
359
|
+
id = resolved.id;
|
|
179
360
|
kind = 'container';
|
|
180
|
-
label =
|
|
361
|
+
label = resolved.label;
|
|
181
362
|
parentId = systemId;
|
|
363
|
+
assoc = resolved.assoc;
|
|
182
364
|
}
|
|
183
365
|
|
|
184
366
|
ownerOf.set(c.alias, id);
|
|
@@ -194,7 +376,12 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
|
|
|
194
376
|
members: [],
|
|
195
377
|
constructs: [],
|
|
196
378
|
isStore: false,
|
|
379
|
+
// A merged box is built from several derived keys. Track them as a
|
|
380
|
+
// set so the confirm UI can show what was folded together — without
|
|
381
|
+
// this, the accumulation would run once per member, not per key.
|
|
382
|
+
...(assoc && assoc.sourceKeys.length > 1 ? { sourceKeys: [...assoc.sourceKeys] } : {}),
|
|
197
383
|
...(kind === 'component' ? { component: c } : {}),
|
|
384
|
+
...(assoc ? { decoration: toDecoration(assoc) } : {}),
|
|
198
385
|
};
|
|
199
386
|
nodes.set(id, node);
|
|
200
387
|
}
|
|
@@ -209,6 +396,10 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
|
|
|
209
396
|
|
|
210
397
|
// --- Groups (compound frames) ------------------------------------------
|
|
211
398
|
const groups: C4Group[] = [];
|
|
399
|
+
const labelForContainerId = (id: string): string =>
|
|
400
|
+
nodes.get(id)?.label ??
|
|
401
|
+
assocById.get(id)?.label ??
|
|
402
|
+
(id.startsWith('container:') ? id.slice('container:'.length) : id);
|
|
212
403
|
if (view === 'container') {
|
|
213
404
|
const containerIds = [...nodes.values()].filter((n) => n.kind === 'container').map((n) => n.id);
|
|
214
405
|
if (containerIds.length > 0) {
|
|
@@ -224,8 +415,7 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
|
|
|
224
415
|
}
|
|
225
416
|
const containerIds: string[] = [];
|
|
226
417
|
for (const [id, memberIds] of byContainer) {
|
|
227
|
-
|
|
228
|
-
groups.push({ id, kind: 'container', label: label === UNASSIGNED ? 'Unassigned' : label, parentId: systemId, memberIds });
|
|
418
|
+
groups.push({ id, kind: 'container', label: labelForContainerId(id), parentId: systemId, memberIds });
|
|
229
419
|
containerIds.push(id);
|
|
230
420
|
}
|
|
231
421
|
if (containerIds.length > 0) {
|
|
@@ -244,6 +434,9 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
|
|
|
244
434
|
mechanism: string,
|
|
245
435
|
) => {
|
|
246
436
|
if (!source || !target || source === target) return;
|
|
437
|
+
// An endpoint whose box was rejected has no owner; the edge cannot be
|
|
438
|
+
// drawn, so drop it rather than inventing a dangling arrow.
|
|
439
|
+
if (!nodes.has(source) || !nodes.has(target)) return;
|
|
247
440
|
const key = `${source}\0${target}`;
|
|
248
441
|
let edge = bucket.get(key);
|
|
249
442
|
if (!edge) {
|
|
@@ -271,3 +464,15 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
|
|
|
271
464
|
edges,
|
|
272
465
|
};
|
|
273
466
|
}
|
|
467
|
+
|
|
468
|
+
/** Project an association down to the attributes a node carries. */
|
|
469
|
+
function toDecoration(a: C4Association): C4Decoration {
|
|
470
|
+
return {
|
|
471
|
+
id: a.id,
|
|
472
|
+
label: a.label,
|
|
473
|
+
type: a.type,
|
|
474
|
+
technology: a.technology,
|
|
475
|
+
description: a.description,
|
|
476
|
+
state: a.state,
|
|
477
|
+
};
|
|
478
|
+
}
|