@lunora/advisor 1.0.0-alpha.2 → 1.0.0-alpha.200

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.
Files changed (159) hide show
  1. package/LICENSE.md +6 -0
  2. package/README.md +64 -7
  3. package/__assets__/package-og.svg +1 -1
  4. package/dist/index.d.mts +4416 -994
  5. package/dist/index.d.ts +4416 -994
  6. package/dist/index.mjs +1 -79
  7. package/dist/packem_shared/MAP_VERSION-MQ5W2B6n.mjs +1 -0
  8. package/dist/packem_shared/actionFetchSsrf-B4ZaOBlW.mjs +1 -0
  9. package/dist/packem_shared/actionWithoutErrorHandling-4GCBT0_z.mjs +1 -0
  10. package/dist/packem_shared/adminRouteWithoutGuard-DPE7LuNh.mjs +1 -0
  11. package/dist/packem_shared/aiRawRunEscapeHatch-B5980eT9.mjs +1 -0
  12. package/dist/packem_shared/aiRunWithoutLogging-CvHHtEN9.mjs +1 -0
  13. package/dist/packem_shared/aiToolSideEffectPromptInjection-_3MrX9Ul.mjs +1 -0
  14. package/dist/packem_shared/aiUnboundedGenerationPublic-C17h6sMV.mjs +1 -0
  15. package/dist/packem_shared/allowUnauthenticatedShardAccessEnabled-BQufF5Kb.mjs +1 -0
  16. package/dist/packem_shared/argument-derived-sink-DLXFC79t.mjs +1 -0
  17. package/dist/packem_shared/authApiCallWithoutHeaders-D8U1hv8s.mjs +1 -0
  18. package/dist/packem_shared/authCsrfCheckDisabled-C6bxME5y.mjs +1 -0
  19. package/dist/packem_shared/authEmailVerificationDisabled-yROPDgn-.mjs +1 -0
  20. package/dist/packem_shared/authScimWithoutTransactions-w9qb3iBe.mjs +1 -0
  21. package/dist/packem_shared/authSecureCookiesDisabled-qrCktlch.mjs +1 -0
  22. package/dist/packem_shared/authSessionFreshageZero-BMisjIIS.mjs +1 -0
  23. package/dist/packem_shared/authTrustedOriginsWildcard-DFDpfN84.mjs +1 -0
  24. package/dist/packem_shared/browserAllowPrivateTargets-Cj5sizhv.mjs +1 -0
  25. package/dist/packem_shared/browserUserUrlWithoutAllowlist-CcK27rvt.mjs +1 -0
  26. package/dist/packem_shared/callSiteCallers-DArHfQSU.mjs +1 -0
  27. package/dist/packem_shared/circularFk-DtcWFJxK.mjs +1 -0
  28. package/dist/packem_shared/classifySensitivity-JnjaTGYi.mjs +1 -0
  29. package/dist/packem_shared/commitOrderedHardDelete-BPdwOKA7.mjs +1 -0
  30. package/dist/packem_shared/compareToBaseline-CUG59IOy.mjs +1 -0
  31. package/dist/packem_shared/containerInstanceKeyFromUserInput-DBdjUnQb.mjs +1 -0
  32. package/dist/packem_shared/containerOversizedInstance-Bx89uR7E.mjs +1 -0
  33. package/dist/packem_shared/containerPublicInternet-BFfZf_P4.mjs +1 -0
  34. package/dist/packem_shared/containerRuntimeEgressRelaxation-CioCA2zY.mjs +1 -0
  35. package/dist/packem_shared/containerStartEnableInternetOverride-C6dbznku.mjs +1 -0
  36. package/dist/packem_shared/crossModuleTableWrite-DjiVTAwK.mjs +1 -0
  37. package/dist/packem_shared/dedupeCacheKeys-DtBOHffV.mjs +1 -0
  38. package/dist/packem_shared/duplicateIndex-Cip6-Rpu.mjs +1 -0
  39. package/dist/packem_shared/emptyIndex-BnHDcXza.mjs +1 -0
  40. package/dist/packem_shared/errorWithoutCatalog-BTfvaXHR.mjs +1 -0
  41. package/dist/packem_shared/exportSinkMisconfigured-JfbAx9AI.mjs +1 -0
  42. package/dist/packem_shared/externalSourceIncrementalNoDeletePath-BCzm3HzF.mjs +1 -0
  43. package/dist/packem_shared/externalSourceOnGlobal-CH7xbJ49.mjs +1 -0
  44. package/dist/packem_shared/externalSourceUnscoped-BxU2uSXk.mjs +1 -0
  45. package/dist/packem_shared/fanOutBreadth-CBtmZnoh.mjs +1 -0
  46. package/dist/packem_shared/filterOnPrimaryKey-DbFriOZA.mjs +1 -0
  47. package/dist/packem_shared/filterWithoutIndex-Dc7Nn0y2.mjs +1 -0
  48. package/dist/packem_shared/finding-NrKO8idM.mjs +1 -0
  49. package/dist/packem_shared/fk-index-DXYS17cc.mjs +1 -0
  50. package/dist/packem_shared/flagGatesSecurityWithUnsafeDefault-DB9M6hLM.mjs +1 -0
  51. package/dist/packem_shared/flagReadInSubscription-DabIhYGD.mjs +1 -0
  52. package/dist/packem_shared/fromServerSchema-fAymN8o2.mjs +1 -0
  53. package/dist/packem_shared/geoIndexFieldNotGeopoint-CDMNbAfp.mjs +1 -0
  54. package/dist/packem_shared/geoIndexUnused-D7C9Qr4U.mjs +1 -0
  55. package/dist/packem_shared/globalTableNearColumnLimit-BFbBBd6A.mjs +1 -0
  56. package/dist/packem_shared/gradeFromScore-KSt58rj1.mjs +1 -0
  57. package/dist/packem_shared/hardcodedSecret-Bw_4FYrs.mjs +1 -0
  58. package/dist/packem_shared/helpers-BmppB5nL.mjs +1 -0
  59. package/dist/packem_shared/hotShard-BwGYZ3Tq.mjs +1 -0
  60. package/dist/packem_shared/httpActionMissingAuthGuard-BhvcmkwW.mjs +1 -0
  61. package/dist/packem_shared/httpActionResponseHeaderInjection-DKz2SlCa.mjs +1 -0
  62. package/dist/packem_shared/hyperdriveOutsideAction-CPDdGP2g.mjs +1 -0
  63. package/dist/packem_shared/identityUndeclaredClaimTrusted-CAUagXkJ.mjs +1 -0
  64. package/dist/packem_shared/imagesUrlSourceFromUserInput-C2CJTTCI.mjs +1 -0
  65. package/dist/packem_shared/indexReferencesUnknownField-Dm8SL4Tp.mjs +1 -0
  66. package/dist/packem_shared/indexUtilization-CkVPZcVe.mjs +1 -0
  67. package/dist/packem_shared/insertManyUnsafeUserData-DWE8d_DF.mjs +1 -0
  68. package/dist/packem_shared/kvUnscopedUserKeyIdor-CXFKAyM2.mjs +1 -0
  69. package/dist/packem_shared/mailInboundDispatchWithoutVerify-CWwqXyPX.mjs +1 -0
  70. package/dist/packem_shared/mailRecipientFromRequestInput-CQZeOsP9.mjs +1 -0
  71. package/dist/packem_shared/maskUncoveredPiiColumn-axEka4nd.mjs +1 -0
  72. package/dist/packem_shared/maskWeakHashStrategyOnPii-njstapQY.mjs +1 -0
  73. package/dist/packem_shared/maskedRelationLeakViaWith-CIFij6vE.mjs +1 -0
  74. package/dist/packem_shared/mutatorFullRowReplace-BzRpZ47r.mjs +1 -0
  75. package/dist/packem_shared/mutatorWithoutOwnerScope-BoS81ns0.mjs +1 -0
  76. package/dist/packem_shared/nondeterministicQueryMutation-sxmixc0l.mjs +1 -0
  77. package/dist/packem_shared/normalizeIdUsedAsAuthorization-BXN-Can6.mjs +1 -0
  78. package/dist/packem_shared/notifyMissingPushConfig-DeQMoNwm.mjs +1 -0
  79. package/dist/packem_shared/notifySendOutsideAction-BJrDkr4h.mjs +1 -0
  80. package/dist/packem_shared/outputProjectionMissingOnPublicRead-DHivs_73.mjs +1 -0
  81. package/dist/packem_shared/ownerFieldFromArgsNotAuth-BFNc023N.mjs +1 -0
  82. package/dist/packem_shared/paymentCreateWithoutAuthorize-CICGmRFR.mjs +1 -0
  83. package/dist/packem_shared/paymentWebhookWideTolerance-CtZb2AVA.mjs +1 -0
  84. package/dist/packem_shared/plaintextSecretInWranglerVariables-bNkqmNqV.mjs +1 -0
  85. package/dist/packem_shared/policyReferencesUnknownTable-CIKuRZ5Y.mjs +1 -0
  86. package/dist/packem_shared/privilegedDispatchUnvalidatedPayload-DhXa36mM.mjs +1 -0
  87. package/dist/packem_shared/privilegedFanoutFromPublicProcedure-DV188gHQ.mjs +1 -0
  88. package/dist/packem_shared/procedure-protections-DRQotu9I.mjs +1 -0
  89. package/dist/packem_shared/procedureWithoutStructuredEvent-CZ_B23nM.mjs +1 -0
  90. package/dist/packem_shared/publicArgumentUsesAny-BDEvMA8Q.mjs +1 -0
  91. package/dist/packem_shared/publicMutationWithoutRatelimit-BeYapu1F.mjs +1 -0
  92. package/dist/packem_shared/publicTableRlsOptoutConfusion-BFmh1kH0.mjs +1 -0
  93. package/dist/packem_shared/queueWithoutDlq-BvHz3Opg.mjs +1 -0
  94. package/dist/packem_shared/r2sqlOutsideAction-BdLiLOAX.mjs +1 -0
  95. package/dist/packem_shared/ratelimitDefaultMemoryStore-Dj0Kk7t9.mjs +1 -0
  96. package/dist/packem_shared/ratelimitKeySpoofableOrGlobal-DlvJfSXs.mjs +1 -0
  97. package/dist/packem_shared/ratelimitMiddlewareFailOpen-DGtyt-pc.mjs +1 -0
  98. package/dist/packem_shared/relationReferencesUnknownField-CqGEJD57.mjs +1 -0
  99. package/dist/packem_shared/relationReferencesUnknownTable-CP4aWtAJ.mjs +1 -0
  100. package/dist/packem_shared/rlsUncoveredTable-CZ4ie4gX.mjs +1 -0
  101. package/dist/packem_shared/shapeTargetsGlobalTable-Bu3eEDic.mjs +1 -0
  102. package/dist/packem_shared/shapeUnknownTable-CREfNnWi.mjs +1 -0
  103. package/dist/packem_shared/signupMutationWithoutDisposableGating-CFtFMJz8.mjs +1 -0
  104. package/dist/packem_shared/softDeleteIncludeDeletedFromArgs-C7Ugg4zb.mjs +1 -0
  105. package/dist/packem_shared/sqlInjectionRisk-y-7c46LJ.mjs +1 -0
  106. package/dist/packem_shared/storageGenerateUploadUrlNoContentTypePin-Bx9oGOjM.mjs +1 -0
  107. package/dist/packem_shared/storageKeyFromUserArgs-BryKJJM-.mjs +1 -0
  108. package/dist/packem_shared/storagePresignedUrlForPrivateContent-lqE6LvNu.mjs +1 -0
  109. package/dist/packem_shared/storageUploadWithoutContentTypeAllowlist-DMr7w29r.mjs +1 -0
  110. package/dist/packem_shared/storageUploadWithoutMaxSize-rSNFNb2l.mjs +1 -0
  111. package/dist/packem_shared/tableWithoutInsert-ClJp_jDr.mjs +1 -0
  112. package/dist/packem_shared/topicTooManySubscriptions-CiZhFm9r.mjs +1 -0
  113. package/dist/packem_shared/ttlFieldNotTimestamp-Dxa-6RfM.mjs +1 -0
  114. package/dist/packem_shared/unboundedStringArgument-jZnCbwB0.mjs +1 -0
  115. package/dist/packem_shared/unindexedForeignKey-Dypgn8uH.mjs +1 -0
  116. package/dist/packem_shared/unindexedRelationTarget-CSqRJWYZ.mjs +1 -0
  117. package/dist/packem_shared/unrestrictedWhereBranch-CcIBmHik.mjs +1 -0
  118. package/dist/packem_shared/userCreatingMutationWithoutCaptcha-C8yaqIIj.mjs +1 -0
  119. package/dist/packem_shared/vectorsNamespaceFromUserInput-7I_Kwxvp.mjs +1 -0
  120. package/dist/packem_shared/workflowDuplicateStepName-B6eAJzwA.mjs +1 -0
  121. package/dist/packem_shared/workflowUnknownTarget-Cm1hfTTs.mjs +1 -0
  122. package/dist/packem_shared/workflowUnused-DHwwE-1P.mjs +1 -0
  123. package/package.json +4 -3
  124. package/dist/packem_shared/AE_METRIC_EVENTS-DexctYv6.mjs +0 -85
  125. package/dist/packem_shared/adminRouteWithoutGuard-UUGBkAjU.mjs +0 -33
  126. package/dist/packem_shared/authApiCallWithoutHeaders-BeJhCZaf.mjs +0 -38
  127. package/dist/packem_shared/circularFk-B2freHrP.mjs +0 -84
  128. package/dist/packem_shared/constraintValidator-Dr9Py3FD.mjs +0 -186
  129. package/dist/packem_shared/containerOversizedInstance-5U1VKPRM.mjs +0 -36
  130. package/dist/packem_shared/containerPublicInternet-CuNerJE5.mjs +0 -30
  131. package/dist/packem_shared/duplicateIndex-BOublMSt.mjs +0 -57
  132. package/dist/packem_shared/emptyIndex-BX8EuEY7.mjs +0 -32
  133. package/dist/packem_shared/filterWithoutIndex-BYVeJaSs.mjs +0 -31
  134. package/dist/packem_shared/finding-Dm_zvzS1.mjs +0 -16
  135. package/dist/packem_shared/fk-index-IUK1ukgs.mjs +0 -7
  136. package/dist/packem_shared/fromServerSchema-DinF1nph.mjs +0 -50
  137. package/dist/packem_shared/hardcodedSecret-W2pz1UZB.mjs +0 -35
  138. package/dist/packem_shared/helpers-DNCkMWZQ.mjs +0 -4
  139. package/dist/packem_shared/hotShard-Ir5D0B6J.mjs +0 -48
  140. package/dist/packem_shared/hyperdriveOutsideAction-BgZqX7Xg.mjs +0 -30
  141. package/dist/packem_shared/indexReferencesUnknownField-DH0_dbUY.mjs +0 -36
  142. package/dist/packem_shared/indexUtilization-B5DMQ3bI.mjs +0 -45
  143. package/dist/packem_shared/maskUncoveredPiiColumn-DjGIPG6M.mjs +0 -61
  144. package/dist/packem_shared/nondeterministicQueryMutation-GXES1fLp.mjs +0 -35
  145. package/dist/packem_shared/policyReferencesUnknownTable-DtaIEovd.mjs +0 -38
  146. package/dist/packem_shared/publicArgumentUsesAny-C71b2NCf.mjs +0 -32
  147. package/dist/packem_shared/publicMutationWithoutRatelimit-xBpJ6GWK.mjs +0 -36
  148. package/dist/packem_shared/r2sqlOutsideAction-CtqxvMuV.mjs +0 -30
  149. package/dist/packem_shared/relationReferencesUnknownField-YznyXt_7.mjs +0 -54
  150. package/dist/packem_shared/relationReferencesUnknownTable-DrorpKYe.mjs +0 -33
  151. package/dist/packem_shared/rlsUncoveredTable-CxEfZ5eZ.mjs +0 -56
  152. package/dist/packem_shared/sqlInjectionRisk-zwytYGLt.mjs +0 -26
  153. package/dist/packem_shared/tableWithoutInsert-CbbaYIP4.mjs +0 -34
  154. package/dist/packem_shared/unboundedStringArgument-DThg2-wt.mjs +0 -32
  155. package/dist/packem_shared/unindexedForeignKey-BgJbKyqK.mjs +0 -45
  156. package/dist/packem_shared/unindexedRelationTarget-D6eyj6Xx.mjs +0 -53
  157. package/dist/packem_shared/userCreatingMutationWithoutCaptcha-CH31YsUZ.mjs +0 -42
  158. package/dist/packem_shared/workflowUnknownTarget-Cdd7WhKQ.mjs +0 -34
  159. package/dist/packem_shared/workflowUnused-D0jHxdz9.mjs +0 -38
