@lunora/codegen 1.0.0-alpha.3 → 1.0.0-alpha.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/__assets__/package-og.svg +1 -1
  2. package/dist/index.d.mts +1084 -25
  3. package/dist/index.d.ts +1084 -25
  4. package/dist/index.mjs +29 -23
  5. package/dist/packem_shared/{CONTAINERS_FILENAME-0K-pjNb8.mjs → CONTAINERS_FILENAME-DjpXMqhp.mjs} +1 -1
  6. package/dist/packem_shared/{CodegenDiagnosticError-54jWDxA9.mjs → CodegenDiagnosticError-DyQ5FwkM.mjs} +7 -5
  7. package/dist/packem_shared/FLAGS_FILENAME-BjpS5w08.mjs +139 -0
  8. package/dist/packem_shared/{GENERATED_HEADER-CuLyj0WQ.mjs → GENERATED_HEADER-f6nMvllu.mjs} +853 -65
  9. package/dist/packem_shared/MUTATORS_FILENAME-BZOfUhlY.mjs +81 -0
  10. package/dist/packem_shared/{OPENRPC_VERSION-BGUrsrt_.mjs → OPENRPC_VERSION-u5SqDDGk.mjs} +1 -1
  11. package/dist/packem_shared/QUEUES_FILENAME-BF0iUmx7.mjs +119 -0
  12. package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-Cpfn80SH.mjs +3481 -0
  13. package/dist/packem_shared/{SCHEMA_SNAPSHOT_VERSION-DzLDbWk3.mjs → SCHEMA_SNAPSHOT_VERSION-D0ARY6rL.mjs} +18 -2
  14. package/dist/packem_shared/SHAPES_FILENAME-DOhPGi-6.mjs +94 -0
  15. package/dist/packem_shared/{WORKFLOWS_FILENAME-DRDQdhfq.mjs → WORKFLOWS_FILENAME-CCC_42o3.mjs} +42 -2
  16. package/dist/packem_shared/{buildOpenApiDocument-CXwnbp4b.mjs → buildOpenApiDocument-AgKWmlry.mjs} +1 -1
  17. package/dist/packem_shared/{discoverAuthApiCalls-C35R6z0T.mjs → discoverAuthApiCalls-URDoTADN.mjs} +1 -1
  18. package/dist/packem_shared/{discoverCrons-BL6iGuJ3.mjs → discoverCrons-DdZOeroL.mjs} +20 -17
  19. package/dist/packem_shared/{discoverFunctions-DEgAcRuD.mjs → discoverFunctions-BJdF7lRs.mjs} +63 -9
  20. package/dist/packem_shared/{discoverHttpRoutes-C978pBiG.mjs → discoverHttpRoutes-CcwP-Rkr.mjs} +2 -2
  21. package/dist/packem_shared/{discoverInserts-CRQdXvHO.mjs → discoverInserts-BpFhM32L.mjs} +24 -2
  22. package/dist/packem_shared/{discoverMaskProcedures-B64zA740.mjs → discoverMaskProcedures-DT-v8wvj.mjs} +58 -2
  23. package/dist/packem_shared/{discoverMigrations-Doj_-BAA.mjs → discoverMigrations-cG1idBVM.mjs} +10 -4
  24. package/dist/packem_shared/{discoverNondeterministicCalls-4KiPQxQU.mjs → discoverNondeterministicCalls-CEOjmfk4.mjs} +1 -1
  25. package/dist/packem_shared/{discoverQueries-BkIi0dBD.mjs → discoverQueries-DeVBQVAG.mjs} +1 -1
  26. package/dist/packem_shared/{discoverR2sqlCalls-BpDqvcUn.mjs → discoverR2sqlCalls-CWCAU-xJ.mjs} +1 -1
  27. package/dist/packem_shared/{discoverRlsMetadata-DpRB1HMe.mjs → discoverRlsMetadata-BL0oukaK.mjs} +1 -1
  28. package/dist/packem_shared/{discoverSchema-BBulgGbH.mjs → discoverSchema-CeXJWVKV.mjs} +349 -17
  29. package/dist/packem_shared/{discoverStorageRulesMetadata-DAqJUxUv.mjs → discoverStorageRulesMetadata-CFHByjIl.mjs} +1 -1
  30. package/dist/packem_shared/{emitApp-CzSxVDaG.mjs → emitApp-DKt3rPtr.mjs} +84 -8
  31. package/dist/packem_shared/{formatAdvisories-8NIv1k0I.mjs → formatAdvisories-C9wNBNvL.mjs} +47 -3
  32. package/dist/packem_shared/{parse-validator-tuQtHrsr.mjs → parse-validator-D6zI2i85.mjs} +5 -3
  33. package/dist/packem_shared/redact-oTmsol5A.mjs +33 -0
  34. package/package.json +9 -7
  35. package/dist/packem_shared/SCHEMA_SNAPSHOT_FILENAME-BQ_kCZ81.mjs +0 -903
package/dist/index.d.mts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { Finding } from '@lunora/advisor';
2
2
  export type { Finding } from '@lunora/advisor';
3
+ import { LunoraError } from '@lunora/errors';
4
+ export { MESSAGE_SOLUTIONS as LUNORA_SOLUTION_RULES, type Solution as LunoraSolution, type SolutionRule as LunoraSolutionRule, findSolutionByMessage as findLunoraSolution } from '@lunora/errors';
3
5
  import { Node, Project } from 'ts-morph';
4
6
  import { StudioFeaturesResult } from '@lunora/do';
5
7
  import { Schema } from '@lunora/server';
