eventmodeler 0.2.1 → 0.2.3

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 (34) hide show
  1. package/README.md +175 -0
  2. package/dist/index.js +51 -19
  3. package/dist/lib/config.d.ts +2 -0
  4. package/dist/lib/config.js +26 -0
  5. package/dist/lib/format.d.ts +3 -0
  6. package/dist/lib/format.js +11 -0
  7. package/dist/projection.js +29 -35
  8. package/dist/slices/list-chapters/index.d.ts +2 -1
  9. package/dist/slices/list-chapters/index.js +10 -11
  10. package/dist/slices/list-commands/index.d.ts +2 -1
  11. package/dist/slices/list-commands/index.js +9 -10
  12. package/dist/slices/list-events/index.d.ts +2 -1
  13. package/dist/slices/list-events/index.js +33 -13
  14. package/dist/slices/list-slices/index.d.ts +2 -1
  15. package/dist/slices/list-slices/index.js +9 -10
  16. package/dist/slices/search/index.d.ts +2 -1
  17. package/dist/slices/search/index.js +148 -21
  18. package/dist/slices/show-actor/index.d.ts +3 -2
  19. package/dist/slices/show-actor/index.js +44 -9
  20. package/dist/slices/show-aggregate-completeness/index.d.ts +3 -2
  21. package/dist/slices/show-aggregate-completeness/index.js +60 -9
  22. package/dist/slices/show-chapter/index.d.ts +2 -1
  23. package/dist/slices/show-chapter/index.js +9 -9
  24. package/dist/slices/show-command/index.d.ts +2 -1
  25. package/dist/slices/show-command/index.js +52 -9
  26. package/dist/slices/show-completeness/index.d.ts +3 -1
  27. package/dist/slices/show-completeness/index.js +225 -32
  28. package/dist/slices/show-event/index.d.ts +2 -1
  29. package/dist/slices/show-event/index.js +41 -9
  30. package/dist/slices/show-model-summary/index.d.ts +2 -1
  31. package/dist/slices/show-model-summary/index.js +18 -9
  32. package/dist/slices/show-slice/index.d.ts +2 -1
  33. package/dist/slices/show-slice/index.js +162 -9
  34. package/package.json +5 -3
@@ -1,9 +1,62 @@
1
- function escapeXml(str) {
2
- return str
3
- .replace(/&/g, '&')
4
- .replace(/</g, '&lt;')
5
- .replace(/>/g, '&gt;')
6
- .replace(/"/g, '&quot;');
1
+ import { escapeXml, outputJson } from '../../lib/format.js';
2
+ const INCOMING_FLOW_TYPES = {
3
+ command: ['ScreenToCommand', 'ProcessorToCommand'],
4
+ event: ['CommandToEvent'],
5
+ readModel: ['EventToReadModel'],
6
+ screen: ['ReadModelToScreen'],
7
+ processor: ['ReadModelToProcessor'],
8
+ };
9
+ function findElementByName(model, name) {
10
+ const nameLower = name.toLowerCase();
11
+ // Search commands
12
+ 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' };
15
+ }
16
+ }
17
+ // Search events
18
+ 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' };
21
+ }
22
+ }
23
+ // Search read models
24
+ 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' };
27
+ }
28
+ }
29
+ // Search screens
30
+ 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' };
33
+ }
34
+ }
35
+ // Search processors
36
+ 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' };
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+ function getSourceFields(model, sourceId) {
44
+ const cmd = model.commands.get(sourceId);
45
+ if (cmd)
46
+ return cmd.fields;
47
+ const evt = model.events.get(sourceId);
48
+ if (evt)
49
+ return evt.fields;
50
+ const rm = model.readModels.get(sourceId);
51
+ if (rm)
52
+ return rm.fields;
53
+ const scr = model.screens.get(sourceId);
54
+ if (scr)
55
+ return scr.fields;
56
+ const proc = model.processors.get(sourceId);
57
+ if (proc)
58
+ return proc.fields;
59
+ return [];
7
60
  }
8
61
  function flattenFields(fields, prefix = '') {
9
62
  const result = [];
@@ -104,40 +157,82 @@ function calculateFlowCompleteness(model, flow, targetFields, sourceFields) {
104
157
  })),
