@diagrammo/dgmo 0.15.0 → 0.15.1

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 (64) hide show
  1. package/README.md +14 -1
  2. package/dist/advanced.cjs +53069 -0
  3. package/dist/advanced.d.cts +4691 -0
  4. package/dist/advanced.d.ts +4691 -0
  5. package/dist/advanced.js +52823 -0
  6. package/dist/auto.cjs +1495 -1288
  7. package/dist/auto.js +132 -109
  8. package/dist/auto.mjs +1491 -1284
  9. package/dist/cli.cjs +173 -150
  10. package/dist/index.cjs +1486 -1276
  11. package/dist/index.d.cts +45 -1
  12. package/dist/index.d.ts +45 -1
  13. package/dist/index.js +1481 -1272
  14. package/dist/internal.cjs +1497 -1289
  15. package/dist/internal.d.cts +80 -79
  16. package/dist/internal.d.ts +80 -79
  17. package/dist/internal.js +1492 -1285
  18. package/dist/pert.cjs +325 -0
  19. package/dist/pert.d.cts +542 -0
  20. package/dist/pert.d.ts +542 -0
  21. package/dist/pert.js +294 -0
  22. package/package.json +28 -3
  23. package/src/advanced.ts +731 -0
  24. package/src/auto/index.ts +14 -13
  25. package/src/boxes-and-lines/layout.ts +481 -445
  26. package/src/c4/parser.ts +7 -7
  27. package/src/chart-types.ts +0 -5
  28. package/src/class/parser.ts +1 -9
  29. package/src/cli.ts +9 -7
  30. package/src/completion-types.ts +28 -0
  31. package/src/completion.ts +15 -18
  32. package/src/cycle/layout.ts +2 -2
  33. package/src/d3.ts +1455 -1122
  34. package/src/echarts.ts +11 -11
  35. package/src/er/parser.ts +1 -9
  36. package/src/er/renderer.ts +1 -1
  37. package/src/gantt/calculator.ts +1 -11
  38. package/src/gantt/parser.ts +16 -16
  39. package/src/gantt/renderer.ts +2 -2
  40. package/src/graph/flowchart-parser.ts +1 -1
  41. package/src/graph/flowchart-renderer.ts +1 -1
  42. package/src/graph/state-renderer.ts +1 -1
  43. package/src/index.ts +17 -1
  44. package/src/infra/parser.ts +19 -19
  45. package/src/infra/renderer.ts +2 -2
  46. package/src/internal.ts +9 -721
  47. package/src/kanban/parser.ts +2 -2
  48. package/src/mindmap/layout.ts +1 -1
  49. package/src/mindmap/parser.ts +1 -1
  50. package/src/org/parser.ts +1 -1
  51. package/src/org/renderer.ts +1 -1
  52. package/src/pert/layout.ts +1 -1
  53. package/src/pert/monte-carlo.ts +2 -2
  54. package/src/pert/parser.ts +3 -3
  55. package/src/raci/parser.ts +4 -4
  56. package/src/raci/renderer.ts +1 -1
  57. package/src/sequence/renderer.ts +1 -4
  58. package/src/sitemap/parser.ts +1 -1
  59. package/src/tech-radar/interactive.ts +1 -1
  60. package/src/tech-radar/renderer.ts +1 -1
  61. package/src/utils/tag-groups.ts +11 -12
  62. package/src/wireframe/layout.ts +11 -7
  63. package/src/wireframe/parser.ts +2 -2
  64. package/src/wireframe/renderer.ts +5 -2
