@lunora/cli 1.0.0-alpha.41 → 1.0.0-alpha.411

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 (136) hide show
  1. package/LICENSE.md +33 -0
  2. package/README.md +3 -1
  3. package/dist/bin.mjs +2 -10
  4. package/dist/index.d.mts +1045 -374
  5. package/dist/index.d.ts +1045 -374
  6. package/dist/index.mjs +1 -19
  7. package/dist/packem_chunks/dispatch.mjs +1 -0
  8. package/dist/packem_chunks/handler.mjs +1 -160
  9. package/dist/packem_chunks/handler10.mjs +1 -16
  10. package/dist/packem_chunks/handler11.mjs +1 -22
  11. package/dist/packem_chunks/handler12.mjs +1 -192
  12. package/dist/packem_chunks/handler13.mjs +3 -131
  13. package/dist/packem_chunks/handler14.mjs +1 -65
  14. package/dist/packem_chunks/handler15.mjs +1 -58
  15. package/dist/packem_chunks/handler16.mjs +1 -80
  16. package/dist/packem_chunks/handler17.mjs +7 -43
  17. package/dist/packem_chunks/handler18.mjs +1 -105
  18. package/dist/packem_chunks/handler19.mjs +1 -170
  19. package/dist/packem_chunks/handler2.mjs +1 -114
  20. package/dist/packem_chunks/handler20.mjs +3 -94
  21. package/dist/packem_chunks/handler21.mjs +3 -94
  22. package/dist/packem_chunks/handler22.mjs +1 -0
  23. package/dist/packem_chunks/handler23.mjs +2 -0
  24. package/dist/packem_chunks/handler24.mjs +99 -0
  25. package/dist/packem_chunks/handler25.mjs +2 -0
  26. package/dist/packem_chunks/handler26.mjs +9 -0
  27. package/dist/packem_chunks/handler27.mjs +1 -0
  28. package/dist/packem_chunks/handler3.mjs +1 -204
  29. package/dist/packem_chunks/handler4.mjs +3 -33
  30. package/dist/packem_chunks/handler5.mjs +1 -49
  31. package/dist/packem_chunks/handler6.mjs +1 -91
  32. package/dist/packem_chunks/handler7.mjs +1 -42
  33. package/dist/packem_chunks/handler8.mjs +2 -174
  34. package/dist/packem_chunks/handler9.mjs +1 -315
  35. package/dist/packem_chunks/runCodegenCommand.mjs +4 -52
  36. package/dist/packem_chunks/runDeployCommand.mjs +23 -592
  37. package/dist/packem_chunks/runDevCommand.mjs +2 -0
  38. package/dist/packem_chunks/runInitCommand.mjs +165 -1432
  39. package/dist/packem_chunks/runResetCommand.mjs +1 -41
  40. package/dist/packem_chunks/runRpcCommand.mjs +1 -68
  41. package/dist/packem_shared/COMMANDS-BpfWKiiZ.mjs +1 -0
  42. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-CyIsI3S1.mjs +1 -0
  43. package/dist/packem_shared/EXIT_CODE-D_XCixv8.mjs +1 -0
  44. package/dist/packem_shared/admin-token-VdUnvnKW.mjs +1 -0
  45. package/dist/packem_shared/admin-url-DJB8P6Jh.mjs +1 -0
  46. package/dist/packem_shared/api-spec-lYyq4vL6.mjs +1 -0
  47. package/dist/packem_shared/binding-manifest-file-CQ1eQYy1.mjs +2 -0
  48. package/dist/packem_shared/buildRegistryIndex-Dc8D8AR6.mjs +1 -0
  49. package/dist/packem_shared/cap-error-body-YBKO32BF.mjs +1 -0
  50. package/dist/packem_shared/catalog-CahzmDLV.mjs +1 -0
  51. package/dist/packem_shared/cli-CPcfZdt0.mjs +2 -0
  52. package/dist/packem_shared/cli-manifest-DoQ_3bzs.mjs +1 -0
  53. package/dist/packem_shared/cloudflare-credentials-BVmZnDZk.mjs +1 -0
  54. package/dist/packem_shared/codegen-error-AmH54ofi.mjs +3 -0
  55. package/dist/packem_shared/command-CB4JM-Jl.mjs +1 -0
  56. package/dist/packem_shared/commands-DvSfLvyt.mjs +13 -0
  57. package/dist/packem_shared/createLogger-4TujevyB.mjs +2 -0
  58. package/dist/packem_shared/createRecordingSpawner-Ch7zuQQE.mjs +1 -0
  59. package/dist/packem_shared/deploy-target-resolve-BTCW1Iv4.mjs +1 -0
  60. package/dist/packem_shared/diffSnapshots-htqNZT8Q.mjs +7 -0
  61. package/dist/packem_shared/docker-AqLAkOyY.mjs +1 -0
  62. package/dist/packem_shared/free-port-DjbFhaaT.mjs +1 -0
  63. package/dist/packem_shared/health-probe-Bw9JFfIy.mjs +1 -0
  64. package/dist/packem_shared/import-BRM1hs7j.mjs +12 -0
  65. package/dist/packem_shared/index-oHZBGBwB.mjs +1 -0
  66. package/dist/packem_shared/insertSchemaExtension-DuiV6cba.mjs +8 -0
  67. package/dist/packem_shared/lint-ignore-report-DKZagpqk.mjs +2 -0
  68. package/dist/packem_shared/mcp-config-file-CcDTeXIq.mjs +2 -0
  69. package/dist/packem_shared/open-url-EnKy--w-.mjs +1 -0
  70. package/dist/packem_shared/output-format-DWe_tkTR.mjs +2 -0
  71. package/dist/packem_shared/parseManifest-CwPTKdtS.mjs +1 -0
  72. package/dist/packem_shared/path-containment-CgxYZggb.mjs +1 -0
  73. package/dist/packem_shared/plan-DFY3CqHD.mjs +5 -0
  74. package/dist/packem_shared/planDevCommand-BDoJUgQ4.mjs +1 -0
  75. package/dist/packem_shared/platform-diagnostics-Cn2g6Jh-.mjs +4 -0
  76. package/dist/packem_shared/prompt-cancelled-BvsNxg_Q.mjs +1 -0
  77. package/dist/packem_shared/render-lunora-error-B6893vbU.mjs +2 -0
  78. package/dist/packem_shared/resolve-Bdc9KEUv.mjs +5 -0
  79. package/dist/packem_shared/resolve-target-C_ZloTvd.mjs +1 -0
  80. package/dist/packem_shared/runAddCommand-BChdJQ5L.mjs +1 -0
  81. package/dist/packem_shared/runExportCommand-slAq5BCF.mjs +5 -0
  82. package/dist/packem_shared/runMigrateGenerateCommand-BwemPIOz.mjs +11 -0
  83. package/dist/packem_shared/schemaIrToSnapshot-BHuNOfgs.mjs +1 -0
  84. package/dist/packem_shared/secret-key-8DPOH98z.mjs +1 -0
  85. package/dist/packem_shared/shared-D-zCOmgY.mjs +1 -0
  86. package/dist/packem_shared/storage-DNzYhXR-.mjs +1 -0
  87. package/dist/packem_shared/tui-prompts-B3YwUhGw.mjs +4 -0
  88. package/dist/packem_shared/variables-CPYyJtsL.mjs +1 -0
  89. package/dist/packem_shared/wrangler-name-Dsk5K1f-.mjs +1 -0
  90. package/dist/packem_shared/wrangler-secrets-CTQPpalP.mjs +1 -0
  91. package/package.json +39 -19
  92. package/skills/README.md +36 -17
  93. package/skills/lunora/SKILL.md +158 -65
  94. package/skills/lunora-cloudflare-audit/SKILL.md +163 -0
  95. package/skills/lunora-create-package/SKILL.md +97 -84
  96. package/skills/lunora-deploy/SKILL.md +175 -105
  97. package/skills/lunora-functions/SKILL.md +363 -56
  98. package/skills/lunora-migration-helper/SKILL.md +162 -137
  99. package/skills/lunora-performance-audit/SKILL.md +137 -95
  100. package/skills/lunora-quickstart/SKILL.md +137 -136
  101. package/skills/lunora-realtime/SKILL.md +217 -97
  102. package/skills/lunora-setup-auth/SKILL.md +162 -102
  103. package/skills/lunora-setup-hyperdrive/SKILL.md +130 -109
  104. package/skills/lunora-setup-hyperdrive-global/SKILL.md +93 -90
  105. package/skills/lunora-setup-mail/SKILL.md +85 -113
  106. package/skills/lunora-setup-scheduler/SKILL.md +132 -96
  107. package/skills/lunora-setup-storage/SKILL.md +189 -95
  108. package/dist/packem_chunks/planDevCommand.mjs +0 -541
  109. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +0 -397
  110. package/dist/packem_shared/COMMANDS-B0ftFD_3.mjs +0 -948
  111. package/dist/packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs +0 -244
  112. package/dist/packem_shared/admin-url-4UzT-CI4.mjs +0 -19
  113. package/dist/packem_shared/api-spec-CtA6ilu4.mjs +0 -13
  114. package/dist/packem_shared/buildRegistryIndex-BcYe607_.mjs +0 -38
  115. package/dist/packem_shared/codegen-error-DJG-ghs_.mjs +0 -31
  116. package/dist/packem_shared/command-D3lB_4Az.mjs +0 -19
  117. package/dist/packem_shared/commands-B-gR09Z_.mjs +0 -845
  118. package/dist/packem_shared/createLogger-B40gPzQo.mjs +0 -78
  119. package/dist/packem_shared/createRecordingSpawner-DxI3mebw.mjs +0 -43
  120. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +0 -61
  121. package/dist/packem_shared/diffSnapshots-BeDvvNiF.mjs +0 -161
  122. package/dist/packem_shared/docker-hMQ97KSQ.mjs +0 -21
  123. package/dist/packem_shared/insertSchemaExtension-DAqbfr9Z.mjs +0 -64
  124. package/dist/packem_shared/open-url-Dfq6fAyT.mjs +0 -41
  125. package/dist/packem_shared/output-format-wUvAN6AL.mjs +0 -17
  126. package/dist/packem_shared/parseArgs-YXFuKdEk.mjs +0 -56
  127. package/dist/packem_shared/parseManifest--vZf2FY1.mjs +0 -94
  128. package/dist/packem_shared/prompt-cancelled-APzX1Im-.mjs +0 -9
  129. package/dist/packem_shared/resolve-target-qbsJ_5sF.mjs +0 -16
  130. package/dist/packem_shared/runAddCommand-bnY6-HKb.mjs +0 -4
  131. package/dist/packem_shared/schema-drift-gate-BtBt0as0.mjs +0 -79
  132. package/dist/packem_shared/schemaIrToSnapshot-DdsljJT-.mjs +0 -43
  133. package/dist/packem_shared/storage-BIsph-Vk.mjs +0 -84
  134. package/dist/packem_shared/tui-prompts-BjEN8XgP.mjs +0 -658
  135. package/dist/packem_shared/wrangler-name-cy4yhm9j.mjs +0 -12
  136. 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,220 @@
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`, `--preview`, `--migrate`), 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` /
49
+ `--preview`, and when the worker doesn't exist yet.
50
+ 8. **Services, then `wrangler deploy`** — services declared in
51
+ `lunora.config` deploy first (`--skip-services` to skip), then the app.
52
+ 9. **After a successful deploy** — records the URL as the project link
53
+ (`lunora link`), optionally runs `--migrate` and `--health-check`, and only
54
+ then re-blesses the schema baseline.
55
+
56
+ `lunora prepare` runs steps 1–6 without deploying (useful in CI), but it takes
57
+ no `--env`, so it validates the top-level config only. `lunora verify` runs
58
+ validation, a codegen dry run and `tsc --noEmit` without writing files, and does
59
+ take `--env`.
60
+
61
+ Flags worth knowing:
62
+
63
+ | Flag | Effect |
64
+ | -------------------------- | ------------------------------------------------------------------------------- |
65
+ | `--env <name>` | Deploy a wrangler environment; checks read that env's (non-inherited) bindings. |
66
+ | `--dry-run` | Run every gate and bundle without publishing; `wrangler.jsonc` edits roll back. |
67
+ | `--preview` | Upload a version (`wrangler versions upload`) with a preview URL; no traffic. |
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; the baseline is not advanced. |
72
+ | `--update-schema-baseline` | Accept the current schema as the new baseline. |
73
+
74
+ `--migrate` is checked before wrangler runs, so a missing requirement aborts the
75
+ whole deploy:
76
+
77
+ - `--migrate-yes` — confirms running production data migrations.
78
+ - `--migrate-url <https://…>` — required unless this checkout is linked for the
79
+ same `--env` (a previous successful deploy links it automatically).
80
+ - An admin token — `--migrate-token` or `LUNORA_ADMIN_TOKEN`.
15
81
 
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).
41
-
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).
46
-
47
- ## Preflight: `lunora doctor`
48
-
49
- Run the read-only preflight before deploying. It checks:
82
+ ```bash
83
+ lunora deploy --migrate --migrate-yes --migrate-url https://app.example.com
84
+ ```
50
85
 
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.
86
+ ## Preflight
56
87
 
