@lunora/codegen 1.0.0-alpha.45 → 1.0.0-alpha.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -7,10 +7,10 @@ import { StudioFeaturesResult } from '@lunora/do';
7
7
  import { Schema } from '@lunora/server';
8
8
  import { JsonSchema } from '@lunora/values';
9
9
  /**
10
- * AST-observable subset of a column's modifier chain (`.unique()`, `.default()`,
11
- * …). Function-valued modifiers (`.$defaultFn`/`.$onUpdateFn`) can't be
12
- * serialized, so only their *presence* is recorded.
13
- */
10
+ * AST-observable subset of a column's modifier chain (`.unique()`, `.default()`,
11
+ * …). Function-valued modifiers (`.$defaultFn`/`.$onUpdateFn`) can't be
12
+ * serialized, so only their *presence* is recorded.
13
+ */
14
14
  interface ColumnMetaIR {
15
15
  /** `.default(...)` or `.$defaultFn(...)` present — field is optional on insert. */
16
16
  hasDefault?: boolean;
@@ -28,13 +28,13 @@ interface ValidatorIR {
28
28
  /** Column modifiers (`.unique()`, `.default()`, `.nullable()`, …) when present. */
29
29
  column?: ColumnMetaIR;
30
30
  /**
31
- * `true` when this validator carries a `.check(...)` refinement. The predicate
32
- * is a runtime closure the AST→IR step can't represent, so the node keeps its
33
- * base `kind` but records the refinement's presence here. The AOT args-validator
34
- * compiler declines any node with this flag (compiling it would silently skip
35
- * the predicate). `.meta(...)` is pure metadata with no parse effect and does
36
- * NOT set this.
37
- */
31
+ * `true` when this validator carries a `.check(...)` refinement. The predicate
32
+ * is a runtime closure the AST→IR step can't represent, so the node keeps its
33
+ * base `kind` but records the refinement's presence here. The AOT args-validator
34
+ * compiler declines any node with this flag (compiling it would silently skip
35
+ * the predicate). `.meta(...)` is pure metadata with no parse effect and does
36
+ * NOT set this.
37
+ */
38
38
  hasRefinement?: boolean;
39
39
  /** For `v.optional(inner)` / `v.array(inner)`. */
40
40
  inner?: ValidatorIR;
@@ -78,19 +78,19 @@ interface VectorIndexIR {
78
78
  table: string;
79
79
  }
80
80
  /**
81
- * One ordering key on a rank index's `sortBy`: the column and direction.
82
- * Mirrors the runtime `RankSortKey` (defaults `direction` to `"asc"`).
83
- */
81
+ * One ordering key on a rank index's `sortBy`: the column and direction.
82
+ * Mirrors the runtime `RankSortKey` (defaults `direction` to `"asc"`).
83
+ */
84
84
  interface RankSortKeyIR {
85
85
  direction: "asc" | "desc";
86
86
  field: string;
87
87
  }
88
88
  /**
89
- * A `.rankIndex(name, { sortBy, partitionBy?, where? })` declaration. The owning
90
- * table is always the table the index is declared on, so — unlike a vector index
91
- * — there is no separate `on`/`table` reference to carry: it rides along on its
92
- * {@link TableIR}. Only the fields needed for type emission are captured.
93
- */
89
+ * A `.rankIndex(name, { sortBy, partitionBy?, where? })` declaration. The owning
90
+ * table is always the table the index is declared on, so — unlike a vector index
91
+ * — there is no separate `on`/`table` reference to carry: it rides along on its
92
+ * {@link TableIR}. Only the fields needed for type emission are captured.
93
+ */
94
94
  interface RankIndexIR {
95
95
  name: string;
96
96
  /** Columns scoping each ranking; omitted ⇒ one global rank over the table. */
@@ -112,11 +112,11 @@ interface RelationIR {
112
112
  table: string;
113
113
  }
114
114
  /**
115
- * Statically-discovered `.source(...)` config (plan 077). Only the bits the
116
- * advisor lints + DO wiring need are captured; `map`/`tenantBy` are functions and
117
- * cannot be serialized, so their presence is recorded as `hasTenantBy` rather than
118
- * the function itself.
119
- */
115
+ * Statically-discovered `.source(...)` config (plan 077). Only the bits the
116
+ * advisor lints + DO wiring need are captured; `map`/`tenantBy` are functions and
117
+ * cannot be serialized, so their presence is recorded as `hasTenantBy` rather than
118
+ * the function itself.
119
+ */
120
120
  interface ExternalSourceIR {
121
121
  /** The wrangler Hyperdrive binding name. */
122
122
  binding: string;
@@ -135,42 +135,42 @@ interface ExternalSourceIR {
135
135
  /** The membership query literal, when statically knowable. */
136
136
  query?: string;
137
137
  /**
138
- * `true` when `.source(...)` was present but its argument was **not** a static
139
- * object literal (e.g. `.source(buildConfig())`), so none of the fields above
140
- * could be read. The source still exists — this flag lets `hasSourcedTables`
141
- * (codegen) and the `external_source_*` lints treat it as a source that can't be
142
- * verified, instead of mistaking it for no `.source()` at all.
143
- */
138
+ * `true` when `.source(...)` was present but its argument was **not** a static
139
+ * object literal (e.g. `.source(buildConfig())`), so none of the fields above
140
+ * could be read. The source still exists — this flag lets `hasSourcedTables`
141
+ * (codegen) and the `external_source_*` lints treat it as a source that can't be
142
+ * verified, instead of mistaking it for no `.source()` at all.
143
+ */
144
144
  unanalyzable?: boolean;
145
145
  }
146
146
  interface TableIR {
147
147
  /**
148
- * `true` when the table chain carried `.externallyManaged()` — its rows are
149
- * written outside Lunora's discoverable insert path (adapter/migration/
150
- * middleware), so advisor insert-path lints skip it. Optional: hand-built
151
- * IR and the runtime `fromServerSchema` path default it to `false`.
152
- */
148
+ * `true` when the table chain carried `.externallyManaged()` — its rows are
149
+ * written outside Lunora's discoverable insert path (adapter/migration/
150
+ * middleware), so advisor insert-path lints skip it. Optional: hand-built
151
+ * IR and the runtime `fromServerSchema` path default it to `false`.
152
+ */
153
153
  externallyManaged?: boolean;
154
154
  /**
155
- * Set when the chain carried `.source(...)` — the table is materialized from an
156
- * external Hyperdrive-backed database by a system poll loop (plan 077). Carries
157
- * the statically-knowable bits the advisor lints read; the functions (`map`,
158
- * `tenantBy`) are not serialized, only their presence (`hasTenantBy`).
159
- */
155
+ * Set when the chain carried `.source(...)` — the table is materialized from an
156
+ * external Hyperdrive-backed database by a system poll loop (plan 077). Carries
157
+ * the statically-knowable bits the advisor lints read; the functions (`map`,
158
+ * `tenantBy`) are not serialized, only their presence (`hasTenantBy`).
159
+ */
160
160
  externalSource?: ExternalSourceIR;
161
161
  /**
162
- * Storage backend for a `.global()` table: `"d1"` (default) or
163
- * `"hyperdrive"` (a Postgres/MySQL database via Cloudflare Hyperdrive). Only
164
- * meaningful when `shardMode === "global"`; absent for sharded/root tables.
165
- */
162
+ * Storage backend for a `.global()` table: `"d1"` (default) or
163
+ * `"hyperdrive"` (a Postgres/MySQL database via Cloudflare Hyperdrive). Only
164
+ * meaningful when `shardMode === "global"`; absent for sharded/root tables.
165
+ */
166
166
  globalBackend?: "d1" | "hyperdrive";
167
167
  indexes: ReadonlyArray<IndexIR>;
168
168
  /**
169
- * `true` when the table chain carried `.public()` — an explicit opt-OUT of
170
- * the schema's `.rls("required")` enforcement for this one table. Optional:
171
- * hand-built IR and tables that never called `.public()` default it to
172
- * `false`.
173
- */
169
+ * `true` when the table chain carried `.public()` — an explicit opt-OUT of
170
+ * the schema's `.rls("required")` enforcement for this one table. Optional:
171
+ * hand-built IR and tables that never called `.public()` default it to
172
+ * `false`.
173
+ */
174
174
  isPublic?: boolean;
175
175
  name: string;
176
176
  /** Rank indexes declared inline via `.rankIndex(name, …)`. */
@@ -194,18 +194,18 @@ interface TableIR {
194
194
  type JurisdictionIR = "eu" | "fedramp" | "us";
195
195
  interface SchemaIR {
196
196
  /**
197
- * Cloudflare data-residency jurisdiction declared via
198
- * `defineSchema(...).jurisdiction("…")`. Emitted into the generated worker's
199
- * `createWorker({ jurisdiction })` (and `ctx.scheduler` / `ctx.containers`).
200
- * Absent ⇒ un-pinned.
201
- */
197
+ * Cloudflare data-residency jurisdiction declared via
198
+ * `defineSchema(...).jurisdiction("…")`. Emitted into the generated worker's
199
+ * `createWorker({ jurisdiction })` (and `ctx.scheduler` / `ctx.containers`).
200
+ * Absent ⇒ un-pinned.
201
+ */
202
202
  jurisdiction?: JurisdictionIR;
203
203
  /**
204
- * Set when `defineSchema(...).rls("required")` was chained onto the schema —
205
- * every table's `ctx.db` write path is denied without an RLS-covering
206
- * procedure unless the table itself is `.public()` (see {@link TableIR.isPublic}).
207
- * Absent when the schema never called `.rls("required")`.
208
- */
204
+ * Set when `defineSchema(...).rls("required")` was chained onto the schema —
205
+ * every table's `ctx.db` write path is denied without an RLS-covering
206
+ * procedure unless the table itself is `.public()` (see {@link TableIR.isPublic}).
207
+ * Absent when the schema never called `.rls("required")`.
208
+ */
209
209
  rlsMode?: "required";
210
210
  tables: ReadonlyArray<TableIR>;
211
211
  /** All vector indexes (inline Shape A hoisted + standalone Shape B), flattened. */
@@ -218,33 +218,33 @@ interface FunctionIR {
218
218
  filePath: string;
219
219
  kind: "action" | "mutation" | "query" | "stream";
220
220
  /**
221
- * Set on connection-lifecycle hooks (`onConnect`/`onDisconnect`): the socket
222
- * side the hook fires on. Such a function is also an internal mutation (so it
223
- * lands in `LUNORA_FUNCTIONS` for path dispatch); emit additionally collects
224
- * it into the `LUNORA_LIFECYCLE_HOOKS` manifest keyed by this side. Absent on
225
- * ordinary functions.
226
- */
221
+ * Set on connection-lifecycle hooks (`onConnect`/`onDisconnect`): the socket
222
+ * side the hook fires on. Such a function is also an internal mutation (so it
223
+ * lands in `LUNORA_FUNCTIONS` for path dispatch); emit additionally collects
224
+ * it into the `LUNORA_LIFECYCLE_HOOKS` manifest keyed by this side. Absent on
225
+ * ordinary functions.
226
+ */
227
227
  lifecycle?: "connect" | "disconnect";
228
228
  /**
229
- * Serialized TS source for the handler's return type, with `Promise&lt;T>`
230
- * unwrapped so callers see `T` directly. Defaults to `"unknown"` when
231
- * ts-morph cannot resolve the type (typically because the consuming
232
- * project lacks a tsconfig that can reach `@lunora/server`).
233
- */
229
+ * Serialized TS source for the handler's return type, with `Promise&lt;T>`
230
+ * unwrapped so callers see `T` directly. Defaults to `"unknown"` when
231
+ * ts-morph cannot resolve the type (typically because the consuming
232
+ * project lacks a tsconfig that can reach `@lunora/server`).
233
+ */
234
234
  returnType: string;
235
235
  /**
236
- * Call surface the function is exposed on. Absent (or `"public"`) means it
237
- * lands in the generated `api`; `"internal"` routes it to the separate
238
- * `internal` object and is rejected by the DO's external RPC path.
239
- */
236
+ * Call surface the function is exposed on. Absent (or `"public"`) means it
237
+ * lands in the generated `api`; `"internal"` routes it to the separate
238
+ * `internal` object and is rejected by the DO's external RPC path.
239
+ */
240
240
  visibility?: "internal" | "public";
241
241
  }
242
242
  /**
243
- * A `defineMigration({...})` declaration discovered in the user's lunora
244
- * sources. The emitted `LUNORA_MIGRATIONS` registry keys on {@link MigrationIR.id}; the
245
- * import wiring needs {@link MigrationIR.exportName}/{@link MigrationIR.filePath}. {@link MigrationIR.table} is
246
- * informational (the runtime object carries the authoritative value).
247
- */
243
+ * A `defineMigration({...})` declaration discovered in the user's lunora
244
+ * sources. The emitted `LUNORA_MIGRATIONS` registry keys on {@link MigrationIR.id}; the
245
+ * import wiring needs {@link MigrationIR.exportName}/{@link MigrationIR.filePath}. {@link MigrationIR.table} is
246
+ * informational (the runtime object carries the authoritative value).
247
+ */
248
248
  interface MigrationIR {
249
249
  /** Export binding name, used to reference the module member in generated imports. */
250
250
  exportName: string;
@@ -256,59 +256,59 @@ interface MigrationIR {
256
256
  table: string;
257
257
  }
258
258
  /**
259
- * A `defineShape({...})` declaration discovered in `lunora/shapes.ts`
260
- * (local-first sync engine, Phase 7). The emitted `LUNORA_SHAPES` registry keys
261
- * on {@link ShapeIR.exportName}; the generated DO's `resolveShape` override
262
- * dispatches a `shape_subscribe` to the matching registered shape. Discovery is
263
- * marker-driven (the `__lunoraShape` brand) — no field metadata is lifted here
264
- * because the runtime object (`columns`/`compileWhere`) carries the authority.
265
- */
259
+ * A `defineShape({...})` declaration discovered in `lunora/shapes.ts`
260
+ * (local-first sync engine, Phase 7). The emitted `LUNORA_SHAPES` registry keys
261
+ * on {@link ShapeIR.exportName}; the generated DO's `resolveShape` override
262
+ * dispatches a `shape_subscribe` to the matching registered shape. Discovery is
263
+ * marker-driven (the `__lunoraShape` brand) — no field metadata is lifted here
264
+ * because the runtime object (`columns`/`compileWhere`) carries the authority.
265
+ */
266
266
  interface ShapeIR {
267
267
  /** Export binding name — the shape's registry key and import member. */
268
268
  exportName: string;
269
269
  /** Path relative to `&lt;projectRoot>/lunora/` without extension — always `"shapes"`. */
270
270
  filePath: string;
271
271
  /**
272
- * The `table` string literal from the `defineShape({ table })` call, lifted
273
- * only for static advisor lints (the runtime object stays authoritative).
274
- * `undefined` when `table` is not a plain string literal — lints skip those.
275
- */
272
+ * The `table` string literal from the `defineShape({ table })` call, lifted
273
+ * only for static advisor lints (the runtime object stays authoritative).
274
+ * `undefined` when `table` is not a plain string literal — lints skip those.
275
+ */
276
276
  table?: string;
277
277
  }
278
278
  /**
279
- * The single `defineIdentity({...})` claim contract discovered in
280
- * `lunora/identity.ts`. Discovery is **marker-driven** (the `__lunoraIdentity`
281
- * brand, exactly like {@link ShapeIR}) — no claim metadata is lifted here
282
- * because the emitted `_generated/server.ts` recovers the claim *type* from the
283
- * declaration itself (`InferIdentity` over the contract's `typeof`), and the
284
- * runtime object (`validate`/`onInvalid`) carries the authority at the boundary.
285
- * Exactly one per app; absent ⇒ generated output is byte-identical to today.
286
- */
279
+ * The single `defineIdentity({...})` claim contract discovered in
280
+ * `lunora/identity.ts`. Discovery is **marker-driven** (the `__lunoraIdentity`
281
+ * brand, exactly like {@link ShapeIR}) — no claim metadata is lifted here
282
+ * because the emitted `_generated/server.ts` recovers the claim *type* from the
283
+ * declaration itself (`InferIdentity` over the contract's `typeof`), and the
284
+ * runtime object (`validate`/`onInvalid`) carries the authority at the boundary.
285
+ * Exactly one per app; absent ⇒ generated output is byte-identical to today.
286
+ */
287
287
  interface IdentityIR {
288
288
  /** Export binding name — the namespace member `_generated/server.ts` reads via `typeof`. */
289
289
  exportName: string;
290
290
  }
291
291
  /**
292
- * The single `defineEnv({...})` contract discovered in `lunora/env.ts`. Like
293
- * {@link IdentityIR}, only the export binding is lifted — the emitted
294
- * `_generated/server.ts` recovers the validated shape from the declaration
295
- * itself (`ReturnType` over the accessor's `typeof`), and the generated ShardDO
296
- * applies the same accessor to the worker `env` at ctx-build time to populate
297
- * `ctx.env`. Exactly one per app; absent ⇒ generated output is byte-identical.
298
- */
292
+ * The single `defineEnv({...})` contract discovered in `lunora/env.ts`. Like
293
+ * {@link IdentityIR}, only the export binding is lifted — the emitted
294
+ * `_generated/server.ts` recovers the validated shape from the declaration
295
+ * itself (`ReturnType` over the accessor's `typeof`), and the generated ShardDO
296
+ * applies the same accessor to the worker `env` at ctx-build time to populate
297
+ * `ctx.env`. Exactly one per app; absent ⇒ generated output is byte-identical.
298
+ */
299
299
  interface EnvIR {
300
300
  /** Export binding name — the namespace member `_generated/server.ts` reads via `typeof`. */
301
301
  exportName: string;
302
302
  }
303
303
  /**
304
- * A `defineMutator({...})` declaration discovered in `lunora/mutators.ts`
305
- * (local-first sync engine, Phase 7). The emitted registry registers the
306
- * authoritative `server` impl into the DO's `LUNORA_FUNCTIONS` table (so
307
- * `handleRpc` transaction-wraps it) and records its path in
308
- * `LUNORA_MUTATOR_PATHS` so the DO's `isCustomMutator` override routes the
309
- * client-watermark push protocol. The client `client` impl is split into the
310
- * browser bundle separately — only the path crosses to the server side.
311
- */
304
+ * A `defineMutator({...})` declaration discovered in `lunora/mutators.ts`
305
+ * (local-first sync engine, Phase 7). The emitted registry registers the
306
+ * authoritative `server` impl into the DO's `LUNORA_FUNCTIONS` table (so
307
+ * `handleRpc` transaction-wraps it) and records its path in
308
+ * `LUNORA_MUTATOR_PATHS` so the DO's `isCustomMutator` override routes the
309
+ * client-watermark push protocol. The client `client` impl is split into the
310
+ * browser bundle separately — only the path crosses to the server side.
311
+ */
312
312
  interface MutatorIR {
313
313
  /** Export binding name — the mutator's registry key and import member. */
314
314
  exportName: string;
@@ -316,15 +316,15 @@ interface MutatorIR {
316
316
  filePath: string;
317
317
  }
318
318
  /**
319
- * A whole-row `ctx.db.replace(id, document)` write discovered inside a custom
320
- * mutator's inline `server` impl (`lunora/mutators.ts`) — the input the
321
- * `mutator_full_row_replace` advisor lint consumes. A `replace` overwrites the
322
- * entire row, so a concurrent edit to a different column on a synced table is
323
- * clobbered; `ctx.db.patch(id, { field })` merges at the column level instead.
324
- * Structurally identical to `AdvisorMutatorWrite` so it passes straight through
325
- * to the advisor without conversion, exactly as `InsertWriteIR` does for
326
- * `AdvisorInsertWrite`.
327
- */
319
+ * A whole-row `ctx.db.replace(id, document)` write discovered inside a custom
320
+ * mutator's inline `server` impl (`lunora/mutators.ts`) — the input the
321
+ * `mutator_full_row_replace` advisor lint consumes. A `replace` overwrites the
322
+ * entire row, so a concurrent edit to a different column on a synced table is
323
+ * clobbered; `ctx.db.patch(id, { field })` merges at the column level instead.
324
+ * Structurally identical to `AdvisorMutatorWrite` so it passes straight through
325
+ * to the advisor without conversion, exactly as `InsertWriteIR` does for
326
+ * `AdvisorInsertWrite`.
327
+ */
328
328
  interface MutatorWriteIR {
329
329
  /** The mutator export whose `server` impl performs the replace, e.g. `renameChannel`. */
330
330
  exportName: string;
@@ -334,12 +334,12 @@ interface MutatorWriteIR {
334
334
  line: number;
335
335
  }
336
336
  /**
337
- * A single cron job lifted from a `cronJobs()` builder in `lunora/crons.ts`.
338
- * Mirrors `@lunora/scheduler`'s `CronJob`: {@link CronJobIR.cron} is the compiled
339
- * standard cron expression, {@link CronJobIR.functionPath} is the target
340
- * `__lunoraRef` (`namespace:fn`), and {@link CronJobIR.args} is the static
341
- * argument object passed at registration.
342
- */
337
+ * A single cron job lifted from a `cronJobs()` builder in `lunora/crons.ts`.
338
+ * Mirrors `@lunora/scheduler`'s `CronJob`: {@link CronJobIR.cron} is the compiled
339
+ * standard cron expression, {@link CronJobIR.functionPath} is the target
340
+ * `__lunoraRef` (`namespace:fn`), and {@link CronJobIR.args} is the static
341
+ * argument object passed at registration.
342
+ */
343
343
  interface CronJobIR {
344
344
  /** Static args object (source-text JSON), defaults to `{}`. */
345
345
  args: Record<string, unknown>;
@@ -350,25 +350,25 @@ interface CronJobIR {
350
350
  /** Unique, human-readable job name. */
351
351
  name: string;
352
352
  /**
353
- * Set when the job targets a durable workflow (a `lunora/workflows.ts`
354
- * export) instead of a function: the workflow's `WORKFLOW_*` binding name
355
- * plus its export name. On each fire the worker starts a new workflow
356
- * INSTANCE (the {@link CronJobIR.args} become its `params`) rather than
357
- * dispatching a one-shot function.
358
- */
353
+ * Set when the job targets a durable workflow (a `lunora/workflows.ts`
354
+ * export) instead of a function: the workflow's `WORKFLOW_*` binding name
355
+ * plus its export name. On each fire the worker starts a new workflow
356
+ * INSTANCE (the {@link CronJobIR.args} become its `params`) rather than
357
+ * dispatching a one-shot function.
358
+ */
359
359
  workflow?: {
360
360
  binding: string;
361
361
  exportName: string;
362
362
  };
363
363
  }
364
364
  /**
365
- * A container lifted from a `defineContainer()` export in
366
- * `lunora/containers.ts`. Carries everything the emitters and the config layer
367
- * need to wire wrangler (`containers[]` + the Durable Object binding +
368
- * migration class) and the generated `_generated/containers.ts` DO class.
369
- * Names are derived via `@lunora/container`'s shared helpers so codegen and
370
- * the config layer can never disagree.
371
- */
365
+ * A container lifted from a `defineContainer()` export in
366
+ * `lunora/containers.ts`. Carries everything the emitters and the config layer
367
+ * need to wire wrangler (`containers[]` + the Durable Object binding +
368
+ * migration class) and the generated `_generated/containers.ts` DO class.
369
+ * Names are derived via `@lunora/container`'s shared helpers so codegen and
370
+ * the config layer can never disagree.
371
+ */
372
372
  interface ContainerIR {
373
373
  /** Durable Object binding name, e.g. `CONTAINER_TRANSCODER`. */
374
374
  bindingName: string;
@@ -377,18 +377,18 @@ interface ContainerIR {
377
377
  /** Generated DO class name, e.g. `TranscoderContainer`. */
378
378
  className: string;
379
379
  /**
380
- * Whether the container may open outbound internet connections, when the
381
- * value was a static literal. `undefined` means the field was omitted (the
382
- * platform default is `true`) or wasn't a literal. Lifted for the advisor.
383
- */
380
+ * Whether the container may open outbound internet connections, when the
381
+ * value was a static literal. `undefined` means the field was omitted (the
382
+ * platform default is `true`) or wasn't a literal. Lifted for the advisor.
383
+ */
384
384
  enableInternet?: boolean;
385
385
  /** The `lunora/containers.ts` export name, e.g. `transcoder`. */
386
386
  exportName: string;
387
387
  /**
388
- * Normalized image source: a local Dockerfile (`dockerfile`), a pre-built
389
- * registry reference (`registry`), or a Railpack source directory (`build`)
390
- * that the deploy step builds and pushes before wrangler runs.
391
- */
388
+ * Normalized image source: a local Dockerfile (`dockerfile`), a pre-built
389
+ * registry reference (`registry`), or a Railpack source directory (`build`)
390
+ * that the deploy step builds and pushes before wrangler runs.
391
+ */
392
392
  image: {
393
393
  buildContext: string;
394
394
  dockerfilePath: string;
@@ -416,20 +416,20 @@ interface ContainerIR {
416
416
  stepPercentage?: number;
417
417
  };
418
418
  /**
419
- * The static `sleepAfter` value, when it was a literal. `undefined` means
420
- * omitted (platform default `"10m"`) or non-literal. Lifted for the advisor.
421
- */
419
+ * The static `sleepAfter` value, when it was a literal. `undefined` means
420
+ * omitted (platform default `"10m"`) or non-literal. Lifted for the advisor.
421
+ */
422
422
  sleepAfter?: number | string;
423
423
  }
424
424
  /**
425
- * A workflow lifted from a `defineWorkflow()` export in `lunora/workflows.ts`.
426
- * Carries what the emitters and the config layer need to wire the wrangler
427
- * `workflows[]` entry and the generated `_generated/workflows.ts`
428
- * `WorkflowEntrypoint` class. Unlike containers, workflows are NOT Durable
429
- * Objects — wrangler gets only a `workflows[]` entry, never a `durable_objects`
430
- * binding or a migration class. Names are derived via `@lunora/workflow`'s
431
- * shared helpers so codegen and the config layer can never disagree.
432
- */
425
+ * A workflow lifted from a `defineWorkflow()` export in `lunora/workflows.ts`.
426
+ * Carries what the emitters and the config layer need to wire the wrangler
427
+ * `workflows[]` entry and the generated `_generated/workflows.ts`
428
+ * `WorkflowEntrypoint` class. Unlike containers, workflows are NOT Durable
429
+ * Objects — wrangler gets only a `workflows[]` entry, never a `durable_objects`
430
+ * binding or a migration class. Names are derived via `@lunora/workflow`'s
431
+ * shared helpers so codegen and the config layer can never disagree.
432
+ */
433
433
  interface WorkflowIR {
434
434
  /** The Cloudflare `Workflow` binding name, e.g. `WORKFLOW_ORDER_PIPELINE`. */
435
435
  bindingName: string;
@@ -438,32 +438,32 @@ interface WorkflowIR {
438
438
  /** The `lunora/workflows.ts` export name, e.g. `orderPipeline`. */
439
439
  exportName: string;
440
440
  /**
441
- * The stable wrangler `workflows[].name`. Defaults to the kebab-cased export
442
- * name (`orderPipeline` → `order-pipeline`); a static `name:` literal in the
443
- * definition overrides it.
444
- */
441
+ * The stable wrangler `workflows[].name`. Defaults to the kebab-cased export
442
+ * name (`orderPipeline` → `order-pipeline`); a static `name:` literal in the
443
+ * definition overrides it.
444
+ */
445
445
  name: string;
446
446
  /**
447
- * Durable step labels lifted from the handler body — the first string-literal
448
- * argument of every `ctx.step.do` / `.sleep` / `.sleepUntil` / `.waitForEvent`
449
- * call. Feeds the duplicate-step-name lint, which flags a name used twice
450
- * (Cloudflare memoizes by name, so the second call silently returns the
451
- * first's cached result). Calls with a non-literal name are omitted (not
452
- * statically comparable).
453
- */
447
+ * Durable step labels lifted from the handler body — the first string-literal
448
+ * argument of every `ctx.step.do` / `.sleep` / `.sleepUntil` / `.waitForEvent`
449
+ * call. Feeds the duplicate-step-name lint, which flags a name used twice
450
+ * (Cloudflare memoizes by name, so the second call silently returns the
451
+ * first's cached result). Calls with a non-literal name are omitted (not
452
+ * statically comparable).
453
+ */
454
454
  steps: ReadonlyArray<WorkflowStepIR>;
455
455
  }
456
456
  /**
457
- * An agent lifted from a `defineAgent()` export in `lunora/agents.ts`. A
458
- * `defineAgent` compiles its durable tool-loop onto a Cloudflare Workflow, so —
459
- * like {@link WorkflowIR} — an agent is NOT a Durable Object: wrangler gets only
460
- * a `workflows[]` entry, never a `durable_objects` binding or a migration class.
461
- * Carries what the emitters and the config layer need to wire the generated
462
- * agent `WorkflowEntrypoint` class (e.g. `SupportAgentWorkflow`), the typed
463
- * per-agent `ctx.agents` producer, and the reconciled wrangler `workflows[]`
464
- * entry. Names are derived via `@lunora/agent`'s shared helpers so codegen and
465
- * the config layer can never disagree.
466
- */
457
+ * An agent lifted from a `defineAgent()` export in `lunora/agents.ts`. A
458
+ * `defineAgent` compiles its durable tool-loop onto a Cloudflare Workflow, so —
459
+ * like {@link WorkflowIR} — an agent is NOT a Durable Object: wrangler gets only
460
+ * a `workflows[]` entry, never a `durable_objects` binding or a migration class.
461
+ * Carries what the emitters and the config layer need to wire the generated
462
+ * agent `WorkflowEntrypoint` class (e.g. `SupportAgentWorkflow`), the typed
463
+ * per-agent `ctx.agents` producer, and the reconciled wrangler `workflows[]`
464
+ * entry. Names are derived via `@lunora/agent`'s shared helpers so codegen and
465
+ * the config layer can never disagree.
466
+ */
467
467
  interface AgentIR {
468
468
  /** The Cloudflare `Workflow` binding name, e.g. `AGENT_SUPPORT`. */
469
469
  bindingName: string;
@@ -472,39 +472,39 @@ interface AgentIR {
472
472
  /** The `lunora/agents.ts` export name, e.g. `support`. */
473
473
  exportName: string;
474
474
  /**
475
- * The stable wrangler `workflows[].name`. Defaults to the kebab-cased export
476
- * name (`support` → `agent-support`); a static `name:` literal in the
477
- * definition overrides it.
478
- */
475
+ * The stable wrangler `workflows[].name`. Defaults to the kebab-cased export
476
+ * name (`support` → `agent-support`); a static `name:` literal in the
477
+ * definition overrides it.
478
+ */
479
479
  name: string;
480
480
  /**
481
- * Whether the definition declares an `onEmail` mapper on
482
- * `defineAgent({ onEmail: … })`. When `true` the emitter wires this agent
483
- * onto the worker's top-level `email()` handler (via `@lunora/agent/inbound`)
484
- * so inbound mail starts a durable run. Detected by AST PRESENCE — the
485
- * closure is never evaluated — and written to IR only when present, so
486
- * email-free agents (and agent-free projects) stay byte-identical.
487
- */
481
+ * Whether the definition declares an `onEmail` mapper on
482
+ * `defineAgent({ onEmail: … })`. When `true` the emitter wires this agent
483
+ * onto the worker's top-level `email()` handler (via `@lunora/agent/inbound`)
484
+ * so inbound mail starts a durable run. Detected by AST PRESENCE — the
485
+ * closure is never evaluated — and written to IR only when present, so
486
+ * email-free agents (and agent-free projects) stay byte-identical.
487
+ */
488
488
  onEmail?: boolean;
489
489
  /**
490
- * Whether the definition opted into public run-starts via
491
- * `defineAgent({ publicRun: true })` — emitted into the `ctx.agents` wiring
492
- * spec so the public `agents:agentRun` mutation can gate on it fail-closed.
493
- * Absent (falsy) means server-side starts only; the field is written to IR
494
- * only when the literal is `true`, so agent-free and non-opted-in output is
495
- * byte-identical.
496
- */
490
+ * Whether the definition opted into public run-starts via
491
+ * `defineAgent({ publicRun: true })` — emitted into the `ctx.agents` wiring
492
+ * spec so the public `agents:agentRun` mutation can gate on it fail-closed.
493
+ * Absent (falsy) means server-side starts only; the field is written to IR
494
+ * only when the literal is `true`, so agent-free and non-opted-in output is
495
+ * byte-identical.
496
+ */
497
497
  publicRun?: boolean;
498
498
  /**
499
- * Whether the definition opted into a real-time voice session via a `voice`
500
- * block on `defineAgent({ voice: … })`. Unlike the durable loop (a Workflow),
501
- * the voice path IS a Durable Object — so when this is `true` the emitter
502
- * generates the `voiceClassName` `VoiceSessionDO` subclass and the
503
- * `api.agents.{name}Voice` client reference, and the config layer reconciles
504
- * a `durable_objects` binding (`voiceBindingName`) + `new_sqlite_classes`
505
- * migration. Written to IR only when the literal is present, so voice-free
506
- * agents (and agent-free projects) stay byte-identical.
507
- */
499
+ * Whether the definition opted into a real-time voice session via a `voice`
500
+ * block on `defineAgent({ voice: … })`. Unlike the durable loop (a Workflow),
501
+ * the voice path IS a Durable Object — so when this is `true` the emitter
502
+ * generates the `voiceClassName` `VoiceSessionDO` subclass and the
503
+ * `api.agents.{name}Voice` client reference, and the config layer reconciles
504
+ * a `durable_objects` binding (`voiceBindingName`) + `new_sqlite_classes`
505
+ * migration. Written to IR only when the literal is present, so voice-free
506
+ * agents (and agent-free projects) stay byte-identical.
507
+ */
508
508
  voice?: boolean;
509
509
  /** The voice DO's Cloudflare `DurableObjectNamespace` binding name, e.g. `VOICE_SUPPORT`. Present only when `voice`. */
510
510
  voiceBindingName?: string;
@@ -521,14 +521,14 @@ interface WorkflowStepIR {
521
521
  name: string;
522
522
  }
523
523
  /**
524
- * A queue lifted from a `defineQueue()` export in `lunora/queues.ts`. Carries
525
- * what the emitters and the config layer need to wire the typed `ctx.queues`
526
- * producer, the generated worker `queue()` dispatch, and the wrangler
527
- * `queues.producers[]` / `queues.consumers[]` entries. Like workflows, a queue
528
- * is NOT a Durable Object — wrangler gets only `queues.*` entries. Names are
529
- * derived via `@lunora/queue`'s shared helpers so codegen and the config layer
530
- * can never disagree.
531
- */
524
+ * A queue lifted from a `defineQueue()` export in `lunora/queues.ts`. Carries
525
+ * what the emitters and the config layer need to wire the typed `ctx.queues`
526
+ * producer, the generated worker `queue()` dispatch, and the wrangler
527
+ * `queues.producers[]` / `queues.consumers[]` entries. Like workflows, a queue
528
+ * is NOT a Durable Object — wrangler gets only `queues.*` entries. Names are
529
+ * derived via `@lunora/queue`'s shared helpers so codegen and the config layer
530
+ * can never disagree.
531
+ */
532
532
  interface QueueIR {
533
533
  /** The Cloudflare `Queue` producer binding name, e.g. `QUEUE_EMAIL`. */
534
534
  bindingName: string;
@@ -537,10 +537,10 @@ interface QueueIR {
537
537
  /** How the queue is consumed: `"push"` (a worker `queue()` handler) or `"pull"` (external HTTP). */
538
538
  mode: "pull" | "push";
539
539
  /**
540
- * The stable wrangler queue name (`queues.producers[].queue`). Defaults to
541
- * the kebab-cased export name (`emailQueue` → `email-queue`); a static
542
- * `name:` literal in the definition overrides it.
543
- */
540
+ * The stable wrangler queue name (`queues.producers[].queue`). Defaults to
541
+ * the kebab-cased export name (`emailQueue` → `email-queue`); a static
542
+ * `name:` literal in the definition overrides it.
543
+ */
544
544
  name: string;
545
545
  /** Push-consumer batch/retry tuning, mirrored onto the wrangler `queues.consumers[]` entry. */
546
546
  tuning: {
@@ -552,38 +552,38 @@ interface QueueIR {
552
552
  };
553
553
  }
554
554
  /**
555
- * The feature-flag provider declared by the default export of `lunora/flags.ts`
556
- * (`defineFlags({ provider, … })`). Discovery is **metadata-only** — codegen
557
- * imports the real module at runtime for the provider value; this IR exists so
558
- * the config layer can reconcile/validate the wrangler `flagship` binding when
559
- * the app uses Flagship in binding mode. A `custom` provider (any other
560
- * OpenFeature factory) carries no binding to reconcile.
561
- */
555
+ * The feature-flag provider declared by the default export of `lunora/flags.ts`
556
+ * (`defineFlags({ provider, … })`). Discovery is **metadata-only** — codegen
557
+ * imports the real module at runtime for the provider value; this IR exists so
558
+ * the config layer can reconcile/validate the wrangler `flagship` binding when
559
+ * the app uses Flagship in binding mode. A `custom` provider (any other
560
+ * OpenFeature factory) carries no binding to reconcile.
561
+ */
562
562
  interface FlagsIR {
563
563
  /**
564
- * The wrangler `flagship[].binding` name — set **only** for a flagship
565
- * `provider` in binding mode (`flagshipProvider({ binding: "FLAGS" })`). The
566
- * config layer hints/validates a matching `flagship` binding from this.
567
- */
564
+ * The wrangler `flagship[].binding` name — set **only** for a flagship
565
+ * `provider` in binding mode (`flagshipProvider({ binding: "FLAGS" })`). The
566
+ * config layer hints/validates a matching `flagship` binding from this.
567
+ */
568
568
  bindingName?: string;
569
569
  /**
570
- * Flagship operating mode — `"binding"` (wrangler binding, needs a
571
- * `flagship` entry) or `"http"` (no binding); `undefined` for a `custom`
572
- * provider or when the mode can't be read statically.
573
- */
570
+ * Flagship operating mode — `"binding"` (wrangler binding, needs a
571
+ * `flagship` entry) or `"http"` (no binding); `undefined` for a `custom`
572
+ * provider or when the mode can't be read statically.
573
+ */
574
574
  mode?: "binding" | "http";
575
575
  /** `"flagship"` when the provider is `flagshipProvider(...)`, else `"custom"` (any other OpenFeature provider factory). */
576
576
  provider: "custom" | "flagship";
577
577
  }
578
578
  /**
579
- * A `ctx.workflows.get("name")…` call discovered in a function body — the
580
- * use-site analog of {@link WorkflowIR} (which is the declaration side). Feeds
581
- * the `workflow_unused` lint (a declared workflow with zero call sites) and the
582
- * `workflow_unknown_target` lint (a `.get("x")` whose `x` isn't declared — a
583
- * typo catcher). {@link WorkflowCallIR.workflow} is `""` when the `get(...)`
584
- * argument is not a string literal (a dynamic name — which suppresses the
585
- * unused-workflow heuristic rather than producing a false positive).
586
- */
579
+ * A `ctx.workflows.get("name")…` call discovered in a function body — the
580
+ * use-site analog of {@link WorkflowIR} (which is the declaration side). Feeds
581
+ * the `workflow_unused` lint (a declared workflow with zero call sites) and the
582
+ * `workflow_unknown_target` lint (a `.get("x")` whose `x` isn't declared — a
583
+ * typo catcher). {@link WorkflowCallIR.workflow} is `""` when the `get(...)`
584
+ * argument is not a string literal (a dynamic name — which suppresses the
585
+ * unused-workflow heuristic rather than producing a false positive).
586
+ */
587
587
  interface WorkflowCallIR {
588
588
  /** Export binding name of the function performing the call, e.g. `create`. */
589
589
  exportName: string;
@@ -595,11 +595,11 @@ interface WorkflowCallIR {
595
595
  workflow: string;
596
596
  }
597
597
  /**
598
- * A `ctx.db.query("table")…` read discovered in a function body, reduced to what
599
- * the `filter_without_index` advisor lint needs: which table, whether the chain
600
- * narrows with an index, and whether it filters. `table` is `""` when the
601
- * `query(...)` argument is not a string literal (a dynamic table — not lintable).
602
- */
598
+ * A `ctx.db.query("table")…` read discovered in a function body, reduced to what
599
+ * the `filter_without_index` advisor lint needs: which table, whether the chain
600
+ * narrows with an index, and whether it filters. `table` is `""` when the
601
+ * `query(...)` argument is not a string literal (a dynamic table — not lintable).
602
+ */
603
603
  interface QueryReadIR {
604
604
  /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
605
605
  file: string;
@@ -613,12 +613,12 @@ interface QueryReadIR {
613
613
  table: string;
614
614
  }
615
615
  /**
616
- * A `ctx.authApi.&lt;method>(...)` call discovered in a function body, attributed
617
- * to the exported function (and its file = api namespace) that performs it.
618
- * Structurally identical to `AdvisorAuthApiCall` so it passes straight through
619
- * to the advisor lint without conversion, exactly as `InsertWriteIR` does for
620
- * `AdvisorInsertWrite`.
621
- */
616
+ * A `ctx.authApi.&lt;method>(...)` call discovered in a function body, attributed
617
+ * to the exported function (and its file = api namespace) that performs it.
618
+ * Structurally identical to `AdvisorAuthApiCall` so it passes straight through
619
+ * to the advisor lint without conversion, exactly as `InsertWriteIR` does for
620
+ * `AdvisorInsertWrite`.
621
+ */
622
622
  interface AuthApiCallIR {
623
623
  /** Export binding name of the function performing the call, e.g. "createOrg". */
624
624
  exportName: string;
@@ -632,12 +632,12 @@ interface AuthApiCallIR {
632
632
  method: string;
633
633
  }
634
634
  /**
635
- * A `ctx.db.insert("table", …)` write discovered in a function body, attributed
636
- * to the exported function (and its file = api namespace) that performs it — the
637
- * write-side analog of {@link QueryReadIR}. Lets tooling wire a table's write
638
- * action by behavior (which function inserts into it) rather than by naming.
639
- * {@link InsertWriteIR.table} is `""` when the argument is not a string literal.
640
- */
635
+ * A `ctx.db.insert("table", …)` write discovered in a function body, attributed
636
+ * to the exported function (and its file = api namespace) that performs it — the
637
+ * write-side analog of {@link QueryReadIR}. Lets tooling wire a table's write
638
+ * action by behavior (which function inserts into it) rather than by naming.
639
+ * {@link InsertWriteIR.table} is `""` when the argument is not a string literal.
640
+ */
641
641
  interface InsertWriteIR {
642
642
  /** Export binding name of the function performing the insert, e.g. "send". */
643
643
  exportName: string;
@@ -649,14 +649,14 @@ interface InsertWriteIR {
649
649
  table: string;
650
650
  }
651
651
  /**
652
- * A non-deterministic API call (`Date.now`, `Math.random`, `crypto.randomUUID`,
653
- * `crypto.getRandomValues`, `fetch`) discovered lexically inside a `query(...)`
654
- * or `mutation(...)` handler body — the `nondeterministic_query_mutation` lint
655
- * input. Structurally identical to `AdvisorNondeterministicCall` so values pass
656
- * straight through to the advisor without conversion, exactly as `AuthApiCallIR`
657
- * does for `AdvisorAuthApiCall`. `action(...)` handlers are never recorded —
658
- * actions are the determinism escape hatch.
659
- */
652
+ * A non-deterministic API call (`Date.now`, `Math.random`, `crypto.randomUUID`,
653
+ * `crypto.getRandomValues`, `fetch`) discovered lexically inside a `query(...)`
654
+ * or `mutation(...)` handler body — the `nondeterministic_query_mutation` lint
655
+ * input. Structurally identical to `AdvisorNondeterministicCall` so values pass
656
+ * straight through to the advisor without conversion, exactly as `AuthApiCallIR`
657
+ * does for `AdvisorAuthApiCall`. `action(...)` handlers are never recorded —
658
+ * actions are the determinism escape hatch.
659
+ */
660
660
  interface NondeterministicCallIR {
661
661
  /** The non-deterministic API invoked, e.g. `Date.now` / `Math.random` / `crypto.randomUUID` / `fetch`. */
662
662
  callee: string;
@@ -670,13 +670,13 @@ interface NondeterministicCallIR {
670
670
  line: number;
671
671
  }
672
672
  /**
673
- * One `ctx.r2sql` access lexically inside a `query`/`mutation` handler — the
674
- * `r2sql_outside_action` advisor lint input. Structurally identical to the
675
- * advisor's `AdvisorR2sqlCall` (same field set) so values pass straight through
676
- * `lintSchema` without conversion, exactly as `NondeterministicCallIR` does.
677
- * Only `query`/`mutation` handlers are recorded; `action(...)` is the intended
678
- * home for `ctx.r2sql` and is skipped.
679
- */
673
+ * One `ctx.r2sql` access lexically inside a `query`/`mutation` handler — the
674
+ * `r2sql_outside_action` advisor lint input. Structurally identical to the
675
+ * advisor's `AdvisorR2sqlCall` (same field set) so values pass straight through
676
+ * `lintSchema` without conversion, exactly as `NondeterministicCallIR` does.
677
+ * Only `query`/`mutation` handlers are recorded; `action(...)` is the intended
678
+ * home for `ctx.r2sql` and is skipped.
679
+ */
680
680
  interface R2sqlCallIR {
681
681
  /** The accessed `ctx.r2sql` surface, e.g. `ctx.r2sql.query` / `ctx.r2sql.from`. */
682
682
  callee: string;
@@ -690,21 +690,21 @@ interface R2sqlCallIR {
690
690
  line: number;
691
691
  }
692
692
  /**
693
- * Per-procedure RLS usage snapshot, produced by `discoverRlsProcedures` for the
694
- * `rls_uncovered_table` advisor lint. Structurally identical to
695
- * `AdvisorRlsProcedure` (they share the same field set) so values pass straight
696
- * through to the advisor without conversion, exactly as `AuthApiCallIR` does for
697
- * `AdvisorAuthApiCall`.
698
- */
693
+ * Per-procedure RLS usage snapshot, produced by `discoverRlsProcedures` for the
694
+ * `rls_uncovered_table` advisor lint. Structurally identical to
695
+ * `AdvisorRlsProcedure` (they share the same field set) so values pass straight
696
+ * through to the advisor without conversion, exactly as `AuthApiCallIR` does for
697
+ * `AdvisorAuthApiCall`.
698
+ */
699
699
  interface RlsProcedureIR {
700
700
  /** Export binding name of the procedure (e.g. `listDocuments`). */
701
701
  exportName: string;
702
702
  /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
703
703
  file: string;
704
704
  /**
705
- * Table names extracted from the `rls(policies)` array literal. Empty when the
706
- * policies argument is not a statically-readable array literal.
707
- */
705
+ * Table names extracted from the `rls(policies)` array literal. Empty when the
706
+ * policies argument is not a statically-readable array literal.
707
+ */
708
708
  rlsTables: string[];
709
709
  /** Tables read by the procedure via `ctx.db.query/findMany/findFirst/…`. */
710
710
  tablesRead: string[];
@@ -716,22 +716,22 @@ interface RlsProcedureIR {
716
716
  visibility: "internal" | "public";
717
717
  }
718
718
  /**
719
- * One procedure reduced to the facts the `mask_uncovered_pii_column` lint needs:
720
- * whether its builder chain includes `.use(mask(...))`, which `(table, column)`
721
- * pairs that mask declares, and which tables the procedure reads/writes. The
722
- * column-level analogue of {@link RlsProcedureIR}. Structurally identical to
723
- * `AdvisorMaskProcedure` so values pass straight through without conversion.
724
- */
719
+ * One procedure reduced to the facts the `mask_uncovered_pii_column` lint needs:
720
+ * whether its builder chain includes `.use(mask(...))`, which `(table, column)`
721
+ * pairs that mask declares, and which tables the procedure reads/writes. The
722
+ * column-level analogue of {@link RlsProcedureIR}. Structurally identical to
723
+ * `AdvisorMaskProcedure` so values pass straight through without conversion.
724
+ */
725
725
  interface MaskProcedureIR {
726
726
  /** Export binding name of the procedure (e.g. `listUsers`). */
727
727
  exportName: string;
728
728
  /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
729
729
  file: string;
730
730
  /**
731
- * `(table, column)` pairs this procedure's `mask(policies)` object literal
732
- * declares. Empty when the policies argument is not a statically-readable
733
- * object literal (conservative: `usesMask` is still `true`).
734
- */
731
+ * `(table, column)` pairs this procedure's `mask(policies)` object literal
732
+ * declares. Empty when the policies argument is not a statically-readable
733
+ * object literal (conservative: `usesMask` is still `true`).
734
+ */
735
735
  maskColumns: {
736
736
  column: string;
737
737
  table: string;
@@ -746,14 +746,14 @@ interface MaskProcedureIR {
746
746
  visibility: "internal" | "public";
747
747
  }
748
748
  /**
749
- * One masked column surfaced to the studio's data-browser mask preview: a
750
- * `(table, column)` pair plus the declared {@link MaskProcedureIR} strategy so
751
- * the preview can pick redact-vs-hash-vs-custom rendering. Aggregated across the
752
- * project's `.use(mask(...))` chains by `discoverMaskMetadata`; the descriptive
753
- * twin of {@link RlsPolicyIR}. `"custom"` covers any non-string strategy (a
754
- * `(value, ctx) => …` function) — its logic is an opaque closure, never read by
755
- * the UI; the preview renders a fixed sentinel for it.
756
- */
749
+ * One masked column surfaced to the studio's data-browser mask preview: a
750
+ * `(table, column)` pair plus the declared {@link MaskProcedureIR} strategy so
751
+ * the preview can pick redact-vs-hash-vs-custom rendering. Aggregated across the
752
+ * project's `.use(mask(...))` chains by `discoverMaskMetadata`; the descriptive
753
+ * twin of {@link RlsPolicyIR}. `"custom"` covers any non-string strategy (a
754
+ * `(value, ctx) => …` function) — its logic is an opaque closure, never read by
755
+ * the UI; the preview renders a fixed sentinel for it.
756
+ */
757
757
  interface MaskColumnMetadataIR {
758
758
  /** Column the mask policy redacts. */
759
759
  column: string;
@@ -763,26 +763,26 @@ interface MaskColumnMetadataIR {
763
763
  table: string;
764
764
  }
765
765
  /**
766
- * Schema-wide masking metadata the codegen emits into the generated ShardDO so
767
- * the studio's data-browser mask toggle can preview what a non-privileged caller
768
- * would see. Aggregated across every `.use(mask(...))` chain in the project —
769
- * purely descriptive (table + column + strategy), never the masking closure. The
770
- * column-level analogue of {@link RlsMetadataIR}.
771
- */
766
+ * Schema-wide masking metadata the codegen emits into the generated ShardDO so
767
+ * the studio's data-browser mask toggle can preview what a non-privileged caller
768
+ * would see. Aggregated across every `.use(mask(...))` chain in the project —
769
+ * purely descriptive (table + column + strategy), never the masking closure. The
770
+ * column-level analogue of {@link RlsMetadataIR}.
771
+ */
772
772
  interface MaskMetadataIR {
773
773
  /** Every statically-discovered masked column, deduped by `(table, column)` (first declaration wins). */
774
774
  columns: MaskColumnMetadataIR[];
775
775
  }
776
776
  /**
777
- * One masked column whose `mask(policies)` strategy is a statically-known
778
- * literal (`"hash"` or `"redact"`) — the `mask_weak_hash_strategy_on_pii` lint
779
- * input. Unlike {@link MaskColumnMetadataIR} (app-wide, deduped by `(table,
780
- * column)`, studio-preview evidence), this is per declaration site (file + line
781
- * + enclosing export), undeduped, so the lint can point at the exact
782
- * `mask(...)` call that applies a weak strategy. A `MaskFn` (custom, non-literal)
783
- * strategy carries no lint-relevant signal and is never recorded here.
784
- * Structurally identical to `AdvisorMaskStrategy`.
785
- */
777
+ * One masked column whose `mask(policies)` strategy is a statically-known
778
+ * literal (`"hash"` or `"redact"`) — the `mask_weak_hash_strategy_on_pii` lint
779
+ * input. Unlike {@link MaskColumnMetadataIR} (app-wide, deduped by `(table,
780
+ * column)`, studio-preview evidence), this is per declaration site (file + line
781
+ * + enclosing export), undeduped, so the lint can point at the exact
782
+ * `mask(...)` call that applies a weak strategy. A `MaskFn` (custom, non-literal)
783
+ * strategy carries no lint-relevant signal and is never recorded here.
784
+ * Structurally identical to `AdvisorMaskStrategy`.
785
+ */
786
786
  interface MaskStrategyIR {
787
787
  /** Masked column name. */
788
788
  column: string;
@@ -798,13 +798,13 @@ interface MaskStrategyIR {
798
798
  table: string;
799
799
  }
800
800
  /**
801
- * One statically-readable policy entry from an `rls([...])` array literal,
802
- * surfaced to the studio's read-only RLS inspector via the generated
803
- * `rlsPolicies()` hook. Captures the policy's `table` + `on` operation and the
804
- * procedure it guards — never the `when` predicate, which is an opaque JS
805
- * closure (its logic stays in code, not the UI). Produced by
806
- * `discoverRlsProcedures` alongside the lint IR.
807
- */
801
+ * One statically-readable policy entry from an `rls([...])` array literal,
802
+ * surfaced to the studio's read-only RLS inspector via the generated
803
+ * `rlsPolicies()` hook. Captures the policy's `table` + `on` operation and the
804
+ * procedure it guards — never the `when` predicate, which is an opaque JS
805
+ * closure (its logic stays in code, not the UI). Produced by
806
+ * `discoverRlsProcedures` alongside the lint IR.
807
+ */
808
808
  interface RlsPolicyIR {
809
809
  /** Source file (relative to `lunora/`, without extension) the policy is declared in. */
810
810
  file: string;
@@ -816,12 +816,12 @@ interface RlsPolicyIR {
816
816
  table: string;
817
817
  }
818
818
  /**
819
- * One statically-readable role entry from an `rls(policies, { roles: [...] })`
820
- * call, surfaced to the studio's RLS inspector. Captures the role's `name`,
821
- * optional `description`, and the names of the permissions it grants (string
822
- * literals or `definePermission("name")` calls). Produced by
823
- * `discoverRlsProcedures`.
824
- */
819
+ * One statically-readable role entry from an `rls(policies, { roles: [...] })`
820
+ * call, surfaced to the studio's RLS inspector. Captures the role's `name`,
821
+ * optional `description`, and the names of the permissions it grants (string
822
+ * literals or `definePermission("name")` calls). Produced by
823
+ * `discoverRlsProcedures`.
824
+ */
825
825
  interface RlsRoleIR {
826
826
  /** Optional human-readable description from `defineRole(name, { description })`. */
827
827
  description?: string;
@@ -831,11 +831,11 @@ interface RlsRoleIR {
831
831
  permissions: string[];
832
832
  }
833
833
  /**
834
- * Schema-wide RLS metadata the codegen emits into the generated ShardDO so the
835
- * studio's read-only inspector can list, per table, which policies guard it and
836
- * what roles are defined. Aggregated across every `.use(rls(...))` chain in the
837
- * project — purely descriptive, never the predicate logic.
838
- */
834
+ * Schema-wide RLS metadata the codegen emits into the generated ShardDO so the
835
+ * studio's read-only inspector can list, per table, which policies guard it and
836
+ * what roles are defined. Aggregated across every `.use(rls(...))` chain in the
837
+ * project — purely descriptive, never the predicate logic.
838
+ */
839
839
  interface RlsMetadataIR {
840
840
  /** Every statically-discovered policy `(table, on, procedure)` entry. */
841
841
  policies: RlsPolicyIR[];
@@ -854,32 +854,32 @@ interface StorageRuleIR {
854
854
  procedure: string;
855
855
  }
856
856
  /**
857
- * Schema-wide storage-access-rule metadata emitted into the generated ShardDO so
858
- * the studio's read-only inspector can list, per bucket, which operations are
859
- * gated and under what key prefix. Aggregated across every
860
- * `.use(storageRules(...))` chain — descriptive only, never the predicate logic.
861
- */
857
+ * Schema-wide storage-access-rule metadata emitted into the generated ShardDO so
858
+ * the studio's read-only inspector can list, per bucket, which operations are
859
+ * gated and under what key prefix. Aggregated across every
860
+ * `.use(storageRules(...))` chain — descriptive only, never the predicate logic.
861
+ */
862
862
  interface StorageRulesMetadataIR {
863
863
  rules: StorageRuleIR[];
864
864
  }
865
865
  /**
866
- * A typed REST route declared with the `httpRoute.&lt;verb>("/path")…` builder in
867
- * `@lunora/server` and mounted on `httpRouter()`. Captured statically from the
868
- * builder chain so the OpenAPI emitter can render a real `paths` entry: the verb
869
- * + path become the operation's method + URL, and the accumulated validator maps
870
- * become its query parameters, path parameters, and request body.
871
- */
866
+ * A typed REST route declared with the `httpRoute.&lt;verb>("/path")…` builder in
867
+ * `@lunora/server` and mounted on `httpRouter()`. Captured statically from the
868
+ * builder chain so the OpenAPI emitter can render a real `paths` entry: the verb
869
+ * + path become the operation's method + URL, and the accumulated validator maps
870
+ * become its query parameters, path parameters, and request body.
871
+ */
872
872
  interface HttpRouteIR {
873
873
  /** `v.*` validators decoding the JSON request body (`.body({...})`), keyed by field. */
874
874
  body: Record<string, ValidatorIR>;
875
875
  /**
876
- * Rendered TS type of one SSE chunk — the `R` the `.stream(handler)`
877
- * handler yields — inferred from the handler via the type checker. Present
878
- * only when {@link HttpRouteIR.stream} is `true`; `"unknown"` when the
879
- * checker can't resolve enough context. Feeds the emitted
880
- * `HttpStreamRef&lt;Chunk, …>` so the chunk type flows to the client.
881
- * @experimental Part of the HTTP-SSE stream surface (the `httpStreams.*` emission).
882
- */
876
+ * Rendered TS type of one SSE chunk — the `R` the `.stream(handler)`
877
+ * handler yields — inferred from the handler via the type checker. Present
878
+ * only when {@link HttpRouteIR.stream} is `true`; `"unknown"` when the
879
+ * checker can't resolve enough context. Feeds the emitted
880
+ * `HttpStreamRef&lt;Chunk, …>` so the chunk type flows to the client.
881
+ * @experimental Part of the HTTP-SSE stream surface (the `httpStreams.*` emission).
882
+ */
883
883
  chunkType?: string;
884
884
  /** Export binding name of the route handler (used only for diagnostics / dedupe). */
885
885
  exportName: string;
@@ -899,17 +899,17 @@ interface HttpRouteIR {
899
899
  stream: boolean;
900
900
  }
901
901
  /**
902
- * Per-procedure protective-middleware snapshot, produced by
903
- * `discoverProcedureMiddleware` for the security lints
904
- * (`public_mutation_without_ratelimit`, `user_creating_mutation_without_captcha`).
905
- * Records which `.use(...)` guards a procedure's builder chain carries plus the
906
- * behavioural facts that decide whether a guard is *expected* (does it write a
907
- * user/session table, does it send mail). `protectPublic({ rateLimit, captcha })`
908
- * is unwrapped: the bundle's object-literal keys set `usesRateLimit`/`usesCaptcha`
909
- * exactly as the individual `.use(rateLimit(...))` / `.use(verifyTurnstile(...))`
910
- * steps would. Structurally identical to `AdvisorProcedureProtection` so values
911
- * pass straight through to the advisor without conversion.
912
- */
902
+ * Per-procedure protective-middleware snapshot, produced by
903
+ * `discoverProcedureMiddleware` for the security lints
904
+ * (`public_mutation_without_ratelimit`, `user_creating_mutation_without_captcha`).
905
+ * Records which `.use(...)` guards a procedure's builder chain carries plus the
906
+ * behavioural facts that decide whether a guard is *expected* (does it write a
907
+ * user/session table, does it send mail). `protectPublic({ rateLimit, captcha })`
908
+ * is unwrapped: the bundle's object-literal keys set `usesRateLimit`/`usesCaptcha`
909
+ * exactly as the individual `.use(rateLimit(...))` / `.use(verifyTurnstile(...))`
910
+ * steps would. Structurally identical to `AdvisorProcedureProtection` so values
911
+ * pass straight through to the advisor without conversion.
912
+ */
913
913
  interface ProcedureMiddlewareIR {
914
914
  /** `true` when the handler (or a helper inside it) references `ctx.mail` / `ctx.email`. */
915
915
  callsMail: boolean;
@@ -939,13 +939,13 @@ interface ProcedureMiddlewareIR {
939
939
  writesUserTable: boolean;
940
940
  }
941
941
  /**
942
- * Per-procedure argument-validator snapshot, produced by the
943
- * argument-validator discoverer for the input-hardening lints
944
- * (`public_arg_uses_any`, `unbounded_string_arg`). Only public procedures are
945
- * recorded — internal functions take server-trusted input. Structurally identical
946
- * to `AdvisorArgumentValidator` so it passes straight through to the advisor
947
- * without conversion.
948
- */
942
+ * Per-procedure argument-validator snapshot, produced by the
943
+ * argument-validator discoverer for the input-hardening lints
944
+ * (`public_arg_uses_any`, `unbounded_string_arg`). Only public procedures are
945
+ * recorded — internal functions take server-trusted input. Structurally identical
946
+ * to `AdvisorArgumentValidator` so it passes straight through to the advisor
947
+ * without conversion.
948
+ */
949
949
  interface ArgumentValidatorIR {
950
950
  /** Arg names declared as `v.any()` (unvalidated, untyped input). */
951
951
  anyArgs: string[];
@@ -959,12 +959,12 @@ interface ArgumentValidatorIR {
959
959
  unboundedStringArgs: string[];
960
960
  }
961
961
  /**
962
- * One factory/constructor call in `lunora/` whose config object literal a
963
- * security lint inspects for a present-or-absent key — the shared input for the
964
- * config-call security lints (payment authorize, inbound-mail verify, rate-limit
965
- * store, browser private-targets). Structurally identical to `AdvisorConfigCall`
966
- * so it passes straight through to the advisor without conversion.
967
- */
962
+ * One factory/constructor call in `lunora/` whose config object literal a
963
+ * security lint inspects for a present-or-absent key — the shared input for the
964
+ * config-call security lints (payment authorize, inbound-mail verify, rate-limit
965
+ * store, browser private-targets). Structurally identical to `AdvisorConfigCall`
966
+ * so it passes straight through to the advisor without conversion.
967
+ */
968
968
  interface ConfigCallIR {
969
969
  /** `true` when the config argument was a static object literal the feeder could read. */
970
970
  analyzable: boolean;
@@ -980,11 +980,11 @@ interface ConfigCallIR {
980
980
  trueKeys: string[];
981
981
  }
982
982
  /**
983
- * One secret-shaped string literal discovered in `lunora/` source — the
984
- * `hardcoded_secret` lint input. Complements the pre-commit `vis secrets` scan by
985
- * surfacing the same class of finding in-IDE via the studio Advisors table.
986
- * Structurally identical to `AdvisorSecretLiteral`.
987
- */
983
+ * One secret-shaped string literal discovered in `lunora/` source — the
984
+ * `hardcoded_secret` lint input. Complements the pre-commit `vis secrets` scan by
985
+ * surfacing the same class of finding in-IDE via the studio Advisors table.
986
+ * Structurally identical to `AdvisorSecretLiteral`.
987
+ */
988
988
  interface SecretLiteralIR {
989
989
  /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
990
990
  file: string;
@@ -996,14 +996,14 @@ interface SecretLiteralIR {
996
996
  preview: string;
997
997
  }
998
998
  /**
999
- * One `ctx.sql.query(text, …)` / `ctx.sql.unsafe(text, …)` call whose `text`
1000
- * argument is built in place rather than passed as a fixed statement — the
1001
- * `sql_injection_risk` lint input. The Hyperdrive driver binds ONLY the `params`
1002
- * array; the `text` string is spliced verbatim into the SQL, so a `text` assembled
1003
- * from a string concatenation or a substitution template literal is an injection
1004
- * vector. A fixed string literal / no-substitution template is safe. Structurally
1005
- * identical to `AdvisorSqlInterpolation`.
1006
- */
999
+ * One `ctx.sql.query(text, …)` / `ctx.sql.unsafe(text, …)` call whose `text`
1000
+ * argument is built in place rather than passed as a fixed statement — the
1001
+ * `sql_injection_risk` lint input. The Hyperdrive driver binds ONLY the `params`
1002
+ * array; the `text` string is spliced verbatim into the SQL, so a `text` assembled
1003
+ * from a string concatenation or a substitution template literal is an injection
1004
+ * vector. A fixed string literal / no-substitution template is safe. Structurally
1005
+ * identical to `AdvisorSqlInterpolation`.
1006
+ */
1007
1007
  interface SqlInterpolationIR {
1008
1008
  /** Export binding name of the procedure performing the `ctx.sql` call. */
1009
1009
  exportName: string;
@@ -1013,14 +1013,14 @@ interface SqlInterpolationIR {
1013
1013
  line: number;
1014
1014
  }
1015
1015
  /**
1016
- * One `ctx.fetch(url, …)` call inside an action whose URL argument is derived
1017
- * from the handler's `args` — the `action_fetch_ssrf` lint input. `ctx.fetch` is
1018
- * the action-only outbound-request escape hatch with no host allowlist, so a URL
1019
- * assembled from request input is a server-side request forgery vector (cloud
1020
- * metadata endpoints, internal services). Only arg-derived URLs reach here; a
1021
- * fixed literal or a URL built from config/`ctx.*` is not recorded. Structurally
1022
- * identical to `AdvisorArgumentDerivedFetch`.
1023
- */
1016
+ * One `ctx.fetch(url, …)` call inside an action whose URL argument is derived
1017
+ * from the handler's `args` — the `action_fetch_ssrf` lint input. `ctx.fetch` is
1018
+ * the action-only outbound-request escape hatch with no host allowlist, so a URL
1019
+ * assembled from request input is a server-side request forgery vector (cloud
1020
+ * metadata endpoints, internal services). Only arg-derived URLs reach here; a
1021
+ * fixed literal or a URL built from config/`ctx.*` is not recorded. Structurally
1022
+ * identical to `AdvisorArgumentDerivedFetch`.
1023
+ */
1024
1024
  interface ArgumentDerivedFetchIR {
1025
1025
  /** Export binding name of the action performing the `ctx.fetch` call. */
1026
1026
  exportName: string;
@@ -1030,15 +1030,15 @@ interface ArgumentDerivedFetchIR {
1030
1030
  line: number;
1031
1031
  }
1032
1032
  /**
1033
- * One `ctx.kv.&lt;method>(key, …)` call whose namespace key is derived from the
1034
- * handler's `args` with no server-side scoping — the `kv_unscoped_user_key_idor`
1035
- * lint input. Workers KV is a single flat namespace, so a key taken straight from
1036
- * request input lets any caller read, overwrite, or delete another user's entry
1037
- * (IDOR). Only arg-derived, unscoped keys reach here; a fixed literal, or a key
1038
- * prefixed with a server-trusted identity (`${ctx.auth.userId}:…` — references
1039
- * `ctx`, so treated as scoped), is not recorded. `list` is excluded (it takes a
1040
- * prefix, not a per-entry key). Structurally identical to `AdvisorKvKeyAccess`.
1041
- */
1033
+ * One `ctx.kv.&lt;method>(key, …)` call whose namespace key is derived from the
1034
+ * handler's `args` with no server-side scoping — the `kv_unscoped_user_key_idor`
1035
+ * lint input. Workers KV is a single flat namespace, so a key taken straight from
1036
+ * request input lets any caller read, overwrite, or delete another user's entry
1037
+ * (IDOR). Only arg-derived, unscoped keys reach here; a fixed literal, or a key
1038
+ * prefixed with a server-trusted identity (`${ctx.auth.userId}:…` — references
1039
+ * `ctx`, so treated as scoped), is not recorded. `list` is excluded (it takes a
1040
+ * prefix, not a per-entry key). Structurally identical to `AdvisorKvKeyAccess`.
1041
+ */
1042
1042
  interface KvKeyAccessIR {
1043
1043
  /** Export binding name of the procedure performing the `ctx.kv` access. */
1044
1044
  exportName: string;
@@ -1050,16 +1050,16 @@ interface KvKeyAccessIR {
1050
1050
  method: string;
1051
1051
  }
1052
1052
  /**
1053
- * One `ctx.db` write (`insert` / `replace` / `patch` / `insertManyUnsafe`) that sets
1054
- * an ownership / identity column — `userId`, `ownerId`, `tenantId`, and the like —
1055
- * from the handler's `args` instead of the server-trusted identity. The
1056
- * `owner_field_from_args_not_auth` lint input: the ownership column decides who a
1057
- * row belongs to, so a value taken from request input lets any caller write rows
1058
- * owned by another user or tenant (the act-as-any-user / cross-tenant IDOR vector).
1059
- * A column stamped from `ctx.*`, or set to a fixed literal, is not recorded; only an
1060
- * arg-derived identity write reaches here. Structurally identical to
1061
- * `AdvisorOwnerFieldWrite`.
1062
- */
1053
+ * One `ctx.db` write (`insert` / `replace` / `patch` / `insertManyUnsafe`) that sets
1054
+ * an ownership / identity column — `userId`, `ownerId`, `tenantId`, and the like —
1055
+ * from the handler's `args` instead of the server-trusted identity. The
1056
+ * `owner_field_from_args_not_auth` lint input: the ownership column decides who a
1057
+ * row belongs to, so a value taken from request input lets any caller write rows
1058
+ * owned by another user or tenant (the act-as-any-user / cross-tenant IDOR vector).
1059
+ * A column stamped from `ctx.*`, or set to a fixed literal, is not recorded; only an
1060
+ * arg-derived identity write reaches here. Structurally identical to
1061
+ * `AdvisorOwnerFieldWrite`.
1062
+ */
1063
1063
  interface OwnerFieldWriteIR {
1064
1064
  /** Export binding name of the procedure performing the write. */
1065
1065
  exportName: string;
@@ -1073,15 +1073,15 @@ interface OwnerFieldWriteIR {
1073
1073
  method: string;
1074
1074
  }
1075
1075
  /**
1076
- * One `ctx.storage.&lt;bucket>.&lt;method>(key, …)` call whose R2 object key is derived
1077
- * from the handler's `args` with no server-side scoping — the
1078
- * `storage_key_from_user_args` lint input. The bucket read/write/URL/delete methods
1079
- * key by their first argument, so an object key taken straight from request input is
1080
- * object-level IDOR (read/overwrite/delete anyone's object). A key referencing a
1081
- * server-trusted `ctx.*` value (e.g. `${ctx.auth.userId}/…`) is treated as scoped
1082
- * and is not recorded; only an arg-derived, `ctx`-free key reaches here.
1083
- * Structurally identical to `AdvisorStorageKeyAccess`.
1084
- */
1076
+ * One `ctx.storage.&lt;bucket>.&lt;method>(key, …)` call whose R2 object key is derived
1077
+ * from the handler's `args` with no server-side scoping — the
1078
+ * `storage_key_from_user_args` lint input. The bucket read/write/URL/delete methods
1079
+ * key by their first argument, so an object key taken straight from request input is
1080
+ * object-level IDOR (read/overwrite/delete anyone's object). A key referencing a
1081
+ * server-trusted `ctx.*` value (e.g. `${ctx.auth.userId}/…`) is treated as scoped
1082
+ * and is not recorded; only an arg-derived, `ctx`-free key reaches here.
1083
+ * Structurally identical to `AdvisorStorageKeyAccess`.
1084
+ */
1085
1085
  interface StorageKeyAccessIR {
1086
1086
  /** Export binding name of the procedure performing the storage call. */
1087
1087
  exportName: string;
@@ -1093,16 +1093,16 @@ interface StorageKeyAccessIR {
1093
1093
  method: string;
1094
1094
  }
1095
1095
  /**
1096
- * One `ctx.containers.&lt;exportName>.get(name, …)` call whose instance key is derived
1097
- * from the handler's `args` with no server-side scoping — the
1098
- * `container_instance_key_from_user_input` lint input. Each container definition's
1099
- * `.get(name)` accessor routes to one instance per `name`, so a key taken straight from
1100
- * request input lets any caller reach another tenant's container (a cross-tenant IDOR). A
1101
- * fixed literal key, or one derived from a server-trusted identity (`${ctx.auth.userId}` —
1102
- * references `ctx`, so treated as scoped), is not recorded; only an arg-derived, unscoped
1103
- * key reaches here. `.any()`/`.pool()` take no key and are not sinks. Structurally
1104
- * identical to `AdvisorContainerKeyAccess`.
1105
- */
1096
+ * One `ctx.containers.&lt;exportName>.get(name, …)` call whose instance key is derived
1097
+ * from the handler's `args` with no server-side scoping — the
1098
+ * `container_instance_key_from_user_input` lint input. Each container definition's
1099
+ * `.get(name)` accessor routes to one instance per `name`, so a key taken straight from
1100
+ * request input lets any caller reach another tenant's container (a cross-tenant IDOR). A
1101
+ * fixed literal key, or one derived from a server-trusted identity (`${ctx.auth.userId}` —
1102
+ * references `ctx`, so treated as scoped), is not recorded; only an arg-derived, unscoped
1103
+ * key reaches here. `.any()`/`.pool()` take no key and are not sinks. Structurally
1104
+ * identical to `AdvisorContainerKeyAccess`.
1105
+ */
1106
1106
  interface ContainerKeyAccessIR {
1107
1107
  /** Export binding name of the procedure performing the `ctx.containers` access. */
1108
1108
  exportName: string;
@@ -1114,15 +1114,15 @@ interface ContainerKeyAccessIR {
1114
1114
  method: string;
1115
1115
  }
1116
1116
  /**
1117
- * One `ctx.ai.run(model, …)` call whose model-id argument is derived from the handler's
1118
- * `args` with no server-side scoping — the `ai_raw_run_escape_hatch` lint input.
1119
- * `ctx.ai.run` is the raw Workers AI binding passthrough, bypassing the typed
1120
- * `ctx.ai.model(...)` + AI-SDK layer (`generateText`/`streamText`/…) that caps output and
1121
- * enforces a schema, so an arg-derived model id lets any caller select an arbitrary model.
1122
- * A fixed literal model, or one scoped by a server-trusted `ctx.*` value, is not recorded;
1123
- * only an arg-derived, unscoped model id reaches here (an arg-derived `inputs` argument is
1124
- * normal usage and is never inspected). Structurally identical to `AdvisorAiRawRun`.
1125
- */
1117
+ * One `ctx.ai.run(model, …)` call whose model-id argument is derived from the handler's
1118
+ * `args` with no server-side scoping — the `ai_raw_run_escape_hatch` lint input.
1119
+ * `ctx.ai.run` is the raw Workers AI binding passthrough, bypassing the typed
1120
+ * `ctx.ai.model(...)` + AI-SDK layer (`generateText`/`streamText`/…) that caps output and
1121
+ * enforces a schema, so an arg-derived model id lets any caller select an arbitrary model.
1122
+ * A fixed literal model, or one scoped by a server-trusted `ctx.*` value, is not recorded;
1123
+ * only an arg-derived, unscoped model id reaches here (an arg-derived `inputs` argument is
1124
+ * normal usage and is never inspected). Structurally identical to `AdvisorAiRawRun`.
1125
+ */
1126
1126
  interface AiRawRunIR {
1127
1127
  /** Export binding name of the procedure performing the `ctx.ai.run` call. */
1128
1128
  exportName: string;
@@ -1132,15 +1132,15 @@ interface AiRawRunIR {
1132
1132
  line: number;
1133
1133
  }
1134
1134
  /**
1135
- * One `ctx.vectors.&lt;method>(indexName, input)` call whose `input.namespace` is derived
1136
- * from the handler's `args` with no server-side scoping — the
1137
- * `vectors_namespace_from_user_input` lint input. A Vectorize namespace partitions one
1138
- * index into isolated sub-collections, so a namespace taken straight from request input
1139
- * lets any caller read or poison another tenant's vectors. A fixed literal namespace, or
1140
- * one prefixed with a server-trusted identity (`${ctx.auth.orgId}` — references `ctx`, so
1141
- * treated as scoped), is not recorded; only an arg-derived, unscoped namespace reaches
1142
- * here. Structurally identical to `AdvisorVectorNamespaceAccess`.
1143
- */
1135
+ * One `ctx.vectors.&lt;method>(indexName, input)` call whose `input.namespace` is derived
1136
+ * from the handler's `args` with no server-side scoping — the
1137
+ * `vectors_namespace_from_user_input` lint input. A Vectorize namespace partitions one
1138
+ * index into isolated sub-collections, so a namespace taken straight from request input
1139
+ * lets any caller read or poison another tenant's vectors. A fixed literal namespace, or
1140
+ * one prefixed with a server-trusted identity (`${ctx.auth.orgId}` — references `ctx`, so
1141
+ * treated as scoped), is not recorded; only an arg-derived, unscoped namespace reaches
1142
+ * here. Structurally identical to `AdvisorVectorNamespaceAccess`.
1143
+ */
1144
1144
  interface VectorNamespaceAccessIR {
1145
1145
  /** Export binding name of the procedure performing the `ctx.vectors` access. */
1146
1146
  exportName: string;
@@ -1152,15 +1152,15 @@ interface VectorNamespaceAccessIR {
1152
1152
  method: string;
1153
1153
  }
1154
1154
  /**
1155
- * One `ctx.mail`/`ctx.email` `send`/`queue` call whose recipient field (`to`/`cc`/`bcc`)
1156
- * is derived from the handler's `args` with no server-side scoping — the
1157
- * `mail_recipient_from_request_input` lint input. A recipient taken straight from request
1158
- * input turns the deployment into an open relay / spam amplifier (any caller can direct
1159
- * mail to an arbitrary address). A fixed literal recipient, or one scoped by a
1160
- * server-trusted `ctx.*` value (e.g. `ctx.auth.user.email`), is not recorded; only an
1161
- * arg-derived, unscoped recipient reaches here. Structurally identical to
1162
- * `AdvisorMailRecipientAccess`.
1163
- */
1155
+ * One `ctx.mail`/`ctx.email` `send`/`queue` call whose recipient field (`to`/`cc`/`bcc`)
1156
+ * is derived from the handler's `args` with no server-side scoping — the
1157
+ * `mail_recipient_from_request_input` lint input. A recipient taken straight from request
1158
+ * input turns the deployment into an open relay / spam amplifier (any caller can direct
1159
+ * mail to an arbitrary address). A fixed literal recipient, or one scoped by a
1160
+ * server-trusted `ctx.*` value (e.g. `ctx.auth.user.email`), is not recorded; only an
1161
+ * arg-derived, unscoped recipient reaches here. Structurally identical to
1162
+ * `AdvisorMailRecipientAccess`.
1163
+ */
1164
1164
  interface MailRecipientAccessIR {
1165
1165
  /** Export binding name of the procedure performing the `ctx.mail`/`ctx.email` call. */
1166
1166
  exportName: string;
@@ -1172,13 +1172,13 @@ interface MailRecipientAccessIR {
1172
1172
  method: string;
1173
1173
  }
1174
1174
  /**
1175
- * One `ctx.browser.&lt;method>(url, …)` call whose navigation URL (`arguments[0]`)
1176
- * is derived from the handler's `args` with no server-side scoping — the
1177
- * `browser_user_url_without_allowlist` lint input. The lint additionally
1178
- * cross-references `createBrowser` config-call evidence to suppress findings
1179
- * when the browser is hardened with an `allowedHosts` allowlist or
1180
- * `resolveDns`. Structurally identical to `AdvisorBrowserUrlAccess`.
1181
- */
1175
+ * One `ctx.browser.&lt;method>(url, …)` call whose navigation URL (`arguments[0]`)
1176
+ * is derived from the handler's `args` with no server-side scoping — the
1177
+ * `browser_user_url_without_allowlist` lint input. The lint additionally
1178
+ * cross-references `createBrowser` config-call evidence to suppress findings
1179
+ * when the browser is hardened with an `allowedHosts` allowlist or
1180
+ * `resolveDns`. Structurally identical to `AdvisorBrowserUrlAccess`.
1181
+ */
1182
1182
  interface BrowserUrlAccessIR {
1183
1183
  /** Export binding name of the procedure performing the `ctx.browser` call. */
1184
1184
  exportName: string;
@@ -1190,14 +1190,14 @@ interface BrowserUrlAccessIR {
1190
1190
  method: string;
1191
1191
  }
1192
1192
  /**
1193
- * One runtime container-override call: a `&lt;handle>.start({ enableInternet: true, … })`
1194
- * launch override, or a `&lt;handle>.egress.&lt;method>(...)` runtime firewall mutation
1195
- * (`allow` / `deny` / `setAllowed`) — the `container_start_enable_internet_override`
1196
- * and `container_runtime_egress_relaxation` lint input. Both shapes re-open network
1197
- * access the static `defineContainer` declaration (and its `container_public_internet`
1198
- * lint) assumes is locked down. Matched structurally by call shape, independent of the
1199
- * receiver's resolved type. Structurally identical to `AdvisorContainerOverride`.
1200
- */
1193
+ * One runtime container-override call: a `&lt;handle>.start({ enableInternet: true, … })`
1194
+ * launch override, or a `&lt;handle>.egress.&lt;method>(...)` runtime firewall mutation
1195
+ * (`allow` / `deny` / `setAllowed`) — the `container_start_enable_internet_override`
1196
+ * and `container_runtime_egress_relaxation` lint input. Both shapes re-open network
1197
+ * access the static `defineContainer` declaration (and its `container_public_internet`
1198
+ * lint) assumes is locked down. Matched structurally by call shape, independent of the
1199
+ * receiver's resolved type. Structurally identical to `AdvisorContainerOverride`.
1200
+ */
1201
1201
  interface ContainerOverrideIR {
1202
1202
  /** e.g. the egress method name, or `"enableInternet: true"`. */
1203
1203
  detail: string;
@@ -1211,18 +1211,18 @@ interface ContainerOverrideIR {
1211
1211
  line: number;
1212
1212
  }
1213
1213
  /**
1214
- * One `buildImageDeliveryUrl({ key, … })` call (`@lunora/bindings/images`) whose
1215
- * `key` — the CDN transform's source image, an absolute URL or an
1216
- * origin-relative key — is derived from the handler's `args` with no
1217
- * server-side scoping — the `images_url_source_from_user_input` lint input.
1218
- * `ctx.images.transform`/`info` take image *bytes*, never a URL, so they are not
1219
- * sinks; only the `key` of `buildImageDeliveryUrl` accepts a URL-or-key source
1220
- * and is inspected. An arg-derived `key` lets any caller point the CDN's
1221
- * `/cdn-cgi/image/` transform at an attacker-chosen origin (SSRF / open proxy)
1222
- * or at an arbitrary key under the account's own store. A fixed literal, or a
1223
- * key scoped by a server-trusted `ctx.*` value, is not recorded. Structurally
1224
- * identical to `AdvisorImageDeliveryUrlAccess`.
1225
- */
1214
+ * One `buildImageDeliveryUrl({ key, … })` call (`@lunora/bindings/images`) whose
1215
+ * `key` — the CDN transform's source image, an absolute URL or an
1216
+ * origin-relative key — is derived from the handler's `args` with no
1217
+ * server-side scoping — the `images_url_source_from_user_input` lint input.
1218
+ * `ctx.images.transform`/`info` take image *bytes*, never a URL, so they are not
1219
+ * sinks; only the `key` of `buildImageDeliveryUrl` accepts a URL-or-key source
1220
+ * and is inspected. An arg-derived `key` lets any caller point the CDN's
1221
+ * `/cdn-cgi/image/` transform at an attacker-chosen origin (SSRF / open proxy)
1222
+ * or at an arbitrary key under the account's own store. A fixed literal, or a
1223
+ * key scoped by a server-trusted `ctx.*` value, is not recorded. Structurally
1224
+ * identical to `AdvisorImageDeliveryUrlAccess`.
1225
+ */
1226
1226
  interface ImageDeliveryUrlAccessIR {
1227
1227
  /** Export binding name of the procedure performing the `buildImageDeliveryUrl` call. */
1228
1228
  exportName: string;
@@ -1232,17 +1232,17 @@ interface ImageDeliveryUrlAccessIR {
1232
1232
  line: number;
1233
1233
  }
1234
1234
  /**
1235
- * One `createAuth({...})` call's configuration snapshot — the shared input for
1236
- * the five `auth_*` security lints (trusted-origins wildcard, CSRF check
1237
- * disabled, secure cookies disabled, email verification disabled, session
1238
- * freshAge zero). Matched by callee NAME (an `import`-agnostic, fail-closed
1239
- * convention the other feeders share), so a re-export or alias still resolves.
1240
- * When the config argument isn't a statically-analyzable object literal (a
1241
- * top-level spread, or not an object literal at all), `analyzable` is `false`
1242
- * and every boolean fact defaults to its SAFE (not-flagged) value — an opaque
1243
- * config can't be relied on either way. Structurally identical to
1244
- * `AdvisorAuthConfig`.
1245
- */
1235
+ * One `createAuth({...})` call's configuration snapshot — the shared input for
1236
+ * the five `auth_*` security lints (trusted-origins wildcard, CSRF check
1237
+ * disabled, secure cookies disabled, email verification disabled, session
1238
+ * freshAge zero). Matched by callee NAME (an `import`-agnostic, fail-closed
1239
+ * convention the other feeders share), so a re-export or alias still resolves.
1240
+ * When the config argument isn't a statically-analyzable object literal (a
1241
+ * top-level spread, or not an object literal at all), `analyzable` is `false`
1242
+ * and every boolean fact defaults to its SAFE (not-flagged) value — an opaque
1243
+ * config can't be relied on either way. Structurally identical to
1244
+ * `AdvisorAuthConfig`.
1245
+ */
1246
1246
  interface AuthConfigIR {
1247
1247
  /** `true` when the call's config argument was a static object literal the feeder could read. */
1248
1248
  analyzable: boolean;
@@ -1266,17 +1266,17 @@ interface AuthConfigIR {
1266
1266
  trustedOriginsWildcard: boolean;
1267
1267
  }
1268
1268
  /**
1269
- * One `rateLimit`/`dbRateLimit` middleware call (`@lunora/ratelimit`) whose
1270
- * `key` selector — the per-caller rate-limit sub-key, `(ctx) => string |
1271
- * undefined` — is derived from the handler's `args` with no server-side
1272
- * scoping (no reference to the trusted `ctx` binding anywhere in the selector)
1273
- * — the `ratelimit_key_spoofable_or_global` lint input. A key an attacker
1274
- * controls lets them rotate it per request and bypass the limit entirely,
1275
- * defeating its purpose. A selector scoped by `ctx` (e.g. `ctx.auth.userId`,
1276
- * `ctx.ip`), or one with no `args` reference at all (a fixed/global bucket —
1277
- * the "no key" case this lint deliberately does not flag, to keep it low-FP),
1278
- * is not recorded. Structurally identical to `AdvisorRatelimitKeySelector`.
1279
- */
1269
+ * One `rateLimit`/`dbRateLimit` middleware call (`@lunora/ratelimit`) whose
1270
+ * `key` selector — the per-caller rate-limit sub-key, `(ctx) => string |
1271
+ * undefined` — is derived from the handler's `args` with no server-side
1272
+ * scoping (no reference to the trusted `ctx` binding anywhere in the selector)
1273
+ * — the `ratelimit_key_spoofable_or_global` lint input. A key an attacker
1274
+ * controls lets them rotate it per request and bypass the limit entirely,
1275
+ * defeating its purpose. A selector scoped by `ctx` (e.g. `ctx.auth.userId`,
1276
+ * `ctx.ip`), or one with no `args` reference at all (a fixed/global bucket —
1277
+ * the "no key" case this lint deliberately does not flag, to keep it low-FP),
1278
+ * is not recorded. Structurally identical to `AdvisorRatelimitKeySelector`.
1279
+ */
1280
1280
  interface RatelimitKeySelectorIR {
1281
1281
  /** The `rateLimit`/`dbRateLimit` callee invoked. */
1282
1282
  callee: string;
@@ -1290,16 +1290,16 @@ interface RatelimitKeySelectorIR {
1290
1290
  line: number;
1291
1291
  }
1292
1292
  /**
1293
- * One payload-derived privileged dispatch — a `ctx.run`/`context.run` back into a
1294
- * Lunora function from inside a `defineQueue` push handler or a `defineWorkflow`
1295
- * handler, whose args reference the handler's untrusted payload (`context.params`
1296
- * for a workflow, a `for (… of batch.messages)` body for a queue) — the
1297
- * `privileged_dispatch_unvalidated_payload` lint input. Both handler kinds run
1298
- * under the **system identity** (RLS disabled), so forwarding attacker-influenced
1299
- * payload into the dispatch bypasses the target's row policy. The resolved
1300
- * `targetFile`/`targetExport` let the lint join RLS-procedure evidence and fire
1301
- * only for RLS-gated targets. Structurally identical to `AdvisorPrivilegedDispatch`.
1302
- */
1293
+ * One payload-derived privileged dispatch — a `ctx.run`/`context.run` back into a
1294
+ * Lunora function from inside a `defineQueue` push handler or a `defineWorkflow`
1295
+ * handler, whose args reference the handler's untrusted payload (`context.params`
1296
+ * for a workflow, a `for (… of batch.messages)` body for a queue) — the
1297
+ * `privileged_dispatch_unvalidated_payload` lint input. Both handler kinds run
1298
+ * under the **system identity** (RLS disabled), so forwarding attacker-influenced
1299
+ * payload into the dispatch bypasses the target's row policy. The resolved
1300
+ * `targetFile`/`targetExport` let the lint join RLS-procedure evidence and fire
1301
+ * only for RLS-gated targets. Structurally identical to `AdvisorPrivilegedDispatch`.
1302
+ */
1303
1303
  interface PrivilegedDispatchIR {
1304
1304
  /** `"queue"` for a `defineQueue` handler, `"workflow"` for a `defineWorkflow` handler. */
1305
1305
  dispatchKind: "queue" | "workflow";
@@ -1315,11 +1315,11 @@ interface PrivilegedDispatchIR {
1315
1315
  targetFile: string;
1316
1316
  }
1317
1317
  /**
1318
- * One discovered `httpRoute.&lt;verb>("/admin/…")` route on an admin/privileged-looking
1319
- * path, with whether its builder chain references an auth/admin guard — the
1320
- * `admin_route_without_guard` lint input. Structurally identical to
1321
- * `AdvisorAdminRoute`.
1322
- */
1318
+ * One discovered `httpRoute.&lt;verb>("/admin/…")` route on an admin/privileged-looking
1319
+ * path, with whether its builder chain references an auth/admin guard — the
1320
+ * `admin_route_without_guard` lint input. Structurally identical to
1321
+ * `AdvisorAdminRoute`.
1322
+ */
1323
1323
  interface AdminRouteIR {
1324
1324
  /** Export binding name of the route handler. */
1325
1325
  exportName: string;
@@ -1333,17 +1333,17 @@ interface AdminRouteIR {
1333
1333
  usesGuard: boolean;
1334
1334
  }
1335
1335
  /**
1336
- * One tracked `ctx.storage.&lt;bucket>.&lt;method>(...)` upload/signing call — the
1337
- * shared input for the storage config-hygiene security lints
1338
- * (`storage_upload_without_content_type_allowlist`, `storage_upload_without_max_size`,
1339
- * `storage_generate_upload_url_no_content_type_pin`, `storage_presigned_url_for_private_content`).
1340
- * `upload`/`store` carry the `UploadOptions` guards (`allowedContentTypes` /
1341
- * `maxSize`); `generateUploadUrl` carries the signed-PUT `contentType` pin;
1342
- * `getPresignedUrl`/`getSignedUrl` carry a statically-known `expiresInSeconds`
1343
- * literal. `presentKeys` is empty (and `expiresInSeconds` unset) when the
1344
- * options argument was absent, a non-literal, or a spread — see `analyzable`.
1345
- * Structurally identical to `AdvisorStorageUpload`.
1346
- */
1336
+ * One tracked `ctx.storage.&lt;bucket>.&lt;method>(...)` upload/signing call — the
1337
+ * shared input for the storage config-hygiene security lints
1338
+ * (`storage_upload_without_content_type_allowlist`, `storage_upload_without_max_size`,
1339
+ * `storage_generate_upload_url_no_content_type_pin`, `storage_presigned_url_for_private_content`).
1340
+ * `upload`/`store` carry the `UploadOptions` guards (`allowedContentTypes` /
1341
+ * `maxSize`); `generateUploadUrl` carries the signed-PUT `contentType` pin;
1342
+ * `getPresignedUrl`/`getSignedUrl` carry a statically-known `expiresInSeconds`
1343
+ * literal. `presentKeys` is empty (and `expiresInSeconds` unset) when the
1344
+ * options argument was absent, a non-literal, or a spread — see `analyzable`.
1345
+ * Structurally identical to `AdvisorStorageUpload`.
1346
+ */
1347
1347
  interface StorageUploadIR {
1348
1348
  /** `true` when the call's options-object argument (or its deliberate absence) was statically resolvable. */
1349
1349
  analyzable: boolean;
@@ -1361,16 +1361,16 @@ interface StorageUploadIR {
1361
1361
  presentKeys: string[];
1362
1362
  }
1363
1363
  /**
1364
- * One discovered `httpAction`/`httpRoute` handler in `lunora/` that performs a
1365
- * side effect (`ctx.runMutation` / `ctx.runAction` / a `ctx.db.{insert,patch,
1366
- * replace,delete,insertManyUnsafe}` write) from the HTTP edge, with whether it
1367
- * reads `ctx.auth` — the `http_action_missing_auth_guard` lint input. A handler
1368
- * that mutates state or dispatches an action without ever consulting the request
1369
- * identity is an unauthenticated write bypassing identity/RLS. Only handlers with
1370
- * a statically-resolvable inline body and `ctx` binding are recorded (fail-safe
1371
- * under-report); read-only handlers are never recorded. Structurally identical to
1372
- * `AdvisorHttpActionGuard`.
1373
- */
1364
+ * One discovered `httpAction`/`httpRoute` handler in `lunora/` that performs a
1365
+ * side effect (`ctx.runMutation` / `ctx.runAction` / a `ctx.db.{insert,patch,
1366
+ * replace,delete,insertManyUnsafe}` write) from the HTTP edge, with whether it
1367
+ * reads `ctx.auth` — the `http_action_missing_auth_guard` lint input. A handler
1368
+ * that mutates state or dispatches an action without ever consulting the request
1369
+ * identity is an unauthenticated write bypassing identity/RLS. Only handlers with
1370
+ * a statically-resolvable inline body and `ctx` binding are recorded (fail-safe
1371
+ * under-report); read-only handlers are never recorded. Structurally identical to
1372
+ * `AdvisorHttpActionGuard`.
1373
+ */
1374
1374
  interface HttpActionGuardIR {
1375
1375
  /** Export binding name of the handler (or `"&lt;module>"` when mounted inline / not a named binding). */
1376
1376
  exportName: string;
@@ -1388,19 +1388,19 @@ interface HttpActionGuardIR {
1388
1388
  sideEffect: string;
1389
1389
  }
1390
1390
  /**
1391
- * One response-header write, inside an `httpAction` handler, whose value is derived
1392
- * from raw request input (`request.headers`, `request.url`/query, `await
1393
- * request.json()`) with no CR/LF sanitizer — the
1394
- * `http_action_response_header_injection` lint input. A `Request`-derived string
1395
- * placed verbatim into a response header lets a caller smuggle `\r\n` and inject
1396
- * extra headers or split the response (header injection / response splitting). Only
1397
- * sites whose value is request-tainted AND unguarded are recorded: a value routed
1398
- * through a CR/LF guard (`isSafeHeaderValue`), a URL/URI encoder
1399
- * (`encodeURIComponent`/`encodeURI`), a numeric coercion (`Number`/`parseInt`/
1400
- * `parseFloat`), or `btoa` is treated as safe and never recorded (`String(...)` /
1401
- * `.toString()` are NOT sanitizers — they don't strip CR/LF). Structurally
1402
- * identical to `AdvisorHttpHeaderWrite`.
1403
- */
1391
+ * One response-header write, inside an `httpAction` handler, whose value is derived
1392
+ * from raw request input (`request.headers`, `request.url`/query, `await
1393
+ * request.json()`) with no CR/LF sanitizer — the
1394
+ * `http_action_response_header_injection` lint input. A `Request`-derived string
1395
+ * placed verbatim into a response header lets a caller smuggle `\r\n` and inject
1396
+ * extra headers or split the response (header injection / response splitting). Only
1397
+ * sites whose value is request-tainted AND unguarded are recorded: a value routed
1398
+ * through a CR/LF guard (`isSafeHeaderValue`), a URL/URI encoder
1399
+ * (`encodeURIComponent`/`encodeURI`), a numeric coercion (`Number`/`parseInt`/
1400
+ * `parseFloat`), or `btoa` is treated as safe and never recorded (`String(...)` /
1401
+ * `.toString()` are NOT sanitizers — they don't strip CR/LF). Structurally
1402
+ * identical to `AdvisorHttpHeaderWrite`.
1403
+ */
1404
1404
  interface HttpHeaderWriteIR {
1405
1405
  /** Export binding name of the enclosing handler, or `"&lt;module>"` when mounted inline. */
1406
1406
  exportName: string;
@@ -1414,16 +1414,16 @@ interface HttpHeaderWriteIR {
1414
1414
  via: "headers-append" | "headers-ctor" | "headers-set" | "response-init";
1415
1415
  }
1416
1416
  /**
1417
- * One rate-limit / Turnstile middleware call in `lunora/` — the
1418
- * `ratelimit_middleware_fail_open` lint input. `rateLimit`/`dbRateLimit`
1419
- * (`@lunora/ratelimit`) and `verifyTurnstileMiddleware` (`@lunora/auth`) each
1420
- * accept a `failOpen` escape hatch that admits every request when the
1421
- * limiter/siteverify is unavailable; `failOpen` is `true` only when the options
1422
- * literal set it to the boolean literal `true` (anything else is fail-closed).
1423
- * The lint escalates a fail-open guard to a finding when the guarded procedure
1424
- * (`exportName`/`limitName`) looks auth/payment-sensitive. Structurally
1425
- * identical to `AdvisorFailOpenGuard`.
1426
- */
1417
+ * One rate-limit / Turnstile middleware call in `lunora/` — the
1418
+ * `ratelimit_middleware_fail_open` lint input. `rateLimit`/`dbRateLimit`
1419
+ * (`@lunora/ratelimit`) and `verifyTurnstileMiddleware` (`@lunora/auth`) each
1420
+ * accept a `failOpen` escape hatch that admits every request when the
1421
+ * limiter/siteverify is unavailable; `failOpen` is `true` only when the options
1422
+ * literal set it to the boolean literal `true` (anything else is fail-closed).
1423
+ * The lint escalates a fail-open guard to a finding when the guarded procedure
1424
+ * (`exportName`/`limitName`) looks auth/payment-sensitive. Structurally
1425
+ * identical to `AdvisorFailOpenGuard`.
1426
+ */
1427
1427
  interface FailOpenGuardIR {
1428
1428
  /** The middleware factory at the call site: `rateLimit` / `dbRateLimit` / `verifyTurnstileMiddleware`. */
1429
1429
  callee: string;
@@ -1439,14 +1439,14 @@ interface FailOpenGuardIR {
1439
1439
  line: number;
1440
1440
  }
1441
1441
  /**
1442
- * One `ctx.flags.boolean("key", &lt;boolean-literal>)` read in `lunora/` — the
1443
- * `flag_gates_security_with_unsafe_default` lint input. OpenFeature returns the
1444
- * `defaultValue` when the provider errors, so a fail-open default on a
1445
- * security-shaped key silently opens access during an outage. Only reads with a
1446
- * statically-known string key and boolean-literal default are recorded; the lint
1447
- * owns the security-shape + polarity judgment. Structurally identical to
1448
- * `AdvisorFlagSecurityDefault`.
1449
- */
1442
+ * One `ctx.flags.boolean("key", &lt;boolean-literal>)` read in `lunora/` — the
1443
+ * `flag_gates_security_with_unsafe_default` lint input. OpenFeature returns the
1444
+ * `defaultValue` when the provider errors, so a fail-open default on a
1445
+ * security-shaped key silently opens access during an outage. Only reads with a
1446
+ * statically-known string key and boolean-literal default are recorded; the lint
1447
+ * owns the security-shape + polarity judgment. Structurally identical to
1448
+ * `AdvisorFlagSecurityDefault`.
1449
+ */
1450
1450
  interface FlagSecurityDefaultIR {
1451
1451
  /** The boolean-literal default returned on a provider outage (fail-open value). */
1452
1452
  defaultValue: boolean;
@@ -1460,13 +1460,13 @@ interface FlagSecurityDefaultIR {
1460
1460
  line: number;
1461
1461
  }
1462
1462
  /**
1463
- * One `generateText` / `streamText` call in `lunora/` whose `tools` reach a
1464
- * privileged side effect (a DB write, function dispatch, or outbound
1465
- * fetch/mail/queue send). `userInputDerived` records whether the model input
1466
- * (`prompt`/`messages`/`system`) flows from the handler's `args`; the
1467
- * `ai_tool_side_effect_prompt_injection` lint fires only when it does.
1468
- * Structurally identical to `AdvisorAiToolSideEffect`.
1469
- */
1463
+ * One `generateText` / `streamText` call in `lunora/` whose `tools` reach a
1464
+ * privileged side effect (a DB write, function dispatch, or outbound
1465
+ * fetch/mail/queue send). `userInputDerived` records whether the model input
1466
+ * (`prompt`/`messages`/`system`) flows from the handler's `args`; the
1467
+ * `ai_tool_side_effect_prompt_injection` lint fires only when it does.
1468
+ * Structurally identical to `AdvisorAiToolSideEffect`.
1469
+ */
1470
1470
  interface AiToolSideEffectIR {
1471
1471
  /** Export binding name of the procedure performing the call. */
1472
1472
  exportName: string;
@@ -1482,13 +1482,13 @@ interface AiToolSideEffectIR {
1482
1482
  userInputDerived: boolean;
1483
1483
  }
1484
1484
  /**
1485
- * One `&lt;receiver>.identity.&lt;key>` claim read in `lunora/`, where `&lt;receiver>` is
1486
- * an RLS/mask policy `auth` (or `ctx.auth`/`context.auth`). `declared` records
1487
- * whether `&lt;key>` is in the app's `defineIdentity({ ... })` contract (or the
1488
- * always-present `userId`); the `identity_undeclared_claim_trusted` lint fires on
1489
- * the undeclared reads. Emitted only when a resolvable identity contract exists.
1490
- * Structurally identical to `AdvisorIdentityClaimRead`.
1491
- */
1485
+ * One `&lt;receiver>.identity.&lt;key>` claim read in `lunora/`, where `&lt;receiver>` is
1486
+ * an RLS/mask policy `auth` (or `ctx.auth`/`context.auth`). `declared` records
1487
+ * whether `&lt;key>` is in the app's `defineIdentity({ ... })` contract (or the
1488
+ * always-present `userId`); the `identity_undeclared_claim_trusted` lint fires on
1489
+ * the undeclared reads. Emitted only when a resolvable identity contract exists.
1490
+ * Structurally identical to `AdvisorIdentityClaimRead`.
1491
+ */
1492
1492
  interface IdentityClaimReadIR {
1493
1493
  /** `true` when `key` is a declared claim (in the `defineIdentity` contract, or the always-present `userId`). */
1494
1494
  declared: boolean;
@@ -1502,13 +1502,13 @@ interface IdentityClaimReadIR {
1502
1502
  line: number;
1503
1503
  }
1504
1504
  /**
1505
- * One payment webhook-adapter construction in `lunora/` (`createStripeAdapter` /
1506
- * `createPolarAdapter` / `createAutumnAdapter` / `createDodoPaymentsAdapter`).
1507
- * `toleranceSeconds` carries the statically-known `webhookToleranceSeconds`
1508
- * replay window when it is a plain numeric literal; the payment-webhook
1509
- * wide-tolerance lint fires when it exceeds a conservative ceiling. Structurally
1510
- * identical to `AdvisorPaymentWebhook`.
1511
- */
1505
+ * One payment webhook-adapter construction in `lunora/` (`createStripeAdapter` /
1506
+ * `createPolarAdapter` / `createAutumnAdapter` / `createDodoPaymentsAdapter`).
1507
+ * `toleranceSeconds` carries the statically-known `webhookToleranceSeconds`
1508
+ * replay window when it is a plain numeric literal; the payment-webhook
1509
+ * wide-tolerance lint fires when it exceeds a conservative ceiling. Structurally
1510
+ * identical to `AdvisorPaymentWebhook`.
1511
+ */
1512
1512
  interface PaymentWebhookIR {
1513
1513
  /** The adapter factory invoked. */
1514
1514
  callee: "createAutumnAdapter" | "createDodoPaymentsAdapter" | "createPolarAdapter" | "createStripeAdapter";
@@ -1522,13 +1522,13 @@ interface PaymentWebhookIR {
1522
1522
  toleranceSeconds?: number;
1523
1523
  }
1524
1524
  /**
1525
- * One `ctx.db.&lt;table>.findMany({ includeDeleted })` list read whose
1526
- * `includeDeleted` is either a hardcoded `true` or derived from the handler's
1527
- * `args` — the `soft_delete_include_deleted_from_args` lint input. The lint joins
1528
- * `table` against the schema's soft-delete tables and `visibility` against
1529
- * `.public()` before flagging. Structurally identical to `AdvisorSoftDeleteRead`
1530
- * so values pass straight through without conversion.
1531
- */
1525
+ * One `ctx.db.&lt;table>.findMany({ includeDeleted })` list read whose
1526
+ * `includeDeleted` is either a hardcoded `true` or derived from the handler's
1527
+ * `args` — the `soft_delete_include_deleted_from_args` lint input. The lint joins
1528
+ * `table` against the schema's soft-delete tables and `visibility` against
1529
+ * `.public()` before flagging. Structurally identical to `AdvisorSoftDeleteRead`
1530
+ * so values pass straight through without conversion.
1531
+ */
1532
1532
  interface SoftDeleteReadIR {
1533
1533
  /** Export binding name of the procedure performing the read. */
1534
1534
  exportName: string;
@@ -1546,14 +1546,14 @@ interface SoftDeleteReadIR {
1546
1546
  visibility: "internal" | "public";
1547
1547
  }
1548
1548
  /**
1549
- * One `ctx.db.&lt;table>.findMany({ with: { &lt;rel> } })` relation-hydrating list read
1550
- * — the `masked_relation_leak_via_with` lint input. Column masking does not
1551
- * descend into `with`-hydrated relations, so a masked table surfaced only through
1552
- * a `with` on an unprotected parent read is returned in the clear. The lint
1553
- * resolves each relation accessor to its target table and joins it against the
1554
- * discovered mask evidence before flagging. Structurally identical to
1555
- * `AdvisorRelationLoad` so values pass straight through without conversion.
1556
- */
1549
+ * One `ctx.db.&lt;table>.findMany({ with: { &lt;rel> } })` relation-hydrating list read
1550
+ * — the `masked_relation_leak_via_with` lint input. Column masking does not
1551
+ * descend into `with`-hydrated relations, so a masked table surfaced only through
1552
+ * a `with` on an unprotected parent read is returned in the clear. The lint
1553
+ * resolves each relation accessor to its target table and joins it against the
1554
+ * discovered mask evidence before flagging. Structurally identical to
1555
+ * `AdvisorRelationLoad` so values pass straight through without conversion.
1556
+ */
1557
1557
  interface RelationLoadIR {
1558
1558
  /** Export binding name of the procedure performing the read. */
1559
1559
  exportName: string;
@@ -1569,16 +1569,16 @@ interface RelationLoadIR {
1569
1569
  visibility: "internal" | "public";
1570
1570
  }
1571
1571
  /**
1572
- * One `query` handler whose `return` hands back the raw rows of a table — the
1573
- * result of a `ctx.db.&lt;table>.findMany()` / `.findFirst()` / `.get()` read, or a
1574
- * `ctx.db.query("&lt;table>")…collect()` fluent chain — returned directly (or through
1575
- * one local `const` hop) with no hand-built projection. The
1576
- * `output_projection_missing_on_public_read` lint keeps only `visibility ===
1577
- * "public"` rows with no `.output(...)` / `.use(mask(...))` on the chain, then
1578
- * joins `table` against the schema and flags one whose columns are PII-named.
1579
- * Structurally identical to `AdvisorRawRowReturn` so values pass straight through
1580
- * without conversion.
1581
- */
1572
+ * One `query` handler whose `return` hands back the raw rows of a table — the
1573
+ * result of a `ctx.db.&lt;table>.findMany()` / `.findFirst()` / `.get()` read, or a
1574
+ * `ctx.db.query("&lt;table>")…collect()` fluent chain — returned directly (or through
1575
+ * one local `const` hop) with no hand-built projection. The
1576
+ * `output_projection_missing_on_public_read` lint keeps only `visibility ===
1577
+ * "public"` rows with no `.output(...)` / `.use(mask(...))` on the chain, then
1578
+ * joins `table` against the schema and flags one whose columns are PII-named.
1579
+ * Structurally identical to `AdvisorRawRowReturn` so values pass straight through
1580
+ * without conversion.
1581
+ */
1582
1582
  interface RawRowReturnIR {
1583
1583
  /** Export binding name of the query returning the raw rows. */
1584
1584
  exportName: string;
@@ -1596,16 +1596,16 @@ interface RawRowReturnIR {
1596
1596
  visibility: "internal" | "public";
1597
1597
  }
1598
1598
  /**
1599
- * One `query`/`mutation` handler that gates a `ctx.db.get`/`patch`/`delete` on a
1600
- * null-checked `ctx.db.normalizeId(table, id)` result — the
1601
- * `normalize_id_used_as_authorization` lint input. `normalizeId` validates an id's
1602
- * structural shape only (it never reads the database), so a non-null result proves
1603
- * the id is well-formed, never that the caller owns the row; gating access on it is
1604
- * an IDOR. The lint owns the negative proof — it keeps only `visibility === "public"`
1605
- * rows with no `.use(rls(...))` and no ownership/identity mention (`mentionsOwnership`),
1606
- * then joins `table` against the schema's RLS mode before flagging. Structurally
1607
- * identical to `AdvisorNormalizeIdAuthorization` so values pass straight through.
1608
- */
1599
+ * One `query`/`mutation` handler that gates a `ctx.db.get`/`patch`/`delete` on a
1600
+ * null-checked `ctx.db.normalizeId(table, id)` result — the
1601
+ * `normalize_id_used_as_authorization` lint input. `normalizeId` validates an id's
1602
+ * structural shape only (it never reads the database), so a non-null result proves
1603
+ * the id is well-formed, never that the caller owns the row; gating access on it is
1604
+ * an IDOR. The lint owns the negative proof — it keeps only `visibility === "public"`
1605
+ * rows with no `.use(rls(...))` and no ownership/identity mention (`mentionsOwnership`),
1606
+ * then joins `table` against the schema's RLS mode before flagging. Structurally
1607
+ * identical to `AdvisorNormalizeIdAuthorization` so values pass straight through.
1608
+ */
1609
1609
  interface NormalizeIdAuthorizationIR {
1610
1610
  /** Export binding name of the procedure performing the normalize-then-access. */
1611
1611
  exportName: string;
@@ -1625,14 +1625,14 @@ interface NormalizeIdAuthorizationIR {
1625
1625
  visibility: "internal" | "public";
1626
1626
  }
1627
1627
  /**
1628
- * One committed `wrangler.jsonc` `vars` entry whose value is a plaintext secret —
1629
- * the `plaintext_secret_in_wrangler_vars` lint input. `vars` are baked into the
1630
- * deployed Worker in cleartext and checked into source control, so a real API key
1631
- * / token / private key there ships the secret to every reader of the repo and the
1632
- * bundle; it belongs in a Secrets Store binding or `wrangler secret put`. Produced
1633
- * by `@lunora/config` (which reads `wrangler.jsonc`), not a ts-morph feeder —
1634
- * codegen only passes it through. Structurally identical to `AdvisorWranglerVariable`.
1635
- */
1628
+ * One committed `wrangler.jsonc` `vars` entry whose value is a plaintext secret —
1629
+ * the `plaintext_secret_in_wrangler_vars` lint input. `vars` are baked into the
1630
+ * deployed Worker in cleartext and checked into source control, so a real API key
1631
+ * / token / private key there ships the secret to every reader of the repo and the
1632
+ * bundle; it belongs in a Secrets Store binding or `wrangler secret put`. Produced
1633
+ * by `@lunora/config` (which reads `wrangler.jsonc`), not a ts-morph feeder —
1634
+ * codegen only passes it through. Structurally identical to `AdvisorWranglerVariable`.
1635
+ */
1636
1636
  interface WranglerVariableIR {
1637
1637
  /** The `wrangler.jsonc` file the var was read from, relative to the project root. */
1638
1638
  file: string;
@@ -1652,30 +1652,30 @@ interface ProjectIR {
1652
1652
  schema: SchemaIR;
1653
1653
  }
1654
1654
  /**
1655
- * Run the static lints against a discovered {@link SchemaIR} and the reads/writes/calls
1656
- * found in function bodies: query reads feed `filter_without_index`, insert writes
1657
- * feed `table_without_insert`, authApi calls feed `auth_api_call_without_headers`,
1658
- * rls procedure snapshots feed `rls_uncovered_table`, mask procedure
1659
- * snapshots feed `mask_uncovered_pii_column`, and per-column mask strategies
1660
- * feed `mask_weak_hash_strategy_on_pii`; declared containers
1661
- * feed the `container_*` lints; declared workflows (with their durable step labels)
1662
- * + `ctx.workflows.get(...)` call sites feed the `workflow_unused` /
1663
- * `workflow_unknown_target` / duplicate-step-name lints; non-deterministic
1664
- * calls inside query/mutation handlers feed the `nondeterministic_query_mutation` lint
1665
- * (all default empty for callers that don't analyze functions/containers/workflows).
1666
- * The IR types are structurally identical to the advisor's evidence types so they
1667
- * pass straight through without conversion. Returns the findings; surfacing them
1668
- * (console, error overlay, studio Advisors table) is the caller's choice.
1669
- */
1670
- /**
1671
- * Named inputs for {@link lintSchema}. Every feeder is a discrete key rather than
1672
- * a positional argument: the feeder list grows every few releases and many IR
1673
- * types are structurally similar (`{file, exportName, line}`-shaped evidence),
1674
- * so a positional call was a silent-transposition hazard — swapping two adjacent
1675
- * arguments could typecheck yet feed the wrong evidence to the wrong lint and
1676
- * corrupt a security advisory. `schema` is the only required field; every other
1677
- * feeder defaults to "not analyzed" when omitted.
1678
- */
1655
+ * Run the static lints against a discovered {@link SchemaIR} and the reads/writes/calls
1656
+ * found in function bodies: query reads feed `filter_without_index`, insert writes
1657
+ * feed `table_without_insert`, authApi calls feed `auth_api_call_without_headers`,
1658
+ * rls procedure snapshots feed `rls_uncovered_table`, mask procedure
1659
+ * snapshots feed `mask_uncovered_pii_column`, and per-column mask strategies
1660
+ * feed `mask_weak_hash_strategy_on_pii`; declared containers
1661
+ * feed the `container_*` lints; declared workflows (with their durable step labels)
1662
+ * + `ctx.workflows.get(...)` call sites feed the `workflow_unused` /
1663
+ * `workflow_unknown_target` / duplicate-step-name lints; non-deterministic
1664
+ * calls inside query/mutation handlers feed the `nondeterministic_query_mutation` lint
1665
+ * (all default empty for callers that don't analyze functions/containers/workflows).
1666
+ * The IR types are structurally identical to the advisor's evidence types so they
1667
+ * pass straight through without conversion. Returns the findings; surfacing them
1668
+ * (console, error overlay, studio Advisors table) is the caller's choice.
1669
+ */
1670
+ /**
1671
+ * Named inputs for {@link lintSchema}. Every feeder is a discrete key rather than
1672
+ * a positional argument: the feeder list grows every few releases and many IR
1673
+ * types are structurally similar (`{file, exportName, line}`-shaped evidence),
1674
+ * so a positional call was a silent-transposition hazard — swapping two adjacent
1675
+ * arguments could typecheck yet feed the wrong evidence to the wrong lint and
1676
+ * corrupt a security advisory. `schema` is the only required field; every other
1677
+ * feeder defaults to "not analyzed" when omitted.
1678
+ */
1679
1679
  interface LintSchemaOptions {
1680
1680
  adminRoutes?: ReadonlyArray<AdminRouteIR>;
1681
1681
  aiRawRuns?: ReadonlyArray<AiRawRunIR>;
@@ -1728,19 +1728,19 @@ interface LintSchemaOptions {
1728
1728
  }
1729
1729
  declare const lintSchema: (options: LintSchemaOptions) => Finding[];
1730
1730
  /**
1731
- * Render advisor findings as a single multi-line string for console surfacing:
1732
- * a one-line summary header followed by one `[LEVEL] name: detail` line per
1733
- * finding. Returns `""` when there are no findings.
1734
- */
1731
+ * Render advisor findings as a single multi-line string for console surfacing:
1732
+ * a one-line summary header followed by one `[LEVEL] name: detail` line per
1733
+ * finding. Returns `""` when there are no findings.
1734
+ */
1735
1735
  declare const formatAdvisories: (findings: ReadonlyArray<Finding>) => string;
1736
1736
  /**
1737
- * An error thrown by codegen discovery when the user's schema or function
1738
- * source has a structural problem that can be pinpointed to a specific source
1739
- * location. A `LunoraError` subclass (`code: "CODEGEN_DIAGNOSTIC"`); the `file`,
1740
- * `line`, and `column` properties (also passed through as the base `loc`) mirror
1741
- * what Vite's error-overlay `loc` field expects so the browser can display the
1742
- * exact spot.
1743
- */
1737
+ * An error thrown by codegen discovery when the user's schema or function
1738
+ * source has a structural problem that can be pinpointed to a specific source
1739
+ * location. A `LunoraError` subclass (`code: "CODEGEN_DIAGNOSTIC"`); the `file`,
1740
+ * `line`, and `column` properties (also passed through as the base `loc`) mirror
1741
+ * what Vite's error-overlay `loc` field expects so the browser can display the
1742
+ * exact spot.
1743
+ */
1744
1744
  declare class CodegenDiagnosticError extends LunoraError {
1745
1745
  readonly column: number;
1746
1746
  readonly file: string;
@@ -1748,234 +1748,230 @@ declare class CodegenDiagnosticError extends LunoraError {
1748
1748
  constructor(message: string, file: string, line: number, column: number);
1749
1749
  }
1750
1750
  /**
1751
- * Build a {@link CodegenDiagnosticError} whose message includes the source
1752
- * location and whose `file`/`line`/`column` properties are set from the
1753
- * ts-morph `Node`'s position in its source file.
1754
- *
1755
- * Message format: `@lunora/codegen: &lt;detail> (&lt;file>:&lt;line>:&lt;column>)`
1756
- *
1757
- * `meta` is merged onto the returned error for callers that also carry the
1758
- * project-wide `LunoraError` envelope (`code`/`name`/`status`) — it never
1759
- * touches `file`/`line`/`column`, and the error stays an instance of
1760
- * {@link CodegenDiagnosticError} so the Vite overlay's `instanceof` location
1761
- * lookup is unaffected.
1762
- */
1751
+ * Build a {@link CodegenDiagnosticError} whose message includes the source
1752
+ * location and whose `file`/`line`/`column` properties are set from the
1753
+ * ts-morph `Node`'s position in its source file.
1754
+ *
1755
+ * Message format: `@lunora/codegen: &lt;detail> (&lt;file>:&lt;line>:&lt;column>)`
1756
+ *
1757
+ * `meta` is merged onto the returned error for callers that also carry the
1758
+ * project-wide `LunoraError` envelope (`code`/`name`/`status`) — it never
1759
+ * touches `file`/`line`/`column`, and the error stays an instance of
1760
+ * {@link CodegenDiagnosticError} so the Vite overlay's `instanceof` location
1761
+ * lookup is unaffected.
1762
+ */
1763
1763
  declare const diagnosticAt: (node: Node, detail: string, meta?: Record<string, unknown>) => CodegenDiagnosticError;
1764
1764
  /** The only file agents may be declared in — mirrors `lunora/workflows.ts`. */
1765
1765
  declare const AGENTS_FILENAME = "agents.ts";
1766
1766
  /**
1767
- * Discover every agent the project declares: exported `defineAgent()` calls in
1768
- * `lunora/agents.ts`. Returns `[]` when the file doesn't exist. Only four things
1769
- * are read statically — the optional `name` override (wrangler `workflows[].name`),
1770
- * the optional `publicRun` opt-in (the `agents:agentRun` capability gate), the
1771
- * presence of a `voice` block (which turns on the voice-session Durable Object),
1772
- * and the presence of an `onEmail` mapper (which wires the worker `email()`
1773
- * handler); the rest of the agent config (model / tools / memory / voice models /
1774
- * the `onEmail` closure body) is runtime-only, so codegen never evaluates it.
1775
- */
1767
+ * Discover every agent the project declares: exported `defineAgent()` calls in
1768
+ * `lunora/agents.ts`. Returns `[]` when the file doesn't exist. Only four things
1769
+ * are read statically — the optional `name` override (wrangler `workflows[].name`),
1770
+ * the optional `publicRun` opt-in (the `agents:agentRun` capability gate), the
1771
+ * presence of a `voice` block (which turns on the voice-session Durable Object),
1772
+ * and the presence of an `onEmail` mapper (which wires the worker `email()`
1773
+ * handler); the rest of the agent config (model / tools / memory / voice models /
1774
+ * the `onEmail` closure body) is runtime-only, so codegen never evaluates it.
1775
+ */
1776
1776
  declare const discoverAgents: (project: Project, lunoraDirectory: string) => AgentIR[];
1777
1777
  /**
1778
- * Discover `ctx.authApi.&lt;method>(...)` (and bare `authApi.&lt;method>(...)`) calls
1779
- * under the lunora source directory and attribute each to the exported function
1780
- * (and file) performing it. Calls outside an exported declaration are dropped.
1781
- */
1778
+ * Discover `ctx.authApi.&lt;method>(...)` (and bare `authApi.&lt;method>(...)`) calls
1779
+ * under the lunora source directory and attribute each to the exported function
1780
+ * (and file) performing it. Calls outside an exported declaration are dropped.
1781
+ */
1782
1782
  declare const discoverAuthApiCalls: (project: Project, lunoraDirectory: string) => AuthApiCallIR[];
1783
1783
  /** The only file containers may be declared in — mirrors `lunora/crons.ts`. */
1784
1784
  declare const CONTAINERS_FILENAME = "containers.ts";
1785
1785
  /**
1786
- * Discover every container the project declares: exported `defineContainer()`
1787
- * calls in `lunora/containers.ts`. Returns `[]` when the file doesn't exist.
1788
- * Wrangler-relevant fields (`image`, `instanceType`, `maxInstances`, `name`)
1789
- * must be static literals; runtime-only fields (`env`, `sleepAfter`, …) may be
1790
- * any expression since the generated class imports the definition object.
1791
- */
1786
+ * Discover every container the project declares: exported `defineContainer()`
1787
+ * calls in `lunora/containers.ts`. Returns `[]` when the file doesn't exist.
1788
+ * Wrangler-relevant fields (`image`, `instanceType`, `maxInstances`, `name`)
1789
+ * must be static literals; runtime-only fields (`env`, `sleepAfter`, …) may be
1790
+ * any expression since the generated class imports the definition object.
1791
+ */
1792
1792
  declare const discoverContainers: (project: Project, lunoraDirectory: string) => ContainerIR[];
1793
1793
  /**
1794
- * Scan every `.ts` file under `lunoraDir` for `cronJobs()` builder registrations
1795
- * (`crons.interval(...)`, `crons.daily(...)`, `crons.cron(...)`, …) and lift them
1796
- * into {@link CronJobIR}. Schedules are compiled to standard cron expressions;
1797
- * function references are resolved to their `namespace:fn` dispatch path, while a
1798
- * `workflows.NAME` / `agents.NAME` reference (or a bare identifier naming a
1799
- * declared workflow) resolves to a durable workflow start. Names must be unique
1800
- * across the project.
1801
- */
1794
+ * Scan every `.ts` file under `lunoraDir` for `cronJobs()` builder registrations
1795
+ * (`crons.interval(...)`, `crons.daily(...)`, `crons.cron(...)`, …) and lift them
1796
+ * into {@link CronJobIR}. Schedules are compiled to standard cron expressions;
1797
+ * function references are resolved to their `namespace:fn` dispatch path, while a
1798
+ * `workflows.NAME` / `agents.NAME` reference (or a bare identifier naming a
1799
+ * declared workflow) resolves to a durable workflow start. Names must be unique
1800
+ * across the project.
1801
+ */
1802
1802
  declare const discoverCrons: (project: Project, lunoraDirectory: string, workflows?: ReadonlyArray<WorkflowIR>, agents?: ReadonlyArray<AgentIR>) => CronJobIR[];
1803
1803
  /** The only file a feature-flag provider may be declared in — mirrors `lunora/queues.ts`. */
1804
1804
  declare const FLAGS_FILENAME = "flags.ts";
1805
- /** One statically-discovered flag read: the key plus the value type its `ctx.flags.&lt;type>` call implies. */
1806
-
1807
- /**
1808
- * Discover the feature-flag provider a project declares in `lunora/flags.ts`.
1809
- * Returns `undefined` when the file doesn't exist (the app has no flags). The
1810
- * read is metadata-only and lenient: codegen wires `ctx.flags` purely from the
1811
- * file's *existence* (`run-codegen.ts`) and imports the real module for the
1812
- * provider value this IR exists solely so the config layer can reconcile the
1813
- * wrangler `flagship` binding for the Flagship binding-mode provider. Anything
1814
- * it can't read statically degrades to a `custom` provider (no binding), never
1815
- * a thrown error.
1816
- */
1805
+ /**
1806
+ * Discover the feature-flag provider a project declares in `lunora/flags.ts`.
1807
+ * Returns `undefined` when the file doesn't exist (the app has no flags). The
1808
+ * read is metadata-only and lenient: codegen wires `ctx.flags` purely from the
1809
+ * file's *existence* (`run-codegen.ts`) and imports the real module for the
1810
+ * provider value this IR exists solely so the config layer can reconcile the
1811
+ * wrangler `flagship` binding for the Flagship binding-mode provider. Anything
1812
+ * it can't read statically degrades to a `custom` provider (no binding), never
1813
+ * a thrown error.
1814
+ */
1817
1815
  declare const discoverFlags: (project: Project, lunoraDirectory: string) => FlagsIR | undefined;
1818
1816
  /**
1819
- * Scan all .ts files under `lunoraDir` (skipping `_generated/` and `schema.ts`)
1820
- * for top-level `export const x = query/mutation/action({...})` registrations.
1821
- */
1817
+ * Scan all .ts files under `lunoraDir` (skipping `_generated/` and `schema.ts`)
1818
+ * for top-level `export const x = query/mutation/action({...})` registrations.
1819
+ */
1822
1820
  declare const discoverFunctions: (project: Project, lunoraDirectory: string) => FunctionIR[];
1823
1821
  /**
1824
- * Scan all `.ts` files under `lunoraDir` (skipping `_generated/` and `schema.ts`)
1825
- * for `export const x = httpRoute.&lt;verb>(...)…handler(...)` typed REST routes.
1826
- * These are the headline OpenAPI target: each becomes a real `paths` entry.
1827
- */
1822
+ * Scan all `.ts` files under `lunoraDir` (skipping `_generated/` and `schema.ts`)
1823
+ * for `export const x = httpRoute.&lt;verb>(...)…handler(...)` typed REST routes.
1824
+ * These are the headline OpenAPI target: each becomes a real `paths` entry.
1825
+ */
1828
1826
  declare const discoverHttpRoutes: (project: Project, lunoraDirectory: string) => HttpRouteIR[];
1829
1827
  /**
1830
- * Discover `ctx.db.insert("table", …)` writes under the lunora source directory
1831
- * and attribute each to the exported function (and file) performing it. Calls
1832
- * with a non-literal table argument, or outside an exported declaration, are
1833
- * dropped (`table === ""` / no enclosing export).
1834
- */
1828
+ * Discover `ctx.db.insert("table", …)` writes under the lunora source directory
1829
+ * and attribute each to the exported function (and file) performing it. Calls
1830
+ * with a non-literal table argument, or outside an exported declaration, are
1831
+ * dropped (`table === ""` / no enclosing export).
1832
+ */
1835
1833
  declare const discoverInserts: (project: Project, lunoraDirectory: string) => InsertWriteIR[];
1836
1834
  /**
1837
- * Discover masking usage for every exported Lunora procedure under the lunora
1838
- * source directory — the column-level twin of `discoverRlsProcedures`. For each
1839
- * procedure, records whether its builder chain includes `.use(mask(...))`, which
1840
- * `(table, column)` pairs that mask declares, and which tables it reads/writes
1841
- * through `ctx.db`. Feeds the `mask_uncovered_pii_column` advisor lint.
1842
- */
1835
+ * Discover masking usage for every exported Lunora procedure under the lunora
1836
+ * source directory — the column-level twin of `discoverRlsProcedures`. For each
1837
+ * procedure, records whether its builder chain includes `.use(mask(...))`, which
1838
+ * `(table, column)` pairs that mask declares, and which tables it reads/writes
1839
+ * through `ctx.db`. Feeds the `mask_uncovered_pii_column` advisor lint.
1840
+ */
1843
1841
  declare const discoverMaskProcedures: (project: Project, lunoraDirectory: string) => MaskProcedureIR[];
1844
1842
  /**
1845
- * Aggregate the schema-wide masking metadata the studio's data-browser mask
1846
- * toggle reads: every statically-discovered `(table, column, strategy)` masked
1847
- * column across the project's `.use(mask(...))` chains. Walks the same builder
1848
- * chains as {@link discoverMaskProcedures} but carries the strategy the preview
1849
- * needs to choose redact-vs-hash-vs-custom rendering. Deduped by `(table,
1850
- * column)` with the first declaration winning, so a column masked by several
1851
- * procedures lists once — the same evidence the advisor lint uses.
1852
- */
1853
- /**
1854
- * Scan all `.ts` files under `lunoraDir` for top-level
1855
- * `export const x = defineMigration({...})` declarations and lift them into
1856
- * {@link MigrationIR}. `id` must be a static string literal (it's the registry
1857
- * key); `table` is best-effort and left `""` when not a literal.
1858
- */
1843
+ * Scan all `.ts` files under `lunoraDir` for top-level
1844
+ * `export const x = defineMigration({...})` declarations and lift them into
1845
+ * {@link MigrationIR}. `id` must be a static string literal (it's the registry
1846
+ * key); `table` is best-effort and left `""` when not a literal.
1847
+ */
1859
1848
  declare const discoverMigrations: (project: Project, lunoraDirectory: string) => MigrationIR[];
1860
1849
  /** The only file custom mutators may be declared in — mirrors `lunora/queues.ts`. */
1861
1850
  declare const MUTATORS_FILENAME = "mutators.ts";
1862
1851
  /**
1863
- * Decide whether a call's callee is `defineMutator` — either the bare imported
1864
- * identifier (`defineMutator(...)`) or a namespace member access
1865
- * (`server.defineMutator(...)`). Both are valid ES module syntax, so discovery
1866
- * must see mutators declared either way.
1867
- */
1868
-
1869
- /**
1870
- * Discover every custom mutator the project declares: exported
1871
- * `defineMutator()` calls in `lunora/mutators.ts`. Returns `[]` when the file
1872
- * doesn't exist. Only the export binding is lifted — the runtime object carries
1873
- * the authoritative `server` impl + `handler`, so codegen never evaluates the
1874
- * body. The client `client` impl is split into the browser bundle separately.
1875
- */
1852
+ * Discover every custom mutator the project declares: exported
1853
+ * `defineMutator()` calls in `lunora/mutators.ts`. Returns `[]` when the file
1854
+ * doesn't exist. Only the export binding is lifted the runtime object carries
1855
+ * the authoritative `server` impl + `handler`, so codegen never evaluates the
1856
+ * body. The client `client` impl is split into the browser bundle separately.
1857
+ */
1876
1858
  declare const discoverMutators: (project: Project, lunoraDirectory: string) => MutatorIR[];
1877
1859
  /**
1878
- * Discover non-deterministic API calls (`Date.now`, `new Date()`, `Date()`,
1879
- * `Math.random`, `crypto.randomUUID`, `crypto.getRandomValues` — including
1880
- * `globalThis`/`self`/`window`-prefixed receivers — and `fetch`) lexically inside
1881
- * the handler body of every exported `query(...)` / `mutation(...)` registration
1882
- * under the lunora source directory — the `nondeterministic_query_mutation` lint
1883
- * input. `action(...)` (and `stream(...)`) registrations are intentionally
1884
- * skipped: actions run exactly once and may use ambient APIs freely.
1885
- *
1886
- * Traversal is scoped to the handler node (not the whole declaration), mirroring
1887
- * how the auth-api / insert feeders attribute calls — so a call in a sibling
1888
- * helper outside the handler, or in a nested `action(...)` passed elsewhere, is
1889
- * not attributed to the query/mutation. One {@link NondeterministicCallIR} is
1890
- * produced per call site.
1891
- */
1860
+ * Discover non-deterministic API calls (`Date.now`, `new Date()`, `Date()`,
1861
+ * `Math.random`, `crypto.randomUUID`, `crypto.getRandomValues` — including
1862
+ * `globalThis`/`self`/`window`-prefixed receivers — and `fetch`) lexically inside
1863
+ * the handler body of every exported `query(...)` / `mutation(...)` registration
1864
+ * under the lunora source directory — the `nondeterministic_query_mutation` lint
1865
+ * input. `action(...)` (and `stream(...)`) registrations are intentionally
1866
+ * skipped: actions run exactly once and may use ambient APIs freely.
1867
+ *
1868
+ * Traversal is scoped to the handler node (not the whole declaration), mirroring
1869
+ * how the auth-api / insert feeders attribute calls — so a call in a sibling
1870
+ * helper outside the handler, or in a nested `action(...)` passed elsewhere, is
1871
+ * not attributed to the query/mutation. One {@link NondeterministicCallIR} is
1872
+ * produced per call site.
1873
+ */
1892
1874
  declare const discoverNondeterministicCalls: (project: Project, lunoraDirectory: string) => NondeterministicCallIR[];
1893
1875
  /**
1894
- * Discover `ctx.db.query("table")…` reads under the lunora source directory and
1895
- * reduce each to a {@link QueryReadIR}. Only reads that call `.filter()` are
1896
- * returned — an unfiltered read is never a `filter_without_index` candidate, so
1897
- * dropping the rest keeps the lint input small.
1898
- */
1876
+ * Discover `ctx.db.query("table")…` reads under the lunora source directory and
1877
+ * reduce each to a {@link QueryReadIR}. Only reads that call `.filter()` are
1878
+ * returned — an unfiltered read is never a `filter_without_index` candidate, so
1879
+ * dropping the rest keeps the lint input small.
1880
+ */
1899
1881
  declare const discoverQueries: (project: Project, lunoraDirectory: string) => QueryReadIR[];
1900
1882
  /** The only file queues may be declared in — mirrors `lunora/workflows.ts`. */
1901
1883
  declare const QUEUES_FILENAME = "queues.ts";
1902
1884
  /**
1903
- * Discover every queue the project declares: exported `defineQueue()` calls in
1904
- * `lunora/queues.ts`. Returns `[]` when the file doesn't exist. Only the
1905
- * wrangler-relevant literals (`name`/`mode`/batch tuning) are read; the handler
1906
- * body is runtime-only, so codegen never evaluates it.
1907
- */
1885
+ * Discover every queue the project declares: exported `defineQueue()` calls in
1886
+ * `lunora/queues.ts`. Returns `[]` when the file doesn't exist. Only the
1887
+ * wrangler-relevant literals (`name`/`mode`/batch tuning) are read; the handler
1888
+ * body is runtime-only, so codegen never evaluates it.
1889
+ */
1908
1890
  declare const discoverQueues: (project: Project, lunoraDirectory: string) => QueueIR[];
1909
1891
  /**
1910
- * Discover `ctx.r2sql` accesses lexically inside the handler body of every
1911
- * exported `query(...)` / `mutation(...)` registration under the lunora source
1912
- * directory — the `r2sql_outside_action` lint input. `action(...)` (and
1913
- * `stream(...)`) registrations are intentionally skipped: R2 SQL is the
1914
- * external, non-reactive surface that belongs in actions.
1915
- *
1916
- * Traversal is scoped to the handler node (not the whole declaration), mirroring
1917
- * `discoverNondeterministicCalls` — so a `ctx.r2sql` touch in a sibling helper
1918
- * outside the handler is not attributed to the query/mutation. One
1919
- * {@link R2sqlCallIR} is produced per access site.
1920
- */
1892
+ * Discover `ctx.r2sql` accesses lexically inside the handler body of every
1893
+ * exported `query(...)` / `mutation(...)` registration under the lunora source
1894
+ * directory — the `r2sql_outside_action` lint input. `action(...)` (and
1895
+ * `stream(...)`) registrations are intentionally skipped: R2 SQL is the
1896
+ * external, non-reactive surface that belongs in actions.
1897
+ *
1898
+ * Traversal is scoped to the handler node (not the whole declaration), mirroring
1899
+ * `discoverNondeterministicCalls` — so a `ctx.r2sql` touch in a sibling helper
1900
+ * outside the handler is not attributed to the query/mutation. One
1901
+ * {@link R2sqlCallIR} is produced per access site.
1902
+ */
1921
1903
  declare const discoverR2sqlCalls: (project: Project, lunoraDirectory: string) => R2sqlCallIR[];
1922
1904
  declare const discoverRlsProcedures: (project: Project, lunoraDirectory: string) => RlsProcedureIR[];
1923
1905
  /**
1924
- * Aggregate the schema-wide RLS metadata the studio's read-only inspector reads:
1925
- * every statically-discovered `(table, on, procedure)` policy entry plus every
1926
- * role declared via `rls(policies, { roles })`. Walks the same builder chains as
1927
- * {@link discoverRlsProcedures} but extracts the richer `{ on }` operation +
1928
- * role/permission shape rather than the lint's table-name set.
1929
- *
1930
- * Only the **builder** form (`c.use(rls(...)).query(...)`) can declare policies,
1931
- * so bare-factory procedures contribute nothing. The `when` predicate is never
1932
- * read — it's an opaque JS closure whose logic belongs in code, not the UI.
1933
- * Roles are deduped by name (first declaration wins) so a role registered on
1934
- * several procedures lists once.
1935
- */
1906
+ * Aggregate the schema-wide RLS metadata the studio's read-only inspector reads:
1907
+ * every statically-discovered `(table, on, procedure)` policy entry plus every
1908
+ * role declared via `rls(policies, { roles })`. Walks the same builder chains as
1909
+ * {@link discoverRlsProcedures} but extracts the richer `{ on }` operation +
1910
+ * role/permission shape rather than the lint's table-name set.
1911
+ *
1912
+ * Only the **builder** form (`c.use(rls(...)).query(...)`) can declare policies,
1913
+ * so bare-factory procedures contribute nothing. The `when` predicate is never
1914
+ * read — it's an opaque JS closure whose logic belongs in code, not the UI.
1915
+ * Roles are deduped by name (first declaration wins) so a role registered on
1916
+ * several procedures lists once.
1917
+ */
1936
1918
  declare const discoverRlsMetadata: (project: Project, lunoraDirectory: string) => RlsMetadataIR;
1937
1919
  /**
1938
- * Load `&lt;projectRoot>/lunora/schema.ts`, find `defineSchema({...})`, and
1939
- * return a structural IR. Throws if the file or call cannot be found.
1940
- */
1920
+ * Which sandbox tools a project imports from `@lunora/agent` (main entry or the
1921
+ * `/sandbox` subpath), detected by NAMED value import. Drives two things:
1922
+ * registering the `sandbox:invoke` dispatcher (either tool) and provisioning the
1923
+ * `BROWSER` wrangler binding (`browserTool` — the browser op runs on
1924
+ * `ctx.browser` inside the dispatcher).
1925
+ */
1926
+ interface SandboxUsage {
1927
+ /** `import { browserTool } from "@lunora/agent"` (or `/sandbox`) appears in `lunora/`. */
1928
+ usesSandboxBrowser: boolean;
1929
+ /** `import { containerTool } from "@lunora/agent"` (or `/sandbox`) appears in `lunora/`. */
1930
+ usesSandboxContainer: boolean;
1931
+ }
1932
+ declare const discoverSandboxUsage: (project: Project, lunoraDirectory: string) => SandboxUsage;
1933
+ /**
1934
+ * Load `&lt;projectRoot>/lunora/schema.ts`, find `defineSchema({...})`, and
1935
+ * return a structural IR. Throws if the file or call cannot be found.
1936
+ */
1941
1937
  declare const discoverSchema: (project: Project, schemaPath: string, projectRoot?: string) => SchemaIR;
1942
1938
  /** The only file shapes may be declared in — mirrors `lunora/queues.ts`. */
1943
1939
  declare const SHAPES_FILENAME = "shapes.ts";
1944
1940
  /**
1945
- * Discover every replication shape the project declares: exported
1946
- * `defineShape()` calls in `lunora/shapes.ts`. Returns `[]` when the file
1947
- * doesn't exist. Only the export binding is lifted — the runtime object carries
1948
- * the authoritative `table`/`columns`/`compileWhere`, so codegen never
1949
- * evaluates the predicate.
1950
- */
1941
+ * Discover every replication shape the project declares: exported
1942
+ * `defineShape()` calls in `lunora/shapes.ts`. Returns `[]` when the file
1943
+ * doesn't exist. Only the export binding is lifted — the runtime object carries
1944
+ * the authoritative `table`/`columns`/`compileWhere`, so codegen never
1945
+ * evaluates the predicate.
1946
+ */
1951
1947
  declare const discoverShapes: (project: Project, lunoraDirectory: string) => ShapeIR[];
1952
1948
  /**
1953
- * Aggregate the schema-wide storage-rule metadata the studio's inspector reads:
1954
- * every statically-discovered `(bucket, on, prefix, procedure)` entry across all
1955
- * `.use(storageRules(...))` chains. Only the builder form can declare rules, so
1956
- * bare-factory procedures contribute nothing.
1957
- */
1949
+ * Aggregate the schema-wide storage-rule metadata the studio's inspector reads:
1950
+ * every statically-discovered `(bucket, on, prefix, procedure)` entry across all
1951
+ * `.use(storageRules(...))` chains. Only the builder form can declare rules, so
1952
+ * bare-factory procedures contribute nothing.
1953
+ */
1958
1954
  declare const discoverStorageRulesMetadata: (project: Project, lunoraDirectory: string) => StorageRulesMetadataIR;
1959
1955
  /** The only file workflows may be declared in — mirrors `lunora/containers.ts`. */
1960
1956
  declare const WORKFLOWS_FILENAME = "workflows.ts";
1961
1957
  /**
1962
- * Discover every workflow the project declares: exported `defineWorkflow()`
1963
- * calls in `lunora/workflows.ts`. Returns `[]` when the file doesn't exist. The
1964
- * only wrangler-relevant literal is the optional `name` override; the workflow
1965
- * body is runtime-only, so codegen never evaluates it.
1966
- */
1958
+ * Discover every workflow the project declares: exported `defineWorkflow()`
1959
+ * calls in `lunora/workflows.ts`. Returns `[]` when the file doesn't exist. The
1960
+ * only wrangler-relevant literal is the optional `name` override; the workflow
1961
+ * body is runtime-only, so codegen never evaluates it.
1962
+ */
1967
1963
  declare const discoverWorkflows: (project: Project, lunoraDirectory: string) => WorkflowIR[];
1968
1964
  declare const GENERATED_HEADER = "// GENERATED by @lunora/codegen — do not edit.\n// Run `lunora codegen` to regenerate.\n\n";
1969
1965
  /** Emit `_generated/dataModel.ts` — `Doc&lt;"name">` + `Id&lt;"name">` for every table. */
1970
1966
  declare const emitDataModel: (schema: SchemaIR, useUmbrella?: boolean) => string;
1971
1967
  /**
1972
- * Emit `_generated/api.ts` — the typed `api.*` registry (public functions), the
1973
- * `internal.*` registry, and (when the project declares them) the typed
1974
- * `workflows.*` / `agents.*` scheduler-target reference objects. `api`/`internal` are the same `anyApi` proxy
1975
- * at runtime (the `__lunoraRef` is identical); visibility is enforced
1976
- * server-side at dispatch, not in the reference. Splitting the *types* keeps
1977
- * internal functions off the client-facing `api` surface.
1978
- */
1968
+ * Emit `_generated/api.ts` — the typed `api.*` registry (public functions), the
1969
+ * `internal.*` registry, and (when the project declares them) the typed
1970
+ * `workflows.*` / `agents.*` scheduler-target reference objects. `api`/`internal` are the same `anyApi` proxy
1971
+ * at runtime (the `__lunoraRef` is identical); visibility is enforced
1972
+ * server-side at dispatch, not in the reference. Splitting the *types* keeps
1973
+ * internal functions off the client-facing `api` surface.
1974
+ */
1979
1975
  interface EmitApiOptions {
1980
1976
  agents?: ReadonlyArray<AgentIR>;
1981
1977
  functions: ReadonlyArray<FunctionIR>;
@@ -1986,47 +1982,37 @@ interface EmitApiOptions {
1986
1982
  }
1987
1983
  declare const emitApi: (options: EmitApiOptions) => string;
1988
1984
  /**
1989
- * Emit `_generated/seed.ts` — a project-bound `createSeedClient` with this
1990
- * schema's `InsertModel` and runtime schema pre-applied, so a test or script
1991
- * calls `createSeedClient({ seed: 1 }).users(5)` with full column types and no
1992
- * manual wiring. The runtime schema is the default export of `lunora/schema.ts`
1993
- * (the same import the generated ShardDO uses).
1994
- *
1995
- * Returns `""` when `@lunora/seed` is not a declared dependency, so projects
1996
- * that don't use it keep a clean `_generated/` and never import the package.
1997
- */
1998
- /**
1999
- * Emit `_generated/collections.ts` one TanStack DB collection factory per
2000
- * `defineShape` in `lunora/shapes.ts` (the local-first partial-replication
2001
- * surface). Each factory builds a live collection that syncs its shape's rowset
2002
- * through the client's poke protocol (`subscribeShape`), so an app feeds the
2003
- * result straight to `useLiveQuery`.
2004
- *
2005
- * Returns `""` (so `writeIfPresent` skips the file) unless the project both
2006
- * declares shapes AND installs `@lunora/db` — the add-on that ships
2007
- * `lunoraCollectionOptions`. `@lunora/db` stays a scoped install even under the
2008
- * `lunorash` umbrella (an opt-in add-on, like `@lunora/auth`), so its import is
2009
- * always `@lunora/db/collections`; only the in-umbrella `@lunora/client` import
2010
- * is remapped to `lunorash/client`.
2011
- */
1985
+ * Emit `_generated/collections.ts` — one TanStack DB collection factory per
1986
+ * `defineShape` in `lunora/shapes.ts` (the local-first partial-replication
1987
+ * surface). Each factory builds a live collection that syncs its shape's rowset
1988
+ * through the client's poke protocol (`subscribeShape`), so an app feeds the
1989
+ * result straight to `useLiveQuery`.
1990
+ *
1991
+ * Returns `""` (so `writeIfPresent` skips the file) unless the project both
1992
+ * declares shapes AND installs `@lunora/db` the add-on that ships
1993
+ * `lunoraCollectionOptions`. `@lunora/db` stays a scoped install even under the
1994
+ * `lunorash` umbrella (an opt-in add-on, like `@lunora/auth`), so its import is
1995
+ * always `@lunora/db/collections`; only the in-umbrella `@lunora/client` import
1996
+ * is remapped to `lunorash/client`.
1997
+ */
2012
1998
  declare const emitCollections: (shapes: ReadonlyArray<ShapeIR>, hasDatabase: boolean, useUmbrella?: boolean) => string;
2013
1999
  interface EmitServerOptions {
2014
2000
  /** Agents declared via `defineAgent` exports — wires the typed `ctx.agents` producers onto Mutation/Action contexts. */
2015
2001
  agents?: ReadonlyArray<AgentIR>;
2016
2002
  containers?: ReadonlyArray<ContainerIR>;
2017
2003
  /**
2018
- * The single `defineEnv(...)` contract declared in `lunora/env.ts`. When
2019
- * present, `ctx.env` is typed as the validated `InferEnv` shape (recovered
2020
- * via `ReturnType` over the accessor's `typeof`). `undefined` leaves `ctx.env`
2021
- * the base optional binding record — byte-identical to today.
2022
- */
2004
+ * The single `defineEnv(...)` contract declared in `lunora/env.ts`. When
2005
+ * present, `ctx.env` is typed as the validated `InferEnv` shape (recovered
2006
+ * via `ReturnType` over the accessor's `typeof`). `undefined` leaves `ctx.env`
2007
+ * the base optional binding record — byte-identical to today.
2008
+ */
2023
2009
  env?: EnvIR;
2024
2010
  /**
2025
- * A `lunora/` source reads `ctx.access` — wires the verified Cloudflare Access
2026
- * facade (`@lunora/cloudflare-access/context`) onto every ctx. Distinct from
2027
- * `emitApp`'s `hasAccess` (which gates the worker's `.access()` resolveIdentity
2028
- * method); this one gates the per-request `ctx.access` read surface.
2029
- */
2011
+ * A `lunora/` source reads `ctx.access` — wires the verified Cloudflare Access
2012
+ * facade (`@lunora/cloudflare-access/context`) onto every ctx. Distinct from
2013
+ * `emitApp`'s `hasAccess` (which gates the worker's `.access()` resolveIdentity
2014
+ * method); this one gates the per-request `ctx.access` read surface.
2015
+ */
2030
2016
  hasAccessFacade?: boolean;
2031
2017
  hasAi?: boolean;
2032
2018
  /** A `lunora/` source uses `@lunora/bindings/analytics` / `ctx.analytics` — wires the write helper onto every ctx. */
@@ -2049,13 +2035,13 @@ interface EmitServerOptions {
2049
2035
  /** A `lunora/` source uses `@lunora/x402/pay` / `ctx.x402` — wires the agent-wallet pay rail onto ActionCtx only. */
2050
2036
  hasX402?: boolean;
2051
2037
  /**
2052
- * The single `defineIdentity(...)` claim contract declared in
2053
- * `lunora/identity.ts` (Plan 080). When present, `ctx.auth.getIdentity()`,
2054
- * the RLS policy `ctx.auth.identity`, and the shard-authorization hooks
2055
- * narrow to the declared shape (recovered via `InferIdentity` over the
2056
- * contract's `typeof`). `undefined` keeps the identity an untyped bag —
2057
- * byte-identical to today.
2058
- */
2038
+ * The single `defineIdentity(...)` claim contract declared in
2039
+ * `lunora/identity.ts` (Plan 080). When present, `ctx.auth.getIdentity()`,
2040
+ * the RLS policy `ctx.auth.identity`, and the shard-authorization hooks
2041
+ * narrow to the declared shape (recovered via `InferIdentity` over the
2042
+ * contract's `typeof`). `undefined` keeps the identity an untyped bag —
2043
+ * byte-identical to today.
2044
+ */
2059
2045
  identity?: IdentityIR;
2060
2046
  /** Queues declared via `defineQueue` exports — wires the typed `ctx.queues` producers onto Mutation/Action contexts. */
2061
2047
  queues?: ReadonlyArray<QueueIR>;
@@ -2065,29 +2051,7 @@ interface EmitServerOptions {
2065
2051
  useUmbrella?: boolean;
2066
2052
  workflows?: ReadonlyArray<WorkflowIR>;
2067
2053
  }
2068
- declare const emitServer: ({
2069
- agents,
2070
- containers,
2071
- env,
2072
- hasAccessFacade,
2073
- hasAi,
2074
- hasAnalytics,
2075
- hasBrowser,
2076
- hasFlags,
2077
- hasHyperdrive,
2078
- hasImages,
2079
- hasKv,
2080
- hasPayments,
2081
- hasPipelines,
2082
- hasR2sql,
2083
- hasX402,
2084
- identity,
2085
- queues,
2086
- schema,
2087
- storageRuleBuckets,
2088
- useUmbrella,
2089
- workflows
2090
- }?: EmitServerOptions) => string;
2054
+ declare const emitServer: ({ agents, containers, env, hasAccessFacade, hasAi, hasAnalytics, hasBrowser, hasFlags, hasHyperdrive, hasImages, hasKv, hasPayments, hasPipelines, hasR2sql, hasX402, identity, queues, schema, storageRuleBuckets, useUmbrella, workflows }?: EmitServerOptions) => string;
2091
2055
  interface EmitFunctionsOptions {
2092
2056
  agents?: ReadonlyArray<AgentIR>;
2093
2057
  functions: ReadonlyArray<FunctionIR>;
@@ -2100,50 +2064,33 @@ interface EmitFunctionsOptions {
2100
2064
  }
2101
2065
  declare const emitFunctions: (options: EmitFunctionsOptions) => string;
2102
2066
  /**
2103
- * Storage-column map per table for the file browser: `{ table: [field, …] }` for
2104
- * every field declared `v.storage(...)` (unwrapping `v.optional(...)`). The
2105
- * generated shard hands this to the base `storageColumns` hook so the admin
2106
- * `storageReferences` read can join R2 objects back to the rows that own them
2107
- * (and flag orphans objects no row references). Only scalar storage columns
2108
- * are emitted; an array-of-storage field can't be matched by an equality scan,
2109
- * so it is skipped here.
2110
- */
2111
- /**
2112
- * Emit `_generated/containers.ts` — one container-enabled Durable Object class
2113
- * per `defineContainer` export, each a thin subclass of `LunoraContainer`
2114
- * (`@lunora/container/do`) constructed with the user's definition object. The
2115
- * worker entry must re-export these classes: wrangler requires every
2116
- * `containers[].class_name` to be exported by the deployed worker. Returns ""
2117
- * when the project declares no containers (the file is not written then).
2118
- */
2067
+ * Emit `_generated/containers.ts` one container-enabled Durable Object class
2068
+ * per `defineContainer` export, each a thin subclass of `LunoraContainer`
2069
+ * (`@lunora/container/do`) constructed with the user's definition object. The
2070
+ * worker entry must re-export these classes: wrangler requires every
2071
+ * `containers[].class_name` to be exported by the deployed worker. Returns ""
2072
+ * when the project declares no containers (the file is not written then).
2073
+ */
2119
2074
  declare const emitContainers: (containers: ReadonlyArray<ContainerIR>, jurisdiction?: JurisdictionIR) => string;
2120
2075
  /**
2121
- * Emit `_generated/workflows.ts` — one `WorkflowEntrypoint` class per
2122
- * `defineWorkflow` export, each a thin subclass of `LunoraWorkflow`
2123
- * (`@lunora/workflow/do`) constructed with the user's definition object. The
2124
- * worker entry must re-export these classes: wrangler requires every
2125
- * `workflows[].class_name` to be exported by the deployed worker. Returns ""
2126
- * when the project declares no workflows (the file is not written then).
2127
- */
2076
+ * Emit `_generated/workflows.ts` — one `WorkflowEntrypoint` class per
2077
+ * `defineWorkflow` export, each a thin subclass of `LunoraWorkflow`
2078
+ * (`@lunora/workflow/do`) constructed with the user's definition object. The
2079
+ * worker entry must re-export these classes: wrangler requires every
2080
+ * `workflows[].class_name` to be exported by the deployed worker. Returns ""
2081
+ * when the project declares no workflows (the file is not written then).
2082
+ */
2128
2083
  declare const emitWorkflows: (workflows: ReadonlyArray<WorkflowIR>) => string;
2129
2084
  /**
2130
- * Emit `_generated/agents.ts` — one `WorkflowEntrypoint` class per `defineAgent`
2131
- * export, each a thin subclass of `LunoraWorkflow` (`@lunora/workflow/do`)
2132
- * constructed with the compiled agent tool-loop (`compileAgentWorkflow`). Like
2133
- * `_generated/workflows.ts`, the worker entry must re-export these classes:
2134
- * wrangler requires every `workflows[].class_name` to be exported by the
2135
- * deployed worker. Returns "" when the project declares no agents (the file is
2136
- * not written then).
2137
- */
2085
+ * Emit `_generated/agents.ts` — one `WorkflowEntrypoint` class per `defineAgent`
2086
+ * export, each a thin subclass of `LunoraWorkflow` (`@lunora/workflow/do`)
2087
+ * constructed with the compiled agent tool-loop (`compileAgentWorkflow`). Like
2088
+ * `_generated/workflows.ts`, the worker entry must re-export these classes:
2089
+ * wrangler requires every `workflows[].class_name` to be exported by the
2090
+ * deployed worker. Returns "" when the project declares no agents (the file is
2091
+ * not written then).
2092
+ */
2138
2093
  declare const emitAgents: (agents: ReadonlyArray<AgentIR>) => string;
2139
- /**
2140
- * Emit `_generated/queues.ts` — the push-consumer registry the worker `queue()`
2141
- * handler dispatches through. Maps each push queue's stable wrangler name (which
2142
- * `batch.queue` carries) to its `defineQueue` definition + export name. Pull
2143
- * queues are consumed by an external worker, so they carry no handler and are
2144
- * omitted here. Returns "" (and the file is not written) when no push queues are
2145
- * declared — a pull-only or queue-free app keeps a clean `_generated/`.
2146
- */
2147
2094
  interface EmitShardOptions {
2148
2095
  advisories?: ReadonlyArray<Finding>;
2149
2096
  /** Agents declared via `defineAgent` exports in `lunora/agents.ts` — wires the typed `ctx.agents` producers. */
@@ -2193,100 +2140,72 @@ interface EmitShardOptions {
2193
2140
  useUmbrella?: boolean;
2194
2141
  workflows?: ReadonlyArray<WorkflowIR>;
2195
2142
  }
2196
- declare const emitShard: ({
2197
- advisories,
2198
- agents,
2199
- containers,
2200
- env,
2201
- flagKeys,
2202
- hasAccessFacade,
2203
- hasAi,
2204
- hasAnalytics,
2205
- hasBrowser,
2206
- hasFlags,
2207
- hasHyperdrive,
2208
- hasImages,
2209
- hasKv,
2210
- hasPayments,
2211
- hasPipelines,
2212
- hasR2sql,
2213
- hasX402,
2214
- maskMetadata,
2215
- mutators,
2216
- queues,
2217
- rlsMetadata,
2218
- schema,
2219
- shapes,
2220
- storageRules,
2221
- studioFeatures,
2222
- useUmbrella,
2223
- workflows
2224
- }: EmitShardOptions) => string;
2225
- /**
2226
- * Emit drizzle `sqliteTable` definitions for the project schema, split into
2227
- * `global` (D1-backed) and `shard` (DO-SQLite-backed) buckets. Tables marked
2228
- * `.global()` go in the global file; everything else (default root + `.shardBy()`)
2229
- * goes in the shard file.
2230
- *
2231
- * `searchIndexes` are intentionally not emitted — drizzle has no `sqliteTable`
2232
- * abstraction for FTS5 virtual tables. FTS plumbing is handled by the runtime
2233
- * outside of drizzle.
2234
- */
2143
+ declare const emitShard: ({ advisories, agents, containers, env, flagKeys, hasAccessFacade, hasAi, hasAnalytics, hasBrowser, hasFlags, hasHyperdrive, hasImages, hasKv, hasPayments, hasPipelines, hasR2sql, hasX402, maskMetadata, mutators, queues, rlsMetadata, schema, shapes, storageRules, studioFeatures, useUmbrella, workflows }: EmitShardOptions) => string;
2144
+ /**
2145
+ * Emit drizzle `sqliteTable` definitions for the project schema, split into
2146
+ * `global` (D1-backed) and `shard` (DO-SQLite-backed) buckets. Tables marked
2147
+ * `.global()` go in the global file; everything else (default root + `.shardBy()`)
2148
+ * goes in the shard file.
2149
+ *
2150
+ * `searchIndexes` are intentionally not emitted — drizzle has no `sqliteTable`
2151
+ * abstraction for FTS5 virtual tables. FTS plumbing is handled by the runtime
2152
+ * outside of drizzle.
2153
+ */
2235
2154
  declare const emitDrizzleSchema: (schema: SchemaIR, useUmbrella?: boolean) => {
2236
2155
  global: string;
2237
2156
  shard: string;
2238
2157
  };
2239
2158
  /**
2240
- * Emit `_generated/crons.ts` from the discovered cron jobs.
2241
- *
2242
- * `LUNORA_CRON_TRIGGERS` is the deduplicated schedule array — what lands in
2243
- * wrangler's `triggers.crons` (the vite plugin reconciles it into
2244
- * `wrangler.jsonc`, and {@link emitWranglerCronTriggers} renders the same list
2245
- * for the CLI / docs).
2246
- *
2247
- * `LUNORA_CRONS` is the dispatcher map keyed by cron expression, each value a
2248
- * list of `{ name, functionPath, args }`. Cloudflare's `scheduled()` handler
2249
- * receives only the cron string, so multiple jobs sharing one expression must
2250
- * all fire — hence a list per key rather than a single entry.
2251
- *
2252
- * Jobs arrive pre-sorted by name (deterministic output); the trigger array
2253
- * preserves first-seen order of distinct expressions.
2254
- *
2255
- * Cloudflare caps a Worker at **3 Cron Triggers** (i.e. 3 distinct cron
2256
- * expressions). Because the dispatcher fires every job sharing an expression,
2257
- * many jobs can ride a single trigger — only the count of *distinct* schedules
2258
- * matters. `lunora codegen` warns when that count exceeds the limit; for
2259
- * finer-grained scheduling use Durable Object alarms (`@lunora/scheduler`),
2260
- * which have no such cap.
2261
- */
2159
+ * Emit `_generated/crons.ts` from the discovered cron jobs.
2160
+ *
2161
+ * `LUNORA_CRON_TRIGGERS` is the deduplicated schedule array — what lands in
2162
+ * wrangler's `triggers.crons` (the vite plugin reconciles it into
2163
+ * `wrangler.jsonc`, and {@link emitWranglerCronTriggers} renders the same list
2164
+ * for the CLI / docs).
2165
+ *
2166
+ * `LUNORA_CRONS` is the dispatcher map keyed by cron expression, each value a
2167
+ * list of `{ name, functionPath, args }`. Cloudflare's `scheduled()` handler
2168
+ * receives only the cron string, so multiple jobs sharing one expression must
2169
+ * all fire — hence a list per key rather than a single entry.
2170
+ *
2171
+ * Jobs arrive pre-sorted by name (deterministic output); the trigger array
2172
+ * preserves first-seen order of distinct expressions.
2173
+ *
2174
+ * Cloudflare caps a Worker at **3 Cron Triggers** (i.e. 3 distinct cron
2175
+ * expressions). Because the dispatcher fires every job sharing an expression,
2176
+ * many jobs can ride a single trigger — only the count of *distinct* schedules
2177
+ * matters. `lunora codegen` warns when that count exceeds the limit; for
2178
+ * finer-grained scheduling use Durable Object alarms (`@lunora/scheduler`),
2179
+ * which have no such cap.
2180
+ */
2262
2181
  declare const emitCrons: (crons: ReadonlyArray<CronJobIR>) => string;
2263
2182
  /**
2264
- * Render `_generated/vectors.ts` — the static registry of every vector index
2265
- * declared in `schema.ts` (inline `.vectorize()` columns + standalone
2266
- * `defineVectorIndex()` definitions).
2267
- *
2268
- * Cloudflare Vectorize exposes no way to enumerate an account's indexes at
2269
- * runtime — a binding can `describe()` itself but the worker can't ask "which
2270
- * indexes exist". So this generated array is the source of truth the studio's
2271
- * vector browser lists, and the worker's admin route pairs each entry with its
2272
- * live `describe()` stats. Entries are sorted by name for deterministic output.
2273
- */
2183
+ * Render `_generated/vectors.ts` — the static registry of every vector index
2184
+ * declared in `schema.ts` (inline `.vectorize()` columns + standalone
2185
+ * `defineVectorIndex()` definitions).
2186
+ *
2187
+ * Cloudflare Vectorize exposes no way to enumerate an account's indexes at
2188
+ * runtime — a binding can `describe()` itself but the worker can't ask "which
2189
+ * indexes exist". So this generated array is the source of truth the studio's
2190
+ * vector browser lists, and the worker's admin route pairs each entry with its
2191
+ * live `describe()` stats. Entries are sorted by name for deterministic output.
2192
+ */
2274
2193
  declare const emitVectors: (vectorIndexes: ReadonlyArray<VectorIndexIR>) => string;
2275
2194
  /**
2276
- * Render the deduplicated cron schedules as a JSON fragment suitable for
2277
- * splicing into `wrangler.jsonc`'s `triggers.crons`. The vite plugin uses this
2278
- * (plus the parsed wrangler config) to reconcile generated triggers without the
2279
- * user hand-editing the file.
2280
- */
2195
+ * Render the deduplicated cron schedules as a JSON fragment suitable for
2196
+ * splicing into `wrangler.jsonc`'s `triggers.crons`. The vite plugin uses this
2197
+ * (plus the parsed wrangler config) to reconcile generated triggers without the
2198
+ * user hand-editing the file.
2199
+ */
2281
2200
  declare const emitWranglerCronTriggers: (crons: ReadonlyArray<CronJobIR>) => string[];
2282
2201
  /** Which capability methods the generated `defineApp` builder exposes — one flag per package-backed feature the app actually uses. */
2283
2202
  interface EmitAppOptions {
2284
2203
  /**
2285
- * Inbound-email agents (`defineAgent({ onEmail })`) → wire the worker's
2286
- * top-level `email()` handler to `dispatchAgentEmail(...)` (from
2287
- * `@lunora/agent/inbound`), so received mail starts a durable run. Empty/absent
2288
- * ⇒ no wiring, byte-identical output for email-free (and agent-free) projects.
2289
- */
2204
+ * Inbound-email agents (`defineAgent({ onEmail })`) → wire the worker's
2205
+ * top-level `email()` handler to `dispatchAgentEmail(...)` (from
2206
+ * `@lunora/agent/inbound`), so received mail starts a durable run. Empty/absent
2207
+ * ⇒ no wiring, byte-identical output for email-free (and agent-free) projects.
2208
+ */
2290
2209
  emailAgents?: ReadonlyArray<{
2291
2210
  bindingName: string;
2292
2211
  exportName: string;
@@ -2336,12 +2255,12 @@ interface EmitAppOptions {
2336
2255
  /** Project depends on the unscoped `lunorash` umbrella → import the runtime via `lunorash/runtime` instead of `@lunora/runtime`. */
2337
2256
  useUmbrella: boolean;
2338
2257
  /**
2339
- * Voice-enabled agents (`defineAgent({ voice: … })`) → wire
2340
- * `options.voiceAgents`, mapping each agent's export name to its `VOICE_*`
2341
- * Durable Object namespace binding so the runtime exposes
2342
- * `/_lunora/voice/&lt;exportName>`. Empty/absent ⇒ no wiring, byte-identical
2343
- * output for voice-free (and agent-free) projects.
2344
- */
2258
+ * Voice-enabled agents (`defineAgent({ voice: … })`) → wire
2259
+ * `options.voiceAgents`, mapping each agent's export name to its `VOICE_*`
2260
+ * Durable Object namespace binding so the runtime exposes
2261
+ * `/_lunora/voice/&lt;exportName>`. Empty/absent ⇒ no wiring, byte-identical
2262
+ * output for voice-free (and agent-free) projects.
2263
+ */
2345
2264
  voiceAgents?: ReadonlyArray<{
2346
2265
  bindingName: string;
2347
2266
  exportName: string;
@@ -2352,20 +2271,20 @@ interface EmitAppOptions {
2352
2271
  wantsOpenRpc: boolean;
2353
2272
  }
2354
2273
  /**
2355
- * Emit `_generated/app.ts` — a fluent, feature-specialized worker-composition
2356
- * builder. Only the methods for capabilities THIS app uses are emitted, so the
2357
- * builder's type surface (IntelliSense) lists exactly what can be configured.
2358
- *
2359
- * Each capability declaration is fanned into BOTH runtime surfaces: the DO-side
2360
- * `createShardDO(...)` factory that backs `ctx.*`, and the worker-side
2361
- * `createWorker(...)` options that back the studio/admin endpoints — so storage
2362
- * / scheduler / global are declared once instead of twice. The builder is pure
2363
- * sugar over the public `createWorker` / `createShardDO`; both stay usable.
2364
- *
2365
- * Lives in generated code (not `@lunora/runtime`, which is dependency-free) so
2366
- * it can import the add-on packages the app installed (`@lunora/auth`,
2367
- * `@lunora/storage`, …) directly.
2368
- */
2274
+ * Emit `_generated/app.ts` — a fluent, feature-specialized worker-composition
2275
+ * builder. Only the methods for capabilities THIS app uses are emitted, so the
2276
+ * builder's type surface (IntelliSense) lists exactly what can be configured.
2277
+ *
2278
+ * Each capability declaration is fanned into BOTH runtime surfaces: the DO-side
2279
+ * `createShardDO(...)` factory that backs `ctx.*`, and the worker-side
2280
+ * `createWorker(...)` options that back the studio/admin endpoints — so storage
2281
+ * / scheduler / global are declared once instead of twice. The builder is pure
2282
+ * sugar over the public `createWorker` / `createShardDO`; both stay usable.
2283
+ *
2284
+ * Lives in generated code (not `@lunora/runtime`, which is dependency-free) so
2285
+ * it can import the add-on packages the app installed (`@lunora/auth`,
2286
+ * `@lunora/storage`, …) directly.
2287
+ */
2369
2288
  declare const emitApp: (options: EmitAppOptions) => string;
2370
2289
  /** Inputs the OpenAPI emitter needs from a codegen run. */
2371
2290
  interface OpenApiEmitInput {
@@ -2375,40 +2294,40 @@ interface OpenApiEmitInput {
2375
2294
  version?: string;
2376
2295
  }
2377
2296
  /**
2378
- * Emit an OpenAPI 3.1.0 document covering both Lunora function surfaces.
2379
- *
2380
- * `httpRouter()` typed REST routes become real `paths` keyed by their method +
2381
- * URL, with query/path parameters and JSON request bodies derived from their
2382
- * `v.*` validators, and a response schema from `.output()` when declared.
2383
- *
2384
- * RPC `query`/`mutation`/`action` functions become one operation each on
2385
- * `POST /_lunora/rpc` (disambiguated by a `#functionPath` path fragment), with a
2386
- * requestBody pinning `functionPath` + typed `args`. `internal`/`stream`
2387
- * functions are excluded (unreachable / not invocable on the external RPC path).
2388
- *
2389
- * Operations are grouped into `tags` by file namespace, and every operation
2390
- * references a reusable `LunoraError` error-response component enumerating the
2391
- * standard error codes. Borrows oRPC's per-procedure-operation + tag-grouping +
2392
- * internal-filtering structure; the JSON Schema dialect matches `@lunora/values`
2393
- * (Draft 2020-12). Returns the document as a plain object (the single source of
2394
- * truth `emitOpenApi` stringifies and `emitOpenApiModule` inlines, so the
2395
- * `.json` and `.ts` artifacts can never drift).
2396
- */
2297
+ * Emit an OpenAPI 3.1.0 document covering both Lunora function surfaces.
2298
+ *
2299
+ * `httpRouter()` typed REST routes become real `paths` keyed by their method +
2300
+ * URL, with query/path parameters and JSON request bodies derived from their
2301
+ * `v.*` validators, and a response schema from `.output()` when declared.
2302
+ *
2303
+ * RPC `query`/`mutation`/`action` functions become one operation each on
2304
+ * `POST /_lunora/rpc` (disambiguated by a `#functionPath` path fragment), with a
2305
+ * requestBody pinning `functionPath` + typed `args`. `internal`/`stream`
2306
+ * functions are excluded (unreachable / not invocable on the external RPC path).
2307
+ *
2308
+ * Operations are grouped into `tags` by file namespace, and every operation
2309
+ * references a reusable `LunoraError` error-response component enumerating the
2310
+ * standard error codes. Borrows oRPC's per-procedure-operation + tag-grouping +
2311
+ * internal-filtering structure; the JSON Schema dialect matches `@lunora/values`
2312
+ * (Draft 2020-12). Returns the document as a plain object (the single source of
2313
+ * truth `emitOpenApi` stringifies and `emitOpenApiModule` inlines, so the
2314
+ * `.json` and `.ts` artifacts can never drift).
2315
+ */
2397
2316
  declare const buildOpenApiDocument: (input: OpenApiEmitInput) => Record<string, unknown>;
2398
2317
  /**
2399
- * Emit the OpenAPI 3.1 document as a pretty-printed JSON string
2400
- * (`_generated/openapi.json`) — the portable artifact for external tooling.
2401
- */
2318
+ * Emit the OpenAPI 3.1 document as a pretty-printed JSON string
2319
+ * (`_generated/openapi.json`) — the portable artifact for external tooling.
2320
+ */
2402
2321
  declare const emitOpenApi: (input: OpenApiEmitInput) => string;
2403
2322
  /**
2404
- * Emit the OpenAPI document as an importable TS module
2405
- * (`_generated/openapi.ts`) the worker entry imports and passes to
2406
- * `createWorker({ openApiSpec })`. The document object literal is inlined
2407
- * verbatim (same `JSON.stringify` form the `.json` uses), so the `.ts` and
2408
- * `.json` are byte-identical content and regenerate together — closing the gap
2409
- * where a Worker cannot read the JSON file at runtime. `document_` is the object
2410
- * returned by {@link buildOpenApiDocument} (reused, never recomputed).
2411
- */
2323
+ * Emit the OpenAPI document as an importable TS module
2324
+ * (`_generated/openapi.ts`) the worker entry imports and passes to
2325
+ * `createWorker({ openApiSpec })`. The document object literal is inlined
2326
+ * verbatim (same `JSON.stringify` form the `.json` uses), so the `.ts` and
2327
+ * `.json` are byte-identical content and regenerate together — closing the gap
2328
+ * where a Worker cannot read the JSON file at runtime. `document_` is the object
2329
+ * returned by {@link buildOpenApiDocument} (reused, never recomputed).
2330
+ */
2412
2331
  declare const emitOpenApiModule: (document_: Record<string, unknown>) => string;
2413
2332
  /** The OpenRPC dialect version this emitter targets. */
2414
2333
  declare const OPENRPC_VERSION = "1.3.2";
@@ -2419,37 +2338,37 @@ interface OpenRpcEmitInput {
2419
2338
  version?: string;
2420
2339
  }
2421
2340
  /**
2422
- * Emit an OpenRPC 1.x document describing Lunora's JSON-RPC surface.
2423
- *
2424
- * Only the RPC `query`/`mutation`/`action` functions become `methods` — one per
2425
- * function, `name` = `file:fn`. `internal` (off the external RPC path) and
2426
- * `stream` (not invocable over the RPC envelope) are excluded, the same filter
2427
- * the OpenAPI emitter applies. Each method's single `args` param is typed from
2428
- * the function's `v.*` validators (`argsObjectSchema`); `result` is the
2429
- * `.output()` schema when declared, else a best-effort inferred schema. The
2430
- * standard `LunoraError` codes ride along under each method's `errors`.
2431
- *
2432
- * `httpRouter()` typed REST routes are deliberately omitted — OpenRPC is
2433
- * RPC-only and cannot represent REST paths; the OpenAPI document is the spec
2434
- * that covers the REST surface. Methods are sorted by name for stable output.
2435
- * Returns the document as a plain object (the single source of truth
2436
- * `emitOpenRpc` stringifies and `emitOpenRpcModule` inlines, so the `.json` and
2437
- * `.ts` artifacts can never drift).
2438
- */
2341
+ * Emit an OpenRPC 1.x document describing Lunora's JSON-RPC surface.
2342
+ *
2343
+ * Only the RPC `query`/`mutation`/`action` functions become `methods` — one per
2344
+ * function, `name` = `file:fn`. `internal` (off the external RPC path) and
2345
+ * `stream` (not invocable over the RPC envelope) are excluded, the same filter
2346
+ * the OpenAPI emitter applies. Each method's single `args` param is typed from
2347
+ * the function's `v.*` validators (`argsObjectSchema`); `result` is the
2348
+ * `.output()` schema when declared, else a best-effort inferred schema. The
2349
+ * standard `LunoraError` codes ride along under each method's `errors`.
2350
+ *
2351
+ * `httpRouter()` typed REST routes are deliberately omitted — OpenRPC is
2352
+ * RPC-only and cannot represent REST paths; the OpenAPI document is the spec
2353
+ * that covers the REST surface. Methods are sorted by name for stable output.
2354
+ * Returns the document as a plain object (the single source of truth
2355
+ * `emitOpenRpc` stringifies and `emitOpenRpcModule` inlines, so the `.json` and
2356
+ * `.ts` artifacts can never drift).
2357
+ */
2439
2358
  declare const buildOpenRpcDocument: (input: OpenRpcEmitInput) => Record<string, unknown>;
2440
2359
  /**
2441
- * Emit the OpenRPC 1.x document as a pretty-printed JSON string
2442
- * (`_generated/openrpc.json`) — the portable artifact for external tooling.
2443
- */
2360
+ * Emit the OpenRPC 1.x document as a pretty-printed JSON string
2361
+ * (`_generated/openrpc.json`) — the portable artifact for external tooling.
2362
+ */
2444
2363
  declare const emitOpenRpc: (input: OpenRpcEmitInput) => string;
2445
2364
  /**
2446
- * Emit the OpenRPC document as an importable TS module
2447
- * (`_generated/openrpc.ts`) the worker entry imports and passes to
2448
- * `createWorker({ openRpcSpec })`. The document object literal is inlined
2449
- * verbatim (same `JSON.stringify` form the `.json` uses), so the `.ts` and
2450
- * `.json` are byte-identical content and regenerate together. `document_` is
2451
- * the object returned by {@link buildOpenRpcDocument} (reused, never recomputed).
2452
- */
2365
+ * Emit the OpenRPC document as an importable TS module
2366
+ * (`_generated/openrpc.ts`) the worker entry imports and passes to
2367
+ * `createWorker({ openRpcSpec })`. The document object literal is inlined
2368
+ * verbatim (same `JSON.stringify` form the `.json` uses), so the `.ts` and
2369
+ * `.json` are byte-identical content and regenerate together. `document_` is
2370
+ * the object returned by {@link buildOpenRpcDocument} (reused, never recomputed).
2371
+ */
2453
2372
  declare const emitOpenRpcModule: (document_: Record<string, unknown>) => string;
2454
2373
  /** Current snapshot format version. Bumped if the structural shape below changes. */
2455
2374
  declare const SCHEMA_SNAPSHOT_VERSION: 1;
@@ -2480,26 +2399,26 @@ interface TableSnapshot {
2480
2399
  /** Relation accessor name → {@link RelationSnapshot}. */
2481
2400
  relations: Record<string, RelationSnapshot>;
2482
2401
  /**
2483
- * `"root"` (default single-DO), `"global"` (D1-replicated), or
2484
- * `"shardBy:&lt;field>"` (partitioned). Encoded as a string so the snapshot
2485
- * stays a plain JSON-stable value.
2486
- */
2402
+ * `"root"` (default single-DO), `"global"` (D1-replicated), or
2403
+ * `"shardBy:&lt;field>"` (partitioned). Encoded as a string so the snapshot
2404
+ * stays a plain JSON-stable value.
2405
+ */
2487
2406
  shardMode: string;
2488
2407
  }
2489
2408
  /** The committed baseline — a deterministic structural view of the whole schema. */
2490
2409
  interface SchemaSnapshot {
2491
2410
  /**
2492
- * Cloudflare DO data-residency jurisdiction declared via `.jurisdiction("…")`,
2493
- * or absent. Tracked because changing it strands all existing Durable Object
2494
- * data (a DO name maps to a different ID per jurisdiction). Optional, so old
2495
- * baselines written before this field parse cleanly (absent ⇒ undefined).
2496
- *
2497
- * Typed as a plain `string` (not the authoring union) on purpose: this is
2498
- * STORED data that a newer Lunora may have written with a jurisdiction this
2499
- * version doesn't yet know. Preserving the raw value keeps the breaking
2500
- * `changedJurisdiction` diff correct under a downgrade — coercing an unknown
2501
- * value to `undefined` would fail OPEN and hide the most destructive change.
2502
- */
2411
+ * Cloudflare DO data-residency jurisdiction declared via `.jurisdiction("…")`,
2412
+ * or absent. Tracked because changing it strands all existing Durable Object
2413
+ * data (a DO name maps to a different ID per jurisdiction). Optional, so old
2414
+ * baselines written before this field parse cleanly (absent ⇒ undefined).
2415
+ *
2416
+ * Typed as a plain `string` (not the authoring union) on purpose: this is
2417
+ * STORED data that a newer Lunora may have written with a jurisdiction this
2418
+ * version doesn't yet know. Preserving the raw value keeps the breaking
2419
+ * `changedJurisdiction` diff correct under a downgrade — coercing an unknown
2420
+ * value to `undefined` would fail OPEN and hide the most destructive change.
2421
+ */
2503
2422
  jurisdiction?: string;
2504
2423
  /** Sorted list of every declared `defineMigration` id at capture time. */
2505
2424
  migrationIds: ReadonlyArray<string>;
@@ -2522,36 +2441,36 @@ interface SchemaDrift {
2522
2441
  changes: ReadonlyArray<DriftChange>;
2523
2442
  }
2524
2443
  /**
2525
- * Build a {@link SchemaSnapshot} from a parsed {@link SchemaIR} and the set of
2526
- * declared migration ids. Tables and migration ids are sorted so the emitted
2527
- * JSON is byte-stable across runs (no spurious diffs / churn).
2528
- */
2444
+ * Build a {@link SchemaSnapshot} from a parsed {@link SchemaIR} and the set of
2445
+ * declared migration ids. Tables and migration ids are sorted so the emitted
2446
+ * JSON is byte-stable across runs (no spurious diffs / churn).
2447
+ */
2529
2448
  declare const buildSchemaSnapshot: (schema: SchemaIR, migrationIds: ReadonlyArray<string>) => SchemaSnapshot;
2530
2449
  /** Serialize a snapshot to the exact bytes written to `lunora/.lunora-schema.json` (trailing newline). */
2531
2450
  declare const serializeSchemaSnapshot: (snapshot: SchemaSnapshot) => string;
2532
2451
  /**
2533
- * Thrown by {@link parseSchemaSnapshot} when the baseline file exists but is
2534
- * malformed (bad JSON / wrong version / invalid table shape). Lets the CLI gate
2535
- * treat a corrupt baseline as a hard error rather than silently degrading to a
2536
- * "first capture" that would mask drift and then overwrite the bad file.
2537
- */
2452
+ * Thrown by {@link parseSchemaSnapshot} when the baseline file exists but is
2453
+ * malformed (bad JSON / wrong version / invalid table shape). Lets the CLI gate
2454
+ * treat a corrupt baseline as a hard error rather than silently degrading to a
2455
+ * "first capture" that would mask drift and then overwrite the bad file.
2456
+ */
2538
2457
  declare class SchemaSnapshotParseError extends LunoraError {
2539
2458
  constructor(message: string);
2540
2459
  }
2541
2460
  /**
2542
- * Parse a committed snapshot file. Returns `undefined` ONLY when the content is
2543
- * absent/empty; throws {@link SchemaSnapshotParseError} when content is present
2544
- * but malformed (bad JSON, wrong version, or structurally-invalid tables) so the
2545
- * caller can distinguish "no baseline yet" (a legitimate first capture) from "a
2546
- * corrupt baseline" (which must not be silently treated as a first capture).
2547
- */
2461
+ * Parse a committed snapshot file. Returns `undefined` ONLY when the content is
2462
+ * absent/empty; throws {@link SchemaSnapshotParseError} when content is present
2463
+ * but malformed (bad JSON, wrong version, or structurally-invalid tables) so the
2464
+ * caller can distinguish "no baseline yet" (a legitimate first capture) from "a
2465
+ * corrupt baseline" (which must not be silently treated as a first capture).
2466
+ */
2548
2467
  declare const parseSchemaSnapshot: (content: string | undefined) => SchemaSnapshot | undefined;
2549
2468
  /**
2550
- * Diff the current snapshot against a committed baseline and classify every
2551
- * structural change. Pure — no I/O. When `baseline` is `undefined` (no committed
2552
- * snapshot yet) there is no drift to report: every table is treated as a fresh
2553
- * additive `addedTable`, so a first deploy is never blocked.
2554
- */
2469
+ * Diff the current snapshot against a committed baseline and classify every
2470
+ * structural change. Pure — no I/O. When `baseline` is `undefined` (no committed
2471
+ * snapshot yet) there is no drift to report: every table is treated as a fresh
2472
+ * additive `addedTable`, so a first deploy is never blocked.
2473
+ */
2555
2474
  declare const diffSchemaSnapshots: (baseline: SchemaSnapshot | undefined, current: SchemaSnapshot) => SchemaDrift;
2556
2475
  /** The decision the pre-deploy gate returns. */
2557
2476
  interface SchemaDriftDecision {
@@ -2562,157 +2481,158 @@ interface SchemaDriftDecision {
2562
2481
  /** Migration ids declared now but absent from the baseline — proof a migration was added. */
2563
2482
  newMigrationIds: ReadonlyArray<string>;
2564
2483
  /**
2565
- * A multi-line, actionable explanation. Always present; empty string when
2566
- * there is no drift at all. Mirrors the D1-placeholder guard's message style.
2567
- */
2484
+ * A multi-line, actionable explanation. Always present; empty string when
2485
+ * there is no drift at all. Mirrors the D1-placeholder guard's message style.
2486
+ */
2568
2487
  reason: string;
2569
2488
  }
2570
2489
  /**
2571
- * Decide whether breaking schema drift should block a deploy.
2572
- *
2573
- * Blocks only when the baseline exists (a first-ever capture is never blocking),
2574
- * there is at least one `breaking` change, no NEW migration id was added since
2575
- * the baseline, and the `allowDrift` override is not set. Safe-only drift (or
2576
- * breaking drift accompanied by a new migration id) passes.
2577
- */
2490
+ * Decide whether breaking schema drift should block a deploy.
2491
+ *
2492
+ * Blocks only when the baseline exists (a first-ever capture is never blocking),
2493
+ * there is at least one `breaking` change, no NEW migration id was added since
2494
+ * the baseline, and the `allowDrift` override is not set. Safe-only drift (or
2495
+ * breaking drift accompanied by a new migration id) passes.
2496
+ */
2578
2497
  declare const evaluateSchemaDrift: (options: {
2579
2498
  allowDrift?: boolean;
2580
2499
  baseline: SchemaSnapshot | undefined;
2581
2500
  current: SchemaSnapshot;
2582
2501
  }) => SchemaDriftDecision;
2583
2502
  /**
2584
- * Committed, tracked baseline file holding the blessed structural schema
2585
- * snapshot the pre-deploy drift gate diffs against. Lives in `lunora/` (NOT the
2586
- * gitignored `_generated/`) so it is committed alongside `schema.ts`. Leading
2587
- * dot keeps it tucked away next to the schema it describes.
2588
- */
2503
+ * Committed, tracked baseline file holding the blessed structural schema
2504
+ * snapshot the pre-deploy drift gate diffs against. Lives in `lunora/` (NOT the
2505
+ * gitignored `_generated/`) so it is committed alongside `schema.ts`. Leading
2506
+ * dot keeps it tucked away next to the schema it describes.
2507
+ */
2589
2508
  declare const SCHEMA_SNAPSHOT_FILENAME = ".lunora-schema.json";
2590
2509
  /**
2591
- * Construct the ts-morph `Project` codegen discovers over. Prefers the user's
2592
- * `tsconfig.json` (when one is found walking up from `lunoraDirectory`) so
2593
- * cross-file type resolution and path aliases work; falls back to an isolated
2594
- * project otherwise. This is the exact construction {@link runCodegen} uses
2595
- * when no `project` is injected — exported so a long-lived caller (the Vite
2596
- * dev-loop) can build one once and reuse it across runs via
2597
- * {@link refreshCodegenProject} instead of re-parsing the user's whole TS
2598
- * program on every save.
2599
- */
2510
+ * Construct the ts-morph `Project` codegen discovers over. Prefers the user's
2511
+ * `tsconfig.json` (when one is found walking up from `lunoraDirectory`) so
2512
+ * cross-file type resolution and path aliases work; falls back to an isolated
2513
+ * project otherwise. This is the exact construction {@link runCodegen} uses
2514
+ * when no `project` is injected — exported so a long-lived caller (the Vite
2515
+ * dev-loop) can build one once and reuse it across runs via
2516
+ * {@link refreshCodegenProject} instead of re-parsing the user's whole TS
2517
+ * program on every save.
2518
+ */
2600
2519
  declare const createCodegenProject: (lunoraDirectory: string) => Project;
2601
2520
  /**
2602
- * Synchronise a reused {@link createCodegenProject} Project with the current
2603
- * on-disk state of `lunoraDirectory`, so the next {@link runCodegen} sees the
2604
- * same files a freshly-constructed Project would — without re-parsing the whole
2605
- * TS program. Adds any on-disk source file the Project doesn't yet have, and
2606
- * `refreshFromFileSystemSync()`es the ones it does (picking up edits); then
2607
- * removes Project source files under `lunoraDirectory` that no longer exist on
2608
- * disk (the classic stale-deleted-file cache bug).
2609
- *
2610
- * Files outside `lunoraDirectory` (e.g. those pulled in by the user's tsconfig)
2611
- * are left untouched — they back type resolution and rarely change in the
2612
- * dev-loop; a tsconfig change invalidates the whole cached Project upstream.
2613
- */
2521
+ * Synchronise a reused {@link createCodegenProject} Project with the current
2522
+ * on-disk state of `lunoraDirectory`, so the next {@link runCodegen} sees the
2523
+ * same files a freshly-constructed Project would — without re-parsing the whole
2524
+ * TS program. Adds any on-disk source file the Project doesn't yet have, and
2525
+ * `refreshFromFileSystemSync()`es the ones it does (picking up edits); then
2526
+ * removes Project source files under `lunoraDirectory` that no longer exist on
2527
+ * disk (the classic stale-deleted-file cache bug).
2528
+ *
2529
+ * Files outside `lunoraDirectory` (e.g. those pulled in by the user's tsconfig)
2530
+ * are left untouched — they back type resolution and rarely change in the
2531
+ * dev-loop; a tsconfig change invalidates the whole cached Project upstream.
2532
+ */
2614
2533
  declare const refreshCodegenProject: (project: Project, lunoraDirectory: string) => void;
2615
2534
  /**
2616
- * Top-level codegen entry. Parses `&lt;projectRoot>/lunora/schema.ts` and every
2617
- * function file under `&lt;projectRoot>/lunora/`, then writes
2618
- * `_generated/{api,server,dataModel}.ts` next to them.
2619
- *
2620
- * When `LUNORA_CODEGEN_TIMING` is set (truthy), a single diagnostic summary
2621
- * line is written to stderr with the total wall time and the discovery-vs-emit
2622
- * split — opt-in instrumentation that is otherwise zero-cost and side-effect-free
2623
- * on the returned {@link CodegenResult}.
2624
- */
2535
+ * Top-level codegen entry. Parses `&lt;projectRoot>/lunora/schema.ts` and every
2536
+ * function file under `&lt;projectRoot>/lunora/`, then writes
2537
+ * `_generated/{api,server,dataModel}.ts` next to them.
2538
+ *
2539
+ * When `LUNORA_CODEGEN_TIMING` is set (truthy), a single diagnostic summary
2540
+ * line is written to stderr with the total wall time and the discovery-vs-emit
2541
+ * split — opt-in instrumentation that is otherwise zero-cost and side-effect-free
2542
+ * on the returned {@link CodegenResult}.
2543
+ */
2625
2544
  declare const runCodegen: (options: CodegenOptions) => CodegenResult;
2626
2545
  interface CodegenOptions {
2627
2546
  /**
2628
- * Which machine-readable API spec(s) to emit into `_generated/`.
2629
- *
2630
- * `"openapi"` (the default) writes only `openapi.json` (OpenAPI 3.1; covers
2631
- * both the RPC functions and `httpRouter()` REST routes). `"openrpc"` writes
2632
- * only `openrpc.json` (OpenRPC 1.x; the RPC functions only — OpenRPC cannot
2633
- * represent REST routes). `"both"` writes both files; `"none"` writes neither.
2634
- *
2635
- * Regardless of the choice, `CodegenResult.generated.openApi` and `.openRpc`
2636
- * always carry the rendered string (computation is cheap and pure); only the
2637
- * on-disk write is gated by this option.
2638
- */
2547
+ * Which machine-readable API spec(s) to emit into `_generated/`.
2548
+ *
2549
+ * `"openapi"` (the default) writes only `openapi.json` (OpenAPI 3.1; covers
2550
+ * both the RPC functions and `httpRouter()` REST routes). `"openrpc"` writes
2551
+ * only `openrpc.json` (OpenRPC 1.x; the RPC functions only — OpenRPC cannot
2552
+ * represent REST routes). `"both"` writes both files; `"none"` writes neither.
2553
+ *
2554
+ * Regardless of the choice, `CodegenResult.generated.openApi` and `.openRpc`
2555
+ * always carry the rendered string (computation is cheap and pure); only the
2556
+ * on-disk write is gated by this option.
2557
+ */
2639
2558
  apiSpec?: "both" | "none" | "openapi" | "openrpc";
2640
2559
  /**
2641
- * When true, run discovery + emit (so any schema/function parse error
2642
- * surfaces) but skip writing files to `_generated/`. The returned
2643
- * `outputDirectory` is still the path that *would* have been written.
2644
- */
2560
+ * When true, run discovery + emit (so any schema/function parse error
2561
+ * surfaces) but skip writing files to `_generated/`. The returned
2562
+ * `outputDirectory` is still the path that *would* have been written.
2563
+ */
2645
2564
  dryRun?: boolean;
2646
2565
  /**
2647
- * Run the static schema advisor (unindexed FKs, …) during codegen.
2648
- * Defaults to `true`. When `false`, `CodegenResult.advisories` is empty.
2649
- * Computed regardless of `dryRun`; codegen never prints them — see
2650
- * {@link CodegenResult.advisories}.
2651
- */
2566
+ * Run the static schema advisor (unindexed FKs, …) during codegen.
2567
+ * Defaults to `true`. When `false`, `CodegenResult.advisories` is empty.
2568
+ * Computed regardless of `dryRun`; codegen never prints them — see
2569
+ * {@link CodegenResult.advisories}.
2570
+ */
2652
2571
  lint?: boolean;
2653
2572
  /** Override the lunora subdirectory name. Defaults to `"lunora"`. */
2654
2573
  lunoraDirectory?: string;
2655
2574
  /**
2656
- * Reuse a previously-constructed ts-morph {@link Project} instead of building
2657
- * a fresh one each run. The caller owns refreshing its source files from disk
2658
- * (see {@link refreshCodegenProject}) — codegen does not re-read changed files
2659
- * off an injected Project. Built via {@link createCodegenProject} when absent.
2660
- * Used by the Vite dev-loop to avoid re-parsing the whole TS program on every
2661
- * save; omit it (CLI one-shot path) to get the default fresh-Project behaviour.
2662
- */
2575
+ * Reuse a previously-constructed ts-morph {@link Project} instead of building
2576
+ * a fresh one each run. The caller owns refreshing its source files from disk
2577
+ * (see {@link refreshCodegenProject}) — codegen does not re-read changed files
2578
+ * off an injected Project. Built via {@link createCodegenProject} when absent.
2579
+ * Used by the Vite dev-loop to avoid re-parsing the whole TS program on every
2580
+ * save; omit it (CLI one-shot path) to get the default fresh-Project behaviour.
2581
+ */
2663
2582
  project?: Project;
2664
2583
  /** Project root containing the `lunora/` directory. */
2665
2584
  projectRoot: string;
2666
2585
  /**
2667
- * Re-bless the committed schema-drift baseline (`lunora/.lunora-schema.json`)
2668
- * with the current structural snapshot. The baseline is ALWAYS written on
2669
- * first capture (when the file is absent); set this to overwrite an existing
2670
- * one — e.g. after the developer has added the data migration that justifies
2671
- * a breaking change. Ignored when `dryRun` is true.
2672
- */
2586
+ * Re-bless the committed schema-drift baseline (`lunora/.lunora-schema.json`)
2587
+ * with the current structural snapshot. The baseline is ALWAYS written on
2588
+ * first capture (when the file is absent); set this to overwrite an existing
2589
+ * one — e.g. after the developer has added the data migration that justifies
2590
+ * a breaking change. Ignored when `dryRun` is true.
2591
+ */
2673
2592
  updateSchemaBaseline?: boolean;
2674
2593
  /**
2675
- * Committed `wrangler.jsonc` `vars` entries that hold plaintext secrets — the
2676
- * `plaintext_secret_in_wrangler_vars` lint input. Produced by `@lunora/config`
2677
- * (which reads `wrangler.jsonc`) and threaded through by the CLI / Vite plugin;
2678
- * codegen only forwards it to the advisor. Absent when no wrangler config is
2679
- * present or the caller doesn't scan it.
2680
- */
2594
+ * Committed `wrangler.jsonc` `vars` entries that hold plaintext secrets — the
2595
+ * `plaintext_secret_in_wrangler_vars` lint input. Produced by `@lunora/config`
2596
+ * (which reads `wrangler.jsonc`) and threaded through by the CLI / Vite plugin;
2597
+ * codegen only forwards it to the advisor. Absent when no wrangler config is
2598
+ * present or the caller doesn't scan it.
2599
+ */
2681
2600
  wranglerVariables?: ReadonlyArray<WranglerVariableIR>;
2682
2601
  }
2683
2602
  interface CodegenResult {
2684
2603
  /**
2685
- * Static schema advisor findings (e.g. unindexed foreign keys) produced
2686
- * this run. Empty when `lint` is `false` or the schema is clean. Codegen
2687
- * does not print these itself — each caller presents them through its own
2688
- * channel (the CLI logger, the vite overlay, the studio Advisors table).
2689
- * `formatAdvisories` is exported for a plain multi-line rendering.
2690
- */
2604
+ * Static schema advisor findings (e.g. unindexed foreign keys) produced
2605
+ * this run. Empty when `lint` is `false` or the schema is clean. Codegen
2606
+ * does not print these itself — each caller presents them through its own
2607
+ * channel (the CLI logger, the vite overlay, the studio Advisors table).
2608
+ * `formatAdvisories` is exported for a plain multi-line rendering.
2609
+ */
2691
2610
  advisories: ReadonlyArray<Finding>;
2692
2611
  /**
2693
- * Agents discovered from `defineAgent` exports in `lunora/agents.ts` — the
2694
- * list the config layer reconciles into wrangler's `workflows[]` array (an
2695
- * agent compiles onto a Cloudflare Workflow). Agents are NOT Durable Objects,
2696
- * so this adds no binding or migration. Empty when the project declares none.
2697
- */
2612
+ * Agents discovered from `defineAgent` exports in `lunora/agents.ts` — the
2613
+ * list the config layer reconciles into wrangler's `workflows[]` array (an
2614
+ * agent compiles onto a Cloudflare Workflow). Agents are NOT Durable Objects,
2615
+ * so this adds no binding or migration. Empty when the project declares none.
2616
+ */
2698
2617
  agents: ReadonlyArray<AgentIR>;
2699
2618
  /**
2700
- * Containers discovered from `defineContainer` exports in
2701
- * `lunora/containers.ts` — the list the config layer reconciles into
2702
- * wrangler's `containers[]`, `CONTAINER_*` Durable Object bindings, and
2703
- * migration classes. Empty when the project declares no containers.
2704
- */
2619
+ * Containers discovered from `defineContainer` exports in
2620
+ * `lunora/containers.ts` — the list the config layer reconciles into
2621
+ * wrangler's `containers[]`, `CONTAINER_*` Durable Object bindings, and
2622
+ * migration classes. Empty when the project declares no containers.
2623
+ */
2705
2624
  containers: ReadonlyArray<ContainerIR>;
2706
2625
  /**
2707
- * Deduplicated cron schedules discovered from `cronJobs()` definitions —
2708
- * the array the vite plugin reconciles into `wrangler.jsonc`'s
2709
- * `triggers.crons`. Empty when the project declares no crons.
2710
- */
2626
+ * Deduplicated cron schedules discovered from `cronJobs()` definitions —
2627
+ * the array the vite plugin reconciles into `wrangler.jsonc`'s
2628
+ * `triggers.crons`. Empty when the project declares no crons.
2629
+ */
2711
2630
  cronTriggers: ReadonlyArray<string>;
2712
2631
  generated: {
2713
2632
  /** WorkflowEntrypoint classes for declared agents (`_generated/agents.ts`); `""` (and not written) when no agents are declared. */
2714
2633
  agents: string;
2715
- api: string; /** Fluent worker-composition builder (`_generated/app.ts`) — `defineApp()`. Always written. */
2634
+ api: string;
2635
+ /** Fluent worker-composition builder (`_generated/app.ts`) — `defineApp()`. Always written. */
2716
2636
  app: string;
2717
2637
  /** Partial-replication collection factories (`_generated/collections.ts`); `""` (and not written) unless the project declares shapes and installs `@lunora/db`. */
2718
2638
  collections: string;
@@ -2722,89 +2642,89 @@ interface CodegenResult {
2722
2642
  dataModel: string;
2723
2643
  drizzleGlobal: string;
2724
2644
  drizzleShard: string;
2725
- functions: string; /** OpenAPI 3.1.0 document (`_generated/openapi.json`), pretty-printed JSON. */
2645
+ functions: string;
2646
+ /** OpenAPI 3.1.0 document (`_generated/openapi.json`), pretty-printed JSON. */
2726
2647
  openApi: string;
2727
2648
  /**
2728
- * OpenAPI document as an importable TS module (`_generated/openapi.ts`) —
2729
- * `export const openApiSpec`, the worker imports it for
2730
- * `createWorker({ openApiSpec })`. Same document as `openApi`. Written
2731
- * alongside `openapi.json` whenever `apiSpec` includes `openapi`.
2732
- */
2649
+ * OpenAPI document as an importable TS module (`_generated/openapi.ts`) —
2650
+ * `export const openApiSpec`, the worker imports it for
2651
+ * `createWorker({ openApiSpec })`. Same document as `openApi`. Written
2652
+ * alongside `openapi.json` whenever `apiSpec` includes `openapi`.
2653
+ */
2733
2654
  openApiModule: string;
2734
2655
  /** OpenRPC 1.x document (`_generated/openrpc.json`), pretty-printed JSON. Always computed; written only when `apiSpec` includes `openrpc`. */
2735
2656
  openRpc: string;
2736
2657
  /**
2737
- * OpenRPC document as an importable TS module (`_generated/openrpc.ts`) —
2738
- * `export const openRpcSpec`, for `createWorker({ openRpcSpec })`. Same
2739
- * document as `openRpc`. Written alongside `openrpc.json` whenever
2740
- * `apiSpec` includes `openrpc`.
2741
- */
2658
+ * OpenRPC document as an importable TS module (`_generated/openrpc.ts`) —
2659
+ * `export const openRpcSpec`, for `createWorker({ openRpcSpec })`. Same
2660
+ * document as `openRpc`. Written alongside `openrpc.json` whenever
2661
+ * `apiSpec` includes `openrpc`.
2662
+ */
2742
2663
  openRpcModule: string;
2743
2664
  /** Push-consumer queue registry (`_generated/queues.ts`); `""` (and not written) when no push queues are declared. */
2744
2665
  queues: string;
2745
2666
  /** Project-bound seed client (`_generated/seed.ts`); `""` (and not written) when `@lunora/seed` is not a declared dependency. */
2746
2667
  seed: string;
2747
2668
  server: string;
2748
- shard: string; /** Static vector-index registry (`_generated/vectors.ts`) — `LUNORA_VECTOR_INDEXES`. Empty array body when the schema declares none. */
2669
+ shard: string;
2670
+ /** Static vector-index registry (`_generated/vectors.ts`) — `LUNORA_VECTOR_INDEXES`. Empty array body when the schema declares none. */
2749
2671
  vectors: string;
2750
2672
  /** WorkflowEntrypoint classes (`_generated/workflows.ts`); `""` (and not written) when no workflows are declared. */
2751
2673
  workflows: string;
2752
2674
  };
2753
2675
  outputDirectory: string;
2754
2676
  /**
2755
- * Queues discovered from `defineQueue` exports in `lunora/queues.ts` — the
2756
- * list the config layer reconciles into wrangler's `queues.producers[]` /
2757
- * `queues.consumers[]`. Queues are NOT Durable Objects, so this adds no
2758
- * binding or migration. Empty when the project declares no queues.
2759
- */
2677
+ * Queues discovered from `defineQueue` exports in `lunora/queues.ts` — the
2678
+ * list the config layer reconciles into wrangler's `queues.producers[]` /
2679
+ * `queues.consumers[]`. Queues are NOT Durable Objects, so this adds no
2680
+ * binding or migration. Empty when the project declares no queues.
2681
+ */
2760
2682
  queues: ReadonlyArray<QueueIR>;
2761
2683
  /**
2762
- * The CURRENT structural schema snapshot computed this run (tables + field
2763
- * kinds/optionality + indexes/relations/shard mode + declared migration ids).
2764
- * The pre-deploy drift gate diffs this against the committed baseline read
2765
- * from {@link CodegenResult.schemaSnapshotPath}. Always present, even on a
2766
- * `dryRun`.
2767
- */
2684
+ * The CURRENT structural schema snapshot computed this run (tables + field
2685
+ * kinds/optionality + indexes/relations/shard mode + declared migration ids).
2686
+ * The pre-deploy drift gate diffs this against the committed baseline read
2687
+ * from {@link CodegenResult.schemaSnapshotPath}. Always present, even on a
2688
+ * `dryRun`.
2689
+ */
2768
2690
  schemaSnapshot: SchemaSnapshot;
2769
2691
  /** Absolute path of the committed baseline file (`lunora/.lunora-schema.json`). */
2770
2692
  schemaSnapshotPath: string;
2771
2693
  /**
2772
- * Workflows discovered from `defineWorkflow` exports in
2773
- * `lunora/workflows.ts` — the list the config layer reconciles into
2774
- * wrangler's `workflows[]` array. Workflows are NOT Durable Objects, so this
2775
- * adds no binding or migration. Empty when the project declares no workflows.
2776
- */
2694
+ * Workflows discovered from `defineWorkflow` exports in
2695
+ * `lunora/workflows.ts` — the list the config layer reconciles into
2696
+ * wrangler's `workflows[]` array. Workflows are NOT Durable Objects, so this
2697
+ * adds no binding or migration. Empty when the project declares no workflows.
2698
+ */
2777
2699
  workflows: ReadonlyArray<WorkflowIR>;
2778
2700
  }
2779
2701
  /**
2780
- * Convert a {@link SchemaIR} into a synthetic runtime {@link Schema} carrying just
2781
- * the `tables[name].shape` surface `@lunora/seed` introspects. System columns
2782
- * (`_id`, `_creationTime`) are absent from the IR shape, exactly as the seed
2783
- * engine expects (it assigns `_id` itself).
2784
- */
2702
+ * Convert a {@link SchemaIR} into a synthetic runtime {@link Schema} carrying just
2703
+ * the `tables[name].shape` surface `@lunora/seed` introspects. System columns
2704
+ * (`_id`, `_creationTime`) are absent from the IR shape, exactly as the seed
2705
+ * engine expects (it assigns `_id` itself).
2706
+ */
2785
2707
  declare const schemaFromIr: (ir: SchemaIR) => Schema;
2786
2708
  /**
2787
- * Convert a codegen {@link ValidatorIR} into a JSON Schema node. A thin wrapper
2788
- * over the shared {@link jsonSchemaFromNode} core (from `@lunora/values`) with the
2789
- * IR-backed {@link irReader}, so the kind→schema mapping is the *same* algorithm
2790
- * `@lunora/values`' `toJsonSchema` runs — codegen never instantiates the runtime
2791
- * `v.*` objects, it only holds the reflected IR. Shared by the OpenAPI and
2792
- * OpenRPC emitters so both surfaces speak one JSON Schema dialect.
2793
- */
2709
+ * Convert a codegen {@link ValidatorIR} into a JSON Schema node. A thin wrapper
2710
+ * over the shared {@link jsonSchemaFromNode} core (from `@lunora/values`) with the
2711
+ * IR-backed {@link irReader}, so the kind→schema mapping is the *same* algorithm
2712
+ * `@lunora/values`' `toJsonSchema` runs — codegen never instantiates the runtime
2713
+ * `v.*` objects, it only holds the reflected IR. Shared by the OpenAPI and
2714
+ * OpenRPC emitters so both surfaces speak one JSON Schema dialect.
2715
+ */
2794
2716
  declare const validatorIrToJsonSchema: (validator: ValidatorIR) => JsonSchema;
2795
- /** Build `{ type: "object", properties, required }` from an IR shape (mirrors `@lunora/values`' object mapping). */
2796
-
2797
- /**
2798
- * The machine-readable `LunoraError` codes Lunora emits on the RPC + REST
2799
- * surfaces, enumerated from `@lunora/server`'s `CODE_STATUS` map plus the
2800
- * runtime/DO dispatch codes (`FUNCTION_NOT_FOUND`, `PAYLOAD_TOO_LARGE`,
2801
- * `METHOD_NOT_ALLOWED`, the `*_NOT_CONFIGURED` admin gates, …). The list documents
2802
- * the contract; clients switch on `error.code`. Kept sorted for stable output.
2803
- */
2717
+ /**
2718
+ * The machine-readable `LunoraError` codes Lunora emits on the RPC + REST
2719
+ * surfaces, enumerated from `@lunora/server`'s `CODE_STATUS` map plus the
2720
+ * runtime/DO dispatch codes (`FUNCTION_NOT_FOUND`, `PAYLOAD_TOO_LARGE`,
2721
+ * `METHOD_NOT_ALLOWED`, the `*_NOT_CONFIGURED` admin gates, …). The list documents
2722
+ * the contract; clients switch on `error.code`. Kept sorted for stable output.
2723
+ */
2804
2724
  declare const LUNORA_ERROR_CODES: ReadonlyArray<string>;
2805
2725
  /** The matching secret rule's `kind` for a string value, or `undefined` when none matches. */
2806
2726
  declare const secretKindOf: (value: string) => string | undefined;
2807
2727
  /** A redacted preview of a secret value — first 4 chars plus its length, never the full value. */
2808
2728
  declare const redact: (value: string) => string;
2809
2729
  declare const VERSION = "0.0.0";
2810
- export { AGENTS_FILENAME, type AgentIR, type AuthApiCallIR, CONTAINERS_FILENAME, CodegenDiagnosticError, type CodegenOptions, type CodegenResult, type ContainerIR, type CronJobIR, type DriftChange, type EmitAppOptions, FLAGS_FILENAME, type FieldSnapshot, type FlagsIR, type FunctionIR, GENERATED_HEADER, type HttpRouteIR, type IndexIR, type IndexSnapshot, type InsertWriteIR, LUNORA_ERROR_CODES, type LintSchemaOptions, MUTATORS_FILENAME, type MaskProcedureIR, type MigrationIR, type MutatorIR, OPENRPC_VERSION, type OpenApiEmitInput, type OpenRpcEmitInput, type ProjectIR, QUEUES_FILENAME, type QueryReadIR, type QueueIR, type R2sqlCallIR, type RelationSnapshot, type RlsMetadataIR, type RlsPolicyIR, type RlsProcedureIR, type RlsRoleIR, SCHEMA_SNAPSHOT_FILENAME, SCHEMA_SNAPSHOT_VERSION, SHAPES_FILENAME, type SchemaDrift, type SchemaDriftDecision, type SchemaIR, type SchemaSnapshot, SchemaSnapshotParseError, type ShapeIR, type StorageRuleIR, type StorageRulesMetadataIR, type TableIR, type TableSnapshot, VERSION, type ValidatorIR, type VectorIndexIR, WORKFLOWS_FILENAME, type WorkflowIR, type WranglerVariableIR, buildOpenApiDocument, buildOpenRpcDocument, buildSchemaSnapshot, createCodegenProject, diagnosticAt, diffSchemaSnapshots, discoverAgents, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFlags, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverMutators, discoverNondeterministicCalls, discoverQueries, discoverQueues, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSchema, discoverShapes, discoverStorageRulesMetadata, discoverWorkflows, emitAgents, emitApi, emitApp, emitCollections, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitOpenApi, emitOpenApiModule, emitOpenRpc, emitOpenRpcModule, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers, evaluateSchemaDrift, formatAdvisories, lintSchema, parseSchemaSnapshot, redact, refreshCodegenProject, runCodegen, schemaFromIr, secretKindOf, serializeSchemaSnapshot, validatorIrToJsonSchema };
2730
+ export { AGENTS_FILENAME, type AgentIR, type AuthApiCallIR, CONTAINERS_FILENAME, CodegenDiagnosticError, type CodegenOptions, type CodegenResult, type ContainerIR, type CronJobIR, type DriftChange, type EmitAppOptions, FLAGS_FILENAME, type FieldSnapshot, type FlagsIR, type FunctionIR, GENERATED_HEADER, type HttpRouteIR, type IndexIR, type IndexSnapshot, type InsertWriteIR, LUNORA_ERROR_CODES, type LintSchemaOptions, MUTATORS_FILENAME, type MaskProcedureIR, type MigrationIR, type MutatorIR, OPENRPC_VERSION, type OpenApiEmitInput, type OpenRpcEmitInput, type ProjectIR, QUEUES_FILENAME, type QueryReadIR, type QueueIR, type R2sqlCallIR, type RelationSnapshot, type RlsMetadataIR, type RlsPolicyIR, type RlsProcedureIR, type RlsRoleIR, SCHEMA_SNAPSHOT_FILENAME, SCHEMA_SNAPSHOT_VERSION, SHAPES_FILENAME, type SandboxUsage, type SchemaDrift, type SchemaDriftDecision, type SchemaIR, type SchemaSnapshot, SchemaSnapshotParseError, type ShapeIR, type StorageRuleIR, type StorageRulesMetadataIR, type TableIR, type TableSnapshot, VERSION, type ValidatorIR, type VectorIndexIR, WORKFLOWS_FILENAME, type WorkflowIR, type WranglerVariableIR, buildOpenApiDocument, buildOpenRpcDocument, buildSchemaSnapshot, createCodegenProject, diagnosticAt, diffSchemaSnapshots, discoverAgents, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFlags, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverMutators, discoverNondeterministicCalls, discoverQueries, discoverQueues, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSandboxUsage, discoverSchema, discoverShapes, discoverStorageRulesMetadata, discoverWorkflows, emitAgents, emitApi, emitApp, emitCollections, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitOpenApi, emitOpenApiModule, emitOpenRpc, emitOpenRpcModule, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers, evaluateSchemaDrift, formatAdvisories, lintSchema, parseSchemaSnapshot, redact, refreshCodegenProject, runCodegen, schemaFromIr, secretKindOf, serializeSchemaSnapshot, validatorIrToJsonSchema };