@lunora/cli 1.0.0-alpha.47 → 1.0.0-alpha.471

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.
Files changed (146) hide show
  1. package/LICENSE.md +33 -0
  2. package/README.md +3 -1
  3. package/dist/bin.mjs +3 -10
  4. package/dist/cli.d.mts +31 -0
  5. package/dist/cli.d.ts +31 -0
  6. package/dist/cli.mjs +1 -0
  7. package/dist/index.d.mts +1167 -416
  8. package/dist/index.d.ts +1167 -416
  9. package/dist/index.mjs +1 -19
  10. package/dist/packem_chunks/cli.mjs +2 -0
  11. package/dist/packem_chunks/dispatch.mjs +1 -0
  12. package/dist/packem_chunks/handler.mjs +55 -158
  13. package/dist/packem_chunks/handler10.mjs +2 -16
  14. package/dist/packem_chunks/handler11.mjs +1 -22
  15. package/dist/packem_chunks/handler12.mjs +1 -192
  16. package/dist/packem_chunks/handler13.mjs +1 -131
  17. package/dist/packem_chunks/handler14.mjs +3 -65
  18. package/dist/packem_chunks/handler15.mjs +1 -58
  19. package/dist/packem_chunks/handler16.mjs +1 -80
  20. package/dist/packem_chunks/handler17.mjs +1 -43
  21. package/dist/packem_chunks/handler18.mjs +1 -105
  22. package/dist/packem_chunks/handler19.mjs +1 -170
  23. package/dist/packem_chunks/handler2.mjs +1 -114
  24. package/dist/packem_chunks/handler20.mjs +1 -94
  25. package/dist/packem_chunks/handler21.mjs +7 -94
  26. package/dist/packem_chunks/handler22.mjs +1 -0
  27. package/dist/packem_chunks/handler23.mjs +3 -0
  28. package/dist/packem_chunks/handler24.mjs +3 -0
  29. package/dist/packem_chunks/handler25.mjs +2 -0
  30. package/dist/packem_chunks/handler26.mjs +1 -0
  31. package/dist/packem_chunks/handler27.mjs +2 -0
  32. package/dist/packem_chunks/handler28.mjs +99 -0
  33. package/dist/packem_chunks/handler29.mjs +3 -0
  34. package/dist/packem_chunks/handler3.mjs +1 -204
  35. package/dist/packem_chunks/handler30.mjs +15 -0
  36. package/dist/packem_chunks/handler31.mjs +1 -0
  37. package/dist/packem_chunks/handler32.mjs +1 -0
  38. package/dist/packem_chunks/handler4.mjs +1 -33
  39. package/dist/packem_chunks/handler5.mjs +1 -49
  40. package/dist/packem_chunks/handler6.mjs +3 -91
  41. package/dist/packem_chunks/handler7.mjs +1 -42
  42. package/dist/packem_chunks/handler8.mjs +1 -174
  43. package/dist/packem_chunks/handler9.mjs +1 -315
  44. package/dist/packem_chunks/runCodegenCommand.mjs +4 -52
  45. package/dist/packem_chunks/runDevCommand.mjs +6 -0
  46. package/dist/packem_chunks/runInitCommand.mjs +423 -1447
  47. package/dist/packem_chunks/runResetCommand.mjs +1 -41
  48. package/dist/packem_chunks/runRpcCommand.mjs +1 -68
  49. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck9tABfr.mjs +1 -0
  50. package/dist/packem_shared/EXIT_CODE-DQVhSE-1.mjs +1 -0
  51. package/dist/packem_shared/admin-token-VdUnvnKW.mjs +1 -0
  52. package/dist/packem_shared/admin-url-DJB8P6Jh.mjs +1 -0
  53. package/dist/packem_shared/api-spec-lYyq4vL6.mjs +1 -0
  54. package/dist/packem_shared/binding-manifest-file-CQ1eQYy1.mjs +2 -0
  55. package/dist/packem_shared/buildRegistryIndex-Dc8D8AR6.mjs +1 -0
  56. package/dist/packem_shared/cap-error-body-YBKO32BF.mjs +1 -0
  57. package/dist/packem_shared/catalog-CahzmDLV.mjs +1 -0
  58. package/dist/packem_shared/cli-manifest-DoQ_3bzs.mjs +1 -0
  59. package/dist/packem_shared/cloudflare-credentials-BVmZnDZk.mjs +1 -0
  60. package/dist/packem_shared/codegen-error-AmH54ofi.mjs +3 -0
  61. package/dist/packem_shared/command-BpMv2n0o.mjs +1 -0
  62. package/dist/packem_shared/commands-CkuCMUP_.mjs +13 -0
  63. package/dist/packem_shared/createLogger-4TujevyB.mjs +2 -0
  64. package/dist/packem_shared/createRecordingSpawner-DchLi0gR.mjs +1 -0
  65. package/dist/packem_shared/deploy-target-resolve-BTCW1Iv4.mjs +1 -0
  66. package/dist/packem_shared/diffSnapshots-Y1G8am6C.mjs +7 -0
  67. package/dist/packem_shared/docker-DpVxvYpL.mjs +1 -0
  68. package/dist/packem_shared/export-rows-EzdNDYpa.mjs +3 -0
  69. package/dist/packem_shared/free-port-DjbFhaaT.mjs +1 -0
  70. package/dist/packem_shared/health-probe-BLnoq_oC.mjs +13 -0
  71. package/dist/packem_shared/import-3Y59Ry9F.mjs +13 -0
  72. package/dist/packem_shared/index-oHZBGBwB.mjs +1 -0
  73. package/dist/packem_shared/insertSchemaExtension-CbTH3lCQ.mjs +10 -0
  74. package/dist/packem_shared/js-literal-FWLi94Lh.mjs +1 -0
  75. package/dist/packem_shared/lint-ignore-report-DKZagpqk.mjs +2 -0
  76. package/dist/packem_shared/mcp-config-file-GR1Y_CVw.mjs +2 -0
  77. package/dist/packem_shared/open-url-EnKy--w-.mjs +1 -0
  78. package/dist/packem_shared/output-format-DWe_tkTR.mjs +2 -0
  79. package/dist/packem_shared/parseManifest-CwPTKdtS.mjs +1 -0
  80. package/dist/packem_shared/path-containment-CgxYZggb.mjs +1 -0
  81. package/dist/packem_shared/plan-pog5AvMk.mjs +5 -0
  82. package/dist/packem_shared/planDevCommand-Bl6OrwXm.mjs +1 -0
  83. package/dist/packem_shared/platform-diagnostics-Cn2g6Jh-.mjs +4 -0
  84. package/dist/packem_shared/prompt-cancelled-BvsNxg_Q.mjs +1 -0
  85. package/dist/packem_shared/render-lunora-error-JsLelWwR.mjs +2 -0
  86. package/dist/packem_shared/resolve-Bdc9KEUv.mjs +5 -0
  87. package/dist/packem_shared/resolve-target-BRvw8Pf7.mjs +1 -0
  88. package/dist/packem_shared/runAddCommand-gThKW-ZB.mjs +1 -0
  89. package/dist/packem_shared/runDeployCommand-CNgOUx91.mjs +1 -0
  90. package/dist/packem_shared/runExportCommand-_PtkJJDT.mjs +5 -0
  91. package/dist/packem_shared/runMigrateGenerateCommand-CguLEIZp.mjs +11 -0
  92. package/dist/packem_shared/schemaIrToSnapshot-Ck7DSPdf.mjs +1 -0
  93. package/dist/packem_shared/shared-CV7ESvuj.mjs +2 -0
  94. package/dist/packem_shared/storage-3GOIFVZm.mjs +1 -0
  95. package/dist/packem_shared/tui-prompts-B3YwUhGw.mjs +4 -0
  96. package/dist/packem_shared/variables-CPYyJtsL.mjs +1 -0
  97. package/dist/packem_shared/vectorize-metadata-DK61NmVx.mjs +2 -0
  98. package/dist/packem_shared/wrangler-name-Dsk5K1f-.mjs +1 -0
  99. package/dist/packem_shared/wrangler-secrets-C9siMBwN.mjs +1 -0
  100. package/package.json +45 -19
  101. package/skills/README.md +36 -17
  102. package/skills/lunora/SKILL.md +158 -65
  103. package/skills/lunora-cloudflare-audit/SKILL.md +164 -0
  104. package/skills/lunora-create-package/SKILL.md +97 -84
  105. package/skills/lunora-deploy/SKILL.md +212 -102
  106. package/skills/lunora-functions/SKILL.md +367 -60
  107. package/skills/lunora-migration-helper/SKILL.md +194 -149
  108. package/skills/lunora-performance-audit/SKILL.md +137 -95
  109. package/skills/lunora-quickstart/SKILL.md +136 -136
  110. package/skills/lunora-realtime/SKILL.md +217 -97
  111. package/skills/lunora-setup-auth/SKILL.md +203 -101
  112. package/skills/lunora-setup-hyperdrive/SKILL.md +153 -110
  113. package/skills/lunora-setup-hyperdrive-global/SKILL.md +152 -94
  114. package/skills/lunora-setup-mail/SKILL.md +87 -113
  115. package/skills/lunora-setup-scheduler/SKILL.md +133 -97
  116. package/skills/lunora-setup-storage/SKILL.md +189 -95
  117. package/dist/packem_chunks/planDevCommand.mjs +0 -541
  118. package/dist/packem_chunks/runDeployCommand.mjs +0 -594
  119. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +0 -397
  120. package/dist/packem_shared/COMMANDS-B0ftFD_3.mjs +0 -948
  121. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs +0 -244
  122. package/dist/packem_shared/admin-url-4UzT-CI4.mjs +0 -19
  123. package/dist/packem_shared/api-spec-CtA6ilu4.mjs +0 -13
  124. package/dist/packem_shared/buildRegistryIndex-BcYe607_.mjs +0 -38
  125. package/dist/packem_shared/codegen-error-DJG-ghs_.mjs +0 -31
  126. package/dist/packem_shared/command-D3lB_4Az.mjs +0 -19
  127. package/dist/packem_shared/commands-B-gR09Z_.mjs +0 -845
  128. package/dist/packem_shared/createLogger-B40gPzQo.mjs +0 -78
  129. package/dist/packem_shared/createRecordingSpawner-DxI3mebw.mjs +0 -43
  130. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +0 -61
  131. package/dist/packem_shared/diffSnapshots-BeDvvNiF.mjs +0 -161
  132. package/dist/packem_shared/docker-hMQ97KSQ.mjs +0 -21
  133. package/dist/packem_shared/insertSchemaExtension-DAqbfr9Z.mjs +0 -64
  134. package/dist/packem_shared/open-url-Dfq6fAyT.mjs +0 -41
  135. package/dist/packem_shared/output-format-wUvAN6AL.mjs +0 -17
  136. package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +0 -56
  137. package/dist/packem_shared/parseManifest--vZf2FY1.mjs +0 -94
  138. package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +0 -9
  139. package/dist/packem_shared/resolve-target-qbsJ_5sF.mjs +0 -16
  140. package/dist/packem_shared/runAddCommand-bnY6-HKb.mjs +0 -4
  141. package/dist/packem_shared/schema-drift-gate-BtBt0as0.mjs +0 -79
  142. package/dist/packem_shared/schemaIrToSnapshot-DdsljJT-.mjs +0 -43
  143. package/dist/packem_shared/storage-BIsph-Vk.mjs +0 -84
  144. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +0 -658
  145. package/dist/packem_shared/wrangler-name-cy4yhm9j.mjs +0 -12
  146. package/dist/packem_shared/wrangler-secrets-P2_ZUR-k.mjs +0 -47
