eventmodeler 0.4.7 → 0.6.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 (204) hide show
  1. package/dist/api/client-config.js +10 -0
  2. package/dist/api/generated/client/client.gen.js +235 -0
  3. package/dist/api/generated/client/index.js +6 -0
  4. package/dist/api/generated/client/types.gen.js +2 -0
  5. package/dist/api/generated/client/utils.gen.js +228 -0
  6. package/dist/api/generated/client.gen.js +4 -0
  7. package/dist/api/generated/core/auth.gen.js +14 -0
  8. package/dist/api/generated/core/bodySerializer.gen.js +57 -0
  9. package/dist/api/generated/core/params.gen.js +100 -0
  10. package/dist/api/generated/core/pathSerializer.gen.js +106 -0
  11. package/dist/api/generated/core/queryKeySerializer.gen.js +92 -0
  12. package/dist/api/generated/core/serverSentEvents.gen.js +133 -0
  13. package/dist/api/generated/core/types.gen.js +2 -0
  14. package/dist/api/generated/core/utils.gen.js +87 -0
  15. package/dist/api/generated/index.js +2 -0
  16. package/dist/api/generated/sdk.gen.js +4222 -0
  17. package/dist/api/generated/types.gen.js +2 -0
  18. package/dist/api/generated/zod.gen.js +7217 -0
  19. package/dist/commands/add.js +315 -0
  20. package/dist/commands/auth.js +14 -0
  21. package/dist/commands/create.js +192 -0
  22. package/dist/commands/design.js +108 -0
  23. package/dist/commands/guide.js +15 -0
  24. package/dist/commands/init.js +21 -0
  25. package/dist/commands/list-schemas.js +177 -0
  26. package/dist/commands/list.js +39 -0
  27. package/dist/commands/loop.js +101 -0
  28. package/dist/commands/map.js +40 -0
  29. package/dist/commands/mark.js +27 -0
  30. package/dist/commands/move.js +35 -0
  31. package/dist/commands/remove.js +170 -0
  32. package/dist/commands/rename.js +53 -0
  33. package/dist/commands/resize.js +30 -0
  34. package/dist/commands/search.js +14 -0
  35. package/dist/commands/set.js +199 -0
  36. package/dist/commands/show-schemas.js +259 -0
  37. package/dist/commands/show.js +56 -0
  38. package/dist/commands/summary.js +13 -0
  39. package/dist/commands/update.js +240 -0
  40. package/dist/index.js +46 -2379
  41. package/dist/lib/auth.js +1 -1
  42. package/dist/lib/config.js +0 -15
  43. package/dist/lib/excalidraw-schema.js +66 -0
  44. package/dist/lib/globals.js +8 -0
  45. package/dist/lib/model.js +11 -0
  46. package/dist/lib/project-config.js +20 -0
  47. package/dist/lib/resolve.js +59 -0
  48. package/dist/lib/scenario.js +15 -0
  49. package/dist/slices/add-scenario/index.js +2 -206
  50. package/dist/slices/guide/guides/codegen.js +1 -1
  51. package/dist/slices/guide/guides/connect-slices.js +12 -37
  52. package/dist/slices/guide/guides/create-slices.js +110 -140
  53. package/dist/slices/guide/guides/explore.js +37 -26
  54. package/dist/slices/guide/guides/information-flow.js +70 -82
  55. package/dist/slices/guide/guides/scenarios.js +82 -137
  56. package/dist/slices/guide/index.js +6 -6
  57. package/dist/slices/help/index.js +96 -0
  58. package/dist/slices/help/topics/build-codegen.js +109 -0
  59. package/dist/slices/help/topics/build-slice.js +147 -0
  60. package/dist/slices/help/topics/check-completeness.js +57 -0
  61. package/dist/slices/help/topics/connect-slices.js +99 -0
  62. package/dist/slices/help/topics/explore-model.js +112 -0
  63. package/dist/slices/help/topics/json-reference.js +188 -0
  64. package/dist/slices/help/topics/linked-copies.js +89 -0
  65. package/dist/slices/help/topics/manipulate-canvas.js +150 -0
  66. package/dist/slices/help/topics/write-scenarios.js +162 -0
  67. package/dist/slices/init/index.js +10 -4
  68. package/dist/slices/init/loop.js +60 -0
  69. package/dist/slices/login/index.js +2 -2
  70. package/dist/slices/logout/index.js +2 -2
  71. package/dist/slices/whoami/index.js +11 -36
  72. package/package.json +8 -3
  73. package/dist/api/index.d.ts +0 -285
  74. package/dist/api/index.js +0 -323
  75. package/dist/cloud/slices/index.d.ts +0 -276
  76. package/dist/cloud/slices/index.js +0 -406
  77. package/dist/eventmodeler.js +0 -5646
  78. package/dist/formatters.d.ts +0 -17
  79. package/dist/formatters.js +0 -482
  80. package/dist/index.d.ts +0 -2
  81. package/dist/lib/auth.d.ts +0 -24
  82. package/dist/lib/backend.d.ts +0 -43
  83. package/dist/lib/backend.js +0 -73
  84. package/dist/lib/chapter-utils.d.ts +0 -13
  85. package/dist/lib/chapter-utils.js +0 -71
  86. package/dist/lib/cloud-client.d.ts +0 -69
  87. package/dist/lib/cloud-client.js +0 -364
  88. package/dist/lib/config.d.ts +0 -30
  89. package/dist/lib/diff/merge-rules.d.ts +0 -45
  90. package/dist/lib/diff/merge-rules.js +0 -210
  91. package/dist/lib/diff/model-differ.d.ts +0 -8
  92. package/dist/lib/diff/model-differ.js +0 -568
  93. package/dist/lib/diff/three-way-merge.d.ts +0 -7
  94. package/dist/lib/diff/three-way-merge.js +0 -390
  95. package/dist/lib/diff/types.d.ts +0 -75
  96. package/dist/lib/diff/types.js +0 -1
  97. package/dist/lib/element-lookup.d.ts +0 -58
  98. package/dist/lib/element-lookup.js +0 -126
  99. package/dist/lib/file-loader.d.ts +0 -8
  100. package/dist/lib/file-loader.js +0 -108
  101. package/dist/lib/flow-utils.d.ts +0 -53
  102. package/dist/lib/flow-utils.js +0 -348
  103. package/dist/lib/format.d.ts +0 -10
  104. package/dist/lib/format.js +0 -23
  105. package/dist/lib/project-config.d.ts +0 -27
  106. package/dist/lib/slice-utils.d.ts +0 -59
  107. package/dist/lib/slice-utils.js +0 -140
  108. package/dist/local/slices/index.d.ts +0 -11
  109. package/dist/local/slices/index.js +0 -13
  110. package/dist/projection.d.ts +0 -3
  111. package/dist/projection.js +0 -828
  112. package/dist/slices/add-field/index.d.ts +0 -8
  113. package/dist/slices/add-field/index.js +0 -211
  114. package/dist/slices/add-scenario/index.d.ts +0 -27
  115. package/dist/slices/codegen-chapter-events/index.d.ts +0 -2
  116. package/dist/slices/codegen-chapter-events/index.js +0 -145
  117. package/dist/slices/codegen-slice/index.d.ts +0 -2
  118. package/dist/slices/codegen-slice/index.js +0 -448
  119. package/dist/slices/create-automation-slice/index.d.ts +0 -2
  120. package/dist/slices/create-automation-slice/index.js +0 -304
  121. package/dist/slices/create-flow/index.d.ts +0 -2
  122. package/dist/slices/create-flow/index.js +0 -183
  123. package/dist/slices/create-state-change-slice/index.d.ts +0 -2
  124. package/dist/slices/create-state-change-slice/index.js +0 -263
  125. package/dist/slices/create-state-view-slice/index.d.ts +0 -2
  126. package/dist/slices/create-state-view-slice/index.js +0 -128
  127. package/dist/slices/diff/index.d.ts +0 -11
  128. package/dist/slices/diff/index.js +0 -293
  129. package/dist/slices/export-eventmodel-to-json/index.d.ts +0 -2
  130. package/dist/slices/export-eventmodel-to-json/index.js +0 -355
  131. package/dist/slices/git/index.d.ts +0 -2
  132. package/dist/slices/git/index.js +0 -125
  133. package/dist/slices/guide/guides/codegen.d.ts +0 -5
  134. package/dist/slices/guide/guides/connect-slices.d.ts +0 -5
  135. package/dist/slices/guide/guides/create-slices.d.ts +0 -5
  136. package/dist/slices/guide/guides/explore.d.ts +0 -5
  137. package/dist/slices/guide/guides/information-flow.d.ts +0 -5
  138. package/dist/slices/guide/guides/scenarios.d.ts +0 -5
  139. package/dist/slices/guide/index.d.ts +0 -1
  140. package/dist/slices/import/index.d.ts +0 -8
  141. package/dist/slices/import/index.js +0 -63
  142. package/dist/slices/init/index.d.ts +0 -5
  143. package/dist/slices/list-chapters/index.d.ts +0 -3
  144. package/dist/slices/list-chapters/index.js +0 -21
  145. package/dist/slices/list-commands/index.d.ts +0 -3
  146. package/dist/slices/list-commands/index.js +0 -20
  147. package/dist/slices/list-events/index.d.ts +0 -3
  148. package/dist/slices/list-events/index.js +0 -98
  149. package/dist/slices/list-processors/index.d.ts +0 -3
  150. package/dist/slices/list-processors/index.js +0 -20
  151. package/dist/slices/list-readmodels/index.d.ts +0 -3
  152. package/dist/slices/list-readmodels/index.js +0 -21
  153. package/dist/slices/list-scenarios/index.d.ts +0 -3
  154. package/dist/slices/list-scenarios/index.js +0 -35
  155. package/dist/slices/list-screens/index.d.ts +0 -3
  156. package/dist/slices/list-screens/index.js +0 -47
  157. package/dist/slices/list-slices/index.d.ts +0 -3
  158. package/dist/slices/list-slices/index.js +0 -35
  159. package/dist/slices/login/index.d.ts +0 -1
  160. package/dist/slices/logout/index.d.ts +0 -1
  161. package/dist/slices/map-fields/index.d.ts +0 -2
  162. package/dist/slices/map-fields/index.js +0 -269
  163. package/dist/slices/mark-slice-status/index.d.ts +0 -2
  164. package/dist/slices/mark-slice-status/index.js +0 -31
  165. package/dist/slices/merge/index.d.ts +0 -19
  166. package/dist/slices/merge/index.js +0 -147
  167. package/dist/slices/open-app/index.d.ts +0 -1
  168. package/dist/slices/remove-field/index.d.ts +0 -8
  169. package/dist/slices/remove-field/index.js +0 -167
  170. package/dist/slices/remove-scenario/index.d.ts +0 -2
  171. package/dist/slices/remove-scenario/index.js +0 -77
  172. package/dist/slices/search/index.d.ts +0 -3
  173. package/dist/slices/search/index.js +0 -302
  174. package/dist/slices/show-actor/index.d.ts +0 -4
  175. package/dist/slices/show-actor/index.js +0 -115
  176. package/dist/slices/show-aggregate/index.d.ts +0 -3
  177. package/dist/slices/show-aggregate/index.js +0 -108
  178. package/dist/slices/show-aggregate-completeness/index.d.ts +0 -4
  179. package/dist/slices/show-aggregate-completeness/index.js +0 -181
  180. package/dist/slices/show-chapter/index.d.ts +0 -3
  181. package/dist/slices/show-chapter/index.js +0 -195
  182. package/dist/slices/show-command/index.d.ts +0 -3
  183. package/dist/slices/show-command/index.js +0 -133
  184. package/dist/slices/show-completeness/index.d.ts +0 -4
  185. package/dist/slices/show-completeness/index.js +0 -731
  186. package/dist/slices/show-event/index.d.ts +0 -3
  187. package/dist/slices/show-event/index.js +0 -118
  188. package/dist/slices/show-model-summary/index.d.ts +0 -3
  189. package/dist/slices/show-model-summary/index.js +0 -31
  190. package/dist/slices/show-processor/index.d.ts +0 -3
  191. package/dist/slices/show-processor/index.js +0 -111
  192. package/dist/slices/show-readmodel/index.d.ts +0 -3
  193. package/dist/slices/show-readmodel/index.js +0 -158
  194. package/dist/slices/show-scenario/index.d.ts +0 -3
  195. package/dist/slices/show-scenario/index.js +0 -196
  196. package/dist/slices/show-screen/index.d.ts +0 -3
  197. package/dist/slices/show-screen/index.js +0 -139
  198. package/dist/slices/show-slice/index.d.ts +0 -3
  199. package/dist/slices/show-slice/index.js +0 -696
  200. package/dist/slices/update-field/index.d.ts +0 -15
  201. package/dist/slices/update-field/index.js +0 -208
  202. package/dist/slices/whoami/index.d.ts +0 -2
  203. package/dist/types.d.ts +0 -195
  204. package/dist/types.js +0 -1
