eventmodeler 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 (37) hide show
  1. package/dist/formatters.d.ts +17 -0
  2. package/dist/formatters.js +482 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +186 -0
  5. package/dist/lib/file-loader.d.ts +5 -0
  6. package/dist/lib/file-loader.js +53 -0
  7. package/dist/projection.d.ts +3 -0
  8. package/dist/projection.js +781 -0
  9. package/dist/slices/export-eventmodel-to-json/index.d.ts +2 -0
  10. package/dist/slices/export-eventmodel-to-json/index.js +296 -0
  11. package/dist/slices/list-chapters/index.d.ts +2 -0
  12. package/dist/slices/list-chapters/index.js +22 -0
  13. package/dist/slices/list-commands/index.d.ts +2 -0
  14. package/dist/slices/list-commands/index.js +21 -0
  15. package/dist/slices/list-events/index.d.ts +2 -0
  16. package/dist/slices/list-events/index.js +21 -0
  17. package/dist/slices/list-slices/index.d.ts +2 -0
  18. package/dist/slices/list-slices/index.js +21 -0
  19. package/dist/slices/mark-slice-status/index.d.ts +2 -0
  20. package/dist/slices/mark-slice-status/index.js +38 -0
  21. package/dist/slices/open-app/index.d.ts +1 -0
  22. package/dist/slices/open-app/index.js +36 -0
  23. package/dist/slices/search/index.d.ts +2 -0
  24. package/dist/slices/search/index.js +175 -0
  25. package/dist/slices/show-chapter/index.d.ts +2 -0
  26. package/dist/slices/show-chapter/index.js +43 -0
  27. package/dist/slices/show-command/index.d.ts +2 -0
  28. package/dist/slices/show-command/index.js +78 -0
  29. package/dist/slices/show-event/index.d.ts +2 -0
  30. package/dist/slices/show-event/index.js +75 -0
  31. package/dist/slices/show-model-summary/index.d.ts +2 -0
  32. package/dist/slices/show-model-summary/index.js +20 -0
  33. package/dist/slices/show-slice/index.d.ts +2 -0
  34. package/dist/slices/show-slice/index.js +239 -0
  35. package/dist/types.d.ts +161 -0
  36. package/dist/types.js +1 -0
  37. package/package.json +40 -0
