@safegres/test 0.0.1 → 1.30.7

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 (483) hide show
  1. package/README.md +825 -412
  2. package/adapters.d.ts +8 -0
  3. package/adapters.js +18 -0
  4. package/ast/helpers.d.ts +16 -0
  5. package/ast/helpers.js +40 -0
  6. package/ast/parse.d.ts +31 -0
  7. package/ast/parse.js +87 -0
  8. package/ast/walk.d.ts +27 -0
  9. package/ast/walk.js +79 -0
  10. package/callgraph/baseline.d.ts +28 -0
  11. package/callgraph/baseline.js +55 -0
  12. package/callgraph/extract.d.ts +93 -0
  13. package/callgraph/extract.js +340 -0
  14. package/callgraph/graph.d.ts +82 -0
  15. package/callgraph/graph.js +276 -0
  16. package/checks/anti-patterns.d.ts +53 -0
  17. package/checks/anti-patterns.js +247 -0
  18. package/checks/column-grants.d.ts +20 -0
  19. package/checks/column-grants.js +74 -0
  20. package/checks/coverage.d.ts +43 -0
  21. package/checks/coverage.js +110 -0
  22. package/checks/definer-function.d.ts +91 -0
  23. package/checks/definer-function.js +458 -0
  24. package/checks/definer-view.d.ts +167 -0
  25. package/checks/definer-view.js +392 -0
  26. package/checks/indexes.d.ts +82 -0
  27. package/checks/indexes.js +263 -0
  28. package/checks/lattice.d.ts +179 -0
  29. package/checks/lattice.js +487 -0
  30. package/checks/object-acls.d.ts +32 -0
  31. package/checks/object-acls.js +129 -0
  32. package/checks/policy-index.d.ts +78 -0
  33. package/checks/policy-index.js +349 -0
  34. package/checks/revocable-grants.d.ts +140 -0
  35. package/checks/revocable-grants.js +377 -0
  36. package/checks/rls-flags.d.ts +25 -0
  37. package/checks/rls-flags.js +82 -0
  38. package/checks/role-reach.d.ts +329 -0
  39. package/checks/role-reach.js +277 -0
  40. package/checks/role-trust.d.ts +45 -0
  41. package/checks/role-trust.js +91 -0
  42. package/checks/set-role.d.ts +21 -0
  43. package/checks/set-role.js +66 -0
  44. package/checks/stats.d.ts +52 -0
  45. package/checks/stats.js +192 -0
  46. package/checks/view-exposure.d.ts +68 -0
  47. package/checks/view-exposure.js +236 -0
  48. package/checks/view-writes.d.ts +88 -0
  49. package/checks/view-writes.js +350 -0
  50. package/ci/baseline.d.ts +104 -0
  51. package/ci/baseline.js +111 -0
  52. package/ci/github.d.ts +47 -0
  53. package/ci/github.js +197 -0
  54. package/cli/audit.d.ts +3 -0
  55. package/cli/audit.js +560 -0
  56. package/cli/baseline.d.ts +3 -0
  57. package/cli/baseline.js +104 -0
  58. package/cli/commands.d.ts +2 -0
  59. package/cli/commands.js +60 -0
  60. package/cli/doctor.d.ts +3 -0
  61. package/cli/doctor.js +72 -0
  62. package/cli/eval.d.ts +3 -0
  63. package/cli/eval.js +153 -0
  64. package/cli/print-config.d.ts +3 -0
  65. package/cli/print-config.js +54 -0
  66. package/cli/shared.d.ts +43 -0
  67. package/cli/shared.js +153 -0
  68. package/cli.d.ts +2 -0
  69. package/cli.js +69 -0
  70. package/commands/audit.d.ts +65 -0
  71. package/commands/audit.js +779 -0
  72. package/commands/doctor.d.ts +19 -0
  73. package/commands/doctor.js +181 -0
  74. package/commands/eval.d.ts +67 -0
  75. package/commands/eval.js +93 -0
  76. package/config/fingerprint.d.ts +40 -0
  77. package/config/fingerprint.js +71 -0
  78. package/config/loader.d.ts +44 -0
  79. package/config/loader.js +210 -0
  80. package/config/presets.d.ts +95 -0
  81. package/config/presets.js +327 -0
  82. package/config/resolve.d.ts +47 -0
  83. package/config/resolve.js +192 -0
  84. package/config/schema.d.ts +54 -0
  85. package/config/schema.js +269 -0
  86. package/config/types.d.ts +562 -0
  87. package/config/validate.d.ts +14 -0
  88. package/config/validate.js +125 -0
  89. package/corpus/README.md +83 -0
  90. package/corpus/bootstrap.sql +14 -0
  91. package/corpus/cases/01-anon-write-grant/case.json +26 -0
  92. package/corpus/cases/01-anon-write-grant/schema.sql +19 -0
  93. package/corpus/cases/02-grant-to-public/case.json +25 -0
  94. package/corpus/cases/02-grant-to-public/schema.sql +18 -0
  95. package/corpus/cases/03-rls-off-reachable-by-anon/case.json +30 -0
  96. package/corpus/cases/03-rls-off-reachable-by-anon/schema.sql +13 -0
  97. package/corpus/cases/04-permissive-write-policy/case.json +30 -0
  98. package/corpus/cases/04-permissive-write-policy/schema.sql +21 -0
  99. package/corpus/cases/05-public-read-policy/case.json +25 -0
  100. package/corpus/cases/05-public-read-policy/schema.sql +16 -0
  101. package/corpus/cases/06-update-without-with-check/case.json +25 -0
  102. package/corpus/cases/06-update-without-with-check/schema.sql +19 -0
  103. package/corpus/cases/07-rls-enabled-no-policies/case.json +29 -0
  104. package/corpus/cases/07-rls-enabled-no-policies/schema.sql +14 -0
  105. package/corpus/cases/08-select-grant-no-policy/case.json +25 -0
  106. package/corpus/cases/08-select-grant-no-policy/schema.sql +20 -0
  107. package/corpus/cases/09-write-grant-no-policy/case.json +24 -0
  108. package/corpus/cases/09-write-grant-no-policy/schema.sql +18 -0
  109. package/corpus/cases/10-rls-not-forced/case.json +24 -0
  110. package/corpus/cases/10-rls-not-forced/schema.sql +18 -0
  111. package/corpus/cases/11-session-user-in-policy/case.json +24 -0
  112. package/corpus/cases/11-session-user-in-policy/schema.sql +18 -0
  113. package/corpus/cases/12-volatile-function-in-policy/case.json +24 -0
  114. package/corpus/cases/12-volatile-function-in-policy/schema.sql +21 -0
  115. package/corpus/cases/13-security-definer-policy-wrapper/case.json +24 -0
  116. package/corpus/cases/13-security-definer-policy-wrapper/schema.sql +21 -0
  117. package/corpus/cases/14-dead-policy/case.json +24 -0
  118. package/corpus/cases/14-dead-policy/schema.sql +18 -0
  119. package/corpus/cases/15-unreachable-grant/case.json +24 -0
  120. package/corpus/cases/15-unreachable-grant/schema.sql +18 -0
  121. package/corpus/cases/16-anon-permissive-write-policy/case.json +34 -0
  122. package/corpus/cases/16-anon-permissive-write-policy/schema.sql +17 -0
  123. package/corpus/cases/17-foreign-key-without-index/case.json +24 -0
  124. package/corpus/cases/17-foreign-key-without-index/schema.sql +18 -0
  125. package/corpus/cases/18-policy-column-unindexed/case.json +24 -0
  126. package/corpus/cases/18-policy-column-unindexed/schema.sql +23 -0
  127. package/corpus/cases/19-policy-column-cast/case.json +24 -0
  128. package/corpus/cases/19-policy-column-cast/schema.sql +19 -0
  129. package/corpus/cases/20-stable-function-per-row/case.json +24 -0
  130. package/corpus/cases/20-stable-function-per-row/schema.sql +25 -0
  131. package/corpus/cases/21-table-without-primary-key/case.json +24 -0
  132. package/corpus/cases/21-table-without-primary-key/schema.sql +14 -0
  133. package/corpus/cases/22-redundant-index/case.json +24 -0
  134. package/corpus/cases/22-redundant-index/schema.sql +16 -0
  135. package/corpus/cases/23-tsvector-without-index/case.json +24 -0
  136. package/corpus/cases/23-tsvector-without-index/schema.sql +13 -0
  137. package/corpus/cases/24-anon-set-role-escalation/case.json +29 -0
  138. package/corpus/cases/24-anon-set-role-escalation/schema.sql +30 -0
  139. package/corpus/cases/25-definer-view-bypass/case.json +31 -0
  140. package/corpus/cases/25-definer-view-bypass/schema.sql +36 -0
  141. package/corpus/cases/26-invoker-view-no-bypass/case.json +31 -0
  142. package/corpus/cases/26-invoker-view-no-bypass/schema.sql +44 -0
  143. package/corpus/cases/27-definer-view-write/case.json +36 -0
  144. package/corpus/cases/27-definer-view-write/schema.sql +40 -0
  145. package/corpus/cases/28-invoker-view-no-write/case.json +31 -0
  146. package/corpus/cases/28-invoker-view-no-write/schema.sql +37 -0
  147. package/corpus/cases/29-rewrite-rule-bypass/case.json +36 -0
  148. package/corpus/cases/29-rewrite-rule-bypass/schema.sql +52 -0
  149. package/corpus/cases/30-instead-nothing-rule/case.json +31 -0
  150. package/corpus/cases/30-instead-nothing-rule/schema.sql +44 -0
  151. package/corpus/cases/31-matview-snapshot/case.json +32 -0
  152. package/corpus/cases/31-matview-snapshot/schema.sql +36 -0
  153. package/corpus/cases/32-matview-no-rls-no-bypass/case.json +30 -0
  154. package/corpus/cases/32-matview-no-rls-no-bypass/schema.sql +18 -0
  155. package/corpus/cases/33-leaky-filter-view/case.json +36 -0
  156. package/corpus/cases/33-leaky-filter-view/schema.sql +29 -0
  157. package/corpus/cases/34-barrier-view-no-leak/case.json +32 -0
  158. package/corpus/cases/34-barrier-view-no-leak/schema.sql +29 -0
  159. package/corpus/cases/35-column-grant-projection/case.json +27 -0
  160. package/corpus/cases/35-column-grant-projection/schema.sql +21 -0
  161. package/corpus/cases/35-policy-function-not-leakproof/case.json +24 -0
  162. package/corpus/cases/35-policy-function-not-leakproof/schema.sql +27 -0
  163. package/corpus/cases/36-column-grant-rls-mediated/case.json +27 -0
  164. package/corpus/cases/36-column-grant-rls-mediated/schema.sql +32 -0
  165. package/corpus/cases/36-policy-function-leakproof/case.json +24 -0
  166. package/corpus/cases/36-policy-function-leakproof/schema.sql +22 -0
  167. package/corpus/cases/37-sort-column-unindexed/case.json +24 -0
  168. package/corpus/cases/37-sort-column-unindexed/schema.sql +14 -0
  169. package/corpus/cases/37-unaudited-base-relation/case.json +30 -0
  170. package/corpus/cases/37-unaudited-base-relation/schema.sql +45 -0
  171. package/corpus/cases/38-foreign-key-with-index/case.json +22 -0
  172. package/corpus/cases/38-foreign-key-with-index/schema.sql +17 -0
  173. package/corpus/cases/38-invoker-view-unaudited-base/case.json +30 -0
  174. package/corpus/cases/38-invoker-view-unaudited-base/schema.sql +39 -0
  175. package/corpus/cases/39-definer-view-column-subset/case.json +30 -0
  176. package/corpus/cases/39-definer-view-column-subset/schema.sql +29 -0
  177. package/corpus/cases/39-policy-cast-with-expression-index/case.json +23 -0
  178. package/corpus/cases/39-policy-cast-with-expression-index/schema.sql +19 -0
  179. package/corpus/cases/40-definer-view-column-overreach/case.json +27 -0
  180. package/corpus/cases/40-definer-view-column-overreach/schema.sql +29 -0
  181. package/corpus/cases/40-stable-function-hoisted/case.json +23 -0
  182. package/corpus/cases/40-stable-function-hoisted/schema.sql +25 -0
  183. package/corpus/cases/41-index-prefix-not-redundant/case.json +21 -0
  184. package/corpus/cases/41-index-prefix-not-redundant/schema.sql +15 -0
  185. package/corpus/cases/41-unreadable-view-body/case.json +33 -0
  186. package/corpus/cases/41-unreadable-view-body/schema.sql +28 -0
  187. package/corpus/cases/42-invoker-view-unreadable-body/case.json +30 -0
  188. package/corpus/cases/42-invoker-view-unreadable-body/schema.sql +27 -0
  189. package/corpus/cases/42-search-column-indexed/case.json +21 -0
  190. package/corpus/cases/42-search-column-indexed/schema.sql +12 -0
  191. package/corpus/cases/43-anon-sequence-grant/case.json +27 -0
  192. package/corpus/cases/43-anon-sequence-grant/schema.sql +23 -0
  193. package/corpus/cases/44-serial-sequence-usage/case.json +27 -0
  194. package/corpus/cases/44-serial-sequence-usage/schema.sql +25 -0
  195. package/corpus/cases/45-anon-foreign-table/case.json +27 -0
  196. package/corpus/cases/45-anon-foreign-table/schema.sql +23 -0
  197. package/corpus/cases/46-unchecked-writable-view/case.json +41 -0
  198. package/corpus/cases/46-unchecked-writable-view/schema.sql +37 -0
  199. package/corpus/cases/47-checked-writable-view/case.json +37 -0
  200. package/corpus/cases/47-checked-writable-view/schema.sql +38 -0
  201. package/corpus/cases/48-definer-function-reach/case.json +38 -0
  202. package/corpus/cases/48-definer-function-reach/schema.sql +41 -0
  203. package/corpus/cases/49-invoker-function-no-reach/case.json +32 -0
  204. package/corpus/cases/49-invoker-function-no-reach/schema.sql +38 -0
  205. package/corpus/cases/50-instead-of-trigger-write/case.json +38 -0
  206. package/corpus/cases/50-instead-of-trigger-write/schema.sql +58 -0
  207. package/corpus/cases/51-invoker-trigger-no-write/case.json +33 -0
  208. package/corpus/cases/51-invoker-trigger-no-write/schema.sql +54 -0
  209. package/corpus/cases/52-revocable-execute-grant/case.json +30 -0
  210. package/corpus/cases/52-revocable-execute-grant/schema.sql +45 -0
  211. package/corpus/cases/53-policy-predicate-retains-execute/case.json +23 -0
  212. package/corpus/cases/53-policy-predicate-retains-execute/schema.sql +49 -0
  213. package/corpus/index.d.ts +79 -0
  214. package/corpus/index.js +125 -0
  215. package/esm/adapters.d.ts +8 -0
  216. package/esm/adapters.js +6 -0
  217. package/esm/ast/helpers.d.ts +16 -0
  218. package/esm/ast/helpers.js +36 -0
  219. package/esm/ast/parse.d.ts +31 -0
  220. package/esm/ast/parse.js +82 -0
  221. package/esm/ast/walk.d.ts +27 -0
  222. package/esm/ast/walk.js +72 -0
  223. package/esm/callgraph/baseline.d.ts +28 -0
  224. package/esm/callgraph/baseline.js +48 -0
  225. package/esm/callgraph/extract.d.ts +93 -0
  226. package/esm/callgraph/extract.js +333 -0
  227. package/esm/callgraph/graph.d.ts +82 -0
  228. package/esm/callgraph/graph.js +273 -0
  229. package/esm/checks/anti-patterns.d.ts +53 -0
  230. package/esm/checks/anti-patterns.js +240 -0
  231. package/esm/checks/column-grants.d.ts +20 -0
  232. package/esm/checks/column-grants.js +71 -0
  233. package/esm/checks/coverage.d.ts +43 -0
  234. package/esm/checks/coverage.js +104 -0
  235. package/esm/checks/definer-function.d.ts +91 -0
  236. package/esm/checks/definer-function.js +452 -0
  237. package/esm/checks/definer-view.d.ts +167 -0
  238. package/esm/checks/definer-view.js +382 -0
  239. package/esm/checks/indexes.d.ts +82 -0
  240. package/esm/checks/indexes.js +256 -0
  241. package/esm/checks/lattice.d.ts +179 -0
  242. package/esm/checks/lattice.js +475 -0
  243. package/esm/checks/object-acls.d.ts +32 -0
  244. package/esm/checks/object-acls.js +125 -0
  245. package/esm/checks/policy-index.d.ts +78 -0
  246. package/esm/checks/policy-index.js +342 -0
  247. package/esm/checks/revocable-grants.d.ts +140 -0
  248. package/esm/checks/revocable-grants.js +373 -0
  249. package/esm/checks/rls-flags.d.ts +25 -0
  250. package/esm/checks/rls-flags.js +77 -0
  251. package/esm/checks/role-reach.d.ts +329 -0
  252. package/esm/checks/role-reach.js +270 -0
  253. package/esm/checks/role-trust.d.ts +45 -0
  254. package/esm/checks/role-trust.js +86 -0
  255. package/esm/checks/set-role.d.ts +21 -0
  256. package/esm/checks/set-role.js +63 -0
  257. package/esm/checks/stats.d.ts +52 -0
  258. package/esm/checks/stats.js +184 -0
  259. package/esm/checks/view-exposure.d.ts +68 -0
  260. package/esm/checks/view-exposure.js +231 -0
  261. package/esm/checks/view-writes.d.ts +88 -0
  262. package/esm/checks/view-writes.js +344 -0
  263. package/esm/ci/baseline.d.ts +104 -0
  264. package/esm/ci/baseline.js +107 -0
  265. package/esm/ci/github.d.ts +47 -0
  266. package/esm/ci/github.js +161 -0
  267. package/esm/cli/audit.d.ts +3 -0
  268. package/esm/cli/audit.js +525 -0
  269. package/esm/cli/baseline.d.ts +3 -0
  270. package/esm/cli/baseline.js +102 -0
  271. package/esm/cli/commands.d.ts +2 -0
  272. package/esm/cli/commands.js +53 -0
  273. package/esm/cli/doctor.d.ts +3 -0
  274. package/esm/cli/doctor.js +67 -0
  275. package/esm/cli/eval.d.ts +3 -0
  276. package/esm/cli/eval.js +115 -0
  277. package/esm/cli/print-config.d.ts +3 -0
  278. package/esm/cli/print-config.js +52 -0
  279. package/esm/cli/shared.d.ts +43 -0
  280. package/esm/cli/shared.js +112 -0
  281. package/esm/cli.d.ts +2 -0
  282. package/esm/cli.js +67 -0
  283. package/esm/commands/audit.d.ts +65 -0
  284. package/esm/commands/audit.js +776 -0
  285. package/esm/commands/doctor.d.ts +19 -0
  286. package/esm/commands/doctor.js +178 -0
  287. package/esm/commands/eval.d.ts +67 -0
  288. package/esm/commands/eval.js +90 -0
  289. package/esm/config/fingerprint.d.ts +40 -0
  290. package/esm/config/fingerprint.js +68 -0
  291. package/esm/config/loader.d.ts +44 -0
  292. package/esm/config/loader.js +172 -0
  293. package/esm/config/presets.d.ts +95 -0
  294. package/esm/config/presets.js +324 -0
  295. package/esm/config/resolve.d.ts +47 -0
  296. package/esm/config/resolve.js +180 -0
  297. package/esm/config/schema.d.ts +54 -0
  298. package/esm/config/schema.js +265 -0
  299. package/esm/config/types.d.ts +562 -0
  300. package/esm/config/validate.d.ts +14 -0
  301. package/esm/config/validate.js +122 -0
  302. package/esm/corpus/index.d.ts +79 -0
  303. package/esm/corpus/index.js +85 -0
  304. package/esm/exposure/adapters.d.ts +152 -0
  305. package/esm/exposure/adapters.js +463 -0
  306. package/esm/exposure/planes.d.ts +59 -0
  307. package/esm/exposure/planes.js +144 -0
  308. package/esm/exposure/reach.d.ts +78 -0
  309. package/esm/exposure/reach.js +105 -0
  310. package/esm/exposure/routines.d.ts +36 -0
  311. package/esm/exposure/routines.js +54 -0
  312. package/esm/index.d.ts +96 -0
  313. package/esm/index.js +57 -6
  314. package/esm/lint/index.d.ts +11 -0
  315. package/esm/lint/index.js +10 -0
  316. package/esm/perf/baseline.d.ts +40 -0
  317. package/esm/perf/baseline.js +83 -0
  318. package/esm/perf/explain.d.ts +46 -0
  319. package/esm/perf/explain.js +201 -0
  320. package/esm/pg/acl.d.ts +72 -0
  321. package/esm/pg/acl.js +186 -0
  322. package/esm/pg/behaviors.d.ts +114 -0
  323. package/esm/pg/behaviors.js +206 -0
  324. package/esm/pg/exposure.d.ts +74 -0
  325. package/esm/pg/exposure.js +198 -0
  326. package/esm/pg/functions.d.ts +52 -0
  327. package/esm/pg/functions.js +128 -0
  328. package/esm/pg/indexes.d.ts +192 -0
  329. package/esm/pg/indexes.js +395 -0
  330. package/esm/pg/introspect.d.ts +134 -0
  331. package/esm/pg/introspect.js +274 -0
  332. package/esm/pg/objects.d.ts +37 -0
  333. package/esm/pg/objects.js +108 -0
  334. package/esm/pg/paths.d.ts +114 -0
  335. package/esm/pg/paths.js +159 -0
  336. package/esm/pg/proc.d.ts +23 -0
  337. package/esm/pg/proc.js +55 -0
  338. package/esm/pg/reach-inputs.d.ts +47 -0
  339. package/esm/pg/reach-inputs.js +133 -0
  340. package/esm/pg/roles.d.ts +28 -0
  341. package/esm/pg/roles.js +50 -0
  342. package/esm/pg/stats.d.ts +65 -0
  343. package/esm/pg/stats.js +129 -0
  344. package/esm/pgpm-test.d.ts +22 -0
  345. package/esm/pgpm-test.js +39 -0
  346. package/esm/report/callgraph.d.ts +9 -0
  347. package/esm/report/callgraph.js +74 -0
  348. package/esm/report/compare.d.ts +143 -0
  349. package/esm/report/compare.js +195 -0
  350. package/esm/report/github.d.ts +74 -0
  351. package/esm/report/github.js +290 -0
  352. package/esm/report/json.d.ts +4 -0
  353. package/esm/report/json.js +3 -0
  354. package/esm/report/markdown.d.ts +25 -0
  355. package/esm/report/markdown.js +352 -0
  356. package/esm/report/pretty.d.ts +14 -0
  357. package/esm/report/pretty.js +318 -0
  358. package/esm/report/sarif.d.ts +40 -0
  359. package/esm/report/sarif.js +189 -0
  360. package/esm/report/view.d.ts +82 -0
  361. package/esm/report/view.js +126 -0
  362. package/esm/rules/registry.d.ts +66 -0
  363. package/esm/rules/registry.js +562 -0
  364. package/esm/score/score.d.ts +84 -0
  365. package/esm/score/score.js +233 -0
  366. package/esm/score/scorecards.d.ts +68 -0
  367. package/esm/score/scorecards.js +157 -0
  368. package/esm/types.d.ts +292 -0
  369. package/esm/types.js +26 -0
  370. package/esm/version.d.ts +8 -0
  371. package/esm/version.js +9 -0
  372. package/exposure/adapters.d.ts +152 -0
  373. package/exposure/adapters.js +468 -0
  374. package/exposure/planes.d.ts +59 -0
  375. package/exposure/planes.js +151 -0
  376. package/exposure/reach.d.ts +78 -0
  377. package/exposure/reach.js +109 -0
  378. package/exposure/routines.d.ts +36 -0
  379. package/exposure/routines.js +57 -0
  380. package/index.d.ts +96 -6
  381. package/index.js +229 -6
  382. package/lint/index.d.ts +11 -0
  383. package/lint/index.js +19 -0
  384. package/package.json +54 -48
  385. package/perf/baseline.d.ts +40 -0
  386. package/perf/baseline.js +92 -0
  387. package/perf/explain.d.ts +46 -0
  388. package/perf/explain.js +204 -0
  389. package/pg/acl.d.ts +72 -0
  390. package/pg/acl.js +191 -0
  391. package/pg/behaviors.d.ts +114 -0
  392. package/pg/behaviors.js +217 -0
  393. package/pg/exposure.d.ts +74 -0
  394. package/pg/exposure.js +205 -0
  395. package/pg/functions.d.ts +52 -0
  396. package/pg/functions.js +131 -0
  397. package/pg/indexes.d.ts +192 -0
  398. package/pg/indexes.js +400 -0
  399. package/pg/introspect.d.ts +134 -0
  400. package/pg/introspect.js +280 -0
  401. package/pg/objects.d.ts +37 -0
  402. package/pg/objects.js +111 -0
  403. package/pg/paths.d.ts +114 -0
  404. package/pg/paths.js +166 -0
  405. package/pg/proc.d.ts +23 -0
  406. package/pg/proc.js +58 -0
  407. package/pg/reach-inputs.d.ts +47 -0
  408. package/pg/reach-inputs.js +136 -0
  409. package/pg/roles.d.ts +28 -0
  410. package/pg/roles.js +54 -0
  411. package/pg/stats.d.ts +65 -0
  412. package/pg/stats.js +133 -0
  413. package/pgpm-test.d.ts +22 -0
  414. package/pgpm-test.js +42 -0
  415. package/report/callgraph.d.ts +9 -0
  416. package/report/callgraph.js +81 -0
  417. package/report/compare.d.ts +143 -0
  418. package/report/compare.js +205 -0
  419. package/report/github.d.ts +74 -0
  420. package/report/github.js +333 -0
  421. package/report/json.d.ts +4 -0
  422. package/report/json.js +6 -0
  423. package/report/markdown.d.ts +25 -0
  424. package/report/markdown.js +355 -0
  425. package/report/pretty.d.ts +14 -0
  426. package/report/pretty.js +324 -0
  427. package/report/sarif.d.ts +40 -0
  428. package/report/sarif.js +193 -0
  429. package/report/view.d.ts +82 -0
  430. package/report/view.js +132 -0
  431. package/rules/registry.d.ts +66 -0
  432. package/rules/registry.js +569 -0
  433. package/schema/safegres.schema.json +1119 -0
  434. package/score/score.d.ts +84 -0
  435. package/score/score.js +238 -0
  436. package/score/scorecards.d.ts +68 -0
  437. package/score/scorecards.js +162 -0
  438. package/types.d.ts +292 -0
  439. package/types.js +32 -0
  440. package/version.d.ts +8 -0
  441. package/version.js +12 -0
  442. package/LICENSE +0 -23
  443. package/admin.d.ts +0 -26
  444. package/admin.js +0 -182
  445. package/connect.d.ts +0 -19
  446. package/connect.js +0 -83
  447. package/dist/README.md +0 -557
  448. package/dist/package.json +0 -72
  449. package/esm/admin.js +0 -178
  450. package/esm/connect.js +0 -78
  451. package/esm/manager.js +0 -136
  452. package/esm/roles.js +0 -32
  453. package/esm/seed/adapters.js +0 -23
  454. package/esm/seed/csv.js +0 -44
  455. package/esm/seed/index.js +0 -16
  456. package/esm/seed/json.js +0 -18
  457. package/esm/seed/launchql.js +0 -19
  458. package/esm/seed/sqitch.js +0 -17
  459. package/esm/stream.js +0 -43
  460. package/esm/test-client.js +0 -150
  461. package/manager.d.ts +0 -25
  462. package/manager.js +0 -140
  463. package/roles.d.ts +0 -17
  464. package/roles.js +0 -38
  465. package/seed/adapters.d.ts +0 -4
  466. package/seed/adapters.js +0 -28
  467. package/seed/csv.d.ts +0 -9
  468. package/seed/csv.js +0 -49
  469. package/seed/index.d.ts +0 -16
  470. package/seed/index.js +0 -33
  471. package/seed/json.d.ts +0 -6
  472. package/seed/json.js +0 -21
  473. package/seed/launchql.d.ts +0 -2
  474. package/seed/launchql.js +0 -22
  475. package/seed/sqitch.d.ts +0 -2
  476. package/seed/sqitch.js +0 -20
  477. package/seed/types.d.ts +0 -13
  478. package/stream.d.ts +0 -2
  479. package/stream.js +0 -46
  480. package/test-client.d.ts +0 -49
  481. package/test-client.js +0 -154
  482. /package/{seed → config}/types.js +0 -0
  483. /package/esm/{seed → config}/types.js +0 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Which report a delta is measured against — chosen, validated, explained.
