@lunora/advisor 1.0.0-alpha.7 → 1.0.0-alpha.70

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 (152) hide show
  1. package/LICENSE.md +6 -0
  2. package/README.md +60 -5
  3. package/dist/index.d.mts +4005 -966
  4. package/dist/index.d.ts +4005 -966
  5. package/dist/index.mjs +1 -79
  6. package/dist/packem_shared/MAP_VERSION-DxkFCiLX.mjs +1 -0
  7. package/dist/packem_shared/actionFetchSsrf-BbWxy1Yf.mjs +1 -0
  8. package/dist/packem_shared/actionWithoutErrorHandling-rhzjCEqm.mjs +1 -0
  9. package/dist/packem_shared/adminRouteWithoutGuard-BbscPp1m.mjs +1 -0
  10. package/dist/packem_shared/aiRawRunEscapeHatch-DfhjYm43.mjs +1 -0
  11. package/dist/packem_shared/aiRunWithoutLogging-B0K93Pga.mjs +1 -0
  12. package/dist/packem_shared/aiToolSideEffectPromptInjection-DnWQOoKi.mjs +1 -0
  13. package/dist/packem_shared/aiUnboundedGenerationPublic-iu7crnSa.mjs +1 -0
  14. package/dist/packem_shared/allowUnauthenticatedShardAccessEnabled-D4aMNBb0.mjs +1 -0
  15. package/dist/packem_shared/argument-derived-sink-BpO3NPPM.mjs +1 -0
  16. package/dist/packem_shared/authApiCallWithoutHeaders-BHkik-EF.mjs +1 -0
  17. package/dist/packem_shared/authCsrfCheckDisabled-De9ammtV.mjs +1 -0
  18. package/dist/packem_shared/authEmailVerificationDisabled-BFKdJcx4.mjs +1 -0
  19. package/dist/packem_shared/authScimWithoutTransactions-DCtLXzks.mjs +1 -0
  20. package/dist/packem_shared/authSecureCookiesDisabled-B4WIzvMm.mjs +1 -0
  21. package/dist/packem_shared/authSessionFreshageZero-Bpcaf_Pe.mjs +1 -0
  22. package/dist/packem_shared/authTrustedOriginsWildcard-DjU9NXEm.mjs +1 -0
  23. package/dist/packem_shared/browserAllowPrivateTargets-Dxyeh6lg.mjs +1 -0
  24. package/dist/packem_shared/browserUserUrlWithoutAllowlist-BHBS6b0N.mjs +1 -0
  25. package/dist/packem_shared/circularFk-ZNjkhLaE.mjs +1 -0
  26. package/dist/packem_shared/classifySensitivity-B1kkm44P.mjs +1 -0
  27. package/dist/packem_shared/compareToBaseline-BwXFusQG.mjs +1 -0
  28. package/dist/packem_shared/constraintValidator-N339mfyx.mjs +1 -0
  29. package/dist/packem_shared/containerInstanceKeyFromUserInput-C8S4T3ty.mjs +1 -0
  30. package/dist/packem_shared/containerOversizedInstance-B9wW_sU5.mjs +1 -0
  31. package/dist/packem_shared/containerPublicInternet-g0TRsayn.mjs +1 -0
  32. package/dist/packem_shared/containerRuntimeEgressRelaxation-BQ2ZD1aV.mjs +1 -0
  33. package/dist/packem_shared/containerStartEnableInternetOverride-B62WUura.mjs +1 -0
  34. package/dist/packem_shared/dedupeCacheKeys-7JgxaQWV.mjs +1 -0
  35. package/dist/packem_shared/duplicateIndex-DFT7qLQD.mjs +1 -0
  36. package/dist/packem_shared/emptyIndex-Bp-Y81D0.mjs +1 -0
  37. package/dist/packem_shared/errorRateOutlier-CgK42sq0.mjs +1 -0
  38. package/dist/packem_shared/errorWithoutCatalog-0PbxiZL4.mjs +1 -0
  39. package/dist/packem_shared/exportSinkMisconfigured-DvpAaa6j.mjs +1 -0
  40. package/dist/packem_shared/externalSourceIncrementalNoDeletePath-DnoL64YY.mjs +1 -0
  41. package/dist/packem_shared/externalSourceOnGlobal-IYnALPlO.mjs +1 -0
  42. package/dist/packem_shared/externalSourceUnscoped-DqBp5jYr.mjs +1 -0
  43. package/dist/packem_shared/filterOnPrimaryKey-Db947tQY.mjs +1 -0
  44. package/dist/packem_shared/filterWithoutIndex-pIpqk-O8.mjs +1 -0
  45. package/dist/packem_shared/finding-BAVgqnCF.mjs +1 -0
  46. package/dist/packem_shared/fk-index-Bmk19RvQ.mjs +1 -0
  47. package/dist/packem_shared/flagGatesSecurityWithUnsafeDefault-DPGFEHTt.mjs +1 -0
  48. package/dist/packem_shared/fromServerSchema-9aaK1J7g.mjs +1 -0
  49. package/dist/packem_shared/geoIndexFieldNotGeopoint-DAGLZtaH.mjs +1 -0
  50. package/dist/packem_shared/geoIndexUnused-B5Oww9o2.mjs +1 -0
  51. package/dist/packem_shared/gradeFromScore-DW82YDVY.mjs +1 -0
  52. package/dist/packem_shared/hardcodedSecret-yu14ITlW.mjs +1 -0
  53. package/dist/packem_shared/helpers-DwlvEUCh.mjs +1 -0
  54. package/dist/packem_shared/hotShard-io-eF2Sx.mjs +1 -0
  55. package/dist/packem_shared/httpActionMissingAuthGuard-BZDdeW_D.mjs +1 -0
  56. package/dist/packem_shared/httpActionResponseHeaderInjection-D063rB7U.mjs +1 -0
  57. package/dist/packem_shared/hyperdriveOutsideAction-B8ffQwgl.mjs +1 -0
  58. package/dist/packem_shared/identityUndeclaredClaimTrusted-BYRYGQ74.mjs +1 -0
  59. package/dist/packem_shared/imagesUrlSourceFromUserInput-Cy5pc_dA.mjs +1 -0
  60. package/dist/packem_shared/indexReferencesUnknownField-BPJjE0zg.mjs +1 -0
  61. package/dist/packem_shared/indexUtilization-QfbIH_w4.mjs +1 -0
  62. package/dist/packem_shared/insertManyUnsafeUserData-xLYAkcKj.mjs +1 -0
  63. package/dist/packem_shared/kvUnscopedUserKeyIdor-B4lGxRxh.mjs +1 -0
  64. package/dist/packem_shared/mailInboundDispatchWithoutVerify-BivYJ5F9.mjs +1 -0
  65. package/dist/packem_shared/mailRecipientFromRequestInput-BR_oSh65.mjs +1 -0
  66. package/dist/packem_shared/maskUncoveredPiiColumn-DqPfZM2O.mjs +1 -0
  67. package/dist/packem_shared/maskWeakHashStrategyOnPii-DcEwc4Sz.mjs +1 -0
  68. package/dist/packem_shared/maskedRelationLeakViaWith-CxYk0MDW.mjs +1 -0
  69. package/dist/packem_shared/mutatorFullRowReplace-CEgecSGm.mjs +1 -0
  70. package/dist/packem_shared/nondeterministicQueryMutation-BZV_biUV.mjs +1 -0
  71. package/dist/packem_shared/normalizeIdUsedAsAuthorization-FjISSXJm.mjs +1 -0
  72. package/dist/packem_shared/notifyMissingPushConfig-C_GbVw3S.mjs +1 -0
  73. package/dist/packem_shared/notifySendOutsideAction-B9DcirhJ.mjs +1 -0
  74. package/dist/packem_shared/outputProjectionMissingOnPublicRead-9Pm9766s.mjs +1 -0
  75. package/dist/packem_shared/ownerFieldFromArgsNotAuth-D4K-_0aD.mjs +1 -0
  76. package/dist/packem_shared/paymentCreateWithoutAuthorize-CurtqN8K.mjs +1 -0
  77. package/dist/packem_shared/paymentWebhookWideTolerance-I7ingfR8.mjs +1 -0
  78. package/dist/packem_shared/plaintextSecretInWranglerVariables-CmbygN6_.mjs +1 -0
  79. package/dist/packem_shared/policyReferencesUnknownTable-D3WelAQa.mjs +1 -0
  80. package/dist/packem_shared/privilegedDispatchUnvalidatedPayload-74GuGye1.mjs +1 -0
  81. package/dist/packem_shared/privilegedFanoutFromPublicProcedure-sMsZN4uP.mjs +1 -0
  82. package/dist/packem_shared/procedure-protections-DRQotu9I.mjs +1 -0
  83. package/dist/packem_shared/procedureWithoutStructuredEvent-l40nx9tX.mjs +1 -0
  84. package/dist/packem_shared/publicArgumentUsesAny-BzdcoJ9C.mjs +1 -0
  85. package/dist/packem_shared/publicMutationWithoutRatelimit-CozomYXw.mjs +1 -0
  86. package/dist/packem_shared/publicTableRlsOptoutConfusion-CaWtPGAn.mjs +1 -0
  87. package/dist/packem_shared/queueWithoutDlq-BLY3iy9Q.mjs +1 -0
  88. package/dist/packem_shared/r2sqlOutsideAction-DCMAsAeM.mjs +1 -0
  89. package/dist/packem_shared/ratelimitDefaultMemoryStore-mOdI3Jmb.mjs +1 -0
  90. package/dist/packem_shared/ratelimitKeySpoofableOrGlobal-DaGId_Fh.mjs +1 -0
  91. package/dist/packem_shared/ratelimitMiddlewareFailOpen-CTWVosEc.mjs +1 -0
  92. package/dist/packem_shared/relationReferencesUnknownField-clqyB69Y.mjs +1 -0
  93. package/dist/packem_shared/relationReferencesUnknownTable-EOtKyaHx.mjs +1 -0
  94. package/dist/packem_shared/rlsUncoveredTable-DYHgnoos.mjs +1 -0
  95. package/dist/packem_shared/shapeTargetsGlobalTable-SAlq2N-T.mjs +1 -0
  96. package/dist/packem_shared/shapeUnknownTable-DhvsbEtT.mjs +1 -0
  97. package/dist/packem_shared/signupMutationWithoutDisposableGating-Tgv9SbsY.mjs +1 -0
  98. package/dist/packem_shared/softDeleteIncludeDeletedFromArgs-DgxpQHRA.mjs +1 -0
  99. package/dist/packem_shared/sqlInjectionRisk-D2NaT-5c.mjs +1 -0
  100. package/dist/packem_shared/storageGenerateUploadUrlNoContentTypePin-C9cjl6Jh.mjs +1 -0
  101. package/dist/packem_shared/storageKeyFromUserArgs-CI-6kyte.mjs +1 -0
  102. package/dist/packem_shared/storagePresignedUrlForPrivateContent-j7VhwIdu.mjs +1 -0
  103. package/dist/packem_shared/storageUploadWithoutContentTypeAllowlist-oxKLecuf.mjs +1 -0
  104. package/dist/packem_shared/storageUploadWithoutMaxSize-DUDxPutk.mjs +1 -0
  105. package/dist/packem_shared/tableWithoutInsert-BV7qsPKx.mjs +1 -0
  106. package/dist/packem_shared/ttlFieldNotTimestamp-BFiRkSZD.mjs +1 -0
  107. package/dist/packem_shared/unboundedStringArgument-DN8w1bg-.mjs +1 -0
  108. package/dist/packem_shared/unindexedForeignKey-DK4eWXkb.mjs +1 -0
  109. package/dist/packem_shared/unindexedRelationTarget-B5fFq1E8.mjs +1 -0
  110. package/dist/packem_shared/unrestrictedWhereBranch-YKiydie4.mjs +1 -0
  111. package/dist/packem_shared/userCreatingMutationWithoutCaptcha-DIeHIeTU.mjs +1 -0
  112. package/dist/packem_shared/vectorsNamespaceFromUserInput-Cyq5B2kF.mjs +1 -0
  113. package/dist/packem_shared/workflowDuplicateStepName-cXF6Rpvg.mjs +1 -0
  114. package/dist/packem_shared/workflowUnknownTarget-BzKlYSCg.mjs +1 -0
  115. package/dist/packem_shared/workflowUnused-D9fC9E9b.mjs +1 -0
  116. package/package.json +3 -2
  117. package/dist/packem_shared/AE_METRIC_EVENTS-DexctYv6.mjs +0 -85
  118. package/dist/packem_shared/adminRouteWithoutGuard-UUGBkAjU.mjs +0 -33
  119. package/dist/packem_shared/authApiCallWithoutHeaders-BeJhCZaf.mjs +0 -38
  120. package/dist/packem_shared/circularFk-B2freHrP.mjs +0 -84
  121. package/dist/packem_shared/constraintValidator-Dr9Py3FD.mjs +0 -186
  122. package/dist/packem_shared/containerOversizedInstance-5U1VKPRM.mjs +0 -36
  123. package/dist/packem_shared/containerPublicInternet-CuNerJE5.mjs +0 -30
  124. package/dist/packem_shared/duplicateIndex-BOublMSt.mjs +0 -57
  125. package/dist/packem_shared/emptyIndex-BX8EuEY7.mjs +0 -32
  126. package/dist/packem_shared/filterWithoutIndex-BYVeJaSs.mjs +0 -31
  127. package/dist/packem_shared/finding-Dm_zvzS1.mjs +0 -16
  128. package/dist/packem_shared/fk-index-IUK1ukgs.mjs +0 -7
  129. package/dist/packem_shared/fromServerSchema-DinF1nph.mjs +0 -50
  130. package/dist/packem_shared/hardcodedSecret-W2pz1UZB.mjs +0 -35
  131. package/dist/packem_shared/helpers-DNCkMWZQ.mjs +0 -4
  132. package/dist/packem_shared/hotShard-Ir5D0B6J.mjs +0 -48
  133. package/dist/packem_shared/hyperdriveOutsideAction-BgZqX7Xg.mjs +0 -30
  134. package/dist/packem_shared/indexReferencesUnknownField-DH0_dbUY.mjs +0 -36
  135. package/dist/packem_shared/indexUtilization-B5DMQ3bI.mjs +0 -45
  136. package/dist/packem_shared/maskUncoveredPiiColumn-DjGIPG6M.mjs +0 -61
  137. package/dist/packem_shared/nondeterministicQueryMutation-GXES1fLp.mjs +0 -35
  138. package/dist/packem_shared/policyReferencesUnknownTable-DtaIEovd.mjs +0 -38
  139. package/dist/packem_shared/publicArgumentUsesAny-C71b2NCf.mjs +0 -32
  140. package/dist/packem_shared/publicMutationWithoutRatelimit-xBpJ6GWK.mjs +0 -36
  141. package/dist/packem_shared/r2sqlOutsideAction-CtqxvMuV.mjs +0 -30
  142. package/dist/packem_shared/relationReferencesUnknownField-YznyXt_7.mjs +0 -54
  143. package/dist/packem_shared/relationReferencesUnknownTable-DrorpKYe.mjs +0 -33
  144. package/dist/packem_shared/rlsUncoveredTable-CxEfZ5eZ.mjs +0 -56
  145. package/dist/packem_shared/sqlInjectionRisk-zwytYGLt.mjs +0 -26
  146. package/dist/packem_shared/tableWithoutInsert-CbbaYIP4.mjs +0 -34
  147. package/dist/packem_shared/unboundedStringArgument-DThg2-wt.mjs +0 -32
  148. package/dist/packem_shared/unindexedForeignKey-BgJbKyqK.mjs +0 -45
  149. package/dist/packem_shared/unindexedRelationTarget-D6eyj6Xx.mjs +0 -53
  150. package/dist/packem_shared/userCreatingMutationWithoutCaptcha-CH31YsUZ.mjs +0 -42
  151. package/dist/packem_shared/workflowUnknownTarget-Cdd7WhKQ.mjs +0 -34
  152. 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
