@lunora/advisor 1.0.0-alpha.213 → 1.0.0-alpha.215

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 CHANGED
@@ -418,6 +418,11 @@ interface AdvisorContainer {
418
418
  interface AdvisorCountRead {
419
419
  /** Source file the call appears in (relative to the lunora dir, no extension). */
420
420
  file: string;
421
+ /**
422
+ * The fields the count's `where` tests, when it is a plain object literal of field names.
423
+ * Absent for a bare count and for a `where` the feeder cannot read.
424
+ */
425
+ filterFields?: string[];
421
426
  /**
422
427
  * `false` only when the table is known and declares no aggregate index, so the count is a
423
428
  * `COUNT(*)` scan. A counter-backed or unknown table reads `true`.
@@ -3211,15 +3216,21 @@ declare const containerPublicInternet: Lint;
3211
3216
  declare const containerRuntimeEgressRelaxation: Lint;
3212
3217
  declare const containerStartEnableInternetOverride: Lint;
3213
3218
  /**
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.
3219
+ * Flags a `count()` that runs `SELECT COUNT(*)` over a table with no aggregate index, and
3220
+ * whose `where` no ordinary index leads with. `count()` reads a counter only when an aggregate
3221
+ * index covers it. A bare count, or one whose `where` tests only fields no index leads with,
3222
+ * reads every row on each call. A count whose `where` leads with an indexed field, or tests
3223
+ * `_id`, runs a range scan, so it is not flagged. A count that runs per request or per poll
3224
+ * repeats the scan every time, and its cost grows with the table.
3218
3225
  *
3219
- * The verdict is per table. A table that declares an aggregate index is not flagged, even
3226
+ * The verdict is per call. A table that declares an aggregate index is not flagged, even
3220
3227
  * 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.
3228
+ * index's coverage. That is a known false negative. A `where` the feeder cannot read (a
3229
+ * variable, a spread, an options object) is treated as unfiltered, which is also a known
3230
+ * false positive when an index does cover it. The feeder records field names, not operators,
3231
+ * so a non-equality test on an indexed field (`ne`, `contains`) is cleared too, which is a
3232
+ * known false negative. The count reads come from the codegen feeder; runtime callers supply
3233
+ * none, so this lint is a no-op there.
3223
3234
  */
3224
3235
  declare const countWithoutIndex: Lint;
3225
3236
  /**
package/dist/index.d.ts CHANGED
@@ -418,6 +418,11 @@ interface AdvisorContainer {
418
418
  interface AdvisorCountRead {
419
419
  /** Source file the call appears in (relative to the lunora dir, no extension). */
420
420
  file: string;
421
+ /**
422
+ * The fields the count's `where` tests, when it is a plain object literal of field names.
423
+ * Absent for a bare count and for a `where` the feeder cannot read.
424
+ */
425
+ filterFields?: string[];
421
426
  /**
422
427
  * `false` only when the table is known and declares no aggregate index, so the count is a
423
428
  * `COUNT(*)` scan. A counter-backed or unknown table reads `true`.
@@ -3211,15 +3216,21 @@ declare const containerPublicInternet: Lint;
3211
3216
  declare const containerRuntimeEgressRelaxation: Lint;
3212
3217
  declare const containerStartEnableInternetOverride: Lint;
3213
3218
  /**
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.
3219
+ * Flags a `count()` that runs `SELECT COUNT(*)` over a table with no aggregate index, and
3220
+ * whose `where` no ordinary index leads with. `count()` reads a counter only when an aggregate
3221
+ * index covers it. A bare count, or one whose `where` tests only fields no index leads with,
3222
+ * reads every row on each call. A count whose `where` leads with an indexed field, or tests
3223
+ * `_id`, runs a range scan, so it is not flagged. A count that runs per request or per poll
3224
+ * repeats the scan every time, and its cost grows with the table.
3218
3225
  *
3219
- * The verdict is per table. A table that declares an aggregate index is not flagged, even
3226
+ * The verdict is per call. A table that declares an aggregate index is not flagged, even
3220
3227
  * 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.
3228
+ * index's coverage. That is a known false negative. A `where` the feeder cannot read (a
3229
+ * variable, a spread, an options object) is treated as unfiltered, which is also a known
3230
+ * false positive when an index does cover it. The feeder records field names, not operators,
3231
+ * so a non-equality test on an indexed field (`ne`, `contains`) is cleared too, which is a
3232
+ * known false negative. The count reads come from the codegen feeder; runtime callers supply
3233
+ * none, so this lint is a no-op there.
3223
3234
  */
3224
3235
  declare const countWithoutIndex: Lint;
3225
3236
  /**
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 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};
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-DjQFNBrw.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 l}from"./finding-NrKO8idM.mjs";import{s as g,q as b,c as m}from"./helpers-DdZ_pyR3.mjs";import{l as f,P as w}from"./fk-index-DXYS17cc.mjs";const y=(a,n)=>{const o=n.filterFields??[],e=a.schema.tables.find(t=>t.name===n.table);if(e===void 0||o.length===0)return!1;const s=f(e);return o.some(t=>t===w||s.has(t))},d={categories:["PERFORMANCE"],description:"A `count()` runs on a table with no aggregate index, and no ordinary index covers its `where`, 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 n=[],o=g(a.schema);for(const e of a.countReads??[]){if(e.indexed||y(a,e))continue;const s=b(e),t=o.get(e.table),i={...m(e),shardKind:t??"unknown",table:e.table},r=`count_without_index:${e.file}:${e.line.toString()}:${e.table}`;if(t==="shardBy"){n.push(l(d,{cacheKey:r,detail:`count() on "${e.table}" at ${s} 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:i}));continue}const c=e.filterFields??[],h=c.length>0?`and no index leads with a field its where tests (${c.map(u=>`"${u}"`).join(", ")}), so it scans`:"so it scans";n.push(l(d,{cacheKey:r,detail:`count() on "${e.table}" at ${s} has no aggregate index, ${h} every row of "${e.table}" to count them.`,metadata:i}))}return n},source:"static",title:"Count without index"};export{d as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/advisor",
3
- "version": "1.0.0-alpha.213",
3
+ "version": "1.0.0-alpha.215",
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.197"
50
+ "@lunora/server": "1.0.0-alpha.199"
51
51
  },
52
52
  "engines": {
53
53
  "node": "^22.15.0 || >=24.11.0"
@@ -1 +0,0 @@
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};