@diagrammo/dgmo 0.15.1 → 0.17.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/README.md +9 -9
- package/dist/advanced.cjs +612 -734
- package/dist/advanced.d.cts +42 -36
- package/dist/advanced.d.ts +42 -36
- package/dist/advanced.js +612 -733
- package/dist/auto.cjs +508 -620
- package/dist/auto.js +105 -105
- package/dist/auto.mjs +508 -620
- package/dist/cli.cjs +144 -144
- package/dist/editor.cjs +8 -9
- package/dist/editor.js +8 -9
- package/dist/highlight.cjs +8 -9
- package/dist/highlight.js +8 -9
- package/dist/index.cjs +497 -608
- package/dist/index.js +497 -608
- package/dist/internal.cjs +612 -734
- package/dist/internal.d.cts +42 -36
- package/dist/internal.d.ts +42 -36
- package/dist/internal.js +612 -733
- package/dist/pert.d.cts +2 -2
- package/dist/pert.d.ts +2 -2
- package/docs/language-reference.md +97 -84
- package/docs/migration-sequence-color-to-tags.md +1 -1
- package/gallery/fixtures/area.dgmo +3 -3
- package/gallery/fixtures/bar-stacked.dgmo +5 -5
- package/gallery/fixtures/boxes-and-lines.dgmo +2 -2
- package/gallery/fixtures/c4-full.dgmo +8 -8
- package/gallery/fixtures/class-full.dgmo +2 -2
- package/gallery/fixtures/doughnut.dgmo +6 -6
- package/gallery/fixtures/flowchart-colors.dgmo +3 -3
- package/gallery/fixtures/function.dgmo +3 -3
- package/gallery/fixtures/gantt-full.dgmo +9 -9
- package/gallery/fixtures/gantt.dgmo +7 -7
- package/gallery/fixtures/infra-full.dgmo +6 -6
- package/gallery/fixtures/infra.dgmo +2 -2
- package/gallery/fixtures/kanban.dgmo +9 -9
- package/gallery/fixtures/line.dgmo +2 -2
- package/gallery/fixtures/multi-line.dgmo +3 -3
- package/gallery/fixtures/org-full.dgmo +6 -6
- package/gallery/fixtures/quadrant.dgmo +2 -2
- package/gallery/fixtures/sankey.dgmo +9 -9
- package/gallery/fixtures/scatter.dgmo +3 -3
- package/gallery/fixtures/sequence-tags-protocols.dgmo +11 -11
- package/gallery/fixtures/sequence-tags.dgmo +10 -10
- package/gallery/fixtures/sequence.dgmo +4 -4
- package/gallery/fixtures/sitemap-full.dgmo +7 -7
- package/gallery/fixtures/slope.dgmo +5 -5
- package/gallery/fixtures/spr-eras.dgmo +9 -9
- package/gallery/fixtures/timeline.dgmo +3 -3
- package/gallery/fixtures/venn.dgmo +3 -3
- package/package.json +7 -3
- package/src/advanced.ts +0 -1
- package/src/auto/index.ts +2 -2
- package/src/boxes-and-lines/layout.ts +1 -2
- package/src/boxes-and-lines/renderer.ts +5 -1
- package/src/c4/parser.ts +2 -2
- package/src/c4/renderer.ts +15 -8
- package/src/chart.ts +18 -9
- package/src/class/parser.ts +8 -7
- package/src/class/renderer.ts +17 -6
- package/src/cli.ts +8 -8
- package/src/completion.ts +14 -17
- package/src/cycle/parser.ts +15 -1
- package/src/cycle/renderer.ts +6 -3
- package/src/d3.ts +88 -49
- package/src/diagnostics.ts +20 -0
- package/src/echarts.ts +28 -11
- package/src/editor/dgmo.grammar +1 -3
- package/src/editor/dgmo.grammar.d.ts +1 -1
- package/src/editor/dgmo.grammar.js +8 -8
- package/src/editor/dgmo.grammar.terms.js +11 -12
- package/src/editor/highlight-api.ts +0 -1
- package/src/editor/highlight.ts +0 -1
- package/src/er/parser.ts +19 -12
- package/src/er/renderer.ts +19 -7
- package/src/gantt/parser.ts +1 -1
- package/src/gantt/renderer.ts +7 -4
- package/src/graph/flowchart-parser.ts +18 -84
- package/src/graph/flowchart-renderer.ts +6 -8
- package/src/graph/layout.ts +0 -2
- package/src/graph/state-parser.ts +17 -62
- package/src/graph/state-renderer.ts +3 -8
- package/src/infra/parser.ts +21 -11
- package/src/infra/renderer.ts +8 -6
- package/src/journey-map/parser.ts +11 -4
- package/src/journey-map/renderer.ts +3 -1
- package/src/kanban/parser.ts +11 -7
- package/src/kanban/renderer.ts +3 -1
- package/src/mindmap/parser.ts +4 -5
- package/src/mindmap/renderer.ts +2 -1
- package/src/org/parser.ts +3 -3
- package/src/org/renderer.ts +4 -3
- package/src/pert/analyzer.ts +10 -10
- package/src/pert/layout.ts +1 -1
- package/src/pert/parser.ts +8 -8
- package/src/pert/renderer.ts +7 -2
- package/src/pert/types.ts +1 -1
- package/src/pyramid/parser.ts +13 -1
- package/src/raci/parser.ts +42 -12
- package/src/raci/renderer.ts +2 -1
- package/src/raci/types.ts +4 -3
- package/src/ring/parser.ts +13 -1
- package/src/sequence/parser.ts +81 -23
- package/src/sequence/participant-inference.ts +18 -181
- package/src/sequence/renderer.ts +48 -137
- package/src/sitemap/layout.ts +0 -2
- package/src/sitemap/parser.ts +12 -38
- package/src/sitemap/renderer.ts +13 -13
- package/src/sitemap/types.ts +0 -1
- package/src/tech-radar/parser.ts +2 -2
- package/src/tech-radar/renderer.ts +5 -3
- package/src/tech-radar/types.ts +2 -0
- package/src/utils/arrows.ts +3 -28
- package/src/utils/extract-alias.ts +1 -1
- package/src/utils/inline-markdown.ts +1 -1
- package/src/utils/legend-d3.ts +12 -6
- package/src/utils/legend-layout.ts +1 -1
- package/src/utils/legend-types.ts +1 -1
- package/src/utils/parsing.ts +64 -35
- package/src/utils/tag-groups.ts +98 -18
- package/src/utils/time-ticks.ts +1 -1
- package/src/wireframe/parser.ts +3 -3
package/src/raci/parser.ts
CHANGED
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
measureIndent,
|
|
25
25
|
parseFirstLine,
|
|
26
26
|
parsePipeMetadata,
|
|
27
|
+
peelTrailingColorName,
|
|
27
28
|
OPTION_NOCOLON_RE,
|
|
28
29
|
tryParseSharedOption,
|
|
29
30
|
} from '../utils/parsing';
|
|
@@ -82,10 +83,10 @@ const KNOWN_BOOLEANS = new Set<string>([
|
|
|
82
83
|
...Object.keys(VARIANT_LOCK_DIRECTIVES),
|
|
83
84
|
]);
|
|
84
85
|
|
|
85
|
-
// Allow optional trailing
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
const PHASE_RE = /^\[(.+?)\]
|
|
86
|
+
// Allow optional trailing color shortcut and/or pipe metadata after the
|
|
87
|
+
// bracket: `[Voyage] blue | desc: …` (per §1.5 universal trailing-token
|
|
88
|
+
// rule + the modern cycle/pyramid/ring/journey-map/b&l idiom).
|
|
89
|
+
const PHASE_RE = /^\[(.+?)\](?:\s+(\S+))?(?:\s*\|\s*(.+))?\s*$/;
|
|
89
90
|
const ROLE_ASSIGNMENT_RE = /^([^:]+):\s*(.*)$/;
|
|
90
91
|
|
|
91
92
|
/**
|
|
@@ -177,7 +178,7 @@ export function parseRaci(
|
|
|
177
178
|
result.diagnostics.push(makeDgmoError(line, message, 'error', code));
|
|
178
179
|
};
|
|
179
180
|
|
|
180
|
-
if (!content
|
|
181
|
+
if (!content?.trim()) {
|
|
181
182
|
return fail(0, 'No content provided');
|
|
182
183
|
}
|
|
183
184
|
|
|
@@ -382,10 +383,10 @@ export function parseRaci(
|
|
|
382
383
|
// Strip a possible trailing comma (user habit tolerance,
|
|
383
384
|
// matches `collectIndentedValues`).
|
|
384
385
|
const stripped = nextTrim.replace(/,\s*$/, '');
|
|
385
|
-
// Optional pipe metadata: `Cap | color: blue` —
|
|
386
|
-
//
|
|
386
|
+
// Optional pipe metadata: `Cap | color: blue` — long form.
|
|
387
|
+
// Optional trailing-token shortcut: `Cap blue` — short form (§1.5).
|
|
387
388
|
const segments = stripped.split('|').map((s) => s.trim());
|
|
388
|
-
|
|
389
|
+
let roleLabel = segments[0] ?? '';
|
|
389
390
|
let roleColor: string | undefined;
|
|
390
391
|
if (segments.length > 1) {
|
|
391
392
|
const meta = parsePipeMetadata(segments);
|
|
@@ -398,6 +399,20 @@ export function parseRaci(
|
|
|
398
399
|
);
|
|
399
400
|
}
|
|
400
401
|
}
|
|
402
|
+
// Apply shortcut only when pipe metadata didn't already set color.
|
|
403
|
+
if (!roleColor) {
|
|
404
|
+
const { label: stripLabel, colorName: shortcutColor } =
|
|
405
|
+
peelTrailingColorName(roleLabel);
|
|
406
|
+
if (shortcutColor) {
|
|
407
|
+
roleColor = resolveColorWithDiagnostic(
|
|
408
|
+
shortcutColor,
|
|
409
|
+
j + 1,
|
|
410
|
+
result.diagnostics,
|
|
411
|
+
palette
|
|
412
|
+
);
|
|
413
|
+
roleLabel = stripLabel;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
401
416
|
if (roleLabel) getOrAddRole(roleLabel, j + 1, roleColor);
|
|
402
417
|
}
|
|
403
418
|
i = j - 1; // outer loop's i++ lands on the first non-block line
|
|
@@ -469,10 +484,13 @@ export function parseRaci(
|
|
|
469
484
|
errorAt(lineNumber, 'Phase label is empty.');
|
|
470
485
|
continue;
|
|
471
486
|
}
|
|
472
|
-
//
|
|
487
|
+
// PHASE_RE captures: 1=label, 2=optional trailing-token color, 3=pipe meta.
|
|
488
|
+
// Long pipe form (`[Voyage] | color: blue`) wins over the shortcut.
|
|
473
489
|
let phaseColor: string | undefined;
|
|
474
|
-
|
|
475
|
-
|
|
490
|
+
const trailingToken = phaseMatch[2];
|
|
491
|
+
const pipeMeta = phaseMatch[3];
|
|
492
|
+
if (pipeMeta) {
|
|
493
|
+
const meta = parsePipeMetadata(['', pipeMeta]);
|
|
476
494
|
if (meta['color']) {
|
|
477
495
|
phaseColor = resolveColorWithDiagnostic(
|
|
478
496
|
meta['color'],
|
|
@@ -482,6 +500,18 @@ export function parseRaci(
|
|
|
482
500
|
);
|
|
483
501
|
}
|
|
484
502
|
}
|
|
503
|
+
if (!phaseColor && trailingToken) {
|
|
504
|
+
// Trailing token must be a recognized color word, or it's a parse error.
|
|
505
|
+
const { colorName } = peelTrailingColorName(`x ${trailingToken}`);
|
|
506
|
+
if (colorName) {
|
|
507
|
+
phaseColor = resolveColorWithDiagnostic(
|
|
508
|
+
colorName,
|
|
509
|
+
lineNumber,
|
|
510
|
+
result.diagnostics,
|
|
511
|
+
palette
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
485
515
|
currentPhase = {
|
|
486
516
|
id: normalizeName(display),
|
|
487
517
|
displayName: display,
|
|
@@ -594,7 +624,7 @@ export function parseRaci(
|
|
|
594
624
|
// detect by whether it was declared before `bodyStarted`.
|
|
595
625
|
// We piggyback the `declaredLine` we recorded.
|
|
596
626
|
const entry = roleStore.get(roleId);
|
|
597
|
-
if (entry
|
|
627
|
+
if (entry?.declaredLine === lineNumber) {
|
|
598
628
|
const candidates = result.roleDisplayNames.filter(
|
|
599
629
|
(n) => n !== entry.displayName
|
|
600
630
|
);
|
package/src/raci/renderer.ts
CHANGED
|
@@ -602,7 +602,8 @@ export function renderRaci(
|
|
|
602
602
|
parsed.roles.forEach((roleId, i) => {
|
|
603
603
|
const cx = roleX(i) + COLUMN_INSET;
|
|
604
604
|
const cw = roleColW - 2 * COLUMN_INSET;
|
|
605
|
-
// Per-role color from `Cap
|
|
605
|
+
// Per-role color from `Cap blue` trailing-token (or `Cap | color: blue`)
|
|
606
|
+
// syntax. When the user provides
|
|
606
607
|
// one, it wins; otherwise rotate through marker-safe accents so
|
|
607
608
|
// each column has a subtle visual identity instead of every column
|
|
608
609
|
// reading as the same neutral gray.
|
package/src/raci/types.ts
CHANGED
|
@@ -67,9 +67,10 @@ export interface ParsedRaci {
|
|
|
67
67
|
/** Display name for each role (parallel to `roles`). */
|
|
68
68
|
roleDisplayNames: string[];
|
|
69
69
|
/**
|
|
70
|
-
* Optional per-role palette color from `Cap
|
|
71
|
-
* roles block
|
|
72
|
-
*
|
|
70
|
+
* Optional per-role palette color from the `Cap blue` trailing-token
|
|
71
|
+
* suffix in the roles block (or the long pipe form `Cap | color: blue`).
|
|
72
|
+
* Parallel to `roles`; entries default to `undefined` (renderer falls
|
|
73
|
+
* back to the neutral column tint).
|
|
73
74
|
*/
|
|
74
75
|
roleColors: Array<string | undefined>;
|
|
75
76
|
phases: RaciPhase[];
|
package/src/ring/parser.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
measureIndent,
|
|
9
9
|
parseFirstLine,
|
|
10
10
|
parsePipeMetadata,
|
|
11
|
+
peelTrailingColorName,
|
|
11
12
|
tryParseSharedOption,
|
|
12
13
|
PIPE_KEY_VALUE_PREFIX_RE,
|
|
13
14
|
PIPE_LIKELY_STRUCTURED_TAIL_RE,
|
|
@@ -73,7 +74,7 @@ export function parseRing(content: string): ParsedRing {
|
|
|
73
74
|
// ── First line: chart type declaration ──
|
|
74
75
|
if (!headerParsed) {
|
|
75
76
|
const firstLineResult = parseFirstLine(trimmed);
|
|
76
|
-
if (firstLineResult
|
|
77
|
+
if (firstLineResult?.chartType === 'ring') {
|
|
77
78
|
result.title = firstLineResult.title ?? '';
|
|
78
79
|
result.titleLineNumber = lineNum;
|
|
79
80
|
headerParsed = true;
|
|
@@ -175,6 +176,17 @@ export function parseRing(content: string): ParsedRing {
|
|
|
175
176
|
continue;
|
|
176
177
|
}
|
|
177
178
|
|
|
179
|
+
// Universal trailing-token shortcut: `Label color` equivalent to
|
|
180
|
+
// `Label | color: <name>` when color is not already set (§1.5).
|
|
181
|
+
if (!color) {
|
|
182
|
+
const { label: stripped, colorName: shortcutColor } =
|
|
183
|
+
peelTrailingColorName(label);
|
|
184
|
+
if (shortcutColor) {
|
|
185
|
+
color = shortcutColor;
|
|
186
|
+
label = stripped;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
178
190
|
currentLayer = {
|
|
179
191
|
label,
|
|
180
192
|
lineNumber: lineNum,
|
package/src/sequence/parser.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
NAME_DIAGNOSTIC_CODES,
|
|
12
12
|
nameMergedMessage,
|
|
13
13
|
akaRemovedMessage,
|
|
14
|
+
participantTypeRemovedMessage,
|
|
14
15
|
} from '../diagnostics';
|
|
15
16
|
import { normalizeName, displayName } from '../utils/name-normalize';
|
|
16
17
|
import { parseArrow, parseInArrowLabel } from '../utils/arrows';
|
|
@@ -60,29 +61,40 @@ const KNOWN_SEQ_BOOLEANS = new Set(['activations', 'solid-fill', 'no-title']);
|
|
|
60
61
|
|
|
61
62
|
/**
|
|
62
63
|
* Participant types that can be declared via "Name is a type" syntax.
|
|
64
|
+
*
|
|
65
|
+
* The 0.16.0 trim retained only the types whose shapes carry semantic
|
|
66
|
+
* weight at a glance: stick figure (actor), cylinder (database),
|
|
67
|
+
* dashed cylinder (cache), horizontal pipe (queue), plus the default
|
|
68
|
+
* rectangle. The legacy `service`/`frontend`/`networking`/`gateway`/
|
|
69
|
+
* `external` keywords are rejected at parse time via
|
|
70
|
+
* `E_PARTICIPANT_TYPE_REMOVED`.
|
|
63
71
|
*/
|
|
64
72
|
export type ParticipantType =
|
|
65
73
|
| 'default'
|
|
66
|
-
| 'service'
|
|
67
74
|
| 'database'
|
|
68
75
|
| 'actor'
|
|
69
76
|
| 'queue'
|
|
70
|
-
| 'cache'
|
|
71
|
-
| 'gateway'
|
|
72
|
-
| 'external'
|
|
73
|
-
| 'networking'
|
|
74
|
-
| 'frontend';
|
|
77
|
+
| 'cache';
|
|
75
78
|
|
|
76
79
|
const VALID_PARTICIPANT_TYPES: ReadonlySet<string> = new Set([
|
|
77
|
-
'service',
|
|
78
80
|
'database',
|
|
79
81
|
'actor',
|
|
80
82
|
'queue',
|
|
81
83
|
'cache',
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Participant-type keywords that were removed in 0.16.0. Used to
|
|
88
|
+
* gate `is a X` declarations with a hard parse error rather than
|
|
89
|
+
* silent fall-through to default — aligns with the pre-1.0
|
|
90
|
+
* break-and-bump policy (see `E_AKA_REMOVED` precedent).
|
|
91
|
+
*/
|
|
92
|
+
const REMOVED_PARTICIPANT_TYPES: ReadonlySet<string> = new Set([
|
|
93
|
+
'service',
|
|
94
|
+
'frontend',
|
|
95
|
+
'networking',
|
|
82
96
|
'gateway',
|
|
83
97
|
'external',
|
|
84
|
-
'networking',
|
|
85
|
-
'frontend',
|
|
86
98
|
]);
|
|
87
99
|
|
|
88
100
|
/**
|
|
@@ -205,9 +217,9 @@ export interface ParsedSequenceDgmo {
|
|
|
205
217
|
error: string | null;
|
|
206
218
|
}
|
|
207
219
|
|
|
208
|
-
// "Name is a type" pattern — e.g. "Auth Server is a
|
|
220
|
+
// "Name is a type" pattern — e.g. "Auth Server is a database"
|
|
209
221
|
// Participant names may contain spaces; [^:]+? stops at colons so that
|
|
210
|
-
// note lines like "note right of A: this is
|
|
222
|
+
// note lines like "note right of A: this is an actor" are not falsely matched.
|
|
211
223
|
// Remainder after type is parsed separately for `position N` modifier.
|
|
212
224
|
const IS_A_PATTERN = /^([^:]+?)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
|
|
213
225
|
|
|
@@ -215,7 +227,10 @@ const IS_A_PATTERN = /^([^:]+?)\s+is\s+an?\s+(\w+)(?:\s+(.+))?$/i;
|
|
|
215
227
|
const POSITION_ONLY_PATTERN = /^([^:]+?)\s+position\s+(-?\d+)$/i;
|
|
216
228
|
|
|
217
229
|
// Colored participant declaration — e.g. "Tapin2(green)", "API(blue)"
|
|
218
|
-
|
|
230
|
+
// Scoped to recognized 11-name palette colors only (§1.5) so legitimate
|
|
231
|
+
// `funcCall(arg)` lines don't trigger the legacy-color diagnostic.
|
|
232
|
+
const COLORED_PARTICIPANT_PATTERN =
|
|
233
|
+
/^(\S+?)\((red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white)\)\s*$/;
|
|
219
234
|
|
|
220
235
|
// Group heading pattern — "[Backend]", "[Backend] | t: Product"
|
|
221
236
|
// Group 1: name (no ] or | inside brackets), Group 2: color in parens, Group 3: after-bracket text
|
|
@@ -477,7 +492,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
477
492
|
result.diagnostics.push(makeDgmoError(line, message, 'warning'));
|
|
478
493
|
};
|
|
479
494
|
|
|
480
|
-
if (!content
|
|
495
|
+
if (!content?.trim()) {
|
|
481
496
|
return fail(0, 'Empty content');
|
|
482
497
|
}
|
|
483
498
|
|
|
@@ -491,7 +506,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
491
506
|
const fl = lines[fi].trim();
|
|
492
507
|
if (!fl || fl.startsWith('//')) continue;
|
|
493
508
|
const parsed = parseFirstLine(fl);
|
|
494
|
-
if (parsed
|
|
509
|
+
if (parsed?.chartType === 'sequence') {
|
|
495
510
|
hasExplicitChart = true;
|
|
496
511
|
firstLineIndex = fi;
|
|
497
512
|
if (parsed.title) {
|
|
@@ -678,7 +693,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
678
693
|
if (groupColor) {
|
|
679
694
|
pushWarning(
|
|
680
695
|
lineNumber,
|
|
681
|
-
`(${groupColor}) color syntax removed from sequence diagrams — use 'tag:' groups for coloring`
|
|
696
|
+
`'(${groupColor})' parens-color syntax removed from sequence diagrams — use 'tag:' groups for coloring`
|
|
682
697
|
);
|
|
683
698
|
}
|
|
684
699
|
contentStarted = true;
|
|
@@ -765,7 +780,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
765
780
|
continue;
|
|
766
781
|
}
|
|
767
782
|
|
|
768
|
-
// Tag group entries (indented Value
|
|
783
|
+
// Tag group entries (indented Value color under tag heading)
|
|
769
784
|
// First entry is the default unless another is marked `default`
|
|
770
785
|
if (currentTagGroup && !contentStarted && measureIndent(raw) > 0) {
|
|
771
786
|
const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
|
|
@@ -778,7 +793,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
778
793
|
if (!color) {
|
|
779
794
|
pushError(
|
|
780
795
|
lineNumber,
|
|
781
|
-
`Expected 'Value
|
|
796
|
+
`Expected 'Value color' in tag group '${currentTagGroup.name}'`
|
|
782
797
|
);
|
|
783
798
|
continue;
|
|
784
799
|
}
|
|
@@ -807,11 +822,14 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
807
822
|
blockStack.pop();
|
|
808
823
|
}
|
|
809
824
|
const labelRaw = sectionMatch[1].trim();
|
|
810
|
-
|
|
825
|
+
// Scoped to recognized 11-name palette colors only (§1.5).
|
|
826
|
+
const colorMatch = labelRaw.match(
|
|
827
|
+
/^(.+?)\((red|orange|yellow|green|blue|purple|teal|cyan|gray|black|white)\)$/
|
|
828
|
+
);
|
|
811
829
|
if (colorMatch) {
|
|
812
830
|
pushWarning(
|
|
813
831
|
lineNumber,
|
|
814
|
-
`(${colorMatch[2]
|
|
832
|
+
`'(${colorMatch[2]})' parens-color syntax removed from sequence diagrams — use 'tag:' groups for coloring`
|
|
815
833
|
);
|
|
816
834
|
}
|
|
817
835
|
contentStarted = true;
|
|
@@ -930,6 +948,23 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
930
948
|
const typeStr = isAMatch[2].toLowerCase();
|
|
931
949
|
let remainder = isAMatch[3]?.trim() || '';
|
|
932
950
|
|
|
951
|
+
// Reject the 5 removed-type keywords with a hard parse error
|
|
952
|
+
// (per Decision #2 / break-and-bump policy). Skip participant
|
|
953
|
+
// registration for the bad line — downstream message references
|
|
954
|
+
// will surface a "participant not declared" diagnostic, which
|
|
955
|
+
// is the intended behavior.
|
|
956
|
+
if (REMOVED_PARTICIPANT_TYPES.has(typeStr)) {
|
|
957
|
+
result.diagnostics.push(
|
|
958
|
+
makeDgmoError(
|
|
959
|
+
lineNumber,
|
|
960
|
+
participantTypeRemovedMessage(typeStr),
|
|
961
|
+
'error',
|
|
962
|
+
NAME_DIAGNOSTIC_CODES.PARTICIPANT_TYPE_REMOVED
|
|
963
|
+
)
|
|
964
|
+
);
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
|
|
933
968
|
const participantType: ParticipantType = VALID_PARTICIPANT_TYPES.has(
|
|
934
969
|
typeStr
|
|
935
970
|
)
|
|
@@ -951,7 +986,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
951
986
|
// register it. Order on the line is `Name is a TYPE [position N] as <alias>`.
|
|
952
987
|
// The leading `(.*?)\s*\b` allows the remainder to be just
|
|
953
988
|
// `as <alias>` (empty prefix) — the canonical example writes
|
|
954
|
-
// `Alice is
|
|
989
|
+
// `Alice is an actor as a` where the entire remainder is `as a`.
|
|
955
990
|
const asInRemainder = remainder.match(
|
|
956
991
|
/^(.*?)\s*\bas\s+([A-Za-z][A-Za-z0-9_]{0,11})\s*$/
|
|
957
992
|
);
|
|
@@ -1016,8 +1051,8 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
1016
1051
|
continue;
|
|
1017
1052
|
}
|
|
1018
1053
|
|
|
1019
|
-
//
|
|
1020
|
-
//
|
|
1054
|
+
// Legacy `Name(color)` participant declaration at any level (§1.5 hard
|
|
1055
|
+
// break). Scoped to the 11-name palette so `funcCall(arg)` doesn't trip.
|
|
1021
1056
|
const { core: colorCore, meta: colorMeta } = splitPipe(trimmed, lineNumber);
|
|
1022
1057
|
const coloredMatch = colorCore.match(COLORED_PARTICIPANT_PATTERN);
|
|
1023
1058
|
if (coloredMatch && !ARROW_PATTERN.test(colorCore)) {
|
|
@@ -1025,7 +1060,7 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
1025
1060
|
const color = coloredMatch[2].trim();
|
|
1026
1061
|
pushError(
|
|
1027
1062
|
lineNumber,
|
|
1028
|
-
`'${id}(${color})' syntax is no longer supported — use 'tag:' groups for coloring`
|
|
1063
|
+
`'${id}(${color})' parens-color syntax is no longer supported — use 'tag:' groups for coloring`
|
|
1029
1064
|
);
|
|
1030
1065
|
contentStarted = true;
|
|
1031
1066
|
const key = addParticipant(id, lineNumber, { metadata: colorMeta });
|
|
@@ -1310,6 +1345,29 @@ export function parseSequenceDgmo(content: string): ParsedSequenceDgmo {
|
|
|
1310
1345
|
continue;
|
|
1311
1346
|
}
|
|
1312
1347
|
|
|
1348
|
+
// 'elif <label>' → suggest 'else if <label>'
|
|
1349
|
+
const elifMatch = trimmed.match(/^elif\b\s*(.*)$/i);
|
|
1350
|
+
if (elifMatch) {
|
|
1351
|
+
const tailRaw = elifMatch[1].trim();
|
|
1352
|
+
const tail = tailRaw ? ' ' + tailRaw : '';
|
|
1353
|
+
pushError(
|
|
1354
|
+
lineNumber,
|
|
1355
|
+
`'elif' is not a keyword. Did you mean 'else if${tail}'?`
|
|
1356
|
+
);
|
|
1357
|
+
continue;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// 'else <text>' (where text isn't 'if …') → suggest 'else if <text>'
|
|
1361
|
+
const elseLabelMatch = trimmed.match(/^else\s+(.+)$/i);
|
|
1362
|
+
if (elseLabelMatch) {
|
|
1363
|
+
const tail = elseLabelMatch[1].trim();
|
|
1364
|
+
pushError(
|
|
1365
|
+
lineNumber,
|
|
1366
|
+
`'else' does not take a label. Did you mean 'else if ${tail}'?`
|
|
1367
|
+
);
|
|
1368
|
+
continue;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1313
1371
|
// ---- Note parsing (space-separated only) ----
|
|
1314
1372
|
// Strategy:
|
|
1315
1373
|
// 1. Try bare note: `note text` — position defaults, text is everything after `note`
|
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
// ============================================================
|
|
4
4
|
//
|
|
5
5
|
// Data-driven rules table that infers participant type from name.
|
|
6
|
-
// First match wins.
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// First match wins. Conflict overrides come before suffix rules
|
|
7
|
+
// to prevent false positives within the surviving 4-type taxonomy.
|
|
8
|
+
//
|
|
9
|
+
// Surviving types: actor, database, cache, queue (plus default).
|
|
10
|
+
// Removed in 0.16.0: service, frontend, networking, gateway, external.
|
|
11
|
+
// Names that previously inferred to a removed type fall through to
|
|
12
|
+
// default (sharp rectangle). Inference is intentionally conservative
|
|
13
|
+
// — narrow product/name lists, no broad suffix catch-alls.
|
|
9
14
|
// ============================================================
|
|
10
15
|
|
|
11
16
|
import type { ParticipantType } from './parser';
|
|
@@ -23,95 +28,17 @@ interface InferenceRule {
|
|
|
23
28
|
*
|
|
24
29
|
* Priority order:
|
|
25
30
|
* 0. Conflict overrides (prevent misclassification by general patterns)
|
|
26
|
-
* 1.
|
|
27
|
-
* 2.
|
|
28
|
-
* 3.
|
|
29
|
-
* 4.
|
|
30
|
-
* 5. Queue/Messaging patterns
|
|
31
|
-
* 6. Actor patterns (suffix + exact)
|
|
32
|
-
* 7. Frontend patterns
|
|
33
|
-
* 8. Service patterns
|
|
34
|
-
* 9. External patterns
|
|
31
|
+
* 1. Database patterns
|
|
32
|
+
* 2. Cache patterns
|
|
33
|
+
* 3. Queue/Messaging patterns
|
|
34
|
+
* 4. Actor patterns (suffix + exact)
|
|
35
35
|
*/
|
|
36
36
|
const PARTICIPANT_RULES: readonly InferenceRule[] = [
|
|
37
37
|
// ── 0. Conflict overrides ────────────────────────────────
|
|
38
38
|
// Names that would incorrectly match general patterns in later groups
|
|
39
39
|
{ pattern: /^KeyDB$/i, type: 'cache' }, // not database (DB$ suffix)
|
|
40
|
-
{ pattern: /Webhook/i, type: 'external' }, // not frontend (Web contains)
|
|
41
|
-
{ pattern: /^Upstream$/i, type: 'external' }, // not queue (Stream$ suffix)
|
|
42
|
-
{ pattern: /^Downstream$/i, type: 'external' }, // not queue (Stream$ suffix)
|
|
43
|
-
|
|
44
|
-
// ── 1. Infrastructure overrides ─────────────────────────
|
|
45
|
-
// These names end in -er/-or but are NOT actors
|
|
46
|
-
{ pattern: /^.*Router$/i, type: 'networking' },
|
|
47
|
-
{ pattern: /^.*Scheduler$/i, type: 'service' },
|
|
48
|
-
{ pattern: /^.*Dispatcher$/i, type: 'service' },
|
|
49
|
-
{ pattern: /^.*Balancer$/i, type: 'networking' },
|
|
50
|
-
{ pattern: /^.*Controller$/i, type: 'service' },
|
|
51
|
-
{ pattern: /^.*Handler$/i, type: 'service' },
|
|
52
|
-
{ pattern: /^.*Processor$/i, type: 'service' },
|
|
53
|
-
{ pattern: /^.*Connector$/i, type: 'service' },
|
|
54
|
-
{ pattern: /^.*Adapter$/i, type: 'service' },
|
|
55
|
-
{ pattern: /^.*Provider$/i, type: 'service' },
|
|
56
|
-
{ pattern: /^.*Manager$/i, type: 'service' },
|
|
57
|
-
{ pattern: /^.*Orchestrator$/i, type: 'service' },
|
|
58
|
-
{ pattern: /^.*Monitor$/i, type: 'service' },
|
|
59
|
-
{ pattern: /^.*Resolver$/i, type: 'service' },
|
|
60
|
-
{ pattern: /^.*Logger$/i, type: 'service' },
|
|
61
|
-
{ pattern: /^.*Server$/i, type: 'service' },
|
|
62
|
-
{ pattern: /^.*Broker$/i, type: 'queue' },
|
|
63
|
-
{ pattern: /^.*Worker$/i, type: 'service' },
|
|
64
|
-
{ pattern: /^.*Consumer$/i, type: 'service' },
|
|
65
|
-
{ pattern: /^.*Producer$/i, type: 'service' },
|
|
66
|
-
{ pattern: /^.*Publisher$/i, type: 'service' },
|
|
67
|
-
{ pattern: /^.*Subscriber$/i, type: 'service' },
|
|
68
|
-
{ pattern: /^.*Listener$/i, type: 'service' },
|
|
69
|
-
// New -er/-or suffixes that are services, not actors
|
|
70
|
-
{ pattern: /^.*Watcher$/i, type: 'service' },
|
|
71
|
-
{ pattern: /^.*Executor$/i, type: 'service' },
|
|
72
|
-
{ pattern: /^.*Aggregator$/i, type: 'service' },
|
|
73
|
-
{ pattern: /^.*Collector$/i, type: 'service' },
|
|
74
|
-
{ pattern: /^.*Transformer$/i, type: 'service' },
|
|
75
|
-
{ pattern: /^.*Validator$/i, type: 'service' },
|
|
76
|
-
{ pattern: /^.*Generator$/i, type: 'service' },
|
|
77
|
-
{ pattern: /^.*Indexer$/i, type: 'service' },
|
|
78
|
-
{ pattern: /^.*Crawler$/i, type: 'service' },
|
|
79
|
-
{ pattern: /^.*Scanner$/i, type: 'service' },
|
|
80
|
-
{ pattern: /^.*Parser$/i, type: 'service' },
|
|
81
|
-
{ pattern: /^.*Emitter$/i, type: 'service' },
|
|
82
|
-
{ pattern: /^.*Exporter$/i, type: 'service' },
|
|
83
|
-
{ pattern: /^.*Importer$/i, type: 'service' },
|
|
84
|
-
{ pattern: /^.*Loader$/i, type: 'service' },
|
|
85
|
-
{ pattern: /^.*Renderer$/i, type: 'service' },
|
|
86
|
-
{ pattern: /^.*Checker$/i, type: 'service' },
|
|
87
|
-
{ pattern: /^.*Inspector$/i, type: 'service' },
|
|
88
|
-
{ pattern: /^.*Encoder$/i, type: 'service' },
|
|
89
|
-
{ pattern: /^.*Decoder$/i, type: 'service' },
|
|
90
|
-
{ pattern: /^.*Notifier$/i, type: 'service' },
|
|
91
|
-
|
|
92
|
-
// ── 2. Networking patterns ──────────────────────────────
|
|
93
|
-
{ pattern: /Gateway/i, type: 'networking' },
|
|
94
|
-
{ pattern: /GW$/i, type: 'networking' },
|
|
95
|
-
{ pattern: /Proxy/i, type: 'networking' },
|
|
96
|
-
{ pattern: /LB$/i, type: 'networking' },
|
|
97
|
-
{ pattern: /LoadBalancer/i, type: 'networking' },
|
|
98
|
-
{ pattern: /CDN/i, type: 'networking' },
|
|
99
|
-
{ pattern: /Firewall/i, type: 'networking' },
|
|
100
|
-
{ pattern: /WAF$/i, type: 'networking' },
|
|
101
|
-
{ pattern: /DNS/i, type: 'networking' },
|
|
102
|
-
{ pattern: /Ingress/i, type: 'networking' },
|
|
103
|
-
// Named products & patterns
|
|
104
|
-
{ pattern: /Nginx/i, type: 'networking' },
|
|
105
|
-
{ pattern: /Traefik/i, type: 'networking' },
|
|
106
|
-
{ pattern: /Envoy/i, type: 'networking' },
|
|
107
|
-
{ pattern: /Istio/i, type: 'networking' },
|
|
108
|
-
{ pattern: /Kong/i, type: 'networking' },
|
|
109
|
-
{ pattern: /Akamai/i, type: 'networking' },
|
|
110
|
-
{ pattern: /Cloudflare/i, type: 'networking' },
|
|
111
|
-
{ pattern: /Mesh$/i, type: 'networking' },
|
|
112
|
-
{ pattern: /ServiceMesh/i, type: 'networking' },
|
|
113
40
|
|
|
114
|
-
// ──
|
|
41
|
+
// ── 1. Database patterns ────────────────────────────────
|
|
115
42
|
{ pattern: /DB$/i, type: 'database' },
|
|
116
43
|
{ pattern: /Database/i, type: 'database' },
|
|
117
44
|
{ pattern: /Datastore/i, type: 'database' },
|
|
@@ -146,17 +73,16 @@ const PARTICIPANT_RULES: readonly InferenceRule[] = [
|
|
|
146
73
|
{ pattern: /Presto/i, type: 'database' },
|
|
147
74
|
{ pattern: /Table$/i, type: 'database' },
|
|
148
75
|
|
|
149
|
-
// ──
|
|
76
|
+
// ── 2. Cache patterns ──────────────────────────────────
|
|
150
77
|
{ pattern: /Cache/i, type: 'cache' },
|
|
151
78
|
{ pattern: /Redis/i, type: 'cache' },
|
|
152
79
|
{ pattern: /Memcache/i, type: 'cache' },
|
|
153
|
-
// CDN already matched by networking above
|
|
154
80
|
// Named products
|
|
155
81
|
{ pattern: /Dragonfly/i, type: 'cache' },
|
|
156
82
|
{ pattern: /Hazelcast/i, type: 'cache' },
|
|
157
83
|
{ pattern: /Valkey/i, type: 'cache' },
|
|
158
84
|
|
|
159
|
-
// ──
|
|
85
|
+
// ── 3. Queue/Messaging patterns ─────────────────────────
|
|
160
86
|
{ pattern: /Queue/i, type: 'queue' },
|
|
161
87
|
{ pattern: /MQ$/i, type: 'queue' },
|
|
162
88
|
{ pattern: /SQS/i, type: 'queue' },
|
|
@@ -169,6 +95,7 @@ const PARTICIPANT_RULES: readonly InferenceRule[] = [
|
|
|
169
95
|
{ pattern: /Stream$/i, type: 'queue' },
|
|
170
96
|
{ pattern: /SNS/i, type: 'queue' },
|
|
171
97
|
{ pattern: /PubSub/i, type: 'queue' },
|
|
98
|
+
{ pattern: /Broker$/i, type: 'queue' },
|
|
172
99
|
// Named products & patterns
|
|
173
100
|
{ pattern: /NATS/i, type: 'queue' },
|
|
174
101
|
{ pattern: /Pulsar/i, type: 'queue' },
|
|
@@ -180,7 +107,7 @@ const PARTICIPANT_RULES: readonly InferenceRule[] = [
|
|
|
180
107
|
{ pattern: /EventHub/i, type: 'queue' },
|
|
181
108
|
{ pattern: /Channel$/i, type: 'queue' },
|
|
182
109
|
|
|
183
|
-
// ──
|
|
110
|
+
// ── 4. Actor patterns ──────────────────────────────────
|
|
184
111
|
// Exact matches first
|
|
185
112
|
{ pattern: /^Admin$/i, type: 'actor' },
|
|
186
113
|
{ pattern: /^User$/i, type: 'actor' },
|
|
@@ -199,101 +126,11 @@ const PARTICIPANT_RULES: readonly InferenceRule[] = [
|
|
|
199
126
|
{ pattern: /^Fan$/i, type: 'actor' },
|
|
200
127
|
{ pattern: /^Purchaser$/i, type: 'actor' },
|
|
201
128
|
{ pattern: /^Reviewer$/i, type: 'actor' },
|
|
202
|
-
// Suffix rules
|
|
129
|
+
// Suffix rules
|
|
203
130
|
{ pattern: /User$/i, type: 'actor' },
|
|
204
131
|
{ pattern: /Actor$/i, type: 'actor' },
|
|
205
132
|
{ pattern: /Analyst$/i, type: 'actor' },
|
|
206
133
|
{ pattern: /Staff$/i, type: 'actor' },
|
|
207
|
-
|
|
208
|
-
// ── 7. Frontend patterns ────────────────────────────────
|
|
209
|
-
{ pattern: /App$/i, type: 'frontend' },
|
|
210
|
-
{ pattern: /Application/i, type: 'frontend' },
|
|
211
|
-
{ pattern: /Mobile/i, type: 'frontend' },
|
|
212
|
-
{ pattern: /iOS/i, type: 'frontend' },
|
|
213
|
-
{ pattern: /Android/i, type: 'frontend' },
|
|
214
|
-
{ pattern: /Web/i, type: 'frontend' },
|
|
215
|
-
{ pattern: /Browser/i, type: 'frontend' },
|
|
216
|
-
{ pattern: /Frontend/i, type: 'frontend' },
|
|
217
|
-
{ pattern: /UI$/i, type: 'frontend' },
|
|
218
|
-
{ pattern: /Dashboard/i, type: 'frontend' },
|
|
219
|
-
{ pattern: /CLI$/i, type: 'frontend' },
|
|
220
|
-
{ pattern: /Terminal/i, type: 'frontend' },
|
|
221
|
-
// Frameworks & patterns
|
|
222
|
-
{ pattern: /React/i, type: 'frontend' },
|
|
223
|
-
{ pattern: /^Vue$/i, type: 'frontend' },
|
|
224
|
-
{ pattern: /Angular/i, type: 'frontend' },
|
|
225
|
-
{ pattern: /Svelte/i, type: 'frontend' },
|
|
226
|
-
{ pattern: /NextJS/i, type: 'frontend' },
|
|
227
|
-
{ pattern: /Nuxt/i, type: 'frontend' },
|
|
228
|
-
{ pattern: /Remix/i, type: 'frontend' },
|
|
229
|
-
{ pattern: /Electron/i, type: 'frontend' },
|
|
230
|
-
{ pattern: /Tauri/i, type: 'frontend' },
|
|
231
|
-
{ pattern: /Widget$/i, type: 'frontend' },
|
|
232
|
-
{ pattern: /Portal/i, type: 'frontend' },
|
|
233
|
-
{ pattern: /Console$/i, type: 'frontend' },
|
|
234
|
-
{ pattern: /^SPA$/i, type: 'frontend' },
|
|
235
|
-
{ pattern: /^PWA$/i, type: 'frontend' },
|
|
236
|
-
|
|
237
|
-
// ── 8. Service patterns ─────────────────────────────────
|
|
238
|
-
{ pattern: /Service/i, type: 'service' },
|
|
239
|
-
{ pattern: /Svc$/i, type: 'service' },
|
|
240
|
-
{ pattern: /API$/i, type: 'service' },
|
|
241
|
-
{ pattern: /Lambda/i, type: 'service' },
|
|
242
|
-
{ pattern: /Function$/i, type: 'service' },
|
|
243
|
-
{ pattern: /Fn$/i, type: 'service' },
|
|
244
|
-
{ pattern: /Job$/i, type: 'service' },
|
|
245
|
-
{ pattern: /Cron/i, type: 'service' },
|
|
246
|
-
{ pattern: /Microservice/i, type: 'service' },
|
|
247
|
-
// Auth
|
|
248
|
-
{ pattern: /^Auth$/i, type: 'service' },
|
|
249
|
-
{ pattern: /^AuthN$/i, type: 'service' },
|
|
250
|
-
{ pattern: /^AuthZ$/i, type: 'service' },
|
|
251
|
-
{ pattern: /^SSO$/i, type: 'service' },
|
|
252
|
-
{ pattern: /OAuth/i, type: 'service' },
|
|
253
|
-
{ pattern: /^OIDC$/i, type: 'service' },
|
|
254
|
-
// SaaS
|
|
255
|
-
{ pattern: /Stripe/i, type: 'service' },
|
|
256
|
-
{ pattern: /Twilio/i, type: 'service' },
|
|
257
|
-
{ pattern: /SendGrid/i, type: 'service' },
|
|
258
|
-
{ pattern: /Mailgun/i, type: 'service' },
|
|
259
|
-
// Cloud/infra
|
|
260
|
-
{ pattern: /^S3$/i, type: 'service' },
|
|
261
|
-
{ pattern: /^Blob$/i, type: 'service' },
|
|
262
|
-
{ pattern: /Vercel/i, type: 'service' },
|
|
263
|
-
{ pattern: /Netlify/i, type: 'service' },
|
|
264
|
-
{ pattern: /Heroku/i, type: 'service' },
|
|
265
|
-
{ pattern: /Docker/i, type: 'service' },
|
|
266
|
-
{ pattern: /Kubernetes/i, type: 'service' },
|
|
267
|
-
{ pattern: /K8s/i, type: 'service' },
|
|
268
|
-
{ pattern: /Terraform/i, type: 'service' },
|
|
269
|
-
// Security
|
|
270
|
-
{ pattern: /Vault/i, type: 'service' },
|
|
271
|
-
{ pattern: /^HSM$/i, type: 'service' },
|
|
272
|
-
{ pattern: /KMS/i, type: 'service' },
|
|
273
|
-
{ pattern: /^IAM$/i, type: 'service' },
|
|
274
|
-
// AI/ML
|
|
275
|
-
{ pattern: /^LLM$/i, type: 'service' },
|
|
276
|
-
{ pattern: /GPT/i, type: 'service' },
|
|
277
|
-
{ pattern: /^Claude$/i, type: 'service' },
|
|
278
|
-
{ pattern: /Embedding/i, type: 'service' },
|
|
279
|
-
{ pattern: /Inference/i, type: 'service' },
|
|
280
|
-
// Suffixes & patterns
|
|
281
|
-
{ pattern: /Pipeline$/i, type: 'service' },
|
|
282
|
-
{ pattern: /Registry/i, type: 'service' },
|
|
283
|
-
{ pattern: /Engine$/i, type: 'service' },
|
|
284
|
-
{ pattern: /Daemon/i, type: 'service' },
|
|
285
|
-
|
|
286
|
-
// ── 9. External patterns ────────────────────────────────
|
|
287
|
-
{ pattern: /External/i, type: 'external' },
|
|
288
|
-
{ pattern: /Ext$/i, type: 'external' },
|
|
289
|
-
{ pattern: /ThirdParty/i, type: 'external' },
|
|
290
|
-
{ pattern: /3P$/i, type: 'external' },
|
|
291
|
-
{ pattern: /Vendor/i, type: 'external' },
|
|
292
|
-
// Named products & patterns
|
|
293
|
-
{ pattern: /Callback/i, type: 'external' },
|
|
294
|
-
{ pattern: /^AWS$/i, type: 'external' },
|
|
295
|
-
{ pattern: /^GCP$/i, type: 'external' },
|
|
296
|
-
{ pattern: /Azure/i, type: 'external' },
|
|
297
134
|
];
|
|
298
135
|
|
|
299
136
|
/**
|