@polagram/core 0.0.4 → 0.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.
Files changed (119) hide show
  1. package/dist/index.d.ts +102 -113
  2. package/dist/polagram-core.js +1532 -1404
  3. package/dist/polagram-core.umd.cjs +22 -22
  4. package/package.json +3 -2
  5. package/dist/src/api.d.ts +0 -84
  6. package/dist/src/api.js +0 -183
  7. package/dist/src/ast/ast.test.d.ts +0 -1
  8. package/dist/src/ast/ast.test.js +0 -146
  9. package/dist/src/ast/index.d.ts +0 -119
  10. package/dist/src/ast/index.js +0 -2
  11. package/dist/src/config/index.d.ts +0 -1
  12. package/dist/src/config/index.js +0 -1
  13. package/dist/src/config/schema.d.ts +0 -198
  14. package/dist/src/config/schema.js +0 -82
  15. package/dist/src/config/schema.test.d.ts +0 -1
  16. package/dist/src/config/schema.test.js +0 -94
  17. package/dist/src/generator/base/walker.d.ts +0 -19
  18. package/dist/src/generator/base/walker.js +0 -56
  19. package/dist/src/generator/base/walker.test.d.ts +0 -1
  20. package/dist/src/generator/base/walker.test.js +0 -49
  21. package/dist/src/generator/generators/mermaid.d.ts +0 -24
  22. package/dist/src/generator/generators/mermaid.js +0 -140
  23. package/dist/src/generator/generators/mermaid.test.d.ts +0 -1
  24. package/dist/src/generator/generators/mermaid.test.js +0 -70
  25. package/dist/src/generator/generators/plantuml.d.ts +0 -17
  26. package/dist/src/generator/generators/plantuml.js +0 -131
  27. package/dist/src/generator/generators/plantuml.test.d.ts +0 -1
  28. package/dist/src/generator/generators/plantuml.test.js +0 -143
  29. package/dist/src/generator/interface.d.ts +0 -17
  30. package/dist/src/generator/interface.js +0 -1
  31. package/dist/src/index.d.ts +0 -13
  32. package/dist/src/index.js +0 -21
  33. package/dist/src/parser/base/lexer.d.ts +0 -18
  34. package/dist/src/parser/base/lexer.js +0 -95
  35. package/dist/src/parser/base/lexer.test.d.ts +0 -1
  36. package/dist/src/parser/base/lexer.test.js +0 -53
  37. package/dist/src/parser/base/parser.d.ts +0 -14
  38. package/dist/src/parser/base/parser.js +0 -43
  39. package/dist/src/parser/base/parser.test.d.ts +0 -1
  40. package/dist/src/parser/base/parser.test.js +0 -90
  41. package/dist/src/parser/base/token.d.ts +0 -18
  42. package/dist/src/parser/base/token.js +0 -1
  43. package/dist/src/parser/base/tokens.d.ts +0 -8
  44. package/dist/src/parser/base/tokens.js +0 -1
  45. package/dist/src/parser/format-detector.d.ts +0 -55
  46. package/dist/src/parser/format-detector.js +0 -98
  47. package/dist/src/parser/index.d.ts +0 -11
  48. package/dist/src/parser/index.js +0 -33
  49. package/dist/src/parser/index.test.d.ts +0 -1
  50. package/dist/src/parser/index.test.js +0 -23
  51. package/dist/src/parser/interface.d.ts +0 -8
  52. package/dist/src/parser/interface.js +0 -1
  53. package/dist/src/parser/languages/mermaid/constants.d.ts +0 -7
  54. package/dist/src/parser/languages/mermaid/constants.js +0 -20
  55. package/dist/src/parser/languages/mermaid/index.d.ts +0 -4
  56. package/dist/src/parser/languages/mermaid/index.js +0 -11
  57. package/dist/src/parser/languages/mermaid/lexer.d.ts +0 -14
  58. package/dist/src/parser/languages/mermaid/lexer.js +0 -152
  59. package/dist/src/parser/languages/mermaid/lexer.test.d.ts +0 -1
  60. package/dist/src/parser/languages/mermaid/lexer.test.js +0 -58
  61. package/dist/src/parser/languages/mermaid/parser.d.ts +0 -22
  62. package/dist/src/parser/languages/mermaid/parser.js +0 -340
  63. package/dist/src/parser/languages/mermaid/parser.test.d.ts +0 -1
  64. package/dist/src/parser/languages/mermaid/parser.test.js +0 -252
  65. package/dist/src/parser/languages/mermaid/tokens.d.ts +0 -9
  66. package/dist/src/parser/languages/mermaid/tokens.js +0 -1
  67. package/dist/src/parser/languages/plantuml/index.d.ts +0 -4
  68. package/dist/src/parser/languages/plantuml/index.js +0 -11
  69. package/dist/src/parser/languages/plantuml/lexer.d.ts +0 -15
  70. package/dist/src/parser/languages/plantuml/lexer.js +0 -143
  71. package/dist/src/parser/languages/plantuml/parser.d.ts +0 -23
  72. package/dist/src/parser/languages/plantuml/parser.js +0 -481
  73. package/dist/src/parser/languages/plantuml/parser.test.d.ts +0 -1
  74. package/dist/src/parser/languages/plantuml/parser.test.js +0 -236
  75. package/dist/src/parser/languages/plantuml/tokens.d.ts +0 -9
  76. package/dist/src/parser/languages/plantuml/tokens.js +0 -1
  77. package/dist/src/transformer/cleaners/prune-empty.d.ts +0 -9
  78. package/dist/src/transformer/cleaners/prune-empty.js +0 -27
  79. package/dist/src/transformer/cleaners/prune-empty.test.d.ts +0 -1
  80. package/dist/src/transformer/cleaners/prune-empty.test.js +0 -69
  81. package/dist/src/transformer/cleaners/prune-unused.d.ts +0 -5
  82. package/dist/src/transformer/cleaners/prune-unused.js +0 -48
  83. package/dist/src/transformer/cleaners/prune-unused.test.d.ts +0 -1
  84. package/dist/src/transformer/cleaners/prune-unused.test.js +0 -71
  85. package/dist/src/transformer/filters/focus.d.ts +0 -13
  86. package/dist/src/transformer/filters/focus.js +0 -71
  87. package/dist/src/transformer/filters/focus.test.d.ts +0 -1
  88. package/dist/src/transformer/filters/focus.test.js +0 -50
  89. package/dist/src/transformer/filters/remove.d.ts +0 -12
  90. package/dist/src/transformer/filters/remove.js +0 -82
  91. package/dist/src/transformer/filters/remove.test.d.ts +0 -1
  92. package/dist/src/transformer/filters/remove.test.js +0 -38
  93. package/dist/src/transformer/filters/resolve.d.ts +0 -9
  94. package/dist/src/transformer/filters/resolve.js +0 -32
  95. package/dist/src/transformer/filters/resolve.test.d.ts +0 -1
  96. package/dist/src/transformer/filters/resolve.test.js +0 -48
  97. package/dist/src/transformer/index.d.ts +0 -10
  98. package/dist/src/transformer/index.js +0 -10
  99. package/dist/src/transformer/lens.d.ts +0 -12
  100. package/dist/src/transformer/lens.js +0 -58
  101. package/dist/src/transformer/lens.test.d.ts +0 -1
  102. package/dist/src/transformer/lens.test.js +0 -60
  103. package/dist/src/transformer/orchestration/engine.d.ts +0 -5
  104. package/dist/src/transformer/orchestration/engine.js +0 -24
  105. package/dist/src/transformer/orchestration/engine.test.d.ts +0 -1
  106. package/dist/src/transformer/orchestration/engine.test.js +0 -52
  107. package/dist/src/transformer/orchestration/registry.d.ts +0 -10
  108. package/dist/src/transformer/orchestration/registry.js +0 -27
  109. package/dist/src/transformer/selector/matcher.d.ts +0 -9
  110. package/dist/src/transformer/selector/matcher.js +0 -62
  111. package/dist/src/transformer/selector/matcher.test.d.ts +0 -1
  112. package/dist/src/transformer/selector/matcher.test.js +0 -70
  113. package/dist/src/transformer/traverse/walker.d.ts +0 -14
  114. package/dist/src/transformer/traverse/walker.js +0 -67
  115. package/dist/src/transformer/traverse/walker.test.d.ts +0 -1
  116. package/dist/src/transformer/traverse/walker.test.js +0 -111
  117. package/dist/src/transformer/types.d.ts +0 -47
  118. package/dist/src/transformer/types.js +0 -1
  119. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,340 +0,0 @@
