@lunora/config 1.0.0-alpha.26 → 1.0.0-alpha.261

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 (100) hide show
  1. package/LICENSE.md +135 -0
  2. package/README.md +3 -3
  3. package/dist/cloudflare/index.d.mts +969 -0
  4. package/dist/cloudflare/index.d.ts +969 -0
  5. package/dist/cloudflare/index.mjs +1 -0
  6. package/dist/index.d.mts +864 -921
  7. package/dist/index.d.ts +864 -921
  8. package/dist/index.mjs +1 -23
  9. package/dist/packem_shared/ACCENT-CotPTfv7.mjs +7 -0
  10. package/dist/packem_shared/AGENT_MODE_ENV-D8MvSwPE.mjs +1 -0
  11. package/dist/packem_shared/AGENT_RULES_DIR-C54Ta_vz.mjs +1 -0
  12. package/dist/packem_shared/ALLOW_FORWARDED_ENV-BUndghJx.mjs +1 -0
  13. package/dist/packem_shared/BINDING_MANIFEST_VERSION-B8dwEKLn.mjs +1 -0
  14. package/dist/packem_shared/CLOUDFLARE_DRIVER-Ci9oJMYb.mjs +1 -0
  15. package/dist/packem_shared/CODEGEN_ENV-ZsCxV1kP.mjs +1 -0
  16. package/dist/packem_shared/COMPOSED_WORKER_ENTRY-Bj793dir.mjs +1 -0
  17. package/dist/packem_shared/DEFAULT_DEPLOY_TARGET-B-e9mqgX.mjs +1 -0
  18. package/dist/packem_shared/DEV_BINDINGS_FILE-pyOiNLP4.mjs +4 -0
  19. package/dist/packem_shared/DEV_VARS_EXAMPLE_FILE-CqLorAxT.mjs +6 -0
  20. package/dist/packem_shared/LINKED_PROJECT_DIR-Ba5qPEwo.mjs +2 -0
  21. package/dist/packem_shared/LUNORA_EVENT_SOURCE-DYdcDU9G.mjs +1 -0
  22. package/dist/packem_shared/LUNORA_IGNORED_PATHS-BHABA9u-.mjs +19 -0
  23. package/dist/packem_shared/LunoraReporter-f277GLNT.mjs +6 -0
  24. package/dist/packem_shared/PACKAGE_SECRETS_REGISTRY-De7gLGIy.mjs +1 -0
  25. package/dist/packem_shared/POLICY_SCAFFOLD_ENDPOINT-0QiGKazX.mjs +1 -0
  26. package/dist/packem_shared/REMOTE_ELIGIBLE_KEYS-D-edJjVY.mjs +1 -0
  27. package/dist/packem_shared/REQUIRED_COMPATIBILITY_DATE-SdRHaUwh.mjs +1 -0
  28. package/dist/packem_shared/SCHEMA_EDIT_ENDPOINT-Dbt0faMd.mjs +1 -0
  29. package/dist/packem_shared/SEED_ENDPOINT-2B56PN_H.mjs +1 -0
  30. package/dist/packem_shared/WORKERS_CACHE_MIN_DATE-0k9ABJtF.mjs +1 -0
  31. package/dist/packem_shared/WRANGLER_FILES-UNW-xY5u.mjs +1 -0
  32. package/dist/packem_shared/addArgsFor-CWb4cPij.mjs +1 -0
  33. package/dist/packem_shared/applyAdditiveEdit-CH0VhMJ2.mjs +6 -0
  34. package/dist/packem_shared/applyStudioAssetCache-DHQEJuuv.mjs +1 -0
  35. package/dist/packem_shared/assetContentType-CUb5bpbI.mjs +1 -0
  36. package/dist/packem_shared/buildPackageSecretsBlock-yv9mjKEW.mjs +19 -0
  37. package/dist/packem_shared/classifyPolicyEdit-D8NwrHxR.mjs +24 -0
  38. package/dist/packem_shared/collectExportGaps-BJw9LdRV.mjs +2 -0
  39. package/dist/packem_shared/collectWranglerSecretVariables-D-usPfT8.mjs +1 -0
  40. package/dist/packem_shared/createConfirm-D5mKTMHj.mjs +7 -0
  41. package/dist/packem_shared/describePreservedCrons-Ci5r2aph.mjs +1 -0
  42. package/dist/packem_shared/detectFramework-CUiVEHDQ.mjs +1 -0
  43. package/dist/packem_shared/discover-info-Vj9d4qYt.mjs +1 -0
  44. package/dist/packem_shared/discoverAgentInfo-CTdCSfYM.mjs +1 -0
  45. package/dist/packem_shared/discoverContainerInfo-EhZtEBQs.mjs +1 -0
  46. package/dist/packem_shared/discoverSchemaInfo-DJvevsCm.mjs +1 -0
  47. package/dist/packem_shared/discoverWorkflowInfo-BsPvlo6O.mjs +1 -0
  48. package/dist/packem_shared/interpretRemote-CQ06qpjM.mjs +1 -0
  49. package/dist/packem_shared/jsonc-edit-BZgQIdAH.mjs +1 -0
  50. package/dist/packem_shared/lunora-manifest-V77OcyFC.mjs +6 -0
  51. package/dist/packem_shared/package-secrets-registry-DvtTiK4b.mjs +1 -0
  52. package/dist/packem_shared/parseDevVariable-teAAahVD.mjs +1 -0
  53. package/dist/packem_shared/parseSchema-D1Wbr1qv.mjs +1 -0
  54. package/dist/packem_shared/policy-scaffold.d-D2H4_G01.d.mts +86 -0
  55. package/dist/packem_shared/policy-scaffold.d-D2H4_G01.d.ts +86 -0
  56. package/dist/packem_shared/reconcileWranglerCompatibilityDate-7OO_xaKR.mjs +1 -0
  57. package/dist/packem_shared/renderStudioHtml-ByowDtMl.mjs +15 -0
  58. package/dist/packem_shared/runPostCodegenHook-CVrOTiGB.mjs +1 -0
  59. package/dist/packem_shared/schema-info.d-DvKkJ7HJ.d.mts +331 -0
  60. package/dist/packem_shared/schema-info.d-DvKkJ7HJ.d.ts +331 -0
  61. package/dist/packem_shared/serveJsonHandler-Dv0EdCb9.mjs +1 -0
  62. package/dist/packem_shared/streamContainerLogs-0nko1qpv.mjs +2 -0
  63. package/dist/packem_shared/wranglerToAlchemy-SDoS_XY0.mjs +6 -0
  64. package/dist/packem_shared/write-atomic-9d9xs5ON.mjs +1 -0
  65. package/dist/studio-host/index.d.mts +250 -95
  66. package/dist/studio-host/index.d.ts +250 -95
  67. package/dist/studio-host/index.mjs +1 -7
  68. package/package.json +15 -7
  69. package/dist/packem_shared/ACCENT-DW1XJn8i.mjs +0 -40
  70. package/dist/packem_shared/AGENT_RULES_DIR-lcgC08aE.mjs +0 -40
  71. package/dist/packem_shared/DEV_VARS_EXAMPLE_FILE-dJPNTEnK.mjs +0 -37
  72. package/dist/packem_shared/LINKED_PROJECT_DIR-CXwXzV_C.mjs +0 -52
  73. package/dist/packem_shared/LUNORA_CONFIG_FILE-CtcIcB5-.mjs +0 -34
  74. package/dist/packem_shared/LUNORA_EVENT_SOURCE-D2fDeGB6.mjs +0 -86
  75. package/dist/packem_shared/LunoraReporter-Ci-bDCK9.mjs +0 -70
  76. package/dist/packem_shared/PACKAGE_SECRETS_REGISTRY-B8t_SdoZ.mjs +0 -70
  77. package/dist/packem_shared/POLICY_SCAFFOLD_ENDPOINT-CiC2IGKx.mjs +0 -103
  78. package/dist/packem_shared/REMOTE_ELIGIBLE_KEYS-BC7_e9Bz.mjs +0 -105
  79. package/dist/packem_shared/REQUIRED_COMPATIBILITY_DATE-2YYyWJUq.mjs +0 -561
  80. package/dist/packem_shared/SCHEMA_EDIT_ENDPOINT-Df-Wrix-.mjs +0 -99
  81. package/dist/packem_shared/SEED_ENDPOINT-DVCjaGO-.mjs +0 -61
  82. package/dist/packem_shared/WRANGLER_FILES-DwSuC-Kn.mjs +0 -25
  83. package/dist/packem_shared/applyAdditiveEdit-C-snTFEV.mjs +0 -228
  84. package/dist/packem_shared/buildPackageSecretsBlock-DWDKHViT.mjs +0 -243
  85. package/dist/packem_shared/classifyPolicyEdit-BHeAqF8P.mjs +0 -99
  86. package/dist/packem_shared/createConfirm-fvpdgJ9s.mjs +0 -100
  87. package/dist/packem_shared/detectFramework-Br-BcPBq.mjs +0 -41
  88. package/dist/packem_shared/discoverContainerInfo-BXFs6Wav.mjs +0 -19
  89. package/dist/packem_shared/discoverSchemaInfo-BB-CKlTK.mjs +0 -25
  90. package/dist/packem_shared/discoverWorkflowInfo-CedvR0mn.mjs +0 -19
  91. package/dist/packem_shared/inferLunoraBindings-ByMwG-gH.mjs +0 -352
  92. package/dist/packem_shared/loadStudioAssets-Csk5RS4E.mjs +0 -28
  93. package/dist/packem_shared/parseDevVariable-CJiq2IwE.mjs +0 -30
  94. package/dist/packem_shared/parseSchema-DSeyktvG.mjs +0 -107
  95. package/dist/packem_shared/policy-scaffold.d-DCmwn7zQ.d.mts +0 -74
  96. package/dist/packem_shared/policy-scaffold.d-DCmwn7zQ.d.ts +0 -74
  97. package/dist/packem_shared/reconcileWranglerBindings-Cruwwx1M.mjs +0 -334
  98. package/dist/packem_shared/renderStudioHtml-449Ysn75.mjs +0 -37
  99. package/dist/packem_shared/serveJsonHandler-B4OLTGLS.mjs +0 -86
  100. package/dist/packem_shared/streamContainerLogs-BZ4cOZwH.mjs +0 -157
