@lunora/advisor 1.0.0-alpha.1 → 1.0.0-alpha.10
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/README.md +1 -1
- package/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +291 -3
- package/dist/index.d.ts +291 -3
- package/dist/index.mjs +14 -2
- package/dist/packem_shared/externalSourceOnGlobal-Bg-NfCX9.mjs +30 -0
- package/dist/packem_shared/externalSourceUnscoped-5vT-Bup3.mjs +44 -0
- package/dist/packem_shared/{fromServerSchema-DinF1nph.mjs → fromServerSchema-WVRvXPy8.mjs} +6 -0
- package/dist/packem_shared/mutatorFullRowReplace-BJnNDaIV.mjs +26 -0
- package/dist/packem_shared/r2sqlOutsideAction-CtqxvMuV.mjs +30 -0
- package/dist/packem_shared/shapeTargetsGlobalTable-DHrf4Koi.mjs +34 -0
- package/dist/packem_shared/shapeUnknownTable-C8aDWFoe.mjs +34 -0
- package/dist/packem_shared/workflowDuplicateStepName-ioBxPBCy.mjs +48 -0
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -214,6 +214,28 @@ interface AdvisorMaskProcedure {
|
|
|
214
214
|
visibility: "internal" | "public";
|
|
215
215
|
}
|
|
216
216
|
/**
|
|
217
|
+
* One whole-row `ctx.db.replace(id, document)` write discovered inside a custom
|
|
218
|
+
* mutator's authoritative `server` impl (`lunora/mutators.ts`) — the input the
|
|
219
|
+
* `mutator_full_row_replace` lint consumes.
|
|
220
|
+
*
|
|
221
|
+
* In the local-first sync engine a `replace` overwrites the entire row, so a
|
|
222
|
+
* concurrent edit to a *different* column (committed between this mutator's read
|
|
223
|
+
* and its write) is silently clobbered. `patch(id, { onlyTheField })` merges at
|
|
224
|
+
* the column level instead, letting independent field edits coexist — the
|
|
225
|
+
* blessed pattern for mutators on a synced (poke-live) table. Produced by the
|
|
226
|
+
* codegen feeder, which attributes each `replace` to the mutator export
|
|
227
|
+
* performing it; runtime callers don't supply it, so the lint finds nothing
|
|
228
|
+
* there.
|
|
229
|
+
*/
|
|
230
|
+
interface AdvisorMutatorWrite {
|
|
231
|
+
/** The mutator export whose `server` impl performs the replace (e.g. `renameChannel`). */
|
|
232
|
+
exportName: string;
|
|
233
|
+
/** Openable source path the replace appears in — always `lunora/mutators.ts`. */
|
|
234
|
+
file: string;
|
|
235
|
+
/** 1-based line of the `replace(...)` call, or `0` when unknown. */
|
|
236
|
+
line: number;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
217
239
|
* One non-deterministic API call discovered lexically inside a `query(...)` or
|
|
218
240
|
* `mutation(...)` handler body — the input the `nondeterministic_query_mutation`
|
|
219
241
|
* lint consumes. Produced by the codegen feeder, which walks each exported
|
|
@@ -285,6 +307,33 @@ interface AdvisorQueryRead {
|
|
|
285
307
|
table: string;
|
|
286
308
|
}
|
|
287
309
|
/**
|
|
310
|
+
* One `ctx.r2sql` access discovered lexically inside a `query(...)` or
|
|
311
|
+
* `mutation(...)` handler body — the input the `r2sql_outside_action` lint
|
|
312
|
+
* consumes. Produced by the codegen feeder, which walks each exported function's
|
|
313
|
+
* handler with ts-morph and records reads of the R2 SQL `ctx.r2sql` surface
|
|
314
|
+
* (`ctx.r2sql.query(...)`, `ctx.r2sql.from(...)`, …).
|
|
315
|
+
*
|
|
316
|
+
* R2 SQL queries Apache Iceberg tables over an **external** REST endpoint Lunora
|
|
317
|
+
* does not own (there is no Workers binding): a `ctx.r2sql` call is a network
|
|
318
|
+
* round-trip with a mutable result (non-deterministic, like `fetch`) and its
|
|
319
|
+
* reads are invisible to Lunora live queries. It therefore belongs **only** in
|
|
320
|
+
* `action(...)` handlers. Calls inside `action(...)` are intentionally **not**
|
|
321
|
+
* recorded — actions are the escape hatch. Runtime callers don't supply this, so
|
|
322
|
+
* the lint finds nothing there.
|
|
323
|
+
*/
|
|
324
|
+
interface AdvisorR2sqlCall {
|
|
325
|
+
/** The accessed `ctx.r2sql` surface, e.g. `ctx.r2sql.query` / `ctx.r2sql.from`. */
|
|
326
|
+
callee: string;
|
|
327
|
+
/** The exported function performing the access (e.g. `topPerRegion`). */
|
|
328
|
+
exportName: string;
|
|
329
|
+
/** Source file the access appears in (relative to the lunora dir, no extension). */
|
|
330
|
+
file: string;
|
|
331
|
+
/** Which procedure kind the access lives in — only `query`/`mutation` are flagged; actions are exempt. */
|
|
332
|
+
kind: "mutation" | "query";
|
|
333
|
+
/** 1-based line of the access, or `0` when unknown. */
|
|
334
|
+
line: number;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
288
337
|
* One procedure (query / mutation / action) discovered in the lunora source,
|
|
289
338
|
* reduced to the facts the `rls_uncovered_table` lint needs: whether the
|
|
290
339
|
* procedure's builder chain includes `.use(rls(...))`, and which tables the
|
|
@@ -336,6 +385,14 @@ interface AdvisorTable {
|
|
|
336
385
|
*/
|
|
337
386
|
externallyManaged?: boolean;
|
|
338
387
|
/**
|
|
388
|
+
* Set when the table was declared with `.source(...)` (plan 077) —
|
|
389
|
+
* materialized from an external Hyperdrive-backed database. Read by the
|
|
390
|
+
* `external_source_*` lints to enforce the tenant-scope boundary (mandatory
|
|
391
|
+
* `tenantBy` under `.shardBy()`) and reject sourcing a `.global()` table.
|
|
392
|
+
* Optional — feeders that don't know about sourced tables omit it.
|
|
393
|
+
*/
|
|
394
|
+
externalSource?: AdvisorExternalSource;
|
|
395
|
+
/**
|
|
339
396
|
* Declared column names (the `defineTable({...})` keys). Excludes the
|
|
340
397
|
* framework-managed system fields `_id` / `_creationTime`, which every table
|
|
341
398
|
* has implicitly — lints that resolve a column treat those as always valid.
|
|
@@ -357,6 +414,16 @@ interface AdvisorTable {
|
|
|
357
414
|
optionalFields?: ReadonlySet<string>;
|
|
358
415
|
/** Declared relations (`.relations((r) => …)`). */
|
|
359
416
|
relations: ReadonlyArray<AdvisorRelation>;
|
|
417
|
+
/**
|
|
418
|
+
* Storage tier the table is declared in: `"global"` (a `.global()` table,
|
|
419
|
+
* lives in D1 — the cross-shard tier), `"shardBy"` (partitioned across
|
|
420
|
+
* shard DOs by a key), or `"root"` (the default single-DO table). Read by
|
|
421
|
+
* the `shape_*` lints to flag replication shapes targeting a `.global()`
|
|
422
|
+
* table (poll-refreshed/latency-tiered, not poke-live). Optional — the
|
|
423
|
+
* codegen feeder always supplies it, the runtime feeder derives it; a feeder
|
|
424
|
+
* that omits it leaves tier-sensitive lints to treat the table as local.
|
|
425
|
+
*/
|
|
426
|
+
shardKind?: "global" | "root" | "shardBy";
|
|
360
427
|
}
|
|
361
428
|
/**
|
|
362
429
|
* One declared index, flattened across Lunora's index kinds so a single lint can
|
|
@@ -373,6 +440,21 @@ interface AdvisorIndex {
|
|
|
373
440
|
name: string;
|
|
374
441
|
unique?: boolean;
|
|
375
442
|
}
|
|
443
|
+
/** The statically-knowable `.source(...)` bits the `external_source_*` lints read. */
|
|
444
|
+
interface AdvisorExternalSource {
|
|
445
|
+
/** `true` when a `reconcileEveryMs` was given (the incremental-mode delete-visibility companion). */
|
|
446
|
+
hasReconcile?: boolean;
|
|
447
|
+
/** `true` when a `tenantBy` mapper was given — the tenant-isolation boundary. */
|
|
448
|
+
hasTenantBy: boolean;
|
|
449
|
+
/** Delete-detection mode literal, when given (`"full-pull"` | `"incremental"`). */
|
|
450
|
+
mode?: string;
|
|
451
|
+
/**
|
|
452
|
+
* `true` when `.source(...)` was declared but its config wasn't a static object
|
|
453
|
+
* literal, so `hasTenantBy` (and the rest) couldn't be read. Only the codegen
|
|
454
|
+
* feeder can hit this; the runtime feeder always holds the real config.
|
|
455
|
+
*/
|
|
456
|
+
unanalyzable?: boolean;
|
|
457
|
+
}
|
|
376
458
|
/**
|
|
377
459
|
* One declared relation. For a `one` relation the FK column `field` lives on
|
|
378
460
|
* the holding table; for `many` it lives on the target. `name` is the accessor
|
|
@@ -413,6 +495,28 @@ interface AdvisorSecretLiteral {
|
|
|
413
495
|
preview: string;
|
|
414
496
|
}
|
|
415
497
|
/**
|
|
498
|
+
* A replication shape declared via `defineShape({ table, where, columns? })` in
|
|
499
|
+
* `lunora/shapes.ts` (the local-first sync engine's partial-replication unit).
|
|
500
|
+
* The `shape_*` lints cross-reference each shape's {@link AdvisorShape.table}
|
|
501
|
+
* against the declared schema to flag a shape targeting an unknown table or a
|
|
502
|
+
* `.global()` table (which replicates through the latency-tiered D1 poll path,
|
|
503
|
+
* not the poke-live op-log). Supplied by the codegen feeder, which lifts only
|
|
504
|
+
* the export name + the static `table` literal; absent for runtime callers,
|
|
505
|
+
* where the shape lints find nothing.
|
|
506
|
+
*/
|
|
507
|
+
interface AdvisorShape {
|
|
508
|
+
/** Export binding name — the shape's registry key (e.g. `channelMessages`). */
|
|
509
|
+
exportName: string;
|
|
510
|
+
/** File the shape is declared in (relative, for the operator to open). */
|
|
511
|
+
file: string;
|
|
512
|
+
/**
|
|
513
|
+
* The `table` string literal the shape replicates from, or `undefined` when
|
|
514
|
+
* the feeder could not read it as a plain string literal — tier-sensitive
|
|
515
|
+
* lints skip a shape with no resolvable table rather than guessing.
|
|
516
|
+
*/
|
|
517
|
+
table?: string;
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
416
520
|
* One shard's observed traffic share — the input the `hot_shard` runtime lint
|
|
417
521
|
* consumes. Produced by the studio backend, which fans out over a sharded
|
|
418
522
|
* function's shards and reads each shard's recorded request volume from the
|
|
@@ -496,10 +600,27 @@ interface AdvisorTableSample {
|
|
|
496
600
|
* how `AdvisorContainer` tracks `ContainerIR` and `AdvisorInsertWrite` tracks
|
|
497
601
|
* `InsertWriteIR`).
|
|
498
602
|
*/
|
|
603
|
+
/** One durable step call lifted from a workflow handler body — the input the duplicate-step-name lint compares. Structural subset of codegen's `WorkflowStepIR`. */
|
|
604
|
+
interface AdvisorWorkflowStep {
|
|
605
|
+
/** 1-based line of the durable step call, or `0` when unknown. */
|
|
606
|
+
line: number;
|
|
607
|
+
/** The native step method invoked: `do` / `sleep` / `sleepUntil` / `waitForEvent`. */
|
|
608
|
+
method: string;
|
|
609
|
+
/** The step's static label (the first string-literal argument). */
|
|
610
|
+
name: string;
|
|
611
|
+
}
|
|
499
612
|
/** One workflow declared via a `defineWorkflow()` export in `lunora/workflows.ts`. */
|
|
500
613
|
interface AdvisorWorkflow {
|
|
501
614
|
/** The `lunora/workflows.ts` export name, e.g. `orderPipeline`. */
|
|
502
615
|
exportName: string;
|
|
616
|
+
/**
|
|
617
|
+
* The durable step labels discovered in the handler body, in source order —
|
|
618
|
+
* the duplicate-step-name input. Cloudflare memoizes a step by its name, so a
|
|
619
|
+
* name used twice makes the second call silently return the first's cached
|
|
620
|
+
* result. Supplied by the codegen feeder; `undefined` for runtime callers,
|
|
621
|
+
* where the lint finds nothing.
|
|
622
|
+
*/
|
|
623
|
+
steps?: ReadonlyArray<AdvisorWorkflowStep>;
|
|
503
624
|
}
|
|
504
625
|
/** One `ctx.workflows.get("name")` call discovered in a function body. */
|
|
505
626
|
interface AdvisorWorkflowCall {
|
|
@@ -633,6 +754,14 @@ interface LintContext {
|
|
|
633
754
|
*/
|
|
634
755
|
maskProcedures?: ReadonlyArray<AdvisorMaskProcedure>;
|
|
635
756
|
/**
|
|
757
|
+
* Whole-row `ctx.db.replace(id, document)` writes lifted from custom
|
|
758
|
+
* mutators' authoritative `server` impls (the `mutator_full_row_replace`
|
|
759
|
+
* input). Each `replace` overwrites the entire row, clobbering a concurrent
|
|
760
|
+
* edit to a different column on a synced table. Supplied by the codegen
|
|
761
|
+
* feeder; absent for runtime callers, where the lint finds nothing.
|
|
762
|
+
*/
|
|
763
|
+
mutatorWrites?: ReadonlyArray<AdvisorMutatorWrite>;
|
|
764
|
+
/**
|
|
636
765
|
* Non-deterministic API calls (`Date.now`, `Math.random`,
|
|
637
766
|
* `crypto.randomUUID`, `crypto.getRandomValues`, `fetch`) discovered lexically
|
|
638
767
|
* inside `query`/`mutation` handler bodies — the `nondeterministic_query_mutation`
|
|
@@ -657,6 +786,13 @@ interface LintContext {
|
|
|
657
786
|
*/
|
|
658
787
|
queries?: ReadonlyArray<AdvisorQueryRead>;
|
|
659
788
|
/**
|
|
789
|
+
* R2 SQL `ctx.r2sql` accesses discovered lexically inside `query`/`mutation`
|
|
790
|
+
* handler bodies — the `r2sql_outside_action` input. Supplied by the codegen
|
|
791
|
+
* feeder, which omits `action` handlers (where `ctx.r2sql` is the typed,
|
|
792
|
+
* intended surface); absent for runtime callers, where the lint finds nothing.
|
|
793
|
+
*/
|
|
794
|
+
r2sqlCalls?: ReadonlyArray<AdvisorR2sqlCall>;
|
|
795
|
+
/**
|
|
660
796
|
* Per-procedure RLS usage discovered in function bodies (the
|
|
661
797
|
* `rls_uncovered_table` input). Carries whether each procedure's builder chain
|
|
662
798
|
* includes `.use(rls(...))`, which tables the procedure reads/writes, and which
|
|
@@ -674,6 +810,14 @@ interface LintContext {
|
|
|
674
810
|
*/
|
|
675
811
|
secretLiterals?: ReadonlyArray<AdvisorSecretLiteral>;
|
|
676
812
|
/**
|
|
813
|
+
* Replication shapes declared via `defineShape` in `lunora/shapes.ts` — the
|
|
814
|
+
* `shape_unknown_table` and `shape_targets_global_table` lint input. Each
|
|
815
|
+
* carries the export name and its static `table` literal, cross-referenced
|
|
816
|
+
* against {@link LintContext.schema}. Supplied by the codegen feeder; absent
|
|
817
|
+
* for runtime callers, where the shape lints find nothing.
|
|
818
|
+
*/
|
|
819
|
+
shapes?: ReadonlyArray<AdvisorShape>;
|
|
820
|
+
/**
|
|
677
821
|
* Per-shard observed traffic — the `hot_shard` lint input. Supplied by the
|
|
678
822
|
* studio backend, which fans out over a sharded function's shards and reads
|
|
679
823
|
* each shard's recorded request volume from the durable `__lunora_metrics`
|
|
@@ -752,9 +896,9 @@ interface Lint {
|
|
|
752
896
|
title: string;
|
|
753
897
|
}
|
|
754
898
|
/**
|
|
755
|
-
* Minimal structural view of the `@lunora/analytics` SQL client — just its
|
|
899
|
+
* Minimal structural view of the `@lunora/bindings/analytics` SQL client — just its
|
|
756
900
|
* `query(sql)` method. Kept structural (not an `import type` from
|
|
757
|
-
* `@lunora/analytics`) so the advisor needn't depend on the analytics package;
|
|
901
|
+
* `@lunora/bindings/analytics`) so the advisor needn't depend on the analytics package;
|
|
758
902
|
* the real `AnalyticsSqlClient` satisfies it, as does a plain test double.
|
|
759
903
|
*/
|
|
760
904
|
interface AnalyticsMetricsSource {
|
|
@@ -997,6 +1141,40 @@ declare const duplicateIndex: Lint;
|
|
|
997
1141
|
*/
|
|
998
1142
|
declare const emptyIndex: Lint;
|
|
999
1143
|
/**
|
|
1144
|
+
* Flags a table that is both `.source(...)` and `.global()`.
|
|
1145
|
+
*
|
|
1146
|
+
* The two are contradictory. `.global()` already places a table in an external
|
|
1147
|
+
* store (D1, or a Hyperdrive-fronted Postgres/MySQL) that Lunora owns the schema
|
|
1148
|
+
* for and reads through the global backend. `.source(...)` declares the table as
|
|
1149
|
+
* **materialized from** an external database into a shard DO's SQLite by the
|
|
1150
|
+
* ingest poll loop. A table cannot simultaneously live in the global tier and be
|
|
1151
|
+
* polled into per-shard SQLite — the ingest loop has no DO-local table to write,
|
|
1152
|
+
* and the global backend has no poll loop. This is a definite misconfiguration,
|
|
1153
|
+
* so it is an `ERROR`.
|
|
1154
|
+
*
|
|
1155
|
+
* **Evidence supply**: reads `table.externalSource` + `table.shardKind`. Skipped
|
|
1156
|
+
* unless both a sourced declaration and the `global` tier are present.
|
|
1157
|
+
*/
|
|
1158
|
+
declare const externalSourceOnGlobal: Lint;
|
|
1159
|
+
/**
|
|
1160
|
+
* Flags a `.source(...)` + `.shardBy(...)` table that has no `tenantBy` mapper.
|
|
1161
|
+
*
|
|
1162
|
+
* Per-shard SQLite isolation only controls *where* materialized rows land — not
|
|
1163
|
+
* what* the ingest query pulls. A sourced + sharded table whose `tenantBy` is
|
|
1164
|
+
* absent runs the same unscoped membership query on every tenant's Durable
|
|
1165
|
+
* Object, so each agent replicates the **entire** multitenant table into its own
|
|
1166
|
+
* SQLite (and then to its clients via `defineShape`). That is a cross-tenant data
|
|
1167
|
+
* leak, not a performance nit — so it is an `ERROR` that fails the build.
|
|
1168
|
+
*
|
|
1169
|
+
* `tenantBy(shardKey)` is the boundary: it binds this DO's shard key into the
|
|
1170
|
+
* query's parameters so the tenant can only ever pull its own rows.
|
|
1171
|
+
*
|
|
1172
|
+
* **Evidence supply**: reads `table.externalSource` (the codegen feeder captures
|
|
1173
|
+
* it from `.source({...})`; the runtime feeder derives it). A table without a
|
|
1174
|
+
* sourced declaration, or one not sharded, is skipped.
|
|
1175
|
+
*/
|
|
1176
|
+
declare const externalSourceUnscoped: Lint;
|
|
1177
|
+
/**
|
|
1000
1178
|
* Flags a query read that calls `.filter()` without first narrowing with
|
|
1001
1179
|
* `.withIndex()` / `.withSearchIndex()`. Such a read loads *every* row of the
|
|
1002
1180
|
* table and applies the predicate in memory — a full table scan that degrades
|
|
@@ -1099,6 +1277,30 @@ declare const indexReferencesUnknownField: Lint;
|
|
|
1099
1277
|
*/
|
|
1100
1278
|
declare const maskUncoveredPiiColumn: Lint;
|
|
1101
1279
|
/**
|
|
1280
|
+
* Flags a custom mutator whose authoritative `server` impl writes a row with
|
|
1281
|
+
* `ctx.db.replace(id, document)` — a whole-document overwrite.
|
|
1282
|
+
*
|
|
1283
|
+
* The local-first sync engine serializes mutators in the shard DO, so two
|
|
1284
|
+
* mutators that touch the *same row* but *different columns* both run to
|
|
1285
|
+
* completion — but only if each writes its own column. A `replace` overwrites
|
|
1286
|
+
* the entire row from the document the mutator assembled, so a concurrent edit
|
|
1287
|
+
* to another column (committed between this mutator's read and its write, or
|
|
1288
|
+
* carried as a pending optimistic overlay on a client) is silently clobbered:
|
|
1289
|
+
* the kind of "two offline edits to different fields fight each other" data loss
|
|
1290
|
+
* a column-level merge avoids. `ctx.db.patch(id, { onlyTheChangedField })`
|
|
1291
|
+
* merges at the column level instead, so independent field edits coexist.
|
|
1292
|
+
*
|
|
1293
|
+
* `WARN`, not `ERROR`: `replace` is legitimate when the mutator genuinely owns
|
|
1294
|
+
* the whole row (a full-form save, a state-machine transition that rewrites
|
|
1295
|
+
* every field). The lint just surfaces the column-clobber risk so a developer
|
|
1296
|
+
* reaches for `patch` by default on a synced table.
|
|
1297
|
+
*
|
|
1298
|
+
* **Evidence supply**: runs only when the codegen feeder supplies
|
|
1299
|
+
* `context.mutatorWrites` (each a `replace` call lifted from a mutator's inline
|
|
1300
|
+
* `server` body); absent for runtime callers, where the lint finds nothing.
|
|
1301
|
+
*/
|
|
1302
|
+
declare const mutatorFullRowReplace: Lint;
|
|
1303
|
+
/**
|
|
1102
1304
|
* Flags a non-deterministic API call inside a `query(...)` or `mutation(...)`
|
|
1103
1305
|
* handler body.
|
|
1104
1306
|
*
|
|
@@ -1172,6 +1374,31 @@ declare const publicArgumentUsesAny: Lint;
|
|
|
1172
1374
|
*/
|
|
1173
1375
|
declare const publicMutationWithoutRatelimit: Lint;
|
|
1174
1376
|
/**
|
|
1377
|
+
* Flags an R2 SQL `ctx.r2sql` access inside a `query(...)` or `mutation(...)`
|
|
1378
|
+
* handler body.
|
|
1379
|
+
*
|
|
1380
|
+
* R2 SQL (`@lunora/bindings/r2sql`) queries Apache Iceberg tables over an **external**
|
|
1381
|
+
* REST endpoint Lunora does not own — there is no Workers binding, every query
|
|
1382
|
+
* is an HTTPS round-trip. A `ctx.r2sql` query is therefore non-deterministic
|
|
1383
|
+
* (exactly like `fetch`), which breaks the determinism the coordinator relies on
|
|
1384
|
+
* when it re-runs a query on subscription re-evaluation or a mutation on OCC
|
|
1385
|
+
* retry. And R2 SQL reads are invisible to the DO/SQLite change-feed, so a
|
|
1386
|
+
* subscription will never re-fire on them. `ctx.r2sql` is therefore wired onto
|
|
1387
|
+
* `ActionCtx` **only** and belongs exclusively in `action(...)` handlers; using
|
|
1388
|
+
* it in a query/mutation is the same class of bug as `fetch`/`Date.now`.
|
|
1389
|
+
*
|
|
1390
|
+
* This mirrors `hyperdrive_outside_action` — the action-only enforcement teeth
|
|
1391
|
+
* for external, non-reactive I/O. Runtime enforcement is still absent (see
|
|
1392
|
+
* `MEMORY.md` "Query/mutation determinism not enforced"), so the lint is the
|
|
1393
|
+
* guardrail.
|
|
1394
|
+
*
|
|
1395
|
+
* This lint runs when the codegen feeder has supplied access evidence
|
|
1396
|
+
* (`context.r2sqlCalls` present); a runtime caller with no evidence flags nothing
|
|
1397
|
+
* rather than raising false alarms. The feeder records accesses only inside
|
|
1398
|
+
* `query`/`mutation` handlers, so `action(...)` bodies never reach here.
|
|
1399
|
+
*/
|
|
1400
|
+
declare const r2sqlOutsideAction: Lint;
|
|
1401
|
+
/**
|
|
1175
1402
|
* A correctness lint covering the columns a relation wires together: the FK
|
|
1176
1403
|
* `field` and the `references` column must each exist on their respective
|
|
1177
1404
|
* tables, or the join can never resolve. Caught here at codegen time rather
|
|
@@ -1226,6 +1453,48 @@ declare const relationReferencesUnknownTable: Lint;
|
|
|
1226
1453
|
*/
|
|
1227
1454
|
declare const rlsUncoveredTable: Lint;
|
|
1228
1455
|
/**
|
|
1456
|
+
* Flags a replication shape whose `table` is a `.global()` table.
|
|
1457
|
+
*
|
|
1458
|
+
* Poke-live replication is a per-shard-DO property: the shard owns its SQLite
|
|
1459
|
+
* and a monotonic `__cdc_log`, so a write produces an ordered op the DO pokes to
|
|
1460
|
+
* every subscriber at the next flush. A `.global()` table lives outside the
|
|
1461
|
+
* shard DO's SQLite op-log (in a global backend — D1, or Hyperdrive-fronted
|
|
1462
|
+
* Postgres/MySQL) — so a shape over a global table cannot be poke-live. It is
|
|
1463
|
+
* served through the cross-shard tier: **coordinator/poll-refreshed, latency-
|
|
1464
|
+
* tiered**, not live. That is a real and supported tier (it is the recommended
|
|
1465
|
+
* answer for cross-shard reads — denormalize, or move the joined table to
|
|
1466
|
+
* `.global()` and read through the global backend), but its freshness semantics
|
|
1467
|
+
* differ from a sharded shape's, so the boundary is surfaced rather than hidden.
|
|
1468
|
+
*
|
|
1469
|
+
* `WARN`, not `ERROR`: a global-table shape is a legitimate design once you
|
|
1470
|
+
* accept the poll-refresh latency; the lint just makes the tier explicit so a
|
|
1471
|
+
* developer does not assume poke-live freshness.
|
|
1472
|
+
*
|
|
1473
|
+
* **Evidence supply**: runs only when the codegen feeder supplies
|
|
1474
|
+
* `context.shapes`; the table's tier comes from the schema's `shardKind`. A
|
|
1475
|
+
* shape whose table is unknown (caught by `shape_unknown_table`) or whose tier
|
|
1476
|
+
* the feeder didn't supply is skipped.
|
|
1477
|
+
*/
|
|
1478
|
+
declare const shapeTargetsGlobalTable: Lint;
|
|
1479
|
+
/**
|
|
1480
|
+
* Flags a replication shape whose `table` names a table that does not exist in
|
|
1481
|
+
* the schema.
|
|
1482
|
+
*
|
|
1483
|
+
* `defineShape({ table: "messages", … })` binds a shape to a table by a plain
|
|
1484
|
+
* string. A live `subscribeShape("…")` resolves that shape server-side and runs
|
|
1485
|
+
* its membership query against the named table — so a typo, a stale name after a
|
|
1486
|
+
* rename, or a copy-paste mistake produces a shape that can never resolve a
|
|
1487
|
+
* rowset: the subscription seeds empty and then errors at the first flush
|
|
1488
|
+
* (`no such table`). This is a definite, build-time-detectable break, so it is
|
|
1489
|
+
* an `ERROR` — surfaced before the broken shape ever ships.
|
|
1490
|
+
*
|
|
1491
|
+
* **Evidence supply**: runs only when the codegen feeder supplies
|
|
1492
|
+
* `context.shapes`. A shape whose `table` wasn't a static string literal (no
|
|
1493
|
+
* resolvable name) is skipped rather than guessed at, so the lint under-reports
|
|
1494
|
+
* rather than raising false alarms.
|
|
1495
|
+
*/
|
|
1496
|
+
declare const shapeUnknownTable: Lint;
|
|
1497
|
+
/**
|
|
1229
1498
|
* Flags a `ctx.sql` tagged-template that splices an unparameterized
|
|
1230
1499
|
* string-building expression into the query.
|
|
1231
1500
|
*
|
|
@@ -1328,6 +1597,25 @@ declare const unindexedRelationTarget: Lint;
|
|
|
1328
1597
|
*/
|
|
1329
1598
|
declare const userCreatingMutationWithoutCaptcha: Lint;
|
|
1330
1599
|
/**
|
|
1600
|
+
* Flags a durable step name reused within one workflow.
|
|
1601
|
+
*
|
|
1602
|
+
* Cloudflare Workflows memoizes every `step.do` / `step.sleep` / `step.sleepUntil`
|
|
1603
|
+
* / `step.waitForEvent` call by its name: on replay the runtime returns the cached
|
|
1604
|
+
* result for a name it has already seen. Two distinct steps that share a name are
|
|
1605
|
+
* therefore a silent bug — the second call never runs its body and instead yields
|
|
1606
|
+
* the first's result, skipping the work (a charge, a write, an external wait)
|
|
1607
|
+
* without error. Hence `ERROR`/`INTERNAL`: it is a developer-facing correctness
|
|
1608
|
+
* defect in the workflow's own code, not a runtime-data nit.
|
|
1609
|
+
*
|
|
1610
|
+
* Only the first string-literal argument of each step call is compared; a step
|
|
1611
|
+
* named dynamically (`step.do(\`load-${id}\`, …)`) is omitted by the feeder, so a
|
|
1612
|
+
* deliberately-parameterized fan-out is never flagged. `ctx.runStep(stepDef, …)`
|
|
1613
|
+
* names (which come from `defineStep` in another file) are out of scope here.
|
|
1614
|
+
* Only runs when the declaration feeder supplied step evidence
|
|
1615
|
+
* (`workflow.steps` present); a runtime caller flags nothing.
|
|
1616
|
+
*/
|
|
1617
|
+
declare const workflowDuplicateStepName: Lint;
|
|
1618
|
+
/**
|
|
1331
1619
|
* A correctness lint: every `ctx.workflows.get("name")` call must reference a
|
|
1332
1620
|
* workflow that exists — i.e. a `defineWorkflow` export in `lunora/workflows.ts`.
|
|
1333
1621
|
* A `.get("x")` whose `"x"` resolves to no declared workflow is a typo or a
|
|
@@ -1389,4 +1677,4 @@ interface RunAdvisorOptions {
|
|
|
1389
1677
|
* `static` lints at build time and defer `runtime` lints to a live shard.
|
|
1390
1678
|
*/
|
|
1391
1679
|
declare const runAdvisor: (context: LintContext, options?: RunAdvisorOptions) => Finding[];
|
|
1392
|
-
export { AE_METRIC_EVENTS, ALL_LINTS, type AdvisorAdminRoute, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorContainer, type AdvisorHyperdriveCall, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorMaskProcedure, type AdvisorNondeterministicCall, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorRelation, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShardTraffic, type AdvisorSqlInterpolation, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorWorkflow, type AdvisorWorkflowCall, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type Category, type Facing, type Finding, type Level, type Lint, type LintContext, type LintSource, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, adminRouteWithoutGuard, authApiCallWithoutHeaders, circularFk, constraintValidator, containerOversizedInstance, containerPublicInternet, duplicateIndex, emptyIndex, filterWithoutIndex, fromServerSchema, hardcodedSecret, hotShard, hyperdriveOutsideAction, indexReferencesUnknownField, indexUtilization, loadAnalyticsRuntimeMetrics, maskUncoveredPiiColumn, nondeterministicQueryMutation, policyReferencesUnknownTable, publicArgumentUsesAny, publicMutationWithoutRatelimit, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, sqlInjectionRisk, tableWithoutInsert, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, userCreatingMutationWithoutCaptcha, workflowUnknownTarget, workflowUnused };
|
|
1680
|
+
export { AE_METRIC_EVENTS, ALL_LINTS, type AdvisorAdminRoute, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorContainer, type AdvisorHyperdriveCall, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorMaskProcedure, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorR2sqlCall, type AdvisorRelation, type AdvisorRlsProcedure, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSqlInterpolation, type AdvisorTable, type AdvisorTableSample, type AdvisorTableScan, type AdvisorWorkflow, type AdvisorWorkflowCall, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type Category, type Facing, type Finding, type Level, type Lint, type LintContext, type LintSource, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, adminRouteWithoutGuard, authApiCallWithoutHeaders, circularFk, constraintValidator, containerOversizedInstance, containerPublicInternet, duplicateIndex, emptyIndex, externalSourceOnGlobal, externalSourceUnscoped, filterWithoutIndex, fromServerSchema, hardcodedSecret, hotShard, hyperdriveOutsideAction, indexReferencesUnknownField, indexUtilization, loadAnalyticsRuntimeMetrics, maskUncoveredPiiColumn, mutatorFullRowReplace, nondeterministicQueryMutation, policyReferencesUnknownTable, publicArgumentUsesAny, publicMutationWithoutRatelimit, r2sqlOutsideAction, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, shapeTargetsGlobalTable, shapeUnknownTable, sqlInjectionRisk, tableWithoutInsert, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, userCreatingMutationWithoutCaptcha, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
|