eventmodeler 0.4.6 → 0.5.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.
@@ -0,0 +1,269 @@
1
+ export const meta = {
2
+ name: 'scenarios',
3
+ description: 'Design Given-When-Then scenarios for slices: happy paths, errors, automations',
4
+ };
5
+ export const content = `
6
+ # Designing Scenarios for Slices
7
+
8
+ Scenarios define the behavior of a slice. There are two main scenario patterns:
9
+
10
+ 1. **State-Change Scenarios** (for state-change slices): Given events → When command → Then events/error
11
+ 2. **Automation Scenarios** (for automation slices): Given events → Then command (simpler Given-Then format)
12
+
13
+ ## Your Workflow
14
+
15
+ When helping design scenarios for a slice:
16
+
17
+ ### 1. Gather Context
18
+ \`\`\`bash
19
+ # Look at the slice to understand its components
20
+ eventmodeler show slice "<slice-name>"
21
+
22
+ # See what chapter it belongs to and nearby slices for context
23
+ eventmodeler list chapters
24
+ eventmodeler show chapter "<chapter-name>"
25
+
26
+ # List all events to understand what's available for Given clauses
27
+ eventmodeler list events
28
+ \`\`\`
29
+
30
+ ### 2. Identify the Slice Type
31
+
32
+ **State-Change Slice** (Screen → Command → Event):
33
+ - User triggers a command
34
+ - Command produces event(s)
35
+ - Use: Given → When command → Then events/error
36
+
37
+ **Automation Slice** (ReadModel → Processor → Command → Event):
38
+ - Events trigger the processor to issue a command
39
+ - Use simpler Given → Then format (no When)
40
+ - Given: All events that lead to the trigger condition
41
+ - Then: The command that should be dispatched (or noCommand)
42
+
43
+ ### 3. Design Scenarios by Type
44
+
45
+ ---
46
+
47
+ ## State-Change Scenarios
48
+
49
+ For slices where a user action triggers a command:
50
+
51
+ **Happy Path** - The command succeeds under normal conditions
52
+ - Given: Any required preconditions (existing events)
53
+ - When: The command with valid input
54
+ - Then: The expected event(s) with field values
55
+
56
+ **Validation Errors** - Bad input is rejected
57
+ - Given: (may be empty)
58
+ - When: Command with invalid input
59
+ - Then: Error with message
60
+
61
+ **Business Rule Violations** - Valid input but rule prevents action
62
+ - Given: Events that create a state where the action isn't allowed
63
+ - When: The command
64
+ - Then: Error explaining why
65
+
66
+ ### State-Change Examples
67
+
68
+ \`\`\`bash
69
+ # Happy path - command succeeds
70
+ eventmodeler add scenario --slice "<slice-name>" --xml '<scenario name="Successfully place order" description="A registered customer can place an order with valid items">
71
+ <given>
72
+ <event name="CustomerRegistered">
73
+ <field name="customerId">cust-123</field>
74
+ </event>
75
+ </given>
76
+ <when>
77
+ <command name="PlaceOrder">
78
+ <field name="customerId">cust-123</field>
79
+ <field name="items">[{"productId": "prod-1", "quantity": 2}]</field>
80
+ </command>
81
+ </when>
82
+ <then type="events">
83
+ <event name="OrderPlaced">
84
+ <field name="orderId">order-456</field>
85
+ <field name="customerId">cust-123</field>
86
+ </event>
87
+ </then>
88
+ </scenario>'
89
+ \`\`\`
90
+
91
+ \`\`\`bash
92
+ # Validation error
93
+ eventmodeler add scenario --slice "<slice-name>" --xml '<scenario name="Cannot place empty order" description="Orders must contain at least one item - empty item lists are rejected">
94
+ <when>
95
+ <command name="PlaceOrder">
96
+ <field name="customerId">cust-123</field>
97
+ <field name="items">[]</field>
98
+ </command>
99
+ </when>
100
+ <then type="error" errorType="ValidationError">Order must contain at least one item</then>
101
+ </scenario>'
102
+ \`\`\`
103
+
104
+ \`\`\`bash
105
+ # Business rule violation
106
+ eventmodeler add scenario --slice "Cancel Order" --xml '<scenario name="Cannot cancel shipped order" description="Once an order has been shipped, it can no longer be cancelled">
107
+ <given>
108
+ <event name="OrderPlaced">
109
+ <field name="orderId">order-123</field>
110
+ </event>
111
+ <event name="OrderShipped">
112
+ <field name="orderId">order-123</field>
113
+ <field name="shippedAt">2024-01-15T10:00:00Z</field>
114
+ </event>
115
+ </given>
116
+ <when>
117
+ <command name="CancelOrder">
118
+ <field name="orderId">order-123</field>
119
+ </command>
120
+ </when>
121
+ <then type="error" errorType="BusinessRuleViolation">Cannot cancel an order that has already shipped</then>
122
+ </scenario>'
123
+ \`\`\`
124
+
125
+ ---
126
+
127
+ ## Automation Scenarios
128
+
129
+ For slices where events trigger a processor to issue a command. Use the simpler **Given-Then** format:
130
+
131
+ **Trigger Conditions Met** - Events cause processor to act
132
+ - Given: All the events that have occurred (including the trigger events)
133
+ - Then: The command the processor issues
134
+
135
+ **Trigger Conditions Not Met** - Events don't cause action
136
+ - Given: Events that don't meet the trigger condition
137
+ - Then: noCommand (processor should not dispatch)
138
+
139
+ ### Automation Examples
140
+
141
+ \`\`\`bash
142
+ # Payment received triggers shipment initiation
143
+ # Given: OrderPlaced + PaymentReceived → Then: InitiateShipment command
144
+ eventmodeler add scenario --slice "Auto Ship Order" --xml '<scenario name="Payment triggers shipment" description="Given an order has been placed and payment received, the system automatically initiates shipment">
145
+ <given>
146
+ <event name="OrderPlaced">
147
+ <field name="orderId">order-123</field>
148
+ <field name="customerId">cust-456</field>
149
+ </event>
150
+ <event name="PaymentReceived">
151
+ <field name="orderId">order-123</field>
152
+ <field name="amount">99.99</field>
153
+ </event>
154
+ </given>
155
+ <then type="command">
156
+ <command name="InitiateShipment">
157
+ <field name="orderId">order-123</field>
158
+ <field name="warehouseId">wh-001</field>
159
+ </command>
160
+ </then>
161
+ </scenario>'
162
+ \`\`\`
163
+
164
+ \`\`\`bash
165
+ # Negative case - conditions not met, no command dispatched
166
+ eventmodeler add scenario --slice "Auto Ship Order" --xml '<scenario name="No shipment without payment" description="Given only an order placed (no payment), the system should not initiate shipment">
167
+ <given>
168
+ <event name="OrderPlaced">
169
+ <field name="orderId">order-123</field>
170
+ <field name="customerId">cust-456</field>
171
+ </event>
172
+ </given>
173
+ <then type="noCommand"/>
174
+ </scenario>'
175
+ \`\`\`
176
+
177
+ ---
178
+
179
+ ## Read Model Assertions
180
+
181
+ For state-view slices, test that events project correctly to read models:
182
+
183
+ \`\`\`bash
184
+ eventmodeler add scenario --slice "<slice-name>" --xml '<scenario name="Order appears in summary" description="After placing an order, it should be visible in the order summary with pending status">
185
+ <then type="readModelAssertion">
186
+ <read-model name="OrderSummary">
187
+ <field name="orderId">order-456</field>
188
+ <field name="status">pending</field>
189
+ </read-model>
190
+ </then>
191
+ </scenario>'
192
+ \`\`\`
193
+
194
+ ---
195
+
196
+ ## Scenario Types Summary
197
+
198
+ | Slice Type | Format | Then Contains |
199
+ |------------|--------|---------------|
200
+ | State-Change | Given → When (Command) → Then | Events or Error |
201
+ | Automation | Given (Events) → Then | Command or noCommand |
202
+ | State-View | Given → Then | Read Model Assertion |
203
+
204
+ ## JSON Format Alternative
205
+
206
+ You can also use JSON format:
207
+
208
+ **State-Change Scenario:**
209
+ \`\`\`bash
210
+ eventmodeler add scenario --slice "<slice-name>" --json '{
211
+ "name": "Successfully place order",
212
+ "description": "A registered customer can place an order",
213
+ "given": [
214
+ { "event": "CustomerRegistered", "fieldValues": { "customerId": "cust-123" } }
215
+ ],
216
+ "when": {
217
+ "command": "PlaceOrder",
218
+ "commandFieldValues": { "customerId": "cust-123", "items": [] }
219
+ },
220
+ "then": {
221
+ "type": "events",
222
+ "events": [
223
+ { "event": "OrderPlaced", "fieldValues": { "orderId": "order-456" } }
224
+ ]
225
+ }
226
+ }'
227
+ \`\`\`
228
+
229
+ **Automation Scenario (Given-Then format):**
230
+ \`\`\`bash
231
+ eventmodeler add scenario --slice "<slice-name>" --json '{
232
+ "name": "Payment triggers shipment",
233
+ "description": "Given order placed and payment received, shipment is initiated",
234
+ "given": [
235
+ { "event": "OrderPlaced", "fieldValues": { "orderId": "order-123" } },
236
+ { "event": "PaymentReceived", "fieldValues": { "orderId": "order-123", "amount": 99.99 } }
237
+ ],
238
+ "then": {
239
+ "type": "command",
240
+ "command": "InitiateShipment",
241
+ "commandFieldValues": { "orderId": "order-123" }
242
+ }
243
+ }'
244
+ \`\`\`
245
+
246
+ **Automation Scenario (negative case):**
247
+ \`\`\`bash
248
+ eventmodeler add scenario --slice "<slice-name>" --json '{
249
+ "name": "No shipment without payment",
250
+ "description": "Given only order placed (no payment), no command dispatched",
251
+ "given": [
252
+ { "event": "OrderPlaced", "fieldValues": { "orderId": "order-123" } }
253
+ ],
254
+ "then": {
255
+ "type": "noCommand"
256
+ }
257
+ }'
258
+ \`\`\`
259
+
260
+ ## Best Practices
261
+
262
+ 1. **Write clear descriptions** - Explain why this scenario matters and what business rule it captures
263
+ 2. **Use realistic example values** - "cust-123" not "string", "2024-01-15" not "date"
264
+ 3. **Name scenarios descriptively** - "Cannot cancel shipped order" not "Error case 1"
265
+ 4. **Include all relevant fields** - Don't skip fields that matter for the scenario
266
+ 5. **Think about state** - What events need to exist for this scenario to make sense?
267
+ 6. **Cover the negative cases** - These often reveal missing business rules (use \`noCommand\` for automations)
268
+ 7. **Match format to slice type** - Use Given-When-Then for state-change, Given-Then for automation
269
+ `;
@@ -0,0 +1 @@
1
+ export declare function runGuide(subcommand: string | undefined): void;
@@ -0,0 +1,40 @@
1
+ import { meta as exploreMeta, content as exploreContent } from './guides/explore.js';
2
+ import { meta as scenariosMeta, content as scenariosContent } from './guides/scenarios.js';
3
+ import { meta as informationFlowMeta, content as informationFlowContent } from './guides/information-flow.js';
4
+ import { meta as createSlicesMeta, content as createSlicesContent } from './guides/create-slices.js';
5
+ import { meta as connectSlicesMeta, content as connectSlicesContent } from './guides/connect-slices.js';
6
+ import { meta as codegenMeta, content as codegenContent } from './guides/codegen.js';
7
+ const GUIDES = [
8
+ { meta: exploreMeta, content: exploreContent },
9
+ { meta: createSlicesMeta, content: createSlicesContent },
10
+ { meta: connectSlicesMeta, content: connectSlicesContent },
11
+ { meta: informationFlowMeta, content: informationFlowContent },
12
+ { meta: scenariosMeta, content: scenariosContent },
13
+ { meta: codegenMeta, content: codegenContent },
14
+ ];
15
+ export function runGuide(subcommand) {
16
+ if (!subcommand) {
17
+ printGuideList();
18
+ return;
19
+ }
20
+ const guide = GUIDES.find(g => g.meta.name === subcommand);
21
+ if (!guide) {
22
+ console.error(`Unknown guide: ${subcommand}`);
23
+ console.error(`Run "eventmodeler guide" to see available guides.`);
24
+ process.exit(1);
25
+ }
26
+ console.log(guide.content);
27
+ }
28
+ function printGuideList() {
29
+ console.log(`
30
+ eventmodeler guide - Conceptual guides for working with event models
31
+
32
+ USAGE:
33
+ eventmodeler guide <name>
34
+
35
+ AVAILABLE GUIDES:
36
+ ${GUIDES.map(g => ` ${g.meta.name.padEnd(20)} ${g.meta.description}`).join('\n')}
37
+
38
+ Run "eventmodeler guide <name>" to read a guide.
39
+ `);
40
+ }
@@ -2,7 +2,7 @@ import { exec } from 'node:child_process';
2
2
  import { platform } from 'node:os';
3
3
  import * as fs from 'node:fs';
4
4
  import * as path from 'node:path';
5
- const APP_URL = 'https://www.eventmodeling.app';
5
+ const APP_URL = 'https://www.eventmodeler.com';
6
6
  function getOpenCommand() {
7
7
  switch (platform()) {
8
8
  case 'darwin':
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "eventmodeler",
3
- "version": "0.4.6",
4
- "description": "CLI tool for interacting with Event Model files - query, update, and export event models from the terminal",
3
+ "version": "0.5.0",
4
+ "description": "CLI tool for event modeling - explore, design, and generate code from your event models",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "eventmodeler": "./dist/index.js"
@@ -24,14 +24,9 @@
24
24
  "cli",
25
25
  "eventmodel"
26
26
  ],
27
- "author": "",
28
- "license": "MIT",
29
- "repository": {
30
- "type": "git",
31
- "url": "https://github.com/theoema/event-modeler",
32
- "directory": "cli"
33
- },
34
- "homepage": "https://www.eventmodeling.app",
27
+ "author": "Theo Emanuelsson",
28
+ "license": "UNLICENSED",
29
+ "homepage": "https://www.eventmodeler.com",
35
30
  "engines": {
36
31
  "node": ">=18.0.0"
37
32
  },