1
- import { BaseParser } from '../../base/parser';
2
- import { ARROW_MAPPING } from './constants';
3
- export class Parser extends BaseParser {
4
- constructor(lexer) {
5
- super(lexer);
6
- Object.defineProperty(this, "currentGroup", {
7
- enumerable: true,
8
- configurable: true,
9
- writable: true,
10
- value: null
11
- });
12
- Object.defineProperty(this, "idCounters", {
13
- enumerable: true,
14
- configurable: true,
15
- writable: true,
16
- value: {
17
- evt: 0,
18
- frag: 0,
19
- br: 0,
20
- note: 0,
21
- group: 0,
22
- }
23
- });
24
- }
25
- parse() {
26
- const root = {
27
- kind: 'root',
28
- meta: { version: '1.0.0', source: 'unknown' },
29
- participants: [],
30
- groups: [],
31
- events: []
32
- };
33
- root.events = this.parseBlock(root);
34
- return root;
35
- }
36
- parseBlock(root, stopTokens = []) {
37
- const events = [];
38
- while (this.currToken.type !== 'EOF') {
39
- const type = this.currToken.type;
40
- if (stopTokens.includes(type)) {
41
- return events;
42
- }
43
- if (type === 'NEWLINE') {
44
- this.advance();
45
- continue;
46
- }
47
- if (type === 'SEQUENCE_DIAGRAM') {
48
- root.meta.source = 'mermaid';
49
- this.advance();
50
- continue;
51
- }
52
- if (type === 'TITLE') {
53
- this.advance(); // eat TITLE
54
- root.meta.title = this.readRestOfLine();
55
- continue;
56
- }
57
- if (type === 'BOX') {
58
- const groupEvents = this.parseGroup(root);
59
- events.push(...groupEvents);
60
- continue;
61
- }
62
- if (type === 'PARTICIPANT' || type === 'ACTOR') {
63
- this.parseParticipant(root);
64
- continue;
65
- }
66
- if (type === 'LOOP' || type === 'ALT' || type === 'OPT') {
67
- events.push(this.parseFragment(root));
68
- continue;
69
- }
70
- if (type === 'NOTE') {
71
- events.push(this.parseNote(root));
72
- continue;
73
- }
74
- if (type === 'ACTIVATE' || type === 'DEACTIVATE') {
75
- events.push(this.parseActivation(root));
76
- continue;
77
- }
78
- if (this.isParticipantToken(this.currToken)) {
79
- const msg = this.parseMessage(root);
80
- if (msg) {
81
- events.push(msg);
82
- continue;
83
- }
84
- }
85
- // Skip unknown or unhandled
86
- this.advance();
87
- }
88
- return events;
89
- }
90
- isParticipantToken(tok) {
91
- return tok.type === 'IDENTIFIER' || tok.type === 'STRING';
92
- }
93
- parseGroup(root) {
94
- this.advance(); // eat 'box'
95
- const rawAttrs = this.readRestOfLine().trim();
96
- let name = rawAttrs;
97
- let color;
98
- const parts = rawAttrs.split(/\s+/);
99
- if (parts.length > 0) {
100
- const first = parts[0];
101
- if (first.startsWith('#') || ['rgb', 'rgba', 'transparent', 'aqua', 'grey', 'gray', 'purple', 'red', 'blue', 'green'].includes(first.toLowerCase())) {
102
- color = first;
103
- name = parts.slice(1).join(' ');
104
- }
105
- }
106
- if (!name)
107
- name = `Group ${this.idCounters.group + 1}`;
108
- const group = {
109
- kind: 'group',
110
- id: this.generateId('group'),
111
- name: name,
112
- type: 'box',
113
- participantIds: [],
114
- style: color ? { backgroundColor: color } : undefined
115
- };
116
- root.groups.push(group);
117
- // Set current group context
118
- const previousGroup = this.currentGroup;
119
- this.currentGroup = group;
120
- // Parse content until 'end'
121
- const events = this.parseBlock(root, ['END']);
122
- // Restore context
123
- this.currentGroup = previousGroup;
124
- if (this.currToken.type === 'END') {
125
- this.advance(); // eat 'end'
126
- }
127
- return events;
128
- }
129
- parseFragment(root) {
130
- const type = this.currToken.type;
131
- let operator = 'loop';
132
- if (type === 'ALT')
133
- operator = 'alt';
134
- if (type === 'OPT')
135
- operator = 'opt';
136
- this.advance(); // eat operator
137
- const condition = this.readRestOfLine();
138
- const branches = [];
139
- const events = this.parseBlock(root, ['END', 'ELSE']);
140
- branches.push({
141
- id: this.generateId('br'),
142
- condition,
143
- events
144
- });
145
- while (this.currToken.type === 'ELSE') {
146
- this.advance();
147
- const elseCond = this.readRestOfLine();
148
- const elseEvents = this.parseBlock(root, ['END', 'ELSE']);
149
- branches.push({
150
- id: this.generateId('br'),
151
- condition: elseCond,
152
- events: elseEvents
153
- });
154
- }
155
- if (this.currToken.type === 'END') {
156
- this.advance();
157
- }
158
- return {
159
- kind: 'fragment',
160
- id: this.generateId('frag'),
161
- operator,
162
- branches
163
- };
164
- }
165
- parseParticipant(root) {
166
- const isActor = this.currToken.type === 'ACTOR';
167
- this.advance(); // eat 'participant' or 'actor'
168
- let id = '';
169
- let name = '';
170
- if (this.isParticipantToken(this.currToken)) {
171
- id = this.currToken.literal;
172
- name = id;
173
- this.advance();
174
- }
175
- // Check for 'as'
176
- if (this.currToken.type === 'AS') {
177
- this.advance(); // eat 'as'
178
- // Use readRestOfLine to capture multi-word aliases (e.g., "API Server")
179
- const alias = this.readRestOfLine().trim();
180
- if (alias) {
181
- name = alias;
182
- }
183
- }
184
- const existing = root.participants.find(p => p.id === id);
185
- if (!existing) {
186
- root.participants.push({
187
- id,
188
- name,
189
- type: isActor ? 'actor' : 'participant'
190
- });
191
- }
192
- else {
193
- if (name !== id)
194
- existing.name = name;
195
- if (isActor)
196
- existing.type = 'actor';
197
- }
198
- // Assign to current group if exists
199
- if (this.currentGroup) {
200
- if (!this.currentGroup.participantIds.includes(id)) {
201
- this.currentGroup.participantIds.push(id);
202
- }
203
- }
204
- }
205
- parseNote(root) {
206
- this.advance(); // eat 'note'
207
- let position = 'over'; // default
208
- if (this.currToken.type === 'LEFT') {
209
- position = 'left';
210
- this.advance();
211
- }
212
- else if (this.currToken.type === 'RIGHT') {
213
- position = 'right';
214
- this.advance();
215
- }
216
- else if (this.currToken.type === 'OVER') {
217
- position = 'over';
218
- this.advance();
219
- }
220
- // consume 'of' if present (optional in some cases but usually note right of A)
221
- if (this.currToken.type === 'OF') {
222
- this.advance();
223
- }
224
- const participantIds = [];
225
- while (this.isParticipantToken(this.currToken)) {
226
- participantIds.push(this.currToken.literal);
227
- this.ensureParticipant(root, this.currToken.literal);
228
- this.advance();
229
- if (this.currToken.type === 'COMMA') {
230
- this.advance();
231
- }
232
- else {
233
- break;
234
- }
235
- }
236
- let text = '';
237
- if (this.currToken.type === 'COLON') {
238
- this.advance();
239
- text = this.readRestOfLine();
240
- }
241
- return {
242
- kind: 'note',
243
- id: this.generateId('note'),
244
- position,
245
- participantIds,
246
- text
247
- };
248
- }
249
- parseActivation(root) {
250
- const action = this.currToken.type === 'ACTIVATE' ? 'activate' : 'deactivate';
251
- this.advance(); // eat command
252
- let participantId = '';
253
- if (this.isParticipantToken(this.currToken)) {
254
- participantId = this.currToken.literal;
255
- this.ensureParticipant(root, participantId);
256
- this.advance();
257
- }
258
- return {
259
- kind: 'activation',
260
- participantId,
261
- action
262
- };
263
- }
264
- parseMessage(root) {
265
- if (this.peekToken.type !== 'ARROW') {
266
- return null;
267
- }
268
- const fromId = this.currToken.literal;
269
- this.ensureParticipant(root, fromId);
270
- this.advance(); // eat from
271
- if (this.currToken.type !== 'ARROW') {
272
- return null;
273
- }
274
- const arrowLiteral = this.currToken.literal;
275
- this.advance(); // eat arrow
276
- let activateTarget = false;
277
- let deactivateSource = false;
278
- if (this.currToken.type === 'PLUS') {
279
- activateTarget = true;
280
- this.advance();
281
- }
282
- if (this.currToken.type === 'MINUS') {
283
- deactivateSource = true;
284
- this.advance();
285
- }
286
- if (!this.isParticipantToken(this.currToken))
287
- return null;
288
- const toId = this.currToken.literal;
289
- this.ensureParticipant(root, toId);
290
- this.advance(); // eat to
291
- let text = '';
292
- if (this.currToken.type === 'COLON') {
293
- this.advance();
294
- text = this.readRestOfLine();
295
- }
296
- const { type, style } = this.resolveArrow(arrowLiteral);
297
- return {
298
- kind: 'message',
299
- id: this.generateId('evt'),
300
- from: fromId,
301
- to: toId,
302
- text: text,
303
- type: type,
304
- style: style,
305
- lifecycle: (activateTarget || deactivateSource) ? { activateTarget, deactivateSource } : undefined
306
- };
307
- }
308
- resolveArrow(arrow) {
309
- const mapping = ARROW_MAPPING[arrow];
310
- if (mapping) {
311
- return mapping;
312
- }
313
- return { type: 'sync', style: { line: 'solid', head: 'arrow' } };
314
- }
315
- generateId(prefix) {
316
- this.idCounters[prefix]++;
317
- return `${prefix}_${this.idCounters[prefix]}`;
318
- }
319
- readRestOfLine() {
320
- if (this.currToken.type === 'NEWLINE' || this.currToken.type === 'EOF') {
321
- return '';
322
- }
323
- const start = this.currToken.start;
324
- let end = this.currToken.end;
325
- while (this.currToken.type !== 'NEWLINE' && this.currToken.type !== 'EOF') {
326
- end = this.currToken.end;
327
- this.advance();
328
- }
329
- return this.lexer.getInput().slice(start, end);
330
- }
331
- ensureParticipant(root, id) {
332
- if (!root.participants.find(p => p.id === id)) {
333
- root.participants.push({
334
- id,
335
- name: id,
336
- type: 'participant'
337
- });
338
- }
339
- }
340
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,252 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { Lexer } from './lexer';
3
- import { Parser } from './parser';
4
- describe('Mermaid Parser', () => {
5
- const parse = (input) => {
6
- const lexer = new Lexer(input);
7
- const parser = new Parser(lexer);
8
- return parser.parse();
9
- };
10
- describe('Basic Document Structure', () => {
11
- it('should parse "sequenceDiagram" header', () => {
12
- const input = `sequenceDiagram`;
13
- const ast = parse(input);
14
- expect(ast.kind).toBe('root');
15
- expect(ast.meta.source).toBe('mermaid');
16
- expect(ast.participants).toEqual([]);
17
- expect(ast.events).toEqual([]);
18
- });
19
- it('should handle newlines around header', () => {
20
- const input = `
21
- sequenceDiagram
22
- `;
23
- const ast = parse(input);
24
- expect(ast.meta.source).toBe('mermaid');
25
- });
26
- });
27
- describe('Participant Definitions', () => {
28
- it('should parse simple participant', () => {
29
- const input = `
30
- sequenceDiagram
31
- participant Alice
32
- participant Bob
33
- `;
34
- const ast = parse(input);
35
- expect(ast.participants).toHaveLength(2);
36
- expect(ast.participants[0]).toMatchObject({
37
- id: 'Alice',
38
- name: 'Alice',
39
- type: 'participant'
40
- });
41
- expect(ast.participants[1]).toMatchObject({
42
- id: 'Bob',
43
- name: 'Bob',
44
- type: 'participant'
45
- });
46
- });
47
- it('should parse participant with multi-word alias', () => {
48
- const input = `
49
- sequenceDiagram
50
- participant API as API Server
51
- participant DB as Database System
52
- API->>DB: Query
53
- `;
54
- const ast = parse(input);
55
- const api = ast.participants.find(p => p.id === 'API');
56
- expect(api).toBeDefined();
57
- expect(api?.name).toBe('API Server');
58
- });
59
- });
60
- describe('Message Definitions', () => {
61
- it('should parse sync message and implicit participants', () => {
62
- const input = `
63
- sequenceDiagram
64
- A->B: Hello
65
- `;
66
- const ast = parse(input);
67
- expect(ast.participants).toHaveLength(2);
68
- expect(ast.participants).toMatchObject([
69
- { id: 'A', name: 'A' },
70
- { id: 'B', name: 'B' }
71
- ]);
72
- expect(ast.events).toHaveLength(1);
73
- expect(ast.events[0]).toMatchObject({
74
- kind: 'message',
75
- from: 'A',
76
- to: 'B',
77
- text: 'Hello',
78
- type: 'sync',
79
- style: { line: 'solid', head: 'open' }
80
- });
81
- });
82
- it('should parse various arrow types', () => {
83
- const input = `
84
- sequenceDiagram
85
- A->>B: Arrow
86
- A-->>B: Reply Arrow
87
- A-->B: Dotted Open
88
- A-xB: Cross
89
- `;
90
- const ast = parse(input);
91
- expect(ast.events).toHaveLength(4);
92
- const m1 = ast.events[0];
93
- const m2 = ast.events[1];
94
- const m3 = ast.events[2];
95
- const m4 = ast.events[3];
96
- // ->> : sync, solid, arrow
97
- expect(m1).toMatchObject({
98
- kind: 'message',
99
- type: 'sync',
100
- style: { line: 'solid', head: 'arrow' }
101
- });
102
- // -->> : reply, dotted, arrow
103
- expect(m2).toMatchObject({
104
- kind: 'message',
105
- type: 'reply',
106
- style: { line: 'dotted', head: 'arrow' }
107
- });
108
- // --> : reply, dotted, open
109
- expect(m3).toMatchObject({
110
- kind: 'message',
111
- type: 'reply',
112
- style: { line: 'dotted', head: 'open' }
113
- });
114
- // -x : destroy, solid, cross
115
- expect(m4).toMatchObject({
116
- kind: 'message',
117
- type: 'destroy',
118
- style: { line: 'solid', head: 'cross' }
119
- });
120
- });
121
- it('should parse lifecycle activations in messages', () => {
122
- const input = `
123
- sequenceDiagram
124
- A->>+B: Activate Target
125
- B-->>-A: Deactivate Source
126
- `;
127
- const ast = parse(input);
128
- const m1 = ast.events[0];
129
- const m2 = ast.events[1];
130
- if (m1.kind !== 'message' || m2.kind !== 'message') {
131
- throw new Error('Expected message events');
132
- }
133
- expect(m1.lifecycle).toMatchObject({ activateTarget: true, deactivateSource: false });
134
- expect(m2.lifecycle).toMatchObject({ activateTarget: false, deactivateSource: true });
135
- });
136
- });
137
- describe('Lifecycle Definitions', () => {
138
- it('should parse standalone activate/deactivate', () => {
139
- const input = `
140
- sequenceDiagram
141
- activate A
142
- deactivate A
143
- `;
144
- const ast = parse(input);
145
- expect(ast.events).toHaveLength(2);
146
- expect(ast.events[0]).toMatchObject({ kind: 'activation', participantId: 'A', action: 'activate' });
147
- expect(ast.events[1]).toMatchObject({ kind: 'activation', participantId: 'A', action: 'deactivate' });
148
- });
149
- });
150
- describe('Note Definitions', () => {
151
- it('should parse note positions', () => {
152
- const input = `
153
- sequenceDiagram
154
- note left of A: Note Left
155
- note right of A: Note Right
156
- note over A: Note Over
157
- `;
158
- const ast = parse(input);
159
- expect(ast.events).toHaveLength(3);
160
- expect(ast.events[0]).toMatchObject({ kind: 'note', position: 'left', participantIds: ['A'], text: 'Note Left' });
161
- expect(ast.events[1]).toMatchObject({ kind: 'note', position: 'right', participantIds: ['A'], text: 'Note Right' });
162
- expect(ast.events[2]).toMatchObject({ kind: 'note', position: 'over', participantIds: ['A'], text: 'Note Over' });
163
- });
164
- it('should parse note over multiple participants', () => {
165
- const input = `sequenceDiagram\nnote over A,B: Shared Note`;
166
- const ast = parse(input);
167
- expect(ast.events[0]).toMatchObject({ kind: 'note', position: 'over', participantIds: ['A', 'B'], text: 'Shared Note' });
168
- });
169
- });
170
- describe('Fragment Definitions', () => {
171
- it('should parse loop fragment', () => {
172
- const input = `
173
- sequenceDiagram
174
- loop Every minute
175
- A->B: Check
176
- end
177
- `;
178
- const ast = parse(input);
179
- expect(ast.events).toHaveLength(1);
180
- const fragment = ast.events[0];
181
- expect(fragment.kind).toBe('fragment');
182
- if (fragment.kind !== 'fragment')
183
- return; // type guard
184
- expect(fragment.operator).toBe('loop');
185
- expect(fragment.branches).toHaveLength(1);
186
- expect(fragment.branches[0].condition).toBe('Every minute');
187
- expect(fragment.branches[0].events).toHaveLength(1);
188
- expect(fragment.branches[0].events[0].kind).toBe('message');
189
- });
190
- it('should parse alt/else fragment', () => {
191
- const input = `
192
- sequenceDiagram
193
- alt Success
194
- A->B: OK
195
- else Failure
196
- A->B: Error
197
- end
198
- `;
199
- const ast = parse(input);
200
- const fragment = ast.events[0];
201
- expect(fragment.kind).toBe('fragment');
202
- expect(fragment.operator).toBe('alt');
203
- expect(fragment.branches).toHaveLength(2);
204
- expect(fragment.branches[0].condition).toBe('Success');
205
- expect(fragment.branches[0].events).toHaveLength(1);
206
- expect(fragment.branches[1].condition).toBe('Failure');
207
- expect(fragment.branches[1].events).toHaveLength(1);
208
- });
209
- });
210
- describe('Box/Group Definitions', () => {
211
- it('should parse box definition with color and name', () => {
212
- const code = `
213
- sequenceDiagram
214
- box "Frontend" #eef
215
- participant A
216
- participant B
217
- end
218
- box Backend
219
- participant C
220
- end
221
- A->>C: Request
222
- C-->>A: Response
223
- `;
224
- const ast = parse(code);
225
- // Verify participants
226
- expect(ast.participants).toHaveLength(3);
227
- expect(ast.participants.map(p => p.id)).toEqual(['A', 'B', 'C']);
228
- // Verify groups
229
- expect(ast.groups).toHaveLength(2);
230
- const group1 = ast.groups[0];
231
- expect(group1.name).toContain('Frontend');
232
- expect(group1.participantIds).toEqual(['A', 'B']);
233
- const group2 = ast.groups[1];
234
- expect(group2.name).toBe('Backend');
235
- expect(group2.participantIds).toEqual(['C']);
236
- });
237
- it('should handle messages inside box block', () => {
238
- const code = `
239
- sequenceDiagram
240
- participant U
241
- box App
242
- participant A
243
- U->>A: inside box
244
- end
245
- `;
246
- const ast = parse(code);
247
- expect(ast.events).toHaveLength(1);
248
- expect(ast.events[0].kind).toBe('message');
249
- expect(ast.groups[0].participantIds).toEqual(['A']);
250
- });
251
- });
252
- });
@@ -1,9 +0,0 @@
1
- export type TokenType = 'SEQUENCE_DIAGRAM' | 'NEWLINE' | 'EOF' | 'PARTICIPANT' | 'COLON' | 'IDENTIFIER' | 'STRING' | 'ARROW' | 'LOOP' | 'ALT' | 'OPT' | 'END' | 'ELSE' | 'UNKNOWN' | 'AS' | 'ACTOR' | 'TITLE' | 'NOTE' | 'LEFT' | 'RIGHT' | 'OVER' | 'OF' | 'ACTIVATE' | 'DEACTIVATE' | 'PLUS' | 'MINUS' | 'COMMA' | 'BOX';
2
- export interface Token {
3
- type: TokenType;
4
- literal: string;
5
- line: number;
6
- column: number;
7
- start: number;
8
- end: number;
9
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,4 +0,0 @@
1
- import { DiagramParser } from '../../interface';
2
- export declare const plantumlParser: DiagramParser;
3
- export { Lexer } from './lexer';
4
- export { Parser } from './parser';
@@ -1,11 +0,0 @@
1
- import { Lexer } from './lexer';
2
- import { Parser } from './parser';
3
- export const plantumlParser = {
4
- parse: (code) => {
5
- const lexer = new Lexer(code);
6
- const parser = new Parser(lexer);
7
- return parser.parse();
8
- }
9
- };
10
- export { Lexer } from './lexer';
11
- export { Parser } from './parser';
@@ -1,15 +0,0 @@
1
- import { BaseLexer } from '../../base/lexer';
2
- import { Token } from './tokens';
3
- export declare class Lexer extends BaseLexer<Token> {
4
- constructor(input: string);
5
- nextToken(): Token;
6
- private newToken;
7
- private readIdentifier;
8
- private readString;
9
- private lookupIdent;
10
- private readArrow;
11
- private peekExact;
12
- private peekString;
13
- readRestOfLine(): string;
14
- private readMulti;
15
- }