@transitrix/diagrams 1.15.0 → 1.16.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.
Files changed (39) hide show
  1. package/dist/compliance/catalogue.d.ts +17 -0
  2. package/dist/compliance/catalogue.js +68 -0
  3. package/dist/compliance/index.d.ts +4 -2
  4. package/dist/compliance/index.js +2 -1
  5. package/dist/compliance/requirement-chain.d.ts +13 -2
  6. package/dist/compliance/requirement-chain.js +60 -33
  7. package/dist/products/types.d.ts +1 -1
  8. package/dist/products/validate.js +1 -1
  9. package/dist/standalone/validate.js +64 -3
  10. package/dist/webview/render-activities.d.ts +6 -0
  11. package/dist/webview/render-activities.js +2 -2
  12. package/dist/webview/render-activity-card.d.ts +6 -0
  13. package/dist/webview/render-activity-card.js +2 -2
  14. package/dist/webview/render-blocks.d.ts +10 -0
  15. package/dist/webview/render-blocks.js +4 -4
  16. package/dist/webview/render-fgca.d.ts +6 -0
  17. package/dist/webview/render-fgca.js +2 -2
  18. package/dist/webview/render-goals.d.ts +5 -0
  19. package/dist/webview/render-goals.js +9 -2
  20. package/dist/webview/render-process-blueprint.d.ts +6 -0
  21. package/dist/webview/render-process-blueprint.js +2 -2
  22. package/dist/webview/render-products.js +1 -0
  23. package/dist/yaml-normalize.d.ts +3 -1
  24. package/dist/yaml-normalize.js +11 -3
  25. package/package.json +1 -1
  26. package/src/compliance/catalogue.ts +49 -0
  27. package/src/compliance/index.ts +5 -2
  28. package/src/compliance/requirement-chain.ts +52 -27
  29. package/src/products/types.ts +1 -1
  30. package/src/products/validate.ts +1 -1
  31. package/src/standalone/validate.ts +38 -2
  32. package/src/webview/render-activities.ts +8 -2
  33. package/src/webview/render-activity-card.ts +8 -3
  34. package/src/webview/render-blocks.ts +14 -4
  35. package/src/webview/render-fgca.ts +8 -2
  36. package/src/webview/render-goals.ts +21 -2
  37. package/src/webview/render-process-blueprint.ts +8 -3
  38. package/src/webview/render-products.ts +1 -0
  39. package/src/yaml-normalize.ts +12 -3
