@eventmodelers/node-kit 0.0.11 → 0.0.12
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/package.json +1 -1
- package/templates/.claude/skills/build-automation/SKILL.md +260 -0
- package/templates/.claude/skills/build-state-change/SKILL.md +329 -0
- package/templates/.claude/skills/build-state-view/SKILL.md +384 -0
- package/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +609 -0
- package/templates/.claude/skills/load-slice/SKILL.md +69 -14
- package/templates/realtime-agent/src/index.js +11 -1
- package/templates/root/.env.example +22 -0
- package/templates/root/Claude.md +58 -0
- package/templates/root/agent.sh +15 -0
- package/templates/root/backend-prompt.md +139 -0
- package/templates/root/flyway.conf +17 -0
- package/templates/root/package.json +52 -0
- package/templates/root/ralph.sh +47 -26
- package/templates/root/server.ts +213 -0
- package/templates/root/setup-env.sh +55 -0
- package/templates/root/src/common/assertions.ts +6 -0
- package/templates/root/src/common/db.ts +32 -0
- package/templates/root/src/common/loadPostgresEventstore.ts +39 -0
- package/templates/root/src/common/parseEndpoint.ts +51 -0
- package/templates/root/src/common/processorDlq.ts +28 -0
- package/templates/root/src/common/realtimeBroadcast.ts +19 -0
- package/templates/root/src/common/replay.ts +16 -0
- package/templates/root/src/common/routes.ts +19 -0
- package/templates/root/src/common/testHelpers.ts +54 -0
- package/templates/root/src/slices/example/routes.ts +134 -0
- package/templates/root/src/supabase/LoginHandler.ts +36 -0
- package/templates/root/src/supabase/ProtectedPageProps.ts +21 -0
- package/templates/root/src/supabase/README.md +171 -0
- package/templates/root/src/supabase/api.ts +56 -0
- package/templates/root/src/supabase/component.ts +12 -0
- package/templates/root/src/supabase/requireOrgaAdmin.ts +32 -0
- package/templates/root/src/supabase/requireUser.ts +72 -0
- package/templates/root/src/supabase/serverProps.ts +25 -0
- package/templates/root/src/supabase/staticProps.ts +10 -0
- package/templates/root/src/swagger.ts +34 -0
- package/templates/root/src/util/assertions.ts +6 -0
- package/templates/root/src/util/hash.ts +9 -0
- package/templates/root/src/util/sanitize.ts +23 -0
- package/templates/root/supabase/config.toml +295 -0
- package/templates/root/supabase/migrations/V1__schema.sql.example +12 -0
- package/templates/root/supabase/seed.sql +1 -0
- package/templates/root/tsconfig.json +32 -0
- package/templates/root/vercel.json +8 -0
- package/templates/root/model.md +0 -1
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: learn-eventmodelers-api
|
|
3
|
+
description: Teaches an agent everything about the eventmodelers platform API — all endpoints, their purpose, request payloads, response shapes, authentication, and element types.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Eventmodelers Platform API Reference
|
|
7
|
+
|
|
8
|
+
You now have complete knowledge of the eventmodelers platform API. Use this reference whenever you need to call, implement, or reason about any endpoint.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Architecture Overview
|
|
13
|
+
|
|
14
|
+
- **Framework**: Express.js + `@event-driven-io/emmett` (event sourcing)
|
|
15
|
+
- **Adapter**: `@event-driven-io/emmett-expressjs`
|
|
16
|
+
- **Database**: PostgreSQL via Knex
|
|
17
|
+
- **Storage / Auth**: Supabase
|
|
18
|
+
- **Route discovery**: Dynamic glob (`**/routes{,-*}.js`) loaded from `dist/src/slices`
|
|
19
|
+
- **Base URL** (local): `http://localhost:3000`
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Authentication & Headers
|
|
24
|
+
|
|
25
|
+
| Header | Required | Purpose |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `Authorization` | Some routes | Supabase JWT bearer token |
|
|
28
|
+
| `x-user-id` | Node operations | User identifier |
|
|
29
|
+
| `x-causation-id` | Optional | Event causation tracing |
|
|
30
|
+
| `x-correlation-id` | Optional | Correlation tracing |
|
|
31
|
+
|
|
32
|
+
- CORS allowed origins: `localhost:3000`, `localhost:3001`, `https://app.eventmodelers.de`
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Element Types
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
MODEL_CONTEXT // Context/domain modeling container
|
|
40
|
+
CHAPTER // Timeline/sequence container
|
|
41
|
+
ACTOR // System participant (swimlane label)
|
|
42
|
+
AUTOMATION // Automated action
|
|
43
|
+
API // External service
|
|
44
|
+
SCREEN // UI screen
|
|
45
|
+
COMMAND // State-changing operation
|
|
46
|
+
EVENT // Domain event
|
|
47
|
+
SPEC_ERROR // Error scenario
|
|
48
|
+
TABLE // Data table
|
|
49
|
+
READMODEL // Query result / materialized view
|
|
50
|
+
SCENARIO // GWT scenario
|
|
51
|
+
LANE // Timeline row
|
|
52
|
+
SLICE_BORDER // Slice boundary marker
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Standard HTTP Status Codes
|
|
58
|
+
|
|
59
|
+
| Code | Meaning |
|
|
60
|
+
|---|---|
|
|
61
|
+
| 200 | OK with data |
|
|
62
|
+
| 201 | Created |
|
|
63
|
+
| 204 | No content |
|
|
64
|
+
| 400 | Validation error / bad input |
|
|
65
|
+
| 401 | Authentication required |
|
|
66
|
+
| 404 | Resource not found |
|
|
67
|
+
| 409 | Conflict (e.g. duplicate) |
|
|
68
|
+
| 500 | Server error |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 1. Boards
|
|
73
|
+
|
|
74
|
+
**File**: `src/slices/change/api-boards/routes.ts`
|
|
75
|
+
|
|
76
|
+
### POST `/api/org/:orgId/boards/:boardId/events`
|
|
77
|
+
Persist board/timeline row events as an array of mixed event types.
|
|
78
|
+
|
|
79
|
+
**Request body**: Array of node, comment, edge, or board events
|
|
80
|
+
**Response**: `200` — processed results array
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
### GET `/api/boards`
|
|
85
|
+
List all boards.
|
|
86
|
+
|
|
87
|
+
**Response**: `200` — `Board[]`
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
### DELETE `/api/org/:orgId/boards/:boardId`
|
|
92
|
+
Delete a board.
|
|
93
|
+
|
|
94
|
+
**Response**: `204`
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### GET `/api/org/:orgId/boards/:boardId/events/search`
|
|
99
|
+
Search events by node name.
|
|
100
|
+
|
|
101
|
+
**Query params**: `name` (string)
|
|
102
|
+
**Response**: `200` — matching event array
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
### GET `/api/org/:orgId/boards/:boardId/events`
|
|
107
|
+
Get all board events in sequence.
|
|
108
|
+
|
|
109
|
+
**Response**: `200` — event array
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### GET `/api/org/:orgId/boards/:boardId/nodes/:nodeId/comments`
|
|
114
|
+
Get all comments for a node.
|
|
115
|
+
|
|
116
|
+
**Response**: `200` — comment array
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### POST `/api/org/:orgId/boards/:boardId/bucket`
|
|
121
|
+
Create a Supabase storage bucket for the board.
|
|
122
|
+
|
|
123
|
+
**Response**: `200` — `{ ok: boolean, bucket: string, alreadyExisted: boolean }`
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 2. Chapters & Timelines
|
|
128
|
+
|
|
129
|
+
**File**: `src/slices/change/api-chapters/routes.ts`
|
|
130
|
+
|
|
131
|
+
### POST `/api/org/:orgId/boards/:boardId/chapters`
|
|
132
|
+
Create a chapter node.
|
|
133
|
+
|
|
134
|
+
**Request body**: `{ position?: { x: number, y: number } }`
|
|
135
|
+
**Response**: `200` — chapter data
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### POST `/api/org/:orgId/boards/:boardId/timelines/:timelineId/columns`
|
|
140
|
+
Add a column to a timeline.
|
|
141
|
+
|
|
142
|
+
**Request body**: `{ index?: number }` (integer index, optional)
|
|
143
|
+
**Response**: `200` — `{ columnId: string, index: number, totalColumns: number }`
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### DELETE `/api/org/:orgId/boards/:boardId/timelines/:timelineId/columns/:columnId`
|
|
148
|
+
Delete a column from a timeline. Removes the column and all its cells. Cannot delete the last column.
|
|
149
|
+
|
|
150
|
+
**Response**:
|
|
151
|
+
- `200` — `{ columnId: string, totalColumns: number }`
|
|
152
|
+
- `400` — validation error (e.g. last column)
|
|
153
|
+
- `404` — timeline or column not found
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
### POST `/api/org/:orgId/boards/:boardId/timelines/:timelineId/lanes`
|
|
158
|
+
Add a lane (row) to a timeline.
|
|
159
|
+
|
|
160
|
+
**Request body**:
|
|
161
|
+
```typescript
|
|
162
|
+
{
|
|
163
|
+
type: 'actor' | 'interaction' | 'swimlane' | 'spec' | 'feedback'
|
|
164
|
+
label?: string
|
|
165
|
+
index?: number
|
|
166
|
+
height?: number
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
**Response**: `200` — lane data
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### POST `/api/org/:orgId/boards/:boardId/timelines/:timelineId/cells/:cellId/drop`
|
|
174
|
+
Drop a node into a timeline cell. Validates placement rules.
|
|
175
|
+
|
|
176
|
+
**Request body**: `{ nodeId: string, nodeType: ElementType }`
|
|
177
|
+
|
|
178
|
+
**Placement rules**:
|
|
179
|
+
- `swimlane` lane → accepts `EVENT`
|
|
180
|
+
- `interaction` lane → accepts `COMMAND`, `READMODEL`
|
|
181
|
+
- `actor` lane → accepts `SCREEN`, `AUTOMATION`
|
|
182
|
+
- `feedback` lane → accepts markdown
|
|
183
|
+
- `spec` lane → accepts `SPEC_NODE`
|
|
184
|
+
|
|
185
|
+
**Response**:
|
|
186
|
+
- `200` — drop result
|
|
187
|
+
- `400` — placement violation
|
|
188
|
+
- `404` — cell or node not found
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 3. Nodes
|
|
193
|
+
|
|
194
|
+
**File**: `src/slices/change/api-nodes/routes.ts`
|
|
195
|
+
|
|
196
|
+
All node endpoints require header: `x-user-id`
|
|
197
|
+
|
|
198
|
+
### POST `/api/org/:orgId/boards/:boardId/nodes/events`
|
|
199
|
+
Submit node change events.
|
|
200
|
+
|
|
201
|
+
**Request body**: `NodeChangeEvent[]`
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
interface NodeChangeEvent {
|
|
205
|
+
id: string // uuid
|
|
206
|
+
eventType: 'node:created' | 'node:changed' | 'node:deleted'
|
|
207
|
+
nodeId: string
|
|
208
|
+
boardId: string
|
|
209
|
+
timestamp: number // unix ms
|
|
210
|
+
userId?: string
|
|
211
|
+
hash?: string // content hash
|
|
212
|
+
changedAttributes?: string[] // dot-paths e.g. 'meta.title'
|
|
213
|
+
node?: {
|
|
214
|
+
id: string
|
|
215
|
+
data: {
|
|
216
|
+
backgroundColor?: string
|
|
217
|
+
title?: string
|
|
218
|
+
type?: string
|
|
219
|
+
url?: string
|
|
220
|
+
// ...other node data fields
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
meta?: {
|
|
224
|
+
type: ElementType
|
|
225
|
+
title?: string
|
|
226
|
+
description?: string
|
|
227
|
+
fields?: Record<string, unknown>
|
|
228
|
+
// ...
|
|
229
|
+
}
|
|
230
|
+
edges?: Array<{
|
|
231
|
+
id: string
|
|
232
|
+
source: string
|
|
233
|
+
target: string
|
|
234
|
+
sourceHandle?: string
|
|
235
|
+
targetHandle?: string
|
|
236
|
+
}>
|
|
237
|
+
chapterId?: string // for cell placement
|
|
238
|
+
cellName?: string // spreadsheet-style e.g. "B2"
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Response**: `200` — `{ hashes: { [eventId: string]: string } }`
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
### GET `/api/org/:orgId/boards/:boardId/nodes`
|
|
247
|
+
List all nodes on a board.
|
|
248
|
+
|
|
249
|
+
**Query params**: `type?: ElementType`
|
|
250
|
+
**Response**: `200` — node record array
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### GET `/api/org/:orgId/boards/:boardId/nodes/:nodeId`
|
|
255
|
+
Get a single node.
|
|
256
|
+
|
|
257
|
+
**Response**: `200` — node record OR `404`
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 4. Images
|
|
262
|
+
|
|
263
|
+
**File**: `src/slices/change/api-images/routes.ts`
|
|
264
|
+
|
|
265
|
+
### POST `/api/org/:orgId/boards/:boardId/images/:imageId`
|
|
266
|
+
Update a board image.
|
|
267
|
+
|
|
268
|
+
**Request**: `multipart/form-data` — field `file` (binary)
|
|
269
|
+
**Response**: `204`
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### POST `/api/org/:orgId/boards/:boardId/imagesnapshots/:imageId`
|
|
274
|
+
Update an image snapshot.
|
|
275
|
+
|
|
276
|
+
**Request**: `multipart/form-data` — field `file` (binary)
|
|
277
|
+
**Response**: `204`
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
### POST `/api/org/:orgId/boards/:boardId/image-nodes/:nodeId`
|
|
282
|
+
Create an image node.
|
|
283
|
+
|
|
284
|
+
**Request**: `multipart/form-data` — fields: `file`, `chapterId`, `cellName`
|
|
285
|
+
**Response**: `204`
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
### POST `/api/org/:orgId/boards/:boardId/images/:imageId/sketch`
|
|
290
|
+
Render a sketch description to WebP and upload.
|
|
291
|
+
|
|
292
|
+
**Request body**:
|
|
293
|
+
```typescript
|
|
294
|
+
{
|
|
295
|
+
elements: object[] // sketch element descriptors
|
|
296
|
+
semanticDescription?: string // human-readable description stored in metadata
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
**Response**: `204`
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
### POST `/api/org/:orgId/boards/:boardId/image-nodes/:nodeId/sketch`
|
|
304
|
+
Create a SCREEN node from a sketch description.
|
|
305
|
+
|
|
306
|
+
**Request body**:
|
|
307
|
+
```typescript
|
|
308
|
+
{
|
|
309
|
+
chapterId: string
|
|
310
|
+
cellName: string
|
|
311
|
+
description: { elements: object[] }
|
|
312
|
+
semanticDescription?: string
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
**Response**: `204` OR `400` (validation error)
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 5. Slices
|
|
320
|
+
|
|
321
|
+
**File**: `src/slices/change/api-slices/routes.ts`
|
|
322
|
+
|
|
323
|
+
### POST `/api/org/:orgId/boards/:boardId/timelines/:timelineId/slices`
|
|
324
|
+
Create a complete slice (1 column + 3 nodes automatically placed).
|
|
325
|
+
|
|
326
|
+
**Request body**:
|
|
327
|
+
```typescript
|
|
328
|
+
{
|
|
329
|
+
type: 'state-change' | 'state-view' | 'automation'
|
|
330
|
+
index?: number
|
|
331
|
+
nodes?: {
|
|
332
|
+
actor?: Partial<NodeData>
|
|
333
|
+
interaction?: Partial<NodeData>
|
|
334
|
+
swimlane?: Partial<NodeData>
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**Slice node mapping**:
|
|
340
|
+
- `state-change` → SCREEN (actor) + COMMAND (interaction) + EVENT (swimlane)
|
|
341
|
+
- `state-view` → SCREEN (actor) + READMODEL (interaction) + EVENT (swimlane)
|
|
342
|
+
- `automation` → AUTOMATION (actor) + COMMAND (interaction) + EVENT (swimlane)
|
|
343
|
+
|
|
344
|
+
**Response**: `200` — slice data
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 6. Specifications (GWT Scenarios)
|
|
349
|
+
|
|
350
|
+
**File**: `src/slices/change/api-specs/routes.ts`
|
|
351
|
+
|
|
352
|
+
### POST `/api/org/:orgId/boards/:boardId/contexts/:contextName/slices/:sliceName/scenarios`
|
|
353
|
+
Append a Given-When-Then scenario to a spec node.
|
|
354
|
+
|
|
355
|
+
**Request body**:
|
|
356
|
+
```typescript
|
|
357
|
+
{
|
|
358
|
+
id: string
|
|
359
|
+
title: string
|
|
360
|
+
vertical?: boolean
|
|
361
|
+
examples?: unknown[]
|
|
362
|
+
given: string[] // nodeIds — must be EVENTs from same timeline
|
|
363
|
+
when: string[] // nodeIds — at most one COMMAND; empty if then has READMODEL
|
|
364
|
+
then: string[] // nodeIds — EVENTs only OR exactly one READMODEL (not mixed)
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
**Validation rules**:
|
|
369
|
+
- `given`: only EVENTs from same timeline
|
|
370
|
+
- `when`: max one COMMAND; must be empty when `then` contains a READMODEL
|
|
371
|
+
- `then`: all EVENTs OR exactly one READMODEL — never mixed
|
|
372
|
+
- All referenced nodes must belong to the same chapter/timeline
|
|
373
|
+
|
|
374
|
+
**Response**:
|
|
375
|
+
- `201` — `{ scenario, scenarios, specNodeId, isNewNode: boolean }`
|
|
376
|
+
- `400` — validation error
|
|
377
|
+
- `404` — context or slice not found
|
|
378
|
+
- `409` — duplicate scenario title
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
### GET `/api/org/:orgId/boards/:boardId/contexts/:contextName/spec-info`
|
|
383
|
+
Get valid elements for a context (by name lookup).
|
|
384
|
+
|
|
385
|
+
**Response**: `200` — `{ chapterId: string, elements: ElementRecord[] }`
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
### GET `/api/org/:orgId/boards/:boardId/contexts/:contextName/slices/:sliceName/spec-info`
|
|
390
|
+
Get valid elements for a specific slice.
|
|
391
|
+
|
|
392
|
+
**Response**: `200` — `{ chapterId: string, elements: ElementRecord[] }`
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## 7. Config Import
|
|
397
|
+
|
|
398
|
+
**File**: `src/slices/change/config-import/routes.ts`
|
|
399
|
+
|
|
400
|
+
### POST `/api/org/:orgId/boards/:boardId/import-config`
|
|
401
|
+
Import an EventModelingJson config to populate a board.
|
|
402
|
+
|
|
403
|
+
**Request**: `multipart/form-data` with field `file` OR `application/json` body:
|
|
404
|
+
```typescript
|
|
405
|
+
{ slices: SliceDefinition[] }
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**Response**: `200` — transformed canvas with nodes and edges
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## 8. Slice Data
|
|
413
|
+
|
|
414
|
+
**File**: `src/slices/slicedata/routes.ts`
|
|
415
|
+
|
|
416
|
+
### GET ` `
|
|
417
|
+
Build structured slice data from board state.
|
|
418
|
+
|
|
419
|
+
**Query params** (one required): `contextId` OR `contextName`; optional: `sliceId`
|
|
420
|
+
**Response**: `200` — slice data matching event modeling schema
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
### GET `/api/org/:orgId/boards/:boardId/slicedata/slices`
|
|
425
|
+
List all slices on a board.
|
|
426
|
+
|
|
427
|
+
**Response**: `200` — `{ slices: Array<{ id: string, title: string, status: string }> }`
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## 9. Extensions
|
|
432
|
+
|
|
433
|
+
**File**: `src/slices/extensions/routes.ts`
|
|
434
|
+
|
|
435
|
+
### GET `/api/org/:orgId/boards/:boardId/extensions`
|
|
436
|
+
List extension configs for a board.
|
|
437
|
+
|
|
438
|
+
**Response**: `200` — extension record array
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
### PUT `/api/org/:orgId/boards/:boardId/extensions/:type`
|
|
443
|
+
Enable or disable an extension.
|
|
444
|
+
|
|
445
|
+
**Request body**: `{ enabled: boolean, config?: object }`
|
|
446
|
+
**Response**: `200` — updated extension config
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## 10. Snapshots
|
|
451
|
+
|
|
452
|
+
**File**: `src/slices/Snapshots/routes.ts`
|
|
453
|
+
|
|
454
|
+
All snapshot endpoints require Supabase JWT authentication.
|
|
455
|
+
|
|
456
|
+
**Constraints**: max 3 snapshots per user, max 30-day retention, max 50 MB file size.
|
|
457
|
+
|
|
458
|
+
### GET `/api/snapshots`
|
|
459
|
+
List current user's snapshots.
|
|
460
|
+
|
|
461
|
+
**Response**: `200` — `Array<{ id, name, payload_id, expiry, shared }>`
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
### POST `/api/snapshots`
|
|
466
|
+
Create a snapshot.
|
|
467
|
+
|
|
468
|
+
**Request**: `multipart/form-data` — fields: `payloadFile` (binary), `name` (string), `retention?` (days, max 30)
|
|
469
|
+
**Response**: `201` — `{ ok: true, id: string }`
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
### GET `/api/snapshots/:id`
|
|
474
|
+
Load a snapshot's payload.
|
|
475
|
+
|
|
476
|
+
**Response**: `200` — snapshot payload JSON
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
### PATCH `/api/snapshots/:id/share`
|
|
481
|
+
Share a snapshot (makes it publicly accessible).
|
|
482
|
+
|
|
483
|
+
**Response**: `200` — `{ ok: true }`
|
|
484
|
+
|
|
485
|
+
---
|
|
486
|
+
|
|
487
|
+
### DELETE `/api/snapshots/:id`
|
|
488
|
+
Delete a snapshot.
|
|
489
|
+
|
|
490
|
+
**Response**: `200` — `{ ok: true }`
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## 11. User Management — Commands (Event Sourced)
|
|
495
|
+
|
|
496
|
+
All commands respond with:
|
|
497
|
+
```typescript
|
|
498
|
+
{
|
|
499
|
+
ok: true
|
|
500
|
+
next_expected_stream_version: number
|
|
501
|
+
last_event_global_position: number
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
Optional headers on all: `correlation_id`, `causation_id`
|
|
506
|
+
|
|
507
|
+
### POST `/api/creategroup`
|
|
508
|
+
**Body**: `{ groupId: string, name: string }`
|
|
509
|
+
**Event emitted**: `GroupCreated`
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
### POST `/api/inviteuser`
|
|
514
|
+
**Body**: `{ groupId: string, email: string, invitationId: string }`
|
|
515
|
+
**Event emitted**: `UserInvited`
|
|
516
|
+
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
### POST `/api/acceptinvite`
|
|
520
|
+
**Body**: `{ userId: string, groupId: string, invitationId: string }`
|
|
521
|
+
**Event emitted**: `InvitationAccepted`
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
### POST `/api/assignrole`
|
|
526
|
+
**Body**: `{ userId: string, groupId: string, role: string }`
|
|
527
|
+
**Event emitted**: `RoleAssigned`
|
|
528
|
+
|
|
529
|
+
---
|
|
530
|
+
|
|
531
|
+
## 12. User Management — Read Models (Projections)
|
|
532
|
+
|
|
533
|
+
All require authentication. Optional query param `_id` to filter by ID.
|
|
534
|
+
|
|
535
|
+
### GET `/api/query/group-details-lookup`
|
|
536
|
+
Group details. Filter: `?_id=groupId`
|
|
537
|
+
|
|
538
|
+
### GET `/api/query/open-invites`
|
|
539
|
+
Pending invitations. Filter: `?_id=invitationId`
|
|
540
|
+
|
|
541
|
+
### GET `/api/query/user-group-assignments`
|
|
542
|
+
User-to-group mappings. Filter: `?_id=groupId`
|
|
543
|
+
|
|
544
|
+
### GET `/api/query/users-to-assign-to-groups`
|
|
545
|
+
Users available for group assignment. Filter: `?_id=userId`
|
|
546
|
+
|
|
547
|
+
---
|
|
548
|
+
|
|
549
|
+
## 13. Utility
|
|
550
|
+
|
|
551
|
+
### GET `/api/user`
|
|
552
|
+
Get current authenticated user info.
|
|
553
|
+
|
|
554
|
+
**Response**: `{ user_id: string, email: string, metadata: object }`
|
|
555
|
+
|
|
556
|
+
### GET `/api-docs`
|
|
557
|
+
Swagger UI (interactive API explorer)
|
|
558
|
+
|
|
559
|
+
### GET `/swagger.json`
|
|
560
|
+
OpenAPI specification (JSON)
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
564
|
+
## Domain Events
|
|
565
|
+
|
|
566
|
+
### Snapshot Events (`src/events/SnapshotsEvents.ts`)
|
|
567
|
+
|
|
568
|
+
```typescript
|
|
569
|
+
SnapshotStored // { name, id, payloadId, expiry }
|
|
570
|
+
SnapshotDeleted // { id }
|
|
571
|
+
SnapshotCleanedUp // { id }
|
|
572
|
+
PublishedSnapshotDeleted // { id }
|
|
573
|
+
SnapshotShared // { id }
|
|
574
|
+
SnapshotPublished // { id, payloadId, bucket, path }
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### User Management Events (`src/events/UserManagementEvents.ts`)
|
|
578
|
+
|
|
579
|
+
```typescript
|
|
580
|
+
GroupCreated // { groupId, owner, name }
|
|
581
|
+
UserAssignedToGroup // { groupId, userId }
|
|
582
|
+
UserInvited // { groupId, invitationId, email }
|
|
583
|
+
InvitationAccepted // { invitationId, groupId, userId }
|
|
584
|
+
RoleAssigned // { groupId, userId, role }
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
All events support optional metadata: `user_id`, `correlation_id`, `causation_id`
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
## Key Source Files
|
|
592
|
+
|
|
593
|
+
| File | Purpose |
|
|
594
|
+
|---|---|
|
|
595
|
+
| `src/slices/change/types.ts` | `ElementType`, `NodeChangeEvent`, `EdgeEvent` |
|
|
596
|
+
| `src/slices/change/api-boards/routes.ts` | Board CRUD + event persistence |
|
|
597
|
+
| `src/slices/change/api-chapters/routes.ts` | Chapters, columns, lanes, cell drops |
|
|
598
|
+
| `src/slices/change/api-nodes/routes.ts` | Node event sourcing |
|
|
599
|
+
| `src/slices/change/api-images/routes.ts` | Image upload + sketch rendering |
|
|
600
|
+
| `src/slices/change/api-slices/routes.ts` | Slice creation |
|
|
601
|
+
| `src/slices/change/api-specs/routes.ts` | GWT scenario management |
|
|
602
|
+
| `src/slices/change/config-import/routes.ts` | Config import |
|
|
603
|
+
| `src/slices/slicedata/routes.ts` | Slice data read models |
|
|
604
|
+
| `src/slices/extensions/routes.ts` | Extension management |
|
|
605
|
+
| `src/slices/Snapshots/routes.ts` | Snapshot CRUD |
|
|
606
|
+
| `src/slices/usermanagement/*/routes*.ts` | User management commands + projections |
|
|
607
|
+
| `src/events/SnapshotsEvents.ts` | Snapshot domain events |
|
|
608
|
+
| `src/events/UserManagementEvents.ts` | User management domain events |
|
|
609
|
+
| `backend/src/server.ts` | Route wiring, CORS, `/api/user` |
|