package/LICENSE.md CHANGED
@@ -103,3 +103,9 @@ Unless required by applicable law or agreed to in writing, software distributed
103
103
  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
104
104
  CONDITIONS OF ANY KIND, either express or implied. See the License for the
105
105
  specific language governing permissions and limitations under the License.
106
+
107
+ <!-- DEPENDENCIES -->
108
+ <!-- /DEPENDENCIES -->
109
+
110
+ <!-- TYPE_DEPENDENCIES -->
111
+ <!-- /TYPE_DEPENDENCIES -->
package/README.md CHANGED
@@ -36,7 +36,7 @@
36
36
 
37
37
  Schema and query lints for Lunora, modeled on Supabase's splinter. Each lint is a pure rule over a normalized `LintContext`; `runAdvisor()` runs a set and flattens their findings for the CLI, the Vite plugin, and the Studio Advisors view.
38
38
 
39
- Most lints are `static`: they run against the declared schema (and the query reads / inserts the codegen feeder discovers in your function bodies), so a problem surfaces at codegen time before it ships — the edge over a live-database-only advisor. A smaller `runtime` tier (`hot_shard`, `index_utilization`, `constraint_validator`) reads observed signal from a running deployment.
39
+ Most lints are `static`: they run against the declared schema (and the query reads / inserts the codegen feeder discovers in your function bodies), so a problem surfaces at codegen time before it ships — the edge over a live-database-only advisor. A smaller `runtime` tier (`hot_shard`, `index_utilization`, `fan_out_breadth`) reads observed signal from a running deployment.
40
40
 