@@ -79,20 +79,75 @@ 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`, `constraint_validator`) reads observed signal off the `LintContext` (`shardTraffic`, `tableScans`, `indexHits`, `tableSamples`). The Studio backend fills those from each shard's durable counters:
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/bindings/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` / `tableSamples` come from wherever
90
+ // you read your shards' durable counters the Studio backend reads its own.
91
+ const findings = runAdvisor({ schema: fromServerSchema(schema), shardTraffic, tableScans, indexHits, tableSamples }, { source: "runtime" });
92
92
  ```
93
93
 
94
+ An Analytics-Engine-backed alternative feeder (querying AE SQL 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
 
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** weight (`Lint.weight`, else a severity ladder: `ERROR` 20 / `WARN` 10 / `INFO` 5) — 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, hand-edited, or written by an older MAP_VERSION. Fail loudly —
128
+ // treating it as "no regression" would silently disable the gate forever.
129
+ throw new Error("advisor baseline is unreadable; regenerate lunora.advisor.map.json");
130
+ }
131
+
132
+ const diff = compareToBaseline(map, baseline);
133
+
134
+ // `comparable` must be narrowed before `regressed` is reachable, so a stale
135
+ // baseline cannot read as a clean run.
136
+ if (!diff.comparable) {
137
+ throw new Error(`advisor baseline not comparable: ${diff.reason}`);
138
+ }
139
+
140
+ // Five independent signals: the global score fell, an existing procedure got
141
+ // worse, one started failing, one's findings grew without its score moving, or
142
+ // the project bucket gained findings. The growth signals matter because a rule is
143
+ // charged once however many times it fires, and the project score saturates at 0.
144
+ if (diff.regressed) {
145
+ process.exitCode = 1;
146
+ }
147
+ ```
148
+
149
+ `@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).
150
+
96
151
  > This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs/addons/studio)**.
97
152
 
98
153
  ## Related