105
158
  };
106
159
  }
107
- export function showCompleteness(model, readModelName) {
108
- // Find the read model
109
- const nameLower = readModelName.toLowerCase();
110
- const readModels = [...model.readModels.values()];
111
- const readModel = readModels.find(rm => rm.name.toLowerCase() === nameLower || rm.name.toLowerCase().includes(nameLower));
112
- if (!readModel) {
113
- console.error(`Error: Read model not found: ${readModelName}`);
114
- console.error('Available read models:');
115
- for (const rm of readModels) {
116
- console.error(` - ${rm.name}`);
117
- }
160
+ export function showCompleteness(model, elementName, format) {
161
+ // Find the element by name (searches all element types)
162
+ const element = findElementByName(model, elementName);
163
+ if (!element) {
164
+ console.error(`Error: Element not found: ${elementName}`);
165
+ console.error('Search looked in: commands, events, read models, screens, processors');
118
166
  process.exit(1);
119
167
  }
120
- // Find all flows into this read model (EventToReadModel flows)
121
- const incomingFlows = [...model.flows.values()].filter(f => f.targetId === readModel.id && f.flowType === 'EventToReadModel');
168
+ // Find all incoming flows for this element type
169
+ const validFlowTypes = INCOMING_FLOW_TYPES[element.type];
170
+ const incomingFlows = [...model.flows.values()].filter(f => f.targetId === element.id && validFlowTypes.includes(f.flowType));
171
+ const elementTypeLabel = element.type === 'readModel' ? 'read-model' : element.type;
122
172
  if (incomingFlows.length === 0) {
123
- console.log(`<completeness readModel="${escapeXml(readModel.name)}">`);
124
- console.log(' <no-flows>No events flow into this read model</no-flows>');
125
- console.log('</completeness>');
173
+ if (format === 'json') {
174
+ outputJson({
175
+ elementType: elementTypeLabel,
176
+ name: element.name,
177
+ message: `No flows into this ${elementTypeLabel}`
178
+ });
179
+ }
180
+ else {
181
+ console.log(`<completeness ${elementTypeLabel}="${escapeXml(element.name)}">`);
182
+ console.log(` <no-flows>No flows into this ${elementTypeLabel}</no-flows>`);
183
+ console.log('</completeness>');
184
+ }
126
185
  return;
127
186
  }
128
187
  // Calculate completeness for each flow
129
188
  const completenessResults = [];
130
189
  for (const flow of incomingFlows) {
131
- const sourceEvent = model.events.get(flow.sourceId);
132
- if (!sourceEvent)
133
- continue;
134
- const result = calculateFlowCompleteness(model, flow, readModel.fields, sourceEvent.fields);
190
+ const sourceFields = getSourceFields(model, flow.sourceId);
191
+ if (sourceFields.length === 0 && element.fields.length > 0) {
192
+ // Source has no fields but target expects some - still calculate
193
+ }
194
+ const result = calculateFlowCompleteness(model, flow, element.fields, sourceFields);
135
195
  completenessResults.push(result);
136
196
  }
137
- // Output XML
138
197
  const overallComplete = completenessResults.every(r => r.isComplete);
139
198
  const overallStatus = overallComplete ? 'complete' : 'incomplete';
140
- console.log(`<completeness readModel="${escapeXml(readModel.name)}" status="${overallStatus}">`);
199
+ if (format === 'json') {
200
+ const flatTargetFields = flattenFields(element.fields);
201
+ outputJson({
202
+ elementType: elementTypeLabel,
203
+ name: element.name,
204
+ status: overallStatus,
205
+ flows: completenessResults.map(result => ({
206
+ flowId: result.flowId,
207
+ from: result.sourceName,
208
+ sourceType: result.sourceType,
209
+ status: result.isComplete ? 'complete' : 'incomplete',
210
+ ...(result.hasWarnings ? { hasWarnings: true } : {}),
211
+ targetFields: result.targetFields.map(f => ({
212
+ name: f.fieldName,
213
+ status: f.status,
214
+ ...(f.sourceFieldName ? { source: f.sourceFieldName } : {})
215
+ })),
216
+ availableSourceFields: result.sourceFields.map(sf => ({
217
+ id: sf.id,
218
+ name: sf.name,
219
+ type: sf.type,
220
+ ...(sf.isList ? { isList: true } : {})
221
+ }))
222
+ })),
223
+ elementFields: flatTargetFields.map(({ id, path, field }) => ({
224
+ id,
225
+ name: path,
226
+ type: field.fieldType,
227
+ ...(field.isList ? { isList: true } : {}),
228
+ ...(field.isOptional ? { isOptional: true } : {}),
229
+ ...(field.isGenerated ? { isGenerated: true } : {})
230
+ }))
231
+ });
232
+ return;
233
+ }
234
+ // Output XML
235
+ console.log(`<completeness ${elementTypeLabel}="${escapeXml(element.name)}" status="${overallStatus}">`);
141
236
  for (const result of completenessResults) {
142
237
  const flowStatus = result.isComplete ? 'complete' : 'incomplete';
143
238
  const warningAttr = result.hasWarnings ? ' hasWarnings="true"' : '';
@@ -162,9 +257,9 @@ export function showCompleteness(model, readModelName) {
162
257
  console.log(' </available-source-fields>');
163
258
  console.log(' </flow>');
164
259
  }
165
- // Also show the read model's fields with their IDs (needed for mapping)
166
- console.log(' <read-model-fields>');
167
- const flatTargetFields = flattenFields(readModel.fields);
260
+ // Also show the element's fields with their IDs (needed for mapping)
261
+ console.log(` <${elementTypeLabel}-fields>`);
262
+ const flatTargetFields = flattenFields(element.fields);
168
263
  for (const { id, path, field } of flatTargetFields) {
169
264
  const attrs = [`id="${id}"`, `name="${escapeXml(path)}"`, `type="${field.fieldType}"`];
170
265
  if (field.isList)
@@ -175,6 +270,104 @@ export function showCompleteness(model, readModelName) {
175
270
  attrs.push('isGenerated="true"');
176
271
  console.log(` <field ${attrs.join(' ')}/>`);
177
272
  }
178
- console.log(' </read-model-fields>');
273
+ console.log(` </${elementTypeLabel}-fields>`);
179
274
  console.log('</completeness>');
180
275
  }
276
+ export function showModelCompleteness(model, format) {
277
+ const allFlows = [...model.flows.values()];
278
+ const incompleteFlows = [];
279
+ let completeCount = 0;
280
+ let totalCount = 0;
281
+ for (const flow of allFlows) {
282
+ // Get target element info
283
+ let targetName = 'Unknown';
284
+ let targetType = 'unknown';
285
+ let targetFields = [];
286
+ const targetCmd = model.commands.get(flow.targetId);
287
+ const targetEvt = model.events.get(flow.targetId);
288
+ const targetRm = model.readModels.get(flow.targetId);
289
+ const targetScr = model.screens.get(flow.targetId);
290
+ const targetProc = model.processors.get(flow.targetId);
291
+ if (targetCmd) {
292
+ targetName = targetCmd.name;
293
+ targetType = 'command';
294
+ targetFields = targetCmd.fields;
295
+ }
296
+ else if (targetEvt) {
297
+ targetName = targetEvt.name;
298
+ targetType = 'event';
299
+ targetFields = targetEvt.fields;
300
+ }
301
+ else if (targetRm) {
302
+ targetName = targetRm.name;
303
+ targetType = 'read-model';
304
+ targetFields = targetRm.fields;
305
+ }
306
+ else if (targetScr) {
307
+ targetName = targetScr.name;
308
+ targetType = 'screen';
309
+ targetFields = targetScr.fields;
310
+ }
311
+ else if (targetProc) {
312
+ targetName = targetProc.name;
313
+ targetType = 'processor';
314
+ targetFields = targetProc.fields;
315
+ }
316
+ // Get source fields
317
+ const sourceFields = getSourceFields(model, flow.sourceId);
318
+ // Calculate completeness
319
+ const result = calculateFlowCompleteness(model, flow, targetFields, sourceFields);
320
+ totalCount++;
321
+ if (result.isComplete) {
322
+ completeCount++;
323
+ }
324
+ else {
325
+ const unsatisfiedFields = result.targetFields
326
+ .filter(f => f.status === 'unsatisfied')
327
+ .map(f => f.fieldName);
328
+ incompleteFlows.push({
329
+ flow,
330
+ sourceName: result.sourceName,
331
+ targetName,
332
+ targetType,
333
+ unsatisfiedFields,
334
+ });
335
+ }
336
+ }
337
+ const incompleteCount = totalCount - completeCount;
338
+ if (format === 'json') {
339
+ outputJson({
340
+ summary: {
341
+ complete: completeCount,
342
+ incomplete: incompleteCount,
343
+ total: totalCount
344
+ },
345
+ incompleteFlows: incompleteFlows.map(item => ({
346
+ from: item.sourceName,
347
+ to: item.targetName,
348
+ targetType: item.targetType,
349
+ flowType: item.flow.flowType,
350
+ unsatisfiedFields: item.unsatisfiedFields
351
+ }))
352
+ });
353
+ return;
354
+ }
355
+ console.log('<model-completeness>');
356
+ console.log(` <summary complete="${completeCount}" incomplete="${incompleteCount}" total="${totalCount}"/>`);
357
+ if (incompleteFlows.length > 0) {
358
+ console.log(' <incomplete-flows>');
359
+ for (const item of incompleteFlows) {
360
+ console.log(` <flow from="${escapeXml(item.sourceName)}" to="${escapeXml(item.targetName)}" target-type="${item.targetType}" flow-type="${item.flow.flowType}">`);
361
+ if (item.unsatisfiedFields.length > 0) {
362
+ console.log(' <unsatisfied-fields>');
363
+ for (const fieldName of item.unsatisfiedFields) {
364
+ console.log(` <field name="${escapeXml(fieldName)}"/>`);
365
+ }
366
+ console.log(' </unsatisfied-fields>');
367
+ }
368
+ console.log(' </flow>');
369
+ }
370
+ console.log(' </incomplete-flows>');
371
+ }
372
+ console.log('</model-completeness>');
373
+ }
@@ -1,2 +1,3 @@
1
1
  import type { EventModel } from '../../types.js';
2
- export declare function showEvent(model: EventModel, name: string): void;
2
+ import { type OutputFormat } from '../../lib/format.js';
3
+ export declare function showEvent(model: EventModel, name: string, format: OutputFormat): void;
@@ -1,11 +1,4 @@
1
- function escapeXml(str) {
2
- return str
3
- .replace(/&/g, '&amp;')
4
- .replace(/</g, '&lt;')
5
- .replace(/>/g, '&gt;')
6
- .replace(/"/g, '&quot;')
7
- .replace(/'/g, '&apos;');
8
- }
1
+ import { escapeXml, outputJson } from '../../lib/format.js';
9
2
  function formatFieldXml(field, indent) {
10
3
  const attrs = [
11
4
  `name="${escapeXml(field.name)}"`,
@@ -27,6 +20,22 @@ function formatFieldXml(field, indent) {
27
20
  }
28
21
  return `${indent}<field ${attrs.join(' ')}/>\n`;
29
22
  }
23
+ function fieldToJson(field) {
24
+ const result = {
25
+ name: field.name,
26
+ type: field.fieldType
27
+ };
28
+ if (field.isList)
29
+ result.list = true;
30
+ if (field.isGenerated)
31
+ result.generated = true;
32
+ if (field.isOptional)
33
+ result.optional = true;
34
+ if (field.subfields && field.subfields.length > 0) {
35
+ result.subfields = field.subfields.map(fieldToJson);
36
+ }
37
+ return result;
38
+ }
30
39
  // Find which aggregate an event belongs to (center point inside aggregate bounds)
31
40
  function findAggregateForEvent(model, event) {
32
41
  const centerX = event.position.x + event.width / 2;
@@ -78,7 +87,7 @@ function formatEventXml(model, event) {
78
87
  xml += '</event>';
79
88
  return xml;
80
89
  }
81
- export function showEvent(model, name) {
90
+ export function showEvent(model, name, format) {
82
91
  const events = [...model.events.values()];
83
92
  const nameLower = name.toLowerCase();
84
93
  const event = events.find(e => e.name.toLowerCase() === nameLower || e.name.toLowerCase().includes(nameLower));
@@ -90,5 +99,28 @@ export function showEvent(model, name) {
90
99
  }
91
100
  process.exit(1);
92
101
  }
102
+ if (format === 'json') {
103
+ const aggregate = findAggregateForEvent(model, event);
104
+ const incomingFlows = [...model.flows.values()].filter(f => f.targetId === event.id);
105
+ const outgoingFlows = [...model.flows.values()].filter(f => f.sourceId === event.id);
106
+ const result = {
107
+ name: event.name,
108
+ fields: event.fields.map(fieldToJson)
109
+ };
110
+ if (aggregate)
111
+ result.aggregate = aggregate.name;
112
+ const producedBy = incomingFlows
113
+ .map(f => model.commands.get(f.sourceId)?.name)
114
+ .filter(Boolean);
115
+ if (producedBy.length > 0)
116
+ result.producedBy = producedBy;
117
+ const consumedBy = outgoingFlows
118
+ .map(f => model.readModels.get(f.targetId)?.name)
119
+ .filter(Boolean);
120
+ if (consumedBy.length > 0)
121
+ result.consumedBy = consumedBy;
122
+ outputJson(result);
123
+ return;
124
+ }
93
125
  console.log(formatEventXml(model, event));
94
126
  }
@@ -1,2 +1,3 @@
1
1
  import type { EventModel } from '../../types.js';
2
- export declare function showModelSummary(model: EventModel): void;
2
+ import { type OutputFormat } from '../../lib/format.js';
3
+ export declare function showModelSummary(model: EventModel, format: OutputFormat): void;
@@ -1,12 +1,21 @@
1
- function escapeXml(str) {
2
- return str
3
- .replace(/&/g, '&amp;')
4
- .replace(/</g, '&lt;')
5
- .replace(/>/g, '&gt;')
6
- .replace(/"/g, '&quot;')
7
- .replace(/'/g, '&apos;');
8
- }
9
- export function showModelSummary(model) {
1
+ import { escapeXml, outputJson } from '../../lib/format.js';
2
+ export function showModelSummary(model, format) {
3
+ if (format === 'json') {
4
+ outputJson({
5
+ name: model.name,
6
+ slices: model.slices.size,
7
+ commands: model.commands.size,
8
+ events: model.events.size,
9
+ readModels: model.readModels.size,
10
+ screens: model.screens.size,
11
+ processors: model.processors.size,
12
+ aggregates: model.aggregates.size,
13
+ actors: model.actors.size,
14
+ scenarios: model.scenarios.size,
15
+ flows: model.flows.size
16
+ });
17
+ return;
18
+ }
10
19
  console.log(`<model name="${escapeXml(model.name)}">`);
11
20
  console.log(` <slices count="${model.slices.size}"/>`);
12
21
  console.log(` <commands count="${model.commands.size}"/>`);
@@ -1,2 +1,3 @@
1
1
  import type { EventModel } from '../../types.js';
2
- export declare function showSlice(model: EventModel, name: string): void;
2
+ import { type OutputFormat } from '../../lib/format.js';
3
+ export declare function showSlice(model: EventModel, name: string, format: OutputFormat): void;
@@ -1,11 +1,4 @@
1
- function escapeXml(str) {
2
- return str
3
- .replace(/&/g, '&amp;')
4
- .replace(/</g, '&lt;')
5
- .replace(/>/g, '&gt;')
6
- .replace(/"/g, '&quot;')
7
- .replace(/'/g, '&apos;');
8
- }
1
+ import { escapeXml, outputJson } from '../../lib/format.js';
9
2
  function formatFieldValues(values) {
10
3
  if (!values || Object.keys(values).length === 0)
11
4
  return '';
@@ -306,7 +299,163 @@ function formatSliceXml(model, slice) {
306
299
  xml += '</slice>';
307
300
  return xml;
308
301
  }
309
- export function showSlice(model, name) {
302
+ function fieldToJson(field) {
303
+ const result = {
304
+ name: field.name,
305
+ type: field.fieldType
306
+ };
307
+ if (field.isList)
308
+ result.list = true;
309
+ if (field.isGenerated)
310
+ result.generated = true;
311
+ if (field.isOptional)
312
+ result.optional = true;
313
+ if (field.isUserInput)
314
+ result.userInput = true;
315
+ if (field.subfields && field.subfields.length > 0) {
316
+ result.subfields = field.subfields.map(fieldToJson);
317
+ }
318
+ return result;
319
+ }
320
+ function formatSliceJson(model, slice) {
321
+ const components = getSliceComponents(model, slice);
322
+ const scenarios = [...model.scenarios.values()].filter(s => s.sliceId === slice.id);
323
+ const componentIds = new Set();
324
+ components.commands.forEach(c => componentIds.add(c.id));
325
+ components.events.forEach(e => componentIds.add(e.id));
326
+ components.readModels.forEach(rm => componentIds.add(rm.id));
327
+ components.screens.forEach(s => componentIds.add(s.id));
328
+ components.processors.forEach(p => componentIds.add(p.id));
329
+ const flows = [...model.flows.values()].filter(f => componentIds.has(f.sourceId) || componentIds.has(f.targetId));
330
+ const internalFlows = flows.filter(f => componentIds.has(f.sourceId) && componentIds.has(f.targetId));
331
+ function getName(id) {
332
+ return (model.commands.get(id)?.name ??
333
+ model.events.get(id)?.name ??
334
+ model.readModels.get(id)?.name ??
335
+ model.screens.get(id)?.name ??
336
+ model.processors.get(id)?.name ??
337
+ id);
338
+ }
339
+ const result = {
340
+ name: slice.name,
341
+ status: slice.status,
342
+ components: {
343
+ screens: components.screens.map(screen => {
344
+ const screenObj = {
345
+ name: screen.name,
346
+ fields: screen.fields.map(fieldToJson)
347
+ };
348
+ if (screen.originalNodeId) {
349
+ screenObj.linkedCopy = true;
350
+ const originSlice = findSliceForNode(model, screen.originalNodeId);
351
+ if (originSlice)
352
+ screenObj.originSlice = originSlice.name;
353
+ }
354
+ const actor = findActorForScreen(model, screen);
355
+ if (actor)
356
+ screenObj.actor = actor.name;
357
+ return screenObj;
358
+ }),
359
+ commands: components.commands.map(cmd => ({
360
+ name: cmd.name,
361
+ fields: cmd.fields.map(fieldToJson)
362
+ })),
363
+ events: components.events.map(event => {
364
+ const eventObj = {
365
+ name: event.name,
366
+ fields: event.fields.map(fieldToJson)
367
+ };
368
+ if (event.originalNodeId) {
369
+ eventObj.linkedCopy = true;
370
+ const originSlice = findSliceForNode(model, event.originalNodeId);
371
+ if (originSlice)
372
+ eventObj.originSlice = originSlice.name;
373
+ }
374
+ const aggregate = findAggregateForEvent(model, event);
375
+ if (aggregate)
376
+ eventObj.aggregate = aggregate.name;
377
+ return eventObj;
378
+ }),
379
+ readModels: components.readModels.map(rm => {
380
+ const rmObj = {
381
+ name: rm.name,
382
+ fields: rm.fields.map(fieldToJson)
383
+ };
384
+ if (rm.originalNodeId) {
385
+ rmObj.linkedCopy = true;
386
+ const originSlice = findSliceForNode(model, rm.originalNodeId);
387
+ if (originSlice)
388
+ rmObj.originSlice = originSlice.name;
389
+ }
390
+ return rmObj;
391
+ }),
392
+ processors: components.processors.map(proc => ({
393
+ name: proc.name,
394
+ fields: proc.fields.map(fieldToJson)
395
+ }))
396
+ }
397
+ };
398
+ if (internalFlows.length > 0) {
399
+ result.informationFlow = internalFlows.map(flow => ({
400
+ from: getName(flow.sourceId),
401
+ to: getName(flow.targetId)
402
+ }));
403
+ }
404
+ if (scenarios.length > 0) {
405
+ result.scenarios = scenarios.map(scenario => {
406
+ const scenarioObj = { name: scenario.name };
407
+ if (scenario.description)
408
+ scenarioObj.description = scenario.description;
409
+ if (scenario.givenEvents.length > 0) {
410
+ scenarioObj.given = scenario.givenEvents.map(given => {
411
+ const evt = model.events.get(given.eventStickyId);
412
+ return {
413
+ eventType: evt?.name ?? 'UnknownEvent',
414
+ ...(given.fieldValues && Object.keys(given.fieldValues).length > 0 ? { fieldValues: given.fieldValues } : {})
415
+ };
416
+ });
417
+ }
418
+ if (scenario.whenCommand) {
419
+ const cmd = model.commands.get(scenario.whenCommand.commandStickyId);
420
+ scenarioObj.when = {
421
+ commandType: cmd?.name ?? 'UnknownCommand',
422
+ ...(scenario.whenCommand.fieldValues && Object.keys(scenario.whenCommand.fieldValues).length > 0 ? { fieldValues: scenario.whenCommand.fieldValues } : {})
423
+ };
424
+ }
425
+ if (scenario.then.type === 'error') {
426
+ scenarioObj.then = {
427
+ type: 'error',
428
+ ...(scenario.then.errorType ? { errorType: scenario.then.errorType } : {}),
429
+ ...(scenario.then.errorMessage ? { errorMessage: scenario.then.errorMessage } : {})
430
+ };
431
+ }
432
+ else if (scenario.then.type === 'events' && scenario.then.expectedEvents) {
433
+ scenarioObj.then = {
434
+ type: 'events',
435
+ expectedEvents: scenario.then.expectedEvents.map(expected => {
436
+ const evt = model.events.get(expected.eventStickyId);
437
+ return {
438
+ eventType: evt?.name ?? 'UnknownEvent',
439
+ ...(expected.fieldValues && Object.keys(expected.fieldValues).length > 0 ? { fieldValues: expected.fieldValues } : {})
440
+ };
441
+ })
442
+ };
443
+ }
444
+ else if (scenario.then.type === 'readModelAssertion' && scenario.then.readModelAssertion) {
445
+ const assertion = scenario.then.readModelAssertion;
446
+ const rm = model.readModels.get(assertion.readModelStickyId);
447
+ scenarioObj.then = {
448
+ type: 'readModelAssertion',
449
+ readModelType: rm?.name ?? 'UnknownReadModel',
450
+ expectedFieldValues: assertion.expectedFieldValues
451
+ };
452
+ }
453
+ return scenarioObj;
454
+ });
455
+ }
456
+ return result;
457
+ }
458
+ export function showSlice(model, name, format) {
310
459
  const slices = [...model.slices.values()];
311
460
  const nameLower = name.toLowerCase();
312
461
  const slice = slices.find(s => s.name.toLowerCase() === nameLower || s.name.toLowerCase().includes(nameLower));
@@ -318,5 +467,9 @@ export function showSlice(model, name) {
318
467
  }
319
468
  process.exit(1);
320
469
  }
470
+ if (format === 'json') {
471
+ outputJson(formatSliceJson(model, slice));
472
+ return;
473
+ }
321
474
  console.log(formatSliceXml(model, slice));
322
475
  }
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "eventmodeler",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
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": {
7
7
  "eventmodeler": "./dist/index.js"
8
8
  },
9
9
  "files": [
10
- "dist"
10
+ "dist",
11
+ "README.md"
11
12
  ],
12
13
  "scripts": {
13
14
  "build": "tsc",
@@ -27,7 +28,8 @@
27
28
  "license": "MIT",
28
29
  "repository": {
29
30
  "type": "git",
30
- "url": "https://github.com/your-username/event-modeler"
31
+ "url": "https://github.com/theoema/event-modeler",
32
+ "directory": "cli"
31
33
  },
32
34
  "homepage": "https://www.eventmodeling.app",
33
35
  "engines": {