@@ -1,47 +1,36 @@
1
1
  ---
2
2
  name: lunora-create-package
3
- description: Builds a reusable Lunora capability — either a registry item installed with
4
- `lunora registry add`, or a publishable `@lunora/*` workspace package. Use for
5
- packaging schema + functions + bindings others can drop into their app.
3
+ description: Builds a reusable Lunora capability, either as a registry item that
4
+ `lunora add` / `lunora registry add` copies into an app's `lunora/`, or as a
5
+ publishable `@lunora/*` workspace package in the Lunora monorepo. Use when the
6
+ user wants to package schema + functions + wrangler bindings + env vars for
7
+ other apps, add or edit an item under `registry/<name>/` (`registry.json`),
8
+ run `vis generate lunora-package`, or wire a new `ctx.*` capability into
9
+ codegen.
6
10
  ---
7
11
 
8
12
  # Lunora Create Package
9
13
 
10
- Package a reusable Lunora capability. There are two distribution shapes; pick
11
- based on whether the capability is **copied into the user's `lunora/`** or
12
- **imported as a dependency**.
14
+ Pick the shape by whether the code is copied into the user's `lunora/` or
15
+ imported as a dependency:
13
16
 
14
- | Shape | Distribution | Use for |
15
- | ----------------- | -------------------------------- | -------------------------------------------------------- |
16
- | **Registry item** | `lunora registry add <name>` | App-owned code (schema/functions) the user edits + wires |
17
- | **Workspace pkg** | `import … from "@lunora/<name>"` | Reusable library code imported as a dependency |
17
+ | Shape | Distribution | Use for |
18
+ | ------------- | -------------------------------- | ------------------------------------------------ |
19
+ | Registry item | `lunora registry add <name>` | App-owned code (schema/functions) the user edits |
20
+ | Workspace pkg | `import … from "@lunora/<name>"` | Library code consumed as a versioned dependency |
18
21
 
