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