@lunora/advisor 1.0.0-alpha.210 → 1.0.0-alpha.212
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 +116 -4
- package/dist/index.d.ts +116 -4
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/containerLongSleepAfter-CVvHEGYC.mjs +1 -0
- package/dist/packem_shared/countWithoutIndex-Dn6rB2Mb.mjs +1 -0
- package/dist/packem_shared/dispatchCycle-BwuXRDiL.mjs +1 -0
- package/dist/packem_shared/{fromServerSchema-fAymN8o2.mjs → fromServerSchema-KWJd4PLE.mjs} +1 -1
- package/dist/packem_shared/graph-CrSMHkOs.mjs +1 -0
- package/dist/packem_shared/queueDispatchCycle-84yt1IR-.mjs +1 -0
- package/dist/packem_shared/relationReferencesUnknownField-Cl6mik8w.mjs +1 -0
- package/dist/packem_shared/relationReferencesUnknownTable-vYjirSxf.mjs +1 -0
- package/dist/packem_shared/selfRescheduleWithoutProgress-CQNZI4FA.mjs +1 -0
- package/dist/packem_shared/throughRelationMissingUniqueIndex-Cg5e3GkS.mjs +1 -0
- package/dist/packem_shared/unindexedRelationTarget-Dj2_NOCm.mjs +1 -0
- package/package.json +2 -2
- package/dist/packem_shared/dispatchCycle-CWc5Q0cd.mjs +0 -1
- package/dist/packem_shared/relationReferencesUnknownField-CJHOCSzs.mjs +0 -1
- package/dist/packem_shared/relationReferencesUnknownTable-CP4aWtAJ.mjs +0 -1
- package/dist/packem_shared/selfRescheduleWithoutProgress-Be9d1bQ0.mjs +0 -1
- package/dist/packem_shared/unindexedRelationTarget-CSqRJWYZ.mjs +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -285,6 +285,11 @@ interface AdvisorCallEdge {
|
|
|
285
285
|
kind: "call" | "enqueue" | "invoke" | "publish" | "schedule";
|
|
286
286
|
/** 1-based line of the call, or `0` when unknown. */
|
|
287
287
|
line: number;
|
|
288
|
+
/**
|
|
289
|
+
* `true` alongside `conditional` when only a loop's body stands in front of the hop.
|
|
290
|
+
* A queue consumer's batch loop sets it: the hop runs once per message it receives.
|
|
291
|
+
*/
|
|
292
|
+
loopOnly?: true;
|
|
288
293
|
/** Why the target could not be read statically — set exactly when `target` is absent. */
|
|
289
294
|
reason?: string;
|
|
290
295
|
/**
|
|
@@ -391,6 +396,8 @@ interface AdvisorContainer {
|
|
|
391
396
|
enableInternet?: boolean;
|
|
392
397
|
/** The `lunora/containers.ts` export name, e.g. `transcoder`. */
|
|
393
398
|
exportName: string;
|
|
399
|
+
/** Declared `hardTimeout` value, when a static literal. Caps total lifetime, idle or not. */
|
|
400
|
+
hardTimeout?: number | string;
|
|
394
401
|
/** Declared `instanceType`: a named size, or a custom `{ vcpu, memoryMib, diskMb }`. */
|
|
395
402
|
instanceType?: string | {
|
|
396
403
|
diskMb?: number;
|
|
@@ -402,6 +409,27 @@ interface AdvisorContainer {
|
|
|
402
409
|
/** Declared `sleepAfter` value, when a static literal. */
|
|
403
410
|
sleepAfter?: number | string;
|
|
404
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* One `count()` call discovered in a function body — the input the `count_without_index`
|
|
414
|
+
* lint consumes. Produced by the codegen feeder (which reads `ctx.db.count("table", …)` and
|
|
415
|
+
* `ctx.db.<table>.count(…)` from the AST); runtime callers don't supply it, so the lint
|
|
416
|
+
* finds nothing there.
|
|
417
|
+
*/
|
|
418
|
+
interface AdvisorCountRead {
|
|
419
|
+
/** Source file the call appears in (relative to the lunora dir, no extension). */
|
|
420
|
+
file: string;
|
|
421
|
+
/**
|
|
422
|
+
* `false` only when the table is known and declares no aggregate index, so the count is a
|
|
423
|
+
* `COUNT(*)` scan. A counter-backed or unknown table reads `true`.
|
|
424
|
+
*/
|
|
425
|
+
indexed: boolean;
|
|
426
|
+
/** 1-based line of the `count(...)` call. */
|
|
427
|
+
line: number;
|
|
428
|
+
/** Who the call runs on behalf of. */
|
|
429
|
+
scope: AdvisorCallSiteScope;
|
|
430
|
+
/** Counted table name, or `""` when the table argument is not a string literal. */
|
|
431
|
+
table: string;
|
|
432
|
+
}
|
|
405
433
|
/**
|
|
406
434
|
* One CDC export-sink construction discovered in a function body — the input the
|
|
407
435
|
* `export_sink_misconfigured` lint consumes. Produced by the codegen feeder,
|
|
@@ -1303,6 +1331,19 @@ interface AdvisorQueueTuning {
|
|
|
1303
1331
|
interface AdvisorQueue {
|
|
1304
1332
|
/** The `lunora/queues.ts` export name, e.g. `notifications`. */
|
|
1305
1333
|
exportName: string;
|
|
1334
|
+
/**
|
|
1335
|
+
* The declaring file, lunora-relative without `.ts` (`queues`, or `billing/queues`).
|
|
1336
|
+
* Its handler's own call sites are attributed to `exportName` in this file.
|
|
1337
|
+
*/
|
|
1338
|
+
filePath?: string;
|
|
1339
|
+
/**
|
|
1340
|
+
* Where a `handler:` passed by reference is declared, when it is exported from a
|
|
1341
|
+
* lunora file. The handler's call sites then belong to this queue, under that export.
|
|
1342
|
+
*/
|
|
1343
|
+
handlerSite?: {
|
|
1344
|
+
exportName: string;
|
|
1345
|
+
file: string;
|
|
1346
|
+
};
|
|
1306
1347
|
/** How the queue is consumed: `"push"` (a worker `queue()` handler) or `"pull"` (external HTTP). */
|
|
1307
1348
|
mode: "pull" | "push";
|
|
1308
1349
|
/**
|
|
@@ -1605,14 +1646,30 @@ interface AdvisorExternalSource {
|
|
|
1605
1646
|
* the holding table; for `many` it lives on the target. `name` is the accessor
|
|
1606
1647
|
* the relation is loaded under.
|
|
1607
1648
|
*/
|
|
1608
|
-
interface
|
|
1649
|
+
interface AdvisorKeyedRelation {
|
|
1609
1650
|
field: string;
|
|
1610
1651
|
kind: "many" | "one";
|
|
1611
1652
|
name: string;
|
|
1612
1653
|
onDelete?: "cascade" | "restrict" | "set null";
|
|
1613
1654
|
references: string;
|
|
1614
1655
|
table: string;
|
|
1656
|
+
through?: never;
|
|
1615
1657
|
}
|
|
1658
|
+
/**
|
|
1659
|
+
* A many-to-many relation (`r.many(target, { through })`): the link lives in the `through.table` join table,
|
|
1660
|
+
* whose `from` column points at the declaring table and `to` at `table`. It has no `field` of its own.
|
|
1661
|
+
*/
|
|
1662
|
+
interface AdvisorThroughRelation {
|
|
1663
|
+
kind: "many";
|
|
1664
|
+
name: string;
|
|
1665
|
+
table: string;
|
|
1666
|
+
through: {
|
|
1667
|
+
from: string;
|
|
1668
|
+
table: string;
|
|
1669
|
+
to: string;
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
type AdvisorRelation = AdvisorKeyedRelation | AdvisorThroughRelation;
|
|
1616
1673
|
/**
|
|
1617
1674
|
* Adapt the runtime `@lunora/server` {@link Schema} into an {@link AdvisorSchema}.
|
|
1618
1675
|
* Runtime callers (the studio backend, a live shard) hold the real schema
|
|
@@ -2135,6 +2192,8 @@ interface LintContext {
|
|
|
2135
2192
|
* the container lints find nothing.
|
|
2136
2193
|
*/
|
|
2137
2194
|
containers?: ReadonlyArray<AdvisorContainer>;
|
|
2195
|
+
/** Every `count()` call found in function bodies; the `count_without_index` lint reads it. Absent for a runtime caller. */
|
|
2196
|
+
countReads?: ReadonlyArray<AdvisorCountRead>;
|
|
2138
2197
|
/**
|
|
2139
2198
|
* The `namespace:export` keys of the function-targeted cron jobs. A cron fires
|
|
2140
2199
|
* its target without a request, so the target is server-driven even when it is
|
|
@@ -3116,6 +3175,18 @@ declare const commitOrderedHardDelete: Lint;
|
|
|
3116
3175
|
* finding per arg-derived, unscoped `ctx.containers.*.get` call.
|
|
3117
3176
|
*/
|
|
3118
3177
|
declare const containerInstanceKeyFromUserInput: Lint;
|
|
3178
|
+
/**
|
|
3179
|
+
* Flags a container whose idle window is longer than an hour. An instance that stops
|
|
3180
|
+
* receiving work keeps running, and billed, until `sleepAfter` elapses, so a long
|
|
3181
|
+
* window is a standing cost for every idle instance. The window an instance can really
|
|
3182
|
+
* idle for is the shorter of `sleepAfter` and `hardTimeout`, since `hardTimeout` stops
|
|
3183
|
+
* the instance however active it is. The check uses that effective window.
|
|
3184
|
+
*
|
|
3185
|
+
* A `sleepAfter` that was not a static literal, or that was omitted (the platform's
|
|
3186
|
+
* 10 minute default), is not flagged. Runs only when the codegen feeder supplied the
|
|
3187
|
+
* container declarations, so a runtime caller flags nothing.
|
|
3188
|
+
*/
|
|
3189
|
+
declare const containerLongSleepAfter: Lint;
|
|
3119
3190
|
/**
|
|
3120
3191
|
* Flags a container declared on a large instance type. The big `standard-3` /
|
|
3121
3192
|
* `standard-4` sizes (and large custom shapes) are billed on their provisioned
|
|
@@ -3139,6 +3210,18 @@ declare const containerOversizedInstance: Lint;
|
|
|
3139
3210
|
declare const containerPublicInternet: Lint;
|
|
3140
3211
|
declare const containerRuntimeEgressRelaxation: Lint;
|
|
3141
3212
|
declare const containerStartEnableInternetOverride: Lint;
|
|
3213
|
+
/**
|
|
3214
|
+
* Flags a `count()` on a table that declares no aggregate index. `count()` reads a counter
|
|
3215
|
+
* only when an aggregate index covers it; otherwise it runs `SELECT COUNT(*)` over the
|
|
3216
|
+
* table, reading every row on each call. A count that runs per request or per poll repeats
|
|
3217
|
+
* that scan every time, and its cost grows with the table.
|
|
3218
|
+
*
|
|
3219
|
+
* The verdict is per table. A table that declares an aggregate index is not flagged, even
|
|
3220
|
+
* when a particular `where` falls outside that index, because the feeder cannot judge the
|
|
3221
|
+
* index's coverage. That is a known false negative. The count reads come from the codegen
|
|
3222
|
+
* feeder; runtime callers supply none, so this lint is a no-op there.
|
|
3223
|
+
*/
|
|
3224
|
+
declare const countWithoutIndex: Lint;
|
|
3142
3225
|
/**
|
|
3143
3226
|
* Flags a function that writes to a table another module declares it owns
|
|
3144
3227
|
* (`defineModule({ tables })`). Ownership is a boundary the app opted into: a
|
|
@@ -3176,7 +3259,7 @@ declare const crossModuleTableWrite: Lint;
|
|
|
3176
3259
|
* cancelled or rolled back, drops out. Only hops in an export's own body count
|
|
3177
3260
|
* — a helper's call may be guarded where the helper is called — and only
|
|
3178
3261
|
* static targets. Re-arming outside `ctx.scheduler` (a raw Durable Object
|
|
3179
|
-
* `setAlarm
|
|
3262
|
+
* `setAlarm`) is not modelled; a queue re-enqueue is `queue_dispatch_cycle`'s.
|
|
3180
3263
|
*
|
|
3181
3264
|
* Runs only when the codegen feeder supplied call edges (`context.callEdges`
|
|
3182
3265
|
* present); a runtime caller flags nothing.
|
|
@@ -4148,6 +4231,21 @@ declare const publicMutationWithoutRatelimit: Lint;
|
|
|
4148
4231
|
* from codegen, with no feeder required.
|
|
4149
4232
|
*/
|
|
4150
4233
|
declare const publicTableRlsOptoutConfusion: Lint;
|
|
4234
|
+
/**
|
|
4235
|
+
* Flags a cycle through a queue or a topic in which every hop runs unconditionally: a
|
|
4236
|
+
* queue consumer that (directly, or through a function it runs) sends back to its own
|
|
4237
|
+
* queue, or a topic whose subscriber leads back to a publish. Each message handled then
|
|
4238
|
+
* produces another, so the queue never drains, and every delivery is billed.
|
|
4239
|
+
*
|
|
4240
|
+
* The cycle is built from the same call-site edges `dispatch_cycle` reads, plus the
|
|
4241
|
+
* enqueue, publish and subscription edges it ignores. A queue consumer is a node: its
|
|
4242
|
+
* call sites start from the queue, and its handler's batch loop counts as a loop, not a
|
|
4243
|
+
* guard. A cycle with no queue or topic node is `dispatch_cycle`'s, so this lint reports
|
|
4244
|
+
* only the components that include one.
|
|
4245
|
+
*
|
|
4246
|
+
* Runs only when the codegen feeder supplied call edges and queues; a runtime caller flags nothing.
|
|
4247
|
+
*/
|
|
4248
|
+
declare const queueDispatchCycle: Lint;
|
|
4151
4249
|
/**
|
|
4152
4250
|
* Flags a declared queue — push or pull — that has no dead-letter queue.
|
|
4153
4251
|
*
|
|
@@ -4317,7 +4415,10 @@ declare const rlsUncoveredTable: Lint;
|
|
|
4317
4415
|
*
|
|
4318
4416
|
* An unconditional self-dispatch is `dispatch_cycle`'s, which already reports it as
|
|
4319
4417
|
* an error. This rule takes only the conditional ones, so each site gets one finding.
|
|
4320
|
-
*
|
|
4418
|
+
*
|
|
4419
|
+
* Two bands. Under {@link SHORT_DELAY_MS} is a spin (WARN): any row change counts as
|
|
4420
|
+
* progress. From there up to {@link POLL_DELAY_MS} is a poll (INFO): a rewrite of a row
|
|
4421
|
+
* on every tick is not progress, so only a removal clears it. Longer is left alone.
|
|
4321
4422
|
*
|
|
4322
4423
|
* Runs only when the codegen feeder supplied call edges (`context.callEdges` present).
|
|
4323
4424
|
*/
|
|
@@ -4569,6 +4670,17 @@ declare const storageUploadWithoutMaxSize: Lint;
|
|
|
4569
4670
|
* a runtime caller with no insert signal flags nothing rather than every table.
|
|
4570
4671
|
*/
|
|
4571
4672
|
declare const tableWithoutInsert: Lint;
|
|
4673
|
+
/**
|
|
4674
|
+
* A correctness lint for many-to-many relations: the join table behind
|
|
4675
|
+
* `r.many(target, { through: { table, from, to } })` must carry a unique index over
|
|
4676
|
+
* exactly `(from, to)`.
|
|
4677
|
+
*
|
|
4678
|
+
* Without it a link is not a single row: a repeated `connect` stores the same pair
|
|
4679
|
+
* twice, and every read through the relation then returns the target twice. The
|
|
4680
|
+
* index is also what makes `set` a diff rather than a rewrite. `defineSchema` throws
|
|
4681
|
+
* on the same condition at load; this reports it at codegen time, before the app runs.
|
|
4682
|
+
*/
|
|
4683
|
+
declare const throughRelationMissingUniqueIndex: Lint;
|
|
4572
4684
|
/**
|
|
4573
4685
|
* Flags a topic with more than {@link MAX_SUBSCRIPTIONS} subscriptions.
|
|
4574
4686
|
*
|
|
@@ -5109,4 +5221,4 @@ interface RunAdvisorOptions {
|
|
|
5109
5221
|
* `static` lints at build time and defer `runtime` lints to a live shard.
|
|
5110
5222
|
*/
|
|
5111
5223
|
declare const runAdvisor: (context: LintContext, options?: RunAdvisorOptions) => Finding[];
|
|
5112
|
-
export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorCallEdge, type AdvisorCallSiteScope, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagRead, type AdvisorFlagSecurityDefault, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorModule, type AdvisorMutatorDeclaration, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorRowEffect, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableScan, type AdvisorTableWrite, type AdvisorUnboundedLoop, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type CallSiteMetadata, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, callSiteCallers, callSiteDescription, callSiteLabel, callSiteMetadata, circularFk, classifySensitivity, commitOrderedHardDelete, compareToBaseline, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, crossModuleTableWrite, dedupeCacheKeys, dispatchCycle, duplicateIndex, emptyIndex, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, fanOutBreadth, filterAfterIndexScan, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, flagReadInSubscription, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, globalTableNearColumnLimit, gradeFromScore, hardcodedSecret, helperRole, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, isReachableSite, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, mutatorWithoutOwnerScope, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, readCallSiteCallers, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, selfRescheduleWithoutProgress, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, topicTooManySubscriptions, ttlFieldNotTimestamp, unboundedCollectOnOpenRange, unboundedLoop, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
|
|
5224
|
+
export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorCallEdge, type AdvisorCallSiteScope, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorCountRead, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagRead, type AdvisorFlagSecurityDefault, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKeyedRelation, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorModule, type AdvisorMutatorDeclaration, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorRowEffect, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableScan, type AdvisorTableWrite, type AdvisorThroughRelation, type AdvisorUnboundedLoop, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type CallSiteMetadata, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, callSiteCallers, callSiteDescription, callSiteLabel, callSiteMetadata, circularFk, classifySensitivity, commitOrderedHardDelete, compareToBaseline, containerInstanceKeyFromUserInput, containerLongSleepAfter, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, countWithoutIndex, crossModuleTableWrite, dedupeCacheKeys, dispatchCycle, duplicateIndex, emptyIndex, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, fanOutBreadth, filterAfterIndexScan, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, flagReadInSubscription, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, globalTableNearColumnLimit, gradeFromScore, hardcodedSecret, helperRole, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, isReachableSite, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, mutatorWithoutOwnerScope, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueDispatchCycle, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, readCallSiteCallers, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, selfRescheduleWithoutProgress, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, throughRelationMissingUniqueIndex, topicTooManySubscriptions, ttlFieldNotTimestamp, unboundedCollectOnOpenRange, unboundedLoop, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
|
package/dist/index.d.ts
CHANGED
|
@@ -285,6 +285,11 @@ interface AdvisorCallEdge {
|
|
|
285
285
|
kind: "call" | "enqueue" | "invoke" | "publish" | "schedule";
|
|
286
286
|
/** 1-based line of the call, or `0` when unknown. */
|
|
287
287
|
line: number;
|
|
288
|
+
/**
|
|
289
|
+
* `true` alongside `conditional` when only a loop's body stands in front of the hop.
|
|
290
|
+
* A queue consumer's batch loop sets it: the hop runs once per message it receives.
|
|
291
|
+
*/
|
|
292
|
+
loopOnly?: true;
|
|
288
293
|
/** Why the target could not be read statically — set exactly when `target` is absent. */
|
|
289
294
|
reason?: string;
|
|
290
295
|
/**
|
|
@@ -391,6 +396,8 @@ interface AdvisorContainer {
|
|
|
391
396
|
enableInternet?: boolean;
|
|
392
397
|
/** The `lunora/containers.ts` export name, e.g. `transcoder`. */
|
|
393
398
|
exportName: string;
|
|
399
|
+
/** Declared `hardTimeout` value, when a static literal. Caps total lifetime, idle or not. */
|
|
400
|
+
hardTimeout?: number | string;
|
|
394
401
|
/** Declared `instanceType`: a named size, or a custom `{ vcpu, memoryMib, diskMb }`. */
|
|
395
402
|
instanceType?: string | {
|
|
396
403
|
diskMb?: number;
|
|
@@ -402,6 +409,27 @@ interface AdvisorContainer {
|
|
|
402
409
|
/** Declared `sleepAfter` value, when a static literal. */
|
|
403
410
|
sleepAfter?: number | string;
|
|
404
411
|
}
|
|
412
|
+
/**
|
|
413
|
+
* One `count()` call discovered in a function body — the input the `count_without_index`
|
|
414
|
+
* lint consumes. Produced by the codegen feeder (which reads `ctx.db.count("table", …)` and
|
|
415
|
+
* `ctx.db.<table>.count(…)` from the AST); runtime callers don't supply it, so the lint
|
|
416
|
+
* finds nothing there.
|
|
417
|
+
*/
|
|
418
|
+
interface AdvisorCountRead {
|
|
419
|
+
/** Source file the call appears in (relative to the lunora dir, no extension). */
|
|
420
|
+
file: string;
|
|
421
|
+
/**
|
|
422
|
+
* `false` only when the table is known and declares no aggregate index, so the count is a
|
|
423
|
+
* `COUNT(*)` scan. A counter-backed or unknown table reads `true`.
|
|
424
|
+
*/
|
|
425
|
+
indexed: boolean;
|
|
426
|
+
/** 1-based line of the `count(...)` call. */
|
|
427
|
+
line: number;
|
|
428
|
+
/** Who the call runs on behalf of. */
|
|
429
|
+
scope: AdvisorCallSiteScope;
|
|
430
|
+
/** Counted table name, or `""` when the table argument is not a string literal. */
|
|
431
|
+
table: string;
|
|
432
|
+
}
|
|
405
433
|
/**
|
|
406
434
|
* One CDC export-sink construction discovered in a function body — the input the
|
|
407
435
|
* `export_sink_misconfigured` lint consumes. Produced by the codegen feeder,
|
|
@@ -1303,6 +1331,19 @@ interface AdvisorQueueTuning {
|
|
|
1303
1331
|
interface AdvisorQueue {
|
|
1304
1332
|
/** The `lunora/queues.ts` export name, e.g. `notifications`. */
|
|
1305
1333
|
exportName: string;
|
|
1334
|
+
/**
|
|
1335
|
+
* The declaring file, lunora-relative without `.ts` (`queues`, or `billing/queues`).
|
|
1336
|
+
* Its handler's own call sites are attributed to `exportName` in this file.
|
|
1337
|
+
*/
|
|
1338
|
+
filePath?: string;
|
|
1339
|
+
/**
|
|
1340
|
+
* Where a `handler:` passed by reference is declared, when it is exported from a
|
|
1341
|
+
* lunora file. The handler's call sites then belong to this queue, under that export.
|
|
1342
|
+
*/
|
|
1343
|
+
handlerSite?: {
|
|
1344
|
+
exportName: string;
|
|
1345
|
+
file: string;
|
|
1346
|
+
};
|
|
1306
1347
|
/** How the queue is consumed: `"push"` (a worker `queue()` handler) or `"pull"` (external HTTP). */
|
|
1307
1348
|
mode: "pull" | "push";
|
|
1308
1349
|
/**
|
|
@@ -1605,14 +1646,30 @@ interface AdvisorExternalSource {
|
|
|
1605
1646
|
* the holding table; for `many` it lives on the target. `name` is the accessor
|
|
1606
1647
|
* the relation is loaded under.
|
|
1607
1648
|
*/
|
|
1608
|
-
interface
|
|
1649
|
+
interface AdvisorKeyedRelation {
|
|
1609
1650
|
field: string;
|
|
1610
1651
|
kind: "many" | "one";
|
|
1611
1652
|
name: string;
|
|
1612
1653
|
onDelete?: "cascade" | "restrict" | "set null";
|
|
1613
1654
|
references: string;
|
|
1614
1655
|
table: string;
|
|
1656
|
+
through?: never;
|
|
1615
1657
|
}
|
|
1658
|
+
/**
|
|
1659
|
+
* A many-to-many relation (`r.many(target, { through })`): the link lives in the `through.table` join table,
|
|
1660
|
+
* whose `from` column points at the declaring table and `to` at `table`. It has no `field` of its own.
|
|
1661
|
+
*/
|
|
1662
|
+
interface AdvisorThroughRelation {
|
|
1663
|
+
kind: "many";
|
|
1664
|
+
name: string;
|
|
1665
|
+
table: string;
|
|
1666
|
+
through: {
|
|
1667
|
+
from: string;
|
|
1668
|
+
table: string;
|
|
1669
|
+
to: string;
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
type AdvisorRelation = AdvisorKeyedRelation | AdvisorThroughRelation;
|
|
1616
1673
|
/**
|
|
1617
1674
|
* Adapt the runtime `@lunora/server` {@link Schema} into an {@link AdvisorSchema}.
|
|
1618
1675
|
* Runtime callers (the studio backend, a live shard) hold the real schema
|
|
@@ -2135,6 +2192,8 @@ interface LintContext {
|
|
|
2135
2192
|
* the container lints find nothing.
|
|
2136
2193
|
*/
|
|
2137
2194
|
containers?: ReadonlyArray<AdvisorContainer>;
|
|
2195
|
+
/** Every `count()` call found in function bodies; the `count_without_index` lint reads it. Absent for a runtime caller. */
|
|
2196
|
+
countReads?: ReadonlyArray<AdvisorCountRead>;
|
|
2138
2197
|
/**
|
|
2139
2198
|
* The `namespace:export` keys of the function-targeted cron jobs. A cron fires
|
|
2140
2199
|
* its target without a request, so the target is server-driven even when it is
|
|
@@ -3116,6 +3175,18 @@ declare const commitOrderedHardDelete: Lint;
|
|
|
3116
3175
|
* finding per arg-derived, unscoped `ctx.containers.*.get` call.
|
|
3117
3176
|
*/
|
|
3118
3177
|
declare const containerInstanceKeyFromUserInput: Lint;
|
|
3178
|
+
/**
|
|
3179
|
+
* Flags a container whose idle window is longer than an hour. An instance that stops
|
|
3180
|
+
* receiving work keeps running, and billed, until `sleepAfter` elapses, so a long
|
|
3181
|
+
* window is a standing cost for every idle instance. The window an instance can really
|
|
3182
|
+
* idle for is the shorter of `sleepAfter` and `hardTimeout`, since `hardTimeout` stops
|
|
3183
|
+
* the instance however active it is. The check uses that effective window.
|
|
3184
|
+
*
|
|
3185
|
+
* A `sleepAfter` that was not a static literal, or that was omitted (the platform's
|
|
3186
|
+
* 10 minute default), is not flagged. Runs only when the codegen feeder supplied the
|
|
3187
|
+
* container declarations, so a runtime caller flags nothing.
|
|
3188
|
+
*/
|
|
3189
|
+
declare const containerLongSleepAfter: Lint;
|
|
3119
3190
|
/**
|
|
3120
3191
|
* Flags a container declared on a large instance type. The big `standard-3` /
|
|
3121
3192
|
* `standard-4` sizes (and large custom shapes) are billed on their provisioned
|
|
@@ -3139,6 +3210,18 @@ declare const containerOversizedInstance: Lint;
|
|
|
3139
3210
|
declare const containerPublicInternet: Lint;
|
|
3140
3211
|
declare const containerRuntimeEgressRelaxation: Lint;
|
|
3141
3212
|
declare const containerStartEnableInternetOverride: Lint;
|
|
3213
|
+
/**
|
|
3214
|
+
* Flags a `count()` on a table that declares no aggregate index. `count()` reads a counter
|
|
3215
|
+
* only when an aggregate index covers it; otherwise it runs `SELECT COUNT(*)` over the
|
|
3216
|
+
* table, reading every row on each call. A count that runs per request or per poll repeats
|
|
3217
|
+
* that scan every time, and its cost grows with the table.
|
|
3218
|
+
*
|
|
3219
|
+
* The verdict is per table. A table that declares an aggregate index is not flagged, even
|
|
3220
|
+
* when a particular `where` falls outside that index, because the feeder cannot judge the
|
|
3221
|
+
* index's coverage. That is a known false negative. The count reads come from the codegen
|
|
3222
|
+
* feeder; runtime callers supply none, so this lint is a no-op there.
|
|
3223
|
+
*/
|
|
3224
|
+
declare const countWithoutIndex: Lint;
|
|
3142
3225
|
/**
|
|
3143
3226
|
* Flags a function that writes to a table another module declares it owns
|
|
3144
3227
|
* (`defineModule({ tables })`). Ownership is a boundary the app opted into: a
|
|
@@ -3176,7 +3259,7 @@ declare const crossModuleTableWrite: Lint;
|
|
|
3176
3259
|
* cancelled or rolled back, drops out. Only hops in an export's own body count
|
|
3177
3260
|
* — a helper's call may be guarded where the helper is called — and only
|
|
3178
3261
|
* static targets. Re-arming outside `ctx.scheduler` (a raw Durable Object
|
|
3179
|
-
* `setAlarm
|
|
3262
|
+
* `setAlarm`) is not modelled; a queue re-enqueue is `queue_dispatch_cycle`'s.
|
|
3180
3263
|
*
|
|
3181
3264
|
* Runs only when the codegen feeder supplied call edges (`context.callEdges`
|
|
3182
3265
|
* present); a runtime caller flags nothing.
|
|
@@ -4148,6 +4231,21 @@ declare const publicMutationWithoutRatelimit: Lint;
|
|
|
4148
4231
|
* from codegen, with no feeder required.
|
|
4149
4232
|
*/
|
|
4150
4233
|
declare const publicTableRlsOptoutConfusion: Lint;
|
|
4234
|
+
/**
|
|
4235
|
+
* Flags a cycle through a queue or a topic in which every hop runs unconditionally: a
|
|
4236
|
+
* queue consumer that (directly, or through a function it runs) sends back to its own
|
|
4237
|
+
* queue, or a topic whose subscriber leads back to a publish. Each message handled then
|
|
4238
|
+
* produces another, so the queue never drains, and every delivery is billed.
|
|
4239
|
+
*
|
|
4240
|
+
* The cycle is built from the same call-site edges `dispatch_cycle` reads, plus the
|
|
4241
|
+
* enqueue, publish and subscription edges it ignores. A queue consumer is a node: its
|
|
4242
|
+
* call sites start from the queue, and its handler's batch loop counts as a loop, not a
|
|
4243
|
+
* guard. A cycle with no queue or topic node is `dispatch_cycle`'s, so this lint reports
|
|
4244
|
+
* only the components that include one.
|
|
4245
|
+
*
|
|
4246
|
+
* Runs only when the codegen feeder supplied call edges and queues; a runtime caller flags nothing.
|
|
4247
|
+
*/
|
|
4248
|
+
declare const queueDispatchCycle: Lint;
|
|
4151
4249
|
/**
|
|
4152
4250
|
* Flags a declared queue — push or pull — that has no dead-letter queue.
|
|
4153
4251
|
*
|
|
@@ -4317,7 +4415,10 @@ declare const rlsUncoveredTable: Lint;
|
|
|
4317
4415
|
*
|
|
4318
4416
|
* An unconditional self-dispatch is `dispatch_cycle`'s, which already reports it as
|
|
4319
4417
|
* an error. This rule takes only the conditional ones, so each site gets one finding.
|
|
4320
|
-
*
|
|
4418
|
+
*
|
|
4419
|
+
* Two bands. Under {@link SHORT_DELAY_MS} is a spin (WARN): any row change counts as
|
|
4420
|
+
* progress. From there up to {@link POLL_DELAY_MS} is a poll (INFO): a rewrite of a row
|
|
4421
|
+
* on every tick is not progress, so only a removal clears it. Longer is left alone.
|
|
4321
4422
|
*
|
|
4322
4423
|
* Runs only when the codegen feeder supplied call edges (`context.callEdges` present).
|
|
4323
4424
|
*/
|
|
@@ -4569,6 +4670,17 @@ declare const storageUploadWithoutMaxSize: Lint;
|
|
|
4569
4670
|
* a runtime caller with no insert signal flags nothing rather than every table.
|
|
4570
4671
|
*/
|
|
4571
4672
|
declare const tableWithoutInsert: Lint;
|
|
4673
|
+
/**
|
|
4674
|
+
* A correctness lint for many-to-many relations: the join table behind
|
|
4675
|
+
* `r.many(target, { through: { table, from, to } })` must carry a unique index over
|
|
4676
|
+
* exactly `(from, to)`.
|
|
4677
|
+
*
|
|
4678
|
+
* Without it a link is not a single row: a repeated `connect` stores the same pair
|
|
4679
|
+
* twice, and every read through the relation then returns the target twice. The
|
|
4680
|
+
* index is also what makes `set` a diff rather than a rewrite. `defineSchema` throws
|
|
4681
|
+
* on the same condition at load; this reports it at codegen time, before the app runs.
|
|
4682
|
+
*/
|
|
4683
|
+
declare const throughRelationMissingUniqueIndex: Lint;
|
|
4572
4684
|
/**
|
|
4573
4685
|
* Flags a topic with more than {@link MAX_SUBSCRIPTIONS} subscriptions.
|
|
4574
4686
|
*
|
|
@@ -5109,4 +5221,4 @@ interface RunAdvisorOptions {
|
|
|
5109
5221
|
* `static` lints at build time and defer `runtime` lints to a live shard.
|
|
5110
5222
|
*/
|
|
5111
5223
|
declare const runAdvisor: (context: LintContext, options?: RunAdvisorOptions) => Finding[];
|
|
5112
|
-
export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorCallEdge, type AdvisorCallSiteScope, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagRead, type AdvisorFlagSecurityDefault, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorModule, type AdvisorMutatorDeclaration, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorRowEffect, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableScan, type AdvisorTableWrite, type AdvisorUnboundedLoop, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type CallSiteMetadata, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, callSiteCallers, callSiteDescription, callSiteLabel, callSiteMetadata, circularFk, classifySensitivity, commitOrderedHardDelete, compareToBaseline, containerInstanceKeyFromUserInput, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, crossModuleTableWrite, dedupeCacheKeys, dispatchCycle, duplicateIndex, emptyIndex, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, fanOutBreadth, filterAfterIndexScan, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, flagReadInSubscription, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, globalTableNearColumnLimit, gradeFromScore, hardcodedSecret, helperRole, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, isReachableSite, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, mutatorWithoutOwnerScope, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, readCallSiteCallers, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, selfRescheduleWithoutProgress, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, topicTooManySubscriptions, ttlFieldNotTimestamp, unboundedCollectOnOpenRange, unboundedLoop, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
|
|
5224
|
+
export { ALL_LINTS, type AdvisorAdminRoute, type AdvisorAiRawRun, type AdvisorAiToolSideEffect, type AdvisorArgumentDerivedFetch, type AdvisorArgumentValidator, type AdvisorAuthApiCall, type AdvisorAuthConfig, type AdvisorBrowserUrlAccess, type AdvisorCallEdge, type AdvisorCallSiteScope, type AdvisorConfigCall, type AdvisorContainer, type AdvisorContainerKeyAccess, type AdvisorContainerOverride, type AdvisorCountRead, type AdvisorExportSink, type AdvisorFailOpenGuard, type AdvisorFlagRead, type AdvisorFlagSecurityDefault, type AdvisorGeoIndexUsage, type AdvisorHttpActionGuard, type AdvisorHttpHeaderWrite, type AdvisorHyperdriveCall, type AdvisorIdentityClaimRead, type AdvisorImageDeliveryUrlAccess, type AdvisorIndex, type AdvisorIndexHit, type AdvisorInsertWrite, type AdvisorKeyedRelation, type AdvisorKvKeyAccess, type AdvisorMailRecipientAccess, type AdvisorMap, type AdvisorMaskProcedure, type AdvisorMaskStrategy, type AdvisorModule, type AdvisorMutatorDeclaration, type AdvisorMutatorWrite, type AdvisorNondeterministicCall, type AdvisorNormalizeIdAuthorization, type AdvisorNotifyCall, type AdvisorNotifyConfig, type AdvisorOwnerFieldWrite, type AdvisorPaymentWebhook, type AdvisorPrivilegedDispatch, type AdvisorProcedureProtection, type AdvisorQueryRead, type AdvisorQueue, type AdvisorQueueTuning, type AdvisorR2sqlCall, type AdvisorRatelimitKeySelector, type AdvisorRawRowReturn, type AdvisorRelation, type AdvisorRelationLoad, type AdvisorRlsProcedure, type AdvisorRowEffect, type AdvisorSchema, type AdvisorSecretLiteral, type AdvisorShape, type AdvisorShardTraffic, type AdvisorSoftDeleteRead, type AdvisorSqlInterpolation, type AdvisorStaleMigrationImport, type AdvisorStorageKeyAccess, type AdvisorStorageUpload, type AdvisorTable, type AdvisorTableScan, type AdvisorTableWrite, type AdvisorThroughRelation, type AdvisorUnboundedLoop, type AdvisorVectorNamespaceAccess, type AdvisorWorkflow, type AdvisorWorkflowCall, type AdvisorWranglerVariable, type AnalyticsMetricsOptions, type AnalyticsMetricsSource, type AnalyticsRuntimeMetrics, type BaselineComparison, type CallSiteMetadata, type Category, type CheckResult, type Coverage, type Facing, type Finding, type Grade, type Level, type Lint, type LintContext, type LintSource, MAP_VERSION, type MapSummary, type ProcedureDelta, type ProcedureScore, type ProjectScore, RUNTIME_LINTS, RunAdvisorOptions, STATIC_LINTS, type ScoreAdvisorOptions, type Sensitivity, type SensitivityLevel, actionFetchSsrf, actionWithoutErrorHandling, adminRouteWithoutGuard, aiRawRunEscapeHatch, aiRunWithoutLogging, aiToolSideEffectPromptInjection, aiUnboundedGenerationPublic, allowUnauthenticatedShardAccessEnabled, authApiCallWithoutHeaders, authCsrfCheckDisabled, authEmailVerificationDisabled, authScimWithoutTransactions, authSecureCookiesDisabled, authSessionFreshageZero, authTrustedOriginsWildcard, browserAllowPrivateTargets, browserUserUrlWithoutAllowlist, byCodepoint, callSiteCallers, callSiteDescription, callSiteLabel, callSiteMetadata, circularFk, classifySensitivity, commitOrderedHardDelete, compareToBaseline, containerInstanceKeyFromUserInput, containerLongSleepAfter, containerOversizedInstance, containerPublicInternet, containerRuntimeEgressRelaxation, containerStartEnableInternetOverride, countWithoutIndex, crossModuleTableWrite, dedupeCacheKeys, dispatchCycle, duplicateIndex, emptyIndex, errorWithoutCatalog, exportSinkMisconfigured, externalSourceIncrementalNoDeletePath, externalSourceOnGlobal, externalSourceUnscoped, fanOutBreadth, filterAfterIndexScan, filterOnPrimaryKey, filterWithoutIndex, flagGatesSecurityWithUnsafeDefault, flagReadInSubscription, fromServerSchema, geoIndexFieldNotGeopoint, geoIndexUnused, globalTableNearColumnLimit, gradeFromScore, hardcodedSecret, helperRole, hotShard, httpActionMissingAuthGuard, httpActionResponseHeaderInjection, hyperdriveOutsideAction, identityUndeclaredClaimTrusted, imagesUrlSourceFromUserInput, indexReferencesUnknownField, indexUtilization, insertManyUnsafeUserData, isReachableSite, kvUnscopedUserKeyIdor, mailInboundDispatchWithoutVerify, mailRecipientFromRequestInput, maskUncoveredPiiColumn, maskWeakHashStrategyOnPii, maskedRelationLeakViaWith, mutatorFullRowReplace, mutatorWithoutOwnerScope, nondeterministicQueryMutation, normalizeIdUsedAsAuthorization, notifyMissingPushConfig, notifySendOutsideAction, outputProjectionMissingOnPublicRead, ownerFieldFromArgsNotAuth, parseAdvisorMap, paymentCreateWithoutAuthorize, paymentWebhookWideTolerance, plaintextSecretInWranglerVariables, policyReferencesUnknownTable, privilegedDispatchUnvalidatedPayload, privilegedFanoutFromPublicProcedure, procedureWithoutStructuredEvent, publicArgumentUsesAny, publicMutationWithoutRatelimit, publicTableRlsOptoutConfusion, queueDispatchCycle, queueWithoutDlq, r2sqlOutsideAction, ratelimitDefaultMemoryStore, ratelimitKeySpoofableOrGlobal, ratelimitMiddlewareFailOpen, readCallSiteCallers, relationReferencesUnknownField, relationReferencesUnknownTable, rlsUncoveredTable, runAdvisor, scoreAdvisor, selfRescheduleWithoutProgress, shapeTargetsGlobalTable, shapeUnknownTable, signupMutationWithoutDisposableGating, softDeleteIncludeDeletedFromArgs, sqlInjectionRisk, storageGenerateUploadUrlNoContentTypePin, storageKeyFromUserArgs, storagePresignedUrlForPrivateContent, storageUploadWithoutContentTypeAllowlist, storageUploadWithoutMaxSize, tableWithoutInsert, throughRelationMissingUniqueIndex, topicTooManySubscriptions, ttlFieldNotTimestamp, unboundedCollectOnOpenRange, unboundedLoop, unboundedStringArgument, unindexedForeignKey, unindexedRelationTarget, unrestrictedWhereBranch, userCreatingMutationWithoutCaptcha, vectorsNamespaceFromUserInput, workflowDuplicateStepName, workflowUnknownTarget, workflowUnused };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{dedupeCacheKeys as c}from"./packem_shared/dedupeCacheKeys-DtBOHffV.mjs";import d from"./packem_shared/fanOutBreadth-CBtmZnoh.mjs";import u from"./packem_shared/hotShard-BwGYZ3Tq.mjs";import h from"./packem_shared/indexUtilization-CkVPZcVe.mjs";import g from"./packem_shared/actionFetchSsrf-BK-kvOPO.mjs";import b from"./packem_shared/actionWithoutErrorHandling-4GCBT0_z.mjs";import y from"./packem_shared/adminRouteWithoutGuard-DPE7LuNh.mjs";import S from"./packem_shared/aiRawRunEscapeHatch-BW7a8kZC.mjs";import w from"./packem_shared/aiRunWithoutLogging-CvHHtEN9.mjs";import v from"./packem_shared/aiToolSideEffectPromptInjection-9eJXRlKt.mjs";import A from"./packem_shared/aiUnboundedGenerationPublic-C17h6sMV.mjs";import R from"./packem_shared/allowUnauthenticatedShardAccessEnabled-BQufF5Kb.mjs";import U from"./packem_shared/authApiCallWithoutHeaders-CReJRsGW.mjs";import W from"./packem_shared/authCsrfCheckDisabled-CRIVsvb9.mjs";import T from"./packem_shared/authEmailVerificationDisabled-BjFlnHr2.mjs";import I from"./packem_shared/authScimWithoutTransactions-B5ATiz5A.mjs";import C from"./packem_shared/authSecureCookiesDisabled-By8PVum3.mjs";import k from"./packem_shared/authSessionFreshageZero-DuxX3Ln9.mjs";import x from"./packem_shared/authTrustedOriginsWildcard-BKIh6788.mjs";import N from"./packem_shared/browserAllowPrivateTargets-Cj5sizhv.mjs";import F from"./packem_shared/browserUserUrlWithoutAllowlist-CzDEraJB.mjs";import O from"./packem_shared/circularFk-DtcWFJxK.mjs";import M from"./packem_shared/commitOrderedHardDelete-BPdwOKA7.mjs";import D from"./packem_shared/containerInstanceKeyFromUserInput-Bl-tAzKV.mjs";import P from"./packem_shared/containerOversizedInstance-Bx89uR7E.mjs";import $ from"./packem_shared/containerPublicInternet-BFfZf_P4.mjs";import E from"./packem_shared/containerRuntimeEgressRelaxation-4tMrhq1k.mjs";import K from"./packem_shared/containerStartEnableInternetOverride-mN_kuRtk.mjs";import L from"./packem_shared/crossModuleTableWrite-DjiVTAwK.mjs";import q from"./packem_shared/dispatchCycle-CWc5Q0cd.mjs";import G from"./packem_shared/duplicateIndex-Cip6-Rpu.mjs";import _ from"./packem_shared/emptyIndex-BnHDcXza.mjs";import z from"./packem_shared/errorWithoutCatalog-BTfvaXHR.mjs";import H from"./packem_shared/exportSinkMisconfigured-JfbAx9AI.mjs";import B from"./packem_shared/externalSourceIncrementalNoDeletePath-BCzm3HzF.mjs";import j from"./packem_shared/externalSourceOnGlobal-CH7xbJ49.mjs";import V from"./packem_shared/externalSourceUnscoped-BxU2uSXk.mjs";import Q from"./packem_shared/filterAfterIndexScan-CV8aPq7v.mjs";import X from"./packem_shared/filterOnPrimaryKey-BeHVg4w3.mjs";import Z from"./packem_shared/filterWithoutIndex-BGv3jbgg.mjs";import J from"./packem_shared/flagGatesSecurityWithUnsafeDefault-B5FBDF8c.mjs";import Y from"./packem_shared/flagReadInSubscription-DabIhYGD.mjs";import oo from"./packem_shared/geoIndexFieldNotGeopoint-BMQuok2-.mjs";import eo from"./packem_shared/geoIndexUnused-D7C9Qr4U.mjs";import ro from"./packem_shared/globalTableNearColumnLimit-BFbBBd6A.mjs";import to from"./packem_shared/hardcodedSecret-Bw_4FYrs.mjs";import io from"./packem_shared/httpActionMissingAuthGuard-DhMIs1EI.mjs";import no from"./packem_shared/httpActionResponseHeaderInjection-mcRYd3Zw.mjs";import ao from"./packem_shared/hyperdriveOutsideAction-CPDdGP2g.mjs";import mo from"./packem_shared/identityUndeclaredClaimTrusted-D1sEd5lr.mjs";import lo from"./packem_shared/imagesUrlSourceFromUserInput-BJXotuyO.mjs";import so from"./packem_shared/indexReferencesUnknownField-DKz4-bla.mjs";import po from"./packem_shared/insertManyUnsafeUserData-DWE8d_DF.mjs";import fo from"./packem_shared/kvUnscopedUserKeyIdor-DhymglIq.mjs";import co from"./packem_shared/mailInboundDispatchWithoutVerify-CWwqXyPX.mjs";import uo from"./packem_shared/mailRecipientFromRequestInput-BVXsZ4Xf.mjs";import ho from"./packem_shared/maskUncoveredPiiColumn-axEka4nd.mjs";import go from"./packem_shared/maskWeakHashStrategyOnPii-DJasagzX.mjs";import bo from"./packem_shared/maskedRelationLeakViaWith-CIFij6vE.mjs";import{e as a}from"./packem_shared/finding-NrKO8idM.mjs";import yo from"./packem_shared/mutatorFullRowReplace-BzRpZ47r.mjs";import So from"./packem_shared/mutatorWithoutOwnerScope-BoS81ns0.mjs";import wo from"./packem_shared/nondeterministicQueryMutation-Bbb-AGOS.mjs";import vo from"./packem_shared/normalizeIdUsedAsAuthorization-BXN-Can6.mjs";import Ao from"./packem_shared/notifyMissingPushConfig-DeQMoNwm.mjs";import Ro from"./packem_shared/notifySendOutsideAction-BJrDkr4h.mjs";import Uo from"./packem_shared/outputProjectionMissingOnPublicRead-DWSeo7oC.mjs";import Wo from"./packem_shared/ownerFieldFromArgsNotAuth-J8Xbz5uI.mjs";import To from"./packem_shared/paymentCreateWithoutAuthorize-CdenWpXD.mjs";import Io from"./packem_shared/paymentWebhookWideTolerance-DVhJB0Ip.mjs";import Co from"./packem_shared/plaintextSecretInWranglerVariables-bNkqmNqV.mjs";import ko from"./packem_shared/policyReferencesUnknownTable-CIKuRZ5Y.mjs";import xo from"./packem_shared/privilegedDispatchUnvalidatedPayload-BM3NtT57.mjs";import No from"./packem_shared/privilegedFanoutFromPublicProcedure-DV188gHQ.mjs";import Fo from"./packem_shared/procedureWithoutStructuredEvent-CZ_B23nM.mjs";import Oo from"./packem_shared/publicArgumentUsesAny-BDEvMA8Q.mjs";import Mo from"./packem_shared/publicMutationWithoutRatelimit-Bd-rRxnz.mjs";import Do from"./packem_shared/publicTableRlsOptoutConfusion-CbNk75Xo.mjs";import Po from"./packem_shared/queueWithoutDlq-BvHz3Opg.mjs";import $o from"./packem_shared/r2sqlOutsideAction-BdLiLOAX.mjs";import Eo from"./packem_shared/ratelimitDefaultMemoryStore-Dj0Kk7t9.mjs";import Ko from"./packem_shared/ratelimitKeySpoofableOrGlobal-DHMl74hR.mjs";import Lo from"./packem_shared/ratelimitMiddlewareFailOpen-Di2t8pDc.mjs";import qo from"./packem_shared/relationReferencesUnknownField-CJHOCSzs.mjs";import Go from"./packem_shared/relationReferencesUnknownTable-CP4aWtAJ.mjs";import _o from"./packem_shared/rlsUncoveredTable-CZ4ie4gX.mjs";import zo from"./packem_shared/selfRescheduleWithoutProgress-Be9d1bQ0.mjs";import Ho from"./packem_shared/shapeTargetsGlobalTable-Bu3eEDic.mjs";import Bo from"./packem_shared/shapeUnknownTable-CREfNnWi.mjs";import jo from"./packem_shared/signupMutationWithoutDisposableGating-BdKwtxDP.mjs";import Vo from"./packem_shared/softDeleteIncludeDeletedFromArgs-C7Ugg4zb.mjs";import Qo from"./packem_shared/sqlInjectionRisk-BYRllgPV.mjs";import Xo from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-CrwFuo83.mjs";import Zo from"./packem_shared/storageKeyFromUserArgs-3vkxmI3v.mjs";import Jo from"./packem_shared/storagePresignedUrlForPrivateContent-C3KeV5tV.mjs";import Yo from"./packem_shared/storageUploadWithoutContentTypeAllowlist-BXoK_QWE.mjs";import oe from"./packem_shared/storageUploadWithoutMaxSize-CW7uKvSi.mjs";import ee from"./packem_shared/tableWithoutInsert-ClJp_jDr.mjs";import re from"./packem_shared/topicTooManySubscriptions-CiZhFm9r.mjs";import te from"./packem_shared/ttlFieldNotTimestamp-lnb5nmM6.mjs";import{s as ie,q as ne,c as ae}from"./packem_shared/helpers-DdZ_pyR3.mjs";import me from"./packem_shared/unboundedCollectOnOpenRange-DHy2RUL7.mjs";import le from"./packem_shared/unboundedLoop-DxN3_apu.mjs";import se from"./packem_shared/unboundedStringArgument-jZnCbwB0.mjs";import pe from"./packem_shared/unindexedForeignKey-Dypgn8uH.mjs";import fe from"./packem_shared/unindexedRelationTarget-CSqRJWYZ.mjs";import ce from"./packem_shared/unrestrictedWhereBranch-CcIBmHik.mjs";import de from"./packem_shared/userCreatingMutationWithoutCaptcha-D4TZEpgz.mjs";import ue from"./packem_shared/vectorsNamespaceFromUserInput-BWvGjJzp.mjs";import he from"./packem_shared/workflowDuplicateStepName-B6eAJzwA.mjs";import ge from"./packem_shared/workflowUnknownTarget-DEKB5H0g.mjs";import be from"./packem_shared/workflowUnused-DHwwE-1P.mjs";import{callSiteCallers as Nt,callSiteDescription as Ft,callSiteLabel as Ot,callSiteMetadata as Mt,helperRole as Dt,isReachableSite as Pt,readCallSiteCallers as $t}from"./packem_shared/callSiteCallers-DArHfQSU.mjs";import{compareToBaseline as Kt,parseAdvisorMap as Lt}from"./packem_shared/compareToBaseline-CUG59IOy.mjs";import{gradeFromScore as Gt}from"./packem_shared/gradeFromScore-KSt58rj1.mjs";import{MAP_VERSION as zt,byCodepoint as Ht,scoreAdvisor as Bt}from"./packem_shared/MAP_VERSION-MQ5W2B6n.mjs";import{default as Vt}from"./packem_shared/classifySensitivity-JnjaTGYi.mjs";import{fromServerSchema as Xt}from"./packem_shared/fromServerSchema-fAymN8o2.mjs";const ye={convex:"migrating/from-convex",firebase:"migrating/from-firebase",supabase:"migrating/from-supabase"},m={categories:["SCHEMA"],description:"A migrated-away platform's SDK is still imported from `lunora/` source. The code compiles and typechecks, so a half-finished port keeps reading from the old platform at runtime and the two stores silently drift apart.",facing:"INTERNAL",level:"WARN",name:"migration_stale_import",remediation:"Replace the call with its Lunora equivalent and drop the dependency. If the import is deliberate — a second data source you still read from — move it out of `lunora/` so it is not mistaken for an unfinished migration.",run:r=>r.staleMigrationImports===void 0?[]:r.staleMigrationImports.map(o=>a(m,{cacheKey:`migration_stale_import:${o.file}:${o.line.toString()}:${o.moduleSpecifier}`,detail:`\`${o.file}\` (line ${o.line.toString()}) still imports \`${o.moduleSpecifier}\`. Finish the port — see \`${ye[o.platform]}\` — or move the import out of \`lunora/\` if you genuinely still read from ${o.platform}.`,metadata:{file:o.file,line:o.line,moduleSpecifier:o.moduleSpecifier,platform:o.platform}})),source:"static",title:"Stale migration import"},Se=new Map([["global",{level:"WARN",scope:r=>`it reads the whole D1 table "${r}" over a cross-region round trip`}],["root",{level:"WARN",scope:r=>`it loads every row of "${r}" from the root Durable Object's SQLite into memory`}],["shardBy",{level:"INFO",scope:r=>`"${r}" is \`.shardBy()\`, so this collects one shard's rows rather than the whole table — bounded by a single tenant's row count`}]]),we={level:"WARN",scope:r=>`it loads every row of "${r}"`},l={categories:["PERFORMANCE"],description:"A query calls `.collect()` with no `.withIndex()` and no `.filter()`, so it materializes every row of the table. Any live subscription over it also re-sends that whole result to every subscribed client on every write to the table.",facing:"EXTERNAL",level:"WARN",name:"unbounded_collect",remediation:'Narrow the read with `.withIndex("name", (q) => q.eq(...))`, cap it with `.take(n)`, or page it with `.paginate(args.paginationOpts)` so neither the scan nor the subscription payload grows with the table.',run:r=>{const o=[],i=ie(r.schema);for(const e of r.queries??[]){if(e.terminal!=="collect"||e.hasIndex||e.table===""||e.hasFilter)continue;const t=i.get(e.table),{level:n,scope:s}=Se.get(t??"")??we,p=ne(e),f=n==="WARN"?` A live subscription over this query records a whole-table dependency, so every write to "${e.table}" re-runs it and re-sends the full result to each subscribed socket.`:" Cap it with `.take(n)` if that count can grow.";o.push(a(l,{cacheKey:`unbounded_collect:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${p} calls .collect() with no index and no filter — ${s(e.table)}.${f}`,level:n,metadata:{...ae(e),shardKind:t??"unknown",table:e.table}}))}return o},source:"static",title:"Unbounded collect"},ve=[so,Go,qo,ge,he,Bo,B,j,V,_,ro,z,oo,eo,H,te,M,O,L,pe,fe,G,ee,be,Po,re,X,Z,Q,l,me,le,q,zo,Ho,yo,So,wo,ao,$o,U,ko,_o,ho,go,P,$,Mo,ce,de,jo,Oo,se,to,Qo,y,To,co,Eo,N,No,Fo,po,A,g,b,Wo,Zo,fo,D,S,w,ue,uo,F,xo,K,E,I,x,W,C,T,k,lo,Ko,Do,R,Yo,oe,Xo,Jo,io,no,Lo,J,Y,v,mo,Io,Vo,bo,Uo,vo,Ro,Ao,Co,m],Ae=[u,h,d],Re=[...ve,...Ae],Ct=(r,o={})=>{const i=o.lints??Re,e=[];for(const t of i)o.source!==void 0&&t.source!==o.source||e.push(...t.run(r));return c(e)};export{Re as ALL_LINTS,zt as MAP_VERSION,Ae as RUNTIME_LINTS,ve as STATIC_LINTS,g as actionFetchSsrf,b as actionWithoutErrorHandling,y as adminRouteWithoutGuard,S as aiRawRunEscapeHatch,w as aiRunWithoutLogging,v as aiToolSideEffectPromptInjection,A as aiUnboundedGenerationPublic,R as allowUnauthenticatedShardAccessEnabled,U as authApiCallWithoutHeaders,W as authCsrfCheckDisabled,T as authEmailVerificationDisabled,I as authScimWithoutTransactions,C as authSecureCookiesDisabled,k as authSessionFreshageZero,x as authTrustedOriginsWildcard,N as browserAllowPrivateTargets,F as browserUserUrlWithoutAllowlist,Ht as byCodepoint,Nt as callSiteCallers,Ft as callSiteDescription,Ot as callSiteLabel,Mt as callSiteMetadata,O as circularFk,Vt as classifySensitivity,M as commitOrderedHardDelete,Kt as compareToBaseline,D as containerInstanceKeyFromUserInput,P as containerOversizedInstance,$ as containerPublicInternet,E as containerRuntimeEgressRelaxation,K as containerStartEnableInternetOverride,L as crossModuleTableWrite,c as dedupeCacheKeys,q as dispatchCycle,G as duplicateIndex,_ as emptyIndex,z as errorWithoutCatalog,H as exportSinkMisconfigured,B as externalSourceIncrementalNoDeletePath,j as externalSourceOnGlobal,V as externalSourceUnscoped,d as fanOutBreadth,Q as filterAfterIndexScan,X as filterOnPrimaryKey,Z as filterWithoutIndex,J as flagGatesSecurityWithUnsafeDefault,Y as flagReadInSubscription,Xt as fromServerSchema,oo as geoIndexFieldNotGeopoint,eo as geoIndexUnused,ro as globalTableNearColumnLimit,Gt as gradeFromScore,to as hardcodedSecret,Dt as helperRole,u as hotShard,io as httpActionMissingAuthGuard,no as httpActionResponseHeaderInjection,ao as hyperdriveOutsideAction,mo as identityUndeclaredClaimTrusted,lo as imagesUrlSourceFromUserInput,so as indexReferencesUnknownField,h as indexUtilization,po as insertManyUnsafeUserData,Pt as isReachableSite,fo as kvUnscopedUserKeyIdor,co as mailInboundDispatchWithoutVerify,uo as mailRecipientFromRequestInput,ho as maskUncoveredPiiColumn,go as maskWeakHashStrategyOnPii,bo as maskedRelationLeakViaWith,yo as mutatorFullRowReplace,So as mutatorWithoutOwnerScope,wo as nondeterministicQueryMutation,vo as normalizeIdUsedAsAuthorization,Ao as notifyMissingPushConfig,Ro as notifySendOutsideAction,Uo as outputProjectionMissingOnPublicRead,Wo as ownerFieldFromArgsNotAuth,Lt as parseAdvisorMap,To as paymentCreateWithoutAuthorize,Io as paymentWebhookWideTolerance,Co as plaintextSecretInWranglerVariables,ko as policyReferencesUnknownTable,xo as privilegedDispatchUnvalidatedPayload,No as privilegedFanoutFromPublicProcedure,Fo as procedureWithoutStructuredEvent,Oo as publicArgumentUsesAny,Mo as publicMutationWithoutRatelimit,Do as publicTableRlsOptoutConfusion,Po as queueWithoutDlq,$o as r2sqlOutsideAction,Eo as ratelimitDefaultMemoryStore,Ko as ratelimitKeySpoofableOrGlobal,Lo as ratelimitMiddlewareFailOpen,$t as readCallSiteCallers,qo as relationReferencesUnknownField,Go as relationReferencesUnknownTable,_o as rlsUncoveredTable,Ct as runAdvisor,Bt as scoreAdvisor,zo as selfRescheduleWithoutProgress,Ho as shapeTargetsGlobalTable,Bo as shapeUnknownTable,jo as signupMutationWithoutDisposableGating,Vo as softDeleteIncludeDeletedFromArgs,Qo as sqlInjectionRisk,Xo as storageGenerateUploadUrlNoContentTypePin,Zo as storageKeyFromUserArgs,Jo as storagePresignedUrlForPrivateContent,Yo as storageUploadWithoutContentTypeAllowlist,oe as storageUploadWithoutMaxSize,ee as tableWithoutInsert,re as topicTooManySubscriptions,te as ttlFieldNotTimestamp,me as unboundedCollectOnOpenRange,le as unboundedLoop,se as unboundedStringArgument,pe as unindexedForeignKey,fe as unindexedRelationTarget,ce as unrestrictedWhereBranch,de as userCreatingMutationWithoutCaptcha,ue as vectorsNamespaceFromUserInput,he as workflowDuplicateStepName,ge as workflowUnknownTarget,be as workflowUnused};
|
|
1
|
+
import{dedupeCacheKeys as c}from"./packem_shared/dedupeCacheKeys-DtBOHffV.mjs";import d from"./packem_shared/fanOutBreadth-CBtmZnoh.mjs";import u from"./packem_shared/hotShard-BwGYZ3Tq.mjs";import h from"./packem_shared/indexUtilization-CkVPZcVe.mjs";import g from"./packem_shared/actionFetchSsrf-BK-kvOPO.mjs";import b from"./packem_shared/actionWithoutErrorHandling-4GCBT0_z.mjs";import y from"./packem_shared/adminRouteWithoutGuard-DPE7LuNh.mjs";import S from"./packem_shared/aiRawRunEscapeHatch-BW7a8kZC.mjs";import w from"./packem_shared/aiRunWithoutLogging-CvHHtEN9.mjs";import v from"./packem_shared/aiToolSideEffectPromptInjection-9eJXRlKt.mjs";import A from"./packem_shared/aiUnboundedGenerationPublic-C17h6sMV.mjs";import R from"./packem_shared/allowUnauthenticatedShardAccessEnabled-BQufF5Kb.mjs";import U from"./packem_shared/authApiCallWithoutHeaders-CReJRsGW.mjs";import W from"./packem_shared/authCsrfCheckDisabled-CRIVsvb9.mjs";import I from"./packem_shared/authEmailVerificationDisabled-BjFlnHr2.mjs";import T from"./packem_shared/authScimWithoutTransactions-B5ATiz5A.mjs";import x from"./packem_shared/authSecureCookiesDisabled-By8PVum3.mjs";import C from"./packem_shared/authSessionFreshageZero-DuxX3Ln9.mjs";import k from"./packem_shared/authTrustedOriginsWildcard-BKIh6788.mjs";import N from"./packem_shared/browserAllowPrivateTargets-Cj5sizhv.mjs";import F from"./packem_shared/browserUserUrlWithoutAllowlist-CzDEraJB.mjs";import M from"./packem_shared/circularFk-DtcWFJxK.mjs";import O from"./packem_shared/commitOrderedHardDelete-BPdwOKA7.mjs";import D from"./packem_shared/containerInstanceKeyFromUserInput-Bl-tAzKV.mjs";import P from"./packem_shared/containerLongSleepAfter-CVvHEGYC.mjs";import $ from"./packem_shared/containerOversizedInstance-Bx89uR7E.mjs";import E from"./packem_shared/containerPublicInternet-BFfZf_P4.mjs";import q from"./packem_shared/containerRuntimeEgressRelaxation-4tMrhq1k.mjs";import L from"./packem_shared/containerStartEnableInternetOverride-mN_kuRtk.mjs";import K from"./packem_shared/countWithoutIndex-Dn6rB2Mb.mjs";import G from"./packem_shared/crossModuleTableWrite-DjiVTAwK.mjs";import _ from"./packem_shared/dispatchCycle-BwuXRDiL.mjs";import z from"./packem_shared/duplicateIndex-Cip6-Rpu.mjs";import H from"./packem_shared/emptyIndex-BnHDcXza.mjs";import B from"./packem_shared/errorWithoutCatalog-BTfvaXHR.mjs";import j from"./packem_shared/exportSinkMisconfigured-JfbAx9AI.mjs";import V from"./packem_shared/externalSourceIncrementalNoDeletePath-BCzm3HzF.mjs";import Q from"./packem_shared/externalSourceOnGlobal-CH7xbJ49.mjs";import X from"./packem_shared/externalSourceUnscoped-BxU2uSXk.mjs";import Z from"./packem_shared/filterAfterIndexScan-CV8aPq7v.mjs";import J from"./packem_shared/filterOnPrimaryKey-BeHVg4w3.mjs";import Y from"./packem_shared/filterWithoutIndex-BGv3jbgg.mjs";import oo from"./packem_shared/flagGatesSecurityWithUnsafeDefault-B5FBDF8c.mjs";import eo from"./packem_shared/flagReadInSubscription-DabIhYGD.mjs";import ro from"./packem_shared/geoIndexFieldNotGeopoint-BMQuok2-.mjs";import to from"./packem_shared/geoIndexUnused-D7C9Qr4U.mjs";import io from"./packem_shared/globalTableNearColumnLimit-BFbBBd6A.mjs";import no from"./packem_shared/hardcodedSecret-Bw_4FYrs.mjs";import ao from"./packem_shared/httpActionMissingAuthGuard-DhMIs1EI.mjs";import mo from"./packem_shared/httpActionResponseHeaderInjection-mcRYd3Zw.mjs";import lo from"./packem_shared/hyperdriveOutsideAction-CPDdGP2g.mjs";import so from"./packem_shared/identityUndeclaredClaimTrusted-D1sEd5lr.mjs";import po from"./packem_shared/imagesUrlSourceFromUserInput-BJXotuyO.mjs";import fo from"./packem_shared/indexReferencesUnknownField-DKz4-bla.mjs";import co from"./packem_shared/insertManyUnsafeUserData-DWE8d_DF.mjs";import uo from"./packem_shared/kvUnscopedUserKeyIdor-DhymglIq.mjs";import ho from"./packem_shared/mailInboundDispatchWithoutVerify-CWwqXyPX.mjs";import go from"./packem_shared/mailRecipientFromRequestInput-BVXsZ4Xf.mjs";import bo from"./packem_shared/maskUncoveredPiiColumn-axEka4nd.mjs";import yo from"./packem_shared/maskWeakHashStrategyOnPii-DJasagzX.mjs";import So from"./packem_shared/maskedRelationLeakViaWith-CIFij6vE.mjs";import{e as a}from"./packem_shared/finding-NrKO8idM.mjs";import wo from"./packem_shared/mutatorFullRowReplace-BzRpZ47r.mjs";import vo from"./packem_shared/mutatorWithoutOwnerScope-BoS81ns0.mjs";import Ao from"./packem_shared/nondeterministicQueryMutation-Bbb-AGOS.mjs";import Ro from"./packem_shared/normalizeIdUsedAsAuthorization-BXN-Can6.mjs";import Uo from"./packem_shared/notifyMissingPushConfig-DeQMoNwm.mjs";import Wo from"./packem_shared/notifySendOutsideAction-BJrDkr4h.mjs";import Io from"./packem_shared/outputProjectionMissingOnPublicRead-DWSeo7oC.mjs";import To from"./packem_shared/ownerFieldFromArgsNotAuth-J8Xbz5uI.mjs";import xo from"./packem_shared/paymentCreateWithoutAuthorize-CdenWpXD.mjs";import Co from"./packem_shared/paymentWebhookWideTolerance-DVhJB0Ip.mjs";import ko from"./packem_shared/plaintextSecretInWranglerVariables-bNkqmNqV.mjs";import No from"./packem_shared/policyReferencesUnknownTable-CIKuRZ5Y.mjs";import Fo from"./packem_shared/privilegedDispatchUnvalidatedPayload-BM3NtT57.mjs";import Mo from"./packem_shared/privilegedFanoutFromPublicProcedure-DV188gHQ.mjs";import Oo from"./packem_shared/procedureWithoutStructuredEvent-CZ_B23nM.mjs";import Do from"./packem_shared/publicArgumentUsesAny-BDEvMA8Q.mjs";import Po from"./packem_shared/publicMutationWithoutRatelimit-Bd-rRxnz.mjs";import $o from"./packem_shared/publicTableRlsOptoutConfusion-CbNk75Xo.mjs";import Eo from"./packem_shared/queueDispatchCycle-84yt1IR-.mjs";import qo from"./packem_shared/queueWithoutDlq-BvHz3Opg.mjs";import Lo from"./packem_shared/r2sqlOutsideAction-BdLiLOAX.mjs";import Ko from"./packem_shared/ratelimitDefaultMemoryStore-Dj0Kk7t9.mjs";import Go from"./packem_shared/ratelimitKeySpoofableOrGlobal-DHMl74hR.mjs";import _o from"./packem_shared/ratelimitMiddlewareFailOpen-Di2t8pDc.mjs";import zo from"./packem_shared/relationReferencesUnknownField-Cl6mik8w.mjs";import Ho from"./packem_shared/relationReferencesUnknownTable-vYjirSxf.mjs";import Bo from"./packem_shared/rlsUncoveredTable-CZ4ie4gX.mjs";import jo from"./packem_shared/selfRescheduleWithoutProgress-CQNZI4FA.mjs";import Vo from"./packem_shared/shapeTargetsGlobalTable-Bu3eEDic.mjs";import Qo from"./packem_shared/shapeUnknownTable-CREfNnWi.mjs";import Xo from"./packem_shared/signupMutationWithoutDisposableGating-BdKwtxDP.mjs";import Zo from"./packem_shared/softDeleteIncludeDeletedFromArgs-C7Ugg4zb.mjs";import Jo from"./packem_shared/sqlInjectionRisk-BYRllgPV.mjs";import Yo from"./packem_shared/storageGenerateUploadUrlNoContentTypePin-CrwFuo83.mjs";import oe from"./packem_shared/storageKeyFromUserArgs-3vkxmI3v.mjs";import ee from"./packem_shared/storagePresignedUrlForPrivateContent-C3KeV5tV.mjs";import re from"./packem_shared/storageUploadWithoutContentTypeAllowlist-BXoK_QWE.mjs";import te from"./packem_shared/storageUploadWithoutMaxSize-CW7uKvSi.mjs";import ie from"./packem_shared/tableWithoutInsert-ClJp_jDr.mjs";import ne from"./packem_shared/throughRelationMissingUniqueIndex-Cg5e3GkS.mjs";import ae from"./packem_shared/topicTooManySubscriptions-CiZhFm9r.mjs";import me from"./packem_shared/ttlFieldNotTimestamp-lnb5nmM6.mjs";import{s as le,q as se,c as pe}from"./packem_shared/helpers-DdZ_pyR3.mjs";import fe from"./packem_shared/unboundedCollectOnOpenRange-DHy2RUL7.mjs";import ce from"./packem_shared/unboundedLoop-DxN3_apu.mjs";import de from"./packem_shared/unboundedStringArgument-jZnCbwB0.mjs";import ue from"./packem_shared/unindexedForeignKey-Dypgn8uH.mjs";import he from"./packem_shared/unindexedRelationTarget-Dj2_NOCm.mjs";import ge from"./packem_shared/unrestrictedWhereBranch-CcIBmHik.mjs";import be from"./packem_shared/userCreatingMutationWithoutCaptcha-D4TZEpgz.mjs";import ye from"./packem_shared/vectorsNamespaceFromUserInput-BWvGjJzp.mjs";import Se from"./packem_shared/workflowDuplicateStepName-B6eAJzwA.mjs";import we from"./packem_shared/workflowUnknownTarget-DEKB5H0g.mjs";import ve from"./packem_shared/workflowUnused-DHwwE-1P.mjs";import{callSiteCallers as qt,callSiteDescription as Lt,callSiteLabel as Kt,callSiteMetadata as Gt,helperRole as _t,isReachableSite as zt,readCallSiteCallers as Ht}from"./packem_shared/callSiteCallers-DArHfQSU.mjs";import{compareToBaseline as jt,parseAdvisorMap as Vt}from"./packem_shared/compareToBaseline-CUG59IOy.mjs";import{gradeFromScore as Xt}from"./packem_shared/gradeFromScore-KSt58rj1.mjs";import{MAP_VERSION as Jt,byCodepoint as Yt,scoreAdvisor as oi}from"./packem_shared/MAP_VERSION-MQ5W2B6n.mjs";import{default as ri}from"./packem_shared/classifySensitivity-JnjaTGYi.mjs";import{fromServerSchema as ii}from"./packem_shared/fromServerSchema-KWJd4PLE.mjs";const Ae={convex:"migrating/from-convex",firebase:"migrating/from-firebase",supabase:"migrating/from-supabase"},m={categories:["SCHEMA"],description:"A migrated-away platform's SDK is still imported from `lunora/` source. The code compiles and typechecks, so a half-finished port keeps reading from the old platform at runtime and the two stores silently drift apart.",facing:"INTERNAL",level:"WARN",name:"migration_stale_import",remediation:"Replace the call with its Lunora equivalent and drop the dependency. If the import is deliberate — a second data source you still read from — move it out of `lunora/` so it is not mistaken for an unfinished migration.",run:r=>r.staleMigrationImports===void 0?[]:r.staleMigrationImports.map(o=>a(m,{cacheKey:`migration_stale_import:${o.file}:${o.line.toString()}:${o.moduleSpecifier}`,detail:`\`${o.file}\` (line ${o.line.toString()}) still imports \`${o.moduleSpecifier}\`. Finish the port — see \`${Ae[o.platform]}\` — or move the import out of \`lunora/\` if you genuinely still read from ${o.platform}.`,metadata:{file:o.file,line:o.line,moduleSpecifier:o.moduleSpecifier,platform:o.platform}})),source:"static",title:"Stale migration import"},Re=new Map([["global",{level:"WARN",scope:r=>`it reads the whole D1 table "${r}" over a cross-region round trip`}],["root",{level:"WARN",scope:r=>`it loads every row of "${r}" from the root Durable Object's SQLite into memory`}],["shardBy",{level:"INFO",scope:r=>`"${r}" is \`.shardBy()\`, so this collects one shard's rows rather than the whole table — bounded by a single tenant's row count`}]]),Ue={level:"WARN",scope:r=>`it loads every row of "${r}"`},l={categories:["PERFORMANCE"],description:"A query calls `.collect()` with no `.withIndex()` and no `.filter()`, so it materializes every row of the table. Any live subscription over it also re-sends that whole result to every subscribed client on every write to the table.",facing:"EXTERNAL",level:"WARN",name:"unbounded_collect",remediation:'Narrow the read with `.withIndex("name", (q) => q.eq(...))`, cap it with `.take(n)`, or page it with `.paginate(args.paginationOpts)` so neither the scan nor the subscription payload grows with the table.',run:r=>{const o=[],i=le(r.schema);for(const e of r.queries??[]){if(e.terminal!=="collect"||e.hasIndex||e.table===""||e.hasFilter)continue;const t=i.get(e.table),{level:n,scope:s}=Re.get(t??"")??Ue,p=se(e),f=n==="WARN"?` A live subscription over this query records a whole-table dependency, so every write to "${e.table}" re-runs it and re-sends the full result to each subscribed socket.`:" Cap it with `.take(n)` if that count can grow.";o.push(a(l,{cacheKey:`unbounded_collect:${e.file}:${e.line.toString()}:${e.table}`,detail:`Query on "${e.table}" at ${p} calls .collect() with no index and no filter — ${s(e.table)}.${f}`,level:n,metadata:{...pe(e),shardKind:t??"unknown",table:e.table}}))}return o},source:"static",title:"Unbounded collect"},We=[fo,Ho,zo,we,Se,Qo,V,Q,X,H,io,B,ro,to,j,me,O,M,K,G,ue,he,ne,z,ie,ve,Eo,qo,ae,J,Y,Z,l,fe,ce,_,jo,Vo,wo,vo,Ao,lo,Lo,U,No,Bo,bo,yo,P,$,E,Po,ge,be,Xo,Do,de,no,Jo,y,xo,ho,Ko,N,Mo,Oo,co,A,g,b,To,oe,uo,D,S,w,ye,go,F,Fo,L,q,T,k,W,x,I,C,po,Go,$o,R,re,te,Yo,ee,ao,mo,_o,oo,eo,v,so,Co,Zo,So,Io,Ro,Wo,Uo,ko,m],Ie=[u,h,d],Te=[...We,...Ie],Pt=(r,o={})=>{const i=o.lints??Te,e=[];for(const t of i)o.source!==void 0&&t.source!==o.source||e.push(...t.run(r));return c(e)};export{Te as ALL_LINTS,Jt as MAP_VERSION,Ie as RUNTIME_LINTS,We as STATIC_LINTS,g as actionFetchSsrf,b as actionWithoutErrorHandling,y as adminRouteWithoutGuard,S as aiRawRunEscapeHatch,w as aiRunWithoutLogging,v as aiToolSideEffectPromptInjection,A as aiUnboundedGenerationPublic,R as allowUnauthenticatedShardAccessEnabled,U as authApiCallWithoutHeaders,W as authCsrfCheckDisabled,I as authEmailVerificationDisabled,T as authScimWithoutTransactions,x as authSecureCookiesDisabled,C as authSessionFreshageZero,k as authTrustedOriginsWildcard,N as browserAllowPrivateTargets,F as browserUserUrlWithoutAllowlist,Yt as byCodepoint,qt as callSiteCallers,Lt as callSiteDescription,Kt as callSiteLabel,Gt as callSiteMetadata,M as circularFk,ri as classifySensitivity,O as commitOrderedHardDelete,jt as compareToBaseline,D as containerInstanceKeyFromUserInput,P as containerLongSleepAfter,$ as containerOversizedInstance,E as containerPublicInternet,q as containerRuntimeEgressRelaxation,L as containerStartEnableInternetOverride,K as countWithoutIndex,G as crossModuleTableWrite,c as dedupeCacheKeys,_ as dispatchCycle,z as duplicateIndex,H as emptyIndex,B as errorWithoutCatalog,j as exportSinkMisconfigured,V as externalSourceIncrementalNoDeletePath,Q as externalSourceOnGlobal,X as externalSourceUnscoped,d as fanOutBreadth,Z as filterAfterIndexScan,J as filterOnPrimaryKey,Y as filterWithoutIndex,oo as flagGatesSecurityWithUnsafeDefault,eo as flagReadInSubscription,ii as fromServerSchema,ro as geoIndexFieldNotGeopoint,to as geoIndexUnused,io as globalTableNearColumnLimit,Xt as gradeFromScore,no as hardcodedSecret,_t as helperRole,u as hotShard,ao as httpActionMissingAuthGuard,mo as httpActionResponseHeaderInjection,lo as hyperdriveOutsideAction,so as identityUndeclaredClaimTrusted,po as imagesUrlSourceFromUserInput,fo as indexReferencesUnknownField,h as indexUtilization,co as insertManyUnsafeUserData,zt as isReachableSite,uo as kvUnscopedUserKeyIdor,ho as mailInboundDispatchWithoutVerify,go as mailRecipientFromRequestInput,bo as maskUncoveredPiiColumn,yo as maskWeakHashStrategyOnPii,So as maskedRelationLeakViaWith,wo as mutatorFullRowReplace,vo as mutatorWithoutOwnerScope,Ao as nondeterministicQueryMutation,Ro as normalizeIdUsedAsAuthorization,Uo as notifyMissingPushConfig,Wo as notifySendOutsideAction,Io as outputProjectionMissingOnPublicRead,To as ownerFieldFromArgsNotAuth,Vt as parseAdvisorMap,xo as paymentCreateWithoutAuthorize,Co as paymentWebhookWideTolerance,ko as plaintextSecretInWranglerVariables,No as policyReferencesUnknownTable,Fo as privilegedDispatchUnvalidatedPayload,Mo as privilegedFanoutFromPublicProcedure,Oo as procedureWithoutStructuredEvent,Do as publicArgumentUsesAny,Po as publicMutationWithoutRatelimit,$o as publicTableRlsOptoutConfusion,Eo as queueDispatchCycle,qo as queueWithoutDlq,Lo as r2sqlOutsideAction,Ko as ratelimitDefaultMemoryStore,Go as ratelimitKeySpoofableOrGlobal,_o as ratelimitMiddlewareFailOpen,Ht as readCallSiteCallers,zo as relationReferencesUnknownField,Ho as relationReferencesUnknownTable,Bo as rlsUncoveredTable,Pt as runAdvisor,oi as scoreAdvisor,jo as selfRescheduleWithoutProgress,Vo as shapeTargetsGlobalTable,Qo as shapeUnknownTable,Xo as signupMutationWithoutDisposableGating,Zo as softDeleteIncludeDeletedFromArgs,Jo as sqlInjectionRisk,Yo as storageGenerateUploadUrlNoContentTypePin,oe as storageKeyFromUserArgs,ee as storagePresignedUrlForPrivateContent,re as storageUploadWithoutContentTypeAllowlist,te as storageUploadWithoutMaxSize,ie as tableWithoutInsert,ne as throughRelationMissingUniqueIndex,ae as topicTooManySubscriptions,me as ttlFieldNotTimestamp,fe as unboundedCollectOnOpenRange,ce as unboundedLoop,de as unboundedStringArgument,ue as unindexedForeignKey,he as unindexedRelationTarget,ge as unrestrictedWhereBranch,be as userCreatingMutationWithoutCaptcha,ye as vectorsNamespaceFromUserInput,Se as workflowDuplicateStepName,we as workflowUnknownTarget,ve as workflowUnused};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{e as d}from"./finding-NrKO8idM.mjs";const l=3600,f={h:3600,m:60,s:1},p=/^(\d+)([smh])$/u,c=n=>{if(typeof n=="number")return n;const o=p.exec(n);if(o===null)return;const[,e="0",t="s"]=o;return Number(e)*(f[t]??1)},u={categories:["PERFORMANCE"],description:"A container's idle window (its `sleepAfter`, capped by any `hardTimeout`) is longer than an hour, so every idle instance keeps running, and billed, for that whole window.",facing:"INTERNAL",level:"WARN",name:"container_long_sleep_after",remediation:"Lower `sleepAfter` to the idle window the workload needs (the default is 10m), or add a `hardTimeout` that caps how long an instance may live.",run:n=>{const o=[];for(const e of n.containers??[]){const{hardTimeout:t,sleepAfter:i}=e;if(i===void 0)continue;const r=c(i);if(r===void 0)continue;const a=t===void 0?void 0:c(t),s=a===void 0?r:Math.min(r,a);s<=l||o.push(d(u,{cacheKey:`container_long_sleep_after:${e.exportName}`,detail:`Container "${e.exportName}" can sit idle for ${s.toString()} s before it sleeps (sleepAfter ${String(i)}${a===void 0?"":`, capped by hardTimeout ${String(t)}`}); every idle instance is billed for that whole window.`,metadata:{container:e.exportName,effectiveIdleSeconds:s,hardTimeout:t,sleepAfter:i}}))}return o},source:"static",title:"Long container idle window"};export{u as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{e as i}from"./finding-NrKO8idM.mjs";import{s as d,q as h,c as u}from"./helpers-DdZ_pyR3.mjs";const c={categories:["PERFORMANCE"],description:"A `count()` runs on a table with no aggregate index, so every call scans every row of the table to count them. The cost grows with the table, and repeats on every call.",facing:"EXTERNAL",level:"WARN",name:"count_without_index",remediation:"Declare an aggregate index that covers the count (`.aggregateIndex(name, { by: [...] })` on the table, or `defineAggregateIndex` in the schema) so `count()` reads a counter. For a small table or a count that runs rarely, the scan may be acceptable.",run:a=>{const t=[],l=d(a.schema);for(const e of a.countReads??[]){if(e.indexed)continue;const n=h(e),o=l.get(e.table),s={...u(e),shardKind:o??"unknown",table:e.table},r=`count_without_index:${e.file}:${e.line.toString()}:${e.table}`;if(o==="shardBy"){t.push(i(c,{cacheKey:r,detail:`count() on "${e.table}" at ${n} has no aggregate index. "${e.table}" is \`.shardBy()\`, so the count covers one shard's rows, not the whole table. It is bounded by one tenant's row count.`,level:"INFO",metadata:s}));continue}t.push(i(c,{cacheKey:r,detail:`count() on "${e.table}" at ${n} has no aggregate index, so it scans every row of "${e.table}" to count them.`,metadata:s}))}return t},source:"static",title:"Count without index"};export{c as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{s as l}from"./dispatch-namespace-BE_LxN7Z.mjs";import{e as d}from"./finding-NrKO8idM.mjs";import{s as f}from"./graph-CrSMHkOs.mjs";const h=o=>{const i=[];for(const e of o)(e.kind==="call"||e.kind==="schedule")&&e.target!==void 0&&e.conditional!==!0&&e.scope.kind==="export"&&i.push({file:e.file,from:`${l(e.file)}:${e.scope.name}`,kind:e.kind,line:e.line,to:e.target});return i},p=o=>`\`${o.from}\` ${o.kind==="schedule"?"schedules":"calls"} \`${o.to}\` (${o.file}:${o.line.toString()})`,m={categories:["PERFORMANCE"],description:"Functions dispatch each other (or themselves) in a cycle where every hop is unconditional — no guard, loop, `try`, or early return in front of any of them. Once one of them runs, the chain re-schedules itself forever, and every storage read or write along it is billed on every pass.",facing:"EXTERNAL",level:"ERROR",name:"dispatch_cycle",remediation:"Put a stop condition in front of the re-dispatch: reschedule only while there is work left (`if (remaining > 0) await ctx.scheduler.runAfter(…)`), carry an attempt counter in the args and stop at a cap, keep the returned id so a stop path can `ctx.scheduler.cancel` it, or move periodic work to a `cron`. If the chain already stops by throwing from a helper, make that stop condition explicit at the call site.",run:o=>{if(o.callEdges===void 0)return[];const i=h(o.callEdges),e=new Map;for(const n of i){const r=e.get(n.from);r===void 0?e.set(n.from,[n.to]):r.push(n.to)}return f([...e.keys()].toSorted((n,r)=>n.localeCompare(r)),e).flatMap(n=>{const r=new Set(n),a=i.filter(t=>r.has(t.from)&&r.has(t.to)).toSorted((t,s)=>t.from.localeCompare(s.from)||t.file.localeCompare(s.file)||t.line-s.line);if(a.length===0)return[];const c=n.toSorted((t,s)=>t.localeCompare(s));return[d(m,{cacheKey:`dispatch_cycle:${c.join(",")}`,detail:`Unconditional dispatch cycle: ${a.map(t=>p(t)).join("; ")}. No guard stands in front of any hop, so once one of these functions runs the chain never stops.`,metadata:{functions:c,hops:a.map(t=>({file:t.file,from:t.from,kind:t.kind,line:t.line,to:t.to}))}})]})},source:"static",title:"Endless dispatch cycle"};export{m as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const a=i=>({rlsMode:i.rlsMode,tables:Object.entries(i.tables).map(([
|
|
1
|
+
const a=i=>({rlsMode:i.rlsMode,tables:Object.entries(i.tables).map(([s,n])=>{const o=[...n.indexes.map(e=>({fields:e.fields,kind:"index",name:e.name,unique:e.unique})),...n.searchIndexes.map(e=>({fields:[e.field,...e.filterFields??[]],kind:"search",name:e.name})),...n.rankIndexes.map(e=>({fields:[...e.sortBy.map(r=>r.field),...e.partitionBy??[]],kind:"rank",name:e.name})),...n.vectorIndexes.map(e=>({fields:[e.field],kind:"vector",name:e.name})),...n.geoIndexes.map(e=>({fields:[e.field],kind:"geo",name:e.name}))],d={};for(const[e,r]of Object.entries(n.shape))if(r.kind==="optional"){const t=r._meta?.inner;d[e]=t?.kind??r.kind}else d[e]=r.kind;return{externallyManaged:n.isExternallyManaged??!1,externalSource:n.externalSource?{hasReconcile:n.externalSource.reconcileEveryMs!==void 0,hasSoftDelete:n.externalSource.softDeleteColumn!==void 0,hasTenantBy:n.externalSource.tenantBy!==void 0,mode:n.externalSource.mode}:void 0,columnKinds:d,commitOrdered:n.commitOrderedMode,fields:Object.keys(n.shape),indexes:o,isPublic:n.isPublic??!1,name:s,shardKind:n.shardMode.kind,softDelete:n.softDeleteMode,ttl:n.ttlPolicy,relations:Object.entries(n.relationMap).map(([e,r])=>r.through!==void 0?{kind:"many",name:e,table:r.table,through:{...r.through}}:{field:r.field,kind:r.kind,name:e,onDelete:r.onDelete,references:r.references,table:r.table})}})});export{a as fromServerSchema};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const f=(a,r)=>{const e=new Map,i=new Set,c=[],l=[],p=n=>{const o={index:e.size,lowLink:e.size};e.set(n,o),c.push(n),i.add(n);for(const s of r.get(n)??[]){const t=e.get(s);t===void 0?o.lowLink=Math.min(o.lowLink,p(s).lowLink):i.has(s)&&(o.lowLink=Math.min(o.lowLink,t.index))}if(o.lowLink===o.index){const s=[];for(let t=c.pop();t!==void 0;t=t===n?void 0:c.pop())i.delete(t),s.push(t);l.push(s)}return o};for(const n of a)e.has(n)||p(n);return l};export{f as s};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{s as d}from"./dispatch-namespace-BE_LxN7Z.mjs";import{e as m}from"./finding-NrKO8idM.mjs";import{s as h}from"./graph-CrSMHkOs.mjs";const u=e=>`queue:${e}`,f=e=>`topic:${e}`,q=e=>{const s=new Map;for(const i of e)i.filePath!==void 0&&s.set(`${d(i.filePath)}:${i.exportName}`,u(i.exportName)),i.handlerSite!==void 0&&s.set(`${d(i.handlerSite.file)}:${i.handlerSite.exportName}`,u(i.exportName));return s},v=(e,s,i,n,r)=>{const a=`${d(e.file)}:${e.scope.kind==="export"?e.scope.name:""}`,o=i.get(a)??a;switch(e.kind){case"call":case"schedule":return{file:e.file,from:o,kind:e.kind,line:e.line,to:i.get(s)??s};case"enqueue":return n.has(s)?{file:e.file,from:o,kind:"enqueue",line:e.line,to:u(s)}:void 0;case"publish":return r.has(s)?{file:e.file,from:o,kind:"publish",line:e.line,to:f(s)}:void 0;default:return}},y=(e,s)=>{const i=q(s),n=new Set(s.map(o=>o.exportName)),r=new Set(s.flatMap(o=>o.topic===void 0?[]:[o.topic])),a=[];for(const o of e){const c=o.target===void 0||o.scope.kind!=="export"||o.conditional===!0&&o.loopOnly!==!0?void 0:v(o,o.target,i,n,r);c!==void 0&&a.push(c)}for(const o of s)o.topic!==void 0&&a.push({file:o.filePath??"",from:f(o.topic),kind:"subscribe",line:0,to:u(o.exportName)});return a},b={call:"calls",enqueue:"enqueues to",publish:"publishes to",schedule:"schedules",subscribe:"delivers to"},k={categories:["PERFORMANCE"],description:"A queue consumer, or a function it runs, sends back to its own queue (directly or through a topic a subscriber publishes to), on every message it handles. Each message produces another, so the queue never drains.",facing:"EXTERNAL",level:"ERROR",name:"queue_dispatch_cycle",remediation:"Stop the re-enqueue behind a condition: send again only while work remains, carry an attempt count in the message body and stop at a cap, or route the failure to the queue's dead-letter queue instead of back into the same one.",run:e=>{if(e.callEdges===void 0||e.queues===void 0)return[];const s=y(e.callEdges,e.queues),i=new Map;for(const n of s){const r=i.get(n.from);r===void 0?i.set(n.from,[n.to]):r.push(n.to)}return h([...i.keys()].toSorted((n,r)=>n.localeCompare(r)),i).flatMap(n=>{const r=new Set(n),a=n.some(t=>t.startsWith("queue:")||t.startsWith("topic:")),o=s.filter(t=>r.has(t.from)&&r.has(t.to)).toSorted((t,l)=>t.from.localeCompare(l.from)||t.file.localeCompare(l.file)||t.line-l.line);if(!a||o.length===0)return[];const c=n.toSorted((t,l)=>t.localeCompare(l)),p=o.map(t=>`\`${t.from}\` ${b[t.kind]} \`${t.to}\` (${t.file}:${t.line.toString()})`);return[m(k,{cacheKey:`queue_dispatch_cycle:${c.join(",")}`,detail:`Unconditional queue cycle: ${p.join("; ")}. No \`if\`, \`try\`, or early return stands in front of any hop, so once one of these runs the chain never stops.`,metadata:{hops:o.map(t=>({file:t.file,from:t.from,kind:t.kind,line:t.line,to:t.to})),nodes:c}})]})},source:"static",title:"Endless queue cycle"};export{k as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{e as m}from"./finding-NrKO8idM.mjs";import{t as d}from"./helpers-DdZ_pyR3.mjs";const u=(o,r,a,n)=>{if(n.through!==void 0){const c=o.get(n.through.table);return c===void 0?[]:[n.through.from,n.through.to].filter(f=>!r(c).has(f)).map(f=>({column:f,owner:c.name,side:"field"}))}const e=o.get(n.table);if(!e)return[];const s=n.kind==="one"?a:e,t=n.kind==="one"?e:a,i=[];return r(s).has(n.field)||i.push({column:n.field,owner:s.name,side:"field"}),r(t).has(n.references)||i.push({column:n.references,owner:t.name,side:"references"}),i},l={categories:["SCHEMA"],description:"A relation references a foreign-key or referenced column that is not declared on its table, so the join can never resolve.",facing:"INTERNAL",level:"ERROR",name:"relation_references_unknown_field",remediation:"Fix the `field` / `references` column name in the relation, or add the missing column.",run:o=>{const r=new Map(o.schema.tables.map(e=>[e.name,e])),a=new Map,n=e=>{let s=a.get(e);return s||(s=d(e),a.set(e,s)),s};return o.schema.tables.flatMap(e=>e.relations.flatMap(s=>u(r,n,e,s).map(t=>m(l,{cacheKey:`relation_references_unknown_field:${e.name}:${s.name}:${t.side}`,detail:`Relation "${s.name}" on table "${e.name}" uses ${t.side} "${t.column}", which is not declared on table "${t.owner}".`,metadata:{column:t.column,owner:t.owner,relation:s.name,side:t.side,table:e.name}}))))},source:"static",title:"Relation references unknown field"};export{l as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{e as s}from"./finding-NrKO8idM.mjs";const l={categories:["SCHEMA"],description:"A relation targets a table that does not exist in the schema. The relation can never load — the target is a typo or a removed/renamed table.",facing:"INTERNAL",level:"ERROR",name:"relation_references_unknown_table",remediation:"Fix the target table name in the relation, or add the missing table to the schema.",run:n=>{const r=new Set(n.schema.tables.map(e=>e.name));return n.schema.tables.flatMap(e=>e.relations.flatMap(a=>[{name:a.table,role:""},...a.through===void 0?[]:[{name:a.through.table,role:":through"}]].filter(({name:t})=>!r.has(t)).map(({name:t,role:o})=>s(l,{cacheKey:`relation_references_unknown_table:${e.name}:${a.name}${o}`,detail:`Relation "${a.name}" on table "${e.name}" targets table "${t}", which does not exist in the schema.`,metadata:{relation:a.name,table:e.name,target:t}}))))},source:"static",title:"Relation references unknown table"};export{l as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{callSiteCallers as l}from"./callSiteCallers-DArHfQSU.mjs";import{e as c}from"./finding-NrKO8idM.mjs";import{r as d,f as u,g as f}from"./helpers-DdZ_pyR3.mjs";const h=1e3,p=6e4,m=new Set(["patch","patchIf","patchMany","patchWhere"]),w=e=>new Map([...e].map(([n,o])=>[n,o.filter(t=>!m.has(t.method))])),g=/^(?:continue|next|page)?_?cursor$/iu,v=e=>{if(!(e.kind!=="schedule"||e.conditional!==!0||e.target===void 0||e.scope.kind!=="export"))return e.target===u(e.file,e.scope.name)?e.scope.name:void 0},y=e=>{const n=e.schedule?.delayMs,o=e.schedule?.argKeys;if(!(n===void 0||n>=p||o===void 0||o.some(t=>g.test(t))))return n},R=(e,n,o)=>e.some(t=>t.kind==="call"&&t.file===n&&t.scope.kind==="export"&&t.scope.name===o),E=(e,n,o,t)=>{const s=(e.queries??[]).filter(r=>r.file===n&&r.table!==""&&l(r.scope).includes(o));return s.length===0||s.some(r=>f(t,r,void 0,[o]))},S={categories:["PERFORMANCE"],description:"A function re-arms itself with `ctx.scheduler.runAfter` on a delay under a minute, and the re-arming passes no cursor and nothing in the function removes the rows it reads. Each run finds the same rows and does the same work again; a delay under a second is a spin, and a longer one is a poll that rewrites a row on every tick.",facing:"EXTERNAL",level:"WARN",name:"self_reschedule_without_progress",remediation:"Carry the progress forward: pass a cursor (or the last processed id) in the dispatch args, or delete or update the rows the function handled so the next run selects only what is left. Re-arm only while work remains, and use a longer delay or a `cron` for periodic work.",run:e=>{if(e.callEdges===void 0)return[];const{callEdges:n}=e,o=d(e.tableWrites,e.rowEffects),t=[];for(const s of n){const r=v(s),i=y(s);if(r===void 0||i===void 0)continue;const a=i>=h;R(n,s.file,r)||E(e,s.file,r,a?w(o):o)||t.push(c(S,{cacheKey:`self_reschedule_without_progress:${s.file}:${s.line.toString()}:${r}`,detail:a?`\`${r}\` (${s.file}:${s.line.toString()}) polls itself every ${i.toString()} ms with no cursor in its args, and it removes none of the rows it reads. Each tick re-reads the same rows and rewrites in place, and each run is billed.`:`\`${r}\` (${s.file}:${s.line.toString()}) re-arms itself after ${i.toString()} ms with no cursor in its args, and it removes none of the rows it reads. The next run finds the same rows and starts the same work again.`,level:a?"INFO":"WARN",metadata:{delayMs:i,file:s.file,function:r,line:s.line,tier:a?"poll":"spin"}}))}return t},source:"static",title:"Self-reschedule without progress"};export{S as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{e as d}from"./finding-NrKO8idM.mjs";const h=(t,s,n)=>t.some(e=>e.unique===!0&&e.fields.length===2&&e.fields.includes(s)&&e.fields.includes(n)),l={categories:["SCHEMA"],description:"A many-to-many relation's join table has no unique index over its two keys, so the same link can be stored twice and every read through the relation returns the target twice.",facing:"INTERNAL",level:"ERROR",name:"through_relation_missing_unique_index",remediation:'Add a unique index over the two keys on the join table, e.g. `.index("byPostTag", ["postId", "tagId"], { unique: true })`.',run:t=>{const s=new Map(t.schema.tables.map(n=>[n.name,n]));return t.schema.tables.flatMap(n=>n.relations.flatMap(e=>{if(e.through===void 0)return[];const{from:i,table:o,to:a}=e.through,r=s.get(o);return r===void 0?[]:h(r.indexes.filter(u=>u.kind==="index"),i,a)?[]:[d(l,{cacheKey:`through_relation_missing_unique_index:${n.name}:${e.name}`,detail:`Relation "${e.name}" on table "${n.name}" goes through "${o}", which has no unique index over ("${i}", "${a}"). A repeated connect stores the same link twice.`,metadata:{relation:e.name,suggestedIndex:{fields:[i,a],name:`by_${i}_${a}`,unique:!0},table:o}})]}))},source:"static",title:"Through relation missing unique index"};export{l as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{e as o}from"./finding-NrKO8idM.mjs";import{s as m,P as l,l as r}from"./fk-index-DXYS17cc.mjs";const d=e=>e.through===void 0?[{column:e.field,references:e.references,table:e.table}]:[{column:e.through.from,references:"_id",table:e.through.table},{column:e.through.to,references:"_id",table:e.through.table}],c=(e,i)=>i===l||e.relations.some(n=>n.kind==="one"&&n.field===i)||r(e).has(i),u={categories:["PERFORMANCE"],description:"A `many` relation's foreign-key column on the target table has no index leading with it. Relation predicates (`some`/`none`/`every`) and `with:` child reads filter the target on that column, full-scanning it as rows accumulate.",facing:"EXTERNAL",level:"INFO",name:"unindexed_relation_target",remediation:'Add a secondary index on the target table leading with the FK column, e.g. `.index("byAuthorId", ["authorId"])`.',run:e=>{const i=new Map(e.schema.tables.map(n=>[n.name,n]));return e.schema.tables.flatMap(n=>n.relations.filter(s=>s.kind==="many").flatMap(s=>d(s).flatMap(a=>{const t=i.get(a.table);return t===void 0||c(t,a.column)?[]:[o(u,{cacheKey:`unindexed_relation_target:${t.name}:${a.column}`,detail:`Relation "${s.name}" on table "${n.name}" is a to-many over "${t.name}" via its column "${a.column}", which is not the leading column of any index on "${t.name}". Relation predicates (\`some\`/\`none\`/\`every\`) and \`with:\` reads of "${s.name}" full-scan "${t.name}".`,metadata:{fkColumn:a.column,references:{column:a.references,table:n.name},relation:s.name,suggestedIndex:{fields:[a.column],name:m(a.column)},table:t.name}})]})))},source:"static",title:"Unindexed relation target"};export{u 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.212",
|
|
4
4
|
"description": "Schema & query lints (splinter-style advisors) for Lunora, feeding the Studio Advisors view",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"advisor",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
|
|
50
|
-
"@lunora/server": "1.0.0-alpha.
|
|
50
|
+
"@lunora/server": "1.0.0-alpha.196"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": "^22.15.0 || >=24.11.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as f}from"./dispatch-namespace-BE_LxN7Z.mjs";import{e as h}from"./finding-NrKO8idM.mjs";const p=i=>{const c=[];for(const t of i)(t.kind==="call"||t.kind==="schedule")&&t.target!==void 0&&t.conditional!==!0&&t.scope.kind==="export"&&c.push({file:t.file,from:`${f(t.file)}:${t.scope.name}`,kind:t.kind,line:t.line,to:t.target});return c},m=(i,c)=>{const t=new Map,n=new Set,s=[],a=[],d=e=>{const o={index:t.size,lowLink:t.size};t.set(e,o),s.push(e),n.add(e);for(const l of c.get(e)??[]){const r=t.get(l);r===void 0?o.lowLink=Math.min(o.lowLink,d(l).lowLink):n.has(l)&&(o.lowLink=Math.min(o.lowLink,r.index))}if(o.lowLink===o.index){const l=[];for(let r=s.pop();r!==void 0;r=r===e?void 0:s.pop())n.delete(r),l.push(r);a.push(l)}return o};for(const e of i)t.has(e)||d(e);return a},u=i=>`\`${i.from}\` ${i.kind==="schedule"?"schedules":"calls"} \`${i.to}\` (${i.file}:${i.line.toString()})`,k={categories:["PERFORMANCE"],description:"Functions dispatch each other (or themselves) in a cycle where every hop is unconditional — no guard, loop, `try`, or early return in front of any of them. Once one of them runs, the chain re-schedules itself forever, and every storage read or write along it is billed on every pass.",facing:"EXTERNAL",level:"ERROR",name:"dispatch_cycle",remediation:"Put a stop condition in front of the re-dispatch: reschedule only while there is work left (`if (remaining > 0) await ctx.scheduler.runAfter(…)`), carry an attempt counter in the args and stop at a cap, keep the returned id so a stop path can `ctx.scheduler.cancel` it, or move periodic work to a `cron`. If the chain already stops by throwing from a helper, make that stop condition explicit at the call site.",run:i=>{if(i.callEdges===void 0)return[];const c=p(i.callEdges),t=new Map;for(const n of c){const s=t.get(n.from);s===void 0?t.set(n.from,[n.to]):s.push(n.to)}return m([...t.keys()].toSorted((n,s)=>n.localeCompare(s)),t).flatMap(n=>{const s=new Set(n),a=c.filter(e=>s.has(e.from)&&s.has(e.to)).toSorted((e,o)=>e.from.localeCompare(o.from)||e.file.localeCompare(o.file)||e.line-o.line);if(a.length===0)return[];const d=n.toSorted((e,o)=>e.localeCompare(o));return[h(k,{cacheKey:`dispatch_cycle:${d.join(",")}`,detail:`Unconditional dispatch cycle: ${a.map(e=>u(e)).join("; ")}. No guard stands in front of any hop, so once one of these functions runs the chain never stops.`,metadata:{functions:d,hops:a.map(e=>({file:e.file,from:e.from,kind:e.kind,line:e.line,to:e.to}))}})]})},source:"static",title:"Endless dispatch cycle"};export{k as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{e as c}from"./finding-NrKO8idM.mjs";import{t as m}from"./helpers-DdZ_pyR3.mjs";const f=(o,i,t,a)=>{const e=o.get(a.table);if(!e)return[];const n=a.kind==="one"?t:e,s=a.kind==="one"?e:t,r=[];return i(n).has(a.field)||r.push({column:a.field,owner:n.name,side:"field"}),i(s).has(a.references)||r.push({column:a.references,owner:s.name,side:"references"}),r},d={categories:["SCHEMA"],description:"A relation references a foreign-key or referenced column that is not declared on its table, so the join can never resolve.",facing:"INTERNAL",level:"ERROR",name:"relation_references_unknown_field",remediation:"Fix the `field` / `references` column name in the relation, or add the missing column.",run:o=>{const i=new Map(o.schema.tables.map(e=>[e.name,e])),t=new Map,a=e=>{let n=t.get(e);return n||(n=m(e),t.set(e,n)),n};return o.schema.tables.flatMap(e=>e.relations.flatMap(n=>f(i,a,e,n).map(s=>c(d,{cacheKey:`relation_references_unknown_field:${e.name}:${n.name}:${s.side}`,detail:`Relation "${n.name}" on table "${e.name}" uses ${s.side} "${s.column}", which is not declared on table "${s.owner}".`,metadata:{column:s.column,owner:s.owner,relation:n.name,side:s.side,table:e.name}}))))},source:"static",title:"Relation references unknown field"};export{d as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{e as r}from"./finding-NrKO8idM.mjs";const s={categories:["SCHEMA"],description:"A relation targets a table that does not exist in the schema. The relation can never load — the target is a typo or a removed/renamed table.",facing:"INTERNAL",level:"ERROR",name:"relation_references_unknown_table",remediation:"Fix the target table name in the relation, or add the missing table to the schema.",run:t=>{const n=new Set(t.schema.tables.map(a=>a.name));return t.schema.tables.flatMap(a=>a.relations.filter(e=>!n.has(e.table)).map(e=>r(s,{cacheKey:`relation_references_unknown_table:${a.name}:${e.name}`,detail:`Relation "${e.name}" on table "${a.name}" targets table "${e.table}", which does not exist in the schema.`,metadata:{relation:e.name,table:a.name,target:e.table}})))},source:"static",title:"Relation references unknown table"};export{s as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{callSiteCallers as d}from"./callSiteCallers-DArHfQSU.mjs";import{e as f}from"./finding-NrKO8idM.mjs";import{r as u,f as h,g}from"./helpers-DdZ_pyR3.mjs";const m=1e3,p=/^(?:continue|next|page)?_?cursor$/iu,w={categories:["PERFORMANCE"],description:"A function re-arms itself with `ctx.scheduler.runAfter` on a delay under one second, and the re-arming passes no cursor and nothing in the function removes the rows it reads. Each run finds the same rows and does the same work again.",facing:"EXTERNAL",level:"WARN",name:"self_reschedule_without_progress",remediation:"Carry the progress forward: pass a cursor (or the last processed id) in the dispatch args, or delete or update the rows the function handled so the next run selects only what is left. Re-arm only while work remains, and use a longer delay or a `cron` for periodic work.",run:o=>{if(o.callEdges===void 0)return[];const{callEdges:r}=o,c=u(o.tableWrites,o.rowEffects),i=[];for(const e of r){if(e.kind!=="schedule"||e.conditional!==!0||e.target===void 0||e.scope.kind!=="export")continue;const{name:t}=e.scope;if(e.target!==h(e.file,t))continue;const n=e.schedule?.delayMs,a=e.schedule?.argKeys;if(n===void 0||n>=m||a===void 0||a.some(s=>p.test(s))||r.some(s=>s.kind==="call"&&s.file===e.file&&s.scope.kind==="export"&&s.scope.name===t))continue;const l=(o.queries??[]).filter(s=>s.file===e.file&&s.table!==""&&d(s.scope).includes(t));l.length===0||l.some(s=>g(c,s,void 0,[t]))||i.push(f(w,{cacheKey:`self_reschedule_without_progress:${e.file}:${e.line.toString()}:${t}`,detail:`\`${t}\` (${e.file}:${e.line.toString()}) re-arms itself after ${n.toString()} ms with no cursor in its args, and it removes none of the rows it reads. The next run finds the same rows and starts the same work again.`,metadata:{delayMs:n,file:e.file,function:t,line:e.line}}))}return i},source:"static",title:"Self-reschedule without progress"};export{w as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{e as d}from"./finding-NrKO8idM.mjs";import{P as c,l as m,s as u}from"./fk-index-DXYS17cc.mjs";const g={categories:["PERFORMANCE"],description:"A `many` relation's foreign-key column on the target table has no index leading with it. Relation predicates (`some`/`none`/`every`) and `with:` child reads filter the target on that column, full-scanning it as rows accumulate.",facing:"EXTERNAL",level:"INFO",name:"unindexed_relation_target",remediation:'Add a secondary index on the target table leading with the FK column, e.g. `.index("byAuthorId", ["authorId"])`.',run:i=>{const o=[],l=new Map(i.schema.tables.map(t=>[t.name,t]));for(const t of i.schema.tables)for(const e of t.relations){if(e.kind!=="many")continue;const a=l.get(e.table);if(a===void 0)continue;const n=e.field;if(n===c||a.relations.some(s=>s.kind==="one"&&s.field===n)||m(a).has(n))continue;const r=u(n);o.push(d(g,{cacheKey:`unindexed_relation_target:${e.table}:${n}`,detail:`Relation "${e.name}" on table "${t.name}" is a to-many over "${e.table}" via its column "${n}", which is not the leading column of any index on "${e.table}". Relation predicates (\`some\`/\`none\`/\`every\`) and \`with:\` reads of "${e.name}" full-scan "${e.table}".`,metadata:{fkColumn:n,references:{column:e.references,table:t.name},relation:e.name,suggestedIndex:{fields:[n],name:r},table:e.table}}))}return o},source:"static",title:"Unindexed relation target"};export{g as default};
|