eventmodeler 0.2.3 → 0.2.4

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 (32) hide show
  1. package/dist/index.js +63 -0
  2. package/dist/lib/element-lookup.d.ts +47 -0
  3. package/dist/lib/element-lookup.js +86 -0
  4. package/dist/lib/slice-utils.d.ts +83 -0
  5. package/dist/lib/slice-utils.js +135 -0
  6. package/dist/projection.js +132 -0
  7. package/dist/slices/add-field/index.js +4 -33
  8. package/dist/slices/add-scenario/index.js +7 -74
  9. package/dist/slices/create-automation-slice/index.d.ts +2 -0
  10. package/dist/slices/create-automation-slice/index.js +217 -0
  11. package/dist/slices/create-flow/index.d.ts +2 -0
  12. package/dist/slices/create-flow/index.js +177 -0
  13. package/dist/slices/create-state-change-slice/index.d.ts +2 -0
  14. package/dist/slices/create-state-change-slice/index.js +239 -0
  15. package/dist/slices/create-state-view-slice/index.d.ts +2 -0
  16. package/dist/slices/create-state-view-slice/index.js +120 -0
  17. package/dist/slices/list-chapters/index.js +2 -2
  18. package/dist/slices/list-commands/index.js +2 -2
  19. package/dist/slices/list-events/index.js +3 -2
  20. package/dist/slices/list-slices/index.js +2 -2
  21. package/dist/slices/mark-slice-status/index.js +2 -11
  22. package/dist/slices/remove-field/index.js +4 -33
  23. package/dist/slices/remove-scenario/index.js +45 -11
  24. package/dist/slices/show-actor/index.js +2 -11
  25. package/dist/slices/show-aggregate-completeness/index.js +2 -11
  26. package/dist/slices/show-chapter/index.js +6 -14
  27. package/dist/slices/show-command/index.js +4 -12
  28. package/dist/slices/show-completeness/index.js +108 -19
  29. package/dist/slices/show-event/index.js +4 -12
  30. package/dist/slices/show-slice/index.js +14 -17
  31. package/dist/slices/update-field/index.js +4 -33
  32. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { escapeXml, outputJson } from '../../lib/format.js';
2
+ import { findElementOrExit } from '../../lib/element-lookup.js';
2
3
  function getSlicesUnderChapter(model, chapter) {
3
4
  // A slice is "under" a chapter if its horizontal center falls within the chapter's x range
4
5
  const chapterLeft = chapter.position.x;
@@ -9,33 +10,24 @@ function getSlicesUnderChapter(model, chapter) {
9
10
  }).sort((a, b) => a.position.x - b.position.x);
10
11
  }
