eventmodeler 0.4.1 → 0.4.2

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.
@@ -84,21 +84,40 @@ export declare function mapFields(modelId: string, sourceName: string, targetNam
84
84
  export interface ScenarioInput {
85
85
  name: string;
86
86
  description?: string;
87
- given?: {
88
- type: 'events';
89
- events: string[];
90
- };
87
+ given?: Array<{
88
+ event: string;
89
+ fieldValues?: Record<string, unknown>;
90
+ }>;
91
91
  when?: {
92
- type: 'command' | 'events';
93
- command?: string;
94
- events?: string[];
92
+ command?: {
93
+ command: string;
94
+ fieldValues?: Record<string, unknown>;
95
+ };
96
+ events?: Array<{
97
+ event: string;
98
+ fieldValues?: Record<string, unknown>;
99
+ }>;
95
100
  };
96
101
  then: {
97
- type: 'events' | 'error' | 'command';
98
- events?: string[];
102
+ type: 'events' | 'error' | 'command' | 'readModelAssertion' | 'noCommand';
103
+ events?: Array<{
104
+ event: string;
105
+ fieldValues?: Record<string, unknown>;
106
+ }>;
99
107
  errorMessage?: string;
100
108
  errorType?: string;
101
- command?: string;
109
+ command?: {
110
+ command: string;
111
+ fieldValues?: Record<string, unknown>;
112
+ };
113
+ readModelAssertion?: {
114
+ readModel: string;
115
+ givenEvents?: Array<{
116
+ event: string;
117
+ fieldValues?: Record<string, unknown>;
118
+ }>;
119
+ expectedFieldValues?: Record<string, unknown>;
120
+ };
102
121
  };
103
122
  }
104
123
  export declare function createScenario(modelId: string, sliceName: string, scenario: ScenarioInput): Promise<{
@@ -118,6 +137,7 @@ export interface CompoundFieldInput {
118
137
  isOptional?: boolean;
119
138
  isGenerated?: boolean;
120
139
  isUserInput?: boolean;
140
+ subfields?: CompoundFieldInput[];
121
141
  }
122
142
  export interface StateChangeSliceInput {
123
143
  sliceName: string;
@@ -200,6 +220,40 @@ export declare function showChapter(modelId: string, chapterName: string, format
200
220
  export declare function showActor(modelId: string, actorName: string, format?: OutputFormat): Promise<string>;
201
221
  export declare function exportJson(modelId: string): Promise<string>;
202
222
  export declare function codegenSlice(modelId: string, sliceName: string): Promise<string>;
223
+ export interface CodegenChapterEventsField {
224
+ name: string;
225
+ type: string;
226
+ list?: boolean;
227
+ generated?: boolean;
228
+ optional?: boolean;
229
+ userInput?: boolean;
230
+ subfields?: CodegenChapterEventsField[];
231
+ }
232
+ export interface CodegenChapterEventsChapter {
233
+ id: string;
234
+ name: string;
235
+ parent?: CodegenChapterEventsChapter | null;
236
+ }
237
+ export interface CodegenChapterEventsSlice {
238
+ id: string;
239
+ name: string;
240
+ sliceType: string;
241
+ }
242
+ export interface CodegenChapterEventsEvent {
243
+ id: string;
244
+ name: string;
245
+ fields: CodegenChapterEventsField[];
246
+ aggregate?: string;
247
+ sourceSlices: string[];
248
+ }
249
+ export interface CodegenChapterEventsResponse {
250
+ chapter: CodegenChapterEventsChapter | null;
251
+ sliceCount: number;
252
+ eventCount: number;
253
+ slices: CodegenChapterEventsSlice[];
254
+ events: CodegenChapterEventsEvent[];
255
+ }
256
+ export declare function codegenEvents(modelId: string, chapterName?: string): Promise<CodegenChapterEventsResponse>;
203
257
  export declare function listAggregates(modelId: string, format?: OutputFormat): Promise<string>;
204
258
  export declare function listReadModels(modelId: string, format?: OutputFormat): Promise<string>;
205
259
  export declare function listScreens(modelId: string, format?: OutputFormat): Promise<string>;
@@ -37,13 +37,13 @@ async function handleHttpError(response, path) {
37
37
  async function cliGet(path, params) {
38
38
  const backendUrl = getBackendUrl();
39
39
  const accessToken = await getValidAccessToken();
40
- const url = new URL(`${backendUrl}${path}`);
41
- for (const [key, value] of Object.entries(params)) {
42
- if (value !== undefined) {
43
- url.searchParams.set(key, value);
44
- }
45
- }
46
- const response = await fetch(url.toString(), {
40
+ // Build query string manually using encodeURIComponent (uses %20 for spaces, not +)
41
+ const queryParts = Object.entries(params)
42
+ .filter(([_, value]) => value !== undefined)
43
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
44
+ const queryString = queryParts.length > 0 ? `?${queryParts.join('&')}` : '';
45
+ const fullUrl = `${backendUrl}${path}${queryString}`;
46
+ const response = await fetch(fullUrl, {
47
47
  method: 'GET',
48
48
  headers: {
49
49
  Accept: 'application/json',
@@ -166,13 +166,14 @@ function formatToMediaType(format) {
166
166
  async function cliGetText(path, params, format) {
167
167
  const backendUrl = getBackendUrl();
168
168
  const accessToken = await getValidAccessToken();
169
- const url = new URL(`${backendUrl}${path}`);
170
- for (const [key, value] of Object.entries(params)) {
171
- if (value !== undefined) {
172
- url.searchParams.set(key, value);
173
- }
174
- }
175
- const response = await fetch(url.toString(), {
169
+ // Build query string manually using encodeURIComponent (uses %20 for spaces, not +)
170
+ // Some servers don't decode + as space in query parameters
171
+ const queryParts = Object.entries(params)
172
+ .filter(([_, value]) => value !== undefined)
173
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
174
+ const queryString = queryParts.length > 0 ? `?${queryParts.join('&')}` : '';
175
+ const fullUrl = `${backendUrl}${path}${queryString}`;
176
+ const response = await fetch(fullUrl, {
176
177
  method: 'GET',
177
178
  headers: {
178
179
  Accept: formatToMediaType(format),
@@ -206,6 +207,106 @@ export async function exportJson(modelId) {
206
207
  export async function codegenSlice(modelId, sliceName) {
207
208
  return cliGetText('/cli/codegen-slice', { modelId, name: sliceName });
208
209
  }
210
+ function countFieldsDeep(fields) {
211
+ if (!fields || fields.length === 0) {
212
+ return 0;
213
+ }
214
+ return fields.reduce((count, field) => count + 1 + countFieldsDeep(field.subfields), 0);
215
+ }
216
+ function normalizeName(name) {
217
+ return (name ?? '').trim().toLowerCase();
218
+ }
219
+ export async function codegenEvents(modelId, chapterName) {
220
+ const slicesOutput = await listSlices(modelId, 'json', chapterName);
221
+ let slicesPayload;
222
+ try {
223
+ slicesPayload = JSON.parse(slicesOutput);
224
+ }
225
+ catch (error) {
226
+ const message = error instanceof Error ? error.message : String(error);
227
+ throw new Error(`Invalid JSON from /cli/list-slices: ${message}`);
228
+ }
229
+ const slices = (slicesPayload.slices ?? [])
230
+ .filter((slice) => typeof slice.id === 'string' && typeof slice.name === 'string');
231
+ if (slices.length === 0) {
232
+ return {
233
+ chapter: null,
234
+ sliceCount: 0,
235
+ eventCount: 0,
236
+ slices: [],
237
+ events: [],
238
+ };
239
+ }
240
+ const codegenOutputs = await Promise.all(slices.map(async (slice) => {
241
+ const raw = await codegenSlice(modelId, slice.name);
242
+ try {
243
+ return JSON.parse(raw);
244
+ }
245
+ catch (error) {
246
+ const message = error instanceof Error ? error.message : String(error);
247
+ throw new Error(`Invalid JSON from /cli/codegen-slice for "${slice.name}": ${message}`);
248
+ }
249
+ }));
250
+ const aggregatedSlices = [];
251
+ const eventsByName = new Map();
252
+ let chapter = null;
253
+ for (let i = 0; i < codegenOutputs.length; i++) {
254
+ const codegenOutput = codegenOutputs[i];
255
+ const fallbackSlice = slices[i];
256
+ const sliceName = codegenOutput.slice?.name ?? fallbackSlice.name;
257
+ aggregatedSlices.push({
258
+ id: codegenOutput.slice?.id ?? fallbackSlice.id,
259
+ name: sliceName,
260
+ sliceType: codegenOutput.sliceType ?? 'STATE_CHANGE',
261
+ });
262
+ if (chapterName && !chapter && codegenOutput.chapter) {
263
+ chapter = codegenOutput.chapter;
264
+ }
265
+ for (const event of codegenOutput.elements?.events ?? []) {
266
+ if (!event.name) {
267
+ continue;
268
+ }
269
+ const key = normalizeName(event.name);
270
+ if (!key) {
271
+ continue;
272
+ }
273
+ const existing = eventsByName.get(key);
274
+ if (!existing) {
275
+ eventsByName.set(key, {
276
+ id: event.id ?? '',
277
+ name: event.name,
278
+ fields: event.fields ?? [],
279
+ aggregate: event.aggregate,
280
+ sourceSlices: [sliceName],
281
+ });
282
+ continue;
283
+ }
284
+ if (!existing.sourceSlices.includes(sliceName)) {
285
+ existing.sourceSlices.push(sliceName);
286
+ }
287
+ if (!existing.aggregate && event.aggregate) {
288
+ existing.aggregate = event.aggregate;
289
+ }
290
+ if (countFieldsDeep(event.fields) > countFieldsDeep(existing.fields)) {
291
+ existing.fields = event.fields ?? [];
292
+ }
293
+ if (!existing.id && event.id) {
294
+ existing.id = event.id;
295
+ }
296
+ }
297
+ }
298
+ const events = [...eventsByName.values()]
299
+ .map(event => ({ ...event, sourceSlices: [...event.sourceSlices].sort((a, b) => a.localeCompare(b)) }))
300
+ .sort((a, b) => a.name.localeCompare(b.name));
301
+ const sortedSlices = [...aggregatedSlices].sort((a, b) => a.name.localeCompare(b.name));
302
+ return {
303
+ chapter,
304
+ sliceCount: sortedSlices.length,
305
+ eventCount: events.length,
306
+ slices: sortedSlices,
307
+ events,
308
+ };
309
+ }
209
310
  export async function listAggregates(modelId, format = 'xml') {
210
311
  return cliGetText('/cli/list-aggregates', { modelId }, format);
211
312
  }
package/dist/index.js CHANGED
@@ -22,7 +22,7 @@ import { openApp } from './slices/open-app/index.js';
22
22
  import { search } from './slices/search/index.js';
23
23
  import { listChapters } from './slices/list-chapters/index.js';
24
24
  import { showChapter } from './slices/show-chapter/index.js';
25
- import { addScenario } from './slices/add-scenario/index.js';
25
+ import { addScenario, parseScenarioInput } from './slices/add-scenario/index.js';
26
26
  import { addField } from './slices/add-field/index.js';
27
27
  import { removeScenario } from './slices/remove-scenario/index.js';
28
28
  import { removeField } from './slices/remove-field/index.js';
@@ -45,6 +45,7 @@ import { createAutomationSlice } from './slices/create-automation-slice/index.js
45
45
  import { createStateViewSlice } from './slices/create-state-view-slice/index.js';
46
46
  import { createFlow } from './slices/create-flow/index.js';
47
47
  import { codegenSlice } from './slices/codegen-slice/index.js';
48
+ import { codegenEvents } from './slices/codegen-chapter-events/index.js';
48
49
  import { diff } from './slices/diff/index.js';
49
50
  import { merge } from './slices/merge/index.js';
50
51
  import { gitSetup, gitStatus } from './slices/git/index.js';
@@ -69,6 +70,17 @@ function getNamedArg(argList, ...names) {
69
70
  function hasHelpFlag(argList) {
70
71
  return argList.includes('-h') || argList.includes('--help');
71
72
  }
73
+ function toCompoundFields(fields) {
74
+ return fields.map(field => ({
75
+ name: field.name,
76
+ fieldType: field.type,
77
+ isList: field.isList,
78
+ isOptional: field.isOptional,
79
+ isGenerated: field.isGenerated,
80
+ isUserInput: field.isUserInput,
81
+ subfields: field.subfields ? toCompoundFields(field.subfields) : undefined,
82
+ }));
83
+ }
72
84
  function printHelp() {
73
85
  console.log(`
74
86
  eventmodeler - CLI tool for interacting with Event Model files
@@ -145,6 +157,8 @@ COMMANDS:
145
157
  Create a flow between elements (Event→ReadModel, ReadModel→Screen/Processor)
146
158
 
147
159
  codegen slice <name> Generate code-ready JSON for a slice (includes dependencies, mappings, scenarios)
160
+ codegen events [--chapter <name>]
161
+ Generate deduplicated events for a chapter or the whole model
148
162
 
149
163
  summary Show model summary statistics
150
164
 
@@ -1743,91 +1757,49 @@ EXAMPLES:
1743
1757
  process.exit(1);
1744
1758
  }
1745
1759
  if (isCloudMode) {
1746
- // Parse scenario data
1747
- let scenario;
1760
+ let parsedScenario;
1748
1761
  try {
1749
- if (jsonArg) {
1750
- const parsed = JSON.parse(inputData);
1751
- scenario = {
1752
- name: parsed.name,
1753
- description: parsed.description,
1754
- given: parsed.given,
1755
- when: parsed.when,
1756
- then: parsed.then,
1757
- };
1758
- }
1759
- else {
1760
- // XML parsing - extract scenario attributes
1761
- const nameMatch = inputData.match(/name="([^"]*)"/);
1762
- const descMatch = inputData.match(/<description>([^<]*)<\/description>/);
1763
- // Parse given events
1764
- const givenMatch = inputData.match(/<given[^>]*type="events"[^>]*>([\s\S]*?)<\/given>/);
1765
- let given;
1766
- if (givenMatch) {
1767
- const eventMatches = givenMatch[1].match(/<event>([^<]*)<\/event>/g);
1768
- if (eventMatches) {
1769
- given = {
1770
- type: 'events',
1771
- events: eventMatches.map(m => m.replace(/<\/?event>/g, '')),
1772
- };
1773
- }
1774
- }
1775
- // Parse when
1776
- const whenCommandMatch = inputData.match(/<when[^>]*type="command"[^>]*>[\s\S]*?<command>([^<]*)<\/command>[\s\S]*?<\/when>/);
1777
- const whenEventsMatch = inputData.match(/<when[^>]*type="events"[^>]*>([\s\S]*?)<\/when>/);
1778
- let when;
1779
- if (whenCommandMatch) {
1780
- when = { type: 'command', command: whenCommandMatch[1] };
1781
- }
1782
- else if (whenEventsMatch) {
1783
- const eventMatches = whenEventsMatch[1].match(/<event>([^<]*)<\/event>/g);
1784
- if (eventMatches) {
1785
- when = {
1786
- type: 'events',
1787
- events: eventMatches.map(m => m.replace(/<\/?event>/g, '')),
1788
- };
1789
- }
1790
- }
1791
- // Parse then
1792
- const thenEventsMatch = inputData.match(/<then[^>]*type="events"[^>]*>([\s\S]*?)<\/then>/);
1793
- const thenErrorMatch = inputData.match(/<then[^>]*type="error"[^>]*>([\s\S]*?)<\/then>/);
1794
- const thenCommandMatch = inputData.match(/<then[^>]*type="command"[^>]*>[\s\S]*?<command>([^<]*)<\/command>[\s\S]*?<\/then>/);
1795
- let then;
1796
- if (thenEventsMatch) {
1797
- const eventMatches = thenEventsMatch[1].match(/<event>([^<]*)<\/event>/g);
1798
- then = {
1799
- type: 'events',
1800
- events: eventMatches ? eventMatches.map(m => m.replace(/<\/?event>/g, '')) : [],
1801
- };
1802
- }
1803
- else if (thenErrorMatch) {
1804
- const msgMatch = thenErrorMatch[1].match(/<message>([^<]*)<\/message>/);
1805
- const typeMatch = thenErrorMatch[1].match(/<errorType>([^<]*)<\/errorType>/);
1806
- then = {
1807
- type: 'error',
1808
- errorMessage: msgMatch?.[1],
1809
- errorType: typeMatch?.[1],
1810
- };
1811
- }
1812
- else if (thenCommandMatch) {
1813
- then = { type: 'command', command: thenCommandMatch[1] };
1814
- }
1815
- else {
1816
- throw new Error('Could not parse <then> element');
1817
- }
1818
- scenario = {
1819
- name: nameMatch?.[1] ?? '',
1820
- description: descMatch?.[1],
1821
- given,
1822
- when,
1823
- then,
1824
- };
1825
- }
1762
+ parsedScenario = parseScenarioInput(inputData);
1826
1763
  }
1827
1764
  catch (err) {
1828
1765
  console.error(`Error parsing scenario data: ${err.message}`);
1829
1766
  process.exit(1);
1830
1767
  }
1768
+ const scenario = {
1769
+ name: parsedScenario.name,
1770
+ description: parsedScenario.description,
1771
+ given: parsedScenario.given,
1772
+ when: parsedScenario.when
1773
+ ? {
1774
+ command: parsedScenario.when.command
1775
+ ? {
1776
+ command: parsedScenario.when.command,
1777
+ fieldValues: parsedScenario.when.commandFieldValues,
1778
+ }
1779
+ : undefined,
1780
+ events: parsedScenario.when.events,
1781
+ }
1782
+ : undefined,
1783
+ then: {
1784
+ type: parsedScenario.then.type,
1785
+ events: parsedScenario.then.events,
1786
+ errorMessage: parsedScenario.then.errorMessage,
1787
+ errorType: parsedScenario.then.errorType,
1788
+ command: parsedScenario.then.command
1789
+ ? {
1790
+ command: parsedScenario.then.command,
1791
+ fieldValues: parsedScenario.then.commandFieldValues,
1792
+ }
1793
+ : undefined,
1794
+ readModelAssertion: parsedScenario.then.readModel
1795
+ ? {
1796
+ readModel: parsedScenario.then.readModel,
1797
+ givenEvents: parsedScenario.then.givenEvents,
1798
+ expectedFieldValues: parsedScenario.then.expected,
1799
+ }
1800
+ : undefined,
1801
+ },
1802
+ };
1831
1803
  await cloudSlices.createScenario(modelId, sliceArg, scenario);
1832
1804
  console.log(`Created scenario "${scenario.name}" in slice "${sliceArg}"`);
1833
1805
  }
@@ -2537,15 +2509,13 @@ EXAMPLES:
2537
2509
  // Parse XML input to extract structured data
2538
2510
  const parser = await import('./lib/slice-utils.js');
2539
2511
  const parsed = parser.parseStateChangeSliceXml(xmlArg);
2540
- // Convert FieldInput to CompoundFieldInput (rename 'type' to 'fieldType')
2541
- const convertFields = (fields) => fields.map(f => ({ ...f, fieldType: f.type, type: undefined }));
2542
2512
  const cloudInput = {
2543
2513
  sliceName: parsed.sliceName,
2544
2514
  after: parsed.after,
2545
2515
  before: parsed.before,
2546
- screen: { name: parsed.screen.name, fields: convertFields(parsed.screen.fields) },
2547
- command: { name: parsed.command.name, fields: convertFields(parsed.command.fields) },
2548
- event: { name: parsed.event.name, fields: convertFields(parsed.event.fields) },
2516
+ screen: { name: parsed.screen.name, fields: toCompoundFields(parsed.screen.fields) },
2517
+ command: { name: parsed.command.name, fields: toCompoundFields(parsed.command.fields) },
2518
+ event: { name: parsed.event.name, fields: toCompoundFields(parsed.event.fields) },
2549
2519
  };
2550
2520
  const result = await cloudSlices.createStateChangeSlice(modelId, cloudInput);
2551
2521
  console.log(`Created state-change slice "${parsed.sliceName}"`);
@@ -2639,10 +2609,30 @@ EXAMPLES:
2639
2609
  process.exit(1);
2640
2610
  }
2641
2611
  if (isCloudMode) {
2642
- console.error('Error: create automation-slice is not yet supported in cloud mode.');
2643
- process.exit(1);
2612
+ // Parse XML input to extract structured data
2613
+ const parser = await import('./lib/slice-utils.js');
2614
+ const parsed = parser.parseAutomationSliceXml(xmlArg);
2615
+ const cloudInput = {
2616
+ sliceName: parsed.sliceName,
2617
+ after: parsed.after,
2618
+ before: parsed.before,
2619
+ readModel: { name: parsed.readModel.name, fields: toCompoundFields(parsed.readModel.fields) },
2620
+ processor: { name: parsed.processor.name },
2621
+ command: { name: parsed.command.name, fields: toCompoundFields(parsed.command.fields) },
2622
+ event: { name: parsed.event.name, fields: toCompoundFields(parsed.event.fields) },
2623
+ };
2624
+ const result = await cloudSlices.createAutomationSlice(modelId, cloudInput);
2625
+ console.log(`Created automation slice "${parsed.sliceName}"`);
2626
+ console.log(` ReadModel: ${parsed.readModel.name} (${parsed.readModel.fields.length} fields)`);
2627
+ console.log(` Processor: ${parsed.processor.name}`);
2628
+ console.log(` Command: ${parsed.command.name} (${parsed.command.fields.length} fields)`);
2629
+ console.log(` Event: ${parsed.event.name} (${parsed.event.fields.length} fields)`);
2630
+ console.log(` ReadModel -> Command mappings: ${result.readModelToCommandMappings}`);
2631
+ console.log(` Command -> Event mappings: ${result.commandToEventMappings}`);
2632
+ }
2633
+ else {
2634
+ createAutomationSlice(model, filePath, xmlArg);
2644
2635
  }
2645
- createAutomationSlice(model, filePath, xmlArg);
2646
2636
  break;
2647
2637
  }
2648
2638
  case 'state-view-slice': {
@@ -2713,13 +2703,11 @@ EXAMPLES:
2713
2703
  // Parse XML input to extract structured data
2714
2704
  const parser = await import('./lib/slice-utils.js');
2715
2705
  const parsed = parser.parseStateViewSliceXml(xmlArg);
2716
- // Convert FieldInput to CompoundFieldInput (rename 'type' to 'fieldType')
2717
- const convertFields = (fields) => fields.map(f => ({ ...f, fieldType: f.type, type: undefined }));
2718
2706
  const cloudInput = {
2719
2707
  sliceName: parsed.sliceName,
2720
2708
  after: parsed.after,
2721
2709
  before: parsed.before,
2722
- readModel: { name: parsed.readModel.name, fields: convertFields(parsed.readModel.fields) },
2710
+ readModel: { name: parsed.readModel.name, fields: toCompoundFields(parsed.readModel.fields) },
2723
2711
  };
2724
2712
  const result = await cloudSlices.createStateViewSlice(modelId, cloudInput);
2725
2713
  console.log(`Created state-view slice "${parsed.sliceName}"`);
@@ -2787,9 +2775,10 @@ USAGE:
2787
2775
  eventmodeler codegen <type> [options]
2788
2776
 
2789
2777
  TYPES:
2790
- slice Generate comprehensive JSON for a slice
2778
+ slice Generate comprehensive JSON for a slice
2779
+ events Generate deduplicated events for a chapter or entire model
2791
2780
 
2792
- Run "eventmodeler codegen slice --help" for detailed help.
2781
+ Run "eventmodeler codegen <type> --help" for detailed help.
2793
2782
  `);
2794
2783
  process.exit(0);
2795
2784
  }
@@ -2824,7 +2813,7 @@ EXAMPLES:
2824
2813
  `);
2825
2814
  process.exit(0);
2826
2815
  }
2827
- const sliceName = filteredArgs[2];
2816
+ const sliceName = target;
2828
2817
  if (!sliceName) {
2829
2818
  console.error('Error: slice name is required');
2830
2819
  console.error('Usage: eventmodeler codegen slice <name>');
@@ -2840,9 +2829,47 @@ EXAMPLES:
2840
2829
  }
2841
2830
  break;
2842
2831
  }
2832
+ case 'events': {
2833
+ if (helpRequested) {
2834
+ console.log(`
2835
+ eventmodeler codegen events - Generate deduplicated events for scaffolding
2836
+
2837
+ USAGE:
2838
+ eventmodeler codegen events [--chapter <chapter-name>]
2839
+ eventmodeler codegen events <chapter-name>
2840
+
2841
+ ARGUMENTS:
2842
+ <chapter-name> Optional chapter name (if omitted, all model events are returned)
2843
+
2844
+ OUTPUT:
2845
+ JSON payload including:
2846
+ - chapter hierarchy metadata (when chapter-scoped)
2847
+ - all relevant slices
2848
+ - deduplicated events across those slices
2849
+ - sourceSlices per event (where each event appears)
2850
+
2851
+ This is designed for event scaffolding flows (e.g. events.kt generation).
2852
+
2853
+ EXAMPLES:
2854
+ eventmodeler codegen events --chapter "Register Products"
2855
+ eventmodeler codegen events "Register Products"
2856
+ eventmodeler codegen events > model-events.json
2857
+ `);
2858
+ process.exit(0);
2859
+ }
2860
+ const chapterName = chapterArg ?? target;
2861
+ if (isCloudMode) {
2862
+ const output = await cloudSlices.codegenEvents(modelId, chapterName);
2863
+ console.log(JSON.stringify(output, null, 2));
2864
+ }
2865
+ else {
2866
+ codegenEvents(model, chapterName);
2867
+ }
2868
+ break;
2869
+ }
2843
2870
  default:
2844
2871
  console.error(`Unknown codegen target: ${subcommand}`);
2845
- console.error('Valid targets: slice');
2872
+ console.error('Valid targets: slice, events');
2846
2873
  console.error('Run "eventmodeler codegen --help" for more information.');
2847
2874
  process.exit(1);
2848
2875
  }
package/dist/lib/auth.js CHANGED
@@ -130,7 +130,6 @@ export async function startAuthFlow() {
130
130
  });
131
131
  const authorizationUrl = `${keycloakUrl}/protocol/openid-connect/auth?${params}`;
132
132
  console.log('\nOpening browser for authentication...');
133
- console.log(`\nIf the browser doesn't open, visit:\n${authorizationUrl}\n`);
134
133
  // Open the browser
135
134
  openBrowser(authorizationUrl);
136
135
  });
@@ -1,5 +1,4 @@
1
1
  import type { EventModel } from '../types.js';
2
- import { type CloudClient } from './cloud-client.js';
3
2
  import { type ProjectConfig } from './project-config.js';
4
3
  /**
5
4
  * Backend abstraction for CLI operations.
@@ -21,10 +20,6 @@ export interface CliBackend {
21
20
  * Create a local file-based backend.
22
21
  */
23
22
  export declare function createLocalBackend(filePath: string): CliBackend;
24
- /**
25
- * Create a cloud backend using the Axon backend.
26
- */
27
- export declare function createCloudBackendFromClient(client: CloudClient, modelId: string, modelName: string): CliBackend;
28
23
  export interface ResolveBackendOptions {
29
24
  /** Explicit file path (overrides auto-detection) */
30
25
  filePath?: string;
@@ -1,8 +1,6 @@
1
1
  import { loadModel, appendEvent as appendEventToFile } from './file-loader.js';
2
- import { createCloudClient } from './cloud-client.js';
3
2
  import { loadProjectConfig } from './project-config.js';
4
3
  import { findEventModelFile } from './file-loader.js';
5
- import { isAuthenticated } from './config.js';
6
4
  /**
7
5
  * Create a local file-based backend.
8
6
  */
@@ -26,32 +24,6 @@ export function createLocalBackend(filePath) {
26
24
  },
27
25
  };
28
26
  }
29
- /**
30
- * Create a cloud backend using the Axon backend.
31
- */
32
- export function createCloudBackendFromClient(client, modelId, modelName) {
33
- return {
34
- async getModel() {
35
- const model = await client.getModel(modelId);
36
- if (!model) {
37
- throw new Error(`Event model not found: ${modelId}`);
38
- }
39
- return model;
40
- },
41
- async dispatch(command) {
42
- const result = await client.dispatch(modelId, command);
43
- if (!result.success) {
44
- throw new Error('Command dispatch failed');
45
- }
46
- },
47
- isCloud() {
48
- return true;
49
- },
50
- getModelIdentifier() {
51
- return `${modelName} (${modelId})`;
52
- },
53
- };
54
- }
55
27
  /**
56
28
  * Resolve which backend to use based on:
57
29
  * 1. Explicit -f <file> flag
@@ -70,11 +42,9 @@ export async function resolveBackend(options = {}) {
70
42
  return createLocalBackend(projectConfig.file);
71
43
  }
72
44
  if (projectConfig.type === 'cloud') {
73
- if (!isAuthenticated()) {
74
- throw new Error('Not authenticated. Run "eventmodeler login" first.');
75
- }
76
- const client = await createCloudClient();
77
- return createCloudBackendFromClient(client, projectConfig.modelId, projectConfig.modelName);
45
+ // Cloud mode uses cloud slices directly, not the CliBackend abstraction
46
+ throw new Error('Cloud mode detected but CliBackend abstraction is not supported for cloud.\n' +
47
+ 'Use cloud slices directly for cloud operations.');
78
48
  }
79
49
  }
80
50
  // 3. Legacy: look for .eventmodel file in current directory
@@ -1,4 +1,4 @@
1
- import type { EventModel, RawEvent } from '../types.js';
1
+ import type { RawEvent } from '../types.js';
2
2
  export interface ImportResult {
3
3
  success: boolean;
4
4
  modelId: string;
@@ -6,7 +6,6 @@ export interface ImportResult {
6
6
  }
7
7
  export interface CloudClient {
8
8
  listModels(): Promise<CloudModelInfo[]>;
9
- getModel(modelId: string): Promise<EventModel | null>;
10
9
  dispatch(modelId: string, command: unknown): Promise<{
11
10
  success: boolean;
12
11
  events: unknown[];