57
88
  ```bash
58
- lunora doctor # FAIL → exit 1; WARN/INFO don't block
89
+ lunora doctor # read-only; exits 1 on any FAIL, WARN/INFO don't block
90
+ lunora doctor --format json # each finding carries a stable `code` for CI/agents
91
+ lunora verify --env production
59
92
  ```
60
93
 
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.
94
+ `lunora doctor` fails on a missing or invalid `wrangler.jsonc`, a missing
95
+ `SHARD` binding, a placeholder D1 id (`d1-placeholder-id`), and a binding whose
96
+ class the worker doesn't export (`wrangler-class-unexported`), and an Artifacts
97
+ namespace in the wrong jurisdiction. It warns on unfilled `.dev.vars` secrets,
98
+ placeholder `send_email` destinations, an account near its Durable Object class
99
+ cap, and a `cloudflare.config.ts` (`cf-config-present`).
63
100
 
64
- ## `wrangler.jsonc` — the binding contract
101
+ ## `wrangler.jsonc`
65
102
 
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:
103
+ Templates ship this shape. The `d1_databases` and `r2_buckets` entries are
104
+ only needed when the app uses `.global()` tables or auth (D1) and storage (R2):
68
105
 
69
106
  ```jsonc
70
107
  {
71
108
  "name": "my-app",
72
- "main": "src/index.ts",
73
- "compatibility_date": "2026-04-07",
109
+ "main": "src/server.ts",
110
+ "compatibility_date": "2026-10-01",
74
111
  "compatibility_flags": ["nodejs_compat"],
75
112
  "durable_objects": {
76
113
  "bindings": [
77
114
  { "name": "SHARD", "class_name": "ShardDO" },
78
- { "name": "SESSION", "class_name": "SessionDO" }, // only with @lunora/auth
115
+ { "name": "SHARD_REGISTRY", "class_name": "ShardRegistryDO" },
79
116
  ],
80
117
  },
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" }],
118
+ "migrations": [{ "tag": "v1", "new_sqlite_classes": ["ShardDO", "ShardRegistryDO"] }],
119
+ "limits": { "cpu_ms": 30000 },
120
+ "d1_databases": [{ "binding": "DB", "database_name": "my-app", "database_id": "<replace-with-d1-create-id>" }],
121
+ "r2_buckets": [{ "binding": "UPLOADS", "bucket_name": "my-app-uploads" }],
84
122
  }
85
123
  ```