11
12
  export function showChapter(model, name, format) {
12
- const chapters = [...model.chapters.values()];
13
- const nameLower = name.toLowerCase();
14
- const chapter = chapters.find(c => c.name.toLowerCase() === nameLower || c.name.toLowerCase().includes(nameLower));
15
- if (!chapter) {
16
- console.error(`Error: Chapter not found: ${name}`);
17
- console.error('Available chapters:');
18
- for (const c of chapters) {
19
- console.error(` - ${c.name}`);
20
- }
21
- process.exit(1);
22
- }
13
+ const chapter = findElementOrExit(model.chapters, name, 'chapter');
23
14
  const slices = getSlicesUnderChapter(model, chapter);
24
15
  if (format === 'json') {
25
16
  outputJson({
17
+ id: chapter.id,
26
18
  name: chapter.name,
27
- slices: slices.map(s => ({ name: s.name, status: s.status }))
19
+ slices: slices.map(s => ({ id: s.id, name: s.name, status: s.status }))
28
20
  });
29
21
  return;
30
22
  }
31
- console.log(`<chapter name="${escapeXml(chapter.name)}">`);
23
+ console.log(`<chapter id="${chapter.id}" name="${escapeXml(chapter.name)}">`);
32
24
  if (slices.length === 0) {
33
25
  console.log(' <slices/>');
34
26
  }
35
27
  else {
36
28
  console.log(' <slices>');
37
29
  for (const slice of slices) {
38
- console.log(` <slice name="${escapeXml(slice.name)}" status="${slice.status}"/>`);
30
+ console.log(` <slice id="${slice.id}" name="${escapeXml(slice.name)}" status="${slice.status}"/>`);
39
31
  }
40
32
  console.log(' </slices>');
41
33
  }
@@ -1,4 +1,5 @@
1
1
  import { escapeXml, outputJson } from '../../lib/format.js';
2
+ import { findElementOrExit } from '../../lib/element-lookup.js';
2
3
  function formatFieldXml(field, indent) {
3
4
  const attrs = [
4
5
  `name="${escapeXml(field.name)}"`,
@@ -54,7 +55,7 @@ function findAggregateForEvent(model, event) {
54
55
  return null;
55
56
  }
56
57
  function formatCommandXml(model, command) {
57
- let xml = `<command name="${escapeXml(command.name)}">\n`;
58
+ let xml = `<command id="${command.id}" name="${escapeXml(command.name)}">\n`;
58
59
  if (command.fields.length > 0) {
59
60
  xml += ' <fields>\n';
60
61
  for (const field of command.fields) {
@@ -92,21 +93,12 @@ function formatCommandXml(model, command) {
92
93
  return xml;
93
94
  }
94
95
  export function showCommand(model, name, format) {
95
- const commands = [...model.commands.values()];
96
- const nameLower = name.toLowerCase();
97
- const command = commands.find(c => c.name.toLowerCase() === nameLower || c.name.toLowerCase().includes(nameLower));
98
- if (!command) {
99
- console.error(`Error: Command not found: ${name}`);
100
- console.error('Available commands:');
101
- for (const c of commands) {
102
- console.error(` - ${c.name}`);
103
- }
104
- process.exit(1);
105
- }
96
+ const command = findElementOrExit(model.commands, name, 'command');
106
97
  if (format === 'json') {
107
98
  const incomingFlows = [...model.flows.values()].filter(f => f.targetId === command.id);
108
99
  const outgoingFlows = [...model.flows.values()].filter(f => f.sourceId === command.id);
109
100
  const result = {
101
+ id: command.id,
110
102
  name: command.name,
111
103
  fields: command.fields.map(fieldToJson)
112
104
  };
@@ -7,38 +7,92 @@ const INCOMING_FLOW_TYPES = {
7
7
  processor: ['ReadModelToProcessor'],
8
8
  };
9
9
  function findElementByName(model, name) {
10
+ // Check for UUID lookup (id:prefix or full UUID format)
11
+ if (name.startsWith('id:')) {
12
+ const idSearch = name.slice(3).toLowerCase();
13
+ // Search all element types by ID
14
+ for (const cmd of model.commands.values()) {
15
+ if (cmd.id.toLowerCase().startsWith(idSearch)) {
16
+ return { element: { id: cmd.id, name: cmd.name, fields: cmd.fields, type: 'command' }, ambiguous: [] };
17
+ }
18
+ }
19
+ for (const evt of model.events.values()) {
20
+ if (evt.id.toLowerCase().startsWith(idSearch)) {
21
+ return { element: { id: evt.id, name: evt.name, fields: evt.fields, type: 'event' }, ambiguous: [] };
22
+ }
23
+ }
24
+ for (const rm of model.readModels.values()) {
25
+ if (rm.id.toLowerCase().startsWith(idSearch)) {
26
+ return { element: { id: rm.id, name: rm.name, fields: rm.fields, type: 'readModel' }, ambiguous: [] };
27
+ }
28
+ }
29
+ for (const scr of model.screens.values()) {
30
+ if (scr.id.toLowerCase().startsWith(idSearch)) {
31
+ return { element: { id: scr.id, name: scr.name, fields: scr.fields, type: 'screen' }, ambiguous: [] };
32
+ }
33
+ }
34
+ for (const proc of model.processors.values()) {
35
+ if (proc.id.toLowerCase().startsWith(idSearch)) {
36
+ return { element: { id: proc.id, name: proc.name, fields: proc.fields, type: 'processor' }, ambiguous: [] };
37
+ }
38
+ }
39
+ return { element: null, ambiguous: [] };
40
+ }
41
+ // Check if it's a full UUID
42
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
43
+ if (uuidPattern.test(name)) {
44
+ const cmd = model.commands.get(name);
45
+ if (cmd)
46
+ return { element: { id: cmd.id, name: cmd.name, fields: cmd.fields, type: 'command' }, ambiguous: [] };
47
+ const evt = model.events.get(name);
48
+ if (evt)
49
+ return { element: { id: evt.id, name: evt.name, fields: evt.fields, type: 'event' }, ambiguous: [] };
50
+ const rm = model.readModels.get(name);
51
+ if (rm)
52
+ return { element: { id: rm.id, name: rm.name, fields: rm.fields, type: 'readModel' }, ambiguous: [] };
53
+ const scr = model.screens.get(name);
54
+ if (scr)
55
+ return { element: { id: scr.id, name: scr.name, fields: scr.fields, type: 'screen' }, ambiguous: [] };
56
+ const proc = model.processors.get(name);
57
+ if (proc)
58
+ return { element: { id: proc.id, name: proc.name, fields: proc.fields, type: 'processor' }, ambiguous: [] };
59
+ return { element: null, ambiguous: [] };
60
+ }
61
+ // Case-insensitive exact name match across all types
10
62
  const nameLower = name.toLowerCase();
11
- // Search commands
63
+ const matches = [];
12
64
  for (const cmd of model.commands.values()) {
13
- if (cmd.name.toLowerCase() === nameLower || cmd.name.toLowerCase().includes(nameLower)) {
14
- return { id: cmd.id, name: cmd.name, fields: cmd.fields, type: 'command' };
65
+ if (cmd.name.toLowerCase() === nameLower) {
66
+ matches.push({ element: { id: cmd.id, name: cmd.name, fields: cmd.fields, type: 'command' }, type: 'command' });
15
67
  }
16
68
  }
17
- // Search events
18
69
  for (const evt of model.events.values()) {
19
- if (evt.name.toLowerCase() === nameLower || evt.name.toLowerCase().includes(nameLower)) {
20
- return { id: evt.id, name: evt.name, fields: evt.fields, type: 'event' };
70
+ if (evt.name.toLowerCase() === nameLower) {
71
+ matches.push({ element: { id: evt.id, name: evt.name, fields: evt.fields, type: 'event' }, type: 'event' });
21
72
  }
22
73
  }
23
- // Search read models
24
74
  for (const rm of model.readModels.values()) {
25
- if (rm.name.toLowerCase() === nameLower || rm.name.toLowerCase().includes(nameLower)) {
26
- return { id: rm.id, name: rm.name, fields: rm.fields, type: 'readModel' };
75
+ if (rm.name.toLowerCase() === nameLower) {
76
+ matches.push({ element: { id: rm.id, name: rm.name, fields: rm.fields, type: 'readModel' }, type: 'readModel' });
27
77
  }
28
78
  }
29
- // Search screens
30
79
  for (const scr of model.screens.values()) {
31
- if (scr.name.toLowerCase() === nameLower || scr.name.toLowerCase().includes(nameLower)) {
32
- return { id: scr.id, name: scr.name, fields: scr.fields, type: 'screen' };
80
+ if (scr.name.toLowerCase() === nameLower) {
81
+ matches.push({ element: { id: scr.id, name: scr.name, fields: scr.fields, type: 'screen' }, type: 'screen' });
33
82
  }
34
83
  }
35
- // Search processors
36
84
  for (const proc of model.processors.values()) {
37
- if (proc.name.toLowerCase() === nameLower || proc.name.toLowerCase().includes(nameLower)) {
38
- return { id: proc.id, name: proc.name, fields: proc.fields, type: 'processor' };
85
+ if (proc.name.toLowerCase() === nameLower) {
86
+ matches.push({ element: { id: proc.id, name: proc.name, fields: proc.fields, type: 'processor' }, type: 'processor' });
39
87
  }
40
88
  }
41
- return null;
89
+ if (matches.length === 1) {
90
+ return { element: matches[0].element, ambiguous: [] };
91
+ }
92
+ if (matches.length > 1) {
93
+ return { element: null, ambiguous: matches };
94
+ }
95
+ return { element: null, ambiguous: [] };
42
96
  }
43
97
  function getSourceFields(model, sourceId) {
44
98
  const cmd = model.commands.get(sourceId);
@@ -159,10 +213,45 @@ function calculateFlowCompleteness(model, flow, targetFields, sourceFields) {
159
213
  }
160
214
  export function showCompleteness(model, elementName, format) {
161
215
  // Find the element by name (searches all element types)
162
- const element = findElementByName(model, elementName);
216
+ const { element, ambiguous } = findElementByName(model, elementName);
217
+ if (ambiguous.length > 0) {
218
+ console.error(`Error: Multiple elements found with name "${elementName}"`);
219
+ console.error('Please specify using the element ID:');
220
+ for (const match of ambiguous) {
221
+ console.error(` - "${match.element.name}" (${match.type}) (id: ${match.element.id})`);
222
+ }
223
+ console.error('');
224
+ console.error(`Usage: eventmodeler show completeness "id:${ambiguous[0].element.id.slice(0, 8)}"`);
225
+ process.exit(1);
226
+ }
163
227
  if (!element) {
164
- console.error(`Error: Element not found: ${elementName}`);
165
- console.error('Search looked in: commands, events, read models, screens, processors');
228
+ console.error(`Error: Element not found: "${elementName}"`);
229
+ // List all available elements with their IDs
230
+ const allElements = [];
231
+ for (const cmd of model.commands.values()) {
232
+ allElements.push({ name: cmd.name, type: 'command', id: cmd.id });
233
+ }
234
+ for (const evt of model.events.values()) {
235
+ allElements.push({ name: evt.name, type: 'event', id: evt.id });
236
+ }
237
+ for (const rm of model.readModels.values()) {
238
+ allElements.push({ name: rm.name, type: 'read model', id: rm.id });
239
+ }
240
+ for (const scr of model.screens.values()) {
241
+ allElements.push({ name: scr.name, type: 'screen', id: scr.id });
242
+ }
243
+ for (const proc of model.processors.values()) {
244
+ allElements.push({ name: proc.name, type: 'processor', id: proc.id });
245
+ }
246
+ if (allElements.length > 0) {
247
+ console.error('Available elements:');
248
+ for (const el of allElements) {
249
+ console.error(` - "${el.name}" (${el.type}) (id: ${el.id.slice(0, 8)})`);
250
+ }
251
+ }
252
+ else {
253
+ console.error('No elements exist in the model.');
254
+ }
166
255
  process.exit(1);
167
256
  }
168
257
  // Find all incoming flows for this element type
@@ -1,4 +1,5 @@
1
1
  import { escapeXml, outputJson } from '../../lib/format.js';
2
+ import { findElementOrExit } from '../../lib/element-lookup.js';
2
3
  function formatFieldXml(field, indent) {
3
4
  const attrs = [
4
5
  `name="${escapeXml(field.name)}"`,
@@ -56,7 +57,7 @@ function findAggregateForEvent(model, event) {
56
57
  function formatEventXml(model, event) {
57
58
  const aggregate = findAggregateForEvent(model, event);
58
59
  const aggregateAttr = aggregate ? ` aggregate="${escapeXml(aggregate.name)}"` : '';
59
- let xml = `<event name="${escapeXml(event.name)}"${aggregateAttr}>\n`;
60
+ let xml = `<event id="${event.id}" name="${escapeXml(event.name)}"${aggregateAttr}>\n`;
60
61
  if (event.fields.length > 0) {
61
62
  xml += ' <fields>\n';
62
63
  for (const field of event.fields) {
@@ -88,22 +89,13 @@ function formatEventXml(model, event) {
88
89
  return xml;
89
90
  }
90
91
  export function showEvent(model, name, format) {
91
- const events = [...model.events.values()];
92
- const nameLower = name.toLowerCase();
93
- const event = events.find(e => e.name.toLowerCase() === nameLower || e.name.toLowerCase().includes(nameLower));
94
- if (!event) {
95
- console.error(`Error: Event not found: ${name}`);
96
- console.error('Available events:');
97
- for (const e of events) {
98
- console.error(` - ${e.name}`);
99
- }
100
- process.exit(1);
101
- }
92
+ const event = findElementOrExit(model.events, name, 'event');
102
93
  if (format === 'json') {
103
94
  const aggregate = findAggregateForEvent(model, event);
104
95
  const incomingFlows = [...model.flows.values()].filter(f => f.targetId === event.id);
105
96
  const outgoingFlows = [...model.flows.values()].filter(f => f.sourceId === event.id);
106
97
  const result = {
98
+ id: event.id,
107
99
  name: event.name,
108
100
  fields: event.fields.map(fieldToJson)
109
101
  };
@@ -1,4 +1,5 @@
1
1
  import { escapeXml, outputJson } from '../../lib/format.js';
2
+ import { findElementOrExit } from '../../lib/element-lookup.js';
2
3
  function formatFieldValues(values) {
3
4
  if (!values || Object.keys(values).length === 0)
4
5
  return '';
@@ -120,7 +121,7 @@ function formatSliceXml(model, slice) {
120
121
  model.processors.get(id)?.name ??
121
122
  id);
122
123
  }
123
- let xml = `<slice name="${escapeXml(slice.name)}" status="${slice.status}">\n`;
124
+ let xml = `<slice id="${slice.id}" name="${escapeXml(slice.name)}" status="${slice.status}">\n`;
124
125
  xml += ' <components>\n';
125
126
  for (const screen of components.screens) {
126
127
  // Check if this is a linked copy
@@ -135,7 +136,7 @@ function formatSliceXml(model, slice) {
135
136
  // Check which actor this screen belongs to
136
137
  const actor = findActorForScreen(model, screen);
137
138
  const actorAttr = actor ? ` actor="${escapeXml(actor.name)}"` : '';
138
- xml += ` <screen name="${escapeXml(screen.name)}"${copyAttr}${originAttr}${actorAttr}>\n`;
139
+ xml += ` <screen id="${screen.id}" name="${escapeXml(screen.name)}"${copyAttr}${originAttr}${actorAttr}>\n`;
139
140
  if (screen.fields.length > 0) {
140
141
  xml += ' <fields>\n';
141
142
  for (const field of screen.fields) {
@@ -146,7 +147,7 @@ function formatSliceXml(model, slice) {
146
147
  xml += ' </screen>\n';
147
148
  }
148
149
  for (const command of components.commands) {
149
- xml += ` <command name="${escapeXml(command.name)}">\n`;
150
+ xml += ` <command id="${command.id}" name="${escapeXml(command.name)}">\n`;
150
151
  if (command.fields.length > 0) {
151
152
  xml += ' <fields>\n';
152
153
  for (const field of command.fields) {
@@ -177,7 +178,7 @@ function formatSliceXml(model, slice) {
177
178
  // Check which aggregate this event belongs to
178
179
  const aggregate = findAggregateForEvent(model, event);
179
180
  const aggregateAttr = aggregate ? ` aggregate="${escapeXml(aggregate.name)}"` : '';
180
- xml += ` <event name="${escapeXml(event.name)}"${copyAttr}${originAttr}${aggregateAttr}>\n`;
181
+ xml += ` <event id="${event.id}" name="${escapeXml(event.name)}"${copyAttr}${originAttr}${aggregateAttr}>\n`;
181
182
  if (event.fields.length > 0) {
182
183
  xml += ' <fields>\n';
183
184
  for (const field of event.fields) {
@@ -208,7 +209,7 @@ function formatSliceXml(model, slice) {
208
209
  originAttr = ` origin-slice="${escapeXml(originSlice.name)}"`;
209
210
  }
210
211
  }
211
- xml += ` <read-model name="${escapeXml(readModel.name)}"${copyAttr}${originAttr}>\n`;
212
+ xml += ` <read-model id="${readModel.id}" name="${escapeXml(readModel.name)}"${copyAttr}${originAttr}>\n`;
212
213
  if (readModel.fields.length > 0) {
213
214
  xml += ' <fields>\n';
214
215
  for (const field of readModel.fields) {
@@ -219,7 +220,7 @@ function formatSliceXml(model, slice) {
219
220
  xml += ' </read-model>\n';
220
221
  }
221
222
  for (const processor of components.processors) {
222
- xml += ` <processor name="${escapeXml(processor.name)}">\n`;
223
+ xml += ` <processor id="${processor.id}" name="${escapeXml(processor.name)}">\n`;
223
224
  if (processor.fields.length > 0) {
224
225
  xml += ' <fields>\n';
225
226
  for (const field of processor.fields) {
@@ -337,11 +338,13 @@ function formatSliceJson(model, slice) {
337
338
  id);
338
339
  }
339
340
  const result = {
341
+ id: slice.id,
340
342
  name: slice.name,
341
343
  status: slice.status,
342
344
  components: {
343
345
  screens: components.screens.map(screen => {
344
346
  const screenObj = {
347
+ id: screen.id,
345
348
  name: screen.name,
346
349
  fields: screen.fields.map(fieldToJson)
347
350
  };
@@ -357,11 +360,13 @@ function formatSliceJson(model, slice) {
357
360
  return screenObj;
358
361
  }),
359
362
  commands: components.commands.map(cmd => ({
363
+ id: cmd.id,
360
364
  name: cmd.name,
361
365
  fields: cmd.fields.map(fieldToJson)
362
366
  })),
363
367
  events: components.events.map(event => {
364
368
  const eventObj = {
369
+ id: event.id,
365
370
  name: event.name,
366
371
  fields: event.fields.map(fieldToJson)
367
372
  };
@@ -378,6 +383,7 @@ function formatSliceJson(model, slice) {
378
383
  }),
379
384
  readModels: components.readModels.map(rm => {
380
385
  const rmObj = {
386
+ id: rm.id,
381
387
  name: rm.name,
382
388
  fields: rm.fields.map(fieldToJson)
383
389
  };
@@ -390,6 +396,7 @@ function formatSliceJson(model, slice) {
390
396
  return rmObj;
391
397
  }),
392
398
  processors: components.processors.map(proc => ({
399
+ id: proc.id,
393
400
  name: proc.name,
394
401
  fields: proc.fields.map(fieldToJson)
395
402
  }))
@@ -456,17 +463,7 @@ function formatSliceJson(model, slice) {
456
463
  return result;
457
464
  }
458
465
  export function showSlice(model, name, format) {
459
- const slices = [...model.slices.values()];
460
- const nameLower = name.toLowerCase();
461
- const slice = slices.find(s => s.name.toLowerCase() === nameLower || s.name.toLowerCase().includes(nameLower));
462
- if (!slice) {
463
- console.error(`Error: Slice not found: ${name}`);
464
- console.error('Available slices:');
465
- for (const s of slices) {
466
- console.error(` - ${s.name}`);
467
- }
468
- process.exit(1);
469
- }
466
+ const slice = findElementOrExit(model.slices, name, 'slice');
470
467
  if (format === 'json') {
471
468
  outputJson(formatSliceJson(model, slice));
472
469
  return;
@@ -1,4 +1,5 @@
1
1
  import { appendEvent } from '../../lib/file-loader.js';
2
+ import { findElementOrExit } from '../../lib/element-lookup.js';
2
3
  function findFieldByName(fields, fieldName) {
3
4
  const nameLower = fieldName.toLowerCase();
4
5
  for (const field of fields) {
@@ -52,17 +53,7 @@ export function updateField(model, filePath, options, fieldName, updates) {
52
53
  }
53
54
  }
54
55
  function updateCommandField(model, filePath, commandName, fieldName, updates) {
55
- const nameLower = commandName.toLowerCase();
56
- const commands = [...model.commands.values()];
57
- const command = commands.find(c => c.name.toLowerCase() === nameLower || c.name.toLowerCase().includes(nameLower));
58
- if (!command) {
59
- console.error(`Error: Command not found: ${commandName}`);
60
- console.error('Available commands:');
61
- for (const c of commands) {
62
- console.error(` - ${c.name}`);
63
- }
64
- process.exit(1);
65
- }
56
+ const command = findElementOrExit(model.commands, commandName, 'command');
66
57
  const field = findFieldByName(command.fields, fieldName);
67
58
  if (!field) {
68
59
  console.error(`Error: Field "${fieldName}" not found on command "${command.name}"`);
@@ -86,17 +77,7 @@ function updateCommandField(model, filePath, commandName, fieldName, updates) {
86
77
  logUpdates(updates);
87
78
  }
88
79
  function updateEventField(model, filePath, eventName, fieldName, updates) {
89
- const nameLower = eventName.toLowerCase();
90
- const events = [...model.events.values()];
91
- const event = events.find(e => e.name.toLowerCase() === nameLower || e.name.toLowerCase().includes(nameLower));
92
- if (!event) {
93
- console.error(`Error: Event not found: ${eventName}`);
94
- console.error('Available events:');
95
- for (const e of events) {
96
- console.error(` - ${e.name}`);
97
- }
98
- process.exit(1);
99
- }
80
+ const event = findElementOrExit(model.events, eventName, 'event');
100
81
  const field = findFieldByName(event.fields, fieldName);
101
82
  if (!field) {
102
83
  console.error(`Error: Field "${fieldName}" not found on event "${event.name}"`);
@@ -120,17 +101,7 @@ function updateEventField(model, filePath, eventName, fieldName, updates) {
120
101
  logUpdates(updates);
121
102
  }
122
103
  function updateReadModelField(model, filePath, readModelName, fieldName, updates) {
123
- const nameLower = readModelName.toLowerCase();
124
- const readModels = [...model.readModels.values()];
125
- const readModel = readModels.find(rm => rm.name.toLowerCase() === nameLower || rm.name.toLowerCase().includes(nameLower));
126
- if (!readModel) {
127
- console.error(`Error: Read model not found: ${readModelName}`);
128
- console.error('Available read models:');
129
- for (const rm of readModels) {
130
- console.error(` - ${rm.name}`);
131
- }
132
- process.exit(1);
133
- }
104
+ const readModel = findElementOrExit(model.readModels, readModelName, 'read model');
134
105
  const field = findFieldByName(readModel.fields, fieldName);
135
106
  if (!field) {
136
107
  console.error(`Error: Field "${fieldName}" not found on read model "${readModel.name}"`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eventmodeler",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "CLI tool for interacting with Event Model files - query, update, and export event models from the terminal",
5
5
  "type": "module",
6
6
  "bin": {