19
- Many capabilities use **both**: a thin `@lunora/<name>` package holding the
20
- reusable runtime, plus a registry item that scaffolds the glue (`lunora/<name>/`
21
- files, bindings, env vars) into the user's project. `auth`, `mail`, `ratelimit`,
22
- and `storage` all follow this pattern.
22
+ Many capabilities use both: a `@lunora/<name>` package holds the runtime and a
23
+ registry item scaffolds the glue (files under `lunora/<name>/`, bindings, env
24
+ vars). `auth`, `mail`, `ratelimit` and `storage` follow this pattern.
23
25
 
24
- ## When to Use
26
+ Skip this skill for a one-off feature in a single app (write it in `lunora/`),
27
+ or when the capability already exists (`lunora registry list`).
25
28
 
26
- - Extracting schema + functions you have written into something reusable.
27
- - Authoring a new capability (presence, search, payments, …) for other apps.
28
- - Adding a new item to this repo's `registry/`.
29
+ ## Path A: registry item
29
30
 
30
- ## When Not to Use
31
-
32
- - A one-off feature for a single app — just write it in `lunora/`.
33
- - The capability already exists as a registry item or `@lunora/*` package — use
34
- it (`lunora registry list` to browse).
35
-
36
- ## Path A: Registry Item
37
-
38
- A registry item is a directory under `registry/<name>/` with three files:
39
-
40
- - `registry.json` — the manifest (deps, bindings, env vars, files, requires).
41
- - `index.ts` (and any siblings) — the code copied into the user's project.
42
- - `README.md` — install + configuration docs.
43
-
44
- ### Manifest shape
31
+ An item is a directory `registry/<name>/` holding `registry.json`, the source
32
+ files it copies, and a `README.md`. Validate against
33
+ `registry/schema/registry-item.schema.json`.
45
34
 
46
35
  ```jsonc
47
36
  // registry/<name>/registry.json
@@ -50,80 +39,104 @@ A registry item is a directory under `registry/<name>/` with three files:
50
39
  "name": "<name>",
51
40
  "title": "Human Title",
52
41
  "description": "One-paragraph summary shown in `lunora registry list`.",
53
- "docs": "Post-install steps surfaced to the user after `lunora registry add`.",
54
- "requires": [], // other item names this one depends on
42
+ "docs": "Post-install steps printed after `lunora registry add`.",
43
+ "requires": [], // other items, installed first (e.g. auth-clerk requires auth)
55
44
  "deps": { "@lunora/server": "workspace:*" },
45
+ "devDependencies": {},
56
46
  "bindings": [
57
- // reconciled into wrangler.jsonc
47
+ // structural edits applied to wrangler.jsonc
58
48
  { "path": ["d1_databases"], "value": [{ "binding": "DB", "database_name": "REPLACE_ME-db", "database_id": "<replace-with-d1-create-id>" }] },
59
49
  ],
60
50
  "envVars": [{ "name": "MY_SECRET", "description": "What it is and how to generate it.", "secret": true }],
61
- "files": [{ "from": "index.ts", "to": "lunora/<name>/index.ts", "merge": "create-or-skip" }],
51
+ "files": [
52
+ { "from": "schema.ts", "to": "lunora/<name>/schema.ts", "merge": "schema-extension" },
53
+ { "from": "<name>.ts", "to": "lunora/<name>/index.ts", "merge": "create-or-skip" },
54
+ ],
62
55
  }
63
56
  ```
64
57
 
65
- - `files[].merge` is typically `create-or-skip` (never clobber edited user code);
66
- `bindings` are reconciled into `wrangler.jsonc`; `envVars` are scaffolded into
67
- `.dev.vars` (secret-looking ones get generated values).
68
- - `requires` lets a provider item (e.g. `auth-clerk`) build on a base item
69
- (`auth`). The resolver installs the chain.
70
-
71
- ### Register and validate
72
-
73
- Add an entry to `registry/index.json`, then rebuild and check the index:
58
+ - `files[].merge`: `create-or-skip` writes whole files and never clobbers a file
59
+ the user edited; `schema-extension` AST-merges into `lunora/schema.ts`.
60
+ - `envVars` land in `.dev.vars`. Non-secret vars get their `value`; secrets get
61
+ an empty placeholder plus a `wrangler secret put <NAME>` reminder.
62
+ - `entrypointReexports` (`[{ "module": "_generated/workflows" }]`) injects
63
+ `export * from './lunora/<module>'` into the worker entry of framework
64
+ (class B/C) projects, for items whose generated classes the worker must
65
+ export. No shipped item uses it yet.
66
+ - Function files import builders from `#lunora/_generated/server.js`, the
67
+ module codegen emits in a consumer project. `registry/tsconfig.json` stubs
68
+ that module so the item typechecks here.
69
+ - `deps` ranges are copied verbatim into the user's `package.json`. Use
70
+ `workspace:*` for `@lunora/*` (resolved at publish); any other range must
71
+ admit the version the workspace resolves, or `pnpm install` fails
72
+ (`scripts/check-registry-catalog-ranges.js`).
73
+
74
+ Verify:
74
75
 