@@ -25,6 +27,15 @@ interface ValidatorIR {
25
27
  bucket?: string;
26
28
  /** Column modifiers (`.unique()`, `.default()`, `.nullable()`, …) when present. */
27
29
  column?: ColumnMetaIR;
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
+ */
38
+ hasRefinement?: boolean;
28
39
  /** For `v.optional(inner)` / `v.array(inner)`. */
29
40
  inner?: ValidatorIR;
30
41
  /** For `v.record(key, value)`. */
@@ -100,6 +111,36 @@ interface RelationIR {
100
111
  /** Target table name. */
101
112
  table: string;
102
113
  }
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
+ */
120
+ interface ExternalSourceIR {
121
+ /** The wrangler Hyperdrive binding name. */
122
+ binding: string;
123
+ /** Whether a `columns` projection allow-list was given. */
124
+ columns?: ReadonlyArray<string>;
125
+ /** `true` when a `reconcileEveryMs` was given (the incremental-mode delete-visibility companion). */
126
+ hasReconcile?: boolean;
127
+ /** `true` when a `tenantBy` mapper was given — the tenant-isolation boundary the `external_source_unscoped` lint checks. */
128
+ hasTenantBy: boolean;
129
+ /** The `idColumn` literal, when given (defaults to `"id"` at runtime). */
130
+ idColumn?: string;
131
+ /** Delete-detection mode literal, when given (`"full-pull"` | `"incremental"`). */
132
+ mode?: string;
133
+ /** The membership query literal, when statically knowable. */
134
+ query?: string;
135
+ /**
136
+ * `true` when `.source(...)` was present but its argument was **not** a static
137
+ * object literal (e.g. `.source(buildConfig())`), so none of the fields above
138
+ * could be read. The source still exists — this flag lets `hasSourcedTables`
139
+ * (codegen) and the `external_source_*` lints treat it as a source that can't be
140
+ * verified, instead of mistaking it for no `.source()` at all.
141
+ */
142
+ unanalyzable?: boolean;
143
+ }
103
144
  interface TableIR {
104
145
  /**
105
146
  * `true` when the table chain carried `.externallyManaged()` — its rows are
@@ -109,12 +150,26 @@ interface TableIR {
109
150
  */
110
151
  externallyManaged?: boolean;
111
152
  /**
153
+ * Set when the chain carried `.source(...)` — the table is materialized from an
154
+ * external Hyperdrive-backed database by a system poll loop (plan 077). Carries
155
+ * the statically-knowable bits the advisor lints read; the functions (`map`,
156
+ * `tenantBy`) are not serialized, only their presence (`hasTenantBy`).
157
+ */
158
+ externalSource?: ExternalSourceIR;
159
+ /**
112
160
  * Storage backend for a `.global()` table: `"d1"` (default) or
113
161
  * `"hyperdrive"` (a Postgres/MySQL database via Cloudflare Hyperdrive). Only
114
162
  * meaningful when `shardMode === "global"`; absent for sharded/root tables.
115
163
  */
116
164
  globalBackend?: "d1" | "hyperdrive";
117
165
  indexes: ReadonlyArray<IndexIR>;
166
+ /**
167
+ * `true` when the table chain carried `.public()` — an explicit opt-OUT of
168
+ * the schema's `.rls("required")` enforcement for this one table. Optional:
169
+ * hand-built IR and tables that never called `.public()` default it to
170
+ * `false`.
171
+ */
172
+ isPublic?: boolean;
118
173
  name: string;
119
174
  /** Rank indexes declared inline via `.rankIndex(name, …)`. */
120
175
  rankIndexes: ReadonlyArray<RankIndexIR>;
@@ -126,10 +181,30 @@ interface TableIR {
126
181
  field: string;
127
182
  kind: "shardBy";
128
183
  };
184
+ /** Set when the chain carried `.softDelete()` — the marker column's name (default `deletedAt`). The column is injected into `shape` so `Doc_*` carries it. */
185
+ softDelete?: {
186
+ field: string;
187
+ };
129
188
  /** Vector indexes declared inline via `.vectorize()` (DSL Shape A). */
130
189
  vectorIndexes: ReadonlyArray<VectorIndexIR>;
131
190
  }
191
+ /** The Cloudflare DO data-residency jurisdictions the schema may declare. Canonical literal set for the codegen package. */
192
+ type JurisdictionIR = "eu" | "fedramp" | "us";
132
193
  interface SchemaIR {
194
+ /**
195
+ * Cloudflare data-residency jurisdiction declared via
196
+ * `defineSchema(...).jurisdiction("…")`. Emitted into the generated worker's
197
+ * `createWorker({ jurisdiction })` (and `ctx.scheduler` / `ctx.containers`).
198
+ * Absent ⇒ un-pinned.
199
+ */
200
+ jurisdiction?: JurisdictionIR;
201
+ /**
202
+ * Set when `defineSchema(...).rls("required")` was chained onto the schema —
203
+ * every table's `ctx.db` write path is denied without an RLS-covering
204
+ * procedure unless the table itself is `.public()` (see {@link TableIR.isPublic}).
205
+ * Absent when the schema never called `.rls("required")`.
206
+ */
207
+ rlsMode?: "required";
133
208
  tables: ReadonlyArray<TableIR>;
134
209
  /** All vector indexes (inline Shape A hoisted + standalone Shape B), flattened. */
135
210
  vectorIndexes: ReadonlyArray<VectorIndexIR>;
@@ -179,6 +254,72 @@ interface MigrationIR {
179
254
  table: string;
180
255
  }
181
256
  /**
257
+ * A `defineShape({...})` declaration discovered in `lunora/shapes.ts`
258
+ * (local-first sync engine, Phase 7). The emitted `LUNORA_SHAPES` registry keys
259
+ * on {@link ShapeIR.exportName}; the generated DO's `resolveShape` override
260
+ * dispatches a `shape_subscribe` to the matching registered shape. Discovery is
261
+ * marker-driven (the `__lunoraShape` brand) — no field metadata is lifted here
262
+ * because the runtime object (`columns`/`compileWhere`) carries the authority.
263
+ */
264
+ interface ShapeIR {
265
+ /** Export binding name — the shape's registry key and import member. */
266
+ exportName: string;
267
+ /** Path relative to `&lt;projectRoot>/lunora/` without extension — always `"shapes"`. */
268
+ filePath: string;
269
+ /**
270
+ * The `table` string literal from the `defineShape({ table })` call, lifted
271
+ * only for static advisor lints (the runtime object stays authoritative).
272
+ * `undefined` when `table` is not a plain string literal — lints skip those.
273
+ */
274
+ table?: string;
275
+ }
276
+ /**
277
+ * The single `defineIdentity({...})` claim contract discovered in
278
+ * `lunora/identity.ts`. Discovery is **marker-driven** (the `__lunoraIdentity`
279
+ * brand, exactly like {@link ShapeIR}) — no claim metadata is lifted here
280
+ * because the emitted `_generated/server.ts` recovers the claim *type* from the
281
+ * declaration itself (`InferIdentity` over the contract's `typeof`), and the
282
+ * runtime object (`validate`/`onInvalid`) carries the authority at the boundary.
283
+ * Exactly one per app; absent ⇒ generated output is byte-identical to today.
284
+ */
285
+ interface IdentityIR {
286
+ /** Export binding name — the namespace member `_generated/server.ts` reads via `typeof`. */
287
+ exportName: string;
288
+ }
289
+ /**
290
+ * A `defineMutator({...})` declaration discovered in `lunora/mutators.ts`
291
+ * (local-first sync engine, Phase 7). The emitted registry registers the
292
+ * authoritative `server` impl into the DO's `LUNORA_FUNCTIONS` table (so
293
+ * `handleRpc` transaction-wraps it) and records its path in
294
+ * `LUNORA_MUTATOR_PATHS` so the DO's `isCustomMutator` override routes the
295
+ * client-watermark push protocol. The client `client` impl is split into the
296
+ * browser bundle separately — only the path crosses to the server side.
297
+ */
298
+ interface MutatorIR {
299
+ /** Export binding name — the mutator's registry key and import member. */
300
+ exportName: string;
301
+ /** Path relative to `&lt;projectRoot>/lunora/` without extension — always `"mutators"`. */
302
+ filePath: string;
303
+ }
304
+ /**
305
+ * A whole-row `ctx.db.replace(id, document)` write discovered inside a custom
306
+ * mutator's inline `server` impl (`lunora/mutators.ts`) — the input the
307
+ * `mutator_full_row_replace` advisor lint consumes. A `replace` overwrites the
308
+ * entire row, so a concurrent edit to a different column on a synced table is
309
+ * clobbered; `ctx.db.patch(id, { field })` merges at the column level instead.
310
+ * Structurally identical to `AdvisorMutatorWrite` so it passes straight through
311
+ * to the advisor without conversion, exactly as `InsertWriteIR` does for
312
+ * `AdvisorInsertWrite`.
313
+ */
314
+ interface MutatorWriteIR {
315
+ /** The mutator export whose `server` impl performs the replace, e.g. `renameChannel`. */
316
+ exportName: string;
317
+ /** Openable source path the replace appears in — always `lunora/mutators.ts`. */
318
+ file: string;
319
+ /** 1-based line of the `replace(...)` call. */
320
+ line: number;
321
+ }
322
+ /**
182
323
  * A single cron job lifted from a `cronJobs()` builder in `lunora/crons.ts`.
183
324
  * Mirrors `@lunora/scheduler`'s `CronJob`: {@link CronJobIR.cron} is the compiled
184
325
  * standard cron expression, {@link CronJobIR.functionPath} is the target
@@ -288,6 +429,79 @@ interface WorkflowIR {
288
429
  * definition overrides it.
289
430
  */
290
431
  name: string;
432
+ /**
433
+ * Durable step labels lifted from the handler body — the first string-literal
434
+ * argument of every `ctx.step.do` / `.sleep` / `.sleepUntil` / `.waitForEvent`
435
+ * call. Feeds the duplicate-step-name lint, which flags a name used twice
436
+ * (Cloudflare memoizes by name, so the second call silently returns the
437
+ * first's cached result). Calls with a non-literal name are omitted (not
438
+ * statically comparable).
439
+ */
440
+ steps: ReadonlyArray<WorkflowStepIR>;
441
+ }
442
+ /** One durable step call lifted from a workflow handler body (the use side of {@link WorkflowIR.steps}). */
443
+ interface WorkflowStepIR {
444
+ /** 1-based line of the durable step call. */
445
+ line: number;
446
+ /** The native step method invoked: `do` / `sleep` / `sleepUntil` / `waitForEvent`. */
447
+ method: string;
448
+ /** The step's static label (the first string-literal argument). */
449
+ name: string;
450
+ }
451
+ /**
452
+ * A queue lifted from a `defineQueue()` export in `lunora/queues.ts`. Carries
453
+ * what the emitters and the config layer need to wire the typed `ctx.queues`
454
+ * producer, the generated worker `queue()` dispatch, and the wrangler
455
+ * `queues.producers[]` / `queues.consumers[]` entries. Like workflows, a queue
456
+ * is NOT a Durable Object — wrangler gets only `queues.*` entries. Names are
457
+ * derived via `@lunora/queue`'s shared helpers so codegen and the config layer
458
+ * can never disagree.
459
+ */
460
+ interface QueueIR {
461
+ /** The Cloudflare `Queue` producer binding name, e.g. `QUEUE_EMAIL`. */
462
+ bindingName: string;
463
+ /** The `lunora/queues.ts` export name, e.g. `emailQueue`. */
464
+ exportName: string;
465
+ /** How the queue is consumed: `"push"` (a worker `queue()` handler) or `"pull"` (external HTTP). */
466
+ mode: "pull" | "push";
467
+ /**
468
+ * The stable wrangler queue name (`queues.producers[].queue`). Defaults to
469
+ * the kebab-cased export name (`emailQueue` → `email-queue`); a static
470
+ * `name:` literal in the definition overrides it.
471
+ */
472
+ name: string;
473
+ /** Push-consumer batch/retry tuning, mirrored onto the wrangler `queues.consumers[]` entry. */
474
+ tuning: {
475
+ deadLetterQueue?: string;
476
+ maxBatchSize?: number;
477
+ maxBatchTimeout?: number;
478
+ maxRetries?: number;
479
+ retryDelay?: number;
480
+ };
481
+ }
482
+ /**
483
+ * The feature-flag provider declared by the default export of `lunora/flags.ts`
484
+ * (`defineFlags({ provider, … })`). Discovery is **metadata-only** — codegen
485
+ * imports the real module at runtime for the provider value; this IR exists so
486
+ * the config layer can reconcile/validate the wrangler `flagship` binding when
487
+ * the app uses Flagship in binding mode. A `custom` provider (any other
488
+ * OpenFeature factory) carries no binding to reconcile.
489
+ */
490
+ interface FlagsIR {
491
+ /**
492
+ * The wrangler `flagship[].binding` name — set **only** for a flagship
493
+ * `provider` in binding mode (`flagshipProvider({ binding: "FLAGS" })`). The
494
+ * config layer hints/validates a matching `flagship` binding from this.
495
+ */
496
+ bindingName?: string;
497
+ /**
498
+ * Flagship operating mode — `"binding"` (wrangler binding, needs a
499
+ * `flagship` entry) or `"http"` (no binding); `undefined` for a `custom`
500
+ * provider or when the mode can't be read statically.
501
+ */
502
+ mode?: "binding" | "http";
503
+ /** `"flagship"` when the provider is `flagshipProvider(...)`, else `"custom"` (any other OpenFeature provider factory). */
504
+ provider: "custom" | "flagship";
291
505
  }
292
506
  /**
293
507
  * A `ctx.workflows.get("name")…` call discovered in a function body — the
@@ -488,6 +702,30 @@ interface MaskMetadataIR {
488
702
  columns: MaskColumnMetadataIR[];
489
703
  }
490
704
  /**
705
+ * One masked column whose `mask(policies)` strategy is a statically-known
706
+ * literal (`"hash"` or `"redact"`) — the `mask_weak_hash_strategy_on_pii` lint
707
+ * input. Unlike {@link MaskColumnMetadataIR} (app-wide, deduped by `(table,
708
+ * column)`, studio-preview evidence), this is per declaration site (file + line
709
+ * + enclosing export), undeduped, so the lint can point at the exact
710
+ * `mask(...)` call that applies a weak strategy. A `MaskFn` (custom, non-literal)
711
+ * strategy carries no lint-relevant signal and is never recorded here.
712
+ * Structurally identical to `AdvisorMaskStrategy`.
713
+ */
714
+ interface MaskStrategyIR {
715
+ /** Masked column name. */
716
+ column: string;
717
+ /** Export binding name of the procedure whose `.use(mask(...))` chain declared this column, or `"&lt;module>"` when declared at file scope. */
718
+ exportName: string;
719
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
720
+ file: string;
721
+ /** 1-based line of the masked column's strategy property. */
722
+ line: number;
723
+ /** The statically-known strategy literal: `"hash"` or `"redact"`. */
724
+ strategy: string;
725
+ /** Logical table the masked column belongs to. */
726
+ table: string;
727
+ }
728
+ /**
491
729
  * One statically-readable policy entry from an `rls([...])` array literal,
492
730
  * surfaced to the studio's read-only RLS inspector via the generated
493
731
  * `rlsPolicies()` hook. Captures the policy's `table` + `on` operation and the
@@ -596,12 +834,18 @@ interface ProcedureMiddlewareIR {
596
834
  callsMail: boolean;
597
835
  /** Export binding name of the procedure (e.g. `signUp`). */
598
836
  exportName: string;
837
+ /** `true` when the handler fans work out to a privileged, cost-bearing dispatch surface (scheduler `runAfter`/`runAt`, a queue producer send, or a workflow create). Feeds the privileged-fanout lint. */
838
+ fanOut: boolean;
599
839
  /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
600
840
  file: string;
601
841
  /** Registration kind — only `mutation`/`action` are write-shaped; `query` is read-only. */
602
842
  kind: "action" | "mutation" | "query";
843
+ /** `true` when the handler runs an AI generation (`generateText`/`streamText`/`generateObject`/`streamObject`) with no `maxOutputTokens` bound in its config literal. Feeds the `ai_unbounded_generation_public` lint. */
844
+ unboundedAiGeneration: boolean;
603
845
  /** `true` when the chain carries `.use(verifyTurnstile(...))` or a `protectPublic({ captcha })` bundle. */
604
846
  usesCaptcha: boolean;
847
+ /** `true` when the handler calls `ctx.db.insertManyUnsafe(...)`, bypassing validators and triggers. Feeds the `insert_many_unsafe_user_data` lint. */
848
+ usesInsertManyUnsafe: boolean;
605
849
  /** `true` when the chain carries `.use(mask(...))`. */
606
850
  usesMask: boolean;
607
851
  /** `true` when the chain carries `.use(rateLimit(...))` or a `protectPublic({ rateLimit })` bundle. */
@@ -634,6 +878,27 @@ interface ArgumentValidatorIR {
634
878
  unboundedStringArgs: string[];
635
879
  }
636
880
  /**
881
+ * One factory/constructor call in `lunora/` whose config object literal a
882
+ * security lint inspects for a present-or-absent key — the shared input for the
883
+ * config-call security lints (payment authorize, inbound-mail verify, rate-limit
884
+ * store, browser private-targets). Structurally identical to `AdvisorConfigCall`
885
+ * so it passes straight through to the advisor without conversion.
886
+ */
887
+ interface ConfigCallIR {
888
+ /** `true` when the config argument was a static object literal the feeder could read. */
889
+ analyzable: boolean;
890
+ /** The factory function or constructor name at the call site, e.g. `createPayment` / `RateLimiter`. */
891
+ callee: string;
892
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
893
+ file: string;
894
+ /** 1-based line of the call site, or `0` when unknown. */
895
+ line: number;
896
+ /** Keys present in the config object literal (empty when not `analyzable`). */
897
+ presentKeys: string[];
898
+ /** Keys in the config object literal explicitly assigned the literal `true`. */
899
+ trueKeys: string[];
900
+ }
901
+ /**
637
902
  * One secret-shaped string literal discovered in `lunora/` source — the
638
903
  * `hardcoded_secret` lint input. Complements the pre-commit `vis secrets` scan by
639
904
  * surfacing the same class of finding in-IDE via the studio Advisors table.
@@ -667,6 +932,308 @@ interface SqlInterpolationIR {
667
932
  line: number;
668
933
  }
669
934
  /**
935
+ * One `ctx.fetch(url, …)` call inside an action whose URL argument is derived
936
+ * from the handler's `args` — the `action_fetch_ssrf` lint input. `ctx.fetch` is
937
+ * the action-only outbound-request escape hatch with no host allowlist, so a URL
938
+ * assembled from request input is a server-side request forgery vector (cloud
939
+ * metadata endpoints, internal services). Only arg-derived URLs reach here; a
940
+ * fixed literal or a URL built from config/`ctx.*` is not recorded. Structurally
941
+ * identical to `AdvisorArgumentDerivedFetch`.
942
+ */
943
+ interface ArgumentDerivedFetchIR {
944
+ /** Export binding name of the action performing the `ctx.fetch` call. */
945
+ exportName: string;
946
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
947
+ file: string;
948
+ /** 1-based line of the `ctx.fetch` call, or `0` when unknown. */
949
+ line: number;
950
+ }
951
+ /**
952
+ * One `ctx.kv.&lt;method>(key, …)` call whose namespace key is derived from the
953
+ * handler's `args` with no server-side scoping — the `kv_unscoped_user_key_idor`
954
+ * lint input. Workers KV is a single flat namespace, so a key taken straight from
955
+ * request input lets any caller read, overwrite, or delete another user's entry
956
+ * (IDOR). Only arg-derived, unscoped keys reach here; a fixed literal, or a key
957
+ * prefixed with a server-trusted identity (`${ctx.auth.userId}:…` — references
958
+ * `ctx`, so treated as scoped), is not recorded. `list` is excluded (it takes a
959
+ * prefix, not a per-entry key). Structurally identical to `AdvisorKvKeyAccess`.
960
+ */
961
+ interface KvKeyAccessIR {
962
+ /** Export binding name of the procedure performing the `ctx.kv` access. */
963
+ exportName: string;
964
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
965
+ file: string;
966
+ /** 1-based line of the `ctx.kv` call, or `0` when unknown. */
967
+ line: number;
968
+ /** The `ctx.kv` method invoked: `get` / `getRaw` / `getWithMetadata` / `put` / `delete`. */
969
+ method: string;
970
+ }
971
+ /**
972
+ * One `ctx.db` write (`insert` / `replace` / `patch` / `insertManyUnsafe`) that sets
973
+ * an ownership / identity column — `userId`, `ownerId`, `tenantId`, and the like —
974
+ * from the handler's `args` instead of the server-trusted identity. The
975
+ * `owner_field_from_args_not_auth` lint input: the ownership column decides who a
976
+ * row belongs to, so a value taken from request input lets any caller write rows
977
+ * owned by another user or tenant (the act-as-any-user / cross-tenant IDOR vector).
978
+ * A column stamped from `ctx.*`, or set to a fixed literal, is not recorded; only an
979
+ * arg-derived identity write reaches here. Structurally identical to
980
+ * `AdvisorOwnerFieldWrite`.
981
+ */
982
+ interface OwnerFieldWriteIR {
983
+ /** Export binding name of the procedure performing the write. */
984
+ exportName: string;
985
+ /** The identity column being written from `args` (e.g. `userId`). */
986
+ field: string;
987
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
988
+ file: string;
989
+ /** 1-based line of the `ctx.db` write call, or `0` when unknown. */
990
+ line: number;
991
+ /** The `ctx.db` write method (`insert` / `replace` / `patch` / `insertManyUnsafe`). */
992
+ method: string;
993
+ }
994
+ /**
995
+ * One `ctx.storage.&lt;bucket>.&lt;method>(key, …)` call whose R2 object key is derived
996
+ * from the handler's `args` with no server-side scoping — the
997
+ * `storage_key_from_user_args` lint input. The bucket read/write/URL/delete methods
998
+ * key by their first argument, so an object key taken straight from request input is
999
+ * object-level IDOR (read/overwrite/delete anyone's object). A key referencing a
1000
+ * server-trusted `ctx.*` value (e.g. `${ctx.auth.userId}/…`) is treated as scoped
1001
+ * and is not recorded; only an arg-derived, `ctx`-free key reaches here.
1002
+ * Structurally identical to `AdvisorStorageKeyAccess`.
1003
+ */
1004
+ interface StorageKeyAccessIR {
1005
+ /** Export binding name of the procedure performing the storage call. */
1006
+ exportName: string;
1007
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1008
+ file: string;
1009
+ /** 1-based line of the storage call, or `0` when unknown. */
1010
+ line: number;
1011
+ /** The bucket method invoked with the arg-derived key, e.g. `get` / `put` / `delete` / `download`. */
1012
+ method: string;
1013
+ }
1014
+ /**
1015
+ * One `ctx.containers.&lt;exportName>.get(name, …)` call whose instance key is derived
1016
+ * from the handler's `args` with no server-side scoping — the
1017
+ * `container_instance_key_from_user_input` lint input. Each container definition's
1018
+ * `.get(name)` accessor routes to one instance per `name`, so a key taken straight from
1019
+ * request input lets any caller reach another tenant's container (a cross-tenant IDOR). A
1020
+ * fixed literal key, or one derived from a server-trusted identity (`${ctx.auth.userId}` —
1021
+ * references `ctx`, so treated as scoped), is not recorded; only an arg-derived, unscoped
1022
+ * key reaches here. `.any()`/`.pool()` take no key and are not sinks. Structurally
1023
+ * identical to `AdvisorContainerKeyAccess`.
1024
+ */
1025
+ interface ContainerKeyAccessIR {
1026
+ /** Export binding name of the procedure performing the `ctx.containers` access. */
1027
+ exportName: string;
1028
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1029
+ file: string;
1030
+ /** 1-based line of the `ctx.containers.*.get` call, or `0` when unknown. */
1031
+ line: number;
1032
+ /** The container accessor method invoked — always `get`. */
1033
+ method: string;
1034
+ }
1035
+ /**
1036
+ * One `ctx.ai.run(model, …)` call whose model-id argument is derived from the handler's
1037
+ * `args` with no server-side scoping — the `ai_raw_run_escape_hatch` lint input.
1038
+ * `ctx.ai.run` is the raw Workers AI binding passthrough, bypassing the typed
1039
+ * `ctx.ai.model(...)` + AI-SDK layer (`generateText`/`streamText`/…) that caps output and
1040
+ * enforces a schema, so an arg-derived model id lets any caller select an arbitrary model.
1041
+ * A fixed literal model, or one scoped by a server-trusted `ctx.*` value, is not recorded;
1042
+ * only an arg-derived, unscoped model id reaches here (an arg-derived `inputs` argument is
1043
+ * normal usage and is never inspected). Structurally identical to `AdvisorAiRawRun`.
1044
+ */
1045
+ interface AiRawRunIR {
1046
+ /** Export binding name of the procedure performing the `ctx.ai.run` call. */
1047
+ exportName: string;
1048
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1049
+ file: string;
1050
+ /** 1-based line of the `ctx.ai.run` call, or `0` when unknown. */
1051
+ line: number;
1052
+ }
1053
+ /**
1054
+ * One `ctx.vectors.&lt;method>(indexName, input)` call whose `input.namespace` is derived
1055
+ * from the handler's `args` with no server-side scoping — the
1056
+ * `vectors_namespace_from_user_input` lint input. A Vectorize namespace partitions one
1057
+ * index into isolated sub-collections, so a namespace taken straight from request input
1058
+ * lets any caller read or poison another tenant's vectors. A fixed literal namespace, or
1059
+ * one prefixed with a server-trusted identity (`${ctx.auth.orgId}` — references `ctx`, so
1060
+ * treated as scoped), is not recorded; only an arg-derived, unscoped namespace reaches
1061
+ * here. Structurally identical to `AdvisorVectorNamespaceAccess`.
1062
+ */
1063
+ interface VectorNamespaceAccessIR {
1064
+ /** Export binding name of the procedure performing the `ctx.vectors` access. */
1065
+ exportName: string;
1066
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1067
+ file: string;
1068
+ /** 1-based line of the `ctx.vectors` call, or `0` when unknown. */
1069
+ line: number;
1070
+ /** The `ctx.vectors` method invoked: `query` / `upsert` / `upsertMany`. */
1071
+ method: string;
1072
+ }
1073
+ /**
1074
+ * One `ctx.mail`/`ctx.email` `send`/`queue` call whose recipient field (`to`/`cc`/`bcc`)
1075
+ * is derived from the handler's `args` with no server-side scoping — the
1076
+ * `mail_recipient_from_request_input` lint input. A recipient taken straight from request
1077
+ * input turns the deployment into an open relay / spam amplifier (any caller can direct
1078
+ * mail to an arbitrary address). A fixed literal recipient, or one scoped by a
1079
+ * server-trusted `ctx.*` value (e.g. `ctx.auth.user.email`), is not recorded; only an
1080
+ * arg-derived, unscoped recipient reaches here. Structurally identical to
1081
+ * `AdvisorMailRecipientAccess`.
1082
+ */
1083
+ interface MailRecipientAccessIR {
1084
+ /** Export binding name of the procedure performing the `ctx.mail`/`ctx.email` call. */
1085
+ exportName: string;
1086
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1087
+ file: string;
1088
+ /** 1-based line of the `ctx.mail`/`ctx.email` call, or `0` when unknown. */
1089
+ line: number;
1090
+ /** The mailer method invoked: `send` / `queue`. */
1091
+ method: string;
1092
+ }
1093
+ /**
1094
+ * One `ctx.browser.&lt;method>(url, …)` call whose navigation URL (`arguments[0]`)
1095
+ * is derived from the handler's `args` with no server-side scoping — the
1096
+ * `browser_user_url_without_allowlist` lint input. The lint additionally
1097
+ * cross-references `createBrowser` config-call evidence to suppress findings
1098
+ * when the browser is hardened with an `allowedHosts` allowlist or
1099
+ * `resolveDns`. Structurally identical to `AdvisorBrowserUrlAccess`.
1100
+ */
1101
+ interface BrowserUrlAccessIR {
1102
+ /** Export binding name of the procedure performing the `ctx.browser` call. */
1103
+ exportName: string;
1104
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1105
+ file: string;
1106
+ /** 1-based line of the `ctx.browser` call, or `0` when unknown. */
1107
+ line: number;
1108
+ /** The browser method invoked: `content` / `pdf` / `scrape` / `screenshot`. */
1109
+ method: string;
1110
+ }
1111
+ /**
1112
+ * One runtime container-override call: a `&lt;handle>.start({ enableInternet: true, … })`
1113
+ * launch override, or a `&lt;handle>.egress.&lt;method>(...)` runtime firewall mutation
1114
+ * (`allow` / `deny` / `setAllowed`) — the `container_start_enable_internet_override`
1115
+ * and `container_runtime_egress_relaxation` lint input. Both shapes re-open network
1116
+ * access the static `defineContainer` declaration (and its `container_public_internet`
1117
+ * lint) assumes is locked down. Matched structurally by call shape, independent of the
1118
+ * receiver's resolved type. Structurally identical to `AdvisorContainerOverride`.
1119
+ */
1120
+ interface ContainerOverrideIR {
1121
+ /** e.g. the egress method name, or `"enableInternet: true"`. */
1122
+ detail: string;
1123
+ /** Export binding name of the procedure performing the call. */
1124
+ exportName: string;
1125
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1126
+ file: string;
1127
+ /** Which override shape matched. */
1128
+ kind: "egress_relaxation" | "enable_internet";
1129
+ /** 1-based line of the call, or `0` when unknown. */
1130
+ line: number;
1131
+ }
1132
+ /**
1133
+ * One `buildImageDeliveryUrl({ key, … })` call (`@lunora/bindings/images`) whose
1134
+ * `key` — the CDN transform's source image, an absolute URL or an
1135
+ * origin-relative key — is derived from the handler's `args` with no
1136
+ * server-side scoping — the `images_url_source_from_user_input` lint input.
1137
+ * `ctx.images.transform`/`info` take image *bytes*, never a URL, so they are not
1138
+ * sinks; only the `key` of `buildImageDeliveryUrl` accepts a URL-or-key source
1139
+ * and is inspected. An arg-derived `key` lets any caller point the CDN's
1140
+ * `/cdn-cgi/image/` transform at an attacker-chosen origin (SSRF / open proxy)
1141
+ * or at an arbitrary key under the account's own store. A fixed literal, or a
1142
+ * key scoped by a server-trusted `ctx.*` value, is not recorded. Structurally
1143
+ * identical to `AdvisorImageDeliveryUrlAccess`.
1144
+ */
1145
+ interface ImageDeliveryUrlAccessIR {
1146
+ /** Export binding name of the procedure performing the `buildImageDeliveryUrl` call. */
1147
+ exportName: string;
1148
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1149
+ file: string;
1150
+ /** 1-based line of the `buildImageDeliveryUrl` call, or `0` when unknown. */
1151
+ line: number;
1152
+ }
1153
+ /**
1154
+ * One `createAuth({...})` call's configuration snapshot — the shared input for
1155
+ * the five `auth_*` security lints (trusted-origins wildcard, CSRF check
1156
+ * disabled, secure cookies disabled, email verification disabled, session
1157
+ * freshAge zero). Matched by callee NAME (an `import`-agnostic, fail-closed
1158
+ * convention the other feeders share), so a re-export or alias still resolves.
1159
+ * When the config argument isn't a statically-analyzable object literal (a
1160
+ * top-level spread, or not an object literal at all), `analyzable` is `false`
1161
+ * and every boolean fact defaults to its SAFE (not-flagged) value — an opaque
1162
+ * config can't be relied on either way. Structurally identical to
1163
+ * `AdvisorAuthConfig`.
1164
+ */
1165
+ interface AuthConfigIR {
1166
+ /** `true` when the call's config argument was a static object literal the feeder could read. */
1167
+ analyzable: boolean;
1168
+ /** `advanced.disableCSRFCheck === true`. */
1169
+ disableCsrfCheck: boolean;
1170
+ /** `emailAndPassword.enabled === true`. */
1171
+ emailPasswordEnabled: boolean;
1172
+ /** Export binding name enclosing the `createAuth(...)` call. */
1173
+ exportName: string;
1174
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1175
+ file: string;
1176
+ /** 1-based line of the `createAuth(...)` call, or `0` when unknown. */
1177
+ line: number;
1178
+ /** `emailAndPassword.requireEmailVerification === true` present. */
1179
+ requireEmailVerification: boolean;
1180
+ /** `advanced.useSecureCookies === false`. */
1181
+ secureCookiesDisabled: boolean;
1182
+ /** `session.freshAge === 0` (explicit literal). */
1183
+ sessionFreshAgeZero: boolean;
1184
+ /** `trustedOrigins` array literal contains a `"*"` element. */
1185
+ trustedOriginsWildcard: boolean;
1186
+ }
1187
+ /**
1188
+ * One `rateLimit`/`dbRateLimit` middleware call (`@lunora/ratelimit`) whose
1189
+ * `key` selector — the per-caller rate-limit sub-key, `(ctx) => string |
1190
+ * undefined` — is derived from the handler's `args` with no server-side
1191
+ * scoping (no reference to the trusted `ctx` binding anywhere in the selector)
1192
+ * — the `ratelimit_key_spoofable_or_global` lint input. A key an attacker
1193
+ * controls lets them rotate it per request and bypass the limit entirely,
1194
+ * defeating its purpose. A selector scoped by `ctx` (e.g. `ctx.auth.userId`,
1195
+ * `ctx.ip`), or one with no `args` reference at all (a fixed/global bucket —
1196
+ * the "no key" case this lint deliberately does not flag, to keep it low-FP),
1197
+ * is not recorded. Structurally identical to `AdvisorRatelimitKeySelector`.
1198
+ */
1199
+ interface RatelimitKeySelectorIR {
1200
+ /** The `rateLimit`/`dbRateLimit` callee invoked. */
1201
+ callee: string;
1202
+ /** Export binding name of the procedure whose `.use(...)` chain carries the call. */
1203
+ exportName: string;
1204
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1205
+ file: string;
1206
+ /** The rate limit's `name` argument (the second positional argument), or `""` when not a string literal. */
1207
+ limitName: string;
1208
+ /** 1-based line of the `rateLimit`/`dbRateLimit` call, or `0` when unknown. */
1209
+ line: number;
1210
+ }
1211
+ /**
1212
+ * One payload-derived privileged dispatch — a `ctx.run`/`context.run` back into a
1213
+ * Lunora function from inside a `defineQueue` push handler or a `defineWorkflow`
1214
+ * handler, whose args reference the handler's untrusted payload (`context.params`
1215
+ * for a workflow, a `for (… of batch.messages)` body for a queue) — the
1216
+ * `privileged_dispatch_unvalidated_payload` lint input. Both handler kinds run
1217
+ * under the **system identity** (RLS disabled), so forwarding attacker-influenced
1218
+ * payload into the dispatch bypasses the target's row policy. The resolved
1219
+ * `targetFile`/`targetExport` let the lint join RLS-procedure evidence and fire
1220
+ * only for RLS-gated targets. Structurally identical to `AdvisorPrivilegedDispatch`.
1221
+ */
1222
+ interface PrivilegedDispatchIR {
1223
+ /** `"queue"` for a `defineQueue` handler, `"workflow"` for a `defineWorkflow` handler. */
1224
+ dispatchKind: "queue" | "workflow";
1225
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1226
+ file: string;
1227
+ /** Export binding name of the handler performing the dispatch. */
1228
+ handlerExport: string;
1229
+ /** 1-based line of the dispatch call, or `0` when unknown. */
1230
+ line: number;
1231
+ /** Export name of the dispatched target (`send` in `api.messages.send`). */
1232
+ targetExport: string;
1233
+ /** File path of the dispatched target relative to `lunora/` (`messages` in `api.messages.send`). */
1234
+ targetFile: string;
1235
+ }
1236
+ /**
670
1237
  * One discovered `httpRoute.&lt;verb>("/admin/…")` route on an admin/privileged-looking
671
1238
  * path, with whether its builder chain references an auth/admin guard — the
672
1239
  * `admin_route_without_guard` lint input. Structurally identical to
@@ -684,6 +1251,316 @@ interface AdminRouteIR {
684
1251
  /** `true` when the handler body references an auth/session/admin guard (`ctx.auth`, `getSession`, `requireAdmin`, …). */
685
1252
  usesGuard: boolean;
686
1253
  }
