@diagrammo/dgmo 0.8.1 → 0.8.3

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.
@@ -49,9 +49,6 @@ export function extractColor(
49
49
  };
50
50
  }
51
51
 
52
- /** @deprecated Matches `chart: <type>` header lines. Remove after all parsers migrate. */
53
- export const CHART_TYPE_RE = /^chart\s*:\s*(.+)/i;
54
-
55
52
  /** @deprecated Matches `title: <text>` header lines. Remove after all parsers migrate. */
56
53
  export const TITLE_RE = /^title\s*:\s*(.+)/i;
57
54
 
@@ -61,11 +58,6 @@ export const OPTION_RE = /^([a-z][a-z0-9-]*)\s*:\s*(.+)$/i;
61
58
  /** Matches `option value` header lines (space-separated, no colon). */
62
59
  export const OPTION_NOCOLON_RE = /^([a-z][a-z0-9-]*)\s+(.+)$/i;
63
60
 
64
- /** Matches `# GroupName` lines — alternate group notation. */
65
- export const GROUP_HASH_RE = /^#\s+(.+)$/;
66
-
67
- /** Matches `## ...` lines — parse error with helpful hint. */
68
- export const DOUBLE_HASH_RE = /^##\s/;
69
61
 
70
62
  // ── New shared utilities ─────────────────────────────────────
71
63
 