package/src/echarts.ts CHANGED
@@ -1026,7 +1026,7 @@ function buildChordOption(
1026
1026
  isDark: boolean,
1027
1027
  textColor: string,
1028
1028
  colors: string[],
1029
- bg: string,
1029
+ _bg: string,
1030
1030
  titleConfig: EChartsOption['title']
1031
1031
  ): EChartsOption {
1032
1032
  // Extract unique nodes from links
@@ -1185,7 +1185,7 @@ function evaluateExpression(expr: string, x: number): number {
1185
1185
  function buildFunctionOption(
1186
1186
  parsed: ParsedExtendedChart,
1187
1187
  palette: PaletteColors,
1188
- isDark: boolean,
1188
+ _isDark: boolean,
1189
1189
  textColor: string,
1190
1190
  axisLineColor: string,
1191
1191
  gridOpacity: number,
@@ -2092,7 +2092,7 @@ function buildFunnelOption(
2092
2092
  isDark: boolean,
2093
2093
  textColor: string,
2094
2094
  colors: string[],
2095
- bg: string,
2095
+ _bg: string,
2096
2096
  titleConfig: EChartsOption['title']
2097
2097
  ): EChartsOption {
2098
2098
  // Sort data descending by value for funnel ordering
@@ -2464,7 +2464,7 @@ function buildBarOption(
2464
2464
  splitLineColor: string,
2465
2465
  gridOpacity: number,
2466
2466
  colors: string[],
2467
- bg: string,
2467
+ _bg: string,
2468
2468
  titleConfig: EChartsOption['title'],
2469
2469
  chartWidth?: number
2470
2470
  ): EChartsOption {
@@ -2893,7 +2893,7 @@ function buildPieOption(
2893
2893
  isDark: boolean,
2894
2894
  textColor: string,
2895
2895
  colors: string[],
2896
- bg: string,
2896
+ _bg: string,
2897
2897
  titleConfig: EChartsOption['title'],
2898
2898
  isDoughnut: boolean
2899
2899
  ): EChartsOption {
@@ -3021,7 +3021,7 @@ function buildPolarAreaOption(
3021
3021
  isDark: boolean,
3022
3022
  textColor: string,
3023
3023
  colors: string[],
3024
- bg: string,
3024
+ _bg: string,
3025
3025
  titleConfig: EChartsOption['title']
3026
3026
  ): EChartsOption {
3027
3027
  const data = parsed.data.map((d, i) => {
@@ -3079,7 +3079,7 @@ function buildBarStackedOption(
3079
3079
  splitLineColor: string,
3080
3080
  gridOpacity: number,
3081
3081
  colors: string[],
3082
- bg: string,
3082
+ _bg: string,
3083
3083
  titleConfig: EChartsOption['title'],
3084
3084
  chartWidth?: number
3085
3085
  ): EChartsOption {
@@ -3282,11 +3282,11 @@ export async function renderExtendedChartForExport(
3282
3282
  // In static export, expand the first group so entries are visible
3283
3283
  // Extract grid offsets for plot-area-centered legend
3284
3284
  const grid = option.grid as Record<string, unknown> | undefined;
3285
- const gridLeftPct = grid?.left
3286
- ? parseFloat(String(grid.left))
3285
+ const gridLeftPct = grid?.['left']
3286
+ ? parseFloat(String(grid['left']))
3287
3287
  : undefined;
3288
- const gridRightPct = grid?.right
3289
- ? parseFloat(String(grid.right))
3288
+ const gridRightPct = grid?.['right']
3289
+ ? parseFloat(String(grid['right']))
3290
3290
  : undefined;
3291
3291
  const { svg: legendSvgStr } = renderLegendSvg(legendGroups, {
3292
3292
  palette: effectivePalette,
package/src/er/parser.ts CHANGED
@@ -229,14 +229,6 @@ export function parseERDiagram(
229
229
  error: null,
230
230
  };
231
231
 
232
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
233
- const _fail = (line: number, message: string): ParsedERDiagram => {
234
- const diag = makeDgmoError(line, message);
235
- result.diagnostics.push(diag);
236
- result.error = formatDgmoError(diag);
237
- return result;
238
- };
239
-
240
232
  const pushError = (line: number, message: string): void => {
241
233
  const diag = makeDgmoError(line, message);
242
234
  result.diagnostics.push(diag);
@@ -628,7 +620,7 @@ export function looksLikeERDiagram(content: string): boolean {
628
620
  // Symbol extraction (for completion API)
629
621
  // ============================================================
630
622
 
631
- import type { DiagramSymbols } from '../completion';
623
+ import type { DiagramSymbols } from '../completion-types';
632
624
 
633
625
  /**
634
626
  * Extract table names (entities) and ER keywords from document text.
@@ -340,7 +340,7 @@ export function renderERDiagram(
340
340
  semanticRoles !== null && (semanticColorsActive ?? true);
341
341
 
342
342
  // ── Edges (behind nodes) ──
343
- const useLabels = parsed.options.notation === 'labels';
343
+ const useLabels = parsed.options['notation'] === 'labels';
344
344
 
345
345
  for (const edge of layout.edges) {
346
346
  if (edge.points.length < 2) continue;
@@ -9,7 +9,7 @@
9
9
  // 3. Forward pass: resolve dates using universal max rule
10
10
  // 4. (Optional) Critical path: backward pass to find zero-slack chain
11
11
 
12
- import { makeDgmoError, formatDgmoError } from '../diagnostics';
12
+ import { makeDgmoError } from '../diagnostics';
13
13
  import type { DgmoError } from '../diagnostics';
14
14
  import type {
15
15
  ParsedGantt,
@@ -67,14 +67,6 @@ export function calculateSchedule(parsed: ParsedGantt): ResolvedSchedule {
67
67
  diagnostics.push(makeDgmoError(line, message, 'warning'));
68
68
  };
69
69
 
70
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
71
- const _fail = (line: number, message: string): ResolvedSchedule => {
72
- const diag = makeDgmoError(line, message);
73
- diagnostics.push(diag);
74
- result.error = formatDgmoError(diag);
75
- return result;
76
- };
77
-
78
70
  // ── Build holiday set ───────────────────────────────────
79
71
 
80
72
  const holidaySet = buildHolidaySet(parsed.holidays);
@@ -124,8 +116,6 @@ export function calculateSchedule(parsed: ParsedGantt): ResolvedSchedule {
124
116
  // ── Resolve explicit -> dependencies ────────────────────
125
117
 
126
118
  for (const task of allTasks) {
127
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
128
- const _node = taskMap.get(task.id)!;
129
119
  for (const dep of task.dependencies) {
130
120
  const resolved = resolveTaskName(dep.targetName, allTasks);
131
121
  if (isResolverError(resolved)) {
@@ -435,15 +435,15 @@ export function parseGantt(
435
435
  metaAliasMap,
436
436
  () => warn(lineNumber, MULTIPLE_PIPE_ERROR)
437
437
  );
438
- if (meta.lag || meta.lead) {
439
- const key = meta.lag ? 'lag' : 'lead';
438
+ if (meta['lag'] || meta['lead']) {
439
+ const key = meta['lag'] ? 'lag' : 'lead';
440
440
  softError(
441
441
  lineNumber,
442
442
  `"${key}" is no longer supported — use "offset: ${meta[key]}" instead.${key === 'lead' ? ' Negate the value for lead behavior: "offset: -...".' : ''}`
443
443
  );
444
444
  }
445
- if (meta.offset) {
446
- const raw = meta.offset;
445
+ if (meta['offset']) {
446
+ const raw = meta['offset'];
447
447
  if (raw.trim().startsWith('+')) {
448
448
  warn(
449
449
  lineNumber,
@@ -903,15 +903,15 @@ export function parseGantt(
903
903
  metaAliasMap,
904
904
  () => warn(lineNumber, MULTIPLE_PIPE_ERROR)
905
905
  );
906
- if (meta.lag || meta.lead) {
907
- const key = meta.lag ? 'lag' : 'lead';
906
+ if (meta['lag'] || meta['lead']) {
907
+ const key = meta['lag'] ? 'lag' : 'lead';
908
908
  softError(
909
909
  lineNumber,
910
910
  `"${key}" is no longer supported — use "offset: ${meta[key]}" instead.${key === 'lead' ? ' Negate the value for lead behavior: "offset: -...".' : ''}`
911
911
  );
912
912
  }
913
- if (meta.offset) {
914
- const raw = meta.offset;
913
+ if (meta['offset']) {
914
+ const raw = meta['offset'];
915
915
  if (raw.trim().startsWith('+')) {
916
916
  warn(
917
917
  lineNumber,
@@ -1023,9 +1023,9 @@ export function parseGantt(
1023
1023
 
1024
1024
  // Extract progress from metadata or shorthand
1025
1025
  let progress: number | null = null;
1026
- if (metadata.progress) {
1027
- progress = parseFloat(metadata.progress);
1028
- delete metadata.progress;
1026
+ if (metadata['progress']) {
1027
+ progress = parseFloat(metadata['progress']);
1028
+ delete metadata['progress'];
1029
1029
  }
1030
1030
  // Check for progress shorthand: `| 80%` or `| t:X, 80%`
1031
1031
  for (const part of segments.slice(1).join(',').split(',')) {
@@ -1037,8 +1037,8 @@ export function parseGantt(
1037
1037
  }
1038
1038
 
1039
1039
  // Reject lag/lead — use offset instead
1040
- if (metadata.lag || metadata.lead) {
1041
- const key = metadata.lag ? 'lag' : 'lead';
1040
+ if (metadata['lag'] || metadata['lead']) {
1041
+ const key = metadata['lag'] ? 'lag' : 'lead';
1042
1042
  softError(
1043
1043
  ln,
1044
1044
  `"${key}" is no longer supported — use "offset: ${metadata[key]}" instead.${key === 'lead' ? ' Negate the value for lead behavior: "offset: -...".' : ''}`
@@ -1047,8 +1047,8 @@ export function parseGantt(
1047
1047
 
1048
1048
  // Extract task-level offset from metadata
1049
1049
  let taskOffset: Offset | undefined;
1050
- if (metadata.offset) {
1051
- const raw = metadata.offset;
1050
+ if (metadata['offset']) {
1051
+ const raw = metadata['offset'];
1052
1052
  if (raw.trim().startsWith('+')) {
1053
1053
  warn(
1054
1054
  ln,
@@ -1063,7 +1063,7 @@ export function parseGantt(
1063
1063
  );
1064
1064
  }
1065
1065
  }
1066
- delete metadata.offset;
1066
+ delete metadata['offset'];
1067
1067
  }
1068
1068
 
1069
1069
  // Inherit metadata from parent groups (tag inheritance)
@@ -1973,7 +1973,7 @@ function renderTagLegend(
1973
1973
  isDark: boolean,
1974
1974
  hasCriticalPath: boolean,
1975
1975
  criticalPathActive: boolean,
1976
- optionLineNumbers: Record<string, number>,
1976
+ _optionLineNumbers: Record<string, number>,
1977
1977
  onToggle?: (groupName: string) => void,
1978
1978
  onToggleControlsExpand?: () => void,
1979
1979
  currentSwimlaneGroup?: string | null,
@@ -3633,7 +3633,7 @@ function resolveTaskColor(
3633
3633
  function renderTimeScaleHorizontal(
3634
3634
  g: d3Selection.Selection<SVGGElement, unknown, null, undefined>,
3635
3635
  scale: d3Scale.ScaleLinear<number, number>,
3636
- innerWidth: number,
3636
+ _innerWidth: number,
3637
3637
  innerHeight: number,
3638
3638
  textColor: string
3639
3639
  ): void {
@@ -663,7 +663,7 @@ export function looksLikeFlowchart(content: string): boolean {
663
663
  // Symbol extraction (for completion API)
664
664
  // ============================================================
665
665
 
666
- import type { DiagramSymbols } from '../completion';
666
+ import type { DiagramSymbols } from '../completion-types';
667
667
 
668
668
  // Node ID: identifier at line start followed by a shape delimiter or space (arrow line)
669
669
  const NODE_ID_RE = /^([a-zA-Z_][\w-]*)[\s([</{]/;
@@ -623,7 +623,7 @@ export function renderFlowchart(
623
623
  }
624
624
 
625
625
  // Render nodes (top layer)
626
- const colorOff = graph.options?.color === 'off';
626
+ const colorOff = graph.options?.['color'] === 'off';
627
627
  const solid = graph.options?.['solid-fill'] === 'on';
628
628
  for (const node of layout.nodes) {
629
629
  const nodeG = contentG
@@ -431,7 +431,7 @@ export function renderState(
431
431
  }
432
432
 
433
433
  // Render nodes (top layer)
434
- const colorOff = graph.options?.color === 'off';
434
+ const colorOff = graph.options?.['color'] === 'off';
435
435
  const solid = graph.options?.['solid-fill'] === 'on';
436
436
  for (const node of layout.nodes) {
437
437
  const isCollapsedGroup = collapsedGroupIds.has(node.id);
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ import { render as renderInternal } from './render';
13
13
  import {
14
14
  encodeDiagramUrl as encodeDiagramUrlInternal,
15
15
  decodeDiagramUrl as decodeDiagramUrlInternal,
16
+ type CompactViewState,
16
17
  } from './sharing';
17
18
  import { parseDgmo as validate } from './dgmo-router';
18
19
  import { palettes, getPalette } from './palettes';
@@ -20,6 +21,8 @@ import type { PaletteConfig } from './palettes/types';
20
21
  import type { Theme } from './themes';
21
22
  import { formatDgmoError, type DgmoError } from './diagnostics';
22
23
 
24
+ export type { CompactViewState } from './sharing';
25
+
23
26
  // ============================================================
24
27
  // render(text, options?)
25
28
  // ============================================================
@@ -34,6 +37,12 @@ export interface RenderOptions {
34
37
  * 'throw' — throw an Error with the diagnostics
35
38
  */
36
39
  onError?: 'svg' | 'silent' | 'throw';
40
+ /**
41
+ * Pre-applied interactive view state — collapsed sections/columns,
42
+ * active swimlane tag-group, etc. Used to render a specific view
43
+ * non-interactively (server-side render, share-link decode).
44
+ */
45
+ viewState?: CompactViewState;
37
46
  }
38
47
 
39
48
  export interface RenderResult {
@@ -65,6 +74,7 @@ export async function render(
65
74
  const result = await renderInternal(text, {
66
75
  theme: options?.theme,
67
76
  palette: palette.id,
77
+ viewState: options?.viewState,
68
78
  });
69
79
 
70
80
  const errors = result.diagnostics.filter((d) => d.severity === 'error');
@@ -139,6 +149,11 @@ export interface EncodeDiagramUrlOptions {
139
149
  palette?: PaletteConfig;
140
150
  theme?: Theme;
141
151
  filename?: string;
152
+ /**
153
+ * Initial view state to embed in the URL — re-applied when the link is
154
+ * decoded so recipients open the diagram in the same configuration.
155
+ */
156
+ viewState?: CompactViewState;
142
157
  }
143
158
 
144
159
  /**
@@ -158,6 +173,7 @@ export function encodeDiagramUrl(
158
173
  palette: options?.palette?.id,
159
174
  theme: internalTheme,
160
175
  filename: options?.filename,
176
+ viewState: options?.viewState,
161
177
  });
162
178
  return 'error' in result && result.error ? null : (result.url ?? null);
163
179
  }
@@ -188,7 +204,7 @@ export function decodeDiagramUrl(url: string): DecodedDiagramUrl | null {
188
204
  // Palettes + themes (namespaces)
189
205
  // ============================================================
190
206
 
191
- export { palettes } from './palettes';
207
+ export { palettes, getPalette } from './palettes';
192
208
  export { themes, type Theme } from './themes';
193
209
 
194
210
  // ============================================================
@@ -351,11 +351,11 @@ export function parseInfra(content: string): ParsedInfra {
351
351
 
352
352
  // animate (default ON) / no-animate
353
353
  if (trimmed === 'animate') {
354
- result.options.animate = 'on';
354
+ result.options['animate'] = 'on';
355
355
  continue;
356
356
  }
357
357
  if (trimmed === 'no-animate') {
358
- result.options.animate = 'off';
358
+ result.options['animate'] = 'off';
359
359
  continue;
360
360
  }
361
361
 
@@ -550,11 +550,11 @@ export function parseInfra(content: string): ParsedInfra {
550
550
  const pipeMeta = extractPipeMetadata(targetRaw);
551
551
  const targetName = pipeMeta.clean || targetRaw;
552
552
  warnUnparsedPipeMeta(targetName, lineNumber, warn);
553
- const split = pipeMeta.tags.split
554
- ? parseFloat(pipeMeta.tags.split)
553
+ const split = pipeMeta.tags['split']
554
+ ? parseFloat(pipeMeta.tags['split'])
555
555
  : null;
556
- const fanoutRaw = pipeMeta.tags.fanout
557
- ? parseInt(pipeMeta.tags.fanout, 10)
556
+ const fanoutRaw = pipeMeta.tags['fanout']
557
+ ? parseInt(pipeMeta.tags['fanout'], 10)
558
558
  : null;
559
559
  if (fanoutRaw !== null && fanoutRaw < 1) {
560
560
  warn(
@@ -588,11 +588,11 @@ export function parseInfra(content: string): ParsedInfra {
588
588
  const pipeMeta = extractPipeMetadata(targetRaw);
589
589
  const targetName = pipeMeta.clean || targetRaw;
590
590
  warnUnparsedPipeMeta(targetName, lineNumber, warn);
591
- const split = pipeMeta.tags.split
592
- ? parseFloat(pipeMeta.tags.split)
591
+ const split = pipeMeta.tags['split']
592
+ ? parseFloat(pipeMeta.tags['split'])
593
593
  : null;
594
- const fanoutRaw = pipeMeta.tags.fanout
595
- ? parseInt(pipeMeta.tags.fanout, 10)
594
+ const fanoutRaw = pipeMeta.tags['fanout']
595
+ ? parseInt(pipeMeta.tags['fanout'], 10)
596
596
  : null;
597
597
  if (fanoutRaw !== null && fanoutRaw < 1) {
598
598
  warn(
@@ -631,11 +631,11 @@ export function parseInfra(content: string): ParsedInfra {
631
631
  const pipeMeta = extractPipeMetadata(targetRaw);
632
632
  const targetName = pipeMeta.clean || targetRaw;
633
633
  warnUnparsedPipeMeta(targetName, lineNumber, warn);
634
- const split = pipeMeta.tags.split
635
- ? parseFloat(pipeMeta.tags.split)
634
+ const split = pipeMeta.tags['split']
635
+ ? parseFloat(pipeMeta.tags['split'])
636
636
  : null;
637
- const fanoutRaw = pipeMeta.tags.fanout
638
- ? parseInt(pipeMeta.tags.fanout, 10)
637
+ const fanoutRaw = pipeMeta.tags['fanout']
638
+ ? parseInt(pipeMeta.tags['fanout'], 10)
639
639
  : null;
640
640
  if (fanoutRaw !== null && fanoutRaw < 1) {
641
641
  warn(
@@ -669,11 +669,11 @@ export function parseInfra(content: string): ParsedInfra {
669
669
  const pipeMeta = extractPipeMetadata(targetRaw);
670
670
  const targetName = pipeMeta.clean || targetRaw;
671
671
  warnUnparsedPipeMeta(targetName, lineNumber, warn);
672
- const split = pipeMeta.tags.split
673
- ? parseFloat(pipeMeta.tags.split)
672
+ const split = pipeMeta.tags['split']
673
+ ? parseFloat(pipeMeta.tags['split'])
674
674
  : null;
675
- const fanoutRaw = pipeMeta.tags.fanout
676
- ? parseInt(pipeMeta.tags.fanout, 10)
675
+ const fanoutRaw = pipeMeta.tags['fanout']
676
+ ? parseInt(pipeMeta.tags['fanout'], 10)
677
677
  : null;
678
678
  if (fanoutRaw !== null && fanoutRaw < 1) {
679
679
  warn(
@@ -892,7 +892,7 @@ export function parseInfra(content: string): ParsedInfra {
892
892
  // Symbol extraction (for completion API)
893
893
  // ============================================================
894
894
 
895
- import type { DiagramSymbols } from '../completion';
895
+ import type { DiagramSymbols } from '../completion-types';
896
896
 
897
897
  /**
898
898
  * Extract component names (entities) from infra document text.
@@ -1165,7 +1165,7 @@ function renderEdgePaths(
1165
1165
  nodes: InfraLayoutNode[],
1166
1166
  groups: InfraLayoutGroup[],
1167
1167
  palette: PaletteColors,
1168
- isDark: boolean,
1168
+ _isDark: boolean,
1169
1169
  animate: boolean,
1170
1170
  direction: 'LR' | 'TB',
1171
1171
  speedMultiplier: number = 1
@@ -1245,7 +1245,7 @@ function renderEdgeLabels(
1245
1245
  nodes: InfraLayoutNode[],
1246
1246
  groups: InfraLayoutGroup[],
1247
1247
  palette: PaletteColors,
1248
- isDark: boolean,
1248
+ _isDark: boolean,
1249
1249
  animate: boolean,
1250
1250
  direction: 'LR' | 'TB'
1251
1251
  ) {