75
76
  ```bash
76
- lunora registry build # regenerate registry/index.json
77
- lunora registry build --check # verify the index is up to date (CI)
78
- lunora registry view <name> # preview what `add` would do
79
- lunora registry add <name> # install into the current project
77
+ lunora registry build --from ./registry # regenerate registry/index.json from the item dirs
78
+ lunora registry build --from ./registry --check # CI: index is current
79
+ pnpm run lint:types:registry # tsc over every item (builds packages first)
80
+ pnpm run lint:registry:items # manifest + generated-deps tests
81
+ lunora registry view <name> --from <repo>/registry # preview
82
+ lunora registry add <name> --from <repo>/registry --dry-run # in a scratch app
80
83
  ```
81
84
 
82
- ## Path B: Workspace Package
85
+ `registry/index.json` is generated; do not hand-edit it.
83
86
 
84
- Scaffold a fresh `@lunora/<name>` package with the generator (always use the
85
- `--name=value` form):
87
+ ## Path B: workspace package (Lunora monorepo)
86
88
 
87
89
  ```bash
88
- vis generate lunora-package --name=search --description='Typed full-text search over Lunora tables'
90
+ vis generate lunora-package --name=search --description='Typed full-text search over Lunora tables' --category=add-on
89
91
  ```
90
92
 
91
- This creates `packages/search/` following the repo's package shape: `src/index.ts`,
92
- `__tests__/`, `vitest.config.ts`, `tsconfig.json` (extends `../../tsconfig.base.json`),
93
- `project.json` (vis tags `type:package` + `category:<slug>`), `package.json` (ESM,
94
- `"sideEffects": false`, conditional exports), and `.releaserc.json`.
93
+ Pass every option as `--name=value`; vis reads `--name search` as
94
+ `--name=true` plus a stray positional.
95
+
96
+ This writes `packages/search/` with `package.json` (ESM-only,
97
+ `"sideEffects": false`, catalog versions), `src/index.ts`, `tsconfig.json`,
98
+ `vitest.config.ts`, `packem.config.ts`, `eslint.config.js`,
99
+ `prettier.config.js`, `project.json` (tags `type:package` +
100
+ `category:<category>`), `.releaserc.json` and `README.md`. It does not create
101
+ `__tests__/` or `LICENSE.md`. Then:
95
102
 
96
- ### Repo conventions to honor
103
+ 1. `cp packages/config/LICENSE.md packages/search/LICENSE.md` (packem and
104
+ `files` expect it).
105
+ 2. Add a row to `.agents/docs/packages.md`, and list the directory in one tier
106
+ of `scripts/api-snapshot.js` and the matching tier in `ROADMAP.md`. The
107
+ `pnpm install` postinstall checks (`check-agents-md-packages`,
108
+ `check-roadmap-tiers`) fail until both are done.
109
+ 3. `pnpm install`, then add tests under `__tests__/`.
97
110
 