3
+ *
4
+ * A comparison is only as trustworthy as the artifact it subtracts, and CI
5
+ * picking that artifact is where it goes wrong. The shape everyone writes is a
6
+ * one-row query for the base branch's last successful run:
7
+ *
8
+ * gh run list --workflow ci.yaml --branch main --status success --limit 1
9
+ *
10
+ * `--limit 1` is `per_page=1`, so one API row decides the baseline with nothing
11
+ * checking it and nothing printing which row it was. Observed cost, on the PR
12
+ * this module was written for: that query answered with a `main` run 13.6 days
13
+ * and ~150 successful runs stale, and a PR touching no SQL at all was reported
14
+ * as `Δ vs main: ▼ −0.8, findings 2877 → 3323` — every merge since. A finished,
15
+ * correct run existed; the artifacts had not expired. An unvalidated answer was
16
+ * simply trusted.
17
+ *
18
+ * The fix is not a cleverer query. This takes several candidates and picks the
19
+ * newest that survives three questions:
20
+ *
21
+ * 1. did it succeed, and is its report artifact still downloadable?
22
+ * 2. is it younger than `maxAgeMs`?
23
+ * 3. is its head commit an ancestor of this branch's merge base with the base?
24
+ *
25
+ * (3) is what makes the delta mean "what this branch did": a baseline from the
26
+ * tip of the base branch *after* the branch point charges the branch for merges
27
+ * it never contained, and one from rewritten or unrelated history is not
28
+ * comparable at all. (2) catches a stale baseline that is a legitimate ancestor.
29
+ *
30
+ * No acceptable candidate is not a failure — it is a report with no deltas and
31
+ * a stated reason (`--compare-skipped`). The absolute grade gate and the perf
32
+ * ratchet never read the comparison, so they still apply.
33
+ *
34
+ * This module is pure and provider-agnostic: ancestry and artifact download
35
+ * arrive as callbacks, which is what makes the decision testable without a
36
+ * network. `./github` supplies them from GitHub Actions.
37
+ */
38
+ /** One run of the base branch, as a provider lists them (newest first). */
39
+ export interface BaselineCandidate {
40
+ /** Provider's id for the run, as it appears in a URL. */
41
+ runId: string;
42
+ /** Commit the run audited. */
43
+ headSha: string;
44
+ /** ISO 8601. When the run finished, or started if that is all there is. */
45
+ finishedAt: string;
46
+ /** `success` qualifies; anything else (including in-progress) does not. */
47
+ conclusion: string | null;
48
+ }
49
+ export interface SelectBaselineOptions {
50
+ /** The branch point this comparison must be relative to. */
51
+ mergeBaseSha: string;
52
+ /** Is `sha` in the merge base's history? */
53
+ isAncestor: (sha: string) => boolean;
54
+ /** Fetch a candidate's report; null when the artifact is gone. */
55
+ download: (candidate: BaselineCandidate) => string | null;
56
+ now?: number;
57
+ maxAgeMs?: number;
58
+ }
59
+ export interface RejectedCandidate {
60
+ runId: string;
61
+ headSha: string;
62
+ reason: string;
63
+ }
64
+ export interface ChosenBaseline extends BaselineCandidate {
65
+ /** Age at selection time, in milliseconds. */
66
+ ageMs: number;
67
+ /** Path to the downloaded report. */
68
+ path: string;
69
+ }
70
+ export interface BaselineSelection {
71
+ chosen: ChosenBaseline | null;
72
+ rejected: RejectedCandidate[];
73
+ /** One line for the job summary when `chosen` is null. */
74
+ reason: string | null;
75
+ }
76
+ /**
77
+ * How stale a baseline may be by default: 48 hours.
78
+ *
79
+ * The window has to be wider than the gap between two base-branch runs, or a
80
+ * branch loses its delta for no reason. Those runs are per merge, so the gap is
81
+ * activity rather than a schedule: on the repository this was measured against,
82
+ * `main` averaged well under an hour between successful runs on a working day
83
+ * and went ~24 hours over a weekend. 48 hours clears the widest observed quiet
84
+ * stretch with room to spare, while still being a *comparison* — anything older
85
+ * describes a schema several merges removed from the branch point.
86
+ */
87
+ export declare const MAX_BASELINE_AGE_MS: number;
88
+ /**
89
+ * How many runs to consider by default.
90
+ *
91
+ * More than one on principle — trusting a single row is the bug — and enough
92
+ * that an in-progress tip, a cancelled run and a couple of expired artifacts
93
+ * can all be skipped without falling out of the age window. Beyond ~20 the
94
+ * candidates are older than the window anyway, so they cost a request to
95
+ * reject.
96
+ */
97
+ export declare const CANDIDATE_LIMIT = 20;
98
+ /**
99
+ * The newest candidate that is a sane comparison, and why the others were not.
100
+ *
101
+ * `isAncestor` and `download` are I/O in CI and stubs in tests, and are only
102
+ * asked about a candidate that already passed the cheap checks.
103
+ */
104
+ export declare function selectBaseline(candidates: BaselineCandidate[], options: SelectBaselineOptions): BaselineSelection;
package/ci/baseline.js ADDED
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ /**
3
+ * Which report a delta is measured against — chosen, validated, explained.
4
+ *
5
+ * A comparison is only as trustworthy as the artifact it subtracts, and CI
6
+ * picking that artifact is where it goes wrong. The shape everyone writes is a
7
+ * one-row query for the base branch's last successful run:
8
+ *
9
+ * gh run list --workflow ci.yaml --branch main --status success --limit 1
10
+ *
11
+ * `--limit 1` is `per_page=1`, so one API row decides the baseline with nothing
12
+ * checking it and nothing printing which row it was. Observed cost, on the PR
13
+ * this module was written for: that query answered with a `main` run 13.6 days
14
+ * and ~150 successful runs stale, and a PR touching no SQL at all was reported
15
+ * as `Δ vs main: ▼ −0.8, findings 2877 → 3323` — every merge since. A finished,
16
+ * correct run existed; the artifacts had not expired. An unvalidated answer was
17
+ * simply trusted.
18
+ *
19
+ * The fix is not a cleverer query. This takes several candidates and picks the
20
+ * newest that survives three questions:
21
+ *
22
+ * 1. did it succeed, and is its report artifact still downloadable?
23
+ * 2. is it younger than `maxAgeMs`?
24
+ * 3. is its head commit an ancestor of this branch's merge base with the base?
25
+ *
26
+ * (3) is what makes the delta mean "what this branch did": a baseline from the
27
+ * tip of the base branch *after* the branch point charges the branch for merges
28
+ * it never contained, and one from rewritten or unrelated history is not
29
+ * comparable at all. (2) catches a stale baseline that is a legitimate ancestor.
30
+ *
31
+ * No acceptable candidate is not a failure — it is a report with no deltas and
32
+ * a stated reason (`--compare-skipped`). The absolute grade gate and the perf
33
+ * ratchet never read the comparison, so they still apply.
34
+ *
35
+ * This module is pure and provider-agnostic: ancestry and artifact download
36
+ * arrive as callbacks, which is what makes the decision testable without a
37
+ * network. `./github` supplies them from GitHub Actions.
38
+ */
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.CANDIDATE_LIMIT = exports.MAX_BASELINE_AGE_MS = void 0;
41
+ exports.selectBaseline = selectBaseline;
42
+ const compare_1 = require("../report/compare");
43
+ /**
44
+ * How stale a baseline may be by default: 48 hours.
45
+ *
46
+ * The window has to be wider than the gap between two base-branch runs, or a
47
+ * branch loses its delta for no reason. Those runs are per merge, so the gap is
48
+ * activity rather than a schedule: on the repository this was measured against,
49
+ * `main` averaged well under an hour between successful runs on a working day
50
+ * and went ~24 hours over a weekend. 48 hours clears the widest observed quiet
51
+ * stretch with room to spare, while still being a *comparison* — anything older
52
+ * describes a schema several merges removed from the branch point.
53
+ */
54
+ exports.MAX_BASELINE_AGE_MS = 48 * 60 * 60 * 1000;
55
+ /**
56
+ * How many runs to consider by default.
57
+ *
58
+ * More than one on principle — trusting a single row is the bug — and enough
59
+ * that an in-progress tip, a cancelled run and a couple of expired artifacts
60
+ * can all be skipped without falling out of the age window. Beyond ~20 the
61
+ * candidates are older than the window anyway, so they cost a request to
62
+ * reject.
63
+ */
64
+ exports.CANDIDATE_LIMIT = 20;
65
+ const short = (sha) => (sha || '').slice(0, 9);
66
+ /**
67
+ * The newest candidate that is a sane comparison, and why the others were not.
68
+ *
69
+ * `isAncestor` and `download` are I/O in CI and stubs in tests, and are only
70
+ * asked about a candidate that already passed the cheap checks.
71
+ */
72
+ function selectBaseline(candidates, options) {
73
+ const { mergeBaseSha, isAncestor, download, now = Date.now(), maxAgeMs = exports.MAX_BASELINE_AGE_MS } = options;
74
+ const rejected = [];
75
+ const reject = (run, why) => {
76
+ rejected.push({ runId: run.runId, headSha: run.headSha, reason: why });
77
+ };
78
+ for (const run of candidates) {
79
+ if (run.conclusion !== 'success') {
80
+ reject(run, `conclusion ${run.conclusion || 'pending'}`);
81
+ continue;
82
+ }
83
+ const ageMs = now - Date.parse(run.finishedAt);
84
+ if (!(ageMs >= 0)) {
85
+ reject(run, `unreadable timestamp ${run.finishedAt}`);
86
+ continue;
87
+ }
88
+ if (ageMs > maxAgeMs) {
89
+ reject(run, `${(0, compare_1.formatAge)(ageMs)} old, older than the ${(0, compare_1.formatAge)(maxAgeMs)} window`);
90
+ continue;
91
+ }
92
+ if (!isAncestor(run.headSha)) {
93
+ reject(run, `${short(run.headSha)} is not an ancestor of merge base ${short(mergeBaseSha)}`);
94
+ continue;
95
+ }
96
+ const path = download(run);
97
+ if (!path) {
98
+ reject(run, 'no downloadable report artifact');
99
+ continue;
100
+ }
101
+ return { chosen: { ...run, ageMs, path }, rejected, reason: null };
102
+ }
103
+ return {
104
+ chosen: null,
105
+ rejected,
106
+ reason: candidates.length > 0
107
+ ? `no run within ${(0, compare_1.formatAge)(maxAgeMs)} whose head is an ancestor of merge base `
108
+ + `${short(mergeBaseSha)} (${rejected.length} candidate(s) rejected)`
109
+ : 'no runs to compare against'
110
+ };
111
+ }
package/ci/github.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * The GitHub Actions half of baseline selection: run discovery, merge base,
3
+ * artifact download. Everything provider-shaped lives here, so `./baseline`
4
+ * stays a pure decision and the report layer never learns what a workflow run
5
+ * is.
6
+ *
7
+ * The I/O goes through the `gh` CLI, which every GitHub-hosted runner has and
8
+ * which already handles pagination, auth from `GITHUB_TOKEN`, and unzipping an
9
+ * artifact. Reaching the API needs `permissions: actions: read`.
10
+ */
11
+ import { type BaselineSelection } from './baseline';
12
+ export interface GithubBaselineOptions {
13
+ /** `owner/repo`. Default `$GITHUB_REPOSITORY`. */
14
+ repo?: string;
15
+ /** Branch whose runs are candidates — the PR's base. Default from the event. */
16
+ base?: string;
17
+ /** The branch tip to find the merge base of. Default from the event. */
18
+ headSha?: string;
19
+ /** Workflow file whose runs produced the reports. Default the current one. */
20
+ workflow?: string;
21
+ /** Artifact holding `safegres.json`. Default `safegres-reports`. */
22
+ artifact?: string;
23
+ /** Where to unpack it. Default `previous`. */
24
+ dir?: string;
25
+ maxAgeMs?: number;
26
+ candidateLimit?: number;
27
+ now?: number;
28
+ /** Where the selection log goes. Default stdout. */
29
+ log?: (line: string) => void;
30
+ }
31
+ /** What the caller needs to turn a selection into `safegres audit` flags. */
32
+ export interface GithubBaselineResult extends BaselineSelection {
33
+ base: string;
34
+ mergeBaseSha: string;
35
+ /** `https://github.com/<repo>/actions/runs/<id>`, when one was chosen. */
36
+ runUrl?: string;
37
+ /** Pre-formatted age of the chosen run. */
38
+ age?: string;
39
+ }
40
+ /**
41
+ * Select a baseline report from the base branch's recent runs.
42
+ *
43
+ * Throws only on a broken invocation (no repository, no PR context, `gh`
44
+ * missing). A candidate that cannot be used is rejected, not fatal: the caller
45
+ * renders the report without deltas and says why.
46
+ */
47
+ export declare function selectGithubBaseline(options?: GithubBaselineOptions): GithubBaselineResult;
package/ci/github.js ADDED
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ /**
3
+ * The GitHub Actions half of baseline selection: run discovery, merge base,
4
+ * artifact download. Everything provider-shaped lives here, so `./baseline`
5
+ * stays a pure decision and the report layer never learns what a workflow run
6
+ * is.
7
+ *
8
+ * The I/O goes through the `gh` CLI, which every GitHub-hosted runner has and
9
+ * which already handles pagination, auth from `GITHUB_TOKEN`, and unzipping an
10
+ * artifact. Reaching the API needs `permissions: actions: read`.
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.selectGithubBaseline = selectGithubBaseline;
47
+ const child_process_1 = require("child_process");
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const compare_1 = require("../report/compare");
51
+ const baseline_1 = require("./baseline");
52
+ const short = (sha) => sha.slice(0, 9);
53
+ const gh = (args) => (0, child_process_1.execFileSync)('gh', args, { encoding: 'utf8' });
54
+ /**
55
+ * `gh run` needs `-R` explicitly: the workspace may be an unpacked artifact
56
+ * with no `.git` for it to infer the repository from. `gh api` carries the
57
+ * repository in the path and rejects the flag.
58
+ */
59
+ const ghRun = (repo, args) => gh(['run', ...args, '-R', repo]);
60
+ /** The workflow file of the run this is called from, e.g. `run-tests.yaml`. */
61
+ function currentWorkflow() {
62
+ const ref = process.env.GITHUB_WORKFLOW_REF; // owner/repo/.github/workflows/x.yml@refs/…
63
+ if (!ref)
64
+ return undefined;
65
+ const file = ref.split('@')[0].split('/').pop();
66
+ return file || undefined;
67
+ }
68
+ /** The PR's head sha, from the event payload the runner wrote to disk. */
69
+ function eventHeadSha() {
70
+ const file = process.env.GITHUB_EVENT_PATH;
71
+ if (!file || !fs.existsSync(file))
72
+ return undefined;
73
+ const event = JSON.parse(fs.readFileSync(file, 'utf8'));
74
+ return event.pull_request?.head?.sha;
75
+ }
76
+ function eventBaseRef() {
77
+ const file = process.env.GITHUB_EVENT_PATH;
78
+ if (!file || !fs.existsSync(file))
79
+ return undefined;
80
+ const event = JSON.parse(fs.readFileSync(file, 'utf8'));
81
+ return event.pull_request?.base?.ref ?? process.env.GITHUB_BASE_REF;
82
+ }
83
+ /**
84
+ * The path, if it holds a report a delta can be measured against — an artifact
85
+ * that exists but is truncated or from an incompatible version would otherwise
86
+ * fail the audit at compare time, which is the one thing a baseline lookup must
87
+ * never do.
88
+ */
89
+ function readableReport(file, log) {
90
+ if (!fs.existsSync(file)) {
91
+ log(`artifact holds no ${path.basename(file)}`);
92
+ return null;
93
+ }
94
+ try {
95
+ const report = JSON.parse(fs.readFileSync(file, 'utf8'));
96
+ if (report.summary === undefined)
97
+ throw new Error('no summary');
98
+ return file;
99
+ }
100
+ catch (err) {
101
+ log(`unusable report — ${err.message}`);
102
+ return null;
103
+ }
104
+ }
105
+ /**
106
+ * Select a baseline report from the base branch's recent runs.
107
+ *
108
+ * Throws only on a broken invocation (no repository, no PR context, `gh`
109
+ * missing). A candidate that cannot be used is rejected, not fatal: the caller
110
+ * renders the report without deltas and says why.
111
+ */
112
+ function selectGithubBaseline(options = {}) {
113
+ const log = options.log ?? ((line) => console.log(line));
114
+ const repo = options.repo ?? process.env.GITHUB_REPOSITORY;
115
+ const base = options.base ?? eventBaseRef();
116
+ const headSha = options.headSha ?? eventHeadSha() ?? process.env.GITHUB_SHA;
117
+ const workflow = options.workflow ?? currentWorkflow();
118
+ const artifact = options.artifact ?? 'safegres-reports';
119
+ const dir = path.resolve(options.dir ?? 'previous');
120
+ if (!repo)
121
+ throw new Error('GITHUB_REPOSITORY is not set: pass --repo owner/repo');
122
+ if (!base)
123
+ throw new Error('no base branch: pass --base (this is a pull-request-only feature)');
124
+ if (!headSha)
125
+ throw new Error('no head commit: pass --head <sha>');
126
+ if (!workflow)
127
+ throw new Error('no workflow: pass --workflow <file.yaml>');
128
+ // The branch point, from the API rather than `git merge-base`: the audit job
129
+ // may not have checked the repository out, and a shallow clone has no history.
130
+ const mergeBaseSha = JSON.parse(gh(['api', `repos/${repo}/compare/${base}...${headSha}`])).merge_base_commit.sha;
131
+ log(`merge base with ${base}: ${short(mergeBaseSha)}`);
132
+ const runs = JSON.parse(ghRun(repo, [
133
+ 'list',
134
+ '--workflow',
135
+ workflow,
136
+ '--branch',
137
+ base,
138
+ '--limit',
139
+ String(options.candidateLimit ?? baseline_1.CANDIDATE_LIMIT),
140
+ '--json',
141
+ 'databaseId,headSha,createdAt,updatedAt,conclusion'
142
+ ]));
143
+ const candidates = runs.map((r) => ({
144
+ runId: String(r.databaseId),
145
+ headSha: r.headSha,
146
+ // `updatedAt` is when the run concluded; `createdAt` until GitHub records it.
147
+ finishedAt: r.updatedAt || r.createdAt,
148
+ conclusion: r.conclusion
149
+ }));
150
+ const ancestry = new Map();
151
+ const isAncestor = (sha) => {
152
+ if (!ancestry.has(sha)) {
153
+ // `ahead` = the merge base is ahead of the candidate, i.e. the candidate
154
+ // is in its history; `identical` = the baseline *is* the branch point, the
155
+ // ideal case. `behind`/`diverged` are the ones to refuse.
156
+ const status = JSON.parse(gh(['api', `repos/${repo}/compare/${sha}...${mergeBaseSha}`])).status;
157
+ ancestry.set(sha, status === 'ahead' || status === 'identical');
158
+ }
159
+ return ancestry.get(sha) === true;
160
+ };
161
+ const download = (run) => {
162
+ // Emptied first: successive candidates unpack into the same directory, and a
163
+ // leftover report from a rejected one would make the next candidate look
164
+ // usable and put the wrong run's numbers in the delta.
165
+ fs.rmSync(dir, { recursive: true, force: true });
166
+ try {
167
+ ghRun(repo, ['download', run.runId, '-n', artifact, '-D', dir]);
168
+ }
169
+ catch (err) {
170
+ // The one ignorable failure here: an expired or never-uploaded artifact is
171
+ // a rejected candidate, not a broken job. Anything else (auth, network)
172
+ // fails the same way on the next candidate and surfaces as "no baseline".
173
+ log(` ${run.runId}: artifact download failed — ${String(err.message).trim()}`);
174
+ return null;
175
+ }
176
+ return readableReport(path.join(dir, 'safegres.json'), (why) => log(` ${run.runId}: ${why}`));
177
+ };
178
+ const selection = (0, baseline_1.selectBaseline)(candidates, {
179
+ mergeBaseSha,
180
+ isAncestor,
181
+ download,
182
+ ...(options.now !== undefined && { now: options.now }),
183
+ maxAgeMs: options.maxAgeMs ?? baseline_1.MAX_BASELINE_AGE_MS
184
+ });
185
+ for (const r of selection.rejected) {
186
+ log(`rejected ${r.runId} (${short(r.headSha)}): ${r.reason}`);
187
+ }
188
+ if (!selection.chosen) {
189
+ log(`no comparison baseline: ${selection.reason}`);
190
+ return { ...selection, base, mergeBaseSha };
191
+ }
192
+ const age = (0, compare_1.formatAge)(selection.chosen.ageMs);
193
+ const runUrl = `${process.env.GITHUB_SERVER_URL ?? 'https://github.com'}/${repo}/actions/runs/${selection.chosen.runId}`;
194
+ log(`baseline: run ${selection.chosen.runId} ${base}@${short(selection.chosen.headSha)}, `
195
+ + `${age} old (${selection.chosen.finishedAt})`);
196
+ return { ...selection, base, mergeBaseSha, runUrl, age };
197
+ }
package/cli/audit.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { CLIOptions, Inquirerer, ParsedArgs } from 'inquirerer';
2
+ declare const _default: (argv: ParsedArgs, _prompter: Inquirerer, _options: CLIOptions) => Promise<void>;
3
+ export default _default;