@@ -0,0 +1,781 @@
1
+ export function createEmptyModel() {
2
+ return {
3
+ modelId: '',
4
+ name: '',
5
+ commands: new Map(),
6
+ events: new Map(),
7
+ readModels: new Map(),
8
+ screens: new Map(),
9
+ processors: new Map(),
10
+ slices: new Map(),
11
+ chapters: new Map(),
12
+ scenarios: new Map(),
13
+ flows: new Map(),
14
+ };
15
+ }
16
+ export function projectEvents(rawEvents) {
17
+ const model = createEmptyModel();
18
+ for (const event of rawEvents) {
19
+ applyEvent(model, event);
20
+ }
21
+ return model;
22
+ }
23
+ function applyEvent(model, event) {
24
+ switch (event.type) {
25
+ // Model creation
26
+ case 'EventModelCreated':
27
+ model.modelId = event.modelId;
28
+ model.name = event.name;
29
+ break;
30
+ // Command sticky events
31
+ case 'CommandStickyPlaced':
32
+ model.commands.set(event.commandStickyId, {
33
+ id: event.commandStickyId,
34
+ name: event.name,
35
+ fields: [],
36
+ position: event.position,
37
+ width: event.width,
38
+ height: event.height,
39
+ });
40
+ break;
41
+ case 'CommandStickyMoved': {
42
+ const cmd = model.commands.get(event.commandStickyId);
43
+ if (cmd)
44
+ cmd.position = event.position;
45
+ break;
46
+ }
47
+ case 'CommandStickyRemoved':
48
+ model.commands.delete(event.commandStickyId);
49
+ break;
50
+ case 'CommandRenamed': {
51
+ const cmd = model.commands.get(event.commandStickyId);
52
+ if (cmd)
53
+ cmd.name = event.name;
54
+ break;
55
+ }
56
+ case 'CommandFieldAdded': {
57
+ const cmd = model.commands.get(event.commandStickyId);
58
+ if (cmd)
59
+ cmd.fields.push(event.field);
60
+ break;
61
+ }
62
+ case 'CommandFieldRemoved': {
63
+ const cmd = model.commands.get(event.commandStickyId);
64
+ if (cmd)
65
+ cmd.fields = cmd.fields.filter(f => f.id !== event.fieldId);
66
+ break;
67
+ }
68
+ case 'CommandFieldAdjusted': {
69
+ const cmd = model.commands.get(event.commandStickyId);
70
+ if (cmd) {
71
+ const idx = cmd.fields.findIndex(f => f.id === event.fieldId);
72
+ if (idx !== -1) {
73
+ cmd.fields[idx] = { ...event.field, id: event.fieldId };
74
+ }
75
+ }
76
+ break;
77
+ }
78
+ case 'CommandStickyDuplicated':
79
+ model.commands.set(event.commandStickyId, {
80
+ id: event.commandStickyId,
81
+ name: event.name,
82
+ fields: event.fields,
83
+ position: event.position,
84
+ width: event.width,
85
+ height: event.height,
86
+ });
87
+ break;
88
+ // Event sticky events
89
+ case 'EventStickyPlaced':
90
+ model.events.set(event.eventStickyId, {
91
+ id: event.eventStickyId,
92
+ name: event.name,
93
+ fields: [],
94
+ position: event.position,
95
+ width: event.width,
96
+ height: event.height,
97
+ });
98
+ break;
99
+ case 'EventStickyMoved': {
100
+ const evt = model.events.get(event.eventStickyId);
101
+ if (evt)
102
+ evt.position = event.position;
103
+ break;
104
+ }
105
+ case 'EventStickyRemoved':
106
+ model.events.delete(event.eventStickyId);
107
+ break;
108
+ case 'EventRenamed': {
109
+ const evt = model.events.get(event.eventStickyId);
110
+ if (evt)
111
+ evt.name = event.name;
112
+ break;
113
+ }
114
+ case 'EventFieldAdded': {
115
+ const evt = model.events.get(event.eventStickyId);
116
+ if (evt)
117
+ evt.fields.push(event.field);
118
+ break;
119
+ }
120
+ case 'EventFieldRemoved': {
121
+ const evt = model.events.get(event.eventStickyId);
122
+ if (evt)
123
+ evt.fields = evt.fields.filter(f => f.id !== event.fieldId);
124
+ break;
125
+ }
126
+ case 'EventFieldAdjusted': {
127
+ const evt = model.events.get(event.eventStickyId);
128
+ if (evt) {
129
+ const idx = evt.fields.findIndex(f => f.id === event.fieldId);
130
+ if (idx !== -1) {
131
+ evt.fields[idx] = { ...event.field, id: event.fieldId };
132
+ }
133
+ }
134
+ break;
135
+ }
136
+ case 'EventStickyLinkedCopyCreated': {
137
+ const source = model.events.get(event.sourceEventStickyId);
138
+ model.events.set(event.eventStickyId, {
139
+ id: event.eventStickyId,
140
+ name: source?.name ?? '',
141
+ fields: source?.fields ? [...source.fields] : [],
142
+ position: event.position,
143
+ width: event.width,
144
+ height: event.height,
145
+ canonicalId: event.canonicalId,
146
+ });
147
+ // Also set canonical on source if not already set
148
+ if (source && !source.canonicalId) {
149
+ source.canonicalId = event.canonicalId;
150
+ }
151
+ break;
152
+ }
153
+ case 'EventStickyCanonicalGroupRemoved': {
154
+ const canonicalId = event.canonicalId;
155
+ for (const [id, evt] of model.events) {
156
+ if (evt.canonicalId === canonicalId) {
157
+ model.events.delete(id);
158
+ }
159
+ }
160
+ break;
161
+ }
162
+ // Read model sticky events
163
+ case 'ReadModelStickyPlaced':
164
+ model.readModels.set(event.readModelStickyId, {
165
+ id: event.readModelStickyId,
166
+ name: event.name,
167
+ fields: [],
168
+ position: event.position,
169
+ width: event.width,
170
+ height: event.height,
171
+ });
172
+ break;
173
+ case 'ReadModelStickyMoved': {
174
+ const rm = model.readModels.get(event.readModelStickyId);
175
+ if (rm)
176
+ rm.position = event.position;
177
+ break;
178
+ }
179
+ case 'ReadModelStickyRemoved':
180
+ model.readModels.delete(event.readModelStickyId);
181
+ break;
182
+ case 'ReadModelRenamed': {
183
+ const rm = model.readModels.get(event.readModelStickyId);
184
+ if (rm)
185
+ rm.name = event.name;
186
+ break;
187
+ }
188
+ case 'ReadModelFieldAdded': {
189
+ const rm = model.readModels.get(event.readModelStickyId);
190
+ if (rm)
191
+ rm.fields.push(event.field);
192
+ break;
193
+ }
194
+ case 'ReadModelFieldRemoved': {
195
+ const rm = model.readModels.get(event.readModelStickyId);
196
+ if (rm)
197
+ rm.fields = rm.fields.filter(f => f.id !== event.fieldId);
198
+ break;
199
+ }
200
+ case 'ReadModelFieldAdjusted': {
201
+ const rm = model.readModels.get(event.readModelStickyId);
202
+ if (rm) {
203
+ const idx = rm.fields.findIndex(f => f.id === event.fieldId);
204
+ if (idx !== -1) {
205
+ rm.fields[idx] = { ...event.field, id: event.fieldId };
206
+ }
207
+ }
208
+ break;
209
+ }
210
+ case 'ReadModelStickyLinkedCopyCreated': {
211
+ const source = model.readModels.get(event.sourceReadModelStickyId);
212
+ model.readModels.set(event.readModelStickyId, {
213
+ id: event.readModelStickyId,
214
+ name: source?.name ?? '',
215
+ fields: source?.fields ? [...source.fields] : [],
216
+ position: event.position,
217
+ width: event.width,
218
+ height: event.height,
219
+ canonicalId: event.canonicalId,
220
+ });
221
+ if (source && !source.canonicalId) {
222
+ source.canonicalId = event.canonicalId;
223
+ }
224
+ break;
225
+ }
226
+ case 'ReadModelStickyCanonicalGroupRemoved': {
227
+ const canonicalId = event.canonicalId;
228
+ for (const [id, rm] of model.readModels) {
229
+ if (rm.canonicalId === canonicalId) {
230
+ model.readModels.delete(id);
231
+ }
232
+ }
233
+ break;
234
+ }
235
+ // Screen events
236
+ case 'ScreenPlaced':
237
+ model.screens.set(event.screenId, {
238
+ id: event.screenId,
239
+ name: event.name,
240
+ fields: [],
241
+ position: event.position,
242
+ width: event.width,
243
+ height: event.height,
244
+ });
245
+ break;
246
+ case 'ScreenMoved': {
247
+ const scr = model.screens.get(event.screenId);
248
+ if (scr)
249
+ scr.position = event.position;
250
+ break;
251
+ }
252
+ case 'ScreenRemoved':
253
+ model.screens.delete(event.screenId);
254
+ break;
255
+ case 'ScreenRenamed': {
256
+ const scr = model.screens.get(event.screenId);
257
+ if (scr)
258
+ scr.name = event.name;
259
+ break;
260
+ }
261
+ case 'ScreenFieldAdded': {
262
+ const scr = model.screens.get(event.screenId);
263
+ if (scr)
264
+ scr.fields.push(event.field);
265
+ break;
266
+ }
267
+ case 'ScreenFieldRemoved': {
268
+ const scr = model.screens.get(event.screenId);
269
+ if (scr)
270
+ scr.fields = scr.fields.filter(f => f.id !== event.fieldId);
271
+ break;
272
+ }
273
+ case 'ScreenFieldAdjusted': {
274
+ const scr = model.screens.get(event.screenId);
275
+ if (scr) {
276
+ const idx = scr.fields.findIndex(f => f.id === event.fieldId);
277
+ if (idx !== -1) {
278
+ scr.fields[idx] = { ...event.field, id: event.fieldId };
279
+ }
280
+ }
281
+ break;
282
+ }
283
+ case 'ScreenLinkedCopyCreated': {
284
+ const source = model.screens.get(event.sourceScreenId);
285
+ model.screens.set(event.screenId, {
286
+ id: event.screenId,
287
+ name: source?.name ?? '',
288
+ fields: source?.fields ? [...source.fields] : [],
289
+ position: event.position,
290
+ width: event.width,
291
+ height: event.height,
292
+ canonicalId: event.canonicalId,
293
+ });
294
+ if (source && !source.canonicalId) {
295
+ source.canonicalId = event.canonicalId;
296
+ }
297
+ break;
298
+ }
299
+ case 'ScreenCanonicalGroupRemoved': {
300
+ const canonicalId = event.canonicalId;
301
+ for (const [id, scr] of model.screens) {
302
+ if (scr.canonicalId === canonicalId) {
303
+ model.screens.delete(id);
304
+ }
305
+ }
306
+ break;
307
+ }
308
+ // Processor events
309
+ case 'ProcessorPlaced':
310
+ model.processors.set(event.processorId, {
311
+ id: event.processorId,
312
+ name: event.name,
313
+ fields: [],
314
+ position: event.position,
315
+ width: event.width,
316
+ height: event.height,
317
+ });
318
+ break;
319
+ case 'ProcessorMoved': {
320
+ const proc = model.processors.get(event.processorId);
321
+ if (proc)
322
+ proc.position = event.position;
323
+ break;
324
+ }
325
+ case 'ProcessorRemoved':
326
+ model.processors.delete(event.processorId);
327
+ break;
328
+ case 'ProcessorRenamed': {
329
+ const proc = model.processors.get(event.processorId);
330
+ if (proc)
331
+ proc.name = event.name;
332
+ break;
333
+ }
334
+ case 'ProcessorFieldAdded': {
335
+ const proc = model.processors.get(event.processorId);
336
+ if (proc)
337
+ proc.fields.push(event.field);
338
+ break;
339
+ }
340
+ case 'ProcessorFieldRemoved': {
341
+ const proc = model.processors.get(event.processorId);
342
+ if (proc)
343
+ proc.fields = proc.fields.filter(f => f.id !== event.fieldId);
344
+ break;
345
+ }
346
+ case 'ProcessorFieldAdjusted': {
347
+ const proc = model.processors.get(event.processorId);
348
+ if (proc) {
349
+ const idx = proc.fields.findIndex(f => f.id === event.fieldId);
350
+ if (idx !== -1) {
351
+ proc.fields[idx] = { ...event.field, id: event.fieldId };
352
+ }
353
+ }
354
+ break;
355
+ }
356
+ case 'ProcessorDuplicated':
357
+ model.processors.set(event.processorId, {
358
+ id: event.processorId,
359
+ name: event.name,
360
+ fields: event.fields,
361
+ position: event.position,
362
+ width: event.width,
363
+ height: event.height,
364
+ });
365
+ break;
366
+ // Slice events
367
+ case 'SlicePlaced':
368
+ model.slices.set(event.sliceId, {
369
+ id: event.sliceId,
370
+ name: event.name,
371
+ status: 'created',
372
+ position: event.position,
373
+ size: event.size,
374
+ nodeIds: [],
375
+ });
376
+ break;
377
+ case 'NodesGroupedIntoSlice':
378
+ model.slices.set(event.sliceId, {
379
+ id: event.sliceId,
380
+ name: event.name,
381
+ status: 'created',
382
+ position: event.position,
383
+ size: event.size,
384
+ nodeIds: event.nodeIds,
385
+ });
386
+ break;
387
+ case 'SliceMoved': {
388
+ const slice = model.slices.get(event.sliceId);
389
+ if (slice)
390
+ slice.position = event.position;
391
+ break;
392
+ }
393
+ case 'SliceResized': {
394
+ const slice = model.slices.get(event.sliceId);
395
+ if (slice) {
396
+ slice.position = event.position;
397
+ slice.size = event.size;
398
+ }
399
+ break;
400
+ }
401
+ case 'SliceRemoved':
402
+ model.slices.delete(event.sliceId);
403
+ break;
404
+ case 'SliceRenamed': {
405
+ const slice = model.slices.get(event.sliceId);
406
+ if (slice)
407
+ slice.name = event.name;
408
+ break;
409
+ }
410
+ case 'SliceMarkedAsCreated': {
411
+ const slice = model.slices.get(event.sliceId);
412
+ if (slice)
413
+ slice.status = 'created';
414
+ break;
415
+ }
416
+ case 'SliceMarkedAsInProgress': {
417
+ const slice = model.slices.get(event.sliceId);
418
+ if (slice)
419
+ slice.status = 'in-progress';
420
+ break;
421
+ }
422
+ case 'SliceMarkedAsBlocked': {
423
+ const slice = model.slices.get(event.sliceId);
424
+ if (slice)
425
+ slice.status = 'blocked';
426
+ break;
427
+ }
428
+ case 'SliceMarkedAsDone': {
429
+ const slice = model.slices.get(event.sliceId);
430
+ if (slice)
431
+ slice.status = 'done';
432
+ break;
433
+ }
434
+ case 'SlicesWidthEqualized': {
435
+ const width = event.width;
436
+ for (const sliceId of event.sliceIds) {
437
+ const slice = model.slices.get(sliceId);
438
+ if (slice)
439
+ slice.size.width = width;
440
+ }
441
+ break;
442
+ }
443
+ case 'SlicesHeightEqualized': {
444
+ const height = event.height;
445
+ const y = event.y;
446
+ for (const sliceId of event.sliceIds) {
447
+ const slice = model.slices.get(sliceId);
448
+ if (slice) {
449
+ slice.size.height = height;
450
+ slice.position.y = y;
451
+ }
452
+ }
453
+ break;
454
+ }
455
+ case 'SliceDuplicated':
456
+ model.slices.set(event.sliceId, {
457
+ id: event.sliceId,
458
+ name: event.name,
459
+ status: event.status,
460
+ position: event.position,
461
+ size: event.size,
462
+ nodeIds: [],
463
+ });
464
+ break;
465
+ // Chapter events
466
+ case 'ChapterPlaced':
467
+ model.chapters.set(event.chapterId, {
468
+ id: event.chapterId,
469
+ name: event.name,
470
+ position: event.position,
471
+ size: event.size,
472
+ });
473
+ break;
474
+ case 'ChapterMoved': {
475
+ const chapter = model.chapters.get(event.chapterId);
476
+ if (chapter)
477
+ chapter.position = event.position;
478
+ break;
479
+ }
480
+ case 'ChapterResized': {
481
+ const chapter = model.chapters.get(event.chapterId);
482
+ if (chapter) {
483
+ chapter.position = event.position;
484
+ chapter.size = event.size;
485
+ }
486
+ break;
487
+ }
488
+ case 'ChapterRemoved':
489
+ model.chapters.delete(event.chapterId);
490
+ break;
491
+ case 'ChapterRenamed': {
492
+ const chapter = model.chapters.get(event.chapterId);
493
+ if (chapter)
494
+ chapter.name = event.name;
495
+ break;
496
+ }
497
+ // Flow events
498
+ case 'CommandToEventFlowSpecified':
499
+ model.flows.set(event.flowId, {
500
+ id: event.flowId,
501
+ flowType: 'CommandToEvent',
502
+ sourceId: event.commandStickyId,
503
+ targetId: event.eventStickyId,
504
+ fieldMappings: [],
505
+ });
506
+ break;
507
+ case 'EventToReadModelFlowSpecified':
508
+ model.flows.set(event.flowId, {
509
+ id: event.flowId,
510
+ flowType: 'EventToReadModel',
511
+ sourceId: event.eventStickyId,
512
+ targetId: event.readModelStickyId,
513
+ fieldMappings: [],
514
+ });
515
+ break;
516
+ case 'ReadModelToScreenFlowSpecified':
517
+ model.flows.set(event.flowId, {
518
+ id: event.flowId,
519
+ flowType: 'ReadModelToScreen',
520
+ sourceId: event.readModelStickyId,
521
+ targetId: event.screenId,
522
+ fieldMappings: [],
523
+ });
524
+ break;
525
+ case 'ReadModelToProcessorFlowSpecified':
526
+ model.flows.set(event.flowId, {
527
+ id: event.flowId,
528
+ flowType: 'ReadModelToProcessor',
529
+ sourceId: event.readModelStickyId,
530
+ targetId: event.processorId,
531
+ fieldMappings: [],
532
+ });
533
+ break;
534
+ case 'ScreenToCommandFlowSpecified':
535
+ model.flows.set(event.flowId, {
536
+ id: event.flowId,
537
+ flowType: 'ScreenToCommand',
538
+ sourceId: event.screenId,
539
+ targetId: event.commandStickyId,
540
+ fieldMappings: [],
541
+ });
542
+ break;
543
+ case 'ProcessorToCommandFlowSpecified':
544
+ model.flows.set(event.flowId, {
545
+ id: event.flowId,
546
+ flowType: 'ProcessorToCommand',
547
+ sourceId: event.processorId,
548
+ targetId: event.commandStickyId,
549
+ fieldMappings: [],
550
+ });
551
+ break;
552
+ case 'FlowRemoved':
553
+ model.flows.delete(event.flowId);
554
+ break;
555
+ case 'FieldMappingSpecified': {
556
+ const flow = model.flows.get(event.flowId);
557
+ if (flow)
558
+ flow.fieldMappings = event.mappings;
559
+ break;
560
+ }
561
+ case 'FieldMappingCleared': {
562
+ const flow = model.flows.get(event.flowId);
563
+ if (flow)
564
+ flow.fieldMappings = [];
565
+ break;
566
+ }
567
+ // Quick place events - create node and flow atomically
568
+ case 'CommandQuickPlacedFromScreen':
569
+ model.commands.set(event.commandStickyId, {
570
+ id: event.commandStickyId,
571
+ name: event.name,
572
+ fields: [],
573
+ position: event.position,
574
+ width: event.width,
575
+ height: event.height,
576
+ });
577
+ model.flows.set(event.flowId, {
578
+ id: event.flowId,
579
+ flowType: 'ScreenToCommand',
580
+ sourceId: event.screenId,
581
+ targetId: event.commandStickyId,
582
+ fieldMappings: [],
583
+ });
584
+ break;
585
+ case 'EventQuickPlacedFromCommand':
586
+ model.events.set(event.eventStickyId, {
587
+ id: event.eventStickyId,
588
+ name: event.name,
589
+ fields: [],
590
+ position: event.position,
591
+ width: event.width,
592
+ height: event.height,
593
+ });
594
+ model.flows.set(event.flowId, {
595
+ id: event.flowId,
596
+ flowType: 'CommandToEvent',
597
+ sourceId: event.commandStickyId,
598
+ targetId: event.eventStickyId,
599
+ fieldMappings: [],
600
+ });
601
+ break;
602
+ case 'ReadModelQuickPlacedFromEvent':
603
+ model.readModels.set(event.readModelStickyId, {
604
+ id: event.readModelStickyId,
605
+ name: event.name,
606
+ fields: [],
607
+ position: event.position,
608
+ width: event.width,
609
+ height: event.height,
610
+ });
611
+ model.flows.set(event.flowId, {
612
+ id: event.flowId,
613
+ flowType: 'EventToReadModel',
614
+ sourceId: event.eventStickyId,
615
+ targetId: event.readModelStickyId,
616
+ fieldMappings: [],
617
+ });
618
+ break;
619
+ case 'ScreenQuickPlacedFromReadModel':
620
+ model.screens.set(event.screenId, {
621
+ id: event.screenId,
622
+ name: event.name,
623
+ fields: [],
624
+ position: event.position,
625
+ width: event.width,
626
+ height: event.height,
627
+ });
628
+ model.flows.set(event.flowId, {
629
+ id: event.flowId,
630
+ flowType: 'ReadModelToScreen',
631
+ sourceId: event.readModelStickyId,
632
+ targetId: event.screenId,
633
+ fieldMappings: [],
634
+ });
635
+ break;
636
+ case 'CommandQuickPlacedFromProcessor':
637
+ model.commands.set(event.commandStickyId, {
638
+ id: event.commandStickyId,
639
+ name: event.name,
640
+ fields: [],
641
+ position: event.position,
642
+ width: event.width,
643
+ height: event.height,
644
+ });
645
+ model.flows.set(event.flowId, {
646
+ id: event.flowId,
647
+ flowType: 'ProcessorToCommand',
648
+ sourceId: event.processorId,
649
+ targetId: event.commandStickyId,
650
+ fieldMappings: [],
651
+ });
652
+ break;
653
+ // Field propagation events
654
+ case 'FieldsPropagatedFromCommandToEvent':
655
+ case 'FieldsPropagatedFromEventToCommand':
656
+ case 'FieldsPropagatedFromEventToReadModel':
657
+ case 'FieldsPropagatedFromReadModelToEvent':
658
+ case 'FieldsPropagatedFromReadModelToScreen':
659
+ case 'FieldsPropagatedFromScreenToReadModel':
660
+ case 'FieldsPropagatedFromReadModelToProcessor':
661
+ case 'FieldsPropagatedFromProcessorToReadModel':
662
+ case 'FieldsPropagatedFromScreenToCommand':
663
+ case 'FieldsPropagatedFromCommandToScreen':
664
+ case 'FieldsPropagatedFromProcessorToCommand':
665
+ case 'FieldsPropagatedFromCommandToProcessor':
666
+ handleFieldPropagation(model, event);
667
+ break;
668
+ // Scenario events
669
+ case 'ScenarioCreated':
670
+ model.scenarios.set(event.scenarioId, {
671
+ id: event.scenarioId,
672
+ sliceId: event.sliceId,
673
+ name: event.name,
674
+ description: '',
675
+ position: event.position,
676
+ width: event.width,
677
+ height: event.height,
678
+ givenEvents: [],
679
+ whenCommand: null,
680
+ then: { type: 'events', expectedEvents: [] },
681
+ });
682
+ break;
683
+ case 'ScenarioMoved': {
684
+ const scenario = model.scenarios.get(event.scenarioId);
685
+ if (scenario)
686
+ scenario.position = event.position;
687
+ break;
688
+ }
689
+ case 'ScenarioRemoved':
690
+ model.scenarios.delete(event.scenarioId);
691
+ break;
692
+ case 'ScenarioRenamed': {
693
+ const scenario = model.scenarios.get(event.scenarioId);
694
+ if (scenario)
695
+ scenario.name = event.name;
696
+ break;
697
+ }
698
+ case 'ScenarioDescriptionUpdated': {
699
+ const scenario = model.scenarios.get(event.scenarioId);
700
+ if (scenario)
701
+ scenario.description = event.description;
702
+ break;
703
+ }
704
+ case 'ScenarioGivenEventsUpdated': {
705
+ const scenario = model.scenarios.get(event.scenarioId);
706
+ if (scenario)
707
+ scenario.givenEvents = event.givenEvents;
708
+ break;
709
+ }
710
+ case 'ScenarioWhenCommandUpdated': {
711
+ const scenario = model.scenarios.get(event.scenarioId);
712
+ if (scenario)
713
+ scenario.whenCommand = event.whenCommand;
714
+ break;
715
+ }
716
+ case 'ScenarioThenUpdated': {
717
+ const scenario = model.scenarios.get(event.scenarioId);
718
+ if (scenario)
719
+ scenario.then = event.then;
720
+ break;
721
+ }
722
+ // Ignore visual-only events
723
+ case 'ScreenDesignUpdated':
724
+ // Screen designs are visual-only, not relevant for CLI
725
+ break;
726
+ }
727
+ }
728
+ function handleFieldPropagation(model, event) {
729
+ const fields = event.fields;
730
+ // Determine target based on event type
731
+ if ('eventStickyId' in event) {
732
+ const evt = model.events.get(event.eventStickyId);
733
+ if (evt) {
734
+ for (const field of fields) {
735
+ if (!evt.fields.some(f => f.name === field.name)) {
736
+ evt.fields.push(field);
737
+ }
738
+ }
739
+ }
740
+ }
741
+ else if ('commandStickyId' in event) {
742
+ const cmd = model.commands.get(event.commandStickyId);
743
+ if (cmd) {
744
+ for (const field of fields) {
745
+ if (!cmd.fields.some(f => f.name === field.name)) {
746
+ cmd.fields.push(field);
747
+ }
748
+ }
749
+ }
750
+ }
751
+ else if ('readModelStickyId' in event) {
752
+ const rm = model.readModels.get(event.readModelStickyId);
753
+ if (rm) {
754
+ for (const field of fields) {
755
+ if (!rm.fields.some(f => f.name === field.name)) {
756
+ rm.fields.push(field);
757
+ }
758
+ }
759
+ }
760
+ }
761
+ else if ('screenId' in event) {
762
+ const scr = model.screens.get(event.screenId);
763
+ if (scr) {
764
+ for (const field of fields) {
765
+ if (!scr.fields.some(f => f.name === field.name)) {
766
+ scr.fields.push(field);
767
+ }
768
+ }
769
+ }
770
+ }
771
+ else if ('processorId' in event) {
772
+ const proc = model.processors.get(event.processorId);
773
+ if (proc) {
774
+ for (const field of fields) {
775
+ if (!proc.fields.some(f => f.name === field.name)) {
776
+ proc.fields.push(field);
777
+ }
778
+ }
779
+ }
780
+ }
781
+ }