create-substrat 0.6.0 → 0.6.1

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-substrat",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Scaffold a Substrat vertical — `npm create substrat <dir>`.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -190,10 +190,21 @@ plain language, so nothing there is a surprise:
190
190
  cross-tenant attacker gets nothing).
191
191
  9. **Open decisions** — each with a **recommended default**, so the user chooses rather
192
192
  than specifies:
193
- - **Auth.** Local dev uses an `x-principal` header — a dev seam, not a login. Default to
194
- it and note it must be replaced before anything real; offer to wire a real login (the
195
- bike-shop reference shows the Better Auth pattern) now if they want it. Real auth gates
196
- *exposing* the app, not *building* it.
193
+ - **Auth.** Local dev uses an `x-principal` header — a dev seam, not a login. Real auth
194
+ gates *exposing* the app, not *building* it — but if the app will be deployed for real
195
+ users, wire the OIDC seam from the start: the standard is a **separate OIDC issuer**
196
+ (an Auth Server app in the same team, or an external issuer — Supabase/Auth0/AuthHero/
197
+ Keycloak), never per-app credential storage. The vertical is a pure OIDC **relying
198
+ party**: depend on `@substrat-run/vertical-auth`, bind its `IdentityDO` (the
199
+ `sub → principal` directory, TOFU owner claim, invites) as a third DO store, read the
200
+ platform-delivered `substrat:auth` config per scope (`authWiring`), and build
201
+ `oidcRpAuthProvider` per request — see https://substrat.net/concepts/identity. The
202
+ dashboard's New-app **Identity** section does the automatic half (dynamic client
203
+ registration at the issuer + `/internal/configure` delivery) — but ONLY at app
204
+ creation, and only for verticals that implement this seam. An install created without
205
+ the identity choice stays `builtin`/unwired forever (switching issuers is deliberately
206
+ create-time only); a starter worker whose `authenticatedPrincipal` returns null answers
207
+ 401 to everything deployed, however many auth servers exist in the team.
197
208
  - **Deploy or stay local.** Local-first is a legitimate endpoint; default to it.
198
209
  10. **Out of scope / deferred** — what you are deliberately not building, so the review is
199
210
  about a bounded thing.
@@ -428,7 +439,9 @@ only in seed.ts would run locally and silently not deploy. The deploy path is th
428
439
  authenticated CLI, and the author never holds a Cloudflare token:
429
440
 
430
441
  - `substrat login` / `substrat whoami` — authenticate against the control plane.
431
- - `substrat push` — push the vertical; the version auto-bumps. A **private** (tenant-owned)
442
+ - `substrat push` — push the vertical. By default the version is the registry's highest
443
+ semver, patch-bumped; `package.json`'s version is only a **seed for the first push of a
444
+ new slug**, and an explicit `--version` always wins. A **private** (tenant-owned)
432
445
  vertical is admitted automatically; a **listed/shared** one waits for staff admission.
433
446
  - `substrat promote <slug> --channel dev|staging|prod --version … [--ack-permissions]
434
447
  [--ack-migrations]` — the owner promotes every channel, prod included, for their own
@@ -436,6 +449,50 @@ authenticated CLI, and the author never holds a Cloudflare token:
436
449
  - `substrat hostnames bind <slug> --surface <s> [--domain <d>]` — mint a live hostname, or
437
450
  record a custom domain pending DNS validation (`substrat hostnames verify`).
438
451
 
452
+ **A SPA ships as NATIVE assets — never inline it into the worker.** Declare it in
453
+ `runtimeNeeds` and `substrat push` builds, hashes, and uploads the directory to the
454
+ runtime's own asset store, served from the edge without invoking the worker:
455
+
456
+ ```jsonc
457
+ "substrat": {
458
+ "runtimeNeeds": {
459
+ "entry": "src/worker.ts",
460
+ "build": "npm --prefix app install && npm --prefix app run build",
461
+ "assets": {
462
+ "directory": "app/dist",
463
+ "notFoundHandling": "single-page-application", // deep client routes → index.html
464
+ "runWorkerFirst": ["/api/*", "/internal/*"] // only these reach the worker
465
+ }
466
+ }
467
+ }
468
+ ```
469
+
470
+ `build` runs before assets are collected, so the directory may be pure build output. Never
471
+ base64-inline a built `app/dist` into a generated worker module: it costs ~+33 % script
472
+ size and a worker invocation per image.
473
+
474
+ **Let changesets own the version, and pass it to push explicitly** — the default bump walks
475
+ the registry forward on its own, so `package.json` and the registry drift apart within a
476
+ few deploys. Set this up when the vertical first deploys:
477
+
478
+ ```sh
479
+ pnpm add -D @changesets/cli && npx changeset init
480
+ ```
481
+
482
+ Then in `.changeset/config.json` add `"privatePackages": { "version": true, "tag": false }`
483
+ (the vertical is a private package, never npm-published), make sure `pnpm-workspace.yaml`
484
+ lists `packages: ["."]` so changesets can see the root package, and add the scripts:
485
+
486
+ ```json
487
+ "changeset": "changeset",
488
+ "release": "pnpm test && pnpm typecheck && pnpm lint:boundaries && changeset version && substrat push --version $(node -p \"require('./package.json').version\") --promote prod"
489
+ ```
490
+
491
+ Read the version with `node -p` at that point in the script, not `$npm_package_version` —
492
+ the latter is captured before `changeset version` rewrites `package.json`, so it would push
493
+ the version you just replaced. Changesets needs a git repo with a commit on the base branch
494
+ (`git init -b main`) — a scaffold that isn't a repo yet must init before the first release.
495
+
439
496
  Updates deploy **in place** from one stable script — data carries forward, migrations run
440
497
  against prod data, backout is a time-boxed PITR rewind.
441
498