86
124
 
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:
125
+ Reconcile adds `SCHEDULER` → `SchedulerDO` when the app schedules work, and
126
+ bindings for generated containers, workflows and agents. Every `class_name`
127
+ must be exported by the worker entry or wrangler rejects the deploy.
128
+
129
+ Lunora writes the bindings but cannot create the remote resources:
90
130
 
91
131
  ```bash
92
- wrangler d1 create my-app-global # paste the returned database_id into wrangler.jsonc
93
- wrangler r2 bucket create my-app-files
132
+ wrangler d1 create my-app # paste the returned database_id into wrangler.jsonc
133
+ wrangler r2 bucket create my-app-uploads
94
134
  ```
95
135
 
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.
136
+ When using `--env`, remember that `vars`, `d1_databases`, `durable_objects` and
137
+ `containers` do not inherit in wrangler. Each environment needs its own entries,
138
+ including its own D1 database.
100
139
 
101
- ## Secrets: `wrangler secret`, not `.dev.vars`
140
+ ## Secrets
102
141
 
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:
142
+ `.dev.vars` is for development only: it is git-ignored and `wrangler deploy`
143
+ never uploads it. Production secrets live in Cloudflare:
106
144
 
107
145
  ```bash
108
- wrangler secret put BETTER_AUTH_SECRET # prompts for the value, stored encrypted
109
- wrangler secret list
146
+ lunora env diff --env production # keys local-only vs set remotely
147
+ lunora env push --env production --yes # uploads .dev.vars values; refuses placeholders
148
+ wrangler secret put RESEND_API_KEY --env production # set a prod-only value by hand
110
149
  ```
