create-seiro 0.1.7 → 0.1.9

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-seiro",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Scaffold a new Seiro project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -130,6 +130,21 @@ export type Site = {
130
130
  - No UML diagrams
131
131
  - No abstract entity modelling before documents are clear
132
132
 
133
+ ## Alternative: seiro model
134
+
135
+ For larger systems where you want to visualise the complete domain (entities, documents, commands, events, sequences), use `seiro model`:
136
+
137
+ ```bash
138
+ bunx seiro model add entity Product
139
+ bunx seiro model add document Catalogue
140
+ bunx seiro model serve # view diagrams
141
+ bunx seiro model export # get JSON for code generation
142
+ ```
143
+
144
+ Then use the `model-generate` skill to create the application from the export.
145
+
146
+ Use this skill (cqrs-document) for conversational design. Use seiro model when you need the full picture and consistency checking across a larger domain.
147
+
133
148
  ## Iteration Pattern
134
149
 
135
150
  When requirements change:
@@ -87,6 +87,7 @@ await server.start({ "/": homepage });
87
87
  ```typescript
88
88
  import type { Sql } from "postgres";
89
89
  import type { Server } from "seiro";
90
+ import { notifyLogger } from "seiro/server";
90
91
  import type { Entity, EntityCommands, EntityQueries, EntityEvents } from "./types";
91
92
 
92
93
  export async function register<
@@ -103,10 +104,10 @@ export async function register<
103
104
  try {
104
105
  server.emit("entity_created", JSON.parse(payload) as Entity);
105
106
  } catch (e) {
106
- console.error("Failed to parse entity_created payload:", payload, e);
107
+ notifyLogger.error("Failed to parse entity_created payload:", payload, e);
107
108
  }
108
109
  },
109
- () => console.log("Listening on entity_created"),
110
+ () => notifyLogger.info("Listening on entity_created"),
110
111
  );
111
112
 
112
113
  await listener.listen(
@@ -115,10 +116,10 @@ export async function register<
115
116
  try {
116
117
  server.emit("entity_updated", JSON.parse(payload) as Entity);
117
118
  } catch (e) {
118
- console.error("Failed to parse entity_updated payload:", payload, e);
119
+ notifyLogger.error("Failed to parse entity_updated payload:", payload, e);
119
120
  }
120
121
  },
121
- () => console.log("Listening on entity_updated"),
122
+ () => notifyLogger.info("Listening on entity_updated"),
122
123
  );
123
124
 
124
125
  // Different payload type for delete - just the id
@@ -128,10 +129,10 @@ export async function register<
128
129
  try {
129
130
  server.emit("entity_deleted", JSON.parse(payload) as { id: number });
130
131
  } catch (e) {
131
- console.error("Failed to parse entity_deleted payload:", payload, e);
132
+ notifyLogger.error("Failed to parse entity_deleted payload:", payload, e);
132
133
  }
133
134
  },
134
- () => console.log("Listening on entity_deleted"),
135
+ () => notifyLogger.info("Listening on entity_deleted"),
135
136
  );
136
137
  }
137
138
 
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: model-build
3
+ description: Build a CQRS domain model through conversation using seiro model CLI. Use when designing larger systems where you want diagrams, consistency checking, and a formal model before generating code. Captures entities, documents, commands, events, and their relationships in model.db.
4
+ ---
5
+
6
+ # Build Domain Model Through Conversation
7
+
8
+ Guide the user through building a complete CQRS domain model using the seiro model CLI. The model can be visualised with `bunx seiro model serve` and used to generate code with the `model-generate` skill.
9
+
10
+ ## Workflow
11
+
12
+ ### 1. Start with Documents
13
+
14
+ Ask: "What documents (views) does the user see?"
15
+
16
+ For each document, capture:
17
+ - Name and description
18
+ - What entities it contains
19
+ - Queries that build it
20
+
21
+ ```bash
22
+ bunx seiro model add document Catalogue "All products with parts and faces"
23
+ bunx seiro model add doc-entity Catalogue Product
24
+ bunx seiro model add doc-entity Catalogue Part
25
+ bunx seiro model add doc-entity Catalogue Face
26
+ bunx seiro model add query Catalogue products "SELECT id, name, spec, tag_ids FROM products"
27
+ ```
28
+
29
+ ### 2. Define Entities
30
+
31
+ For each entity in the documents:
32
+ - Name and description
33
+ - Attributes with types
34
+ - Relationships to other entities
35
+
36
+ ```bash
37
+ bunx seiro model add entity Product "A product in the catalogue"
38
+ bunx seiro model add attribute Product name string
39
+ bunx seiro model add attribute Product spec json
40
+ bunx seiro model add relationship Product tagIds Tag --many --reference
41
+ bunx seiro model add relationship Product parts Part --many
42
+ bunx seiro model add relationship Product faces Face --many
43
+ ```
44
+
45
+ Types: `string`, `integer`, `boolean`, `json`, `integer[]`
46
+ Options: `--nullable`, `--many`, `--reference`
47
+
48
+ ### 3. Define Commands for Each Document
49
+
50
+ For each entity in a document, determine what commands change its state. At minimum: save and delete.
51
+
52
+ ```bash
53
+ # Command with input type
54
+ bunx seiro model add entity ProductSaveCmd
55
+ bunx seiro model add attribute ProductSaveCmd id integer --nullable
56
+ bunx seiro model add attribute ProductSaveCmd name string
57
+ bunx seiro model add attribute ProductSaveCmd spec json
58
+ bunx seiro model add attribute ProductSaveCmd tagIds integer[]
59
+
60
+ bunx seiro model add command product.save --entity ProductSaveCmd
61
+ bunx seiro model add command-doc product.save Catalogue
62
+ ```
63
+
64
+ ### 4. Define Events for Each Command
65
+
66
+ Each command emits an event with payload describing the change:
67
+
68
+ ```bash
69
+ # Event with payload type
70
+ bunx seiro model add entity ProductSavedEvt
71
+ bunx seiro model add attribute ProductSavedEvt id integer
72
+ bunx seiro model add attribute ProductSavedEvt name string
73
+ bunx seiro model add attribute ProductSavedEvt spec json
74
+ bunx seiro model add attribute ProductSavedEvt tagIds integer[]
75
+
76
+ bunx seiro model add event product_saved product.save --entity ProductSavedEvt
77
+ ```
78
+
79
+ ### 5. Ensure Complete Coverage
80
+
81
+ For each entity in each document, verify:
82
+
83
+ | Entity | save command | delete command | save event | delete event |
84
+ |--------|--------------|----------------|------------|--------------|
85
+ | Product | product.save | product.delete | product_saved | product_deleted |
86
+ | Part | part.save | part.delete | part_saved | part_deleted |
87
+ | Face | face.save | face.delete | face_saved | face_deleted |
88
+ | Tag | tag.save | tag.delete | tag_saved | tag_deleted |
89
+
90
+ This is the minimum surface. Additional commands (e.g., `product.tag`) can be added for specific operations.
91
+
92
+ ### 6. Visualise and Verify
93
+
94
+ ```bash
95
+ bunx seiro model up # start PlantUML server
96
+ bunx seiro model serve # open browser to view diagrams
97
+ ```
98
+
99
+ Review:
100
+ - Entity diagram - are relationships correct?
101
+ - Document diagrams - do they contain the right entities?
102
+ - Command coverage - does each document have all needed commands?
103
+
104
+ ### 7. Export for Code Generation
105
+
106
+ ```bash
107
+ bunx seiro model export > model.json
108
+ ```
109
+
110
+ Then use the `model-generate` skill to create the application code.
111
+
112
+ ## CLI Reference
113
+
114
+ ```bash
115
+ # Entities
116
+ bunx seiro model add entity <name> [description]
117
+ bunx seiro model add attribute <entity> <name> <type> [--nullable]
118
+ bunx seiro model add relationship <from> <field> <to> [--many] [--reference]
119
+
120
+ # Documents
121
+ bunx seiro model add document <name> [description]
122
+ bunx seiro model add doc-entity <document> <entity>
123
+ bunx seiro model add query <document> <name> <sql>
124
+
125
+ # Commands & Events
126
+ bunx seiro model add command <name> [--entity <type>]
127
+ bunx seiro model add command-doc <command> <document>
128
+ bunx seiro model add event <name> <command> [--entity <type>]
129
+
130
+ # View
131
+ bunx seiro model list entities|documents|commands|events
132
+ bunx seiro model serve
133
+ bunx seiro model export
134
+ ```
135
+
136
+ ## Conversation Pattern
137
+
138
+ 1. "What does the user see?" → identify documents
139
+ 2. "What's in each document?" → identify entities and their attributes
140
+ 3. "How are they related?" → define relationships
141
+ 4. "What can change each document?" → define commands per entity
142
+ 5. "What does each change notify?" → define events
143
+ 6. "Is the surface complete?" → verify coverage matrix
144
+ 7. "Let's visualise" → run serve, review diagrams
145
+ 8. "Ready to generate" → export and use model-generate skill
@@ -0,0 +1,237 @@
1
+ ---
2
+ name: model-generate
3
+ description: Generate a seiro application from seiro model export. Use when you have a model.db and want to generate schema.sql, TypeScript types, and command handlers. Run `bunx seiro model export` to get the JSON input.
4
+ ---
5
+
6
+ # Generate Seiro Application from Model
7
+
8
+ Takes the JSON export from seiro model and generates a complete seiro application.
9
+
10
+ ## Usage
11
+
12
+ ```bash
13
+ # Get the model export
14
+ bunx seiro model export > model.json
15
+
16
+ # Or pipe directly
17
+ bunx seiro model export | # use in generation
18
+ ```
19
+
20
+ ## Input Format
21
+
22
+ The export JSON contains:
23
+
24
+ ```json
25
+ {
26
+ "entities": [
27
+ {
28
+ "name": "Product",
29
+ "attributes": [
30
+ { "name": "id", "type": "integer" },
31
+ { "name": "name", "type": "string" }
32
+ ],
33
+ "relationships": [
34
+ { "field": "tagIds", "to": "Tag", "many": true, "reference": true }
35
+ ]
36
+ }
37
+ ],
38
+ "documents": [
39
+ {
40
+ "name": "Catalogue",
41
+ "entities": ["Product", "Part", "Face"],
42
+ "queries": [
43
+ { "name": "products", "sql": "SELECT ..." }
44
+ ],
45
+ "commands": [
46
+ {
47
+ "name": "product.save",
48
+ "input": { "name": "ProductSaveCmd", "attributes": [...] },
49
+ "events": [
50
+ { "name": "product_saved", "payload": { "name": "ProductSavedEvt", "attributes": [...] } }
51
+ ]
52
+ }
53
+ ]
54
+ }
55
+ ]
56
+ }
57
+ ```
58
+
59
+ ## Output Files
60
+
61
+ ### 1. SQL Schema (`src/db/schema.sql`)
62
+
63
+ Generate tables from entities:
64
+
65
+ ```sql
66
+ CREATE TABLE IF NOT EXISTS products (
67
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
68
+ name TEXT NOT NULL,
69
+ spec TEXT NOT NULL,
70
+ tag_ids TEXT NOT NULL DEFAULT '[]'
71
+ );
72
+
73
+ CREATE TABLE IF NOT EXISTS parts (
74
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
75
+ product_id INTEGER NOT NULL REFERENCES products(id) ON DELETE CASCADE,
76
+ child_product_id INTEGER NOT NULL REFERENCES products(id),
77
+ quantity INTEGER NOT NULL
78
+ );
79
+ ```
80
+
81
+ Type mapping:
82
+ - `string` → `TEXT`
83
+ - `integer` → `INTEGER`
84
+ - `json` → `TEXT` (stored as JSON string)
85
+ - `integer[]` → `TEXT` (stored as JSON array)
86
+ - `boolean` → `INTEGER` (0/1)
87
+
88
+ Relationships:
89
+ - `reference: false` (composition) → foreign key with `ON DELETE CASCADE`
90
+ - `reference: true` (association) → stored as `_ids` JSON array
91
+
92
+ ### 2. TypeScript Types (`src/types.ts`)
93
+
94
+ Generate from entities and command/event types:
95
+
96
+ ```typescript
97
+ // Domain entities
98
+ export type Product = {
99
+ id: number
100
+ name: string
101
+ spec: Record<string, unknown>
102
+ tagIds: number[]
103
+ }
104
+
105
+ // Command types
106
+ export type ProductSaveCmd = {
107
+ id?: number
108
+ name: string
109
+ spec: Record<string, unknown>
110
+ tagIds: number[]
111
+ }
112
+
113
+ // Event types
114
+ export type ProductSavedEvt = {
115
+ id: number
116
+ name: string
117
+ spec: Record<string, unknown>
118
+ tagIds: number[]
119
+ }
120
+
121
+ // Document types
122
+ export type CatalogueDocument = {
123
+ products: Product[]
124
+ parts: Part[]
125
+ faces: Face[]
126
+ }
127
+ ```
128
+
129
+ Type mapping:
130
+ - `string` → `string`
131
+ - `integer` → `number`
132
+ - `json` → `Record<string, unknown>`
133
+ - `integer[]` → `number[]`
134
+ - `boolean` → `boolean`
135
+ - `nullable: true` → `| null`
136
+
137
+ ### 3. Command Handlers (`src/commands.ts`)
138
+
139
+ Generate handler stubs for each command:
140
+
141
+ ```typescript
142
+ import type { CommandContext } from "seiro"
143
+ import type { Events, ProductSaveCmd } from "./types"
144
+ import { db } from "./db"
145
+
146
+ export async function productSave(
147
+ data: ProductSaveCmd,
148
+ ctx: CommandContext<Events>
149
+ ): Promise<{ id: number }> {
150
+ const { id, name, spec, tagIds } = data
151
+
152
+ if (id) {
153
+ // Update
154
+ db.run(
155
+ `UPDATE products SET name = ?, spec = ?, tag_ids = ? WHERE id = ?`,
156
+ [name, JSON.stringify(spec), JSON.stringify(tagIds), id]
157
+ )
158
+ } else {
159
+ // Insert
160
+ const result = db.run(
161
+ `INSERT INTO products (name, spec, tag_ids) VALUES (?, ?, ?)`,
162
+ [name, JSON.stringify(spec), JSON.stringify(tagIds)]
163
+ )
164
+ id = result.lastInsertRowid
165
+ }
166
+
167
+ ctx.send("product_saved", { id, name, spec, tagIds })
168
+ return { id }
169
+ }
170
+ ```
171
+
172
+ ### 4. Query Handlers (`src/queries.ts`)
173
+
174
+ Generate from document queries:
175
+
176
+ ```typescript
177
+ import { db } from "./db"
178
+ import type { Product, Part, Face } from "./types"
179
+
180
+ export async function* catalogueProducts(): AsyncIterable<Product> {
181
+ const rows = db.query(`SELECT id, name, spec, tag_ids FROM products`).all()
182
+ for (const row of rows) {
183
+ yield {
184
+ id: row.id,
185
+ name: row.name,
186
+ spec: JSON.parse(row.spec),
187
+ tagIds: JSON.parse(row.tag_ids)
188
+ }
189
+ }
190
+ }
191
+ ```
192
+
193
+ ### 5. Server Setup (`src/server.ts`)
194
+
195
+ Wire up commands and queries:
196
+
197
+ ```typescript
198
+ import { createServer } from "seiro"
199
+ import type { Commands, Queries, Events } from "./types"
200
+ import { productSave, productDelete, ... } from "./commands"
201
+ import { catalogueProducts, ... } from "./queries"
202
+
203
+ const server = createServer<Commands, Queries, Events>()
204
+
205
+ // Register commands
206
+ server.command("product.save", productSave)
207
+ server.command("product.delete", productDelete)
208
+ // ...
209
+
210
+ // Register queries
211
+ server.query("catalogue.products", catalogueProducts)
212
+ // ...
213
+
214
+ export { server }
215
+ ```
216
+
217
+ ## Conventions
218
+
219
+ Follow the same patterns as the `cqrs-document` skill:
220
+
221
+ - Commands are `entity.action` (e.g., `product.save`, `product.delete`)
222
+ - Events are `entity_actioned` (e.g., `product_saved`, `product_deleted`)
223
+ - Save commands handle both create (no id) and update (with id)
224
+ - Delete commands return void, emit event with just `{ id }`
225
+ - Queries yield rows for streaming
226
+ - JSON fields stored as TEXT, parsed on read
227
+
228
+ ## Workflow
229
+
230
+ 1. Run `bunx @seiro/model export` to get JSON
231
+ 2. Generate schema.sql from entities
232
+ 3. Generate types.ts from entities + command/event types
233
+ 4. Generate command handler stubs
234
+ 5. Generate query handlers from document queries
235
+ 6. Wire up in server.ts
236
+
237
+ The generated code is a starting point - handlers will need business logic added.
@@ -13,7 +13,40 @@ bun test # run tests
13
13
 
14
14
  ## Adding Features
15
15
 
16
- Use the `cqrs-document` skill for document-first CQRS design through conversation.
16
+ Two approaches for designing CQRS systems:
17
+
18
+ ### Quick Path: Conversational Design
19
+
20
+ Use the `cqrs-document` skill for direct conversation-to-code design. Good for smaller systems or rapid prototyping.
21
+
22
+ 1. Discuss what the user sees (documents)
23
+ 2. Derive entities and commands through conversation
24
+ 3. Output SQL and TypeScript directly
25
+
26
+ ### Model Path: Formal Domain Model
27
+
28
+ Use `seiro model` for larger systems where you want diagrams, consistency checking, and a complete view of the domain.
29
+
30
+ 1. Use the `model-build` skill to build model.db through conversation
31
+ ```bash
32
+ bunx seiro model add entity Product
33
+ bunx seiro model add document Catalogue
34
+ bunx seiro model add command product.save
35
+ # ... builds complete model via CLI
36
+ ```
37
+
38
+ 2. Visualise and verify
39
+ ```bash
40
+ bunx seiro model up # start PlantUML
41
+ bunx seiro model serve # view diagrams
42
+ ```
43
+
44
+ 3. Use the `model-generate` skill to create application code from the export
45
+ ```bash
46
+ bunx seiro model export # outputs JSON for code generation
47
+ ```
48
+
49
+ Choose the quick path when you know what you're building. Choose the model path when you need to see the full picture first.
17
50
 
18
51
  ## Type Patterns
19
52
 
@@ -10,7 +10,7 @@
10
10
  "test": "bun test server.test.ts"
11
11
  },
12
12
  "dependencies": {
13
- "seiro": "^0.1.7",
13
+ "seiro": "^0.1.9",
14
14
  "@preact/signals-core": "^1.12.2",
15
15
  "postgres": "^3.4.8"
16
16
  },