package/dist/lib/auth.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as http from 'node:http';
2
2
  import * as crypto from 'node:crypto';
3
3
  import { exec } from 'node:child_process';
4
- import { saveAuthTokens, clearAuthTokens, getAuthTokens, getKeycloakUrl } from './config.js';
4
+ import { saveAuthTokens, clearAuthTokens, getAuthTokens, getKeycloakUrl } from './config';
5
5
  const KEYCLOAK_CLIENT_ID = 'eventmodeler-cli';
6
6
  const REDIRECT_URI = 'http://localhost:8787/callback';
7
7
  /**
@@ -78,18 +78,3 @@ export function setKeycloakUrl(url) {
78
78
  config.keycloakUrl = url;
79
79
  saveGlobalConfig(config);
80
80
  }
81
- export function getDefaultFormat() {
82
- // 1. Check environment variable
83
- const envFormat = process.env.EVENTMODELER_FORMAT;
84
- if (envFormat === 'json')
85
- return 'json';
86
- if (envFormat === 'xml')
87
- return 'xml';
88
- // 2. Check config file
89
- const config = loadGlobalConfig();
90
- if (config.format === 'json' || config.format === 'xml') {
91
- return config.format;
92
- }
93
- // 3. Default to XML (better for AI agents)
94
- return 'xml';
95
- }
@@ -0,0 +1,66 @@
1
+ import { z } from 'zod';
2
+ const FillStyle = z.enum(['solid', 'hachure', 'cross-hatch', 'zigzag']);
3
+ const StrokeStyle = z.enum(['solid', 'dashed', 'dotted']);
4
+ const TextAlign = z.enum(['left', 'center', 'right']);
5
+ const VerticalAlign = z.enum(['top', 'middle', 'bottom']);
6
+ const StyleFields = {
7
+ strokeColor: z.string().optional(),
8
+ backgroundColor: z.string().optional(),
9
+ fillStyle: FillStyle.optional(),
10
+ strokeStyle: StrokeStyle.optional(),
11
+ strokeWidth: z.number().nonnegative().optional(),
12
+ roughness: z.number().nonnegative().optional(),
13
+ opacity: z.number().min(0).max(100).optional(),
14
+ angle: z.number().optional(),
15
+ };
16
+ const BoxFields = {
17
+ id: z.string().min(1),
18
+ x: z.number(),
19
+ y: z.number(),
20
+ width: z.number(),
21
+ height: z.number(),
22
+ ...StyleFields,
23
+ };
24
+ const ShapeElement = z.object({
25
+ type: z.enum(['rectangle', 'ellipse', 'diamond', 'frame', 'magicframe', 'iframe', 'embeddable', 'image', 'freedraw']),
26
+ ...BoxFields,
27
+ }).passthrough();
28
+ const LineElement = z.object({
29
+ type: z.enum(['line', 'arrow']),
30
+ ...BoxFields,
31
+ points: z.array(z.tuple([z.number(), z.number()])).optional(),
32
+ }).passthrough();
33
+ const TextElement = z.object({
34
+ type: z.literal('text'),
35
+ id: z.string().min(1),
36
+ x: z.number(),
37
+ y: z.number(),
38
+ width: z.number().optional(),
39
+ height: z.number().optional(),
40
+ text: z.string(),
41
+ fontSize: z.number().positive(),
42
+ fontFamily: z.number().int().positive().optional(),
43
+ textAlign: TextAlign.optional(),
44
+ verticalAlign: VerticalAlign.optional(),
45
+ ...StyleFields,
46
+ }).passthrough();
47
+ const Element = z.discriminatedUnion('type', [ShapeElement, LineElement, TextElement]);
48
+ export const ExcalidrawDesign = z.array(Element).superRefine((elements, ctx) => {
49
+ const seen = new Set();
50
+ elements.forEach((el, i) => {
51
+ if (seen.has(el.id)) {
52
+ ctx.addIssue({
53
+ code: 'custom',
54
+ path: [i, 'id'],
55
+ message: `duplicate id "${el.id}"`,
56
+ });
57
+ }
58
+ seen.add(el.id);
59
+ });
60
+ });
61
+ export function formatZodError(error) {
62
+ return error.issues.map(issue => {
63
+ const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';
64
+ return `${path}: ${issue.message}`;
65
+ });
66
+ }
@@ -0,0 +1,8 @@
1
+ let _program;
2
+ export function setProgram(program) {
3
+ _program = program;
4
+ }
5
+ /** Get the global --id option value. */
6
+ export function getGlobalId() {
7
+ return _program?.opts()?.id;
8
+ }
@@ -0,0 +1,11 @@
1
+ import { loadProjectConfig } from './project-config';
2
+ /** Load modelId from .eventmodeler.json or exit with error. */
3
+ export function requireModelId() {
4
+ const config = loadProjectConfig();
5
+ if (!config || config.type !== 'cloud') {
6
+ console.error('Error: No project configured.');
7
+ console.error('Run "eventmodeler init" to connect this directory to a model.');
8
+ process.exit(1);
9
+ }
10
+ return config.modelId;
11
+ }
@@ -81,3 +81,23 @@ export function getProjectRoot(startDir = process.cwd()) {
81
81
  export function isInProject(startDir) {
82
82
  return findProjectConfigPath(startDir) !== null;
83
83
  }
84
+ /**
85
+ * Load the `loop` block from .eventmodeler.json, if present.
86
+ */
87
+ export function loadLoopConfig(startDir) {
88
+ const configPath = findProjectConfigPath(startDir);
89
+ if (!configPath)
90
+ return null;
91
+ try {
92
+ const content = fs.readFileSync(configPath, 'utf-8');
93
+ const config = JSON.parse(content);
94
+ const loop = config.loop;
95
+ if (loop && typeof loop.command === 'string' && typeof loop.interval === 'number') {
96
+ return { command: loop.command, interval: loop.interval };
97
+ }
98
+ return null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
@@ -0,0 +1,59 @@
1
+ import * as sdk from '../api/generated/sdk.gen';
2
+ const FIELD_BEARING_TYPES = ['command', 'event', 'readmodel', 'screen', 'processor', 'external-event'];
3
+ /** Map element type to the body key name used by the SDK. */
4
+ export function elementIdKey(type) {
5
+ switch (type) {
6
+ case 'command': return 'commandStickyId';
7
+ case 'event': return 'eventStickyId';
8
+ case 'readmodel': return 'readModelStickyId';
9
+ case 'external-event': return 'externalEventId';
10
+ case 'swimlane': return 'swimLaneId';
11
+ default: return `${type}Id`;
12
+ }
13
+ }
14
+ /** Unwrap SDK response — throw on error, return data on success. */
15
+ export function unwrap(result) {
16
+ if (result.error) {
17
+ const err = result.error;
18
+ const message = err?.error ?? err?.message ?? err?.detail ?? JSON.stringify(err);
19
+ throw new Error(String(message));
20
+ }
21
+ if (result.data === undefined) {
22
+ throw new Error(`Empty response (HTTP ${result.response.status})`);
23
+ }
24
+ return result.data;
25
+ }
26
+ /**
27
+ * Resolve an element name to its UUID via the CLI resolver.
28
+ * If idOverride is provided, skip resolution and return it directly.
29
+ */
30
+ export async function resolve(modelId, elementType, name, idOverride) {
31
+ if (idOverride)
32
+ return idOverride;
33
+ const result = unwrap(await sdk.resolveElement({
34
+ query: { modelId, elementType, name }
35
+ }));
36
+ const elementId = result.elementId;
37
+ if (!elementId) {
38
+ throw new Error(`${elementType} "${name}" not found`);
39
+ }
40
+ return String(elementId);
41
+ }
42
+ /** Resolve an element name to its UUID, trying all field-bearing types. */
43
+ export async function resolveAnyElement(modelId, name, idOverride) {
44
+ if (idOverride) {
45
+ return { elementId: idOverride, elementType: 'unknown' };
46
+ }
47
+ for (const t of FIELD_BEARING_TYPES) {
48
+ try {
49
+ const id = await resolve(modelId, t, name);
50
+ return { elementId: id, elementType: t };
51
+ }
52
+ catch { /* try next */ }
53
+ }
54
+ throw new Error(`Element "${name}" not found`);
55
+ }
56
+ /** Pretty-print JSON to stdout. */
57
+ export function out(data) {
58
+ console.log(JSON.stringify(data, null, 2));
59
+ }
@@ -0,0 +1,15 @@
1
+ import * as sdk from '../api/generated/sdk.gen';
2
+ import { unwrap } from './resolve';
3
+ /** Resolve a scenario name to its UUID. If idOverride is provided, skip resolution. */
4
+ export async function resolveScenarioId(modelId, name, idOverride) {
5
+ if (idOverride)
6
+ return idOverride;
7
+ const result = unwrap(await sdk.resolveScenario({
8
+ query: { modelId, name }
9
+ }));
10
+ const scenarioId = result.scenarioId;
11
+ if (!scenarioId) {
12
+ throw new Error(`Scenario "${name}" not found`);
13
+ }
14
+ return String(scenarioId);
15
+ }
@@ -1,101 +1,9 @@
1
- import { XMLParser } from 'fast-xml-parser';
2
- const xmlParser = new XMLParser({
3
- ignoreAttributes: false,
4
- attributeNamePrefix: '@_',
5
- allowBooleanAttributes: false,
6
- parseAttributeValue: false,
7
- isArray: (name) => name === 'event' || name === 'field',
8
- trimValues: true,
9
- });
10
- function asArray(value) {
11
- if (value === undefined || value === null)
12
- return [];
13
- return Array.isArray(value) ? value : [value];
14
- }
15
1
  function asRecord(value, context) {
16
2
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
17
3
  throw new Error(`${context} must be an object`);
18
4
  }
19
5
  return value;
20
6
  }
21
- function parseFieldValue(value) {
22
- const trimmed = value.trim();
23
- if (trimmed === '')
24
- return '';
25
- if (trimmed === 'true')
26
- return true;
27
- if (trimmed === 'false')
28
- return false;
29
- if (trimmed === 'null')
30
- return null;
31
- if (/^-?\d+$/.test(trimmed))
32
- return parseInt(trimmed, 10);
33
- if (/^-?\d+\.\d+$/.test(trimmed))
34
- return parseFloat(trimmed);
35
- try {
36
- return JSON.parse(trimmed);
37
- }
38
- catch {
39
- return trimmed;
40
- }
41
- }
42
- function parseFieldValuesFromNodes(nodes) {
43
- if (nodes.length === 0)
44
- return undefined;
45
- const result = {};
46
- for (const node of nodes) {
47
- const n = node;
48
- const name = n['@_name'];
49
- if (!name)
50
- continue;
51
- const childFields = asArray(n['field']);
52
- let value;
53
- if (childFields.length > 0) {
54
- value = parseFieldValuesFromNodes(childFields);
55
- }
56
- else {
57
- // Get text content (fast-xml-parser stores it as #text)
58
- const text = n['#text'];
59
- if (text === undefined || text === '') {
60
- value = null;
61
- }
62
- else {
63
- value = parseFieldValue(String(text));
64
- }
65
- }
66
- // Handle duplicate keys (multiple values → array)
67
- if (name in result) {
68
- const current = result[name];
69
- if (Array.isArray(current)) {
70
- current.push(value);
71
- }
72
- else {
73
- result[name] = [current, value];
74
- }
75
- }
76
- else {
77
- result[name] = value;
78
- }
79
- }
80
- return Object.keys(result).length > 0 ? result : undefined;
81
- }
82
- function parseEventInputsFromNodes(nodes) {
83
- const result = [];
84
- for (const node of nodes) {
85
- const n = node;
86
- const nameFromAttr = n['@_name'];
87
- const textContent = n['#text'];
88
- const eventName = nameFromAttr ?? textContent;
89
- if (!eventName)
90
- continue;
91
- const childFields = asArray(n['field']);
92
- result.push({
93
- event: eventName,
94
- fieldValues: parseFieldValuesFromNodes(childFields),
95
- });
96
- }
97
- return result;
98
- }
99
7
  function parseEventInput(value, context) {
100
8
  if (typeof value === 'string') {
101
9
  return { event: value };
@@ -142,8 +50,8 @@ function parseObjectValue(value, context) {
142
50
  }
143
51
  return value;
144
52
  }
145
- function normalizeScenarioInput(rawValue) {
146
- const raw = asRecord(rawValue, 'scenario');
53
+ export function parseScenarioInput(input) {
54
+ const raw = asRecord(JSON.parse(input.trim()), 'scenario');
147
55
  const name = raw.name;
148
56
  if (typeof name !== 'string' || name.trim() === '') {
149
57
  throw new Error('scenario.name is required');
@@ -193,115 +101,3 @@ function normalizeScenarioInput(rawValue) {
193
101
  then,
194
102
  };
195
103
  }
196
- function parseJsonInput(input) {
197
- return normalizeScenarioInput(JSON.parse(input));
198
- }
199
- function parseXmlInput(input) {
200
- const parsed = xmlParser.parse(input);
201
- const scenario = parsed['scenario'];
202
- if (!scenario) {
203
- throw new Error('Invalid XML: missing <scenario> tag');
204
- }
205
- const name = scenario['@_name'];
206
- if (!name) {
207
- throw new Error('Invalid XML: scenario must have a name attribute');
208
- }
209
- const description = scenario['@_description'];
210
- // Parse given events
211
- const givenNode = scenario['given'];
212
- const given = givenNode ? parseEventInputsFromNodes(asArray(givenNode['event'])) : [];
213
- // Parse when
214
- let when;
215
- const whenNode = scenario['when'];
216
- if (whenNode) {
217
- const commandNodes = asArray(whenNode['command']);
218
- const commandNode = commandNodes[0];
219
- const whenEvents = parseEventInputsFromNodes(asArray(whenNode['event']));
220
- if (commandNode || whenEvents.length > 0) {
221
- let commandName;
222
- let commandFieldValues;
223
- if (commandNode) {
224
- const nameFromAttr = commandNode['@_name'];
225
- const textContent = commandNode['#text'];
226
- commandName = nameFromAttr ?? textContent ?? undefined;
227
- commandFieldValues = parseFieldValuesFromNodes(asArray(commandNode['field']));
228
- }
229
- when = {
230
- command: commandName || undefined,
231
- commandFieldValues,
232
- events: whenEvents.length > 0 ? whenEvents : undefined,
233
- };
234
- }
235
- }
236
- // Parse then
237
- const thenNode = scenario['then'];
238
- if (!thenNode) {
239
- throw new Error('Invalid XML: missing <then> tag');
240
- }
241
- const thenType = thenNode['@_type'];
242
- if (thenType !== 'error' &&
243
- thenType !== 'events' &&
244
- thenType !== 'readModelAssertion' &&
245
- thenType !== 'command' &&
246
- thenType !== 'noCommand') {
247
- throw new Error('Invalid XML: <then> must have a type attribute');
248
- }
249
- const then = { type: thenType };
250
- if (thenType === 'error') {
251
- then.errorType = thenNode['@_errorType'];
252
- const errorTypeNode = thenNode['errorType'];
253
- if (!then.errorType && errorTypeNode) {
254
- then.errorType = typeof errorTypeNode === 'string' ? errorTypeNode : errorTypeNode['#text'];
255
- }
256
- const messageNode = thenNode['message'];
257
- if (messageNode) {
258
- then.errorMessage = typeof messageNode === 'string' ? messageNode : messageNode['#text'];
259
- }
260
- }
261
- else if (thenType === 'events') {
262
- then.events = parseEventInputsFromNodes(asArray(thenNode['event']));
263
- }
264
- else if (thenType === 'command') {
265
- const commandNodes = asArray(thenNode['command']);
266
- const commandNode = commandNodes[0];
267
- if (commandNode) {
268
- const nameFromAttr = commandNode['@_name'];
269
- const textContent = commandNode['#text'];
270
- then.command = nameFromAttr ?? textContent ?? undefined;
271
- then.commandFieldValues = parseFieldValuesFromNodes(asArray(commandNode['field']));
272
- }
273
- }
274
- else if (thenType === 'readModelAssertion') {
275
- const readModelNode = thenNode['read-model'];
276
- if (readModelNode) {
277
- const nameFromAttr = readModelNode['@_name'];
278
- const textContent = readModelNode['#text'];
279
- then.readModel = nameFromAttr ?? textContent ?? undefined;
280
- const expectedNode = readModelNode['expected'];
281
- const directFields = parseFieldValuesFromNodes(asArray(readModelNode['field']));
282
- const expectedFields = expectedNode ? parseFieldValuesFromNodes(asArray(expectedNode['field'])) : undefined;
283
- then.expected = {
284
- ...(directFields ?? {}),
285
- ...(expectedFields ?? {}),
286
- };
287
- const givenEventsNode = (readModelNode['given'] ?? readModelNode['given-events']);
288
- if (givenEventsNode) {
289
- then.givenEvents = parseEventInputsFromNodes(asArray(givenEventsNode['event']));
290
- }
291
- }
292
- }
293
- return {
294
- name,
295
- description,
296
- given: given.length > 0 ? given : undefined,
297
- when,
298
- then,
299
- };
300
- }
301
- export function parseScenarioInput(input) {
302
- const trimmed = input.trim();
303
- if (trimmed.startsWith('<')) {
304
- return parseXmlInput(trimmed);
305
- }
306
- return parseJsonInput(trimmed);
307
- }
@@ -316,7 +316,7 @@ export class \${aggregate}Aggregate {
316
316
  eventmodeler codegen slice "Place Order" | node generator.js
317
317
 
318
318
  # All slices (bash loop)
319
- for slice in $(eventmodeler list slices --format json | node -e 'const fs=require("fs");const d=JSON.parse(fs.readFileSync(0,"utf8"));for(const s of (d.slices||[])) console.log(s.name)'); do
319
+ for slice in $(eventmodeler list slices | node -e 'const fs=require("fs");const d=JSON.parse(fs.readFileSync(0,"utf8"));for(const s of (d.slices||[])) console.log(s.name)'); do
320
320
  eventmodeler codegen slice "$slice" | node generator.js
321
321
  done
322
322
 
@@ -50,30 +50,7 @@ eventmodeler create flow --from "<source>" --to "<target>"
50
50
 
51
51
  ### 1. Create Your Slices First
52
52
 
53
- \`\`\`bash
54
- # User places an order
55
- eventmodeler create state-change-slice --xml '<state-change-slice name="Place Order">
56
- <screen name="Checkout">...</screen>
57
- <command name="PlaceOrder">...</command>
58
- <event name="OrderPlaced">...</event>
59
- </state-change-slice>'
60
-
61
- # View for order status
62
- eventmodeler create state-view-slice --xml '<state-view-slice name="View Order Status" after="Place Order">
63
- <read-model name="OrderStatus">...</read-model>
64
- </state-view-slice>'
65
-
66
- # System automatically fulfills order (includes its own read model)
67
- eventmodeler create automation-slice --xml '<automation-slice name="Auto Fulfill" after="View Order Status">
68
- <read-model name="OrderReadyToFulfill">
69
- <field name="orderId" type="UUID"/>
70
- <field name="isPaid" type="Boolean"/>
71
- </read-model>
72
- <processor name="Fulfillment Processor"/>
73
- <command name="FulfillOrder">...</command>
74
- <event name="OrderFulfilled">...</event>
75
- </automation-slice>'
76
- \`\`\`
53
+ Build each slice by adding fields to its elements and connecting them with internal flows.
77
54
 
78
55
  ### 2. Connect Events to Read Models
79
56
 
@@ -115,11 +92,11 @@ eventmodeler create flow --from "OrderStatus" --to "Order Details Screen"
115
92
  After creating flows, map the fields:
116
93
 
117
94
  \`\`\`bash
118
- eventmodeler map fields --flow "OrderPlaced→OrderStatus" --xml '
119
- <mapping from="orderId" to="orderId"/>
120
- <mapping from="customerId" to="customerId"/>
121
- <mapping from="placedAt" to="placedAt"/>
122
- '
95
+ eventmodeler map fields --from "OrderPlaced" --to "OrderStatus" '[
96
+ {"from": "orderId", "to": "orderId"},
97
+ {"from": "customerId", "to": "customerId"},
98
+ {"from": "placedAt", "to": "placedAt"}
99
+ ]'
123
100
  \`\`\`
124
101
 
125
102
  ### 6. Verify Completeness
@@ -140,11 +117,9 @@ When multiple elements share the same name (e.g., linked copies of events or rea
140
117
  \`\`\`bash
141
118
  # List events with their IDs
142
119
  eventmodeler list events
143
- # Output:
144
- # <events>
145
- # <event id="abc12345-..." name="OrderPlaced" fields="4"/>
146
- # <event id="def67890-..." name="OrderPlaced" fields="4"/> <!-- linked copy -->
147
- # </events>
120
+ # Output includes id and name for each event, e.g.:
121
+ # [{"id": "abc12345-...", "name": "OrderPlaced", "fields": 4},
122
+ # {"id": "def67890-...", "name": "OrderPlaced", "fields": 4}]
148
123
  \`\`\`
149
124
 
150
125
  ### Using IDs in Commands
@@ -156,9 +131,9 @@ Prefix the ID (or ID prefix) with \`id:\`:
156
131
  eventmodeler create flow --from "id:abc12345" --to "OrderStatus"
157
132
 
158
133
  # Map fields using ID prefix
159
- eventmodeler map fields --flow "id:abc12345→OrderStatus" --xml '
160
- <mapping from="orderId" to="orderId"/>
161
- '
134
+ eventmodeler map fields --from "id:abc12345" --to "OrderStatus" '[
135
+ {"from": "orderId", "to": "orderId"}
136
+ ]'
162
137
  \`\`\`
163
138
 
164
139
  ### When the CLI Finds Duplicates