create-substrat 0.4.0 → 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/README.md CHANGED
@@ -1,25 +1,59 @@
1
1
  # create-substrat
2
2
 
3
- Reserves `npm create substrat` for the Substrat initializer.
3
+ Scaffold a [Substrat](https://substrat.net) vertical.
4
4
 
5
- **The initializer is not released yet.** This package prints a pointer to the docs and
6
- exits. It does not scaffold anything, because a stub that pretended to would be worse than
7
- nothing.
5
+ ```sh
6
+ npm create substrat <dir>
7
+ npm create substrat . # scaffold into the current directory
8
+ ```
9
+
10
+ **Full documentation: https://substrat.net/reference/create-substrat**
11
+
12
+ ## What you get
13
+
14
+ A project that installs and runs, plus an **instruction layer** so your AI editor already
15
+ knows the rules and the build flow:
16
+
17
+ - `src/` — `manifest.ts`, `migrations.ts`, `module.ts`, `provision.ts`, `seed.ts`, a Node
18
+ `server.ts` for local work, and a `worker.ts` that mounts the platform surface via
19
+ [`@substrat-run/vertical-host`](https://npmjs.com/package/@substrat-run/vertical-host).
20
+ - `test/scenario.test.ts` — the scenario test the build flow grows.
21
+ - `AGENTS.md` + `.substrat/playbook.md` — the rules and the build flow, read by Claude Code,
22
+ Cursor, and opencode alike; each gets its own command stub.
23
+ - Generated `package.json`, `tsconfig.json`, `vitest.config.ts`, `.gitignore`, `README.md`,
24
+ including the `substrat` block that `substrat push` reads — so you never author wrangler
25
+ config.
8
26
 
9
- To start a vertical today, follow the [getting started guide](https://substrat.net/guide/getting-started).
10
- The packages are published and usable:
27
+ The scaffolder writes the skeleton; **the agent writes the vertical**, guided by the playbook.
11
28
 
12
29
  ```sh
13
- pnpm add @substrat-run/kernel @substrat-run/contracts @substrat-run/adapter-sqlite zod
30
+ cd <dir> && pnpm install
31
+ # then, in your AI editor:
32
+ # Claude Code: /substrat
33
+ # Cursor / opencode: the new-vertical command
14
34
  ```
15
35
 
36
+ ## Don't add `zod`
37
+
38
+ Substrat is on Zod 4, and Zod schemas do not compose across copies or majors — mixing two
39
+ makes `z.object({ facility: entityRef })` fail at runtime with `expected a Zod schema`,
40
+ pointing nowhere near the cause. Import `z` from contracts instead, and never install zod:
41
+
42
+ ```ts
43
+ import { z, entityRef, money } from '@substrat-run/contracts';
44
+ ```
45
+
46
+ ## Dependency-free by design
47
+
48
+ Node built-ins only, no build step. It can never break at install time and damage the name it
49
+ exists to protect.
50
+
16
51
  ## Note on the name
17
52
 
18
53
  `substrat` on npm is an **unrelated** package — an HTML5 build system published in 2013.
19
- Substrat's own packages are all under the [`@substrat-run`](https://www.npmjs.com/org/substrat-run)
20
- scope. Never `npm install substrat`.
54
+ Substrat's own packages are all under the
55
+ [`@substrat-run`](https://www.npmjs.com/org/substrat-run) scope. Never `npm install substrat`.
21
56
 
22
57
  ---
23
58
 
24
- [Docs](https://substrat.net) · [Repo](https://github.com/substrat-run/substrat) ·
25
- Apache-2.0
59
+ [Docs](https://substrat.net) · [Repo](https://github.com/substrat-run/substrat) · Apache-2.0
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
- // 0.45.0 is the release that ships `@substrat-run/vertical-host` (#510) — the
24
- // mountPlatformSurface the template's worker mounts — so this pin and that release
25
- // move together (it also covers `defineScopeSweeperDO`, #461, from 0.40.0).
26
- const SUBSTRAT = '^0.45.0';
27
- // Engines version on their own line (0.3.x), independent of the kernel/contracts line.
28
- const ENGINES = '^0.3.37';
29
- const BOUNDARY_LINT = '^0.0.5';
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': ENGINES,
99
- '@substrat-run/engine-invoicing': ENGINES,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-substrat",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Scaffold a Substrat vertical — `npm create substrat <dir>`.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -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 — Reshape the reference
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 6 — Run it
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 7 — The two checkpoints. STOP HERE.
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 8 — Deploy (optional)
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 9 — Leave the project competent
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