@transitrix/diagrams 1.0.0 → 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/LICENSE +21 -21
- package/README.md +47 -47
- package/dist/__tests__/edge-path.test.js +8 -0
- package/dist/activities/cpm.js +11 -2
- package/dist/activities/gantt.js +6 -3
- package/dist/activities/types.d.ts +1 -0
- package/dist/activities/validate.js +3 -3
- package/dist/activity-card/__tests__/example.test.js +20 -1
- package/dist/activity-card/__tests__/layout.test.js +102 -11
- package/dist/activity-card/__tests__/resolver.test.js +161 -7
- package/dist/activity-card/index.d.ts +1 -1
- package/dist/activity-card/layout.d.ts +1 -1
- package/dist/activity-card/layout.js +220 -103
- package/dist/activity-card/resolver.js +115 -28
- package/dist/activity-card/types.d.ts +150 -27
- package/dist/activity-card/validate.js +3 -0
- package/dist/actor/__tests__/validate.test.js +63 -31
- package/dist/actor/index.d.ts +1 -1
- package/dist/actor/index.js +1 -1
- package/dist/actor/types.d.ts +7 -3
- package/dist/actor/types.js +17 -3
- package/dist/actor/validate.js +55 -26
- package/dist/assertion/types.d.ts +3 -1
- package/dist/assertion/types.js +1 -1
- package/dist/change/__tests__/validate.test.js +40 -34
- package/dist/change/index.d.ts +1 -0
- package/dist/change/types.d.ts +1 -1
- package/dist/change/types.js +2 -2
- package/dist/change/validate.d.ts +6 -1
- package/dist/change/validate.js +68 -41
- package/dist/compliance/__tests__/classify.test.js +34 -0
- package/dist/compliance/__tests__/coverage-metric.test.js +146 -46
- package/dist/compliance/__tests__/html.test.js +2 -2
- package/dist/compliance/__tests__/impact.test.js +509 -6
- package/dist/compliance/classify.d.ts +6 -0
- package/dist/compliance/classify.js +9 -1
- package/dist/compliance/coverage-metric.d.ts +31 -11
- package/dist/compliance/coverage-metric.js +149 -39
- package/dist/compliance/html.js +78 -81
- package/dist/compliance/impact.d.ts +100 -16
- package/dist/compliance/impact.js +272 -28
- package/dist/compliance/index.d.ts +2 -2
- package/dist/compliance/index.js +1 -1
- package/dist/compliance/markdown.js +1 -0
- package/dist/compliance/types.d.ts +25 -6
- package/dist/edge-path.d.ts +8 -4
- package/dist/edge-path.js +11 -6
- package/dist/factor/__tests__/validate.test.js +75 -46
- package/dist/factor/index.d.ts +3 -2
- package/dist/factor/index.js +1 -1
- package/dist/factor/types.d.ts +5 -8
- package/dist/factor/types.js +10 -5
- package/dist/factor/validate.d.ts +6 -1
- package/dist/factor/validate.js +68 -50
- package/dist/fgca/__tests__/chain-table.test.js +2 -2
- package/dist/fgca/__tests__/layout.test.js +11 -11
- package/dist/fgca/__tests__/preview-layout.test.js +7 -7
- package/dist/fgca/__tests__/resolver.test.js +185 -0
- package/dist/fgca/chain-table.d.ts +1 -1
- package/dist/fgca/chain-table.js +4 -4
- package/dist/fgca/index.d.ts +4 -2
- package/dist/fgca/index.js +2 -1
- package/dist/fgca/layout.d.ts +2 -2
- package/dist/fgca/layout.js +9 -9
- package/dist/fgca/nodeTypes.d.ts +1 -1
- package/dist/fgca/nodeTypes.js +2 -2
- package/dist/fgca/nodes/FGCAFactorNode.d.ts +3 -3
- package/dist/fgca/nodes/FGCAFactorNode.js +5 -5
- package/dist/fgca/parse-canonical.js +15 -10
- package/dist/fgca/preview-layout.d.ts +1 -1
- package/dist/fgca/preview-layout.js +4 -4
- package/dist/fgca/resolver.d.ts +49 -0
- package/dist/fgca/resolver.js +126 -0
- package/dist/fgca/types.d.ts +11 -11
- package/dist/fgca/types.js +2 -2
- package/dist/fgca/validate.d.ts +2 -2
- package/dist/fgca/validate.js +17 -14
- package/dist/goals/index.d.ts +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/process-blueprint/__tests__/layout.test.js +37 -0
- package/dist/process-blueprint/index.d.ts +1 -1
- package/dist/process-blueprint/layout.js +66 -35
- package/dist/process-blueprint/types.d.ts +36 -1
- package/dist/scenarios/index.d.ts +1 -1
- package/dist/scenarios/types.d.ts +4 -4
- package/dist/stakeholder/__tests__/validate.test.js +49 -47
- package/dist/stakeholder/index.d.ts +1 -0
- package/dist/stakeholder/types.d.ts +3 -4
- package/dist/stakeholder/types.js +2 -3
- package/dist/stakeholder/validate.d.ts +6 -1
- package/dist/stakeholder/validate.js +71 -50
- package/dist/target-state/__tests__/validate.test.js +43 -27
- package/dist/target-state/index.d.ts +2 -0
- package/dist/target-state/index.js +1 -0
- package/dist/target-state/types.d.ts +6 -1
- package/dist/target-state/types.js +8 -7
- package/dist/target-state/validate.d.ts +6 -1
- package/dist/target-state/validate.js +69 -45
- package/dist/theme/themes.js +71 -69
- package/dist/theme/tokens.d.ts +7 -1
- package/dist/theme/tokens.js +3 -1
- package/dist/webview/__tests__/entry-notations.test.js +2 -0
- package/dist/webview/__tests__/entry.test.js +11 -11
- package/dist/webview/__tests__/render-process.test.js +463 -0
- package/dist/webview/__tests__/render-util.test.d.ts +1 -0
- package/dist/webview/__tests__/render-util.test.js +54 -0
- package/dist/webview/entry.d.ts +1 -1
- package/dist/webview/entry.js +9 -5
- package/dist/webview/render-activities.d.ts +24 -2
- package/dist/webview/render-activities.js +92 -57
- package/dist/webview/render-activity-card.d.ts +11 -1
- package/dist/webview/render-activity-card.js +115 -79
- package/dist/webview/render-blocks.d.ts +24 -1
- package/dist/webview/render-blocks.js +58 -24
- package/dist/webview/render-fgca.d.ts +31 -0
- package/dist/webview/render-fgca.js +30 -19
- package/dist/webview/render-goals.d.ts +27 -1
- package/dist/webview/render-goals.js +42 -32
- package/dist/webview/render-process-blueprint.d.ts +11 -1
- package/dist/webview/render-process-blueprint.js +118 -53
- package/dist/webview/render-process.d.ts +95 -0
- package/dist/webview/render-process.js +392 -0
- package/dist/webview/render-scenarios.js +2 -2
- package/dist/webview/render-util.d.ts +19 -6
- package/dist/webview/render-util.js +23 -15
- package/package.json +54 -40
- package/src/__tests__/edge-path.test.ts +58 -46
- package/src/__tests__/scope.test.ts +27 -27
- package/src/__tests__/yaml-normalize.test.ts +158 -158
- package/src/activities/__tests__/cpm.test.ts +164 -164
- package/src/activities/__tests__/gantt.test.ts +245 -245
- package/src/activities/__tests__/layout.test.ts +112 -112
- package/src/activities/__tests__/validate.test.ts +409 -409
- package/src/activities/cpm.ts +107 -95
- package/src/activities/gantt.ts +277 -276
- package/src/activities/index.ts +28 -28
- package/src/activities/layout.ts +112 -112
- package/src/activities/types.ts +149 -148
- package/src/activities/validate.ts +357 -357
- package/src/activity-card/__tests__/example.test.ts +85 -66
- package/src/activity-card/__tests__/layout.test.ts +169 -63
- package/src/activity-card/__tests__/resolver.test.ts +330 -161
- package/src/activity-card/__tests__/validate.test.ts +131 -131
- package/src/activity-card/index.ts +38 -32
- package/src/activity-card/layout.ts +324 -194
- package/src/activity-card/resolver.ts +422 -337
- package/src/activity-card/types.ts +376 -237
- package/src/activity-card/validate.ts +162 -158
- package/src/actor/__tests__/validate.test.ts +70 -31
- package/src/actor/index.ts +1 -1
- package/src/actor/types.ts +21 -7
- package/src/actor/validate.ts +59 -27
- package/src/applications/__tests__/validate.test.ts +256 -256
- package/src/applications/index.ts +11 -11
- package/src/applications/types.ts +43 -43
- package/src/applications/validate.ts +131 -131
- package/src/assertion/__tests__/example.test.ts +37 -37
- package/src/assertion/__tests__/validate.test.ts +142 -142
- package/src/assertion/index.ts +12 -12
- package/src/assertion/types.ts +60 -58
- package/src/assertion/validate.ts +145 -145
- package/src/blocks/__tests__/layout.test.ts +169 -169
- package/src/blocks/__tests__/validate.test.ts +260 -260
- package/src/blocks/index.ts +17 -17
- package/src/blocks/layout.ts +186 -186
- package/src/blocks/types.ts +62 -62
- package/src/blocks/validate.ts +206 -206
- package/src/capability-map/__tests__/validate.test.ts +238 -238
- package/src/capability-map/index.ts +8 -8
- package/src/capability-map/types.ts +29 -29
- package/src/capability-map/validate.ts +127 -127
- package/src/change/__tests__/validate.test.ts +43 -34
- package/src/change/index.ts +1 -0
- package/src/change/types.ts +5 -3
- package/src/change/validate.ts +75 -43
- package/src/compliance/__tests__/classify.test.ts +64 -27
- package/src/compliance/__tests__/confidence.test.ts +98 -98
- package/src/compliance/__tests__/coverage-metric.test.ts +300 -188
- package/src/compliance/__tests__/demo-scenario.test.ts +273 -273
- package/src/compliance/__tests__/example.test.ts +57 -57
- package/src/compliance/__tests__/gap-report.test.ts +128 -128
- package/src/compliance/__tests__/html.test.ts +141 -141
- package/src/compliance/__tests__/impact.test.ts +1059 -481
- package/src/compliance/__tests__/markdown.test.ts +94 -94
- package/src/compliance/__tests__/views.test.ts +64 -64
- package/src/compliance/classify.ts +100 -84
- package/src/compliance/confidence.ts +69 -69
- package/src/compliance/coverage-metric.ts +404 -263
- package/src/compliance/gap-report.ts +94 -94
- package/src/compliance/html.ts +291 -295
- package/src/compliance/impact.ts +979 -677
- package/src/compliance/index.ts +57 -56
- package/src/compliance/markdown.ts +136 -135
- package/src/compliance/reverse-index.ts +33 -33
- package/src/compliance/types.ts +136 -120
- package/src/compliance/views.ts +46 -46
- package/src/compliance-matrix/__tests__/build.test.ts +200 -200
- package/src/compliance-matrix/__tests__/example.test.ts +57 -57
- package/src/compliance-matrix/index.ts +11 -11
- package/src/compliance-matrix/types.ts +102 -102
- package/src/edge-path.ts +50 -44
- package/src/factor/__tests__/validate.test.ts +81 -46
- package/src/factor/index.ts +3 -7
- package/src/factor/types.ts +15 -19
- package/src/factor/validate.ts +77 -51
- package/src/fgca/__tests__/chain-table.test.ts +172 -172
- package/src/fgca/__tests__/layout.test.ts +179 -179
- package/src/fgca/__tests__/parse-canonical.test.ts +248 -248
- package/src/fgca/__tests__/preview-layout.test.ts +124 -124
- package/src/fgca/__tests__/resolver.test.ts +209 -0
- package/src/fgca/__tests__/validate.test.ts +142 -142
- package/src/fgca/chain-table.ts +225 -225
- package/src/fgca/index.ts +47 -45
- package/src/fgca/layout.ts +204 -204
- package/src/fgca/nodeTypes.ts +11 -11
- package/src/fgca/nodes/FGCAActivityNode.tsx +65 -65
- package/src/fgca/nodes/FGCAChangeNode.tsx +64 -64
- package/src/fgca/nodes/FGCAFactorNode.tsx +64 -64
- package/src/fgca/nodes/FGCAGoalNode.tsx +72 -72
- package/src/fgca/parse-canonical.ts +405 -400
- package/src/fgca/preview-layout.ts +217 -217
- package/src/fgca/resolver.ts +159 -0
- package/src/fgca/types.ts +50 -50
- package/src/fgca/validate.ts +129 -126
- package/src/geometry.ts +9 -9
- package/src/goals/__tests__/layout.test.ts +175 -175
- package/src/goals/__tests__/mutations.test.ts +111 -111
- package/src/goals/__tests__/parse-canonical.test.ts +145 -145
- package/src/goals/__tests__/validate.test.ts +202 -202
- package/src/goals/index.ts +20 -21
- package/src/goals/layout.ts +172 -172
- package/src/goals/mutations.ts +123 -123
- package/src/goals/parse-canonical.ts +286 -286
- package/src/goals/types.ts +85 -85
- package/src/goals/validate.ts +136 -136
- package/src/index.ts +28 -23
- package/src/process-blueprint/__tests__/compliance-lane.test.ts +320 -320
- package/src/process-blueprint/__tests__/layout.test.ts +240 -195
- package/src/process-blueprint/__tests__/validate.test.ts +310 -310
- package/src/process-blueprint/index.ts +32 -31
- package/src/process-blueprint/layout.ts +564 -531
- package/src/process-blueprint/types.ts +278 -242
- package/src/process-blueprint/validate.ts +181 -181
- package/src/process-map/__tests__/validate.test.ts +223 -223
- package/src/process-map/index.ts +10 -10
- package/src/process-map/types.ts +36 -36
- package/src/process-map/validate.ts +135 -135
- package/src/products/__tests__/validate.test.ts +239 -239
- package/src/products/index.ts +9 -9
- package/src/products/types.ts +31 -31
- package/src/products/validate.ts +112 -112
- package/src/repo-validate/__tests__/validate-repo.test.ts +149 -149
- package/src/repo-validate/index.ts +2 -2
- package/src/repo-validate/types.ts +50 -50
- package/src/repo-validate/validate-repo.ts +196 -196
- package/src/requirement/__tests__/example.test.ts +24 -24
- package/src/requirement/__tests__/validate.test.ts +96 -96
- package/src/requirement/index.ts +4 -4
- package/src/requirement/types.ts +35 -35
- package/src/requirement/validate.ts +92 -92
- package/src/scenarios/__tests__/validate.test.ts +209 -209
- package/src/scenarios/index.ts +15 -15
- package/src/scenarios/types.ts +37 -37
- package/src/scenarios/validate.ts +122 -122
- package/src/schema-version.ts +16 -16
- package/src/scope.ts +46 -46
- package/src/stakeholder/__tests__/validate.test.ts +51 -47
- package/src/stakeholder/index.ts +1 -0
- package/src/stakeholder/types.ts +7 -7
- package/src/stakeholder/validate.ts +79 -52
- package/src/target-state/__tests__/validate.test.ts +48 -27
- package/src/target-state/index.ts +2 -0
- package/src/target-state/types.ts +11 -7
- package/src/target-state/validate.ts +76 -46
- package/src/theme/index.ts +4 -4
- package/src/theme/themes.ts +239 -237
- package/src/theme/tokens.ts +135 -133
- package/src/typed-id.ts +56 -56
- package/src/validation-types.ts +36 -36
- package/src/webview/__tests__/entry-notations.test.ts +79 -77
- package/src/webview/__tests__/entry.test.ts +64 -64
- package/src/webview/__tests__/render-goals.test.ts +72 -72
- package/src/webview/__tests__/render-process.test.ts +538 -0
- package/src/webview/__tests__/render-util.test.ts +66 -0
- package/src/webview/entry.ts +298 -292
- package/src/webview/render-activities.ts +111 -69
- package/src/webview/render-activity-card.ts +275 -234
- package/src/webview/render-blocks.ts +78 -28
- package/src/webview/render-fgca.ts +53 -28
- package/src/webview/render-goals.ts +133 -110
- package/src/webview/render-process-blueprint.ts +281 -207
- package/src/webview/render-process.ts +507 -0
- package/src/webview/render-scenarios.ts +3 -3
- package/src/webview/render-util.ts +27 -14
- package/src/webview/styles.css +322 -323
- package/src/yaml-normalize.ts +56 -56
- package/dist/element-envelope.d.ts +0 -15
- package/dist/element-envelope.js +0 -55
- package/dist/issues/__tests__/layout.test.js +0 -62
- package/dist/issues/__tests__/validate.test.js +0 -82
- package/dist/issues/index.d.ts +0 -4
- package/dist/issues/index.js +0 -2
- package/dist/issues/layout.d.ts +0 -10
- package/dist/issues/layout.js +0 -82
- package/dist/issues/types.d.ts +0 -61
- package/dist/issues/types.js +0 -6
- package/dist/issues/validate.d.ts +0 -3
- package/dist/issues/validate.js +0 -143
- package/dist/webview/transitrix-render.css +0 -323
- package/dist/webview/transitrix-render.js +0 -6452
- package/dist/webview/transitrix-render.js.map +0 -7
- package/src/element-envelope.ts +0 -76
- /package/dist/{issues/__tests__/layout.test.d.ts → fgca/__tests__/resolver.test.d.ts} +0 -0
- /package/dist/{issues/__tests__/validate.test.d.ts → webview/__tests__/render-process.test.d.ts} +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FGCA canon resolver — assembles a flat canonical FGCA document from a
|
|
3
|
+
* view_config projection and a canon element/relation store (VP-3).
|
|
4
|
+
*
|
|
5
|
+
* Output shape matches the flat form that `parseCanonicalFGCA` expects:
|
|
6
|
+
* top-level `factors[]`, `goals[]`, `changes[]`, `activities[]` with typed
|
|
7
|
+
* string IDs. Filesystem-free — callable from unit tests without vscode.
|
|
8
|
+
*/
|
|
9
|
+
function isObject(v) {
|
|
10
|
+
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
11
|
+
}
|
|
12
|
+
function str(v) {
|
|
13
|
+
return typeof v === 'string' && v.trim().length > 0 ? v : undefined;
|
|
14
|
+
}
|
|
15
|
+
function strArray(v) {
|
|
16
|
+
if (!Array.isArray(v))
|
|
17
|
+
return [];
|
|
18
|
+
return v.filter((x) => typeof x === 'string' && x.trim().length > 0);
|
|
19
|
+
}
|
|
20
|
+
function collectByNotation(docs, notation) {
|
|
21
|
+
const out = new Map();
|
|
22
|
+
for (const doc of docs) {
|
|
23
|
+
if (!isObject(doc))
|
|
24
|
+
continue;
|
|
25
|
+
if (str(doc['notation']) !== notation)
|
|
26
|
+
continue;
|
|
27
|
+
const id = str(doc['id']);
|
|
28
|
+
if (!id)
|
|
29
|
+
continue;
|
|
30
|
+
if (!out.has(id))
|
|
31
|
+
out.set(id, doc);
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Returns true when the parsed view document uses the canon-projection form
|
|
37
|
+
* (`view_config` key present, no inline `factors[]` / `goals[]` arrays).
|
|
38
|
+
* Used by the preview to decide whether to run the resolver or fall back to
|
|
39
|
+
* direct `parseCanonicalFGCA` on the inline document.
|
|
40
|
+
*/
|
|
41
|
+
export function isFGCAViewDoc(parsed) {
|
|
42
|
+
if (!isObject(parsed))
|
|
43
|
+
return false;
|
|
44
|
+
return 'view_config' in parsed && !('factors' in parsed) && !('goals' in parsed);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Assemble a flat canonical FGCA document from the canon element store,
|
|
48
|
+
* filtered by the view document's `view_config`. The return value is ready
|
|
49
|
+
* to pass to `parseCanonicalFGCA`.
|
|
50
|
+
*
|
|
51
|
+
* Cross-reference fields (`goal.factors[]`, `change.goals[]`,
|
|
52
|
+
* `activity.changes[]`) are carried from the canon elements unchanged; the
|
|
53
|
+
* resolver decides which elements to include, not how they are shaped.
|
|
54
|
+
*/
|
|
55
|
+
export function resolveFGCA(viewDoc, sources) {
|
|
56
|
+
if (!isObject(viewDoc)) {
|
|
57
|
+
return { notation: 'fgca', factors: [], goals: [], changes: [], activities: [] };
|
|
58
|
+
}
|
|
59
|
+
const vc = isObject(viewDoc['view_config']) ? viewDoc['view_config'] : {};
|
|
60
|
+
const goalsConf = isObject(vc['goals']) ? vc['goals'] : {};
|
|
61
|
+
const factorsConf = isObject(vc['factors']) ? vc['factors'] : {};
|
|
62
|
+
const changesConf = isObject(vc['changes']) ? vc['changes'] : {};
|
|
63
|
+
const activitiesConf = isObject(vc['activities']) ? vc['activities'] : {};
|
|
64
|
+
const factorElemsLegacy = collectByNotation(sources.elements, 'factor');
|
|
65
|
+
const factorElemsNew = collectByNotation(sources.elements, 'driver');
|
|
66
|
+
const factorElems = new Map([...factorElemsLegacy, ...factorElemsNew]);
|
|
67
|
+
const goalElems = collectByNotation(sources.elements, 'goal');
|
|
68
|
+
const changeElems = collectByNotation(sources.elements, 'change');
|
|
69
|
+
const activityElems = collectByNotation(sources.elements, 'activity');
|
|
70
|
+
// 1. Select goal set
|
|
71
|
+
const goalsFilter = str(goalsConf['filter']) ?? 'all';
|
|
72
|
+
let selectedGoals;
|
|
73
|
+
if (goalsFilter === 'ids') {
|
|
74
|
+
const ids = strArray(goalsConf['ids']);
|
|
75
|
+
selectedGoals = ids.flatMap((id) => { const g = goalElems.get(id); return g ? [g] : []; });
|
|
76
|
+
}
|
|
77
|
+
else if (goalsFilter === 'tags') {
|
|
78
|
+
const tags = strArray(goalsConf['tags']);
|
|
79
|
+
selectedGoals = [...goalElems.values()].filter((g) => strArray(g['tags']).some((t) => tags.includes(t)));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// 'all' — include every goal element in the canon store
|
|
83
|
+
selectedGoals = [...goalElems.values()];
|
|
84
|
+
}
|
|
85
|
+
const selectedGoalIds = new Set(selectedGoals.map((g) => str(g['id'])).filter((x) => x !== undefined));
|
|
86
|
+
// 2. Select factors: derived follows goal.factors[]; all = every factor
|
|
87
|
+
let selectedFactors;
|
|
88
|
+
if (str(factorsConf['surface']) === 'all') {
|
|
89
|
+
selectedFactors = [...factorElems.values()];
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const refIds = new Set();
|
|
93
|
+
for (const g of selectedGoals)
|
|
94
|
+
for (const fid of strArray(g['factors']))
|
|
95
|
+
refIds.add(fid);
|
|
96
|
+
selectedFactors = [...refIds].flatMap((id) => { const f = factorElems.get(id); return f ? [f] : []; });
|
|
97
|
+
}
|
|
98
|
+
// 3. Select changes: derived = changes referencing ≥1 selected goal
|
|
99
|
+
let selectedChanges;
|
|
100
|
+
if (str(changesConf['surface']) === 'all') {
|
|
101
|
+
selectedChanges = [...changeElems.values()];
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
selectedChanges = [...changeElems.values()].filter((c) => strArray(c['goals']).some((gid) => selectedGoalIds.has(gid)));
|
|
105
|
+
}
|
|
106
|
+
const selectedChangeIds = new Set(selectedChanges.map((c) => str(c['id'])).filter((x) => x !== undefined));
|
|
107
|
+
// 4. Select activities: derived = reference ≥1 selected change or goal (degenerate FGA link)
|
|
108
|
+
let selectedActivities;
|
|
109
|
+
if (str(activitiesConf['surface']) === 'all') {
|
|
110
|
+
selectedActivities = [...activityElems.values()];
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
selectedActivities = [...activityElems.values()].filter((a) => strArray(a['changes']).some((cid) => selectedChangeIds.has(cid)) ||
|
|
114
|
+
strArray(a['goals']).some((gid) => selectedGoalIds.has(gid)));
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
notation: 'fgca',
|
|
118
|
+
id: viewDoc['id'],
|
|
119
|
+
name: viewDoc['name'],
|
|
120
|
+
spec_version: viewDoc['spec_version'],
|
|
121
|
+
factors: selectedFactors,
|
|
122
|
+
goals: selectedGoals,
|
|
123
|
+
changes: selectedChanges,
|
|
124
|
+
activities: selectedActivities,
|
|
125
|
+
};
|
|
126
|
+
}
|
package/dist/fgca/types.d.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
export interface
|
|
2
|
-
id: number;
|
|
1
|
+
export interface DriverItem {
|
|
2
|
+
id: number | string;
|
|
3
3
|
name: string;
|
|
4
4
|
}
|
|
5
5
|
export interface GoalItem {
|
|
6
|
-
id: number;
|
|
6
|
+
id: number | string;
|
|
7
7
|
name: string;
|
|
8
8
|
level?: number;
|
|
9
9
|
/** Goals may carry embedded factor links returned by the API. */
|
|
10
10
|
factor?: Array<{
|
|
11
|
-
id: number;
|
|
11
|
+
id: number | string;
|
|
12
12
|
}>;
|
|
13
13
|
}
|
|
14
14
|
export interface BdnChangeWithActivities {
|
|
15
|
-
id: number;
|
|
15
|
+
id: number | string;
|
|
16
16
|
name: string;
|
|
17
|
-
goal_id: number;
|
|
18
|
-
activity_ids: number
|
|
17
|
+
goal_id: number | string;
|
|
18
|
+
activity_ids: Array<number | string>;
|
|
19
19
|
}
|
|
20
20
|
export interface ActivityItem {
|
|
21
|
-
id: number;
|
|
21
|
+
id: number | string;
|
|
22
22
|
name: string;
|
|
23
|
-
goal_id?: number | null;
|
|
23
|
+
goal_id?: number | string | null;
|
|
24
24
|
activity_type_id?: number;
|
|
25
25
|
}
|
|
26
26
|
export interface ActivityTypeItem {
|
|
27
|
-
id: number;
|
|
27
|
+
id: number | string;
|
|
28
28
|
name?: string;
|
|
29
29
|
}
|
|
30
30
|
export interface DiagramStyle {
|
|
@@ -34,6 +34,6 @@ export interface DiagramStyle {
|
|
|
34
34
|
edgeWidth?: number;
|
|
35
35
|
}
|
|
36
36
|
/** The four columns of the FGCA diagram. */
|
|
37
|
-
export type FGCAColumn = "
|
|
37
|
+
export type FGCAColumn = "driver" | "goal" | "change" | "activity";
|
|
38
38
|
export declare const ALL_FGCA_COLUMNS: FGCAColumn[];
|
|
39
39
|
export declare const FGCA_COLUMN_LABELS: Record<FGCAColumn, string>;
|
package/dist/fgca/types.js
CHANGED
package/dist/fgca/validate.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DriverItem, GoalItem, BdnChangeWithActivities, ActivityItem } from './types.js';
|
|
2
2
|
import type { ValidationError, ValidationWarning, ValidationResult } from '../validation-types.js';
|
|
3
3
|
export type FGCAValidationError = ValidationError;
|
|
4
4
|
export type FGCAValidationWarning = ValidationWarning;
|
|
@@ -6,7 +6,7 @@ export type FGCAValidationResult = ValidationResult;
|
|
|
6
6
|
export interface FGCADoc {
|
|
7
7
|
notation: string;
|
|
8
8
|
spec_version?: string;
|
|
9
|
-
factors:
|
|
9
|
+
factors: DriverItem[];
|
|
10
10
|
goals: GoalItem[];
|
|
11
11
|
changes: BdnChangeWithActivities[];
|
|
12
12
|
activities: ActivityItem[];
|
package/dist/fgca/validate.js
CHANGED
|
@@ -9,10 +9,13 @@ export function validateFGCADoc(input) {
|
|
|
9
9
|
errors.push({ code: 'MISSING_NOTATION', message: 'notation field is required' });
|
|
10
10
|
return { valid: false, errors, warnings };
|
|
11
11
|
}
|
|
12
|
-
if (raw.notation !== 'fgca') {
|
|
13
|
-
errors.push({ code: 'WRONG_NOTATION', message: `notation must be "fgca", got "${String(raw.notation)}"` });
|
|
12
|
+
if (raw.notation !== 'fgca' && raw.notation !== 'dgca') {
|
|
13
|
+
errors.push({ code: 'WRONG_NOTATION', message: `notation must be "dgca" (or legacy "fgca"), got "${String(raw.notation)}"` });
|
|
14
14
|
return { valid: false, errors, warnings };
|
|
15
15
|
}
|
|
16
|
+
if (raw.notation === 'fgca') {
|
|
17
|
+
warnings.push({ code: 'DEPRECATED_NOTATION', message: 'notation "fgca" is deprecated — rename to "dgca"' });
|
|
18
|
+
}
|
|
16
19
|
if (!Array.isArray(raw.factors))
|
|
17
20
|
errors.push({ code: 'SCHEMA_INVALID', message: 'factors must be an array', path: 'factors' });
|
|
18
21
|
if (!Array.isArray(raw.goals))
|
|
@@ -29,7 +32,7 @@ export function validateFGCADoc(input) {
|
|
|
29
32
|
for (const el of arr) {
|
|
30
33
|
if (el && typeof el === 'object') {
|
|
31
34
|
const id = el.id;
|
|
32
|
-
if (typeof id === 'number')
|
|
35
|
+
if (typeof id === 'number' || typeof id === 'string')
|
|
33
36
|
out.add(id);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
@@ -45,8 +48,8 @@ export function validateFGCADoc(input) {
|
|
|
45
48
|
continue;
|
|
46
49
|
}
|
|
47
50
|
const factor = f;
|
|
48
|
-
if (typeof factor.id !== 'number')
|
|
49
|
-
errors.push({ code: 'SCHEMA_INVALID', message: 'factor id must be a number', path: `factors[${i}]` });
|
|
51
|
+
if (typeof factor.id !== 'number' && typeof factor.id !== 'string')
|
|
52
|
+
errors.push({ code: 'SCHEMA_INVALID', message: 'factor id must be a number or string', path: `factors[${i}]` });
|
|
50
53
|
if (typeof factor.name !== 'string' || !factor.name.trim())
|
|
51
54
|
errors.push({ code: 'EMPTY_NAME', message: `Factor ${String(factor.id)} has empty name`, path: `factors[${i}]` });
|
|
52
55
|
}
|
|
@@ -57,8 +60,8 @@ export function validateFGCADoc(input) {
|
|
|
57
60
|
continue;
|
|
58
61
|
}
|
|
59
62
|
const goal = g;
|
|
60
|
-
if (typeof goal.id !== 'number')
|
|
61
|
-
errors.push({ code: 'SCHEMA_INVALID', message: 'goal id must be a number', path: `goals[${i}]` });
|
|
63
|
+
if (typeof goal.id !== 'number' && typeof goal.id !== 'string')
|
|
64
|
+
errors.push({ code: 'SCHEMA_INVALID', message: 'goal id must be a number or string', path: `goals[${i}]` });
|
|
62
65
|
if (typeof goal.name !== 'string' || !goal.name.trim())
|
|
63
66
|
errors.push({ code: 'EMPTY_NAME', message: `Goal ${String(goal.id)} has empty name`, path: `goals[${i}]` });
|
|
64
67
|
const factorRefs = Array.isArray(goal.factor) ? goal.factor : [];
|
|
@@ -66,7 +69,7 @@ export function validateFGCADoc(input) {
|
|
|
66
69
|
if (!fr || typeof fr !== 'object')
|
|
67
70
|
continue;
|
|
68
71
|
const fid = fr.id;
|
|
69
|
-
if (typeof fid === 'number' && !factorIds.has(fid)) {
|
|
72
|
+
if ((typeof fid === 'number' || typeof fid === 'string') && !factorIds.has(fid)) {
|
|
70
73
|
warnings.push({ code: 'BROKEN_REF', message: `Goal ${String(goal.id)} references missing factor ${fid}`, path: `goals[${i}].factor` });
|
|
71
74
|
}
|
|
72
75
|
}
|
|
@@ -78,16 +81,16 @@ export function validateFGCADoc(input) {
|
|
|
78
81
|
continue;
|
|
79
82
|
}
|
|
80
83
|
const change = c;
|
|
81
|
-
if (typeof change.id !== 'number')
|
|
82
|
-
errors.push({ code: 'SCHEMA_INVALID', message: 'change id must be a number', path: `changes[${i}]` });
|
|
84
|
+
if (typeof change.id !== 'number' && typeof change.id !== 'string')
|
|
85
|
+
errors.push({ code: 'SCHEMA_INVALID', message: 'change id must be a number or string', path: `changes[${i}]` });
|
|
83
86
|
if (typeof change.name !== 'string' || !change.name.trim())
|
|
84
87
|
errors.push({ code: 'EMPTY_NAME', message: `Change ${String(change.id)} has empty name`, path: `changes[${i}]` });
|
|
85
|
-
if (typeof change.goal_id === 'number' && !goalIds.has(change.goal_id)) {
|
|
88
|
+
if ((typeof change.goal_id === 'number' || typeof change.goal_id === 'string') && !goalIds.has(change.goal_id)) {
|
|
86
89
|
warnings.push({ code: 'BROKEN_REF', message: `Change ${String(change.id)} references missing goal ${change.goal_id}`, path: `changes[${i}].goal_id` });
|
|
87
90
|
}
|
|
88
91
|
const aids = Array.isArray(change.activity_ids) ? change.activity_ids : [];
|
|
89
92
|
for (const aid of aids) {
|
|
90
|
-
if (typeof aid === 'number' && !activityIds.has(aid)) {
|
|
93
|
+
if ((typeof aid === 'number' || typeof aid === 'string') && !activityIds.has(aid)) {
|
|
91
94
|
warnings.push({ code: 'BROKEN_REF', message: `Change ${String(change.id)} references missing activity ${aid}`, path: `changes[${i}].activity_ids` });
|
|
92
95
|
}
|
|
93
96
|
}
|
|
@@ -99,8 +102,8 @@ export function validateFGCADoc(input) {
|
|
|
99
102
|
continue;
|
|
100
103
|
}
|
|
101
104
|
const activity = a;
|
|
102
|
-
if (typeof activity.id !== 'number')
|
|
103
|
-
errors.push({ code: 'SCHEMA_INVALID', message: 'activity id must be a number', path: `activities[${i}]` });
|
|
105
|
+
if (typeof activity.id !== 'number' && typeof activity.id !== 'string')
|
|
106
|
+
errors.push({ code: 'SCHEMA_INVALID', message: 'activity id must be a number or string', path: `activities[${i}]` });
|
|
104
107
|
if (typeof activity.name !== 'string' || !activity.name.trim())
|
|
105
108
|
errors.push({ code: 'EMPTY_NAME', message: `Activity ${String(activity.id)} has empty name`, path: `activities[${i}]` });
|
|
106
109
|
}
|
package/dist/goals/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { Goal, GoalType, GoalTree,
|
|
1
|
+
export type { Goal, GoalType, GoalTree, ImpactType, ValidationResult, ValidationError, ValidationWarning, MutationResult, LayoutOptions, LaidOutNode, LaidOutEdge, GoalTreeLayout, } from './types.js';
|
|
2
2
|
export { validateGoalTree } from './validate.js';
|
|
3
3
|
export { parseCanonicalGoals } from './parse-canonical.js';
|
|
4
4
|
export type { CanonicalGoalsResult } from './parse-canonical.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
export * from "./activities/index";
|
|
2
2
|
export * from "./activity-card/index";
|
|
3
|
+
export * from "./actor/index";
|
|
3
4
|
export * from "./geometry";
|
|
4
5
|
export * from "./applications/index";
|
|
5
6
|
export * from "./assertion/index";
|
|
6
7
|
export * from "./blocks/index";
|
|
7
8
|
export * from "./capability-map/index";
|
|
9
|
+
export * from "./change/index";
|
|
8
10
|
export * from "./compliance/index";
|
|
9
11
|
export * from "./compliance-matrix/index";
|
|
10
12
|
export * from "./confidence/index";
|
|
13
|
+
export * from "./factor/index";
|
|
11
14
|
export * from "./fgca/index";
|
|
12
15
|
export * from "./goals/index";
|
|
13
16
|
export * from "./process-blueprint/index";
|
|
@@ -16,6 +19,8 @@ export * from "./products/index";
|
|
|
16
19
|
export * from "./repo-validate/index";
|
|
17
20
|
export * from "./requirement/index";
|
|
18
21
|
export * from "./scenarios/index";
|
|
22
|
+
export * from "./stakeholder/index";
|
|
23
|
+
export * from "./target-state/index";
|
|
19
24
|
export { SCHEMA_VERSION } from "./schema-version";
|
|
20
25
|
export * from "./theme/index";
|
|
21
26
|
export * from "./typed-id";
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
export * from "./activities/index";
|
|
2
2
|
export * from "./activity-card/index";
|
|
3
|
+
export * from "./actor/index";
|
|
3
4
|
export * from "./geometry";
|
|
4
5
|
export * from "./applications/index";
|
|
5
6
|
export * from "./assertion/index";
|
|
6
7
|
export * from "./blocks/index";
|
|
7
8
|
export * from "./capability-map/index";
|
|
9
|
+
export * from "./change/index";
|
|
8
10
|
export * from "./compliance/index";
|
|
9
11
|
export * from "./compliance-matrix/index";
|
|
10
12
|
export * from "./confidence/index";
|
|
13
|
+
export * from "./factor/index";
|
|
11
14
|
export * from "./fgca/index";
|
|
12
15
|
export * from "./goals/index";
|
|
13
16
|
export * from "./process-blueprint/index";
|
|
@@ -16,6 +19,8 @@ export * from "./products/index";
|
|
|
16
19
|
export * from "./repo-validate/index";
|
|
17
20
|
export * from "./requirement/index";
|
|
18
21
|
export * from "./scenarios/index";
|
|
22
|
+
export * from "./stakeholder/index";
|
|
23
|
+
export * from "./target-state/index";
|
|
19
24
|
export { SCHEMA_VERSION } from "./schema-version";
|
|
20
25
|
export * from "./theme/index";
|
|
21
26
|
export * from "./typed-id";
|
|
@@ -156,4 +156,41 @@ describe('layoutProcessBlueprint', () => {
|
|
|
156
156
|
expect(layout.stageColumnWidth).toBe(300);
|
|
157
157
|
expect(layout.stageHeaders[1].x).toBe(200 + 300);
|
|
158
158
|
});
|
|
159
|
+
describe('visibleAspects', () => {
|
|
160
|
+
const FILE_WITH_ALL = build({
|
|
161
|
+
stages: [{ id: 'STAGE-1', name: 'A', goal: 'g', result: 'r' }],
|
|
162
|
+
systems: [{ name: 'OMS', stages: ['STAGE-1'] }],
|
|
163
|
+
actors: [{ name: 'Ops', stages: ['STAGE-1'] }],
|
|
164
|
+
equipment: [{ name: 'Printer', stages: ['STAGE-1'] }],
|
|
165
|
+
});
|
|
166
|
+
it('shows all categories when visibleAspects is not set', () => {
|
|
167
|
+
const layout = layoutProcessBlueprint(FILE_WITH_ALL);
|
|
168
|
+
expect(layout.aspectRows.map(r => r.category)).toEqual(expect.arrayContaining(['systems', 'actors', 'equipment']));
|
|
169
|
+
expect(layout.aspectRows).toHaveLength(3);
|
|
170
|
+
});
|
|
171
|
+
it('limits rows to those in visibleAspects', () => {
|
|
172
|
+
const layout = layoutProcessBlueprint(FILE_WITH_ALL, {
|
|
173
|
+
visibleAspects: ['systems'],
|
|
174
|
+
});
|
|
175
|
+
expect(layout.aspectRows).toHaveLength(1);
|
|
176
|
+
expect(layout.aspectRows[0].category).toBe('systems');
|
|
177
|
+
});
|
|
178
|
+
it('suppresses all aspect rows when visibleAspects is empty', () => {
|
|
179
|
+
const layout = layoutProcessBlueprint(FILE_WITH_ALL, { visibleAspects: [] });
|
|
180
|
+
expect(layout.aspectRows).toHaveLength(0);
|
|
181
|
+
});
|
|
182
|
+
it('preserves the canonical ASPECT_CATEGORIES order within the filtered set', () => {
|
|
183
|
+
const layout = layoutProcessBlueprint(FILE_WITH_ALL, {
|
|
184
|
+
visibleAspects: ['equipment', 'systems'],
|
|
185
|
+
});
|
|
186
|
+
expect(layout.aspectRows.map(r => r.category)).toEqual(['systems', 'equipment']);
|
|
187
|
+
});
|
|
188
|
+
it('ignores categories in visibleAspects that have no data', () => {
|
|
189
|
+
const layout = layoutProcessBlueprint(FILE_WITH_ALL, {
|
|
190
|
+
visibleAspects: ['systems', 'information_entities'],
|
|
191
|
+
});
|
|
192
|
+
expect(layout.aspectRows).toHaveLength(1);
|
|
193
|
+
expect(layout.aspectRows[0].category).toBe('systems');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
159
196
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { AspectCategory, Stage, AspectEntry, LaneConfig, ProcessBlueprintHeader, ProcessBlueprintFile, ProcessBlueprintLayoutOptions, LegendCell, StageHeaderCell, StageTextCell, AspectPill, AspectRow, ProcessBlueprintLayout, ComplianceDecoration, ComplianceChip, ComplianceRow, ComplianceLaneConfig, ComplianceLaneAssertion, ComplianceLaneRequirement, ComplianceLaneInput, } from './types.js';
|
|
1
|
+
export type { AspectCategory, RowId, Stage, AspectEntry, LaneConfig, ProcessBlueprintHeader, ProcessBlueprintFile, ProcessBlueprintLayoutOptions, LegendCell, StageHeaderCell, StageTextCell, AspectPill, AspectRow, ProcessBlueprintLayout, ComplianceDecoration, ComplianceChip, ComplianceRow, ComplianceLaneConfig, ComplianceLaneAssertion, ComplianceLaneRequirement, ComplianceLaneInput, } from './types.js';
|
|
2
2
|
export { validateProcessBlueprint } from './validate.js';
|
|
3
3
|
export type { ValidationError as ProcessBlueprintValidationError, ValidationWarning as ProcessBlueprintValidationWarning, ValidationResult as ProcessBlueprintValidationResult, } from './validate.js';
|
|
4
4
|
export { layoutProcessBlueprint } from './layout.js';
|
|
@@ -12,14 +12,19 @@ const DEFAULTS = {
|
|
|
12
12
|
goalRowHeight: 56,
|
|
13
13
|
resultRowHeight: 56,
|
|
14
14
|
aspectRowMinHeight: 60,
|
|
15
|
-
pillHeight:
|
|
15
|
+
pillHeight: 40,
|
|
16
16
|
pillGap: 4,
|
|
17
17
|
cellPadding: 8,
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
// 17px gives comfortable breathing room between wrapped lines at typical
|
|
19
|
+
// webview font sizes (VS Code uses ~13–14px body, SVG text-secondary ≈ same).
|
|
20
|
+
textLineHeight: 17,
|
|
21
|
+
// 7.5px per char is a conservative estimate that causes wrap to trigger
|
|
22
|
+
// before text visually overflows the cell border.
|
|
23
|
+
textCharWidth: 7.5,
|
|
20
24
|
cellTextPadX: 10,
|
|
21
25
|
cellTextPadY: 10,
|
|
22
|
-
|
|
26
|
+
// No hard truncation: rows grow to fit all wrapped lines.
|
|
27
|
+
maxTextLines: 100,
|
|
23
28
|
};
|
|
24
29
|
function resolveOptions(options) {
|
|
25
30
|
return { ...DEFAULTS, ...(options ?? {}) };
|
|
@@ -285,8 +290,21 @@ function deriveComplianceRow(stages, stageIndexById, input, opts, yStart) {
|
|
|
285
290
|
export function layoutProcessBlueprint(file, options) {
|
|
286
291
|
const opts = resolveOptions(options);
|
|
287
292
|
const pb = file.process_blueprint;
|
|
288
|
-
|
|
293
|
+
// Filter stages by visibleStages if set
|
|
294
|
+
const allStages = Array.isArray(pb?.stages) ? pb.stages : [];
|
|
295
|
+
const stages = opts.visibleStages
|
|
296
|
+
? allStages.filter(s => s?.id != null && opts.visibleStages.includes(s.id))
|
|
297
|
+
: allStages;
|
|
289
298
|
const stageIndexById = buildStageIndex(stages);
|
|
299
|
+
// Row visibility from visibleRows
|
|
300
|
+
const visRows = opts.visibleRows;
|
|
301
|
+
const showGoal = !visRows || visRows.includes('goal');
|
|
302
|
+
const showResult = !visRows || visRows.includes('result');
|
|
303
|
+
const effectiveVisibleAspects = visRows
|
|
304
|
+
? ASPECT_CATEGORIES.filter(c => visRows.includes(c))
|
|
305
|
+
: opts.visibleAspects;
|
|
306
|
+
const effectiveComplianceEnabled = opts.complianceLane?.enabled === true &&
|
|
307
|
+
(!visRows || visRows.includes('compliance'));
|
|
290
308
|
// Stage headers
|
|
291
309
|
const stageHeaders = stages.map((s, i) => ({
|
|
292
310
|
stageIndex: i,
|
|
@@ -307,33 +325,40 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
307
325
|
};
|
|
308
326
|
const goalLines = stages.map(s => wrap(s?.goal ?? ''));
|
|
309
327
|
const resultLines = stages.map(s => wrap(s?.result ?? ''));
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
328
|
+
let cursorY = opts.stageHeaderHeight;
|
|
329
|
+
let goalRowY = 0, goalRowHeight = 0;
|
|
330
|
+
let resultRowY = 0, resultRowHeight = 0;
|
|
331
|
+
const goalCells = [];
|
|
332
|
+
const resultCells = [];
|
|
333
|
+
if (showGoal) {
|
|
334
|
+
goalRowY = cursorY;
|
|
335
|
+
goalRowHeight = rowHeightFor(goalLines.map(l => l.length), opts.goalRowHeight);
|
|
336
|
+
goalCells.push(...stages.map((s, i) => ({
|
|
337
|
+
stageIndex: i, text: s?.goal ?? '', lines: goalLines[i],
|
|
338
|
+
x: opts.legendColumnWidth + i * opts.stageColumnWidth, y: goalRowY,
|
|
339
|
+
width: opts.stageColumnWidth, height: goalRowHeight,
|
|
340
|
+
})));
|
|
341
|
+
cursorY += goalRowHeight;
|
|
342
|
+
}
|
|
343
|
+
if (showResult) {
|
|
344
|
+
resultRowY = cursorY;
|
|
345
|
+
resultRowHeight = rowHeightFor(resultLines.map(l => l.length), opts.resultRowHeight);
|
|
346
|
+
resultCells.push(...stages.map((s, i) => ({
|
|
347
|
+
stageIndex: i, text: s?.result ?? '', lines: resultLines[i],
|
|
348
|
+
x: opts.legendColumnWidth + i * opts.stageColumnWidth, y: resultRowY,
|
|
349
|
+
width: opts.stageColumnWidth, height: resultRowHeight,
|
|
350
|
+
})));
|
|
351
|
+
cursorY += resultRowHeight;
|
|
352
|
+
}
|
|
353
|
+
const aspectsStartY = cursorY;
|
|
333
354
|
// Aspect rows: only categories with at least one entry, in fixed order.
|
|
355
|
+
// When effectiveVisibleAspects is set, skip categories not in the list.
|
|
356
|
+
const aspectCategoriesToRender = effectiveVisibleAspects !== undefined
|
|
357
|
+
? ASPECT_CATEGORIES.filter(c => effectiveVisibleAspects.includes(c))
|
|
358
|
+
: ASPECT_CATEGORIES;
|
|
334
359
|
const aspectRows = [];
|
|
335
360
|
let aspectCursorY = aspectsStartY;
|
|
336
|
-
for (const category of
|
|
361
|
+
for (const category of aspectCategoriesToRender) {
|
|
337
362
|
const arr = pb?.[category];
|
|
338
363
|
if (!Array.isArray(arr) || arr.length === 0)
|
|
339
364
|
continue;
|
|
@@ -354,6 +379,13 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
354
379
|
const x = opts.legendColumnWidth + p.startStageIndex * opts.stageColumnWidth + opts.cellPadding;
|
|
355
380
|
const width = (p.endStageIndex - p.startStageIndex + 1) * opts.stageColumnWidth - 2 * opts.cellPadding;
|
|
356
381
|
const y = aspectCursorY + opts.cellPadding + slot[i] * (opts.pillHeight + opts.pillGap);
|
|
382
|
+
const pillMaxChars = Math.max(4, Math.floor((width - 2 * opts.cellPadding) / opts.textCharWidth));
|
|
383
|
+
const nameWrapped = wrapText(p.name, pillMaxChars, 2);
|
|
384
|
+
// Show id as second line only when the name fits on one line; otherwise
|
|
385
|
+
// use both lines for the name so it isn't truncated.
|
|
386
|
+
const lines = p.id && nameWrapped.length <= 1
|
|
387
|
+
? [...nameWrapped, p.id].slice(0, 2)
|
|
388
|
+
: nameWrapped;
|
|
357
389
|
return {
|
|
358
390
|
category: p.category,
|
|
359
391
|
entryIndex: p.entryIndex,
|
|
@@ -365,6 +397,7 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
365
397
|
y,
|
|
366
398
|
width,
|
|
367
399
|
height: opts.pillHeight,
|
|
400
|
+
lines,
|
|
368
401
|
};
|
|
369
402
|
});
|
|
370
403
|
aspectRows.push({
|
|
@@ -377,7 +410,7 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
377
410
|
}
|
|
378
411
|
// Compliance lane — derived from assertions/requirements, rendered after aspect rows.
|
|
379
412
|
let complianceRow;
|
|
380
|
-
if (
|
|
413
|
+
if (effectiveComplianceEnabled && opts.complianceInput) {
|
|
381
414
|
complianceRow = deriveComplianceRow(stages, stageIndexById, opts.complianceInput, opts, aspectCursorY);
|
|
382
415
|
if (complianceRow) {
|
|
383
416
|
aspectCursorY += complianceRow.height;
|
|
@@ -385,8 +418,8 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
385
418
|
}
|
|
386
419
|
// Legend (left column labels): one entry per row.
|
|
387
420
|
const legend = [
|
|
388
|
-
{ kind: 'goal', label: 'Goal', y: goalRowY, height: goalRowHeight },
|
|
389
|
-
{ kind: 'result', label: 'Result', y: resultRowY, height: resultRowHeight },
|
|
421
|
+
...(showGoal ? [{ kind: 'goal', label: 'Goal', y: goalRowY, height: goalRowHeight }] : []),
|
|
422
|
+
...(showResult ? [{ kind: 'result', label: 'Result', y: resultRowY, height: resultRowHeight }] : []),
|
|
390
423
|
...aspectRows.map(row => ({
|
|
391
424
|
kind: 'aspect',
|
|
392
425
|
category: row.category,
|
|
@@ -394,9 +427,7 @@ export function layoutProcessBlueprint(file, options) {
|
|
|
394
427
|
y: row.y,
|
|
395
428
|
height: row.height,
|
|
396
429
|
})),
|
|
397
|
-
...(complianceRow
|
|
398
|
-
? [{ kind: 'compliance', label: 'Compliance', y: complianceRow.y, height: complianceRow.height }]
|
|
399
|
-
: []),
|
|
430
|
+
...(complianceRow ? [{ kind: 'compliance', label: 'Compliance', y: complianceRow.y, height: complianceRow.height }] : []),
|
|
400
431
|
];
|
|
401
432
|
const totalWidth = opts.legendColumnWidth + stages.length * opts.stageColumnWidth;
|
|
402
433
|
const totalHeight = aspectCursorY;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type AspectCategory = 'systems' | 'actors' | 'equipment' | 'information_entities';
|
|
2
|
+
export type RowId = 'goal' | 'result' | 'systems' | 'actors' | 'equipment' | 'information_entities' | 'compliance';
|
|
2
3
|
/**
|
|
3
4
|
* One of the three orthogonal decoration signals that can appear on a compliance
|
|
4
5
|
* law chip (ADR 2026-06-09-compliance-impact-as-blueprint-lane §3):
|
|
@@ -24,7 +25,7 @@ export interface ComplianceLaneAssertion {
|
|
|
24
25
|
/** Requirement ID this assertion is about. */
|
|
25
26
|
about: string;
|
|
26
27
|
/** Compliance realisation status. */
|
|
27
|
-
status: 'compliant' | 'partial' | 'non_compliant' | 'under_review' | 'n_a';
|
|
28
|
+
status: 'compliant' | 'partial' | 'non_compliant' | 'under_review' | 'pending_owner' | 'n_a';
|
|
28
29
|
/**
|
|
29
30
|
* Typed IDs of stages / process steps where the requirement is realised.
|
|
30
31
|
* When empty/absent, the assertion covers the entire subject, not a specific stage.
|
|
@@ -106,7 +107,22 @@ export interface LaneConfig {
|
|
|
106
107
|
compliance_filter?: {
|
|
107
108
|
/** ISO jurisdiction codes; empty array means all jurisdictions. */
|
|
108
109
|
jurisdictions?: string[];
|
|
110
|
+
/**
|
|
111
|
+
* Per-stage map of law IDs known from the previous generated snapshot.
|
|
112
|
+
* Key: stage ID; Value: array of law IDs present in that stage's previous report.
|
|
113
|
+
* Laws absent from the previous snapshot receive the `new` decoration.
|
|
114
|
+
* When absent, every law chip is treated as new.
|
|
115
|
+
*/
|
|
116
|
+
previous_snapshot?: Record<string, string[]>;
|
|
109
117
|
};
|
|
118
|
+
/**
|
|
119
|
+
* Lanes to show; controls which aspect rows and the compliance lane are rendered.
|
|
120
|
+
* Valid values: `"systems"` | `"actors"` | `"equipment"` | `"information_entities"` | `"compliance"`.
|
|
121
|
+
* When absent, all lanes with data are shown (current default behaviour).
|
|
122
|
+
* Named report config — committed, versioned. Per-user overrides live in
|
|
123
|
+
* `.transitrix/display-preferences/process-blueprint.json` (gitignored, never committed).
|
|
124
|
+
*/
|
|
125
|
+
visible_lanes?: string[];
|
|
110
126
|
}
|
|
111
127
|
export interface ProcessBlueprintHeader {
|
|
112
128
|
id: string;
|
|
@@ -155,6 +171,23 @@ export interface ProcessBlueprintLayoutOptions {
|
|
|
155
171
|
complianceLane?: ComplianceLaneConfig;
|
|
156
172
|
/** Assertion + requirement data for the compliance lane derivation. */
|
|
157
173
|
complianceInput?: ComplianceLaneInput;
|
|
174
|
+
/**
|
|
175
|
+
* Restrict which aspect-category rows are rendered.
|
|
176
|
+
* When absent, all categories that have at least one entry are shown.
|
|
177
|
+
* Pass an empty array to suppress all aspect rows.
|
|
178
|
+
*/
|
|
179
|
+
visibleAspects?: AspectCategory[];
|
|
180
|
+
/**
|
|
181
|
+
* Restrict which rows are rendered. When absent, all rows with data are shown.
|
|
182
|
+
* When set, takes precedence over `visibleAspects` for aspect categories and
|
|
183
|
+
* overrides `complianceLane.enabled` for the compliance row.
|
|
184
|
+
*/
|
|
185
|
+
visibleRows?: RowId[];
|
|
186
|
+
/**
|
|
187
|
+
* Restrict which stages (columns) are rendered by their stage ID.
|
|
188
|
+
* When absent, all stages are shown.
|
|
189
|
+
*/
|
|
190
|
+
visibleStages?: string[];
|
|
158
191
|
}
|
|
159
192
|
import type { LayoutBounds } from '../geometry.js';
|
|
160
193
|
export type { LayoutBounds };
|
|
@@ -195,6 +228,8 @@ export interface AspectPill {
|
|
|
195
228
|
y: number;
|
|
196
229
|
width: number;
|
|
197
230
|
height: number;
|
|
231
|
+
/** Word-wrapped lines of `name`, pre-fitted to the pill width by the layout. */
|
|
232
|
+
lines: string[];
|
|
198
233
|
}
|
|
199
234
|
export interface AspectRow {
|
|
200
235
|
category: AspectCategory;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type { ScenarioStatus,
|
|
1
|
+
export type { ScenarioStatus, DriverRelevance, DriverView, GoalRef, CapabilityRef, ActivityRef, ProductRef, ProcessRef, ApplicationRef, ScenarioHeader, ScenarioFile, } from './types.js';
|
|
2
2
|
export { validateScenario } from './validate.js';
|