@@ -0,0 +1,969 @@
1
+ import { D as DeployDriver, G as GeneratedClassModule, I as InferredBindings, S as SchemaInfo } from "../packem_shared/schema-info.d-DvKkJ7HJ.js";
2
+ import { WranglerVariableIR } from '@lunora/codegen';
3
+ /**
4
+ * Translate a `wrangler.jsonc` into an [Alchemy](https://alchemy.run) program.
5
+ *
6
+ * # Why translate rather than ask for a second config
7
+ *
8
+ * `wrangler.jsonc` is already the source of truth for what an app needs, and
9
+ * Lunora already infers and reconciles it — `inferLunoraBindings` decides that
10
+ * a project needs a shard namespace and a bucket, `reconcileWranglerBindings`
11
+ * writes them. Asking a developer to restate all of that in an
12
+ * `alchemy.run.ts` would give the project two sources of truth that drift, and
13
+ * the drift would surface as a deploy that provisions something the app does
14
+ * not bind.
15
+ *
16
+ * So Alchemy is an implementation detail of `deploy`, not a thing to configure:
17
+ * read the config, emit the program, run it.
18
+ *
19
+ * # Why this emits source rather than calling Alchemy
20
+ *
21
+ * `alchemy@0.93` has thirty dependencies, nine of them Node-shaped —
22
+ * `wrangler`, `miniflare`, `esbuild`, `execa`, `find-process`, `glob`, `open`,
23
+ * `proper-lockfile`, `signal-exit`. `@lunora/config` is imported by
24
+ * `@lunora/vite`, so importing Alchemy here would push that tree into every
25
+ * project that merely wanted to read `lunora.config.*`, and into any bundle
26
+ * targeting workerd — where none of it survives.
27
+ *
28
+ * Emitting text keeps this module pure and dependency-free. Alchemy is invoked
29
+ * as a CLI against the generated file, so it only has to exist on the machine
30
+ * that deploys.
31
+ *
32
+ * # Adoption, not re-creation
33
+ *
34
+ * Every resource is emitted with `adopt: true`. A project translated from an
35
+ * existing `wrangler.jsonc` already *has* its D1 database and its bucket, with
36
+ * data in them. Without adoption Alchemy would treat them as new and try to
37
+ * create alongside — the one outcome a deploy must never have.
38
+ */
39
+ /** A Durable Object binding as `wrangler.jsonc` spells it. */
40
+ interface WranglerDurableObjectBinding$1 {
41
+ class_name?: string;
42
+ name?: string;
43
+ script_name?: string;
44
+ }
45
+ /** The slice of `wrangler.jsonc` that translates into Alchemy resources. */
46
+ interface WranglerConfigShape {
47
+ compatibility_date?: string;
48
+ compatibility_flags?: ReadonlyArray<string>;
49
+ d1_databases?: ReadonlyArray<{
50
+ binding?: string;
51
+ database_id?: string;
52
+ database_name?: string;
53
+ }>;
54
+ durable_objects?: {
55
+ bindings?: ReadonlyArray<WranglerDurableObjectBinding$1>;
56
+ };
57
+ kv_namespaces?: ReadonlyArray<{
58
+ binding?: string;
59
+ id?: string;
60
+ }>;
61
+ main?: string;
62
+ /** `new_sqlite_classes` marks which DO classes get SQLite storage — Alchemy needs that per namespace. */
63
+ migrations?: ReadonlyArray<{
64
+ new_classes?: ReadonlyArray<string>;
65
+ new_sqlite_classes?: ReadonlyArray<string>;
66
+ }>;
67
+ name?: string;
68
+ /** Only `producers` translate; a `consumers` entry is dropped and reported as `queues.consumers`. */
69
+ queues?: {
70
+ consumers?: ReadonlyArray<{
71
+ queue?: string;
72
+ }>;
73
+ producers?: ReadonlyArray<{
74
+ binding?: string;
75
+ queue?: string;
76
+ }>;
77
+ };
78
+ r2_buckets?: ReadonlyArray<{
79
+ binding?: string;
80
+ bucket_name?: string;
81
+ }>;
82
+ triggers?: {
83
+ crons?: ReadonlyArray<string>;
84
+ };
85
+ vars?: Readonly<Record<string, unknown>>;
86
+ }
87
+ /** What the translation could not carry over, so the caller can say so out loud. */
88
+ interface AlchemyTranslation {
89
+ /** The emitted program source. */
90
+ source: string;
91
+ /**
92
+ * Bindings present in `wrangler.jsonc` that this translation drops.
93
+ *
94
+ * Reported rather than silently omitted: a deploy that quietly loses a
95
+ * Vectorize index produces a worker whose `env.POSTS_SEARCH` is undefined
96
+ * at runtime, and nothing in the build says why.
97
+ */
98
+ unsupported: ReadonlyArray<string>;
99
+ }
100
+ /**
101
+ * Translate a parsed `wrangler.jsonc` into an Alchemy program.
102
+ *
103
+ * Pure: it reads nothing and writes nothing, so the caller decides where the
104
+ * source lands and the whole thing stays testable as a string comparison.
105
+ * @param config The parsed `wrangler.jsonc`.
106
+ * @returns the program source, plus whatever could not be carried over.
107
+ */
108
+ declare const wranglerToAlchemy: (config: WranglerConfigShape) => AlchemyTranslation;
109
+ /** Schema version of the emitted document, so a consumer can gate on shape changes. */
110
+ declare const BINDING_MANIFEST_VERSION = 1;
111
+ /**
112
+ * One resource the built Worker expects to be bound to. `binding` is the name the
113
+ * Worker reads off `env`; the remaining fields are whatever identifies the
114
+ * resource for that type, omitted when the config does not carry them (an id
115
+ * Lunora could not mint is absent here exactly as it is absent from the config).
116
+ */
117
+ interface BindingRequirement {
118
+ /** The `env` property name the Worker reads. */
119
+ binding: string;
120
+ /**
121
+ * Durable Object / Workflow / Container class the Worker must export. Present
122
+ * for the class-backed binding types, so a deployer can assert the bundle
123
+ * exports it before publishing.
124
+ */
125
+ className?: string;
126
+ /** Bucket name (`r2`), database name (`d1`), dataset (`analytics_engine`), queue name (`queue`), index (`vectorize`), pipeline (`pipelines`). */
127
+ resource?: string;
128
+ /** Remote resource id, when the config declares one (`d1`, `kv`, `hyperdrive`). */
129
+ resourceId?: string;
130
+ /** For `durable_object`: whether the class uses SQLite storage (`new_sqlite_classes`). */
131
+ sqlite?: boolean;
132
+ /** The kind of resource, keyed to the wrangler section it came from. */
133
+ type: "ai" | "analytics_engine" | "assets" | "browser" | "container" | "d1" | "durable_object" | "hyperdrive" | "images" | "kv" | "pipeline" | "queue_consumer" | "queue_producer" | "r2" | "vectorize" | "workflow";
134
+ }
135
+ /** The document the `lunora build --emit-bindings` flag writes. */
136
+ interface BindingManifest {
137
+ /** Every resource the Worker expects, sorted by `type` then `binding` so the file is diff-stable. */
138
+ bindings: ReadonlyArray<BindingRequirement>;
139
+ /** `compatibility_date`, when declared. */
140
+ compatibilityDate?: string;
141
+ /** `compatibility_flags`, when declared. */
142
+ compatibilityFlags?: ReadonlyArray<string>;
143
+ /** Cron expressions the Worker must be triggered on (`triggers.crons`). */
144
+ crons: ReadonlyArray<string>;
145
+ /** Worker name from `wrangler.jsonc`. */
146
+ name?: string;
147
+ /**
148
+ * Wrangler sections present in the config that this version does not model,
149
+ * by field name. Empty for a fully-understood config; non-empty is a prompt to
150
+ * extend the collector, and is surfaced to the user rather than silently
151
+ * dropped.
152
+ */
153
+ unknown: ReadonlyArray<string>;
154
+ /** Names of `vars` entries. Values are deliberately excluded — a manifest is committed and read by CI, and a `vars` entry can hold a value a project would rather not publish. */
155
+ vars: ReadonlyArray<string>;
156
+ /** Schema version — {@link BINDING_MANIFEST_VERSION}. */
157
+ version: number;
158
+ }
159
+ /**
160
+ * The wider config this reads. {@link WranglerConfigShape} covers what the
161
+ * Alchemy translation models; a manifest additionally reports the sections that
162
+ * translation lists as unsupported, so those are declared here.
163
+ */
164
+ interface ManifestConfigShape extends WranglerConfigShape {
165
+ ai?: {
166
+ binding?: string;
167
+ };
168
+ analytics_engine_datasets?: ReadonlyArray<{
169
+ binding?: string;
170
+ dataset?: string;
171
+ }>;
172
+ /** Static assets carry a real `binding` the Worker reads (`env.ASSETS`). */
173
+ assets?: {
174
+ binding?: string;
175
+ directory?: string;
176
+ };
177
+ browser?: {
178
+ binding?: string;
179
+ };
180
+ containers?: ReadonlyArray<{
181
+ class_name?: string;
182
+ image?: string;
183
+ max_instances?: number;
184
+ }>;
185
+ hyperdrive?: ReadonlyArray<{
186
+ binding?: string;
187
+ id?: string;
188
+ }>;
189
+ images?: {
190
+ binding?: string;
191
+ };
192
+ pipelines?: ReadonlyArray<{
193
+ binding?: string;
194
+ pipeline?: string;
195
+ }>;
196
+ /** Adds `consumers` — the Alchemy translation models producers only. */
197
+ queues?: {
198
+ consumers?: ReadonlyArray<{
199
+ queue?: string;
200
+ }>;
201
+ producers?: ReadonlyArray<{
202
+ binding?: string;
203
+ queue?: string;
204
+ }>;
205
+ };
206
+ vectorize?: ReadonlyArray<{
207
+ binding?: string;
208
+ index_name?: string;
209
+ }>;
210
+ workflows?: ReadonlyArray<{
211
+ binding?: string;
212
+ class_name?: string;
213
+ name?: string;
214
+ }>;
215
+ }
216
+ /**
217
+ * Build the manifest for a parsed `wrangler.jsonc`.
218
+ *
219
+ * Pure: reads nothing and writes nothing, so the caller decides where the
220
+ * document lands and the mapping stays testable as a plain object comparison.
221
+ * @param config The parsed, already-reconciled `wrangler.jsonc`.
222
+ * @returns the requirements document.
223
+ */
224
+ declare const buildBindingManifest: (config: ManifestConfigShape) => BindingManifest;
225
+ /** The Cloudflare deploy driver. */
226
+ declare const CLOUDFLARE_DRIVER: DeployDriver;
227
+ /**
228
+ * A container/workflow that is declared (so codegen emits its class) but the
229
+ * worker entry never re-exports — the one wiring step the generators can't always
230
+ * do for the developer. wrangler rejects a `class_name` the deployed worker
231
+ * doesn't export, so a deploy fails late on this; surfacing it as structured data
232
+ * lets the Vite plugin raise it in the dev error overlay (not just the console)
233
+ * the moment the gap appears. The human-readable form is also folded into
234
+ * {@link ReconcileBindingsResult.warnings}.
235
+ */
236
+ interface ExportGap {
237
+ /** Generated class wrangler needs exported, e.g. `OrderPipelineWorkflow`. */
238
+ className: string;
239
+ /** The `lunora/{agents,containers,workflows}.ts` export name, e.g. `orderPipeline`. */
240
+ exportName: string;
241
+ /** Which declaration is unexported. */
242
+ kind: "agent" | "container" | "workflow";
243
+ /** The `_generated/{module}` to re-export from, e.g. `workflows`. */
244
+ module: GeneratedClassModule;
245
+ }
246
+ interface ReconcileBindingsResult {
247
+ /** Short labels for each binding written (e.g. `"SCHEDULER/SchedulerDO"`). */
248
+ added: string[];
249
+ /** `true` when `wrangler.jsonc` was rewritten. */
250
+ changed: boolean;
251
+ /**
252
+ * Declared containers/workflows the worker entry doesn't re-export — the
253
+ * structured form of the corresponding `warnings` entries, for the dev error
254
+ * overlay. Empty when every declaration is wired.
255
+ */
256
+ exportGaps: ExportGap[];
257
+ /** Reason reconciliation was skipped, for logging. */
258
+ reason?: string;
259
+ /**
260
+ * Short labels for each EXISTING entry whose settings were brought in line
261
+ * with its declaration (e.g. `"queues.consumers/receipt-queue (max_retries)"`).
262
+ * Kept apart from `added` so a retune is not logged as a new binding.
263
+ */
264
+ updated: string[];
265
+ /** Non-fatal hints for capabilities that cannot be auto-provisioned. */
266
+ warnings: string[];
267
+ /** Resolved wrangler path, or `undefined` when none was found. */
268
+ wranglerPath?: string;
269
+ }
270
+ /**
271
+ * The declared-but-not-re-exported containers and workflows, as structured
272
+ * {@link ExportGap}s. The same gaps `collectWarnings` renders into prose — kept
273
+ * here as data so the Vite plugin can raise them in the error overlay with a
274
+ * precise remediation, rather than re-parsing the warning strings.
275
+ *
276
+ * Exported because reconciliation is not the only caller that needs the answer:
277
+ * `lunora codegen` and `lunora doctor` both want the gaps WITHOUT rewriting
278
+ * `wrangler.jsonc`, which is the rest of what {@link reconcileWranglerBindings}
279
+ * does. Deriving them again from `inferred.{containers,workflows,agents}` at each
280
+ * call site is how one kind ends up silently uncovered.
281
+ */
282
+ declare const collectExportGaps: (inferred: InferredBindings) => ExportGap[];
283
+ /**
284
+ * Reconcile inferred Durable Object / D1 bindings into `wrangler.jsonc`.
285
+ *
286
+ * Writes only when something is missing; returns `changed: false` when the
287
+ * config already satisfies the inferred needs.
288
+ *
289
+ * `environment`, when passed, does NOT change where this provisions — every
290
+ * step below still only ADDS to the TOP-LEVEL config; wrangler's `env.<name>`
291
+ * blocks have no auto-provisioning path today. The one write into the block is
292
+ * {@link reconcileEnvQueues}, which retunes queue consumers the block already
293
+ * declares and adds nothing. Otherwise it is used only to emit an
294
+ * advisory warning, because bindings (`durable_objects`, `d1_databases`, …)
295
+ * are non-inheritable (see `wrangler-validator.ts`'s `NON_INHERITABLE_KEYS`):
296
+ * a `--env production` deploy needs its OWN copy of each one, and silently
297
+ * writing only to the top level would leave that gap unmentioned. Extending
298
+ * the JSONC writer itself to target `env.<name>.*` idempotently for every
299
+ * binding kind here is a separate, larger change (each of the ~10 pipeline
300
+ * steps below reads AND writes the top-level path) that this fix does not
301
+ * attempt — `lunora deploy --env <name>` now VALIDATES the env-scoped view
302
+ * (closing the reported gap), it just doesn't yet auto-provision it.
303
+ */
304
+ declare const reconcileWranglerBindings: (projectRoot: string, inferred: InferredBindings, environment?: string) => ReconcileBindingsResult;
305
+ interface ReconcileCompatibilityDateResult {
306
+ /** `true` when `wrangler.jsonc` was rewritten. */
307
+ changed: boolean;
308
+ /** The new date value, or the existing one when unchanged. */
309
+ date: string | undefined;
310
+ /** Human-readable reason when reconciliation was skipped. */
311
+ reason?: string;
312
+ /** Resolved wrangler path, or `undefined` when none was found. */
313
+ wranglerPath?: string;
314
+ }
315
+ /**
316
+ * Reconcile the `compatibility_date` in `wrangler.jsonc` when Workers Cache
317
+ * is enabled but the date is below the minimum required.
318
+ */
319
+ declare const reconcileWranglerCompatibilityDate: (projectRoot: string) => ReconcileCompatibilityDateResult;
320
+ interface ReconcileResult {
321
+ /** `true` when the wrangler config's `triggers.crons` was rewritten. */
322
+ changed: boolean;
323
+ /**
324
+ * Entries left in `triggers.crons` that this reconciler did not generate and
325
+ * does not own — a hand-written `backupCron` trigger, or anything already
326
+ * there before ownership was first recorded. Non-empty means the array is
327
+ * NOT the codegen-derived set, which is the surprising half of what this
328
+ * does and the one thing worth printing.
329
+ */
330
+ preserved: string[];
331
+ /** Human-readable reason when reconciliation was skipped (for logging). */
332
+ reason?: string;
333
+ /**
334
+ * Problems with the ownership record itself, for the caller to `warn`. A
335
+ * damaged `lunora.crons` is not fatal — the reconciler degrades to add-only
336
+ * — but the degradation is invisible: the generated cron it wrote last pass
337
+ * is reported back as hand-written and is then kept for good. Silence is
338
+ * what turns a merge conflict into a permanent orphan.
339
+ */
340
+ warnings: string[];
341
+ /** Resolved wrangler path, or `undefined` when none was found. */
342
+ wranglerPath?: string;
343
+ }
344
+ /**
345
+ * Reconcile the codegen-derived cron schedules into the project's wrangler
346
+ * config `triggers.crons` array, preserving comments and formatting via
347
+ * `jsonc-parser`'s structural edits.
348
+ *
349
+ * **This does not own the whole array.** Two runtime cron surfaces live on
350
+ * `createWorker` rather than in `lunora/crons.ts`: `backupCron` (the nightly
351
+ * NDJSON backup) and `crons` (handlers keyed by expression). Replacing the array
352
+ * wholesale deleted both on the next `lunora deploy` or dev-server schema save,
353
+ * silently — the nightly backup simply stopped. So an entry this reconciler did
354
+ * not generate is the user's, is kept, and is reported in
355
+ * {@link ReconcileResult.preserved}.
356
+ *
357
+ * Codegen now scans the worker entry for both, so the STATIC case arrives in
358
+ * `cronTriggers` like any declared cron — kept because it is generated, and
359
+ * cleared when the entry stops declaring it. What it cannot resolve is the
360
+ * dynamic case, and that case is supported: `.extend((env) => ({ backupCron:
361
+ * env.NIGHTLY_CRON }))` is the documented escape hatch, and its value exists
362
+ * only at runtime. That residue — plus a computed `crons` key, a spread options
363
+ * object, and anything a project put in `triggers.crons` for its own reasons —
364
+ * is what the preserve rule below is for. It is not redundant with discovery; it
365
+ * is the half discovery provably cannot reach.
366
+ *
367
+ * Ownership is recorded in the project's `package.json` under `lunora.crons`,
368
+ * which is why a REMOVED generated cron still gets cleared (it is in the record
369
+ * and no longer in `cronTriggers`) while a hand-written one never is. Three
370
+ * properties pick that location:
371
+ *
372
+ * COMMITTED, so it survives a fresh clone. Gitignored `.lunora/` state does not:
373
+ * a CI-only deploy finds none, and falling back to add-only there leaves a
374
+ * removed cron firing forever.
375
+ *
376
+ * VALID JSON, so `wrangler.json` — a supported config name — behaves exactly like
377
+ * `wrangler.jsonc`. A `//` marker inside the config does not: wrangler tolerates
378
+ * one in either (both go through its JSONC parser), but the project's own
379
+ * `JSON.parse`, its deploy wrapper and its editor's JSON schema validation all
380
+ * break the moment a `.json` grows a comment. Nor can the record be a plain key
381
+ * in the wrangler config — wrangler reports unknown fields ("Unexpected fields
382
+ * found in top-level field") on every command.
383
+ *
384
+ * READ AND WRITTEN AT ONE ADDRESS. A comment has to be found textually on the way
385
+ * in and positioned structurally on the way out, and those are not the same
386
+ * place: a stale duplicate higher in the file was read as the record and deleted
387
+ * the user's backup trigger.
388
+ *
389
+ * The wrangler config is rewritten only when an entry actually moves, so
390
+ * {@link ReconcileResult.changed} means what a `synced N cron trigger(s)` log
391
+ * claims it means.
392
+ *
393
+ * A project with nothing recorded yet — one predating this, or one with no
394
+ * manifest — treats every entry as the user's, which is the safe direction: a
395
+ * wrongly-kept trigger costs one no-op invocation, a wrongly-deleted one
396
+ * silently ends the backups. It becomes precise after a codegen run that still
397
+ * declares the cron; upgrading and deleting a cron in the same change records an
398
+ * empty set, and that orphan is then kept for good.
399
+ *
400
+ * This intentionally writes the SAME `triggers.crons` shape the
401
+ * `@lunora/config` validator accepts, so the wrangler validator never fights the
402
+ * generated value.
403
+ */
404
+ declare const reconcileWranglerCrons: (projectRoot: string, cronTriggers: ReadonlyArray<string>) => ReconcileResult;
405
+ /**
406
+ * The one log line worth printing for a reconcile that kept entries it does not
407
+ * own, or `undefined` when it owned the whole array. Lives here rather than in
408
+ * each caller because `lunora deploy` and the Vite plugin print it verbatim and
409
+ * had already drifted into two copies of the same sentence.
410
+ */
411
+ declare const describePreservedCrons: (preserved: ReadonlyArray<string>) => string | undefined;
412
+ /**
413
+ * The wrangler config sections Lunora can safely flip to remote mode in dev,
414
+ * each with the human label used in logs and the structural `shape` the entry
415
+ * lives in.
416
+ *
417
+ * `"array"` is a top-level array of binding objects (`d1_databases`,
418
+ * `kv_namespaces`, `r2_buckets`, `vectorize`, `services`). `"producers"` is
419
+ * `queues.producers[]` — consumers are NOT remoted (their schema has no `remote`
420
+ * field) and the edit path is two levels deep. `"object"` is a single binding
421
+ * object, not an array (`ai`), whose edit path targets the section key directly.
422
+ *
423
+ * Every kind here was confirmed against `wrangler/config-schema.json`: the
424
+ * entry's schema declares a `remote` property. Deliberately omits
425
+ * `durable_objects` (no CF remote-DO mode; shards stay local) and sections whose
426
+ * schema has no `remote` field (`hyperdrive`, `analytics_engine_datasets`,
427
+ * `secrets_store_secrets`, queue consumers, …). Widening further is a one-line
428
+ * table edit.
429
+ */
430
+ declare const REMOTE_ELIGIBLE_KEYS: {
431
+ readonly ai: {
432
+ readonly label: "AI";
433
+ readonly shape: "object";
434
+ };
435
+ readonly d1_databases: {
436
+ readonly label: "D1";
437
+ readonly shape: "array";
438
+ };
439
+ readonly kv_namespaces: {
440
+ readonly label: "KV";
441
+ readonly shape: "array";
442
+ };
443
+ readonly queues: {
444
+ readonly label: "Queue";
445
+ readonly shape: "producers";
446
+ };
447
+ readonly r2_buckets: {
448
+ readonly label: "R2";
449
+ readonly shape: "array";
450
+ };
451
+ readonly services: {
452
+ readonly label: "Service";
453
+ readonly shape: "array";
454
+ };
455
+ readonly vectorize: {
456
+ readonly label: "Vectorize";
457
+ readonly shape: "array";
458
+ };
459
+ };
460
+ type RemoteEligibleKey = keyof typeof REMOTE_ELIGIBLE_KEYS;
461
+ /** One binding object as it appears in any eligible section. */
462
+ interface BindingEntry {
463
+ binding?: string;
464
+ remote?: boolean;
465
+ }
466
+ /** One binding entry we mark remote, with enough provenance to log + edit it. */
467
+ interface RemoteBindingPlan {
468
+ /** The binding name as declared in the config (e.g. `"DB"`, `"FILES"`). */
469
+ binding: string;
470
+ /** Short kind label for logging (`"D1"`, `"KV"`, `"R2"`, `"Vectorize"`, …). */
471
+ kind: string;
472
+ /**
473
+ * The jsonc edit path within {@link RemoteBindingPlan.section}, relative to
474
+ * the section key: `[index]` for an `"array"` section, `["producers", index]`
475
+ * for a queue producer, or `[]` for the single-object `ai` section. The
476
+ * materializer prepends the section key and appends `"remote"`.
477
+ */
478
+ path: ReadonlyArray<number | string>;
479
+ /** The wrangler config key the entry lives under. */
480
+ section: RemoteEligibleKey;
481
+ }
482
+ /** The structural slice of a wrangler config the remote planner reads. */
483
+ interface RemoteWranglerShape {
484
+ ai?: BindingEntry | null;
485
+ d1_databases?: ReadonlyArray<BindingEntry | null | undefined>;
486
+ kv_namespaces?: ReadonlyArray<BindingEntry | null | undefined>;
487
+ queues?: {
488
+ producers?: ReadonlyArray<BindingEntry | null | undefined>;
489
+ } | null;
490
+ r2_buckets?: ReadonlyArray<BindingEntry | null | undefined>;
491
+ services?: ReadonlyArray<BindingEntry | null | undefined>;
492
+ vectorize?: ReadonlyArray<BindingEntry | null | undefined>;
493
+ }
494
+ /**
495
+ * Inspect a parsed wrangler config and list every eligible binding that should
496
+ * be flipped to remote mode. Pure — no file-system access, no mutation. An
497
+ * entry already carrying `"remote": true` is still reported (so logging is
498
+ * complete) but the materializer's edit is a harmless no-op for it.
499
+ */
500
+ declare const planRemoteBindings: (parsed: RemoteWranglerShape) => RemoteBindingPlan[];
501
+ /**
502
+ * Inject `"remote": true` onto each planned binding in the config `text`,
503
+ * comment-preservingly via jsonc edits. Pure string→string; the edits target
504
+ * disjoint entries so applying them sequentially is safe. The edit path is
505
+ * `[section, ...plan.path, "remote"]`, which resolves to the array element, the
506
+ * `queues.producers[i]` entry, or the single `ai` object as the plan demands.
507
+ */
508
+ declare const injectRemoteFlags: (text: string, plans: ReadonlyArray<RemoteBindingPlan>) => string;
509
+ interface MaterializeOptions {
510
+ /** When `false`, the call is a no-op (returns `enabled: false`). */
511
+ enabled: boolean;
512
+ projectRoot: string;
513
+ }
514
+ interface MaterializeResult {
515
+ /**
516
+ * Removes the generated temp config file. Always present and always safe to
517
+ * call: it is idempotent, a no-op when nothing was written (disabled /
518
+ * fall-through cases), and never throws if the path is already gone. The dev
519
+ * command calls this on every exit path (normal, signal, error).
520
+ */
521
+ cleanup: () => void;
522
+ /**
523
+ * Absolute path to the generated temp config to pass to
524
+ * `wrangler dev --config`. `undefined` when remote mode is disabled, no
525
+ * wrangler config was found, it failed to parse, or it declared no eligible
526
+ * binding (nothing to remote — run plain local dev).
527
+ */
528
+ configPath?: string;
529
+ /** Whether remote mode was requested at all. */
530
+ enabled: boolean;
531
+ /** Why no temp config was produced, for logging (only set when none was). */
532
+ reason?: string;
533
+ /** The bindings flipped to remote, for the dev banner. */
534
+ remoteBindings: RemoteBindingPlan[];
535
+ }
536
+ /**
537
+ * Produce a temporary wrangler config with `"remote": true` on every eligible
538
+ * binding, so `lunora dev` can run `wrangler dev --config <temp>` against the
539
+ * deployed D1/KV/R2 without touching the user's file.
540
+ *
541
+ * The temp file is written as a sibling of the source `wrangler.jsonc` (in the
542
+ * project root), NOT an OS temp dir: wrangler resolves a config's relative paths
543
+ * (`main`, `assets`, `migrations_dir`, …) against the **config file's own
544
+ * directory**, so a temp config in `/tmp` would make wrangler look for
545
+ * `/tmp/src/server.ts` and fail to start the worker. Keeping it beside the real
546
+ * config preserves those relative paths. Returns `configPath: undefined` (with a
547
+ * `reason`) for every fall-through case so the caller degrades to plain local
548
+ * dev instead of failing.
549
+ */
550
+ declare const materializeRemoteWranglerConfig: (options: MaterializeOptions) => MaterializeResult;
551
+ /**
552
+ * Parse a `LUNORA_REMOTE` env value into the on/off decision. Truthy when set to
553
+ * `"1"` or `"true"` (case-insensitive); anything else — unset, `"0"`, `"false"`,
554
+ * empty — is off. Mirrors the `"1" | "true"` convention used across the runtime.
555
+ */
556
+ declare const isRemoteEnvEnabled: (value: string | undefined) => boolean;
557
+ /** The three inputs that can switch remote-binding dev on, in precedence order. */
558
+ interface RemoteEnableInputs {
559
+ /**
560
+ * The `remote` preference from `lunora.config.*` (the lowest-priority signal).
561
+ * `undefined` means "no project preference"; an explicit `false` here loses
562
+ * to neither the flag nor the env when those are absent — it just stays off.
563
+ */
564
+ configPreference?: boolean;
565
+ /** The raw `LUNORA_REMOTE` env value (parsed with {@link isRemoteEnvEnabled}). */
566
+ envValue?: string;
567
+ /** The explicit `--remote` CLI flag — `true` when passed, `undefined`/`false` otherwise. */
568
+ flag?: boolean;
569
+ }
570
+ /**
571
+ * Resolve whether remote-binding dev is on, with a clear precedence:
572
+ *
573
+ * 1. an explicit `--remote` flag (highest — a deliberate per-invocation choice),
574
+ * 2. then `LUNORA_REMOTE` in the environment,
575
+ * 3. then the `remote` key in `lunora.config.*` (lowest — a project default).
576
+ *
577
+ * The flag and env are one-directional (they can only turn remote *on*); only
578
+ * the config preference carries a meaningful `false`, and it applies solely when
579
+ * neither stronger signal is present. So a project that sets `"remote": false`
580
+ * is still overridable per-run by `--remote` or `LUNORA_REMOTE=1`.
581
+ */
582
+ declare const resolveRemoteEnabled: (inputs: RemoteEnableInputs) => boolean;
583
+ /**
584
+ * Single source of truth for "does this wrangler config enable Workers
585
+ * Cache?" and "what compatibility_date does that require?".
586
+ *
587
+ * Before this module, `reconcile-compatibility-date.ts` (auto-bump) and
588
+ * `wrangler-validator.ts` (validation) each carried their own
589
+ * `WORKERS_CACHE_MIN_DATE` literal and their own top-level/`exports[]`
590
+ * cache-enabled walk — two copies of the same fact that could silently drift
591
+ * apart (a date bumped in one file without the other would either validate a
592
+ * config the reconciler wouldn't produce, or vice versa).
593
+ */
594
+ /** The `compatibility_date` Workers Cache (`cache.enabled: true`) requires. */
595
+ declare const WORKERS_CACHE_MIN_DATE = "2026-05-01";
596
+ /** The subset of a parsed `wrangler.jsonc` the cache-enabled check reads. */
597
+ interface WranglerCacheShape {
598
+ cache?: {
599
+ enabled?: boolean;
600
+ } | null;
601
+ exports?: Record<string, {
602
+ cache?: {
603
+ enabled?: boolean;
604
+ } | null;
605
+ } | null> | null;
606
+ }
607
+ /**
608
+ * Whether Workers Cache is enabled anywhere in a parsed wrangler config — the
609
+ * top-level `cache.enabled` toggle, or a per-export override in
610
+ * `exports[name].cache.enabled` (Workers can scope cache per named export).
611
+ * `undefined`/`null` (an unparsed or absent config) is treated as disabled.
612
+ */
613
+ declare const isCacheEnabled: (parsed: WranglerCacheShape | null | undefined) => boolean;
614
+ /** Candidate wrangler config filenames, in the order every consumer probes them. */
615
+ declare const WRANGLER_FILES: readonly ["wrangler.jsonc", "wrangler.json"];
616
+ /** Locate the project's wrangler config, or `undefined` when none exists. */
617
+ declare const findWranglerFile: (projectRoot: string) => string | undefined;
618
+ interface ReadWranglerResult<T> {
619
+ /** Parsed config, or `undefined` when the file was not valid JSONC. */
620
+ parsed: T | undefined;
621
+ /** Raw file text — needed for comment-preserving `modify`/`applyEdits`. */
622
+ text: string;
623
+ }
624
+ /**
625
+ * Read and JSONC-parse a wrangler config file. Returns the raw `text` (for
626
+ * structural edits) alongside `parsed`, which is `undefined` when the file is
627
+ * not valid JSONC or does not parse to an object. Allows trailing commas, as
628
+ * wrangler does.
629
+ */
630
+ declare const readWranglerJsonc: <T = unknown>(wranglerPath: string) => ReadWranglerResult<T>;
631
+ /**
632
+ * Pure scan of a `vars` map for plaintext secrets — the FS-free core, exported for
633
+ * unit tests. A variable is flagged when, for a **string** value that is neither a
634
+ * placeholder nor a public/publishable key, EITHER the value matches a known
635
+ * secret shape (`secretKindOf`) OR the key name strongly implies a secret and the
636
+ * value is long enough to plausibly be one. `kind` is the matched shape, or
637
+ * `secret_named_var` for the key-name path.
638
+ */
639
+ declare const scanWranglerVariablesForSecrets: (variables: Record<string, unknown> | undefined, file: string) => WranglerVariableIR[];
640
+ /**
641
+ * Read the project's `wrangler.jsonc` and return its plaintext-secret `vars` as IR
642
+ * for the `plaintext_secret_in_wrangler_vars` lint. Returns `[]` when there is no
643
+ * wrangler config, it doesn't parse, or nothing looks like a secret. Scans the
644
+ * top-level `vars` block (mirroring the existing `validateCorsVariables` scope);
645
+ * per-environment `env.<name>.vars` overrides are out of scope for now.
646
+ */
647
+ declare const collectWranglerSecretVariables: (projectRoot: string) => WranglerVariableIR[];
648
+ declare const REQUIRED_COMPATIBILITY_DATE: string;
649
+ declare const REQUIRED_FLAG: string;
650
+ interface WranglerDurableObjectBinding {
651
+ class_name?: string;
652
+ name?: string;
653
+ /** Present when the class lives in ANOTHER Worker — then it is that script's to export, not this entry's. */
654
+ script_name?: string;
655
+ }
656
+ /**
657
+ * A `tail_consumers` entry: a Worker that receives this Worker's tail events
658
+ * (logs, exceptions, fetch metadata) for forwarding to an external sink. See
659
+ * `withTailConsumer` for the wiring helper.
660
+ */
661
+ interface TailConsumer {
662
+ /** Optional Cloudflare environment of the consumer Worker. */
663
+ environment?: string;
664
+ /** Name of the Worker that consumes tail events. */
665
+ service?: string;
666
+ }
667
+ /** A wrangler `containers[]` entry (parsed from untrusted JSONC). */
668
+ interface WranglerContainerEntry {
669
+ class_name?: string;
670
+ image?: string;
671
+ instance_type?: string | {
672
+ disk_mb?: number;
673
+ memory_mib?: number;
674
+ vcpu?: number;
675
+ };
676
+ max_instances?: number;
677
+ }
678
+ /**
679
+ * A wrangler `workflows[]` entry (parsed from untrusted JSONC). Unlike
680
+ * containers, workflows are NOT Durable Objects — the entry stands alone (no
681
+ * `durable_objects` binding, no migration class).
682
+ */
683
+ interface WranglerWorkflowEntry {
684
+ binding?: string;
685
+ class_name?: string;
686
+ name?: string;
687
+ /** Present when the class lives in ANOTHER Worker — then it is that script's to export. */
688
+ script_name?: string;
689
+ }
690
+ /** A wrangler `queues.producers[]` entry — a `Queue` binding sending to `queue`. */
691
+ interface WranglerQueueProducer {
692
+ binding?: string;
693
+ delivery_delay?: number;
694
+ queue?: string;
695
+ }
696
+ /** A wrangler `queues.consumers[]` entry — push (worker) or `type: "http_pull"`. */
697
+ interface WranglerQueueConsumer {
698
+ dead_letter_queue?: string;
699
+ max_batch_size?: number;
700
+ max_batch_timeout?: number;
701
+ max_retries?: number;
702
+ queue?: string;
703
+ retry_delay?: number;
704
+ type?: string;
705
+ }
706
+ interface WranglerConfig {
707
+ analytics_engine_datasets?: ReadonlyArray<{
708
+ binding?: string;
709
+ dataset?: string;
710
+ } | null | undefined>;
711
+ assets?: {
712
+ binding?: string;
713
+ directory?: string;
714
+ html_handling?: string;
715
+ not_found_handling?: string;
716
+ };
717
+ browser?: {
718
+ binding?: string;
719
+ };
720
+ cache?: {
721
+ enabled?: boolean;
722
+ } | null;
723
+ compatibility_date?: string;
724
+ compatibility_flags?: ReadonlyArray<string>;
725
+ containers?: ReadonlyArray<WranglerContainerEntry | null | undefined>;
726
+ d1_databases?: ReadonlyArray<{
727
+ binding?: string;
728
+ database_id?: string;
729
+ database_name?: string;
730
+ } | null | undefined>;
731
+ dispatch_namespaces?: ReadonlyArray<{
732
+ binding?: string;
733
+ namespace?: string;
734
+ outbound?: unknown;
735
+ } | null | undefined>;
736
+ durable_objects?: {
737
+ bindings?: ReadonlyArray<WranglerDurableObjectBinding>;
738
+ };
739
+ env?: Record<string, WranglerConfig>;
740
+ exports?: Record<string, {
741
+ cache?: {
742
+ enabled?: boolean;
743
+ } | null;
744
+ type?: string;
745
+ } | null> | null;
746
+ flagship?: ReadonlyArray<{
747
+ app_id?: string;
748
+ binding?: string;
749
+ } | null | undefined>;
750
+ hyperdrive?: ReadonlyArray<{
751
+ binding?: string;
752
+ id?: string;
753
+ localConnectionString?: string;
754
+ } | null | undefined>;
755
+ images?: {
756
+ binding?: string;
757
+ };
758
+ kv_namespaces?: ReadonlyArray<{
759
+ binding?: string;
760
+ id?: string;
761
+ } | null | undefined>;
762
+ limits?: {
763
+ cpu_ms?: number;
764
+ };
765
+ logpush?: boolean;
766
+ main?: string;
767
+ migrations?: ReadonlyArray<{
768
+ deleted_classes?: ReadonlyArray<string>;
769
+ new_classes?: ReadonlyArray<string>;
770
+ new_sqlite_classes?: ReadonlyArray<string>;
771
+ renamed_classes?: ReadonlyArray<{
772
+ from?: string;
773
+ to?: string;
774
+ } | null | undefined>;
775
+ } | null | undefined>;
776
+ mtls_certificates?: ReadonlyArray<{
777
+ binding?: string;
778
+ certificate_id?: string;
779
+ } | null | undefined>;
780
+ observability?: {
781
+ enabled?: boolean;
782
+ head_sampling_rate?: number;
783
+ logs?: {
784
+ enabled?: boolean;
785
+ head_sampling_rate?: number;
786
+ invocation_logs?: boolean;
787
+ };
788
+ };
789
+ pipelines?: ReadonlyArray<{
790
+ binding?: string;
791
+ pipeline?: string;
792
+ stream?: string;
793
+ } | null | undefined>;
794
+ placement?: {
795
+ host?: string;
796
+ hostname?: string;
797
+ mode?: string;
798
+ region?: string;
799
+ };
800
+ queues?: {
801
+ consumers?: ReadonlyArray<WranglerQueueConsumer | null | undefined>;
802
+ producers?: ReadonlyArray<WranglerQueueProducer | null | undefined>;
803
+ };
804
+ r2_buckets?: ReadonlyArray<{
805
+ binding?: string;
806
+ bucket_name?: string;
807
+ } | null | undefined>;
808
+ secrets_store_secrets?: ReadonlyArray<{
809
+ binding?: string;
810
+ secret_name?: string;
811
+ store_id?: string;
812
+ } | null | undefined>;
813
+ send_email?: ReadonlyArray<{
814
+ allowed_destination_addresses?: ReadonlyArray<string>;
815
+ destination_address?: string;
816
+ name?: string;
817
+ } | null | undefined>;
818
+ services?: ReadonlyArray<{
819
+ binding?: string;
820
+ entrypoint?: string;
821
+ environment?: string;
822
+ service?: string;
823
+ } | null | undefined>;
824
+ tail_consumers?: ReadonlyArray<TailConsumer | null | undefined>;
825
+ vars?: Record<string, unknown>;
826
+ vectorize?: ReadonlyArray<{
827
+ binding?: string;
828
+ index_name?: string;
829
+ } | null | undefined>;
830
+ workflows?: ReadonlyArray<WranglerWorkflowEntry | null | undefined>;
831
+ }
832
+ interface WranglerValidationReport {
833
+ errors: string[];
834
+ valid: boolean;
835
+ warnings: string[];
836
+ }
837
+ interface WranglerEnvironmentMerge {
838
+ /** Set when `environment` names no `env.<name>` block declared in the config — the caller should treat this as a hard validation failure. */
839
+ error?: string;
840
+ /** The env-scoped view: `wrangler` unchanged when `environment` is `undefined`, otherwise merged per {@link NON_INHERITABLE_KEYS} / {@link INHERITABLE_KEYS}. */
841
+ merged: WranglerConfig;
842
+ /**
843
+ * Keys the env block overrides whose inheritance status this validator
844
+ * cannot verify (not in either table above) — validated against the
845
+ * TOP-LEVEL value only, per the "do not guess" rule; the override is
846
+ * silently ignored for validation purposes. The caller logs this ONCE
847
+ * (not per key) so an unusual `env.<name>` block doesn't spam warnings.
848
+ */
849
+ unverifiedKeys: string[];
850
+ }
851
+ /**
852
+ * Resolve the config view `wrangler deploy --env <environment>` will actually
853
+ * use. Undefined `environment` returns `wrangler` unchanged — the top-level
854
+ * config is what a plain `wrangler deploy` reads, same as today.
855
+ *
856
+ * Deliberately independent of any other merge in this module: called fresh
857
+ * from the ORIGINAL `wrangler` each time (see both call sites), so there is
858
+ * no risk of merging an already-merged config and silently losing the
859
+ * top-level fallback a second merge pass would no longer have access to.
860
+ */
861
+ declare const mergeWranglerEnvironment: (wrangler: WranglerConfig, environment: string | undefined) => WranglerEnvironmentMerge;
862
+ /**
863
+ * Return a new `WranglerConfig` with `consumer` present in `tail_consumers`,
864
+ * wiring this Worker to forward its tail events (logs/exceptions) to another
865
+ * Worker that fans them out to an external sink. Pure and idempotent: an
866
+ * existing entry with the same `service` + `environment` is left untouched
867
+ * rather than duplicated, so it is safe to call on every codegen/deploy.
868
+ */
869
+ declare const withTailConsumer: (wrangler: WranglerConfig, consumer: TailConsumer) => WranglerConfig;
870
+ /**
871
+ * Pure validator: given a parsed `WranglerConfig` object and an optional
872
+ * `SchemaInfo`, produce a structured report. Performs no I/O.
873
+ *
874
+ * `environment`, when set, validates the `env.<environment>` view
875
+ * ({@link mergeWranglerEnvironment}) instead of the top-level config — e.g. a
876
+ * `durable_objects` binding present only at the top level is a validation
877
+ * FAILURE for `--env production` if `env.production` doesn't repeat it,
878
+ * because `durable_objects` is non-inheritable and wrangler will not carry it
879
+ * over. Omit `environment` to validate the top level only (unchanged default).
880
+ */
881
+ declare const validateWranglerConfig: (wranglerInput: WranglerConfig | undefined, schema?: SchemaInfo, environment?: string) => WranglerValidationReport;
882
+ /**
883
+ * Convenience alias matching the original task-spec signature
884
+ * `validateWrangler(wranglerJson, schema)` returning
885
+ * `{ valid, errors, warnings }`.
886
+ */
887
+ declare const validateWrangler: typeof validateWranglerConfig;
888
+ interface WranglerProjectValidationOptions {
889
+ /**
890
+ * Cloudflare environment to validate against `env.<name>` in
891
+ * wrangler.jsonc. See {@link mergeWranglerEnvironment} for which keys
892
+ * inherit the top-level value vs must be redeclared per environment.
893
+ * Omit to validate the top-level config only (unchanged default).
894
+ */
895
+ environment?: string;
896
+ projectRoot: string;
897
+ schemaDir?: string;
898
+ }
899
+ interface WranglerProjectValidationResult {
900
+ problems: ReadonlyArray<string>;
901
+ report: WranglerValidationReport;
902
+ wranglerPath: string | undefined;
903
+ }
904
+ /**
905
+ * The phrase every unexported-class error carries.
906
+ *
907
+ * Exported because it is a CONTRACT, not prose: `lunora doctor` picks these
908
+ * errors out of the report by substring to raise its own finding, and the
909
+ * validator's report is a flat `string[]`. Until the report entries carry a
910
+ * code, a copy-edit here would silently disable that check — so the copy-edit
911
+ * has to go through this constant.
912
+ */
913
+ declare const UNEXPORTED_CLASS_MARKER = "does not export it";
914
+ /**
915
+ * File-system aware variant: reads `wrangler.jsonc`/`wrangler.json` from
916
+ * the given project root, discovers the schema (if any), and delegates to
917
+ * `validateWranglerConfig`. Returns the legacy
918
+ * `{ problems, wranglerPath }` shape plus the structured `report`.
919
+ */
920
+ declare const validateWranglerProject: (options: WranglerProjectValidationOptions) => WranglerProjectValidationResult;
921
+ export { type AlchemyTranslation, BINDING_MANIFEST_VERSION,
922
+ /**
923
+ * `@lunora/config/cloudflare` — the wrangler layer.
924
+ *
925
+ * Everything here speaks `wrangler.jsonc`: validating it, reconciling inferred
926
+ * bindings/crons/compatibility-date into it, resolving remote bindings, and the
927
+ * `DeployDriver` that ties them together for `--target cloudflare`.
928
+ *
929
+ * Split out from the package root so `@lunora/config` itself stays
930
+ * provider-neutral. The root keeps what any target needs — the `DeployDriver`
931
+ * contract, the driver registry, project config and target resolution,
932
+ * `.dev.vars` grammar, binding *inference* — while emission and validation,
933
+ * which are wrangler-shaped by definition, live behind this subpath.
934
+ * Plan 114 §5.3 (D6): a package carrying real provider code isolates it behind a
935
+ * subpath rather than relocating wholesale.
936
+ */
937
+ type BindingManifest,
938
+ /**
939
+ * `@lunora/config/cloudflare` — the wrangler layer.
940
+ *
941
+ * Everything here speaks `wrangler.jsonc`: validating it, reconciling inferred
942
+ * bindings/crons/compatibility-date into it, resolving remote bindings, and the
943
+ * `DeployDriver` that ties them together for `--target cloudflare`.
944
+ *
945
+ * Split out from the package root so `@lunora/config` itself stays
946
+ * provider-neutral. The root keeps what any target needs — the `DeployDriver`
947
+ * contract, the driver registry, project config and target resolution,
948
+ * `.dev.vars` grammar, binding *inference* — while emission and validation,
949
+ * which are wrangler-shaped by definition, live behind this subpath.
950
+ * Plan 114 §5.3 (D6): a package carrying real provider code isolates it behind a
951
+ * subpath rather than relocating wholesale.
952
+ */
953
+ type BindingRequirement, CLOUDFLARE_DRIVER, type ExportGap,
954
+ /**
955
+ * `@lunora/config/cloudflare` — the wrangler layer.
956
+ *
957
+ * Everything here speaks `wrangler.jsonc`: validating it, reconciling inferred
958
+ * bindings/crons/compatibility-date into it, resolving remote bindings, and the
959
+ * `DeployDriver` that ties them together for `--target cloudflare`.
960
+ *
961
+ * Split out from the package root so `@lunora/config` itself stays
962
+ * provider-neutral. The root keeps what any target needs — the `DeployDriver`
963
+ * contract, the driver registry, project config and target resolution,
964
+ * `.dev.vars` grammar, binding *inference* — while emission and validation,
965
+ * which are wrangler-shaped by definition, live behind this subpath.
966
+ * Plan 114 §5.3 (D6): a package carrying real provider code isolates it behind a
967
+ * subpath rather than relocating wholesale.
968
+ */
969
+ type ManifestConfigShape, type MaterializeOptions, type MaterializeResult, REMOTE_ELIGIBLE_KEYS, REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, type ReadWranglerResult, type ReconcileBindingsResult, type ReconcileCompatibilityDateResult, type ReconcileResult as ReconcileCronsResult, type RemoteBindingPlan, type RemoteEnableInputs, type RemoteWranglerShape, type TailConsumer, UNEXPORTED_CLASS_MARKER, WORKERS_CACHE_MIN_DATE, WRANGLER_FILES, type WranglerCacheShape, type WranglerConfig, type WranglerConfigShape, type WranglerContainerEntry, type WranglerEnvironmentMerge, type WranglerProjectValidationOptions, type WranglerProjectValidationResult, type WranglerValidationReport, type WranglerWorkflowEntry, buildBindingManifest, collectExportGaps, collectWranglerSecretVariables, describePreservedCrons, findWranglerFile, injectRemoteFlags, isCacheEnabled, isRemoteEnvEnabled, materializeRemoteWranglerConfig, mergeWranglerEnvironment, planRemoteBindings, readWranglerJsonc, reconcileWranglerBindings, reconcileWranglerCompatibilityDate, reconcileWranglerCrons, resolveRemoteEnabled, scanWranglerVariablesForSecrets, validateWrangler, validateWranglerConfig, validateWranglerProject, withTailConsumer, wranglerToAlchemy };