eventmodeler 0.6.1 → 0.6.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.
- package/README.md +221 -91
- package/dist/index.js +281 -73
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# eventmodeler
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Works with `.eventmodel` files created by the [Event Modeling App](https://www.eventmodeling.app).
|
|
3
|
+
Command line client for Event Modeler cloud models. Use it to inspect a model, place elements on the canvas, maintain fields and scenarios, and drive code generation loops from a terminal or agent.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
@@ -10,166 +8,298 @@ Works with `.eventmodel` files created by the [Event Modeling App](https://www.e
|
|
|
10
8
|
npm install -g eventmodeler
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
# Open Event Modeling app in browser
|
|
17
|
-
eventmodeler
|
|
11
|
+
Requires Node.js 18 or newer.
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
eventmodeler list slices
|
|
13
|
+
## Setup
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
```bash
|
|
16
|
+
# Authenticate in the browser
|
|
17
|
+
eventmodeler login
|
|
24
18
|
|
|
25
|
-
#
|
|
26
|
-
eventmodeler
|
|
19
|
+
# Create a cloud model, or use an existing model id from the app
|
|
20
|
+
eventmodeler create model "Ordering"
|
|
27
21
|
|
|
28
|
-
#
|
|
29
|
-
eventmodeler
|
|
22
|
+
# Link the current repo/directory to a model
|
|
23
|
+
eventmodeler init
|
|
30
24
|
```
|
|
31
25
|
|
|
32
|
-
|
|
26
|
+
`eventmodeler init` writes `.eventmodeler.json` in your project. Most commands read the model id from that file. You can also target an element directly with the global `--id <uuid>` option when a command accepts an optional name.
|
|
33
27
|
|
|
34
|
-
|
|
28
|
+
Useful auth commands:
|
|
35
29
|
|
|
36
30
|
```bash
|
|
37
|
-
eventmodeler
|
|
38
|
-
eventmodeler
|
|
39
|
-
eventmodeler list commands # List all commands
|
|
40
|
-
eventmodeler list chapters # List all chapters
|
|
41
|
-
eventmodeler list aggregates # List all aggregates
|
|
42
|
-
eventmodeler list actors # List all actors
|
|
31
|
+
eventmodeler whoami
|
|
32
|
+
eventmodeler logout
|
|
43
33
|
```
|
|
44
34
|
|
|
45
|
-
|
|
35
|
+
## Common Workflow
|
|
46
36
|
|
|
47
37
|
```bash
|
|
48
|
-
|
|
49
|
-
eventmodeler show
|
|
50
|
-
eventmodeler
|
|
51
|
-
eventmodeler show
|
|
52
|
-
eventmodeler
|
|
38
|
+
# Explore the model
|
|
39
|
+
eventmodeler show model
|
|
40
|
+
eventmodeler list slices
|
|
41
|
+
eventmodeler show slice "Place Order"
|
|
42
|
+
eventmodeler search order
|
|
43
|
+
|
|
44
|
+
# Build out the canvas
|
|
45
|
+
eventmodeler create place command PlaceOrder --x 120 --y 200
|
|
46
|
+
eventmodeler create place event OrderPlaced --x 120 --y 360
|
|
47
|
+
eventmodeler create flow --from PlaceOrder --to OrderPlaced
|
|
48
|
+
|
|
49
|
+
# Add fields
|
|
50
|
+
eventmodeler add field PlaceOrder '{"name":"orderId","type":"UUID"}'
|
|
51
|
+
eventmodeler add subfield PlaceOrder --field customer '{"name":"email","type":"String"}'
|
|
52
|
+
|
|
53
|
+
# Create a scenario atomically
|
|
54
|
+
eventmodeler create scenario --slice "Place Order" '{
|
|
55
|
+
"name": "happy path",
|
|
56
|
+
"when": [
|
|
57
|
+
{
|
|
58
|
+
"entryType": "command",
|
|
59
|
+
"elementName": "PlaceOrder",
|
|
60
|
+
"fieldValues": { "orderId": "uuid-1" }
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
"then": [
|
|
64
|
+
{
|
|
65
|
+
"entryType": "event",
|
|
66
|
+
"elementName": "OrderPlaced",
|
|
67
|
+
"fieldValues": { "orderId": "uuid-1" }
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}'
|
|
53
71
|
```
|
|
54
72
|
|
|
55
|
-
|
|
73
|
+
## Read Commands
|
|
56
74
|
|
|
57
75
|
```bash
|
|
58
|
-
eventmodeler show
|
|
59
|
-
eventmodeler show
|
|
60
|
-
eventmodeler show
|
|
76
|
+
eventmodeler show model
|
|
77
|
+
eventmodeler show context [name]
|
|
78
|
+
eventmodeler show chapter [name]
|
|
79
|
+
eventmodeler show slice [name]
|
|
80
|
+
eventmodeler show event [name]
|
|
81
|
+
eventmodeler show command [name]
|
|
82
|
+
eventmodeler show readmodel [name]
|
|
83
|
+
eventmodeler show screen [name]
|
|
84
|
+
eventmodeler show processor [name]
|
|
85
|
+
eventmodeler show external-event [name]
|
|
86
|
+
eventmodeler show completeness
|
|
87
|
+
eventmodeler summary
|
|
88
|
+
eventmodeler search <term>
|
|
61
89
|
```
|
|
62
90
|
|
|
63
|
-
|
|
91
|
+
List commands:
|
|
64
92
|
|
|
65
93
|
```bash
|
|
66
|
-
eventmodeler
|
|
94
|
+
eventmodeler list slices [--chapter <name>]
|
|
95
|
+
eventmodeler list events
|
|
96
|
+
eventmodeler list commands
|
|
97
|
+
eventmodeler list readmodels
|
|
98
|
+
eventmodeler list screens
|
|
99
|
+
eventmodeler list processors
|
|
100
|
+
eventmodeler list external-events
|
|
101
|
+
eventmodeler list aggregates
|
|
102
|
+
eventmodeler list actors
|
|
103
|
+
eventmodeler list chapters
|
|
104
|
+
eventmodeler list contexts
|
|
105
|
+
eventmodeler list swimlanes
|
|
106
|
+
eventmodeler list notes
|
|
107
|
+
eventmodeler list scenarios
|
|
67
108
|
```
|
|
68
109
|
|
|
69
|
-
|
|
110
|
+
Output is JSON from the backend SDK. Use `eventmodeler help <topic>` or command-specific `--help` for schema examples.
|
|
111
|
+
|
|
112
|
+
## Canvas And Model Editing
|
|
113
|
+
|
|
114
|
+
Create models, slices, elements, flows, and linked copies:
|
|
70
115
|
|
|
71
116
|
```bash
|
|
72
|
-
|
|
73
|
-
eventmodeler
|
|
74
|
-
eventmodeler
|
|
117
|
+
eventmodeler create model <name>
|
|
118
|
+
eventmodeler create slice '<json>'
|
|
119
|
+
eventmodeler create place <type> <name> --x <n> --y <n>
|
|
120
|
+
eventmodeler create flow --from <source> --to <target>
|
|
121
|
+
eventmodeler create place-copy <type> [originalName] --x <n> --y <n>
|
|
122
|
+
eventmodeler create move-copy <type> [name] --x <n> --y <n>
|
|
123
|
+
eventmodeler create remove-copy <type> [name]
|
|
124
|
+
```
|
|
75
125
|
|
|
76
|
-
|
|
77
|
-
eventmodeler add scenario --slice "Place Order" --json '{"name": "Happy path", ...}'
|
|
126
|
+
Element types for `create place`:
|
|
78
127
|
|
|
79
|
-
|
|
80
|
-
|
|
128
|
+
```text
|
|
129
|
+
command, event, readmodel, screen, processor, external-event,
|
|
130
|
+
aggregate, actor, chapter, context, note, swimlane, slice
|
|
131
|
+
```
|
|
81
132
|
|
|
82
|
-
|
|
83
|
-
eventmodeler remove field --event "OrderPlaced" --field "legacyId"
|
|
133
|
+
Linked copies are supported for:
|
|
84
134
|
|
|
85
|
-
|
|
86
|
-
|
|
135
|
+
```text
|
|
136
|
+
event, readmodel, screen, external-event
|
|
137
|
+
```
|
|
87
138
|
|
|
88
|
-
|
|
89
|
-
|
|
139
|
+
Move, resize, rename, and remove existing elements:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
eventmodeler move <type> [name] --x <n> --y <n>
|
|
143
|
+
eventmodeler resize <type> [name] --width <n> --height <n>
|
|
144
|
+
eventmodeler rename <type> <oldName> <newName>
|
|
145
|
+
eventmodeler remove <type> [name]
|
|
146
|
+
eventmodeler remove flow --from <source> --to <target>
|
|
90
147
|
```
|
|
91
148
|
|
|
92
|
-
|
|
149
|
+
Mark slice status:
|
|
93
150
|
|
|
94
151
|
```bash
|
|
95
|
-
eventmodeler
|
|
96
|
-
eventmodeler
|
|
152
|
+
eventmodeler mark "Place Order" planned
|
|
153
|
+
eventmodeler mark "Place Order" created
|
|
154
|
+
eventmodeler mark "Place Order" in-progress
|
|
155
|
+
eventmodeler mark "Place Order" blocked
|
|
156
|
+
eventmodeler mark "Place Order" done
|
|
97
157
|
```
|
|
98
158
|
|
|
99
|
-
##
|
|
159
|
+
## Fields
|
|
160
|
+
|
|
161
|
+
Fields are supported on commands, events, readmodels, screens, processors, and external events.
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
eventmodeler add field [elementName] '{"name":"customerId","type":"UUID"}'
|
|
165
|
+
eventmodeler add subfield [elementName] --field <parentFieldName> '{"name":"email","type":"String"}'
|
|
166
|
+
|
|
167
|
+
eventmodeler update field [elementName] --field <name> --name <newName>
|
|
168
|
+
eventmodeler update field [elementName] --field <name> --type <newType>
|
|
169
|
+
eventmodeler update field [elementName] --field <name> --optional true
|
|
170
|
+
eventmodeler update field [elementName] --field <name> --generated false
|
|
171
|
+
eventmodeler update field [elementName] --field <name> --list true
|
|
172
|
+
eventmodeler update field [elementName] --field <name> --user-input true
|
|
173
|
+
|
|
174
|
+
eventmodeler update subfield [elementName] --subfield <id> --name <newName>
|
|
175
|
+
eventmodeler update subfield [elementName] --subfield <id> --type <newType>
|
|
176
|
+
eventmodeler update reorder [elementName] --field <name> --position <n>
|
|
177
|
+
eventmodeler update reorder-subfield [elementName] --subfield <id> --position <n>
|
|
178
|
+
|
|
179
|
+
eventmodeler remove field [elementName] --field <name>
|
|
180
|
+
eventmodeler remove subfield [elementName] --subfield <id>
|
|
181
|
+
eventmodeler set aggregate-id [aggregateName] --field-name <name> --field-type UUID
|
|
182
|
+
```
|
|
100
183
|
|
|
101
|
-
|
|
184
|
+
For field mappings:
|
|
102
185
|
|
|
103
186
|
```bash
|
|
104
|
-
eventmodeler
|
|
105
|
-
eventmodeler
|
|
187
|
+
eventmodeler map fields '[{"source":"orderId","target":"orderId"}]' --from PlaceOrder --to OrderPlaced
|
|
188
|
+
eventmodeler remove mapping --from PlaceOrder --to OrderPlaced --mapping <mappingId>
|
|
106
189
|
```
|
|
107
190
|
|
|
108
|
-
|
|
191
|
+
## Scenarios
|
|
109
192
|
|
|
110
|
-
|
|
193
|
+
Create a full Given/When/Then scenario in one backend transaction:
|
|
111
194
|
|
|
112
|
-
**Environment variable:**
|
|
113
195
|
```bash
|
|
114
|
-
|
|
196
|
+
eventmodeler add scenario --slice "Place Order" '<scenario-json>'
|
|
197
|
+
eventmodeler create scenario --slice "Place Order" '<scenario-json>'
|
|
115
198
|
```
|
|
116
199
|
|
|
117
|
-
|
|
200
|
+
Scenario JSON shape:
|
|
201
|
+
|
|
118
202
|
```json
|
|
119
203
|
{
|
|
120
|
-
"
|
|
204
|
+
"name": "happy path",
|
|
205
|
+
"description": "optional",
|
|
206
|
+
"given": [
|
|
207
|
+
{ "entryType": "event", "elementName": "OrderPlaced" }
|
|
208
|
+
],
|
|
209
|
+
"when": [
|
|
210
|
+
{ "entryType": "command", "elementName": "PlaceOrder" }
|
|
211
|
+
],
|
|
212
|
+
"then": [
|
|
213
|
+
{ "entryType": "event", "elementName": "OrderPlaced" },
|
|
214
|
+
{ "entryType": "error", "errorType": "Validation", "errorMessage": "Invalid order" }
|
|
215
|
+
]
|
|
121
216
|
}
|
|
122
217
|
```
|
|
123
218
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
## Options
|
|
219
|
+
Allowed entry types:
|
|
127
220
|
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
-v, --version Show version number
|
|
221
|
+
```text
|
|
222
|
+
given: event, readmodel
|
|
223
|
+
when: command, event
|
|
224
|
+
then: event, command, readmodel, error
|
|
133
225
|
```
|
|
134
226
|
|
|
135
|
-
|
|
227
|
+
Example values can be provided in the scenario JSON via `fieldValues`, or edited later:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
eventmodeler add entry --scenario "happy path" --section then --type event --element OrderPlaced
|
|
231
|
+
eventmodeler set example --scenario "happy path" --entry <entryId> --type event --field orderId --value uuid-1
|
|
232
|
+
eventmodeler set example --scenario "happy path" --entry <entryId> --type event --field tags --values vip,web
|
|
233
|
+
eventmodeler set example --scenario "happy path" --entry <entryId> --type event --field address --json '{"city":"NYC"}'
|
|
234
|
+
eventmodeler set description --scenario "happy path" --text "Updated description"
|
|
235
|
+
eventmodeler set position --scenario "happy path" --section then --entry <entryId> --position 0
|
|
236
|
+
eventmodeler remove example --scenario "happy path" --entry <entryId> --type event --field orderId
|
|
237
|
+
eventmodeler remove entry --scenario "happy path" --section then --entry <entryId>
|
|
238
|
+
eventmodeler remove scenario "happy path"
|
|
239
|
+
```
|
|
136
240
|
|
|
137
|
-
|
|
241
|
+
Primitive example values are strings. Custom fields use JSON objects, and Custom list fields use arrays of objects.
|
|
138
242
|
|
|
139
|
-
|
|
243
|
+
## Screen And Note Details
|
|
140
244
|
|
|
141
245
|
```bash
|
|
142
|
-
eventmodeler
|
|
246
|
+
eventmodeler design <screenName> '<excalidraw-json-array>'
|
|
247
|
+
eventmodeler set note-description [noteName] --text "Markdown or plain text"
|
|
143
248
|
```
|
|
144
249
|
|
|
145
|
-
|
|
250
|
+
## Automation Loop
|
|
146
251
|
|
|
147
|
-
|
|
252
|
+
The CLI can poll planned slices and run your generator command for each one.
|
|
148
253
|
|
|
149
254
|
```bash
|
|
150
|
-
eventmodeler
|
|
151
|
-
eventmodeler
|
|
255
|
+
eventmodeler init loop
|
|
256
|
+
eventmodeler loop
|
|
152
257
|
```
|
|
153
258
|
|
|
154
|
-
|
|
259
|
+
`eventmodeler init loop` adds a `loop` block to `.eventmodeler.json` and creates a sample `generate.sh`. The generated script marks a slice in progress, exports slice JSON, runs your codegen placeholder, and marks the slice done or blocked.
|
|
155
260
|
|
|
156
|
-
|
|
261
|
+
## Configuration
|
|
157
262
|
|
|
158
|
-
|
|
159
|
-
|
|
263
|
+
Global config lives at:
|
|
264
|
+
|
|
265
|
+
```text
|
|
266
|
+
~/.eventmodeler/config.json
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Project config lives at:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
.eventmodeler.json
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The production backend is used by default:
|
|
276
|
+
|
|
277
|
+
```text
|
|
278
|
+
https://api.eventmodeler.com
|
|
160
279
|
```
|
|
161
280
|
|
|
162
|
-
|
|
281
|
+
Override endpoints when developing locally:
|
|
163
282
|
|
|
164
|
-
|
|
165
|
-
|
|
283
|
+
```bash
|
|
284
|
+
export EVENTMODELER_BACKEND_URL=http://localhost:8080
|
|
285
|
+
export EVENTMODELER_KEYCLOAK_URL=http://localhost:18180/realms/eventmodeler
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Help Topics
|
|
166
289
|
|
|
167
|
-
|
|
290
|
+
Task-oriented help is built into the CLI:
|
|
168
291
|
|
|
169
|
-
|
|
292
|
+
```bash
|
|
293
|
+
eventmodeler help
|
|
294
|
+
eventmodeler help build-slice
|
|
295
|
+
eventmodeler help write-scenarios
|
|
296
|
+
eventmodeler help manipulate-canvas
|
|
297
|
+
eventmodeler help linked-copies
|
|
298
|
+
eventmodeler help check-completeness
|
|
299
|
+
eventmodeler guide json-reference
|
|
300
|
+
```
|
|
170
301
|
|
|
171
302
|
## Links
|
|
172
303
|
|
|
173
|
-
-
|
|
174
|
-
-
|
|
175
|
-
- [GitHub Repository](https://github.com/theoema/event-modeler)
|
|
304
|
+
- Event Modeling: https://eventmodeling.org
|
|
305
|
+
- GitHub: https://github.com/theoema/event-modeler
|
package/dist/index.js
CHANGED
|
@@ -1234,6 +1234,15 @@ var createClient = (config = {}) => {
|
|
|
1234
1234
|
var client = createClient(createClientConfig(createConfig({ baseUrl: "http://localhost:8080" })));
|
|
1235
1235
|
|
|
1236
1236
|
// src/api/generated/sdk.gen.ts
|
|
1237
|
+
var resizeSwimLane = (options) => (options.client ?? client).post({
|
|
1238
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
1239
|
+
url: "/api/swim-lanes/resize-swim-lane",
|
|
1240
|
+
...options,
|
|
1241
|
+
headers: {
|
|
1242
|
+
"Content-Type": "application/json",
|
|
1243
|
+
...options.headers
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1237
1246
|
var renameSwimLane = (options) => (options.client ?? client).post({
|
|
1238
1247
|
security: [{ scheme: "bearer", type: "http" }],
|
|
1239
1248
|
url: "/api/swim-lanes/rename-swim-lane",
|
|
@@ -1270,6 +1279,15 @@ var moveSwimLane = (options) => (options.client ?? client).post({
|
|
|
1270
1279
|
...options.headers
|
|
1271
1280
|
}
|
|
1272
1281
|
});
|
|
1282
|
+
var resizeSlice = (options) => (options.client ?? client).post({
|
|
1283
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
1284
|
+
url: "/api/slices/resize-slice",
|
|
1285
|
+
...options,
|
|
1286
|
+
headers: {
|
|
1287
|
+
"Content-Type": "application/json",
|
|
1288
|
+
...options.headers
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1273
1291
|
var renameSlice = (options) => (options.client ?? client).post({
|
|
1274
1292
|
security: [{ scheme: "bearer", type: "http" }],
|
|
1275
1293
|
url: "/api/slices/rename-slice",
|
|
@@ -3097,6 +3115,15 @@ var addEventField = (options) => (options.client ?? client).post({
|
|
|
3097
3115
|
...options.headers
|
|
3098
3116
|
}
|
|
3099
3117
|
});
|
|
3118
|
+
var resizeContext = (options) => (options.client ?? client).post({
|
|
3119
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3120
|
+
url: "/api/contexts/resize-context",
|
|
3121
|
+
...options,
|
|
3122
|
+
headers: {
|
|
3123
|
+
"Content-Type": "application/json",
|
|
3124
|
+
...options.headers
|
|
3125
|
+
}
|
|
3126
|
+
});
|
|
3100
3127
|
var renameContext = (options) => (options.client ?? client).post({
|
|
3101
3128
|
security: [{ scheme: "bearer", type: "http" }],
|
|
3102
3129
|
url: "/api/contexts/rename-context",
|
|
@@ -3367,6 +3394,15 @@ var addCommandField = (options) => (options.client ?? client).post({
|
|
|
3367
3394
|
...options.headers
|
|
3368
3395
|
}
|
|
3369
3396
|
});
|
|
3397
|
+
var resizeChapter = (options) => (options.client ?? client).post({
|
|
3398
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3399
|
+
url: "/api/chapters/resize-chapter",
|
|
3400
|
+
...options,
|
|
3401
|
+
headers: {
|
|
3402
|
+
"Content-Type": "application/json",
|
|
3403
|
+
...options.headers
|
|
3404
|
+
}
|
|
3405
|
+
});
|
|
3370
3406
|
var renameChapter = (options) => (options.client ?? client).post({
|
|
3371
3407
|
security: [{ scheme: "bearer", type: "http" }],
|
|
3372
3408
|
url: "/api/chapters/rename-chapter",
|
|
@@ -3412,6 +3448,15 @@ var setAggregateIdField = (options) => (options.client ?? client).post({
|
|
|
3412
3448
|
...options.headers
|
|
3413
3449
|
}
|
|
3414
3450
|
});
|
|
3451
|
+
var resizeAggregate = (options) => (options.client ?? client).post({
|
|
3452
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3453
|
+
url: "/api/aggregates/resize-aggregate",
|
|
3454
|
+
...options,
|
|
3455
|
+
headers: {
|
|
3456
|
+
"Content-Type": "application/json",
|
|
3457
|
+
...options.headers
|
|
3458
|
+
}
|
|
3459
|
+
});
|
|
3415
3460
|
var renameAggregate = (options) => (options.client ?? client).post({
|
|
3416
3461
|
security: [{ scheme: "bearer", type: "http" }],
|
|
3417
3462
|
url: "/api/aggregates/rename-aggregate",
|
|
@@ -3448,6 +3493,15 @@ var moveAggregate = (options) => (options.client ?? client).post({
|
|
|
3448
3493
|
...options.headers
|
|
3449
3494
|
}
|
|
3450
3495
|
});
|
|
3496
|
+
var resizeActor = (options) => (options.client ?? client).post({
|
|
3497
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3498
|
+
url: "/api/actors/resize-actor",
|
|
3499
|
+
...options,
|
|
3500
|
+
headers: {
|
|
3501
|
+
"Content-Type": "application/json",
|
|
3502
|
+
...options.headers
|
|
3503
|
+
}
|
|
3504
|
+
});
|
|
3451
3505
|
var renameActor = (options) => (options.client ?? client).post({
|
|
3452
3506
|
security: [{ scheme: "bearer", type: "http" }],
|
|
3453
3507
|
url: "/api/actors/rename-actor",
|
|
@@ -3524,6 +3578,36 @@ var listModels = (options) => (options?.client ?? client).get({
|
|
|
3524
3578
|
url: "/api/models",
|
|
3525
3579
|
...options
|
|
3526
3580
|
});
|
|
3581
|
+
var swimLanes = (options) => (options.client ?? client).get({
|
|
3582
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3583
|
+
url: "/api/models/{modelId}/swim-lanes",
|
|
3584
|
+
...options
|
|
3585
|
+
});
|
|
3586
|
+
var slices = (options) => (options.client ?? client).get({
|
|
3587
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3588
|
+
url: "/api/models/{modelId}/slices",
|
|
3589
|
+
...options
|
|
3590
|
+
});
|
|
3591
|
+
var contexts = (options) => (options.client ?? client).get({
|
|
3592
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3593
|
+
url: "/api/models/{modelId}/contexts",
|
|
3594
|
+
...options
|
|
3595
|
+
});
|
|
3596
|
+
var chapters = (options) => (options.client ?? client).get({
|
|
3597
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3598
|
+
url: "/api/models/{modelId}/chapters",
|
|
3599
|
+
...options
|
|
3600
|
+
});
|
|
3601
|
+
var aggregates = (options) => (options.client ?? client).get({
|
|
3602
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3603
|
+
url: "/api/models/{modelId}/aggregates",
|
|
3604
|
+
...options
|
|
3605
|
+
});
|
|
3606
|
+
var actors = (options) => (options.client ?? client).get({
|
|
3607
|
+
security: [{ scheme: "bearer", type: "http" }],
|
|
3608
|
+
url: "/api/models/{modelId}/actors",
|
|
3609
|
+
...options
|
|
3610
|
+
});
|
|
3527
3611
|
var listExternalEvents = (options) => (options.client ?? client).get({
|
|
3528
3612
|
security: [{ scheme: "bearer", type: "http" }],
|
|
3529
3613
|
url: "/api/external-event-stickies/{modelId}/external-event-list",
|
|
@@ -4030,6 +4114,9 @@ EXAMPLE:
|
|
|
4030
4114
|
NOTES:
|
|
4031
4115
|
- The JSON is a positional argument (not a --json flag)
|
|
4032
4116
|
- Every element and flow needs a unique UUID
|
|
4117
|
+
- Field and subfield UUIDs are generated when omitted
|
|
4118
|
+
- Field flags such as isGenerated, isList, isOptional, and isUserInput are not
|
|
4119
|
+
applied by create slice; add or update them after creation
|
|
4033
4120
|
- Valid flow pairs: screen->command, command->event, command->external-event,
|
|
4034
4121
|
event->readmodel, readmodel->screen, readmodel->processor, processor->command,
|
|
4035
4122
|
external-event->processor
|
|
@@ -4238,10 +4325,10 @@ WHEN TO USE: You need to specify slice behavior as Given-When-Then test cases.
|
|
|
4238
4325
|
## Task: Add a full scenario to a slice (compound command)
|
|
4239
4326
|
|
|
4240
4327
|
COMMAND:
|
|
4241
|
-
eventmodeler
|
|
4328
|
+
eventmodeler create scenario '<json>' --slice <sliceName>
|
|
4242
4329
|
|
|
4243
4330
|
EXAMPLE (happy path — state-change slice):
|
|
4244
|
-
eventmodeler
|
|
4331
|
+
eventmodeler create scenario '{
|
|
4245
4332
|
"name": "Successfully place order",
|
|
4246
4333
|
"given": [
|
|
4247
4334
|
{"entryType": "event", "elementName": "CustomerRegistered", "fieldValues": {"customerId": "cust-001"}}
|
|
@@ -4255,7 +4342,7 @@ EXAMPLE (happy path — state-change slice):
|
|
|
4255
4342
|
}' --slice "Place Order"
|
|
4256
4343
|
|
|
4257
4344
|
EXAMPLE (error case):
|
|
4258
|
-
eventmodeler
|
|
4345
|
+
eventmodeler create scenario '{
|
|
4259
4346
|
"name": "Reject order with no items",
|
|
4260
4347
|
"when": [
|
|
4261
4348
|
{"entryType": "command", "elementName": "PlaceOrder", "fieldValues": {"customerId": "cust-001"}}
|
|
@@ -4266,7 +4353,7 @@ EXAMPLE (error case):
|
|
|
4266
4353
|
}' --slice "Place Order"
|
|
4267
4354
|
|
|
4268
4355
|
EXAMPLE (readmodel assertion in then):
|
|
4269
|
-
eventmodeler
|
|
4356
|
+
eventmodeler create scenario '{
|
|
4270
4357
|
"name": "Order appears in summary",
|
|
4271
4358
|
"given": [
|
|
4272
4359
|
{"entryType": "event", "elementName": "OrderPlaced", "fieldValues": {"orderId": "ord-001", "customerId": "cust-001"}}
|
|
@@ -4731,11 +4818,9 @@ WHEN TO USE: You need the exact JSON shape for a CLI command.
|
|
|
4731
4818
|
|
|
4732
4819
|
Field types: UUID, String, Int, Long, Double, Decimal, Boolean, Date, DateTime, Custom
|
|
4733
4820
|
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
"isList": true
|
|
4738
|
-
"isUserInput": true (screens only)
|
|
4821
|
+
Note: create slice generates field/subfield IDs when omitted. Field metadata
|
|
4822
|
+
flags such as isOptional, isGenerated, isList, and isUserInput are not applied
|
|
4823
|
+
by create slice; add or update those flags after creation.
|
|
4739
4824
|
|
|
4740
4825
|
Custom type with subfields:
|
|
4741
4826
|
{
|
|
@@ -4790,9 +4875,9 @@ WHEN TO USE: You need the exact JSON shape for a CLI command.
|
|
|
4790
4875
|
{ "source": "sourceFieldName", "target": "targetFieldName" }
|
|
4791
4876
|
]
|
|
4792
4877
|
|
|
4793
|
-
##
|
|
4878
|
+
## create scenario '<json>' --slice <sliceName>
|
|
4794
4879
|
|
|
4795
|
-
eventmodeler
|
|
4880
|
+
eventmodeler create scenario '<json>' --slice "Place Order"
|
|
4796
4881
|
|
|
4797
4882
|
SCENARIO JSON:
|
|
4798
4883
|
{
|
|
@@ -5371,6 +5456,65 @@ function registerListCommands(program) {
|
|
|
5371
5456
|
}
|
|
5372
5457
|
}
|
|
5373
5458
|
|
|
5459
|
+
// src/lib/scenario-field-values.ts
|
|
5460
|
+
function isPlainObject(v) {
|
|
5461
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
5462
|
+
}
|
|
5463
|
+
function toFieldValueWire(value, path4 = "fieldValues") {
|
|
5464
|
+
if (Array.isArray(value)) {
|
|
5465
|
+
const allObjects = value.length > 0 && value.every(isPlainObject);
|
|
5466
|
+
const anyObject = value.some(isPlainObject);
|
|
5467
|
+
if (allObjects) {
|
|
5468
|
+
return { treeList: value.map((item, i) => toFieldValueWire(item, `${path4}[${i}]`)) };
|
|
5469
|
+
}
|
|
5470
|
+
if (anyObject) {
|
|
5471
|
+
throw new Error(`${path4}: list mixes primitives and objects; use either all scalar values or all objects.`);
|
|
5472
|
+
}
|
|
5473
|
+
return { list: value.map(String) };
|
|
5474
|
+
}
|
|
5475
|
+
if (isPlainObject(value)) {
|
|
5476
|
+
return {
|
|
5477
|
+
tree: Object.fromEntries(Object.entries(value).map(([key, child]) => [key, toFieldValueWire(child, `${path4}.${key}`)]))
|
|
5478
|
+
};
|
|
5479
|
+
}
|
|
5480
|
+
return { scalar: String(value) };
|
|
5481
|
+
}
|
|
5482
|
+
function toFieldValuesWire(values) {
|
|
5483
|
+
if (!values)
|
|
5484
|
+
return {};
|
|
5485
|
+
return Object.fromEntries(Object.entries(values).map(([fieldName, value]) => [fieldName, toFieldValueWire(value, `fieldValues.${fieldName}`)]));
|
|
5486
|
+
}
|
|
5487
|
+
function unwrapFieldValueWire(value) {
|
|
5488
|
+
if (!isPlainObject(value))
|
|
5489
|
+
return value;
|
|
5490
|
+
if ("scalar" in value)
|
|
5491
|
+
return value.scalar;
|
|
5492
|
+
if ("list" in value)
|
|
5493
|
+
return value.list;
|
|
5494
|
+
if ("tree" in value && isPlainObject(value.tree)) {
|
|
5495
|
+
return Object.fromEntries(Object.entries(value.tree).map(([fieldName, child]) => [fieldName, unwrapFieldValueWire(child)]));
|
|
5496
|
+
}
|
|
5497
|
+
if ("treeList" in value && Array.isArray(value.treeList)) {
|
|
5498
|
+
return value.treeList.map((item) => unwrapFieldValueWire(item));
|
|
5499
|
+
}
|
|
5500
|
+
return value;
|
|
5501
|
+
}
|
|
5502
|
+
function unwrapFieldValuesWire(values) {
|
|
5503
|
+
if (!isPlainObject(values))
|
|
5504
|
+
return values;
|
|
5505
|
+
return Object.fromEntries(Object.entries(values).map(([fieldName, value]) => [fieldName, unwrapFieldValueWire(value)]));
|
|
5506
|
+
}
|
|
5507
|
+
function unwrapScenarioFieldValuesInResponse(value) {
|
|
5508
|
+
if (Array.isArray(value))
|
|
5509
|
+
return value.map(unwrapScenarioFieldValuesInResponse);
|
|
5510
|
+
if (!isPlainObject(value))
|
|
5511
|
+
return value;
|
|
5512
|
+
return Object.fromEntries(Object.entries(value).map(([key, child]) => [
|
|
5513
|
+
key,
|
|
5514
|
+
key === "fieldValues" ? unwrapFieldValuesWire(child) : unwrapScenarioFieldValuesInResponse(child)
|
|
5515
|
+
]));
|
|
5516
|
+
}
|
|
5517
|
+
|
|
5374
5518
|
// src/commands/show-schemas.ts
|
|
5375
5519
|
var FIELD_VIEW = `FieldView:
|
|
5376
5520
|
{
|
|
@@ -5629,22 +5773,22 @@ function registerShowCommands(program) {
|
|
|
5629
5773
|
const show = program.command("show").description("Show detailed information");
|
|
5630
5774
|
show.command("model").description("Show full model hierarchy").addHelpText("after", SHOW_MODEL_HELP).action(async () => {
|
|
5631
5775
|
const modelId = requireModelId();
|
|
5632
|
-
out(unwrap(await showModel({ path: { modelId } })));
|
|
5776
|
+
out(unwrapScenarioFieldValuesInResponse(unwrap(await showModel({ path: { modelId } }))));
|
|
5633
5777
|
});
|
|
5634
5778
|
show.command("context [name]").description("Show context with contained chapters and slices").addHelpText("after", SHOW_CONTEXT_HELP).action(async (name, cmd) => {
|
|
5635
5779
|
const modelId = requireModelId();
|
|
5636
5780
|
const contextId = await resolve2(modelId, "context", name ?? "", getGlobalId());
|
|
5637
|
-
out(unwrap(await showContext({ path: { modelId, contextId } })));
|
|
5781
|
+
out(unwrapScenarioFieldValuesInResponse(unwrap(await showContext({ path: { modelId, contextId } }))));
|
|
5638
5782
|
});
|
|
5639
5783
|
show.command("chapter [name]").description("Show chapter with contained slices").addHelpText("after", SHOW_CHAPTER_HELP).action(async (name, cmd) => {
|
|
5640
5784
|
const modelId = requireModelId();
|
|
5641
5785
|
const chapterId = await resolve2(modelId, "chapter", name ?? "", getGlobalId());
|
|
5642
|
-
out(unwrap(await showChapter({ path: { modelId, chapterId } })));
|
|
5786
|
+
out(unwrapScenarioFieldValuesInResponse(unwrap(await showChapter({ path: { modelId, chapterId } }))));
|
|
5643
5787
|
});
|
|
5644
5788
|
show.command("slice [name]").description("Show slice with all elements, flows, and scenarios").addHelpText("after", SHOW_SLICE_HELP).action(async (name, cmd) => {
|
|
5645
5789
|
const modelId = requireModelId();
|
|
5646
5790
|
const sliceId = await resolve2(modelId, "slice", name ?? "", getGlobalId());
|
|
5647
|
-
out(unwrap(await showSlice({ path: { modelId, sliceId } })));
|
|
5791
|
+
out(unwrapScenarioFieldValuesInResponse(unwrap(await showSlice({ path: { modelId, sliceId } }))));
|
|
5648
5792
|
});
|
|
5649
5793
|
for (const type of ["event", "command", "readmodel", "screen", "processor", "external-event"]) {
|
|
5650
5794
|
show.command(`${type} [name]`).description(`Show ${type} detail with fields and flows`).addHelpText("after", SHOW_ELEMENT_HELP).action(async (name, cmd) => {
|
|
@@ -5733,7 +5877,7 @@ var ADD_SUBFIELD_MAP = {
|
|
|
5733
5877
|
processor: addProcessorSubfield,
|
|
5734
5878
|
"external-event": addExternalEventSubfield
|
|
5735
5879
|
};
|
|
5736
|
-
function
|
|
5880
|
+
function isPlainObject2(v) {
|
|
5737
5881
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
5738
5882
|
}
|
|
5739
5883
|
var ENTRY_DISPATCH = {
|
|
@@ -5787,8 +5931,8 @@ async function addEntries(modelId, scenarioId, section, entries, positionOffset
|
|
|
5787
5931
|
if (entry.fieldValues) {
|
|
5788
5932
|
for (const [fieldName, value] of Object.entries(entry.fieldValues)) {
|
|
5789
5933
|
if (Array.isArray(value)) {
|
|
5790
|
-
const allObjects = value.length > 0 && value.every(
|
|
5791
|
-
const anyObject = value.some((v) =>
|
|
5934
|
+
const allObjects = value.length > 0 && value.every(isPlainObject2);
|
|
5935
|
+
const anyObject = value.some((v) => isPlainObject2(v));
|
|
5792
5936
|
if (allObjects) {
|
|
5793
5937
|
if (!info.setFieldCustomListValues)
|
|
5794
5938
|
throw new Error(`Cannot set Custom list values on ${entry.entryType}`);
|
|
@@ -5804,7 +5948,7 @@ async function addEntries(modelId, scenarioId, section, entries, positionOffset
|
|
|
5804
5948
|
body: { modelId, scenarioId, entryId, fieldName, values: value.map(String) }
|
|
5805
5949
|
}));
|
|
5806
5950
|
}
|
|
5807
|
-
} else if (
|
|
5951
|
+
} else if (isPlainObject2(value)) {
|
|
5808
5952
|
if (!info.setFieldCustomValue)
|
|
5809
5953
|
throw new Error(`Cannot set Custom value on ${entry.entryType}`);
|
|
5810
5954
|
unwrap(await info.setFieldCustomValue({
|
|
@@ -5822,6 +5966,60 @@ async function addEntries(modelId, scenarioId, section, entries, positionOffset
|
|
|
5822
5966
|
}
|
|
5823
5967
|
}
|
|
5824
5968
|
}
|
|
5969
|
+
async function createScenarioFromJson(json, sliceName) {
|
|
5970
|
+
const modelId = requireModelId();
|
|
5971
|
+
const sliceId = await resolve2(modelId, "slice", sliceName, getGlobalId());
|
|
5972
|
+
const spec = JSON.parse(json);
|
|
5973
|
+
const scenarioId = randomUUID();
|
|
5974
|
+
const buildEntries = async (section, entries) => {
|
|
5975
|
+
if (!entries)
|
|
5976
|
+
return [];
|
|
5977
|
+
const dispatch = ENTRY_DISPATCH[section];
|
|
5978
|
+
const out2 = [];
|
|
5979
|
+
for (const entry of entries) {
|
|
5980
|
+
const info = dispatch[entry.entryType];
|
|
5981
|
+
if (!info) {
|
|
5982
|
+
throw new Error(`Unknown ${section} entry type: ${entry.entryType}`);
|
|
5983
|
+
}
|
|
5984
|
+
if (entry.entryType === "error") {
|
|
5985
|
+
out2.push({
|
|
5986
|
+
entryType: "error",
|
|
5987
|
+
errorType: entry.errorType ?? "",
|
|
5988
|
+
errorMessage: entry.errorMessage ?? "",
|
|
5989
|
+
fieldValues: {}
|
|
5990
|
+
});
|
|
5991
|
+
} else {
|
|
5992
|
+
if (!entry.elementName) {
|
|
5993
|
+
throw new Error(`${section} ${entry.entryType} entry is missing elementName`);
|
|
5994
|
+
}
|
|
5995
|
+
const elementId = await resolve2(modelId, info.resolveType, entry.elementName);
|
|
5996
|
+
out2.push({
|
|
5997
|
+
entryType: entry.entryType,
|
|
5998
|
+
elementId,
|
|
5999
|
+
fieldValues: toFieldValuesWire(entry.fieldValues)
|
|
6000
|
+
});
|
|
6001
|
+
}
|
|
6002
|
+
}
|
|
6003
|
+
return out2;
|
|
6004
|
+
};
|
|
6005
|
+
const given = await buildEntries("given", spec.given);
|
|
6006
|
+
const when = await buildEntries("when", spec.when);
|
|
6007
|
+
const then = await buildEntries("then", spec.then);
|
|
6008
|
+
unwrap(await createScenario({
|
|
6009
|
+
body: {
|
|
6010
|
+
modelId,
|
|
6011
|
+
sliceId,
|
|
6012
|
+
scenarioId,
|
|
6013
|
+
name: spec.name,
|
|
6014
|
+
...spec.description ? { description: spec.description } : {},
|
|
6015
|
+
given,
|
|
6016
|
+
when,
|
|
6017
|
+
then
|
|
6018
|
+
}
|
|
6019
|
+
}));
|
|
6020
|
+
const entryCount = given.length + when.length + then.length;
|
|
6021
|
+
return { name: spec.name, entryCount };
|
|
6022
|
+
}
|
|
5825
6023
|
function registerAddCommands(program) {
|
|
5826
6024
|
const add = program.command("add").description("Add fields or scenarios");
|
|
5827
6025
|
add.command("field [elementName] <json>").description("Add a field to an element").action(async (elementNameOrJson, jsonOrUndefined, cmd) => {
|
|
@@ -5914,7 +6112,7 @@ element name doesn't resolve, the whole spec is rejected before hitting the
|
|
|
5914
6112
|
backend.
|
|
5915
6113
|
|
|
5916
6114
|
Example (primitives + Custom + list):
|
|
5917
|
-
|
|
6115
|
+
create scenario --slice "Place Order" '{
|
|
5918
6116
|
"name": "happy path",
|
|
5919
6117
|
"when": [{ "entryType": "command", "elementName": "PlaceOrder",
|
|
5920
6118
|
"fieldValues": { "orderId": "uuid-1",
|
|
@@ -5925,58 +6123,8 @@ Example (primitives + Custom + list):
|
|
|
5925
6123
|
{ "sku": "sku-2", "qty": "1" } ] } }]
|
|
5926
6124
|
}'
|
|
5927
6125
|
`).action(async (json, opts, cmd) => {
|
|
5928
|
-
const
|
|
5929
|
-
|
|
5930
|
-
const spec = JSON.parse(json);
|
|
5931
|
-
const scenarioId = randomUUID();
|
|
5932
|
-
const buildEntries = async (section, entries) => {
|
|
5933
|
-
if (!entries)
|
|
5934
|
-
return [];
|
|
5935
|
-
const dispatch = ENTRY_DISPATCH[section];
|
|
5936
|
-
const out2 = [];
|
|
5937
|
-
for (const entry of entries) {
|
|
5938
|
-
const info = dispatch[entry.entryType];
|
|
5939
|
-
if (!info) {
|
|
5940
|
-
throw new Error(`Unknown ${section} entry type: ${entry.entryType}`);
|
|
5941
|
-
}
|
|
5942
|
-
if (entry.entryType === "error") {
|
|
5943
|
-
out2.push({
|
|
5944
|
-
entryType: "error",
|
|
5945
|
-
errorType: entry.errorType ?? "",
|
|
5946
|
-
errorMessage: entry.errorMessage ?? "",
|
|
5947
|
-
fieldValues: {}
|
|
5948
|
-
});
|
|
5949
|
-
} else {
|
|
5950
|
-
if (!entry.elementName) {
|
|
5951
|
-
throw new Error(`${section} ${entry.entryType} entry is missing elementName`);
|
|
5952
|
-
}
|
|
5953
|
-
const elementId = await resolve2(modelId, info.resolveType, entry.elementName);
|
|
5954
|
-
out2.push({
|
|
5955
|
-
entryType: entry.entryType,
|
|
5956
|
-
elementId,
|
|
5957
|
-
fieldValues: entry.fieldValues ?? {}
|
|
5958
|
-
});
|
|
5959
|
-
}
|
|
5960
|
-
}
|
|
5961
|
-
return out2;
|
|
5962
|
-
};
|
|
5963
|
-
const given = await buildEntries("given", spec.given);
|
|
5964
|
-
const when = await buildEntries("when", spec.when);
|
|
5965
|
-
const then = await buildEntries("then", spec.then);
|
|
5966
|
-
unwrap(await createScenario({
|
|
5967
|
-
body: {
|
|
5968
|
-
modelId,
|
|
5969
|
-
sliceId,
|
|
5970
|
-
scenarioId,
|
|
5971
|
-
name: spec.name,
|
|
5972
|
-
...spec.description ? { description: spec.description } : {},
|
|
5973
|
-
given,
|
|
5974
|
-
when,
|
|
5975
|
-
then
|
|
5976
|
-
}
|
|
5977
|
-
}));
|
|
5978
|
-
const entryCount = given.length + when.length + then.length;
|
|
5979
|
-
console.log(`Added scenario "${spec.name}"${entryCount > 0 ? ` with ${entryCount} entries` : ""}.`);
|
|
6126
|
+
const result = await createScenarioFromJson(json, opts.slice);
|
|
6127
|
+
console.log(`Created scenario "${result.name}"${result.entryCount > 0 ? ` with ${result.entryCount} entries` : ""}.`);
|
|
5980
6128
|
});
|
|
5981
6129
|
add.command("entry").description("Add a GWT entry to a scenario").requiredOption("--scenario <name>", "Scenario name").requiredOption("--section <section>", "Section: given, when, or then").requiredOption("--type <type>", "Entry type: event, command, readmodel, or error").option("--element <name>", "Element name (required unless --type error)").option("--error-type <type>", "Error type (for --type error)").option("--error-message <msg>", "Error message (for --type error)").option("--position <n>", "Position within section (appends at end by default)", parseInt).action(async (opts) => {
|
|
5982
6130
|
const modelId = requireModelId();
|
|
@@ -6194,8 +6342,62 @@ function registerMoveCommands(program) {
|
|
|
6194
6342
|
});
|
|
6195
6343
|
}
|
|
6196
6344
|
|
|
6345
|
+
// src/commands/resize.ts
|
|
6346
|
+
var RESIZE_TABLE = {
|
|
6347
|
+
slice: { resize: resizeSlice, list: slices, idKey: "sliceId" },
|
|
6348
|
+
swimlane: { resize: resizeSwimLane, list: swimLanes, idKey: "swimLaneId" },
|
|
6349
|
+
context: { resize: resizeContext, list: contexts, idKey: "contextId" },
|
|
6350
|
+
chapter: { resize: resizeChapter, list: chapters, idKey: "chapterId" },
|
|
6351
|
+
aggregate: { resize: resizeAggregate, list: aggregates, idKey: "aggregateId" },
|
|
6352
|
+
actor: { resize: resizeActor, list: actors, idKey: "actorId" }
|
|
6353
|
+
};
|
|
6354
|
+
function registerResizeCommands(program) {
|
|
6355
|
+
program.command("resize <type> [name]").description(`Resize an element. Types: ${Object.keys(RESIZE_TABLE).join(", ")}`).requiredOption("--width <n>", "New width", Number).requiredOption("--height <n>", "New height", Number).option("--x <n>", "Override X coordinate (defaults to current)", Number).option("--y <n>", "Override Y coordinate (defaults to current)", Number).action(async (type, name, opts) => {
|
|
6356
|
+
const entry = RESIZE_TABLE[type];
|
|
6357
|
+
if (!entry) {
|
|
6358
|
+
throw new Error(`Unknown type: ${type}. Valid types: ${Object.keys(RESIZE_TABLE).join(", ")}`);
|
|
6359
|
+
}
|
|
6360
|
+
const modelId = requireModelId();
|
|
6361
|
+
const id = await resolve2(modelId, type, name ?? "", getGlobalId());
|
|
6362
|
+
let x = opts.x;
|
|
6363
|
+
let y = opts.y;
|
|
6364
|
+
if (x === undefined || y === undefined) {
|
|
6365
|
+
const list = unwrap(await entry.list({ path: { modelId } }));
|
|
6366
|
+
const current = list.items.find((item) => String(item[entry.idKey]) === id);
|
|
6367
|
+
if (!current) {
|
|
6368
|
+
throw new Error(`${type} ${name ?? id} not found in current model state`);
|
|
6369
|
+
}
|
|
6370
|
+
if (x === undefined)
|
|
6371
|
+
x = Number(current.x);
|
|
6372
|
+
if (y === undefined)
|
|
6373
|
+
y = Number(current.y);
|
|
6374
|
+
}
|
|
6375
|
+
unwrap(await entry.resize({
|
|
6376
|
+
body: { modelId, [entry.idKey]: id, x, y, width: opts.width, height: opts.height }
|
|
6377
|
+
}));
|
|
6378
|
+
console.log(`Resized ${type} to ${opts.width}x${opts.height} at (${x}, ${y}).`);
|
|
6379
|
+
});
|
|
6380
|
+
}
|
|
6381
|
+
|
|
6197
6382
|
// src/commands/create.ts
|
|
6198
6383
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
6384
|
+
function normalizeSliceFields(fields) {
|
|
6385
|
+
return (fields ?? []).map((field) => ({
|
|
6386
|
+
...field,
|
|
6387
|
+
id: field.id ?? randomUUID2(),
|
|
6388
|
+
type: field.type ?? field.fieldType,
|
|
6389
|
+
subfields: normalizeSliceFields(field.subfields)
|
|
6390
|
+
}));
|
|
6391
|
+
}
|
|
6392
|
+
function normalizeSliceSpec(spec) {
|
|
6393
|
+
return {
|
|
6394
|
+
...spec,
|
|
6395
|
+
elements: (spec.elements ?? []).map((element) => ({
|
|
6396
|
+
...element,
|
|
6397
|
+
fields: normalizeSliceFields(element.fields)
|
|
6398
|
+
}))
|
|
6399
|
+
};
|
|
6400
|
+
}
|
|
6199
6401
|
var FLOW_DISPATCH = {
|
|
6200
6402
|
"screen->command": { fn: specifyScreenToCommandFlow, sourceHandle: "bottom-source", targetHandle: "top-target" },
|
|
6201
6403
|
"command->event": { fn: specifyCommandToEventFlow, sourceHandle: "bottom-source", targetHandle: "top-target" },
|
|
@@ -6251,9 +6453,14 @@ function registerCreateCommands(program) {
|
|
|
6251
6453
|
create.command("slice <json>").description("Create a full slice from JSON specification").action(async (json) => {
|
|
6252
6454
|
const modelId = requireModelId();
|
|
6253
6455
|
const spec = JSON.parse(json);
|
|
6254
|
-
|
|
6456
|
+
const body = { modelId, sliceId: randomUUID2(), ...normalizeSliceSpec(spec) };
|
|
6457
|
+
unwrap(await createSlice({ body }));
|
|
6255
6458
|
console.log("Created slice.");
|
|
6256
6459
|
});
|
|
6460
|
+
create.command("scenario <json>").description("Atomically create a full Given/When/Then scenario").requiredOption("--slice <name>", "Slice name").action(async (json, opts) => {
|
|
6461
|
+
const result = await createScenarioFromJson(json, opts.slice);
|
|
6462
|
+
console.log(`Created scenario "${result.name}"${result.entryCount > 0 ? ` with ${result.entryCount} entries` : ""}.`);
|
|
6463
|
+
});
|
|
6257
6464
|
create.command("flow").description("Create a flow between two elements").requiredOption("--from <source>", "Source element name").requiredOption("--to <target>", "Target element name").option("--source-handle <handle>", "Source handle ID (default: auto based on flow type)").option("--target-handle <handle>", "Target handle ID (default: auto based on flow type)").action(async (opts) => {
|
|
6258
6465
|
const modelId = requireModelId();
|
|
6259
6466
|
const source = await resolveAnyElement(modelId, opts.from);
|
|
@@ -7021,6 +7228,7 @@ registerAddCommands(program);
|
|
|
7021
7228
|
registerRemoveCommands(program);
|
|
7022
7229
|
registerRenameCommands(program);
|
|
7023
7230
|
registerMoveCommands(program);
|
|
7231
|
+
registerResizeCommands(program);
|
|
7024
7232
|
registerCreateCommands(program);
|
|
7025
7233
|
registerUpdateCommands(program);
|
|
7026
7234
|
registerMapCommands(program);
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eventmodeler",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "CLI tool for event modeling - explore, design, and generate code from your event models",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/theoema/event-modeler.git"
|
|
9
|
+
},
|
|
6
10
|
"bin": {
|
|
7
11
|
"eventmodeler": "./dist/index.js"
|
|
8
12
|
},
|