@@ -24,7 +24,7 @@ import {
24
24
  import type { FGCADoc } from '../fgca/validate.js';
25
25
  import { previewEdgePath, DEFAULT_EDGE_CURVATURE, type EdgeStyle } from '../edge-path.js';
26
26
  import { parseNodeSizePreset, resolveDgcaNodeSize, type NodeSizePreset } from '../node-size-presets.js';
27
- import { generateSvgEmbedCss } from '../theme/index.js';
27
+ import { generateSvgEmbedCss, type ThemeId } from '../theme/index.js';
28
28
  import { emitCenteredTextSvg, layoutCenteredEntityText, truncateLine } from './entity-text-layout.js';
29
29
  import { escXml } from './render-util.js';
30
30
 
@@ -137,6 +137,11 @@ export interface RenderFgcaOptions {
137
137
  scopeCaption?: boolean;
138
138
  /** Show completion percent badges on action nodes. */
139
139
  showCompletionPercent?: boolean;
140
+ /**
141
+ * Theme CSS embedded as `<style>` so the SVG is self-contained.
142
+ * Defaults to `'transitrix'`. VS Code live preview uses body emitters + webview CSS instead.
143
+ */
144
+ embedCssTheme?: ThemeId;
140
145
  }
141
146
 
142
147
  export function renderFgcaSvg(doc: FGCADoc, options: RenderFgcaOptions = {}): string {
@@ -150,6 +155,7 @@ export function renderFgcaSvg(doc: FGCADoc, options: RenderFgcaOptions = {}): st
150
155
  layoutOptions,
151
156
  scopeCaption = false,
152
157
  showCompletionPercent = true,
158
+ embedCssTheme = 'transitrix',
153
159
  } = options;
154
160
  const hideChanges = variant === 'dga' || doc.hideChanges === true;
155
161
  const nodeSize = resolveDgcaNodeSize(parseNodeSizePreset(nodeSizePreset));
@@ -180,7 +186,7 @@ export function renderFgcaSvg(doc: FGCADoc, options: RenderFgcaOptions = {}): st
180
186
  ? `<text class="text-caption" x="${PAD}" y="${height + 16}">${escXml('This diagram is shown for the selected goal scope. Actions on it may also serve goals outside that scope.')}</text>`
181
187
  : '';
182
188
 
183
- const embedCss = generateSvgEmbedCss('transitrix');
189
+ const embedCss = generateSvgEmbedCss(embedCssTheme);
184
190
 
185
191
  // Calculate final height including caption if present
186
192
  const finalHeight = scopeCaption ? height + 24 : height;
@@ -30,6 +30,11 @@ export interface RenderGoalsOptions {
30
30
  entryCurvature?: number;
31
31
  edgeStyle?: EdgeStyle;
32
32
  nodeSizePreset?: NodeSizePreset;
33
+ /**
34
+ * Theme CSS embedded via {@link renderGoalsLayoutSvg}.
35
+ * Defaults to `'transitrix'` for self-contained host-neutral SVG.
36
+ */
37
+ embedCssTheme?: ThemeId;
33
38
  }
34
39
 
35
40
  /**
@@ -38,7 +43,14 @@ export interface RenderGoalsOptions {
38
43
  * with the shared theme CSS embedded so the output is self-contained.
39
44
  */
40
45
  export function renderGoalsSvg(tree: GoalTree, options: RenderGoalsOptions = {}): string {
41
- const { treeName = '', curvature = DEFAULT_EDGE_CURVATURE, entryCurvature, edgeStyle, nodeSizePreset = 'normal' } = options;
46
+ const {
47
+ treeName = '',
48
+ curvature = DEFAULT_EDGE_CURVATURE,
49
+ entryCurvature,
50
+ edgeStyle,
51
+ nodeSizePreset = 'normal',
52
+ embedCssTheme = 'transitrix',
53
+ } = options;
42
54
  const nodeSize = resolveGoalsNodeSize(parseNodeSizePreset(nodeSizePreset));
43
55
 
44
56
  const layout: GoalTreeLayout = layoutGoalTree(tree, {
@@ -57,7 +69,14 @@ export function renderGoalsSvg(tree: GoalTree, options: RenderGoalsOptions = {})
57
69
  : '';
58
70
 
59
71
  // Reserve a separate header row; the theme centers text on its y coordinate.
60
- return renderGoalsLayoutSvg(layout, { curvature, entryCurvature, edgeStyle, title, topInset: title ? PAD : 0, embedCssTheme: 'transitrix' });
72
+ return renderGoalsLayoutSvg(layout, {
73
+ curvature,
74
+ entryCurvature,
75
+ edgeStyle,
76
+ title,
77
+ topInset: title ? PAD : 0,
78
+ embedCssTheme,
79
+ });
61
80
  }
62
81
 
63
82
  export interface RenderGoalsLayoutOptions {
@@ -21,7 +21,7 @@ import {
21
21
  resolveProcessBlueprintSize,
22
22
  type NodeSizePreset,
23
23
  } from '../node-size-presets.js';
24
- import { generateSvgEmbedCss } from '../theme/index.js';
24
+ import { generateSvgEmbedCss, type ThemeId } from '../theme/index.js';
25
25
  import {
26
26
  layoutCenteredPillText,
27
27
  layoutLeftCellLines,
@@ -206,13 +206,18 @@ export interface RenderProcessBlueprintOptions {
206
206
  title?: string;
207
207
  nodeSizePreset?: NodeSizePreset;
208
208
  layoutOptions?: ProcessBlueprintLayoutOptions;
209
+ /**
210
+ * Theme CSS embedded as `<style>` so the SVG is self-contained.
211
+ * Defaults to `'transitrix'`. VS Code live preview uses body emitters + webview CSS instead.
212
+ */
213
+ embedCssTheme?: ThemeId;
209
214
  }
210
215
 
211
216
  export function renderProcessBlueprintSvg(
212
217
  doc: ProcessBlueprintFile,
213
218
  options: RenderProcessBlueprintOptions = {},
214
219
  ): string {
215
- const { title = '', nodeSizePreset = 'normal', layoutOptions } = options;
220
+ const { title = '', nodeSizePreset = 'normal', layoutOptions, embedCssTheme = 'transitrix' } = options;
216
221
  const sizing = resolveProcessBlueprintSize(parseNodeSizePreset(nodeSizePreset));
217
222
 
218
223
  const layout: ProcessBlueprintLayout = layoutProcessBlueprint(doc, {
@@ -236,7 +241,7 @@ export function renderProcessBlueprintSvg(
236
241
  ? `<text class="text-header" x="${PAD}" y="${PAD - 6}">${escXml(title)}</text>`
237
242
  : '';
238
243
 
239
- const embedCss = generateSvgEmbedCss('transitrix') + BP_EMBED_CSS;
244
+ const embedCss = generateSvgEmbedCss(embedCssTheme) + BP_EMBED_CSS;
240
245
 
241
246
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}">
242
247
  <style>${embedCss}</style>
@@ -7,6 +7,7 @@ import type { Product, ProductsCatalogueHeader } from '../products/types.js';
7
7
  import { escHtml } from './render-util.js';
8
8
 
9
9
  const TYPE_LABEL: Record<string, string> = {
10
+ physical_product: 'Physical Product',
10
11
  digital_product: 'Digital Product',
11
12
  service: 'Service',
12
13
  platform: 'Platform',
@@ -15,22 +15,31 @@
15
15
  * A bare `2026-06-01` parses to midnight UTC and round-trips to
16
16
  * `"2026-06-01"` regardless of `process.env.TZ`.
17
17
  *
18
- * Walks in place and returns the same reference for convenience.
18
+ * Walks in place and returns the same reference for convenience. Shared
19
+ * object graphs from YAML aliases (and cycles) are visited once via WeakSet
20
+ * so diamond / recursive anchors cannot amplify the walk into a hang.
19
21
  */
20
22
  export function coerceDatesToIsoStrings<T>(value: T): T {
23
+ return walk(value, new WeakSet<object>());
24
+ }
25
+
26
+ function walk<T>(value: T, seen: WeakSet<object>): T {
21
27
  if (value === null || value === undefined) return value;
22
28
  if (value instanceof Date) {
23
29
  return dateToIsoYmd(value) as unknown as T;
24
30
  }
25
31
  if (typeof value !== 'object') return value;
26
32
 
33
+ if (seen.has(value as object)) return value;
34
+ seen.add(value as object);
35
+
27
36
  if (Array.isArray(value)) {
28
37
  for (let i = 0; i < value.length; i++) {
29
38
  const v = value[i];
30
39
  if (v instanceof Date) {
31
40
  value[i] = dateToIsoYmd(v) as unknown as typeof v;
32
41
  } else if (v !== null && typeof v === 'object') {
33
- coerceDatesToIsoStrings(v);
42
+ walk(v, seen);
34
43
  }
35
44
  }
36
45
  return value;
@@ -42,7 +51,7 @@ export function coerceDatesToIsoStrings<T>(value: T): T {
42
51
  if (v instanceof Date) {
43
52
  obj[key] = dateToIsoYmd(v);
44
53
  } else if (v !== null && typeof v === 'object') {
45
- coerceDatesToIsoStrings(v);
54
+ walk(v, seen);
46
55
  }
47
56
  }
48
57
  return value;