98
- - **No `.js` extensions** in relative imports (`moduleResolution: "bundler"`).
99
- The lone exception is `@lunora/codegen`'s emitted output.
100
- - **No mixed default + named exports** in one file — named-only when there is
101
- more than one export.
102
- - **Use the dependency catalogs** in `pnpm-workspace.yaml` (`catalog:test`,
103
- `catalog:lint`, …) — never hard-code a version that lives in a catalog.
104
- - Tag `project.json` with `type:package` and a `category:<slug>`.
111
+ Repo conventions: relative imports without `.js`, named exports only (a
112
+ `default` only as a file's sole export), dependency versions via `catalog:*`
113
+ from `pnpm-workspace.yaml`.
105
114
 
106
- Build and test the new package in isolation:
115
+ Verify:
107
116
 
108
117
  ```bash
118
+ pnpm --filter "@lunora/search..." run build
109
119
  pnpm --filter "@lunora/search" run lint:types
110
120
  pnpm --filter "@lunora/search" run test
121
+ pnpm run lint:package-json
122
+ pnpm run api:update # writes api-snapshots/search.api.md from the fresh build; commit it
111
123
  ```
112
124
 
113
- ## Codegen-Wired Capabilities
114
-
115
- If your capability surfaces functions on a context (e.g. `ctx.ai`, `ctx.containers`)
116
- or new generated tables, it must be discoverable by `@lunora/codegen` — codegen
117
- parses `lunora/schema.ts` and the function files. Document any `lunora/*.ts`
118
- declaration the user must add so codegen wires the typed surface.
119
-
120
- ## Checklist
121
-
122
- - [ ] Chose the right shape (registry item, workspace package, or both).
123
- - [ ] Registry item: `registry.json` + `index.ts` + `README.md` authored;
124
- `bindings`/`envVars`/`requires`/`files` correct.
125
- - [ ] `lunora registry build` run; `lunora registry build --check` passes.
126
- - [ ] Workspace package: scaffolded via `vis generate lunora-package`; no `.js`
127
- extensions, no mixed default+named exports, catalog versions used.
128
- - [ ] `lint:types` and `test` pass for the new package.
129
- - [ ] README documents install, bindings, env vars, and any `lunora/` glue.
125
+ ## Wiring a `ctx.*` capability into codegen
126
+
127
+ Codegen decides which `ctx.*` fields exist by detecting usage, so a new
128
+ binding-backed helper is a literal row in `CAPABILITY_ROWS`
129
+ (`packages/codegen/src/capabilities.ts`; `CAPABILITIES` is a read-only view of
130
+ it): its package, `ctx` property, tier
131
+ (`"every"` or `"action"`-only), ctx type fragment, ShardDO binding, and
132
+ `defineApp` method. Rate the feature for every target in the
133
+ `PlatformCapabilities` matrices in `@lunora/platform`
134
+ (`packages/platform/src/capabilities/`) as `native`, `emulated` or
135
+ `unsupported` in the same change. A feature a target leaves unrated is dropped
136
+ from that target's generated surface with a diagnostic. Declaration-gated
137
+ surfaces (flags, notify, containers, workflows, queues, agents) also have their
138
+ own emitters; some of them (flags, notify, containers, workflows) keep a row too,
139
+ for usage probing, so a row and a dedicated emitter can coexist.
140
+
141
+ Document in the README any `lunora/*.ts` declaration the user must add for
142
+ codegen to emit the typed surface.
@@ -1,150 +1,260 @@
1
1
  ---
2
2
  name: lunora-deploy
3
- description: Deploys a Lunora app to Cloudflare. Use for `lunora deploy`, wrangler.jsonc
4
- bindings (SHARD/SESSION DOs, D1, R2), provisioning databases/buckets, secrets
5
- (`wrangler secret` vs `.dev.vars`), the `lunora doctor` preflight, the
6
- schema-drift gate, and dev-vs-prod separation.
3
+ description: Deploys a Lunora app to Cloudflare Workers + Durable Objects and gets wrangler.jsonc, remote resources and secrets to line up. Covers `lunora deploy` (and `--env`, `--dry-run`, `--migrate`), isolated per-PR previews (`lunora preview deploy | list | delete`), the `lunora doctor` / `lunora verify` / `lunora prepare` preflights, Durable Object bindings (SHARD, SHARD_REGISTRY, SCHEDULER), the `DB` D1 binding and its placeholder id, R2 buckets, production secrets (`lunora env push`, `wrangler secret put`) versus `.dev.vars`, the schema-drift gate, the architecture diff, and rollback with `lunora cloudflare deployments`. Use when the user asks to "deploy", "ship to production", "set up a staging environment", wire CI deploys, or when a deploy fails on a binding, a placeholder `database_id`, an unexported Durable Object class, a missing secret, or "schema drift gate blocked deploy".
7
4
  ---
8
5
 
9
6
  # Lunora Deploy
10
7
 
11
- Ship a Lunora app to Cloudflare Workers + Durable Objects. Unlike a managed
12
- backend, deployment owns real Cloudflare resources — Durable Object bindings, a
13
- D1 database, R2 buckets, and secrets — so the work is mostly making
14
- `wrangler.jsonc` and the remote resources line up.
8
+ Deploying a Lunora app means making `wrangler.jsonc`, the remote Cloudflare
9
+ resources (D1, R2, secrets) and the worker's exports agree. `lunora deploy`
10
+ automates most of that and refuses to ship when they don't.
11
+
12
+ Local development is `lunora dev` (see `lunora-quickstart`). If the deploy is
13
+ blocked by a breaking schema change, stage it with `lunora-migration-helper`
14
+ first.
15
+
16
+ ## What `lunora deploy` does
17
+
18
+ In order, stopping at the first failure:
19
+
20
+ 1. **Codegen** — regenerates `lunora/_generated/`. ERROR-level codegen
21
+ advisories fail the deploy in CI (`--strict-advisories` forces this locally,
22
+ `--no-strict-advisories` turns it off). Nothing here runs `tsc` and wrangler
23
+ strips types, so a project with a type error deploys — gate CI on
24
+ `lunora verify`, which does type-check.
25
+ 2. **Schema-drift gate** — compares the schema with the committed baseline
26
+ `lunora/.lunora-schema.json` (details below).
27
+ 3. **Architecture diff** — prints modules and edges (calls, table reads/writes,
28
+ enqueues) added or removed since the last successful deploy. Informational
29
+ only; the manifest is recorded in `.lunora/architecture[.<env>].json` after
30
+ wrangler succeeds.
31
+ 4. **Binding reconcile** — writes the bindings the code implies into
32
+ `wrangler.jsonc`: Durable Object bindings plus their `new_sqlite_classes`
33
+ migration, the `DB` D1 binding for `.global()` tables, crons, and the
34
+ compatibility date. R2 buckets are not auto-written (the bucket name is
35
+ yours to choose); you get a warning instead.
36
+ 5. **Read-only checks** — blocks on a D1 `database_id` still set to
37
+ `<replace-with-d1-create-id>`, on a loopback URL (`localhost`, `127.x`,
38
+ `::1`) in `vars`, on a missing container Dockerfile, or on no Docker engine
39
+ when a container builds locally.
40
+ 6. **Validate `wrangler.jsonc`** — the `SHARD` binding to `ShardDO` (or
41
+ `LunoraDO` for apps that merge their DOs), a minimum `compatibility_date`,
42
+ DO classes registered in `migrations`, and the shape of every binding.
43
+ `compatibility_flags` are not checked; templates ship `nodejs_compat` for
44
+ dependencies that import `node:` builtins.
45
+ 7. **Secrets gate** — lists the secrets the app's packages need and checks
46
+ which are set on the worker. Interactive runs offer to mint and push the
47
+ generatable ones (`BETTER_AUTH_SECRET`, `LUNORA_ADMIN_TOKEN`, …). In CI a
48
+ missing required secret aborts the deploy. Skipped for `--dry-run`, for
49
+ `lunora preview` (it uploads its own secrets), and when the worker doesn't
50
+ exist yet.
51
+ 8. **Services, then `wrangler deploy`** — services declared in
52
+ `lunora.config` deploy first (`--skip-services` to skip), then the app.
53
+ 9. **After a successful deploy** — records the URL as the project link
54
+ (`lunora link`), optionally runs `--migrate` and `--health-check`, and only
55
+ then re-blesses the schema baseline.
56
+
57
+ `lunora prepare` runs steps 1–6 without deploying (useful in CI), but it takes
58
+ no `--env`, so it validates the top-level config only. `lunora verify` runs
59
+ validation, a codegen dry run and `tsc --noEmit` without writing files, and does
60
+ take `--env`.
61
+
62
+ Flags worth knowing:
63
+
64
+ | Flag | Effect |
65
+ | ---------------------- | ------------------------------------------------------------------------------- |
66
+ | `--env <name>` | Deploy a wrangler environment; checks read that env's (non-inherited) bindings. |
67
+ | `--dry-run` | Run every gate and bundle without publishing; `wrangler.jsonc` edits roll back. |
68
+ | `--health-check` | Probe `/_lunora/health/ready` on the new version; fail if it never answers. |
69
+ | `--prebuilt` | Skip codegen and the drift gate when `lunora prepare` already ran in this job. |
70
+ | `--migrate` | Run every declared data migration (`up` is idempotent) against the live worker. |
71
+ | `--allow-schema-drift` | Deploy despite blocked drift; checks nothing, the baseline is not advanced. |
72
+
73
+ `--migrate` is checked before wrangler runs, so a missing requirement aborts the
74
+ whole deploy:
75
+
76
+ - `--migrate-yes` — confirms running production data migrations.
77
+ - `--migrate-url <https://…>` — required unless this checkout is linked for the
78
+ same `--env` (a previous successful deploy links it automatically).
79
+ - An admin token — `--migrate-token` or `LUNORA_ADMIN_TOKEN`.
15
80
 
16
- ## When to Use
17
-
18
- - Deploying to production (or a Cloudflare environment) for the first time.
19
- - A deploy fails on a binding, a placeholder id, or the schema-drift gate.
20
- - Provisioning D1 / R2 / secrets for a Lunora app.
21
-
22
- ## When Not to Use
23
-
24
- - Local development — that is `lunora dev` (see `lunora-quickstart`).
25
- - A schema/data change that needs migrating — do that first with
26
- `lunora-migration-helper`, then deploy.
27
-
28
- ## What `lunora deploy` Does
29
-
30
- `lunora deploy` runs a fixed pipeline:
31
-
32
- 1. **Codegen** — regenerates `lunora/_generated/` and typechecks.
33
- 2. **Validate `wrangler.jsonc`** — required `compatibility_date`, the
34
- `nodejs_compat` flag, and the `SHARD` Durable Object binding.
35
- 3. **Schema-drift gate** — blocks if the committed baseline
36
- (`lunora/.lunora-schema.json`) drifted with a breaking change and no
37
- accompanying migration. The baseline is re-blessed only after the deploy
38
- succeeds.
39
- 4. **`wrangler deploy`** — builds and pushes the worker (and any container
40
- images).
81
+ ```bash
82
+ lunora deploy --migrate --migrate-yes --migrate-url https://app.example.com
83
+ ```
41
84
 
42
- Useful flags: `--env <name>` (Cloudflare environment), `--migrate` (run pending
43
- data migrations against the live worker after deploy, with `--migrate-token` /
44
- `--migrate-url`), `--allow-schema-drift` (override the gate — use sparingly), and
45
- `--update-schema-baseline` (re-bless the baseline with the current shape).
85
+ ## Per-PR previews
46
86
 
47
- ## Preflight: `lunora doctor`
87
+ `lunora deploy --preview` is refused: a Worker version shares production's
88
+ Durable Objects, D1 and R2, and gets no preview URL. Use `lunora preview`:
48
89
 
49
- Run the read-only preflight before deploying. It checks:
90
+ ```bash
91
+ lunora preview deploy 123 --seed # own Worker + DO namespace + D1/KV/R2, migrated, seeded
92
+ lunora preview deploy 123 --dry-run # the plan and env block, no API call
93
+ lunora preview list
94
+ lunora preview delete 123 # Worker + every resource on its record
95
+ ```
50
96
 
51
- - `wrangler.jsonc` present with the `SHARD` durable-object binding.
52
- - D1 `database_id`s are real, not placeholders (`<replace>` / empty).
53
- - `send_email` destination addresses aren't placeholders.
54
- - `.dev.vars` secret-looking keys are filled.
55
- - Declared containers are exported by the worker entry.
97
+ - Records are keyed by `lunora.appId` in `package.json` (the first local
98
+ `lunora preview deploy` writes it; commit it, CI cannot mint one), so a Worker rename does not
99
+ lose a preview. Old Worker-name keys are re-keyed on the next deploy/delete.
100
+ - `--env <name>` plans from that Wrangler environment (top level plus `env.<name>`)
101
+ for an app whose bindings live only there; recorded, so `delete` needs no flag for the
102
+ resources. If that environment names a different `account_id`, pass the same `--env` to
103
+ `list`/`delete` (or set `CLOUDFLARE_ACCOUNT_ID`); they refuse rather than guess.
104
+ Without it, env-only bindings are refused. CI leaves it unset.
105
+ - Resources are named `<worker>-preview-<name>-<binding>` and recorded in the
106
+ account's `lunora-preview-registry` KV namespace; `delete` only removes what a
107
+ record lists, and refuses anything production's config names.
108
+ - Mintable secrets are minted per deploy, or derived from
109
+ `LUNORA_PREVIEW_SECRET_SEED` when set (stable across deploys); a provider key
110
+ reaches the preview only via `--secret KEY` (value from the environment).
111
+ - Production `vars` are copied; override one with `--var KEY=VALUE` (e.g. an
112
+ auth base URL pointing at the preview's workers.dev URL).
113
+ - Refused (with the list) when the app binds Hyperdrive, services, queues,
114
+ workflows, Vectorize or another binding a preview cannot isolate yet.
115
+ - Token needs Workers Scripts, D1, Workers KV Storage and Workers R2 Storage:
116
+ Edit. Cloudflare only; celld and Node refuse.
117
+ - `lunora init --ci github|gitlab` scaffolds the deploy-on-PR and
118
+ delete-on-close jobs.
119
+
120
+ ## Preflight
56
121
 
57
122
  ```bash
58
- lunora doctor # FAIL → exit 1; WARN/INFO don't block
123
+ lunora doctor # read-only; exits 1 on any FAIL, WARN/INFO don't block
124
+ lunora doctor --format json # each finding carries a stable `code` for CI/agents
125
+ lunora verify --env production
59
126
  ```
60
127
 
61
- `lunora verify` and `lunora prepare` run related checks (drift gate, wrangler
62
- validation) — wire `lunora verify` into CI to catch drift before a deploy.
128
+ `lunora doctor` fails on a missing or invalid `wrangler.jsonc`, a missing
129
+ `SHARD` binding, a placeholder D1 id (`d1-placeholder-id`), and a binding whose
130
+ class the worker doesn't export (`wrangler-class-unexported`), and an Artifacts
131
+ namespace in the wrong jurisdiction. It warns on unfilled `.dev.vars` secrets,
132
+ placeholder `send_email` destinations, an account near its Durable Object class
133
+ cap, and a `cloudflare.config.ts` (`cf-config-present`).
63
134
 
64
- ## `wrangler.jsonc` — the binding contract
135
+ ## `wrangler.jsonc`
65
136
 
66
- A Lunora worker needs the ShardDO (and SessionDO when auth is wired), the
67
- SQLite-DO migration tag, and whatever D1/R2 the app uses:
137
+ Templates ship this shape. The `d1_databases` and `r2_buckets` entries are
138
+ only needed when the app uses `.global()` tables or auth (D1) and storage (R2):
68
139
 
69
140
  ```jsonc
70
141
  {
71
142
  "name": "my-app",
72
- "main": "src/index.ts",
73
- "compatibility_date": "2026-04-07",
143
+ "main": "src/server.ts",
144
+ "compatibility_date": "2026-10-01",
74
145
  "compatibility_flags": ["nodejs_compat"],
75
146
  "durable_objects": {
76
147
  "bindings": [
77
148
  { "name": "SHARD", "class_name": "ShardDO" },
78
- { "name": "SESSION", "class_name": "SessionDO" }, // only with @lunora/auth
149
+ { "name": "SHARD_REGISTRY", "class_name": "ShardRegistryDO" },
79
150
  ],
80
151
  },
81
- "migrations": [{ "tag": "v1", "new_sqlite_classes": ["ShardDO", "SessionDO"] }],
82
- "d1_databases": [{ "binding": "DB", "database_name": "my-app-global", "database_id": "<replace>" }],
83
- "r2_buckets": [{ "binding": "FILES", "bucket_name": "my-app-files" }],
152
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["ShardDO", "ShardRegistryDO"] }],
153
+ "limits": { "cpu_ms": 30000 },
154
+ "d1_databases": [{ "binding": "DB", "database_name": "my-app", "database_id": "<replace-with-d1-create-id>" }],
155
+ "r2_buckets": [{ "binding": "UPLOADS", "bucket_name": "my-app-uploads" }],
84
156
  }
85
157
  ```
86
158
 
87
- `lunora dev` auto-reconciles most of this (and `lunora registry add` adds the
88
- bindings an item needs), but the **resources themselves** must exist and their
89
- ids must be filled in:
159
+ Reconcile adds `SCHEDULER` → `SchedulerDO` when the app schedules work, and
160
+ bindings for generated containers, workflows and agents. Every `class_name`
161
+ must be exported by the worker entry or wrangler rejects the deploy.
162
+
163
+ Lunora writes the bindings but cannot create the remote resources:
90
164
 
91
165
  ```bash
92
- wrangler d1 create my-app-global # paste the returned database_id into wrangler.jsonc
93
- wrangler r2 bucket create my-app-files
166
+ wrangler d1 create my-app # paste the returned database_id into wrangler.jsonc
167
+ wrangler r2 bucket create my-app-uploads
94
168
  ```
95
169
 
96
- The DO `class_name`s must be exported by your worker entry (the
97
- `createShardDO()` / generated container exports) — wrangler rejects a binding
98
- whose class the worker doesn't export. `lunora doctor` surfaces a missing
99
- container export proactively.
170
+ When using `--env`, remember that `vars`, `d1_databases`, `durable_objects` and
171
+ `containers` do not inherit in wrangler. Each environment needs its own entries,
172
+ including its own D1 database.
100
173
 
101
- ## Secrets: `wrangler secret`, not `.dev.vars`
174
+ ## Secrets
102
175
 
103
- `.dev.vars` is **dev only** — it is git-ignored and never deployed. Production
104
- secrets (`BETTER_AUTH_SECRET`, `RESEND_API_KEY`, provider client secrets, …) go
105
- into Cloudflare:
176
+ `.dev.vars` is for development only: it is git-ignored and `wrangler deploy`
177
+ never uploads it. Production secrets live in Cloudflare:
106
178
 
107
179
  ```bash
108
- wrangler secret put BETTER_AUTH_SECRET # prompts for the value, stored encrypted
109
- wrangler secret list
180
+ lunora env diff --env production # keys local-only vs set remotely
181
+ lunora env push --env production --yes # uploads .dev.vars values; refuses placeholders
182
+ wrangler secret put RESEND_API_KEY --env production # set a prod-only value by hand
110
183
  ```
111
184
 
112
- Set every secret your app reads (mirror the secret-looking keys in `.dev.vars`)
113
- before the first request hits production.
185
+ `lunora env push` uploads the values from `.dev.vars`. If production needs
186
+ different values than dev (it usually does for provider keys), set those with
187
+ `wrangler secret put` instead.
188
+
189
+ ## Schema-drift gate
190
+
191
+ The gate blocks when the schema changed in a breaking way since
192
+ `lunora/.lunora-schema.json` and no new `defineMigration` on the affected table
193
+ covers it. The block message lists each change and a paste-ready
194
+ `lunora migrate create … --table …` command. Commit the baseline file so CI
195
+ and every machine gate against the same shape. It is advanced only after a
196
+ successful deploy, so a failed deploy never moves it past a change that didn't
197
+ ship.
198
+
199
+ When the stored rows already fit a tightening, `lunora deploy` notices on its own:
200
+ it reads the affected tables from the deployed worker (`--migrate-url` or the
201
+ project link, plus `LUNORA_ADMIN_TOKEN`) and lets the change through, naming any
202
+ row that does not fit otherwise. A new unique index is checked the same way, for repeated values.
203
+ `--allow-schema-drift` skips the gate for one run without checking. Otherwise
204
+ stage the change with `lunora-migration-helper`.
205
+
206
+ ## `.global()` table DDL
114
207
 
115
- ## Dev vs Production
208
+ The runtime provisions `.global()` tables itself on first use
209
+ (`CREATE TABLE IF NOT EXISTS` plus additive `ADD COLUMN` / indexes), so additive
210
+ changes need no step. `lunora migrate generate` writes a reviewable SQL file for
211
+ D1-backed global tables, but no command applies it, `lunora deploy` included.
212
+ Destructive or hand-written statements in it (drops, a backfilling `UPDATE`, a
213
+ rename) must be applied by hand:
116
214
 
117
- - **Development:** `lunora dev` (Vite + workerd + Studio + codegen-on-save). The
118
- schema baseline and `.dev.vars` belong to dev.
119
- - **Production:** `lunora deploy`. Separate D1 database / R2 buckets / secrets
120
- from dev. Never point a dev worker at prod resources.
215
+ ```bash
216
+ wrangler d1 execute DB --remote --file=lunora/migrations/<file>.sql
217
+ ```
218
+
219
+ See `lunora-migration-helper` for the full flow.
121
220
 
122
- For `.global()` table DDL, generate and commit SQL migrations with `lunora
123
- migrate generate` before deploying; `@lunora/d1`'s runner applies them. For data
124
- backfills, deploy first, then `lunora deploy --migrate` (or `lunora migrate up
125
- --prod`). See `lunora-migration-helper`.
221
+ ## Rollback
126
222
 
127
- ## Common Pitfalls
223
+ ```bash
224
+ lunora cloudflare deployments list
225
+ lunora cloudflare deployments status # which versions carry traffic, and how much
226
+ lunora cloudflare deployments rollback --yes # previous version
227
+ lunora cloudflare deployments promote <version-id> --yes # send 100% of traffic to a version
228
+ ```
128
229
 
129
- 1. **Placeholder `database_id`.** The D1 binding ships with `<replace>`; run
130
- `wrangler d1 create` and paste the id. `lunora doctor` catches this.
131
- 2. **DO class not exported.** `wrangler deploy` fails if a `class_name` isn't
132
- exported by the worker entry — export `ShardDO`/`SessionDO`/generated
133
- containers.
134
- 3. **Secrets only in `.dev.vars`.** They never reach production; use `wrangler
135
- secret put` for every prod secret.
136
- 4. **Bypassing the drift gate.** `--allow-schema-drift` ships a breaking schema
137
- with no migration — stage the change (`lunora-migration-helper`) instead.
138
- 5. **Deploying with uncommitted codegen.** Commit `lunora/_generated/` and
139
- `lunora/.lunora-schema.json` so CI and the gate see the same baseline.
230
+ A partial rollout (`--percent N --from <live-version-id> --confirm-shared-state`) runs two versions against one Durable Object database. Nothing in `promote` compares the two versions' schemas, so confirm them yourself before using it. Two facts bound the risk: `lunora deploy --preview` is refused, so a version that never carried traffic was uploaded outside Lunora with `wrangler versions upload`, which never ran the schema-drift gate, while a superseded version that `lunora deploy` once shipped passed it against the schema of its own time, unless that deploy used `--prebuilt` or `--allow-schema-drift`, which skip or override the gate; and `wrangler versions upload` refuses a version with a pending Durable Object migration, so that migration reaches traffic only through `deploy`, at 100%. A split whose live version predates a migration the target relies on is not detected by either check.
231
+
232
+ Rolling back code does not roll back data migrations or D1 DDL.
233
+
234
+ ## Common pitfalls
235
+
236
+ 1. **Placeholder `database_id`.** Reconcile writes
237
+ `<replace-with-d1-create-id>`; deploy refuses it. Run `wrangler d1 create`
238
+ and paste the id.
239
+ 2. **DO class not exported.** Export `ShardDO` (and any generated container /
240
+ workflow class) from the worker entry; `lunora doctor` names the gap.
241
+ 3. **Secrets only in `.dev.vars`.** They never reach production. Push or
242
+ `wrangler secret put` them before the first request.
243
+ 4. **Dev and prod sharing resources.** Give each environment its own D1
244
+ database, R2 buckets and secrets; never point a dev worker at prod data.
245
+ 5. **Uncommitted schema baseline.** Commit `lunora/.lunora-schema.json` (and
246
+ `lunora/migrations/.snapshot.json` if you use `migrate generate`).
247
+ `lunora/_generated/` is regenerated by deploy; templates git-ignore it.
248
+ 6. **Using `cf dev` / `cf build` / `cf deploy`.** They read
249
+ `cloudflare.config.ts`, which Lunora never updates
250
+ ([#964](https://github.com/anolilab/lunora/issues/964)). Use `lunora dev` /
251
+ `lunora deploy`; `cf` resource commands (zones, DNS, KV) are fine.
140
252
 
141
253
  ## Checklist
142
254
 
143
- - [ ] `lunora doctor` passes (no FAIL).
144
- - [ ] `wrangler.jsonc` has `compatibility_date`, `nodejs_compat`, the `SHARD` DO
145
- binding, and the SQLite migration tag.
146
- - [ ] D1 / R2 resources created; real ids pasted into `wrangler.jsonc`.
147
- - [ ] DO + container `class_name`s exported by the worker entry.
148
- - [ ] Production secrets set via `wrangler secret put`.
149
- - [ ] Schema changes migrated; the drift gate is green (no `--allow-schema-drift`).
150
- - [ ] `lunora deploy` succeeded; `--migrate` run if data backfills were pending.
255
+ - [ ] `lunora doctor` has no FAIL; `lunora verify --env <name>` passes in CI.
256
+ - [ ] D1 / R2 resources created per environment; real ids in `wrangler.jsonc`.
257
+ - [ ] Every DO / container / workflow `class_name` is exported by the worker entry.
258
+ - [ ] Production secrets set (`lunora env diff --env <name>` shows nothing local-only that prod needs).
259
+ - [ ] Breaking schema changes staged with a migration; drift gate green.
260
+ - [ ] `lunora deploy --env <name> --health-check` succeeded; `--migrate --migrate-yes` run if backfills were pending.