@lunora/advisor 1.0.0-alpha.1 → 1.0.0-alpha.3
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 +60 -1
- package/dist/index.d.ts +60 -1
- package/dist/index.mjs +3 -1
- package/dist/packem_shared/r2sqlOutsideAction-CtqxvMuV.mjs +30 -0
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -285,6 +285,33 @@ interface AdvisorQueryRead {
|
|
|
285
285
|
table: string;
|
|
286
286
|
}
|
|
287
287
|
/**
|
|
288
|
+
* One `ctx.r2sql` access discovered lexically inside a `query(...)` or
|
|
289
|
+
* `mutation(...)` handler body — the input the `r2sql_outside_action` lint
|
|
290
|
+
* consumes. Produced by the codegen feeder, which walks each exported function's
|
|
291
|
+
* handler with ts-morph and records reads of the R2 SQL `ctx.r2sql` surface
|
|
292
|
+
* (`ctx.r2sql.query(...)`, `ctx.r2sql.from(...)`, …).
|
|
293
|
+
*
|
|
294
|
+
* R2 SQL queries Apache Iceberg tables over an **external** REST endpoint Lunora
|
|
295
|
+
* does not own (there is no Workers binding): a `ctx.r2sql` call is a network
|
|
296
|
+
* round-trip with a mutable result (non-deterministic, like `fetch`) and its
|
|
297
|
+
* reads are invisible to Lunora live queries. It therefore belongs **only** in
|
|
298
|
+
* `action(...)` handlers. Calls inside `action(...)` are intentionally **not**
|
|
299
|
+
* recorded — actions are the escape hatch. Runtime callers don't supply this, so
|
|
300
|
+
* the lint finds nothing there.
|
|
301
|
+
*/
|
|
302
|
+
interface AdvisorR2sqlCall {
|
|
303
|
+
/** The accessed `ctx.r2sql` surface, e.g. `ctx.r2sql.query` / `ctx.r2sql.from`. */
|
|
304
|
+
callee: string;
|
|
305
|
+
/** The exported function performing the access (e.g. `topPerRegion`). */
|
|
306
|
+
exportName: string;
|
|
307
|
+
/** Source file the access appears in (relative to the lunora dir, no extension). */
|
|
308
|
+
file: string;
|
|
309
|
+
/** Which procedure kind the access lives in — only `query`/`mutation` are flagged; actions are exempt. */
|
|
310
|
+
kind: "mutation" | "query";
|
|
311
|
+
/** 1-based line of the access, or `0` when unknown. */
|
|
312
|
+
line: number;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
288
315
|
* One procedure (query / mutation / action) discovered in the lunora source,
|
|
289
316
|
* reduced to the facts the `rls_uncovered_table` lint needs: whether the
|
|
290
317
|
* procedure's builder chain includes `.use(rls(...))`, and which tables the
|
|
@@ -657,6 +684,13 @@ interface LintContext {
|
|
|
657
684
|
*/
|
|
658
685
|
queries?: ReadonlyArray<AdvisorQueryRead>;
|
|
659
686
|
/**
|
|
687
|
+
* R2 SQL `ctx.r2sql` accesses discovered lexically inside `query`/`mutation`
|
|
688
|
+
* handler bodies — the `r2sql_outside_action` input. Supplied by the codegen
|
|
689
|
+
* feeder, which omits `action` handlers (where `ctx.r2sql` is the typed,
|
|
690
|
+
* intended surface); absent for runtime callers, where the lint finds nothing.
|
|
691
|
+
*/
|
|
692
|
+
r2sqlCalls?: ReadonlyArray<AdvisorR2sqlCall>;
|
|
693
|
+
/**
|
|
660
694
|
* Per-procedure RLS usage discovered in function bodies (the
|
|
661
695
|
* `rls_uncovered_table` input). Carries whether each procedure's builder chain
|
|
662
696
|
* includes `.use(rls(...))`, which tables the procedure reads/writes, and which
|
|
@@ -1172,6 +1206,31 @@ declare const publicArgumentUsesAny: Lint;
|
|
|
1172
1206
|
*/
|
|
1173
1207
|
declare const publicMutationWithoutRatelimit: Lint;
|
|
1174
1208
|
/**
|
|
1209
|
+
* Flags an R2 SQL `ctx.r2sql` access inside a `query(...)` or `mutation(...)`
|
|
1210
|
+
* handler body.
|
|
1211
|
+
*
|
|
1212
|
+
* R2 SQL (`@lunora/r2sql`) queries Apache Iceberg tables over an **external**
|
|
1213
|
+
* REST endpoint Lunora does not own — there is no Workers binding, every query
|
|
1214
|
+
* is an HTTPS round-trip. A `ctx.r2sql` query is therefore non-deterministic
|
|
1215
|
+
* (exactly like `fetch`), which breaks the determinism the coordinator relies on
|
|
1216
|
+
* when it re-runs a query on subscription re-evaluation or a mutation on OCC
|
|
1217
|
+
* retry. And R2 SQL reads are invisible to the DO/SQLite change-feed, so a
|
|
1218
|
+
* subscription will never re-fire on them. `ctx.r2sql` is therefore wired onto
|
|
1219
|
+
* `ActionCtx` **only** and belongs exclusively in `action(...)` handlers; using
|
|
1220
|
+
* it in a query/mutation is the same class of bug as `fetch`/`Date.now`.
|
|
1221
|
+
*
|
|
1222
|
+
* This mirrors `hyperdrive_outside_action` — the action-only enforcement teeth
|
|
1223
|
+
* for external, non-reactive I/O. Runtime enforcement is still absent (see
|
|
1224
|
+
* `MEMORY.md` "Query/mutation determinism not enforced"), so the lint is the
|
|
1225
|
+
* guardrail.
|
|
1226
|
+
*
|
|
1227
|
+
* This lint runs when the codegen feeder has supplied access evidence
|
|
1228
|
+
* (`context.r2sqlCalls` present); a runtime caller with no evidence flags nothing
|
|
1229
|
+
* rather than raising false alarms. The feeder records accesses only inside
|
|
1230
|
+
* `query`/`mutation` handlers, so `action(...)` bodies never reach here.
|
|
1231
|
+
*/
|
|
1232
|
+
declare const r2sqlOutsideAction: Lint;
|
|
1233
|
+
/**
|
|
1175
1234
|
* A correctness lint covering the columns a relation wires together: the FK
|
|
1176
1235
|
* `field` and the `references` column must each exist on their respective
|
|
1177
1236
|
* tables, or the join can never resolve. Caught here at codegen time rather
|
|
@@ -1389,4 +1448,4 @@ interface RunAdvisorOptions {
|
|
|
1389
1448
|
* `static` lints at build time and defer `runtime` lints to a live shard.
|
|
1390
1449
|
*/
|
|
1391
1450
|
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 };
|
|
1451
|
+
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 AdvisorR2sqlCall, 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, r2sqlOutsideAction, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, sqlInjectionRisk, tableWithoutInsert, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, userCreatingMutationWithoutCaptcha, workflowUnknownTarget, workflowUnused };
|
package/dist/index.d.ts
CHANGED
|
@@ -285,6 +285,33 @@ interface AdvisorQueryRead {
|
|
|
285
285
|
table: string;
|
|
286
286
|
}
|
|
287
287
|
/**
|
|
288
|
+
* One `ctx.r2sql` access discovered lexically inside a `query(...)` or
|
|
289
|
+
* `mutation(...)` handler body — the input the `r2sql_outside_action` lint
|
|
290
|
+
* consumes. Produced by the codegen feeder, which walks each exported function's
|
|
291
|
+
* handler with ts-morph and records reads of the R2 SQL `ctx.r2sql` surface
|
|
292
|
+
* (`ctx.r2sql.query(...)`, `ctx.r2sql.from(...)`, …).
|
|
293
|
+
*
|
|
294
|
+
* R2 SQL queries Apache Iceberg tables over an **external** REST endpoint Lunora
|
|
295
|
+
* does not own (there is no Workers binding): a `ctx.r2sql` call is a network
|
|
296
|
+
* round-trip with a mutable result (non-deterministic, like `fetch`) and its
|
|
297
|
+
* reads are invisible to Lunora live queries. It therefore belongs **only** in
|
|
298
|
+
* `action(...)` handlers. Calls inside `action(...)` are intentionally **not**
|
|
299
|
+
* recorded — actions are the escape hatch. Runtime callers don't supply this, so
|
|
300
|
+
* the lint finds nothing there.
|
|
301
|
+
*/
|
|
302
|
+
interface AdvisorR2sqlCall {
|
|
303
|
+
/** The accessed `ctx.r2sql` surface, e.g. `ctx.r2sql.query` / `ctx.r2sql.from`. */
|
|
304
|
+
callee: string;
|
|
305
|
+
/** The exported function performing the access (e.g. `topPerRegion`). */
|
|
306
|
+
exportName: string;
|
|
307
|
+
/** Source file the access appears in (relative to the lunora dir, no extension). */
|
|
308
|
+
file: string;
|
|
309
|
+
/** Which procedure kind the access lives in — only `query`/`mutation` are flagged; actions are exempt. */
|
|
310
|
+
kind: "mutation" | "query";
|
|
311
|
+
/** 1-based line of the access, or `0` when unknown. */
|
|
312
|
+
line: number;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
288
315
|
* One procedure (query / mutation / action) discovered in the lunora source,
|
|
289
316
|
* reduced to the facts the `rls_uncovered_table` lint needs: whether the
|
|
290
317
|
* procedure's builder chain includes `.use(rls(...))`, and which tables the
|
|
@@ -657,6 +684,13 @@ interface LintContext {
|
|
|
657
684
|
*/
|
|
658
685
|
queries?: ReadonlyArray<AdvisorQueryRead>;
|
|
659
686
|
/**
|
|
687
|
+
* R2 SQL `ctx.r2sql` accesses discovered lexically inside `query`/`mutation`
|
|
688
|
+
* handler bodies — the `r2sql_outside_action` input. Supplied by the codegen
|
|
689
|
+
* feeder, which omits `action` handlers (where `ctx.r2sql` is the typed,
|
|
690
|
+
* intended surface); absent for runtime callers, where the lint finds nothing.
|
|
691
|
+
*/
|
|
692
|
+
r2sqlCalls?: ReadonlyArray<AdvisorR2sqlCall>;
|
|
693
|
+
/**
|
|
660
694
|
* Per-procedure RLS usage discovered in function bodies (the
|
|
661
695
|
* `rls_uncovered_table` input). Carries whether each procedure's builder chain
|
|
662
696
|
* includes `.use(rls(...))`, which tables the procedure reads/writes, and which
|
|
@@ -1172,6 +1206,31 @@ declare const publicArgumentUsesAny: Lint;
|
|
|
1172
1206
|
*/
|
|
1173
1207
|
declare const publicMutationWithoutRatelimit: Lint;
|
|
1174
1208
|
/**
|
|
1209
|
+
* Flags an R2 SQL `ctx.r2sql` access inside a `query(...)` or `mutation(...)`
|
|
1210
|
+
* handler body.
|
|
1211
|
+
*
|
|
1212
|
+
* R2 SQL (`@lunora/r2sql`) queries Apache Iceberg tables over an **external**
|
|
1213
|
+
* REST endpoint Lunora does not own — there is no Workers binding, every query
|
|
1214
|
+
* is an HTTPS round-trip. A `ctx.r2sql` query is therefore non-deterministic
|
|
1215
|
+
* (exactly like `fetch`), which breaks the determinism the coordinator relies on
|
|
1216
|
+
* when it re-runs a query on subscription re-evaluation or a mutation on OCC
|
|
1217
|
+
* retry. And R2 SQL reads are invisible to the DO/SQLite change-feed, so a
|
|
1218
|
+
* subscription will never re-fire on them. `ctx.r2sql` is therefore wired onto
|
|
1219
|
+
* `ActionCtx` **only** and belongs exclusively in `action(...)` handlers; using
|
|
1220
|
+
* it in a query/mutation is the same class of bug as `fetch`/`Date.now`.
|
|
1221
|
+
*
|
|
1222
|
+
* This mirrors `hyperdrive_outside_action` — the action-only enforcement teeth
|
|
1223
|
+
* for external, non-reactive I/O. Runtime enforcement is still absent (see
|
|
1224
|
+
* `MEMORY.md` "Query/mutation determinism not enforced"), so the lint is the
|
|
1225
|
+
* guardrail.
|
|
1226
|
+
*
|
|
1227
|
+
* This lint runs when the codegen feeder has supplied access evidence
|
|
1228
|
+
* (`context.r2sqlCalls` present); a runtime caller with no evidence flags nothing
|
|
1229
|
+
* rather than raising false alarms. The feeder records accesses only inside
|
|
1230
|
+
* `query`/`mutation` handlers, so `action(...)` bodies never reach here.
|
|
1231
|
+
*/
|
|
1232
|
+
declare const r2sqlOutsideAction: Lint;
|
|
1233
|
+
/**
|
|
1175
1234
|
* A correctness lint covering the columns a relation wires together: the FK
|
|
1176
1235
|
* `field` and the `references` column must each exist on their respective
|
|
1177
1236
|
* tables, or the join can never resolve. Caught here at codegen time rather
|
|
@@ -1389,4 +1448,4 @@ interface RunAdvisorOptions {
|
|
|
1389
1448
|
* `static` lints at build time and defer `runtime` lints to a live shard.
|
|
1390
1449
|
*/
|
|
1391
1450
|
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 };
|
|
1451
|
+
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 AdvisorR2sqlCall, 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, r2sqlOutsideAction, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, sqlInjectionRisk, tableWithoutInsert, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, userCreatingMutationWithoutCaptcha, workflowUnknownTarget, workflowUnused };
|
package/dist/index.mjs
CHANGED
|
@@ -17,6 +17,7 @@ import nondeterministicQueryMutation from './packem_shared/nondeterministicQuery
|
|
|
17
17
|
import policyReferencesUnknownTable from './packem_shared/policyReferencesUnknownTable-DtaIEovd.mjs';
|
|
18
18
|
import publicArgumentUsesAny from './packem_shared/publicArgumentUsesAny-C71b2NCf.mjs';
|
|
19
19
|
import publicMutationWithoutRatelimit from './packem_shared/publicMutationWithoutRatelimit-xBpJ6GWK.mjs';
|
|
20
|
+
import r2sqlOutsideAction from './packem_shared/r2sqlOutsideAction-CtqxvMuV.mjs';
|
|
20
21
|
import relationReferencesUnknownField from './packem_shared/relationReferencesUnknownField-YznyXt_7.mjs';
|
|
21
22
|
import relationReferencesUnknownTable from './packem_shared/relationReferencesUnknownTable-DrorpKYe.mjs';
|
|
22
23
|
import rlsUncoveredTable from './packem_shared/rlsUncoveredTable-CxEfZ5eZ.mjs';
|
|
@@ -46,6 +47,7 @@ const STATIC_LINTS = [
|
|
|
46
47
|
filterWithoutIndex,
|
|
47
48
|
nondeterministicQueryMutation,
|
|
48
49
|
hyperdriveOutsideAction,
|
|
50
|
+
r2sqlOutsideAction,
|
|
49
51
|
authApiCallWithoutHeaders,
|
|
50
52
|
policyReferencesUnknownTable,
|
|
51
53
|
rlsUncoveredTable,
|
|
@@ -74,4 +76,4 @@ const runAdvisor = (context, options = {}) => {
|
|
|
74
76
|
return findings;
|
|
75
77
|
};
|
|
76
78
|
|
|
77
|
-
export { ALL_LINTS, RUNTIME_LINTS, STATIC_LINTS, adminRouteWithoutGuard, authApiCallWithoutHeaders, circularFk, constraintValidator, containerOversizedInstance, containerPublicInternet, duplicateIndex, emptyIndex, filterWithoutIndex, hardcodedSecret, hotShard, hyperdriveOutsideAction, indexReferencesUnknownField, indexUtilization, maskUncoveredPiiColumn, nondeterministicQueryMutation, policyReferencesUnknownTable, publicArgumentUsesAny, publicMutationWithoutRatelimit, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, sqlInjectionRisk, tableWithoutInsert, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, userCreatingMutationWithoutCaptcha, workflowUnknownTarget, workflowUnused };
|
|
79
|
+
export { ALL_LINTS, RUNTIME_LINTS, STATIC_LINTS, adminRouteWithoutGuard, authApiCallWithoutHeaders, circularFk, constraintValidator, containerOversizedInstance, containerPublicInternet, duplicateIndex, emptyIndex, filterWithoutIndex, hardcodedSecret, hotShard, hyperdriveOutsideAction, indexReferencesUnknownField, indexUtilization, maskUncoveredPiiColumn, nondeterministicQueryMutation, policyReferencesUnknownTable, publicArgumentUsesAny, publicMutationWithoutRatelimit, r2sqlOutsideAction, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, sqlInjectionRisk, tableWithoutInsert, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, userCreatingMutationWithoutCaptcha, workflowUnknownTarget, workflowUnused };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { e as emit } from './finding-Dm_zvzS1.mjs';
|
|
2
|
+
|
|
3
|
+
const r2sqlOutsideAction = {
|
|
4
|
+
categories: ["SCHEMA"],
|
|
5
|
+
description: "A `query`/`mutation` handler accesses R2 SQL via `ctx.r2sql`. R2 SQL queries external Apache Iceberg tables over REST (no Workers binding): queries are non-deterministic (like `fetch`) and the reads are invisible to live queries. `ctx.r2sql` is available on `ActionCtx` only and must be confined to `action` handlers.",
|
|
6
|
+
facing: "EXTERNAL",
|
|
7
|
+
level: "WARN",
|
|
8
|
+
name: "r2sql_outside_action",
|
|
9
|
+
remediation: "Move the `ctx.r2sql` access into an `action(...)` (the only context where it is typed), where external I/O is allowed. If a query/mutation needs the data, have the action read it via `ctx.r2sql` and write a projection into a `defineSchema` DO/D1 table — that write is tracked by live queries, whereas R2 SQL is not.",
|
|
10
|
+
run: (context) => {
|
|
11
|
+
if (context.r2sqlCalls === void 0) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
const findings = [];
|
|
15
|
+
for (const call of context.r2sqlCalls) {
|
|
16
|
+
findings.push(
|
|
17
|
+
emit(r2sqlOutsideAction, {
|
|
18
|
+
cacheKey: `r2sql_outside_action:${call.file}:${call.line.toString()}:${call.callee}`,
|
|
19
|
+
detail: `\`${call.callee}(…)\` in ${call.exportName} (${call.file}:${call.line.toString()}) runs inside a ${call.kind} handler — R2 SQL's \`ctx.r2sql\` is non-deterministic and non-reactive, so it is available only in actions. Move the external query into an \`action\` and project the result into a Lunora table if a query/mutation needs it.`,
|
|
20
|
+
metadata: { callee: call.callee, exportName: call.exportName, file: call.file, kind: call.kind, line: call.line }
|
|
21
|
+
})
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return findings;
|
|
25
|
+
},
|
|
26
|
+
source: "static",
|
|
27
|
+
title: "R2 SQL `ctx.r2sql` used outside an action"
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export { r2sqlOutsideAction as default };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/advisor",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.3",
|
|
4
4
|
"description": "Schema & query lints (splinter-style advisors) for Lunora, feeding the Studio Advisors view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"advisor",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@lunora/server": "1.0.0-alpha.
|
|
49
|
+
"@lunora/server": "1.0.0-alpha.2"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
52
|
"node": "^22.15.0 || >=24.11.0"
|