@@ -81,24 +73,7 @@ export function parseFirstLine(
81
73
  const trimmed = line.trim();
82
74
  if (!trimmed || trimmed.startsWith('//')) return null;
83
75
 
84
- // Try old-style `chart: type` first (for transition)
85
- const oldMatch = trimmed.match(CHART_TYPE_RE);
86
- if (oldMatch) {
87
- const parts = oldMatch[1].trim();
88
- // Could be `chart: gantt My Title` — first token is type
89
- const spaceIdx = parts.indexOf(' ');
90
- if (spaceIdx === -1) {
91
- const ct = parts.toLowerCase();
92
- return ALL_CHART_TYPES.has(ct) ? { chartType: ct, title: undefined } : null;
93
- }
94
- const ct = parts.substring(0, spaceIdx).toLowerCase();
95
- if (ALL_CHART_TYPES.has(ct)) {
96
- return { chartType: ct, title: parts.substring(spaceIdx + 1).trim() || undefined };
97
- }
98
- return null;
99
- }
100
-
101
- // New-style: first token is chart type, rest is title
76
+ // First token is chart type, rest is title
102
77
  const spaceIdx = trimmed.indexOf(' ');
103
78
  if (spaceIdx === -1) {
104
79
  const ct = trimmed.toLowerCase();
@@ -330,17 +305,7 @@ export function parseSeriesNames(
330
305
  return { series, names, nameColors, nameLineNumbers, newIndex };
331
306
  }
332
307
 
333
- /**
334
- * Normalize a direction/orientation value to canonical form ('LR' | 'TB').
335
- * Accepts 'lr', 'tb', 'horizontal', 'vertical' (case-insensitive).
336
- * Returns null if the value is not recognized.
337
- */
338
- export function normalizeDirection(value: string): 'LR' | 'TB' | null {
339
- const v = value.trim().toLowerCase();
340
- if (v === 'lr' || v === 'horizontal') return 'LR';
341
- if (v === 'tb' || v === 'vertical') return 'TB';
342
- return null;
343
- }
308
+
344
309
 
345
310
  /**
346
311
  * Infer arrow color from label text.
@@ -358,22 +323,23 @@ export function inferArrowColor(label: string): string | undefined {
358
323
  return undefined;
359
324
  }
360
325
 
361
- /** Warning message for multiple pipes on a single line. */
362
- export const MULTIPLE_PIPE_WARNING =
326
+ /** Error message for multiple pipes on a single line. */
327
+ export const MULTIPLE_PIPE_ERROR =
363
328
  'Use a single "|" to start metadata, then separate items with commas.';
364
329
 
365
330
  /**
366
331
  * Parse metadata from segments after the first (name) segment.
367
332
  * A single `|` separates the label from metadata; items after the pipe are comma-delimited.
368
- * Multiple pipes are treated as commas for backward compatibility but trigger a warning.
333
+ * Multiple pipes produce an error.
369
334
  */
370
335
  export function parsePipeMetadata(
371
336
  segments: string[],
372
337
  aliasMap: Map<string, string> = new Map(),
373
- warnMultiplePipes?: () => void,
338
+ errorMultiplePipes?: () => void,
374
339
  ): Record<string, string> {
375
- if (segments.length > 2 && warnMultiplePipes) {
376
- warnMultiplePipes();
340
+ if (segments.length > 2) {
341
+ if (errorMultiplePipes) errorMultiplePipes();
342
+ return {};
377
343
  }
378
344
  const metadata: Record<string, string> = {};
379
345
  const raw = segments.slice(1).join(',');
@@ -26,25 +26,15 @@ export interface TagBlockMatch {
26
26
  name: string;
27
27
  alias: string | undefined;
28
28
  colorHint: string | undefined;
29
- /** true when the heading used `## …` (deprecated) */
30
- deprecated: boolean;
31
29
  /** Inline tag values parsed from single-line form (e.g., `tag Priority p High(red), Low(blue)`) */
32
30
  inlineValues?: string[];
33
31
  }
34
32
 
35
33
  // ── Regexes ─────────────────────────────────────────────────
36
34
 
37
- /** @deprecated Old syntax: `tag: GroupName [alias X] [(color)]` remove after migration. */
38
- export const TAG_BLOCK_RE =
39
- /^tag:\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/i;
40
-
41
- /** New canonical syntax: line starting with `tag` keyword (no colon). */
35
+ /** Canonical syntax: line starting with `tag` keyword (no colon). */
42
36
  export const TAG_BLOCK_NOCOLON_RE = /^tag\s+/i;
43
37
 
44
- /** @deprecated Legacy syntax: `## GroupName [alias X] [(color)]` */
45
- export const GROUP_HEADING_RE =
46
- /^##\s+(.+?)(?:\s+alias\s+(\w+))?(?:\s*\(([^)]+)\))?\s*$/;
47
-
48
38
  // ── Alias Inference ─────────────────────────────────────────
49
39
 
50
40
  /** Returns true if the token looks like an alias: 1-4 lowercase ASCII characters. */
@@ -54,9 +44,9 @@ function isAliasToken(token: string): boolean {
54
44
 
55
45
  // ── Matchers ────────────────────────────────────────────────
56
46
 
57
- /** Returns true if `trimmed` is a tag block heading in any syntax. */
47
+ /** Returns true if `trimmed` is a tag block heading. */
58
48
  export function isTagBlockHeading(trimmed: string): boolean {
59
- return TAG_BLOCK_NOCOLON_RE.test(trimmed) || TAG_BLOCK_RE.test(trimmed) || GROUP_HEADING_RE.test(trimmed);
49
+ return TAG_BLOCK_NOCOLON_RE.test(trimmed);
60
50
  }
61
51
 
62
52
  /**
@@ -173,7 +163,6 @@ export function parseTagDeclaration(line: string): TagBlockMatch | null {
173
163
  name,
174
164
  alias,
175
165
  colorHint,
176
- deprecated: false,
177
166
  inlineValues: inlineValues && inlineValues.length > 0 ? inlineValues : undefined,
178
167
  };
179
168
  }
@@ -310,31 +299,5 @@ export function injectDefaultTagMetadata(
310
299
  // ── Matchers ────────────────────────────────────────────────
311
300
 
312
301
  export function matchTagBlockHeading(trimmed: string): TagBlockMatch | null {
313
- // Try new no-colon syntax first: `tag Name [alias] [Values...]`
314
- const nocolonResult = parseTagDeclaration(trimmed);
315
- if (nocolonResult) return nocolonResult;
316
-
317
- // Try old colon syntax: `tag: GroupName [alias X] [(color)]`
318
- const tagMatch = trimmed.match(TAG_BLOCK_RE);
319
- if (tagMatch) {
320
- return {
321
- name: tagMatch[1].trim(),
322
- alias: tagMatch[2] || undefined,
323
- colorHint: tagMatch[3] || undefined,
324
- deprecated: false,
325
- };
326
- }
327
-
328
- // Fall back to legacy ## syntax
329
- const groupMatch = trimmed.match(GROUP_HEADING_RE);
330
- if (groupMatch) {
331
- return {
332
- name: groupMatch[1].trim(),
333
- alias: groupMatch[2] || undefined,
334
- colorHint: groupMatch[3] || undefined,
335
- deprecated: true,
336
- };
337
- }
338
-
339
- return null;
302
+ return parseTagDeclaration(trimmed);
340
303
  }
@@ -1,67 +0,0 @@
1
- // ============================================================
2
- // Infra Scenario Serializer
3
- // ============================================================
4
- //
5
- // Converts interactive overrides into a `scenario:` DSL block.
6
- // Only includes properties that differ from the base diagram.
7
-
8
- import type { ParsedInfra, InfraComputeParams } from './types';
9
-
10
- /**
11
- * Serialize interactive overrides as a DSL `scenario:` block.
12
- * Returns an empty string if nothing differs from the base diagram.
13
- */
14
- export function serializeScenario(name: string, parsed: ParsedInfra, overrides: InfraComputeParams): string {
15
- const lines: string[] = [];
16
-
17
- // Edge RPS override
18
- const edgeNode = parsed.nodes.find((n) => n.isEdge);
19
- if (edgeNode && overrides.rps != null) {
20
- const baseRps = edgeNode.properties.find((p) => p.key === 'rps');
21
- const baseVal = baseRps ? (typeof baseRps.value === 'number' ? baseRps.value : parseFloat(String(baseRps.value)) || 0) : 0;
22
- if (overrides.rps !== baseVal) {
23
- lines.push(` ${edgeNode.id}`);
24
- lines.push(` rps: ${overrides.rps}`);
25
- }
26
- }
27
-
28
- // Instance overrides and property overrides per node
29
- const instanceOv = overrides.instanceOverrides ?? {};
30
- const propOv = overrides.propertyOverrides ?? {};
31
-
32
- for (const node of parsed.nodes) {
33
- if (node.isEdge) continue;
34
-
35
- const nodeLines: string[] = [];
36
-
37
- // Instance override
38
- if (instanceOv[node.id] != null) {
39
- const baseProp = node.properties.find((p) => p.key === 'instances');
40
- const baseVal = baseProp ? (typeof baseProp.value === 'number' ? baseProp.value : parseFloat(String(baseProp.value)) || 1) : 1;
41
- if (instanceOv[node.id] !== baseVal) {
42
- nodeLines.push(` instances: ${instanceOv[node.id]}`);
43
- }
44
- }
45
-
46
- // Property overrides
47
- const nodePropOv = propOv[node.id];
48
- if (nodePropOv) {
49
- for (const [key, val] of Object.entries(nodePropOv)) {
50
- const baseProp = node.properties.find((p) => p.key === key);
51
- const baseVal = baseProp ? (typeof baseProp.value === 'number' ? baseProp.value : parseFloat(String(baseProp.value)) || 0) : 0;
52
- if (val !== baseVal) {
53
- nodeLines.push(` ${key}: ${val}`);
54
- }
55
- }
56
- }
57
-
58
- if (nodeLines.length > 0) {
59
- lines.push(` ${node.id}`);
60
- lines.push(...nodeLines);
61
- }
62
- }
63
-
64
- if (lines.length === 0) return '';
65
-
66
- return `scenario: ${name}\n${lines.join('\n')}\n`;
67
- }