create-substrat 0.4.1 → 0.4.2
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/index.js +12 -9
- package/package.json +1 -1
- package/template/.substrat/playbook.md +60 -5
package/index.js
CHANGED
|
@@ -20,13 +20,16 @@ const HERE = dirname(fileURLToPath(import.meta.url));
|
|
|
20
20
|
const TEMPLATE = join(HERE, 'template');
|
|
21
21
|
|
|
22
22
|
// Published today; Substrat is 0.x, so these are caret ranges on the current minor.
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
// The runtime packages release together off one version line, so one constant is right
|
|
24
|
+
// for all of them.
|
|
25
|
+
const SUBSTRAT = '^0.71.0';
|
|
26
|
+
// Engines do NOT share a line — each one versions on its own, so a single ENGINES
|
|
27
|
+
// constant silently stops resolving the moment any engine crosses a minor. It did:
|
|
28
|
+
// this file pinned `^0.3.37` while workorder had moved to 0.4.x and invoicing to 0.6.x,
|
|
29
|
+
// so a freshly scaffolded project could not install. One pin per engine, deliberately.
|
|
30
|
+
const ENGINE_WORKORDER = '^0.4.3';
|
|
31
|
+
const ENGINE_INVOICING = '^0.6.2';
|
|
32
|
+
const BOUNDARY_LINT = '^0.0.7';
|
|
30
33
|
|
|
31
34
|
const DOCS = 'https://substrat.net';
|
|
32
35
|
|
|
@@ -95,8 +98,8 @@ function packageJson(name) {
|
|
|
95
98
|
'@substrat-run/adapter-sqlite': SUBSTRAT,
|
|
96
99
|
'@substrat-run/adapter-cloudflare': SUBSTRAT,
|
|
97
100
|
'@substrat-run/vertical-host': SUBSTRAT,
|
|
98
|
-
'@substrat-run/engine-workorder':
|
|
99
|
-
'@substrat-run/engine-invoicing':
|
|
101
|
+
'@substrat-run/engine-workorder': ENGINE_WORKORDER,
|
|
102
|
+
'@substrat-run/engine-invoicing': ENGINE_INVOICING,
|
|
100
103
|
hono: '^4.6.0',
|
|
101
104
|
'@hono/node-server': '^1.13.0',
|
|
102
105
|
'better-sqlite3': '^13.0.3',
|
package/package.json
CHANGED
|
@@ -222,7 +222,62 @@ Approval of the design is what unlocks Step 5. Until you have it, you are still
|
|
|
222
222
|
|
|
223
223
|
---
|
|
224
224
|
|
|
225
|
-
## Step 5 —
|
|
225
|
+
## Step 5 — Declare the model
|
|
226
|
+
|
|
227
|
+
The design is approved. Before reshaping any code, declare **what exists** in
|
|
228
|
+
`spec/model.ts`: entities, the operations over them, and the permissions those operations
|
|
229
|
+
check. One TypeScript module, and the compiler checks the joins between them.
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
import { defineEntities, defineOperations, emitModel } from '@substrat-run/contracts';
|
|
233
|
+
import { z } from '@substrat-run/contracts';
|
|
234
|
+
|
|
235
|
+
export const entities = defineEntities({
|
|
236
|
+
customer: {
|
|
237
|
+
table: 'acme_customers',
|
|
238
|
+
fields: z.object({ id: z.string(), number: z.string(), name: z.string() }),
|
|
239
|
+
key: ['number'],
|
|
240
|
+
erasable: ['name'],
|
|
241
|
+
},
|
|
242
|
+
site: { table: 'acme_sites', fields: z.object({ id: z.string(), customer_id: z.string() }), parents: ['customer'] },
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
export const PERMISSIONS = ['customer:manage'] as const;
|
|
246
|
+
|
|
247
|
+
export const operations = defineOperations(entities, PERMISSIONS)({
|
|
248
|
+
'acme/create-customer': {
|
|
249
|
+
summary: 'Register a customer',
|
|
250
|
+
permission: 'customer:manage',
|
|
251
|
+
input: z.object({ number: z.string(), name: z.string() }),
|
|
252
|
+
output: entities.customer.fields,
|
|
253
|
+
emits: { entity: 'customer', entityIdFrom: 'id', type: 'acme.customer-created', schemaVersion: 1, piiClass: 'none' },
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
export const model = emitModel(entities);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
These are compile errors, not lints: a `parents` naming no entity, a `permission` that is
|
|
261
|
+
not declared, an `entityIdFrom` naming no field of that operation's `output`, a `payload`
|
|
262
|
+
carrying a field the entity marks `erasable`, a `{var}` in an HTTP path that names no input
|
|
263
|
+
field. All before a handler exists.
|
|
264
|
+
|
|
265
|
+
Field names mirror the SQL columns, snake_case included — a prettier naming here is a second
|
|
266
|
+
description of the same rows. Not every table is an entity: an entity is something the
|
|
267
|
+
platform can point at (attachments hang off one, grants narrow to one, events are about one).
|
|
268
|
+
|
|
269
|
+
Behaviour stays prose in `DESIGN.md`. Inventing a way to declare a state *transition* means
|
|
270
|
+
the boundary slipped.
|
|
271
|
+
|
|
272
|
+
Full reference: https://substrat.net/concepts/model
|
|
273
|
+
|
|
274
|
+
**Do not edit `spec/model.ts` while reshaping the code.** If a handler cannot return what the
|
|
275
|
+
model declares, that is real information — say so and stop, rather than reshaping the model
|
|
276
|
+
to make the build pass.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Step 6 — Reshape the reference
|
|
226
281
|
|
|
227
282
|
The design is approved. The scaffold already contains a working vertical in `src/` + `test/` —
|
|
228
283
|
the bike-repair shop. **Read it first** (it's your Callout: the real, green implementation of
|
|
@@ -314,7 +369,7 @@ closed-door assertion with a control proving a neighbouring door is still open.
|
|
|
314
369
|
|
|
315
370
|
---
|
|
316
371
|
|
|
317
|
-
## Step
|
|
372
|
+
## Step 7 — Run it
|
|
318
373
|
|
|
319
374
|
Build confidence in this order, and **show the user the output of each**:
|
|
320
375
|
|
|
@@ -336,7 +391,7 @@ and typed wrappers over the routes. Ask first — it roughly doubles the work.
|
|
|
336
391
|
|
|
337
392
|
---
|
|
338
393
|
|
|
339
|
-
## Step
|
|
394
|
+
## Step 8 — The two checkpoints. STOP HERE.
|
|
340
395
|
|
|
341
396
|
**You may never self-approve these. Present them and wait.** The design gate (Step 4) already
|
|
342
397
|
took the user's approval of *what* to build; these confirm that the code matches it.
|
|
@@ -357,7 +412,7 @@ and who can see other tenants' data?* A permission diff nobody understands is th
|
|
|
357
412
|
|
|
358
413
|
---
|
|
359
414
|
|
|
360
|
-
## Step
|
|
415
|
+
## Step 9 — Deploy (optional)
|
|
361
416
|
|
|
362
417
|
Only if the user asks. Local-first is a legitimate stopping point.
|
|
363
418
|
|
|
@@ -389,7 +444,7 @@ cross-tenant hole with a UI.
|
|
|
389
444
|
|
|
390
445
|
---
|
|
391
446
|
|
|
392
|
-
## Step
|
|
447
|
+
## Step 10 — Leave the project competent
|
|
393
448
|
|
|
394
449
|
The next session — in any tool — starts cold. The scaffold already ships `AGENTS.md`,
|
|
395
450
|
`CLAUDE.md`, and the Cursor/opencode command stubs, so the rules and this flow survive. Your
|