1254
+ /**
1255
+ * One tracked `ctx.storage.&lt;bucket>.&lt;method>(...)` upload/signing call — the
1256
+ * shared input for the storage config-hygiene security lints
1257
+ * (`storage_upload_without_content_type_allowlist`, `storage_upload_without_max_size`,
1258
+ * `storage_generate_upload_url_no_content_type_pin`, `storage_presigned_url_for_private_content`).
1259
+ * `upload`/`store` carry the `UploadOptions` guards (`allowedContentTypes` /
1260
+ * `maxSize`); `generateUploadUrl` carries the signed-PUT `contentType` pin;
1261
+ * `getPresignedUrl`/`getSignedUrl` carry a statically-known `expiresInSeconds`
1262
+ * literal. `presentKeys` is empty (and `expiresInSeconds` unset) when the
1263
+ * options argument was absent, a non-literal, or a spread — see `analyzable`.
1264
+ * Structurally identical to `AdvisorStorageUpload`.
1265
+ */
1266
+ interface StorageUploadIR {
1267
+ /** `true` when the call's options-object argument (or its deliberate absence) was statically resolvable. */
1268
+ analyzable: boolean;
1269
+ /** Numeric literal value of an `expiresInSeconds` option, when statically known (`getSignedUrl` / `getPresignedUrl` only). */
1270
+ expiresInSeconds?: number;
1271
+ /** Export binding name of the procedure performing the call. */
1272
+ exportName: string;
1273
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1274
+ file: string;
1275
+ /** 1-based line of the call, or `0` when unknown. */
1276
+ line: number;
1277
+ /** The `ctx.storage` method invoked. */
1278
+ method: "generateUploadUrl" | "getPresignedUrl" | "getSignedUrl" | "store" | "upload";
1279
+ /** Options-object keys present at the call site (empty when not `analyzable`, or when no options argument was passed). */
1280
+ presentKeys: string[];
1281
+ }
1282
+ /**
1283
+ * One discovered `httpAction`/`httpRoute` handler in `lunora/` that performs a
1284
+ * side effect (`ctx.runMutation` / `ctx.runAction` / a `ctx.db.{insert,patch,
1285
+ * replace,delete,insertManyUnsafe}` write) from the HTTP edge, with whether it
1286
+ * reads `ctx.auth` — the `http_action_missing_auth_guard` lint input. A handler
1287
+ * that mutates state or dispatches an action without ever consulting the request
1288
+ * identity is an unauthenticated write bypassing identity/RLS. Only handlers with
1289
+ * a statically-resolvable inline body and `ctx` binding are recorded (fail-safe
1290
+ * under-report); read-only handlers are never recorded. Structurally identical to
1291
+ * `AdvisorHttpActionGuard`.
1292
+ */
1293
+ interface HttpActionGuardIR {
1294
+ /** Export binding name of the handler (or `"&lt;module>"` when mounted inline / not a named binding). */
1295
+ exportName: string;
1296
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1297
+ file: string;
1298
+ /** Which HTTP surface the handler is: a raw `httpAction` or a typed `httpRoute` route. */
1299
+ kind: "httpAction" | "httpRoute";
1300
+ /** 1-based line of the handler call, or `0` when unknown. */
1301
+ line: number;
1302
+ /** For an `httpRoute`, the uppercased verb (`"POST"`); absent for a raw `httpAction`. */
1303
+ method?: string;
1304
+ /** `true` when the handler reads `ctx.auth` (a direct member access or a `const { auth } = ctx` destructure). */
1305
+ readsAuth: boolean;
1306
+ /** The first side effect found, as a stable label: `runMutation`, `runAction`, or `db.&lt;method>`. */
1307
+ sideEffect: string;
1308
+ }
1309
+ /**
1310
+ * One response-header write, inside an `httpAction` handler, whose value is derived
1311
+ * from raw request input (`request.headers`, `request.url`/query, `await
1312
+ * request.json()`) with no CR/LF sanitizer — the
1313
+ * `http_action_response_header_injection` lint input. A `Request`-derived string
1314
+ * placed verbatim into a response header lets a caller smuggle `\r\n` and inject
1315
+ * extra headers or split the response (header injection / response splitting). Only
1316
+ * sites whose value is request-tainted AND unguarded are recorded: a value routed
1317
+ * through a CR/LF guard (`isSafeHeaderValue`), a URL/URI encoder
1318
+ * (`encodeURIComponent`/`encodeURI`), a numeric coercion (`Number`/`parseInt`/
1319
+ * `parseFloat`), or `btoa` is treated as safe and never recorded (`String(...)` /
1320
+ * `.toString()` are NOT sanitizers — they don't strip CR/LF). Structurally
1321
+ * identical to `AdvisorHttpHeaderWrite`.
1322
+ */
1323
+ interface HttpHeaderWriteIR {
1324
+ /** Export binding name of the enclosing handler, or `"&lt;module>"` when mounted inline. */
1325
+ exportName: string;
1326
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1327
+ file: string;
1328
+ /** The header name being written (`"location"`), or `""` when the key is not a string literal. */
1329
+ headerName: string;
1330
+ /** 1-based line of the request-tainted header value. */
1331
+ line: number;
1332
+ /** How the header was written. */
1333
+ via: "headers-append" | "headers-ctor" | "headers-set" | "response-init";
1334
+ }
1335
+ /**
1336
+ * One rate-limit / Turnstile middleware call in `lunora/` — the
1337
+ * `ratelimit_middleware_fail_open` lint input. `rateLimit`/`dbRateLimit`
1338
+ * (`@lunora/ratelimit`) and `verifyTurnstileMiddleware` (`@lunora/auth`) each
1339
+ * accept a `failOpen` escape hatch that admits every request when the
1340
+ * limiter/siteverify is unavailable; `failOpen` is `true` only when the options
1341
+ * literal set it to the boolean literal `true` (anything else is fail-closed).
1342
+ * The lint escalates a fail-open guard to a finding when the guarded procedure
1343
+ * (`exportName`/`limitName`) looks auth/payment-sensitive. Structurally
1344
+ * identical to `AdvisorFailOpenGuard`.
1345
+ */
1346
+ interface FailOpenGuardIR {
1347
+ /** The middleware factory at the call site: `rateLimit` / `dbRateLimit` / `verifyTurnstileMiddleware`. */
1348
+ callee: string;
1349
+ /** Export binding name of the procedure the guard is attached to, or `"&lt;module>"` at file scope. */
1350
+ exportName: string;
1351
+ /** `true` only when the options literal set `failOpen: true` as a boolean literal; a non-literal or absent option is treated as fail-closed. */
1352
+ failOpen: boolean;
1353
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1354
+ file: string;
1355
+ /** The rate-limit `name` (second string argument) for `rateLimit`/`dbRateLimit`; `""` for `verifyTurnstileMiddleware`. */
1356
+ limitName: string;
1357
+ /** 1-based line of the middleware call, or `0` when unknown. */
1358
+ line: number;
1359
+ }
1360
+ /**
1361
+ * One `ctx.flags.boolean("key", &lt;boolean-literal>)` read in `lunora/` — the
1362
+ * `flag_gates_security_with_unsafe_default` lint input. OpenFeature returns the
1363
+ * `defaultValue` when the provider errors, so a fail-open default on a
1364
+ * security-shaped key silently opens access during an outage. Only reads with a
1365
+ * statically-known string key and boolean-literal default are recorded; the lint
1366
+ * owns the security-shape + polarity judgment. Structurally identical to
1367
+ * `AdvisorFlagSecurityDefault`.
1368
+ */
1369
+ interface FlagSecurityDefaultIR {
1370
+ /** The boolean-literal default returned on a provider outage (fail-open value). */
1371
+ defaultValue: boolean;
1372
+ /** Export binding name of the procedure performing the flag read, or `"&lt;module>"` at file scope. */
1373
+ exportName: string;
1374
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1375
+ file: string;
1376
+ /** The flag key — the first string-literal argument of `ctx.flags.boolean`. */
1377
+ key: string;
1378
+ /** 1-based line of the `ctx.flags.boolean` call, or `0` when unknown. */
1379
+ line: number;
1380
+ }
1381
+ /**
1382
+ * One `generateText` / `streamText` call in `lunora/` whose `tools` reach a
1383
+ * privileged side effect (a DB write, function dispatch, or outbound
1384
+ * fetch/mail/queue send). `userInputDerived` records whether the model input
1385
+ * (`prompt`/`messages`/`system`) flows from the handler's `args`; the
1386
+ * `ai_tool_side_effect_prompt_injection` lint fires only when it does.
1387
+ * Structurally identical to `AdvisorAiToolSideEffect`.
1388
+ */
1389
+ interface AiToolSideEffectIR {
1390
+ /** Export binding name of the procedure performing the call. */
1391
+ exportName: string;
1392
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1393
+ file: string;
1394
+ /** 1-based line of the generation call, or `0` when unknown. */
1395
+ line: number;
1396
+ /** The generation entrypoint invoked. */
1397
+ method: "generateText" | "streamText";
1398
+ /** The privileged side-effect sink a model-callable tool reaches (`ctx.db.insert`, `ctx.run`, `ctx.fetch`, …). */
1399
+ sideEffect: string;
1400
+ /** `true` when a model-input option is derived from the handler's `args` (a bare `args.x`, or a name destructured from `args`). */
1401
+ userInputDerived: boolean;
1402
+ }
1403
+ /**
1404
+ * One `&lt;receiver>.identity.&lt;key>` claim read in `lunora/`, where `&lt;receiver>` is
1405
+ * an RLS/mask policy `auth` (or `ctx.auth`/`context.auth`). `declared` records
1406
+ * whether `&lt;key>` is in the app's `defineIdentity({ ... })` contract (or the
1407
+ * always-present `userId`); the `identity_undeclared_claim_trusted` lint fires on
1408
+ * the undeclared reads. Emitted only when a resolvable identity contract exists.
1409
+ * Structurally identical to `AdvisorIdentityClaimRead`.
1410
+ */
1411
+ interface IdentityClaimReadIR {
1412
+ /** `true` when `key` is a declared claim (in the `defineIdentity` contract, or the always-present `userId`). */
1413
+ declared: boolean;
1414
+ /** Export binding name of the enclosing declaration (`&lt;module>` at file scope). */
1415
+ exportName: string;
1416
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1417
+ file: string;
1418
+ /** The claim key read off the identity bag. */
1419
+ key: string;
1420
+ /** 1-based line of the read, or `0` when unknown. */
1421
+ line: number;
1422
+ }
1423
+ /**
1424
+ * One payment webhook-adapter construction in `lunora/` (`createStripeAdapter` /
1425
+ * `createPolarAdapter`). `toleranceSeconds` carries the statically-known
1426
+ * `webhookToleranceSeconds` replay window when it is a plain numeric literal; the
1427
+ * payment-webhook wide-tolerance lint fires when it exceeds a conservative
1428
+ * ceiling. Structurally identical to `AdvisorPaymentWebhook`.
1429
+ */
1430
+ interface PaymentWebhookIR {
1431
+ /** The adapter factory invoked. */
1432
+ callee: "createPolarAdapter" | "createStripeAdapter";
1433
+ /** Export binding name of the enclosing declaration (`&lt;module>` at file scope). */
1434
+ exportName: string;
1435
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1436
+ file: string;
1437
+ /** 1-based line of the construction, or `0` when unknown. */
1438
+ line: number;
1439
+ /** Statically-known `webhookToleranceSeconds` literal, when present and a plain numeric literal. */
1440
+ toleranceSeconds?: number;
1441
+ }
1442
+ /**
1443
+ * One `ctx.db.&lt;table>.findMany({ includeDeleted })` list read whose
1444
+ * `includeDeleted` is either a hardcoded `true` or derived from the handler's
1445
+ * `args` — the `soft_delete_include_deleted_from_args` lint input. The lint joins
1446
+ * `table` against the schema's soft-delete tables and `visibility` against
1447
+ * `.public()` before flagging. Structurally identical to `AdvisorSoftDeleteRead`
1448
+ * so values pass straight through without conversion.
1449
+ */
1450
+ interface SoftDeleteReadIR {
1451
+ /** Export binding name of the procedure performing the read. */
1452
+ exportName: string;
1453
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1454
+ file: string;
1455
+ /** `true` when `includeDeleted` was derived from the handler's `args` (any caller can flip it). */
1456
+ fromArgs: boolean;
1457
+ /** `true` when `includeDeleted` was a hardcoded `true` literal (always resurfaces soft-deleted rows). */
1458
+ hardcodedTrue: boolean;
1459
+ /** 1-based line of the read call. */
1460
+ line: number;
1461
+ /** Table read, or `""` when the table-arg form's first argument wasn't a string literal. */
1462
+ table: string;
1463
+ /** `"internal"` for `internalQuery` / `internalMutation` / `internalAction`. */
1464
+ visibility: "internal" | "public";
1465
+ }
1466
+ /**
1467
+ * One `ctx.db.&lt;table>.findMany({ with: { &lt;rel> } })` relation-hydrating list read
1468
+ * — the `masked_relation_leak_via_with` lint input. Column masking does not
1469
+ * descend into `with`-hydrated relations, so a masked table surfaced only through
1470
+ * a `with` on an unprotected parent read is returned in the clear. The lint
1471
+ * resolves each relation accessor to its target table and joins it against the
1472
+ * discovered mask evidence before flagging. Structurally identical to
1473
+ * `AdvisorRelationLoad` so values pass straight through without conversion.
1474
+ */
1475
+ interface RelationLoadIR {
1476
+ /** Export binding name of the procedure performing the read. */
1477
+ exportName: string;
1478
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1479
+ file: string;
1480
+ /** 1-based line of the read call. */
1481
+ line: number;
1482
+ /** Parent table the read targets, or `""` when the table-arg form's first argument wasn't a string literal. */
1483
+ parentTable: string;
1484
+ /** Relation accessor names named in the read's `with: { … }` map — matched against the parent table's declared relations. */
1485
+ relations: string[];
1486
+ /** `"internal"` for `internalQuery` / `internalMutation` / `internalAction`. */
1487
+ visibility: "internal" | "public";
1488
+ }
1489
+ /**
1490
+ * One `query` handler whose `return` hands back the raw rows of a table — the
1491
+ * result of a `ctx.db.&lt;table>.findMany()` / `.findFirst()` / `.get()` read, or a
1492
+ * `ctx.db.query("&lt;table>")…collect()` fluent chain — returned directly (or through
1493
+ * one local `const` hop) with no hand-built projection. The
1494
+ * `output_projection_missing_on_public_read` lint keeps only `visibility ===
1495
+ * "public"` rows with no `.output(...)` / `.use(mask(...))` on the chain, then
1496
+ * joins `table` against the schema and flags one whose columns are PII-named.
1497
+ * Structurally identical to `AdvisorRawRowReturn` so values pass straight through
1498
+ * without conversion.
1499
+ */
1500
+ interface RawRowReturnIR {
1501
+ /** Export binding name of the query returning the raw rows. */
1502
+ exportName: string;
1503
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1504
+ file: string;
1505
+ /** 1-based line of the `return` (or concise-body) expression. */
1506
+ line: number;
1507
+ /** Table whose raw rows are returned, or `""` when the read's table wasn't a string literal. */
1508
+ table: string;
1509
+ /** `true` when the procedure's builder chain carries a `.use(mask(...))` step. */
1510
+ usesMask: boolean;
1511
+ /** `true` when the procedure's builder chain carries an `.output(...)` return-shape projection. */
1512
+ usesOutput: boolean;
1513
+ /** `"internal"` for `internalQuery`; `"public"` for `query`. */
1514
+ visibility: "internal" | "public";
1515
+ }
1516
+ /**
1517
+ * One `query`/`mutation` handler that gates a `ctx.db.get`/`patch`/`delete` on a
1518
+ * null-checked `ctx.db.normalizeId(table, id)` result — the
1519
+ * `normalize_id_used_as_authorization` lint input. `normalizeId` validates an id's
1520
+ * structural shape only (it never reads the database), so a non-null result proves
1521
+ * the id is well-formed, never that the caller owns the row; gating access on it is
1522
+ * an IDOR. The lint owns the negative proof — it keeps only `visibility === "public"`
1523
+ * rows with no `.use(rls(...))` and no ownership/identity mention (`mentionsOwnership`),
1524
+ * then joins `table` against the schema's RLS mode before flagging. Structurally
1525
+ * identical to `AdvisorNormalizeIdAuthorization` so values pass straight through.
1526
+ */
1527
+ interface NormalizeIdAuthorizationIR {
1528
+ /** Export binding name of the procedure performing the normalize-then-access. */
1529
+ exportName: string;
1530
+ /** Source file relative to `&lt;projectRoot>/lunora/`, without extension. */
1531
+ file: string;
1532
+ /** 1-based line of the `ctx.db.normalizeId(...)` call the access is gated on. */
1533
+ line: number;
1534
+ /** `true` when the handler anywhere reads an ownership-named identifier or `ctx.auth`/`ctx.identity`/… — an intervening ownership signal. */
1535
+ mentionsOwnership: boolean;
1536
+ /** The id-first `ctx.db` sink the normalized id reaches. */
1537
+ sinkMethod: "delete" | "get" | "patch";
1538
+ /** Table named in the `normalizeId` call, or `""` when its table argument wasn't a string literal. */
1539
+ table: string;
1540
+ /** `true` when the procedure's builder chain carries a `.use(rls(...))` step. */
1541
+ usesRls: boolean;
1542
+ /** `"internal"` for `internalQuery`/`internalMutation`; `"public"` for `query`/`mutation`. */
1543
+ visibility: "internal" | "public";
1544
+ }
1545
+ /**
1546
+ * One committed `wrangler.jsonc` `vars` entry whose value is a plaintext secret —
1547
+ * the `plaintext_secret_in_wrangler_vars` lint input. `vars` are baked into the
1548
+ * deployed Worker in cleartext and checked into source control, so a real API key
1549
+ * / token / private key there ships the secret to every reader of the repo and the
1550
+ * bundle; it belongs in a Secrets Store binding or `wrangler secret put`. Produced
1551
+ * by `@lunora/config` (which reads `wrangler.jsonc`), not a ts-morph feeder —
1552
+ * codegen only passes it through. Structurally identical to `AdvisorWranglerVariable`.
1553
+ */
1554
+ interface WranglerVariableIR {
1555
+ /** The `wrangler.jsonc` file the var was read from, relative to the project root. */
1556
+ file: string;
1557
+ /** The offending `vars` key (e.g. `STRIPE_SECRET_KEY`). */
1558
+ key: string;
1559
+ /** Heuristic that matched, e.g. `stripe_live_key` / `private_key` / `secret_named_var`. */
1560
+ kind: string;
1561
+ /** Redacted preview of the value (first few chars + length) for the finding detail — never the full secret. */
1562
+ preview: string;
1563
+ }
687
1564
  interface ProjectIR {
688
1565
  crons: ReadonlyArray<CronJobIR>;
689
1566
  functions: ReadonlyArray<FunctionIR>;
@@ -696,17 +1573,19 @@ interface ProjectIR {
696
1573
  * Run the static lints against a discovered {@link SchemaIR} and the reads/writes/calls
697
1574
  * found in function bodies: query reads feed `filter_without_index`, insert writes
698
1575
  * feed `table_without_insert`, authApi calls feed `auth_api_call_without_headers`,
699
- * rls procedure snapshots feed `rls_uncovered_table`, and mask procedure
700
- * snapshots feed `mask_uncovered_pii_column`; declared containers
701
- * feed the `container_*` lints; declared workflows + `ctx.workflows.get(...)` call
702
- * sites feed the `workflow_unused` / `workflow_unknown_target` lints; non-deterministic
1576
+ * rls procedure snapshots feed `rls_uncovered_table`, mask procedure
1577
+ * snapshots feed `mask_uncovered_pii_column`, and per-column mask strategies
1578
+ * feed `mask_weak_hash_strategy_on_pii`; declared containers
1579
+ * feed the `container_*` lints; declared workflows (with their durable step labels)
1580
+ * + `ctx.workflows.get(...)` call sites feed the `workflow_unused` /
1581
+ * `workflow_unknown_target` / duplicate-step-name lints; non-deterministic
703
1582
  * calls inside query/mutation handlers feed the `nondeterministic_query_mutation` lint
704
1583
  * (all default empty for callers that don't analyze functions/containers/workflows).
705
1584
  * The IR types are structurally identical to the advisor's evidence types so they
706
1585
  * pass straight through without conversion. Returns the findings; surfacing them
707
1586
  * (console, error overlay, studio Advisors table) is the caller's choice.
708
1587
  */
709
- declare const lintSchema: (schema: SchemaIR, queries?: ReadonlyArray<QueryReadIR>, inserts?: ReadonlyArray<InsertWriteIR>, authApiCalls?: ReadonlyArray<AuthApiCallIR>, rlsProcedures?: ReadonlyArray<RlsProcedureIR>, containers?: ReadonlyArray<ContainerIR>, workflows?: ReadonlyArray<WorkflowIR>, workflowCalls?: ReadonlyArray<WorkflowCallIR>, maskProcedures?: ReadonlyArray<MaskProcedureIR>, nondeterministicCalls?: ReadonlyArray<NondeterministicCallIR>, procedureProtections?: ReadonlyArray<ProcedureMiddlewareIR>, argumentValidators?: ReadonlyArray<ArgumentValidatorIR>, secretLiterals?: ReadonlyArray<SecretLiteralIR>, sqlInterpolations?: ReadonlyArray<SqlInterpolationIR>, adminRoutes?: ReadonlyArray<AdminRouteIR>, r2sqlCalls?: ReadonlyArray<R2sqlCallIR>) => Finding[];
1588
+ declare const lintSchema: (schema: SchemaIR, queries?: ReadonlyArray<QueryReadIR>, inserts?: ReadonlyArray<InsertWriteIR>, authApiCalls?: ReadonlyArray<AuthApiCallIR>, rlsProcedures?: ReadonlyArray<RlsProcedureIR>, containers?: ReadonlyArray<ContainerIR>, workflows?: ReadonlyArray<WorkflowIR>, workflowCalls?: ReadonlyArray<WorkflowCallIR>, maskProcedures?: ReadonlyArray<MaskProcedureIR>, nondeterministicCalls?: ReadonlyArray<NondeterministicCallIR>, procedureProtections?: ReadonlyArray<ProcedureMiddlewareIR>, argumentValidators?: ReadonlyArray<ArgumentValidatorIR>, secretLiterals?: ReadonlyArray<SecretLiteralIR>, sqlInterpolations?: ReadonlyArray<SqlInterpolationIR>, adminRoutes?: ReadonlyArray<AdminRouteIR>, r2sqlCalls?: ReadonlyArray<R2sqlCallIR>, shapes?: ReadonlyArray<ShapeIR>, mutatorWrites?: ReadonlyArray<MutatorWriteIR>, configCalls?: ReadonlyArray<ConfigCallIR>, argumentDerivedFetches?: ReadonlyArray<ArgumentDerivedFetchIR>, kvKeyAccesses?: ReadonlyArray<KvKeyAccessIR>, ownerFieldWrites?: ReadonlyArray<OwnerFieldWriteIR>, storageKeyAccesses?: ReadonlyArray<StorageKeyAccessIR>, aiRawRuns?: ReadonlyArray<AiRawRunIR>, containerKeyAccesses?: ReadonlyArray<ContainerKeyAccessIR>, mailRecipientAccesses?: ReadonlyArray<MailRecipientAccessIR>, vectorNamespaceAccesses?: ReadonlyArray<VectorNamespaceAccessIR>, browserUrlAccesses?: ReadonlyArray<BrowserUrlAccessIR>, privilegedDispatches?: ReadonlyArray<PrivilegedDispatchIR>, containerOverrides?: ReadonlyArray<ContainerOverrideIR>, authConfigs?: ReadonlyArray<AuthConfigIR>, maskStrategies?: ReadonlyArray<MaskStrategyIR>, imageDeliveryUrlAccesses?: ReadonlyArray<ImageDeliveryUrlAccessIR>, ratelimitKeySelectors?: ReadonlyArray<RatelimitKeySelectorIR>, storageUploads?: ReadonlyArray<StorageUploadIR>, httpActionGuards?: ReadonlyArray<HttpActionGuardIR>, httpHeaderWrites?: ReadonlyArray<HttpHeaderWriteIR>, failOpenGuards?: ReadonlyArray<FailOpenGuardIR>, flagSecurityDefaults?: ReadonlyArray<FlagSecurityDefaultIR>, aiToolSideEffects?: ReadonlyArray<AiToolSideEffectIR>, identityClaimReads?: ReadonlyArray<IdentityClaimReadIR>, paymentWebhooks?: ReadonlyArray<PaymentWebhookIR>, softDeleteReads?: ReadonlyArray<SoftDeleteReadIR>, relationLoads?: ReadonlyArray<RelationLoadIR>, rawRowReturns?: ReadonlyArray<RawRowReturnIR>, normalizeIdAuthorizations?: ReadonlyArray<NormalizeIdAuthorizationIR>, wranglerVariables?: ReadonlyArray<WranglerVariableIR>) => Finding[];
710
1589
  /**
711
1590
  * Render advisor findings as a single multi-line string for console surfacing:
712
1591
  * a one-line summary header followed by one `[LEVEL] name: detail` line per
@@ -716,10 +1595,12 @@ declare const formatAdvisories: (findings: ReadonlyArray<Finding>) => string;
716
1595
  /**
717
1596
  * An error thrown by codegen discovery when the user's schema or function
718
1597
  * source has a structural problem that can be pinpointed to a specific source
719
- * location. The `file`, `line`, and `column` properties mirror what Vite's
720
- * error-overlay `loc` field expects so the browser can display the exact spot.
1598
+ * location. A `LunoraError` subclass (`code: "CODEGEN_DIAGNOSTIC"`); the `file`,
1599
+ * `line`, and `column` properties (also passed through as the base `loc`) mirror
1600
+ * what Vite's error-overlay `loc` field expects so the browser can display the
1601
+ * exact spot.
721
1602
  */
722
- declare class CodegenDiagnosticError extends Error {
1603
+ declare class CodegenDiagnosticError extends LunoraError {
723
1604
  readonly column: number;
724
1605
  readonly file: string;
725
1606
  readonly line: number;
@@ -731,8 +1612,14 @@ declare class CodegenDiagnosticError extends Error {
731
1612
  * ts-morph `Node`'s position in its source file.
732
1613
  *
733
1614
  * Message format: `@lunora/codegen: &lt;detail> (&lt;file>:&lt;line>:&lt;column>)`
1615
+ *
1616
+ * `meta` is merged onto the returned error for callers that also carry the
1617
+ * project-wide `LunoraError` envelope (`code`/`name`/`status`) — it never
1618
+ * touches `file`/`line`/`column`, and the error stays an instance of
1619
+ * {@link CodegenDiagnosticError} so the Vite overlay's `instanceof` location
1620
+ * lookup is unaffected.
734
1621
  */
735
- declare const diagnosticAt: (node: Node, detail: string) => CodegenDiagnosticError;
1622
+ declare const diagnosticAt: (node: Node, detail: string, meta?: Record<string, unknown>) => CodegenDiagnosticError;
736
1623
  /**
737
1624
  * Discover `ctx.authApi.&lt;method>(...)` (and bare `authApi.&lt;method>(...)`) calls
738
1625
  * under the lunora source directory and attribute each to the exported function
@@ -758,6 +1645,21 @@ declare const discoverContainers: (project: Project, lunoraDirectory: string) =>
758
1645
  * workflow start. Names must be unique across the project.
759
1646
  */
760
1647
  declare const discoverCrons: (project: Project, lunoraDirectory: string, workflows?: ReadonlyArray<WorkflowIR>) => CronJobIR[];
1648
+ /** The only file a feature-flag provider may be declared in — mirrors `lunora/queues.ts`. */
1649
+ declare const FLAGS_FILENAME = "flags.ts";
1650
+ /** One statically-discovered flag read: the key plus the value type its `ctx.flags.&lt;type>` call implies. */
1651
+
1652
+ /**
1653
+ * Discover the feature-flag provider a project declares in `lunora/flags.ts`.
1654
+ * Returns `undefined` when the file doesn't exist (the app has no flags). The
1655
+ * read is metadata-only and lenient: codegen wires `ctx.flags` purely from the
1656
+ * file's *existence* (`run-codegen.ts`) and imports the real module for the
1657
+ * provider value — this IR exists solely so the config layer can reconcile the
1658
+ * wrangler `flagship` binding for the Flagship binding-mode provider. Anything
1659
+ * it can't read statically degrades to a `custom` provider (no binding), never
1660
+ * a thrown error.
1661
+ */
1662
+ declare const discoverFlags: (project: Project, lunoraDirectory: string) => FlagsIR | undefined;
761
1663
  /**
762
1664
  * Scan all .ts files under `lunoraDir` (skipping `_generated/` and `schema.ts`)
763
1665
  * for top-level `export const x = query/mutation/action({...})` registrations.
@@ -800,6 +1702,23 @@ declare const discoverMaskProcedures: (project: Project, lunoraDirectory: string
800
1702
  * key); `table` is best-effort and left `""` when not a literal.
801
1703
  */
802
1704
  declare const discoverMigrations: (project: Project, lunoraDirectory: string) => MigrationIR[];
1705
+ /** The only file custom mutators may be declared in — mirrors `lunora/queues.ts`. */
1706
+ declare const MUTATORS_FILENAME = "mutators.ts";
1707
+ /**
1708
+ * Decide whether a call's callee is `defineMutator` — either the bare imported
1709
+ * identifier (`defineMutator(...)`) or a namespace member access
1710
+ * (`server.defineMutator(...)`). Both are valid ES module syntax, so discovery
1711
+ * must see mutators declared either way.
1712
+ */
1713
+
1714
+ /**
1715
+ * Discover every custom mutator the project declares: exported
1716
+ * `defineMutator()` calls in `lunora/mutators.ts`. Returns `[]` when the file
1717
+ * doesn't exist. Only the export binding is lifted — the runtime object carries
1718
+ * the authoritative `server` impl + `handler`, so codegen never evaluates the
1719
+ * body. The client `client` impl is split into the browser bundle separately.
1720
+ */
1721
+ declare const discoverMutators: (project: Project, lunoraDirectory: string) => MutatorIR[];
803
1722
  /**
804
1723
  * Discover non-deterministic API calls (`Date.now`, `new Date()`, `Date()`,
805
1724
  * `Math.random`, `crypto.randomUUID`, `crypto.getRandomValues` — including
@@ -823,6 +1742,15 @@ declare const discoverNondeterministicCalls: (project: Project, lunoraDirectory:
823
1742
  * dropping the rest keeps the lint input small.
824
1743
  */
825
1744
  declare const discoverQueries: (project: Project, lunoraDirectory: string) => QueryReadIR[];
1745
+ /** The only file queues may be declared in — mirrors `lunora/workflows.ts`. */
1746
+ declare const QUEUES_FILENAME = "queues.ts";
1747
+ /**
1748
+ * Discover every queue the project declares: exported `defineQueue()` calls in
1749
+ * `lunora/queues.ts`. Returns `[]` when the file doesn't exist. Only the
1750
+ * wrangler-relevant literals (`name`/`mode`/batch tuning) are read; the handler
1751
+ * body is runtime-only, so codegen never evaluates it.
1752
+ */
1753
+ declare const discoverQueues: (project: Project, lunoraDirectory: string) => QueueIR[];
826
1754
  /**
827
1755
  * Discover `ctx.r2sql` accesses lexically inside the handler body of every
828
1756
  * exported `query(...)` / `mutation(...)` registration under the lunora source
@@ -855,7 +1783,17 @@ declare const discoverRlsMetadata: (project: Project, lunoraDirectory: string) =
855
1783
  * Load `&lt;projectRoot>/lunora/schema.ts`, find `defineSchema({...})`, and
856
1784
  * return a structural IR. Throws if the file or call cannot be found.
857
1785
  */
858
- declare const discoverSchema: (project: Project, schemaPath: string) => SchemaIR;
1786
+ declare const discoverSchema: (project: Project, schemaPath: string, projectRoot?: string) => SchemaIR;
1787
+ /** The only file shapes may be declared in — mirrors `lunora/queues.ts`. */
1788
+ declare const SHAPES_FILENAME = "shapes.ts";
1789
+ /**
1790
+ * Discover every replication shape the project declares: exported
1791
+ * `defineShape()` calls in `lunora/shapes.ts`. Returns `[]` when the file
1792
+ * doesn't exist. Only the export binding is lifted — the runtime object carries
1793
+ * the authoritative `table`/`columns`/`compileWhere`, so codegen never
1794
+ * evaluates the predicate.
1795
+ */
1796
+ declare const discoverShapes: (project: Project, lunoraDirectory: string) => ShapeIR[];
859
1797
  /**
860
1798
  * Aggregate the schema-wide storage-rule metadata the studio's inspector reads:
861
1799
  * every statically-discovered `(bucket, on, prefix, procedure)` entry across all
@@ -894,24 +1832,60 @@ declare const emitApi: (functions: ReadonlyArray<FunctionIR>, workflows?: Readon
894
1832
  * Returns `""` when `@lunora/seed` is not a declared dependency, so projects
895
1833
  * that don't use it keep a clean `_generated/` and never import the package.
896
1834
  */
1835
+
1836
+ /**
1837
+ * Emit `_generated/collections.ts` — one TanStack DB collection factory per
1838
+ * `defineShape` in `lunora/shapes.ts` (the local-first partial-replication
1839
+ * surface). Each factory builds a live collection that syncs its shape's rowset
1840
+ * through the client's poke protocol (`subscribeShape`), so an app feeds the
1841
+ * result straight to `useLiveQuery`.
1842
+ *
1843
+ * Returns `""` (so `writeIfPresent` skips the file) unless the project both
1844
+ * declares shapes AND installs `@lunora/db` — the add-on that ships
1845
+ * `lunoraCollectionOptions`. `@lunora/db` stays a scoped install even under the
1846
+ * `lunorash` umbrella (an opt-in add-on, like `@lunora/auth`), so its import is
1847
+ * always `@lunora/db/collections`; only the in-umbrella `@lunora/client` import
1848
+ * is remapped to `lunorash/client`.
1849
+ */
1850
+ declare const emitCollections: (shapes: ReadonlyArray<ShapeIR>, hasDatabase: boolean, useUmbrella?: boolean) => string;
897
1851
  interface EmitServerOptions {
898
1852
  containers?: ReadonlyArray<ContainerIR>;
1853
+ /**
1854
+ * A `lunora/` source reads `ctx.access` — wires the verified Cloudflare Access
1855
+ * facade (`@lunora/cloudflare-access/context`) onto every ctx. Distinct from
1856
+ * `emitApp`'s `hasAccess` (which gates the worker's `.access()` resolveIdentity
1857
+ * method); this one gates the per-request `ctx.access` read surface.
1858
+ */
1859
+ hasAccessFacade?: boolean;
899
1860
  hasAi?: boolean;
900
- /** A `lunora/` source uses `@lunora/analytics` / `ctx.analytics` — wires the write helper onto every ctx. */
1861
+ /** A `lunora/` source uses `@lunora/bindings/analytics` / `ctx.analytics` — wires the write helper onto every ctx. */
901
1862
  hasAnalytics?: boolean;
902
1863
  /** A `lunora/` source uses `@lunora/browser` / `ctx.browser` — wires `ctx.browser` onto ActionCtx only. */
903
1864
  hasBrowser?: boolean;
1865
+ /** The project declares `lunora/flags.ts` — wires `ctx.flags` (OpenFeature) onto every ctx. */
1866
+ hasFlags?: boolean;
904
1867
  /** A `lunora/` source uses `@lunora/hyperdrive` / `ctx.sql` — wires `ctx.sql` onto ActionCtx only. */
905
1868
  hasHyperdrive?: boolean;
906
- /** A `lunora/` source uses `@lunora/images` / `ctx.images` — wires `ctx.images` onto ActionCtx only. */
1869
+ /** A `lunora/` source uses `@lunora/bindings/images` / `ctx.images` — wires `ctx.images` onto ActionCtx only. */
907
1870
  hasImages?: boolean;
908
- /** A `lunora/` source uses `@lunora/kv` / `ctx.kv` — wires `ctx.kv` onto every ctx. */
1871
+ /** A `lunora/` source uses `@lunora/bindings/kv` / `ctx.kv` — wires `ctx.kv` onto every ctx. */
909
1872
  hasKv?: boolean;
910
1873
  hasPayments?: boolean;
911
- /** A `lunora/` source uses `@lunora/pipelines` / `ctx.pipelines` — wires `ctx.pipelines` onto ActionCtx only. */
1874
+ /** A `lunora/` source uses `@lunora/bindings/pipelines` / `ctx.pipelines` — wires `ctx.pipelines` onto ActionCtx only. */
912
1875
  hasPipelines?: boolean;
913
- /** A `lunora/` source uses `@lunora/r2sql` / `ctx.r2sql` — wires `ctx.r2sql` onto ActionCtx only. */
1876
+ /** A `lunora/` source uses `@lunora/bindings/r2sql` / `ctx.r2sql` — wires `ctx.r2sql` onto ActionCtx only. */
914
1877
  hasR2sql?: boolean;
1878
+ /**
1879
+ * The single `defineIdentity(...)` claim contract declared in
1880
+ * `lunora/identity.ts` (Plan 080). When present, `ctx.auth.getIdentity()`,
1881
+ * the RLS policy `ctx.auth.identity`, and the shard-authorization hooks
1882
+ * narrow to the declared shape (recovered via `InferIdentity` over the
1883
+ * contract's `typeof`). `undefined` keeps the identity an untyped bag —
1884
+ * byte-identical to today.
1885
+ */
1886
+ identity?: IdentityIR;
1887
+ /** Queues declared via `defineQueue` exports — wires the typed `ctx.queues` producers onto Mutation/Action contexts. */
1888
+ queues?: ReadonlyArray<QueueIR>;
915
1889
  schema?: SchemaIR;
916
1890
  storageRuleBuckets?: ReadonlyArray<string>;
917
1891
  /** The project depends on the `lunora` umbrella — import base packages via its subpaths. */
@@ -920,21 +1894,25 @@ interface EmitServerOptions {
920
1894
  }
921
1895
  declare const emitServer: ({
922
1896
  containers,
1897
+ hasAccessFacade,
923
1898
  hasAi,
924
1899
  hasAnalytics,
925
1900
  hasBrowser,
1901
+ hasFlags,
926
1902
  hasHyperdrive,
927
1903
  hasImages,
928
1904
  hasKv,
929
1905
  hasPayments,
930
1906
  hasPipelines,
931
1907
  hasR2sql,
1908
+ identity,
1909
+ queues,
932
1910
  schema,
933
1911
  storageRuleBuckets,
934
1912
  useUmbrella,
935
1913
  workflows
936
1914
  }?: EmitServerOptions) => string;
937
- declare const emitFunctions: (functions: ReadonlyArray<FunctionIR>, migrations?: ReadonlyArray<MigrationIR>) => string;
1915
+ declare const emitFunctions: (functions: ReadonlyArray<FunctionIR>, migrations?: ReadonlyArray<MigrationIR>, useUmbrella?: boolean, mutators?: ReadonlyArray<MutatorIR>, shapes?: ReadonlyArray<ShapeIR>) => string;
938
1916
  /**
939
1917
  * Storage-column map per table for the file browser: `{ table: [field, …] }` for
940
1918
  * every field declared `v.storage(...)` (unwrapping `v.optional(...)`). The
@@ -952,7 +1930,7 @@ declare const emitFunctions: (functions: ReadonlyArray<FunctionIR>, migrations?:
952
1930
  * `containers[].class_name` to be exported by the deployed worker. Returns ""
953
1931
  * when the project declares no containers (the file is not written then).
954
1932
  */
955
- declare const emitContainers: (containers: ReadonlyArray<ContainerIR>) => string;
1933
+ declare const emitContainers: (containers: ReadonlyArray<ContainerIR>, jurisdiction?: JurisdictionIR) => string;
956
1934
  /**
957
1935
  * Emit `_generated/workflows.ts` — one `WorkflowEntrypoint` class per
958
1936
  * `defineWorkflow` export, each a thin subclass of `LunoraWorkflow`
@@ -962,14 +1940,31 @@ declare const emitContainers: (containers: ReadonlyArray<ContainerIR>) => string
962
1940
  * when the project declares no workflows (the file is not written then).
963
1941
  */
964
1942
  declare const emitWorkflows: (workflows: ReadonlyArray<WorkflowIR>) => string;
1943
+ /**
1944
+ * Emit `_generated/queues.ts` — the push-consumer registry the worker `queue()`
1945
+ * handler dispatches through. Maps each push queue's stable wrangler name (which
1946
+ * `batch.queue` carries) to its `defineQueue` definition + export name. Pull
1947
+ * queues are consumed by an external worker, so they carry no handler and are
1948
+ * omitted here. Returns "" (and the file is not written) when no push queues are
1949
+ * declared — a pull-only or queue-free app keeps a clean `_generated/`.
1950
+ */
965
1951
  interface EmitShardOptions {
966
1952
  advisories?: ReadonlyArray<Finding>;
967
1953
  containers?: ReadonlyArray<ContainerIR>;
1954
+ /** Statically-discovered `ctx.flags.&lt;type>("key")` reads — the studio Flags page + reactive evaluation iterate these. */
1955
+ flagKeys?: ReadonlyArray<{
1956
+ key: string;
1957
+ type: "boolean" | "number" | "object" | "string";
1958
+ }>;
1959
+ /** A `lunora/` source reads `ctx.access` — wires the verified Cloudflare Access facade onto every ctx. */
1960
+ hasAccessFacade?: boolean;
968
1961
  hasAi?: boolean;
969
1962
  /** A `lunora/` source reads `ctx.analytics` — wires the Analytics Engine write helper onto every ctx. */
970
1963
  hasAnalytics?: boolean;
971
1964
  /** A `lunora/` source reads `ctx.browser` — wires `ctx.browser` onto the ActionCtx only. */
972
1965
  hasBrowser?: boolean;
1966
+ /** The project declares `lunora/flags.ts` — wires `ctx.flags` (OpenFeature) onto every ctx. */
1967
+ hasFlags?: boolean;
973
1968
  /** A `lunora/` source reads `ctx.sql` (Hyperdrive) — wires `ctx.sql` onto the ActionCtx only. */
974
1969
  hasHyperdrive?: boolean;
975
1970
  /** A `lunora/` source reads `ctx.images` — wires `ctx.images` onto the ActionCtx only. */
@@ -977,11 +1972,19 @@ interface EmitShardOptions {
977
1972
  /** A `lunora/` source reads `ctx.kv` — wires `ctx.kv` onto every ctx. */
978
1973
  hasKv?: boolean;
979
1974
  hasPayments?: boolean;
1975
+ /** A `lunora/` source reads `ctx.pipelines` — wires `ctx.pipelines` onto the ActionCtx only. */
1976
+ hasPipelines?: boolean;
980
1977
  /** A `lunora/` source reads `ctx.r2sql` (R2 SQL) — wires `ctx.r2sql` onto the ActionCtx only. */
981
1978
  hasR2sql?: boolean;
982
1979
  maskMetadata?: MaskMetadataIR;
1980
+ /** Custom mutators declared via `defineMutator` in `lunora/mutators.ts` — wires the `isCustomMutator` push-protocol override. */
1981
+ mutators?: ReadonlyArray<MutatorIR>;
1982
+ /** Queues declared via `defineQueue` exports in `lunora/queues.ts` — wires the typed `ctx.queues` producers. */
1983
+ queues?: ReadonlyArray<QueueIR>;
983
1984
  rlsMetadata?: RlsMetadataIR;
984
1985
  schema: SchemaIR;
1986
+ /** Replication shapes declared via `defineShape` in `lunora/shapes.ts` — wires the `resolveShape` subscription override. */
1987
+ shapes?: ReadonlyArray<ShapeIR>;
985
1988
  storageRules?: StorageRulesMetadataIR;
986
1989
  studioFeatures?: StudioFeaturesResult;
987
1990
  /** The project depends on the `lunora` umbrella — import base packages via its subpaths. */
@@ -991,17 +1994,24 @@ interface EmitShardOptions {
991
1994
  declare const emitShard: ({
992
1995
  advisories,
993
1996
  containers,
1997
+ flagKeys,
1998
+ hasAccessFacade,
994
1999
  hasAi,
995
2000
  hasAnalytics,
996
2001
  hasBrowser,
2002
+ hasFlags,
997
2003
  hasHyperdrive,
998
2004
  hasImages,
999
2005
  hasKv,
1000
2006
  hasPayments,
2007
+ hasPipelines,
1001
2008
  hasR2sql,
1002
2009
  maskMetadata,
2010
+ mutators,
2011
+ queues,
1003
2012
  rlsMetadata,
1004
2013
  schema,
2014
+ shapes,
1005
2015
  storageRules,
1006
2016
  studioFeatures,
1007
2017
  useUmbrella,
@@ -1066,9 +2076,11 @@ declare const emitVectors: (vectorIndexes: ReadonlyArray<VectorIndexIR>) => stri
1066
2076
  declare const emitWranglerCronTriggers: (crons: ReadonlyArray<CronJobIR>) => string[];
1067
2077
  /** Which capability methods the generated `defineApp` builder exposes — one flag per package-backed feature the app actually uses. */
1068
2078
  interface EmitAppOptions {
2079
+ /** App depends on `@lunora/cloudflare-access` → emit `.access()` (wire the Cloudflare Access `resolveIdentity`, composed ahead of `@lunora/auth` when both are present). */
2080
+ hasAccess: boolean;
1069
2081
  /** App uses `@lunora/ai` / `ctx.ai` → emit `.ai()` (override the Workers AI binding backing `ctx.ai`). */
1070
2082
  hasAi: boolean;
1071
- /** App uses `@lunora/analytics` / `ctx.analytics` → emit `.analytics()` (override the dataset backing `ctx.analytics`). */
2083
+ /** App uses `@lunora/bindings/analytics` / `ctx.analytics` → emit `.analytics()` (override the dataset backing `ctx.analytics`). */
1072
2084
  hasAnalytics: boolean;
1073
2085
  /** App depends on `@lunora/auth` → emit `.auth()` + the lazy build/migrate dance. */
1074
2086
  hasAuth: boolean;
@@ -1082,13 +2094,15 @@ interface EmitAppOptions {
1082
2094
  hasHyperdrive: boolean;
1083
2095
  /** Schema declares **Hyperdrive-backed** `.global({ backend: "hyperdrive" })` tables → emit `.hyperdriveGlobal()` (reactive Postgres/MySQL ctx-db over Hyperdrive). */
1084
2096
  hasHyperdriveGlobal: boolean;
1085
- /** App uses `@lunora/images` / `ctx.images` → emit `.images()`. */
2097
+ /** App uses `@lunora/bindings/images` / `ctx.images` → emit `.images()`. */
1086
2098
  hasImages: boolean;
1087
- /** App uses `@lunora/kv` / `ctx.kv` → emit `.kv()`. */
2099
+ /** App uses `@lunora/bindings/kv` / `ctx.kv` → emit `.kv()`. */
1088
2100
  hasKv: boolean;
1089
2101
  /** App uses `@lunora/payment` / `ctx.payments` → emit `.payment()`. */
1090
2102
  hasPayments: boolean;
1091
- /** App uses `@lunora/r2sql` / `ctx.r2sql` → emit `.r2sql()`. */
2103
+ /** App declares push queues (`defineQueue`)wire `LUNORA_QUEUE_REGISTRY` into the worker's `queue()` consumer entry. */
2104
+ hasQueue: boolean;
2105
+ /** App uses `@lunora/bindings/r2sql` / `ctx.r2sql` → emit `.r2sql()`. */
1092
2106
  hasR2sql: boolean;
1093
2107
  /** App imports `@lunora/scheduler` / declares crons → emit `.scheduler()`. */
1094
2108
  hasScheduler: boolean;
@@ -1098,6 +2112,10 @@ interface EmitAppOptions {
1098
2112
  hasVectors: boolean;
1099
2113
  /** App declares Cloudflare Workflows (`defineWorkflow`) → wire `options.workflowsClient` so the studio's workflow-instance proxy can reach the CF REST API. */
1100
2114
  hasWorkflow: boolean;
2115
+ /** The single `defineIdentity(...)` contract in `lunora/identity.ts` (Plan 080) → import it as a VALUE and wire `options.identity`, so the runtime trust boundary validates every resolved identity before it becomes `ctx.auth`. `undefined` ⇒ no wiring, byte-identical output. */
2116
+ identity?: IdentityIR;
2117
+ /** Schema declares `.jurisdiction("…")` → pin every DO the worker reaches (shards, fan-out, scheduler, containers) to the Cloudflare data-residency jurisdiction. */
2118
+ jurisdiction?: JurisdictionIR;
1101
2119
  /** Project depends on the unscoped `lunorash` umbrella → import the runtime via `lunorash/runtime` instead of `@lunora/runtime`. */
1102
2120
  useUmbrella: boolean;
1103
2121
  /** An OpenAPI spec is emitted (`openapi.ts`) → wire `openApiSpec` into the worker. */
@@ -1242,6 +2260,19 @@ interface TableSnapshot {
1242
2260
  }
1243
2261
  /** The committed baseline — a deterministic structural view of the whole schema. */
1244
2262
  interface SchemaSnapshot {
2263
+ /**
2264
+ * Cloudflare DO data-residency jurisdiction declared via `.jurisdiction("…")`,
2265
+ * or absent. Tracked because changing it strands all existing Durable Object
2266
+ * data (a DO name maps to a different ID per jurisdiction). Optional, so old
2267
+ * baselines written before this field parse cleanly (absent ⇒ undefined).
2268
+ *
2269
+ * Typed as a plain `string` (not the authoring union) on purpose: this is
2270
+ * STORED data that a newer Lunora may have written with a jurisdiction this
2271
+ * version doesn't yet know. Preserving the raw value keeps the breaking
2272
+ * `changedJurisdiction` diff correct under a downgrade — coercing an unknown
2273
+ * value to `undefined` would fail OPEN and hide the most destructive change.
2274
+ */
2275
+ jurisdiction?: string;
1245
2276
  /** Sorted list of every declared `defineMigration` id at capture time. */
1246
2277
  migrationIds: ReadonlyArray<string>;
1247
2278
  /** Table name → {@link TableSnapshot}, keys sorted for stable serialization. */
@@ -1255,7 +2286,7 @@ interface DriftChange {
1255
2286
  /** Human-readable, actionable description (used in the gate message). */
1256
2287
  summary: string;
1257
2288
  /** A machine-readable change discriminator. */
1258
- type: "addedIndex" | "addedOptionalField" | "addedRelation" | "addedRequiredField" | "addedTable" | "changedFieldKind" | "changedIndex" | "changedShardMode" | "fieldOptionalToRequired" | "fieldRequiredToOptional" | "removedField" | "removedIndex" | "removedRelation" | "removedTable";
2289
+ type: "addedIndex" | "addedOptionalField" | "addedRelation" | "addedRequiredField" | "addedTable" | "changedJurisdiction" | "changedFieldKind" | "changedIndex" | "changedShardMode" | "fieldOptionalToRequired" | "fieldRequiredToOptional" | "removedField" | "removedIndex" | "removedRelation" | "removedTable";
1259
2290
  }
1260
2291
  /** The result of diffing two snapshots: every classified change. */
1261
2292
  interface SchemaDrift {
@@ -1276,8 +2307,8 @@ declare const serializeSchemaSnapshot: (snapshot: SchemaSnapshot) => string;
1276
2307
  * treat a corrupt baseline as a hard error rather than silently degrading to a
1277
2308
  * "first capture" that would mask drift and then overwrite the bad file.
1278
2309
  */
1279
- declare class SchemaSnapshotParseError extends Error {
1280
- override readonly name = "SchemaSnapshotParseError";
2310
+ declare class SchemaSnapshotParseError extends LunoraError {
2311
+ constructor(message: string);
1281
2312
  }
1282
2313
  /**
1283
2314
  * Parse a committed snapshot file. Returns `undefined` ONLY when the content is
@@ -1357,6 +2388,11 @@ declare const refreshCodegenProject: (project: Project, lunoraDirectory: string)
1357
2388
  * Top-level codegen entry. Parses `&lt;projectRoot>/lunora/schema.ts` and every
1358
2389
  * function file under `&lt;projectRoot>/lunora/`, then writes
1359
2390
  * `_generated/{api,server,dataModel}.ts` next to them.
2391
+ *
2392
+ * When `LUNORA_CODEGEN_TIMING` is set (truthy), a single diagnostic summary
2393
+ * line is written to stderr with the total wall time and the discovery-vs-emit
2394
+ * split — opt-in instrumentation that is otherwise zero-cost and side-effect-free
2395
+ * on the returned {@link CodegenResult}.
1360
2396
  */
1361
2397
  declare const runCodegen: (options: CodegenOptions) => CodegenResult;
1362
2398
  interface CodegenOptions {
@@ -1407,6 +2443,14 @@ interface CodegenOptions {
1407
2443
  * a breaking change. Ignored when `dryRun` is true.
1408
2444
  */
1409
2445
  updateSchemaBaseline?: boolean;
2446
+ /**
2447
+ * Committed `wrangler.jsonc` `vars` entries that hold plaintext secrets — the
2448
+ * `plaintext_secret_in_wrangler_vars` lint input. Produced by `@lunora/config`
2449
+ * (which reads `wrangler.jsonc`) and threaded through by the CLI / Vite plugin;
2450
+ * codegen only forwards it to the advisor. Absent when no wrangler config is
2451
+ * present or the caller doesn't scan it.
2452
+ */
2453
+ wranglerVariables?: ReadonlyArray<WranglerVariableIR>;
1410
2454
  }
1411
2455
  interface CodegenResult {
1412
2456
  /**
@@ -1433,6 +2477,8 @@ interface CodegenResult {
1433
2477
  generated: {
1434
2478
  api: string; /** Fluent worker-composition builder (`_generated/app.ts`) — `defineApp()`. Always written. */
1435
2479
  app: string;
2480
+ /** Partial-replication collection factories (`_generated/collections.ts`); `""` (and not written) unless the project declares shapes and installs `@lunora/db`. */
2481
+ collections: string;
1436
2482
  /** Container DO classes (`_generated/containers.ts`); `""` (and not written) when no containers are declared. */
1437
2483
  containers: string;
1438
2484
  crons: string;
@@ -1457,6 +2503,8 @@ interface CodegenResult {
1457
2503
  * `apiSpec` includes `openrpc`.
1458
2504
  */
1459
2505
  openRpcModule: string;
2506
+ /** Push-consumer queue registry (`_generated/queues.ts`); `""` (and not written) when no push queues are declared. */
2507
+ queues: string;
1460
2508
  /** Project-bound seed client (`_generated/seed.ts`); `""` (and not written) when `@lunora/seed` is not a declared dependency. */
1461
2509
  seed: string;
1462
2510
  server: string;
@@ -1467,6 +2515,13 @@ interface CodegenResult {
1467
2515
  };
1468
2516
  outputDirectory: string;
1469
2517
  /**
2518
+ * Queues discovered from `defineQueue` exports in `lunora/queues.ts` — the
2519
+ * list the config layer reconciles into wrangler's `queues.producers[]` /
2520
+ * `queues.consumers[]`. Queues are NOT Durable Objects, so this adds no
2521
+ * binding or migration. Empty when the project declares no queues.
2522
+ */
2523
+ queues: ReadonlyArray<QueueIR>;
2524
+ /**
1470
2525
  * The CURRENT structural schema snapshot computed this run (tables + field
1471
2526
  * kinds/optionality + indexes/relations/shard mode + declared migration ids).
1472
2527
  * The pre-deploy drift gate diffs this against the committed baseline read
@@ -1510,5 +2565,9 @@ declare const validatorIrToJsonSchema: (validator: ValidatorIR) => JsonSchema;
1510
2565
  * the contract; clients switch on `error.code`. Kept sorted for stable output.
1511
2566
  */
1512
2567
  declare const LUNORA_ERROR_CODES: ReadonlyArray<string>;
2568
+ /** The matching secret rule's `kind` for a string value, or `undefined` when none matches. */
2569
+ declare const secretKindOf: (value: string) => string | undefined;
2570
+ /** A redacted preview of a secret value — first 4 chars plus its length, never the full value. */
2571
+ declare const redact: (value: string) => string;
1513
2572
  declare const VERSION = "0.0.0";
1514
- export { type AuthApiCallIR, CONTAINERS_FILENAME, CodegenDiagnosticError, type CodegenOptions, type CodegenResult, type ContainerIR, type CronJobIR, type DriftChange, type EmitAppOptions, type FieldSnapshot, type FunctionIR, GENERATED_HEADER, type HttpRouteIR, type IndexIR, type IndexSnapshot, type InsertWriteIR, LUNORA_ERROR_CODES, type MaskProcedureIR, type MigrationIR, OPENRPC_VERSION, type OpenApiEmitInput, type OpenRpcEmitInput, type ProjectIR, type QueryReadIR, type R2sqlCallIR, type RelationSnapshot, type RlsMetadataIR, type RlsPolicyIR, type RlsProcedureIR, type RlsRoleIR, SCHEMA_SNAPSHOT_FILENAME, SCHEMA_SNAPSHOT_VERSION, type SchemaDrift, type SchemaDriftDecision, type SchemaIR, type SchemaSnapshot, SchemaSnapshotParseError, type StorageRuleIR, type StorageRulesMetadataIR, type TableIR, type TableSnapshot, VERSION, type ValidatorIR, type VectorIndexIR, WORKFLOWS_FILENAME, type WorkflowIR, buildOpenApiDocument, buildOpenRpcDocument, buildSchemaSnapshot, createCodegenProject, diagnosticAt, diffSchemaSnapshots, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverNondeterministicCalls, discoverQueries, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSchema, discoverStorageRulesMetadata, discoverWorkflows, emitApi, emitApp, emitContainers, emitCrons, emitDataModel, emitDrizzleSchema, emitFunctions, emitOpenApi, emitOpenApiModule, emitOpenRpc, emitOpenRpcModule, emitServer, emitShard, emitVectors, emitWorkflows, emitWranglerCronTriggers, evaluateSchemaDrift, formatAdvisories, lintSchema, parseSchemaSnapshot, refreshCodegenProject, runCodegen, schemaFromIr, serializeSchemaSnapshot, validatorIrToJsonSchema };
2573
+ export { 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, 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, discoverAuthApiCalls, discoverContainers, discoverCrons, discoverFlags, discoverFunctions, discoverHttpRoutes, discoverInserts, discoverMaskProcedures, discoverMigrations, discoverMutators, discoverNondeterministicCalls, discoverQueries, discoverQueues, discoverR2sqlCalls, discoverRlsMetadata, discoverRlsProcedures, discoverSchema, discoverShapes, discoverStorageRulesMetadata, discoverWorkflows, 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 };