111
150
 
112
- Set every secret your app reads (mirror the secret-looking keys in `.dev.vars`)
113
- before the first request hits production.
151
+ `lunora env push` uploads the values from `.dev.vars`. If production needs
152
+ different values than dev (it usually does for provider keys), set those with
153
+ `wrangler secret put` instead.
154
+
155
+ ## Schema-drift gate
156
+
157
+ The gate blocks when the schema changed in a breaking way since
158
+ `lunora/.lunora-schema.json` and no new `defineMigration` on the affected table
159
+ covers it. The block message lists each change and a paste-ready
160
+ `lunora migrate create … --table …` command. Commit the baseline file so CI
161
+ and every machine gate against the same shape. It is advanced only after a
162
+ successful deploy, so a failed deploy never moves it past a change that didn't
163
+ ship.
164
+
165
+ Reach for `--allow-schema-drift` / `--update-schema-baseline` only when you know
166
+ existing data is compatible. Otherwise stage the change with
167
+ `lunora-migration-helper`.
168
+
169
+ ## `.global()` table DDL
114
170
 
115
- ## Dev vs Production
171
+ The runtime provisions `.global()` tables itself on first use
172
+ (`CREATE TABLE IF NOT EXISTS` plus additive `ADD COLUMN` / indexes), so additive
173
+ changes need no step. `lunora migrate generate` writes a reviewable SQL file for
174
+ D1-backed global tables, but no command applies it, `lunora deploy` included.
175
+ Destructive or hand-written statements in it (drops, a backfilling `UPDATE`, a
176
+ rename) must be applied by hand:
116
177
 
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.
178
+ ```bash
179
+ wrangler d1 execute DB --remote --file=lunora/migrations/<file>.sql
180
+ ```
181
+
182
+ See `lunora-migration-helper` for the full flow.
121
183
 
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`.
184
+ ## Rollback
126
185
 
127
- ## Common Pitfalls
186
+ ```bash
187
+ lunora cloudflare deployments list
188
+ lunora cloudflare deployments rollback --yes # previous version
189
+ lunora cloudflare deployments promote <version-id> --yes # send 100% of traffic to a version
190
+ ```
128
191
 
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.
192
+ Rolling back code does not roll back data migrations or D1 DDL.
193
+
194
+ ## Common pitfalls
195
+
196
+ 1. **Placeholder `database_id`.** Reconcile writes
197
+ `<replace-with-d1-create-id>`; deploy refuses it. Run `wrangler d1 create`
198
+ and paste the id.
199
+ 2. **DO class not exported.** Export `ShardDO` (and any generated container /
200
+ workflow class) from the worker entry; `lunora doctor` names the gap.
201
+ 3. **Secrets only in `.dev.vars`.** They never reach production. Push or
202
+ `wrangler secret put` them before the first request.
203
+ 4. **Dev and prod sharing resources.** Give each environment its own D1
204
+ database, R2 buckets and secrets; never point a dev worker at prod data.
205
+ 5. **Uncommitted schema baseline.** Commit `lunora/.lunora-schema.json` (and
206
+ `lunora/migrations/.snapshot.json` if you use `migrate generate`).
207
+ `lunora/_generated/` is regenerated by deploy; templates git-ignore it.
208
+ 6. **Using `cf dev` / `cf build` / `cf deploy`.** They read
209
+ `cloudflare.config.ts`, which Lunora never updates
210
+ ([#964](https://github.com/anolilab/lunora/issues/964)). Use `lunora dev` /
211
+ `lunora deploy`; `cf` resource commands (zones, DNS, KV) are fine.
140
212
 
141
213
  ## Checklist
142
214
 
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.
215
+ - [ ] `lunora doctor` has no FAIL; `lunora verify --env <name>` passes in CI.
216
+ - [ ] D1 / R2 resources created per environment; real ids in `wrangler.jsonc`.
217
+ - [ ] Every DO / container / workflow `class_name` is exported by the worker entry.
218
+ - [ ] Production secrets set (`lunora env diff --env <name>` shows nothing local-only that prod needs).
219
+ - [ ] Breaking schema changes staged with a migration; drift gate green.
220
+ - [ ] `lunora deploy --env <name> --health-check` succeeded; `--migrate --migrate-yes` run if backfills were pending.