41
41
  Part of the [Lunora](https://github.com/anolilab/lunora) framework — a type-safe, real-time backend on Cloudflare Workers + Durable Objects with a Vite-first DX.
42
42
 
@@ -79,21 +79,78 @@ for (const finding of findings) {
79
79
 
80
80
  ### Runtime lints
81
81
 
82
- The runtime tier (`hot_shard`, `index_utilization`, `constraint_validator`) reads observed signal off the `LintContext` (`shardTraffic`, `tableScans`, `indexHits`, `tableSamples`). The Studio backend fills those from each shard's durable counters. As an alternative feeder, `loadAnalyticsRuntimeMetrics` reconstructs the same arrays from the Analytics Engine SQL API:
82
+ The runtime tier (`hot_shard`, `index_utilization`, `fan_out_breadth`) reads observed signal off the `LintContext` (`shardTraffic`, `tableScans`, `indexHits`). The Studio backend fills all three from the shards' admin signal.
83
83
 
84
84
  ```ts
85
- import { fromServerSchema, loadAnalyticsRuntimeMetrics, runAdvisor } from "@lunora/advisor";
85
+ import { fromServerSchema, runAdvisor } from "@lunora/advisor";
86
86
 
87
87
  import schema from "./lunora/schema";
88
88
 
89
- // `client` is an `@lunora/analytics` SQL client (anything with `query(sql)`).
90
- const metrics = await loadAnalyticsRuntimeMetrics(client, { dataset: "ANALYTICS" });
91
- const findings = runAdvisor({ schema: fromServerSchema(schema), ...metrics }, { source: "runtime" });
89
+ // `shardTraffic` / `tableScans` / `indexHits` come from wherever you read your
90
+ // shards' durable counters — the Studio backend reads its own.
91
+ const findings = runAdvisor({ schema: fromServerSchema(schema), shardTraffic, tableScans, indexHits }, { source: "runtime" });
92
92
  ```
93
93
 
94
+ An Analytics-Engine-backed alternative feeder (querying `ctx.analyticsSql` for `lunora.index.hit`/`lunora.shard.request`/`lunora.table.scan` events instead of the in-DO counters) was quarantined off the package root: nothing in the runtime ever writes those AE events, so `shardTraffic` and `tableScans` always came back empty. `indexHits` came back empty too, unless the caller passed `declaredIndexes` — then it listed every declared index with `reads: 0`, a real zero-reads fact rather than a stand-in for "no data". The `AnalyticsMetricsOptions` / `AnalyticsMetricsSource` / `AnalyticsRuntimeMetrics` types it would have produced are still exported from `@lunora/advisor` — they describe the still-valid, still-optional shape of `runAdvisor`'s runtime input — but the loader function itself is not, until something actually emits those events.
95
+
94
96
  A missing metric degrades to an empty array rather than throwing, so a partially configured read path still returns what it can.
95
97
 
96
- > This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs/addons/studio)**.
98
+ ### Observability map (score, coverage, baseline)
99
+
100
+ `runAdvisor` answers "what is wrong?". `scoreAdvisor` answers "how are we doing, and did it get worse?" — a scored coverage map over your procedures. It is a pure function over findings you already have, so it never re-runs a lint:
101
+
102
+ ```ts
103
+ import { fromServerSchema, runAdvisor, scoreAdvisor } from "@lunora/advisor";
104
+
105
+ import schema from "./lunora/schema";
106
+
107
+ const context = { schema: fromServerSchema(schema) };
108
+ const findings = runAdvisor(context, { source: "static" });
109
+ const map = scoreAdvisor(context.procedureProtections ?? [], findings);
110
+
111
+ console.log(map.score, map.grade); // e.g. 84 "good"
112
+ console.log(map.summary); // { clean: 9, exempt: 0, failing: 1, procedures: 12, rulesFired: 6, warned: 2 }
113
+ ```
114
+
115
+ Each procedure starts at 100 and loses each fired **rule's** severity weight (`ERROR` 20 / `WARN` 10 / `INFO` 5 — severity is the only input; a lint carries no weight of its own) — charged once however many times that rule fires — then rolls up into a weighted global mean: public handlers count double, internal ones and queries half. Findings that name no procedure (schema shape, wrangler config) land in a project bucket weighted against the procedure population, so schema debt genuinely moves the grade.
116
+
117
+ The verdicts are `clean` / `warned` / `failing` / `exempt` — named for severity, because the score is driven by every lint family rather than an observability family.
118
+
119
+ Commit the map and gate CI on it:
120
+
121
+ ```ts
122
+ import { compareToBaseline, parseAdvisorMap } from "@lunora/advisor";
123
+
124
+ const baseline = parseAdvisorMap(JSON.parse(await readFile("lunora.advisor.map.json", "utf8")));
125
+
126
+ if (baseline === undefined) {
127
+ // Missing or unreadable (hand-edited, truncated). Fail loudly — treating it
128
+ // as "no regression" would silently disable the gate forever. A version
129
+ // mismatch is not rejected here: `compareToBaseline` reports it as
130
+ // `comparable: false`.
131
+ throw new Error("advisor baseline is unreadable; regenerate lunora.advisor.map.json");
132
+ }
133
+
134
+ const diff = compareToBaseline(map, baseline);
135
+
136
+ // `comparable` must be narrowed before `regressed` is reachable, so a stale
137
+ // baseline cannot read as a clean run.
138
+ if (!diff.comparable) {
139
+ throw new Error(`advisor baseline not comparable: ${diff.reason}`);
140
+ }
141
+
142
+ // Five independent signals: the global score fell, an existing procedure got
143
+ // worse, one started failing, one's findings grew without its score moving, or
144
+ // the project bucket gained findings. The growth signals matter because a rule is
145
+ // charged once however many times it fires, and the project score saturates at 0.
146
+ if (diff.regressed) {
147
+ process.exitCode = 1;
148
+ }
149
+ ```
150
+
151
+ `@lunora/codegen` exposes `toAdvisorContext()` to build the context straight from the feeder, and `lunora advisor` wraps all of this as a command. Full reference for the scoring, verdicts, and the baseline gate is in the [package docs](https://lunora.sh/docs/packages/advisor).
152
+
153
+ > This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs/packages/advisor)**.
97
154
 
98
155
  ## Related
99
156