@reddoorla/maintenance 0.95.1 → 0.96.0

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 (268) hide show
  1. package/README.md +37 -2
  2. package/dist/airtable-SJV4ZSMS.js +9 -0
  3. package/dist/{announce-CLQYX5I4.js → announce-45HVP4J4.js} +7 -7
  4. package/dist/{blux-4KNXUP2K.js → blux-RUFMNOUJ.js} +5 -5
  5. package/dist/{bump-deps-C5G2EFBB.js → bump-deps-EL3TKMIL.js} +6 -6
  6. package/dist/{chunk-XN37VTDB.js → chunk-37B4LVB3.js} +2 -2
  7. package/dist/{chunk-BVEDKMNG.js → chunk-3EA43S3R.js} +28 -10
  8. package/dist/chunk-3EA43S3R.js.map +1 -0
  9. package/dist/{chunk-XHHMM5HX.js → chunk-43SW3FGW.js} +63 -1
  10. package/dist/chunk-43SW3FGW.js.map +1 -0
  11. package/dist/{chunk-WMBLR5VA.js → chunk-44SDGPGQ.js} +3 -3
  12. package/dist/chunk-4UTJ4G6P.js +52 -0
  13. package/dist/chunk-4UTJ4G6P.js.map +1 -0
  14. package/dist/{chunk-R53VL3RI.js → chunk-5CJVUPJU.js} +1 -1
  15. package/dist/{chunk-R53VL3RI.js.map → chunk-5CJVUPJU.js.map} +1 -1
  16. package/dist/{chunk-KJRVMMM3.js → chunk-62YWOFVN.js} +36 -5
  17. package/dist/chunk-62YWOFVN.js.map +1 -0
  18. package/dist/{chunk-VSGMCCGQ.js → chunk-6INOBNRO.js} +2 -2
  19. package/dist/{chunk-BYGPUUCT.js → chunk-72FJZ6LO.js} +43 -11
  20. package/dist/chunk-72FJZ6LO.js.map +1 -0
  21. package/dist/{chunk-5GUJ36TL.js → chunk-74QRWE6G.js} +2 -2
  22. package/dist/chunk-A5QKNOXW.js +72 -0
  23. package/dist/chunk-A5QKNOXW.js.map +1 -0
  24. package/dist/{chunk-2NQUCS7O.js → chunk-BIYQN7FH.js} +3 -1
  25. package/dist/{chunk-2NQUCS7O.js.map → chunk-BIYQN7FH.js.map} +1 -1
  26. package/dist/{chunk-S7RYFUIK.js → chunk-CA6ADGOC.js} +2 -2
  27. package/dist/chunk-CGQZFQAY.js +38 -0
  28. package/dist/chunk-CGQZFQAY.js.map +1 -0
  29. package/dist/{chunk-YLYXW5PH.js → chunk-CXICCLNM.js} +34 -4
  30. package/dist/chunk-CXICCLNM.js.map +1 -0
  31. package/dist/{chunk-KZRR4NFG.js → chunk-D3CTOCUR.js} +3 -3
  32. package/dist/{chunk-CWXTNPNZ.js → chunk-DVJNYPFR.js} +3 -3
  33. package/dist/chunk-DVJNYPFR.js.map +1 -0
  34. package/dist/{chunk-AVEQUR44.js → chunk-EASYCD6F.js} +60 -17
  35. package/dist/chunk-EASYCD6F.js.map +1 -0
  36. package/dist/{chunk-BCKEP5S6.js → chunk-ECDJDHKT.js} +2 -2
  37. package/dist/{chunk-BI2VI6EM.js → chunk-EQQ4H6L3.js} +2 -2
  38. package/dist/{chunk-WVNDEYXL.js → chunk-GEUC5VGM.js} +11 -5
  39. package/dist/chunk-GEUC5VGM.js.map +1 -0
  40. package/dist/{chunk-Z2K4JPQ3.js → chunk-H445EH33.js} +2 -2
  41. package/dist/chunk-IHHAYXKY.js +351 -0
  42. package/dist/chunk-IHHAYXKY.js.map +1 -0
  43. package/dist/{chunk-FUWFLAWQ.js → chunk-IMYL67JV.js} +3 -3
  44. package/dist/{chunk-3Z6X3BHU.js → chunk-KN52PM63.js} +7 -2
  45. package/dist/{chunk-3Z6X3BHU.js.map → chunk-KN52PM63.js.map} +1 -1
  46. package/dist/{chunk-WJMPCVOR.js → chunk-LAJ3FF54.js} +12 -4
  47. package/dist/chunk-LAJ3FF54.js.map +1 -0
  48. package/dist/{chunk-5UINJNAJ.js → chunk-MZZDMBDT.js} +16 -3
  49. package/dist/chunk-MZZDMBDT.js.map +1 -0
  50. package/dist/{chunk-DFKBTBO3.js → chunk-NVLREB5R.js} +35 -6
  51. package/dist/chunk-NVLREB5R.js.map +1 -0
  52. package/dist/{chunk-R5IMFAFR.js → chunk-OTEVKRAH.js} +3 -3
  53. package/dist/{chunk-2ZAZ7ETT.js → chunk-P4A6D6HO.js} +4 -4
  54. package/dist/{chunk-XPJQXSC4.js → chunk-P7323YVW.js} +27 -5
  55. package/dist/chunk-P7323YVW.js.map +1 -0
  56. package/dist/{chunk-4763KQ6O.js → chunk-PXRN2MER.js} +2 -2
  57. package/dist/{chunk-TR3HFH6J.js → chunk-QFSL2L2B.js} +67 -10
  58. package/dist/chunk-QFSL2L2B.js.map +1 -0
  59. package/dist/{chunk-2LYOAB2E.js → chunk-RRE74VWI.js} +2 -2
  60. package/dist/{chunk-LM2NHRCJ.js → chunk-SHGIDTYF.js} +4 -4
  61. package/dist/chunk-SHGIDTYF.js.map +1 -0
  62. package/dist/{chunk-ZQV7UQNE.js → chunk-SQ4FSNYL.js} +2 -2
  63. package/dist/{chunk-C3IQQKXL.js → chunk-T76MA2FH.js} +110 -1
  64. package/dist/{chunk-C3IQQKXL.js.map → chunk-T76MA2FH.js.map} +1 -1
  65. package/dist/{chunk-6R56OKJZ.js → chunk-UIITN37J.js} +2 -2
  66. package/dist/{chunk-CJ2RMTT7.js → chunk-VEIIUCYL.js} +2 -2
  67. package/dist/{chunk-SVAAONYB.js → chunk-WQLNUEZL.js} +12 -2
  68. package/dist/{chunk-SVAAONYB.js.map → chunk-WQLNUEZL.js.map} +1 -1
  69. package/dist/{chunk-WSJ7RO54.js → chunk-XED2DNAI.js} +23 -5
  70. package/dist/chunk-XED2DNAI.js.map +1 -0
  71. package/dist/{chunk-FSGICFXB.js → chunk-XG2A6GIG.js} +17 -5
  72. package/dist/chunk-XG2A6GIG.js.map +1 -0
  73. package/dist/{chunk-TO7DJYLZ.js → chunk-XY7YV2YQ.js} +178 -39
  74. package/dist/chunk-XY7YV2YQ.js.map +1 -0
  75. package/dist/{chunk-HTABUCF4.js → chunk-Y5BVR4HW.js} +3 -3
  76. package/dist/{chunk-ZCL7C5LH.js → chunk-Y6FCXRIC.js} +1661 -387
  77. package/dist/chunk-Y6FCXRIC.js.map +1 -0
  78. package/dist/{chunk-2SLED3NB.js → chunk-YDZ7XGPZ.js} +1 -1
  79. package/dist/{chunk-2SLED3NB.js.map → chunk-YDZ7XGPZ.js.map} +1 -1
  80. package/dist/{chunk-QF6IQQFP.js → chunk-Z7FDJ35L.js} +18 -12
  81. package/dist/chunk-Z7FDJ35L.js.map +1 -0
  82. package/dist/{chunk-5EEMTJMT.js → chunk-ZH5LOQET.js} +4 -4
  83. package/dist/cli/bin.js +49 -31
  84. package/dist/cli/bin.js.map +1 -1
  85. package/dist/cli/commands/audit.js +18 -18
  86. package/dist/client-L7NAUG6J.js +11 -0
  87. package/dist/configs/playwright-a11y.d.ts +11 -1
  88. package/dist/configs/playwright-a11y.js +3 -1
  89. package/dist/{convert-to-pnpm-YTFREUJK.js → convert-to-pnpm-4DON7IFS.js} +6 -6
  90. package/dist/{db-XKZYDCKM.js → db-5K4CDEGJ.js} +45 -20
  91. package/dist/db-5K4CDEGJ.js.map +1 -0
  92. package/dist/deadletter-G4M3HSVM.js +17 -0
  93. package/dist/{digest-7UBGUBRK.js → digest-5AJGWS33.js} +45 -22
  94. package/dist/digest-5AJGWS33.js.map +1 -0
  95. package/dist/{digest-collectors-DJFN5ZDK.js → digest-collectors-MC5NWEUA.js} +12 -10
  96. package/dist/{digest-state-3KHZF7GD.js → digest-state-ZDPKR7MW.js} +18 -2
  97. package/dist/digest-state-ZDPKR7MW.js.map +1 -0
  98. package/dist/{ensure-site-OAAE5YE2.js → ensure-site-XESZHZEB.js} +39 -11
  99. package/dist/ensure-site-XESZHZEB.js.map +1 -0
  100. package/dist/{fleet-state-UEVZ3X3J.js → fleet-state-GJWGKTLD.js} +6 -4
  101. package/dist/{forms-notify-target-UC6PF3AE.js → forms-notify-target-4NPLX2UG.js} +5 -5
  102. package/dist/{freeze-A6MUCZU4.js → freeze-GUQ43EAU.js} +2 -2
  103. package/dist/{github-signals-TX5FPYP6.js → github-signals-DMU74SAX.js} +10 -10
  104. package/dist/{header-image-PSPT7CUM.js → header-image-BRTOLHJF.js} +6 -5
  105. package/dist/header-image-BRTOLHJF.js.map +1 -0
  106. package/dist/{health-endpoint-KVCJYWET.js → health-endpoint-UCLMSSCN.js} +8 -7
  107. package/dist/{health-endpoint-KVCJYWET.js.map → health-endpoint-UCLMSSCN.js.map} +1 -1
  108. package/dist/{health-mirror-XJAKA3EY.js → health-mirror-ONRT4DQ5.js} +7 -7
  109. package/dist/{import-airtable-TAKSN4NV.js → import-airtable-IIK2QUUF.js} +3 -3
  110. package/dist/index.d.ts +135 -4
  111. package/dist/index.js +68 -34
  112. package/dist/index.js.map +1 -1
  113. package/dist/{init-FFE4FCO2.js → init-3LMZTCVD.js} +19 -18
  114. package/dist/{init-FFE4FCO2.js.map → init-3LMZTCVD.js.map} +1 -1
  115. package/dist/{launch-UD36AHUN.js → launch-XTDMC6LD.js} +209 -61
  116. package/dist/launch-XTDMC6LD.js.map +1 -0
  117. package/dist/{lighthouse-VHI77PFB.js → lighthouse-52WSPZ5D.js} +2 -2
  118. package/dist/{lighthouse-airtable-YMH4UA7R.js → lighthouse-airtable-3NOFB42B.js} +3 -3
  119. package/dist/{match-harness-QRG6XZAR.js → match-harness-OW45CB5H.js} +14 -7
  120. package/dist/match-harness-OW45CB5H.js.map +1 -0
  121. package/dist/migrate-NE3DUMTM.js +7 -0
  122. package/dist/{notify-IISTHNBR.js → notify-K3WVH3DM.js} +3 -3
  123. package/dist/{onboard-TM2KESGF.js → onboard-CZYBWVWH.js} +6 -6
  124. package/dist/{orchestrate-HYVQPRCE.js → orchestrate-TGDFGY52.js} +8 -8
  125. package/dist/{parity-CAJ477PS.js → parity-AEKYSLN6.js} +5 -5
  126. package/dist/{pipeline-YROAY6DT.js → pipeline-UUPXOS2J.js} +3 -3
  127. package/dist/{preflight-RYIP54NY.js → preflight-POZZI3GN.js} +9 -9
  128. package/dist/{prismic-ci-6GKKZGB5.js → prismic-ci-AHY5ULJR.js} +38 -8
  129. package/dist/prismic-ci-AHY5ULJR.js.map +1 -0
  130. package/dist/{prismic-models-P6FWWVRS.js → prismic-models-URYGODNY.js} +9 -9
  131. package/dist/prospect/types.d.ts +72 -0
  132. package/dist/prospect/types.js +1 -1
  133. package/dist/{prospect-audit-PRHU53HS.js → prospect-audit-AJEE3Z4L.js} +16 -7
  134. package/dist/prospect-audit-AJEE3Z4L.js.map +1 -0
  135. package/dist/{prospect-audits-R3LWF6U7.js → prospect-audits-IZVQY3OQ.js} +4 -2
  136. package/dist/protection-audit-3J3UEOVT.js +413 -0
  137. package/dist/protection-audit-3J3UEOVT.js.map +1 -0
  138. package/dist/recipes/sync-configs.js +3 -3
  139. package/dist/{render-YGNGTX3W.js → render-MHUUHPLN.js} +36 -9
  140. package/dist/render-MHUUHPLN.js.map +1 -0
  141. package/dist/{render-from-row-IFSBHWDH.js → render-from-row-65DC6ZP3.js} +4 -4
  142. package/dist/{renovate-dispatch-U5Q52SKT.js → renovate-dispatch-ONJMBUHJ.js} +3 -3
  143. package/dist/{replay-PCSQOGCZ.js → replay-4FVFOMHH.js} +10 -23
  144. package/dist/replay-4FVFOMHH.js.map +1 -0
  145. package/dist/{report-LQS4ECTM.js → report-RC5LGPU3.js} +22 -20
  146. package/dist/report-RC5LGPU3.js.map +1 -0
  147. package/dist/{report-mirror-HDTOJE64.js → report-mirror-KKZZ6RMZ.js} +13 -9
  148. package/dist/{report-mirror-HDTOJE64.js.map → report-mirror-KKZZ6RMZ.js.map} +1 -1
  149. package/dist/{schema-A4WCPIF4.js → schema-KWUQEZ5O.js} +1 -1
  150. package/dist/schema-KWUQEZ5O.js.map +1 -0
  151. package/dist/{self-updating-MWXIZLL7.js → self-updating-RKKIYNV4.js} +8 -7
  152. package/dist/{self-updating-MWXIZLL7.js.map → self-updating-RKKIYNV4.js.map} +1 -1
  153. package/dist/{selftest-VXTHRPRN.js → selftest-AFLR7YV7.js} +10 -10
  154. package/dist/site-lookup-DTOLJ4EA.js +27 -0
  155. package/dist/site-lookup-DTOLJ4EA.js.map +1 -0
  156. package/dist/{site-mirror-WON4FTJ2.js → site-mirror-H2FYMN55.js} +23 -9
  157. package/dist/site-mirror-H2FYMN55.js.map +1 -0
  158. package/dist/{smoke-suite-7DGITCTZ.js → smoke-suite-BWHR3XX5.js} +8 -7
  159. package/dist/{smoke-suite-7DGITCTZ.js.map → smoke-suite-BWHR3XX5.js.map} +1 -1
  160. package/dist/{submissions-OVZPVBYG.js → submissions-II5J35QM.js} +4 -4
  161. package/dist/{submissions-3I3GAQAX.js → submissions-JDZP35JM.js} +49 -9
  162. package/dist/submissions-JDZP35JM.js.map +1 -0
  163. package/dist/{svelte-codemods-WSV2FGNG.js → svelte-codemods-QWGIRNN6.js} +6 -6
  164. package/dist/{sync-K3PKHVLH.js → sync-NKPDCMYW.js} +5 -5
  165. package/dist/{sync-configs-RRIHNLIJ.js → sync-configs-HGLQV55H.js} +6 -6
  166. package/dist/{upgrade-VTD6V4HS.js → upgrade-WZ44IARG.js} +6 -6
  167. package/dist/util/git.d.ts +48 -9
  168. package/dist/util/git.js +1 -1
  169. package/dist/{webflow-IRKOIT7S.js → webflow-624FEBFD.js} +4 -4
  170. package/dist/{websites-TB4TCAWM.js → websites-6DHIHJ2D.js} +4 -2
  171. package/dist/{write-audits-to-airtable-53EVXKMU.js → write-audits-to-airtable-BIK6LNVC.js} +5 -5
  172. package/dist/write-audits-to-airtable-BIK6LNVC.js.map +1 -0
  173. package/package.json +3 -2
  174. package/dist/airtable-JGCPS7JH.js +0 -9
  175. package/dist/chunk-5UINJNAJ.js.map +0 -1
  176. package/dist/chunk-AVEQUR44.js.map +0 -1
  177. package/dist/chunk-BVEDKMNG.js.map +0 -1
  178. package/dist/chunk-BYGPUUCT.js.map +0 -1
  179. package/dist/chunk-CWXTNPNZ.js.map +0 -1
  180. package/dist/chunk-DFKBTBO3.js.map +0 -1
  181. package/dist/chunk-FSGICFXB.js.map +0 -1
  182. package/dist/chunk-GASBX52O.js +0 -12
  183. package/dist/chunk-GASBX52O.js.map +0 -1
  184. package/dist/chunk-KJRVMMM3.js.map +0 -1
  185. package/dist/chunk-LM2NHRCJ.js.map +0 -1
  186. package/dist/chunk-QF6IQQFP.js.map +0 -1
  187. package/dist/chunk-TO7DJYLZ.js.map +0 -1
  188. package/dist/chunk-TR3HFH6J.js.map +0 -1
  189. package/dist/chunk-WJMPCVOR.js.map +0 -1
  190. package/dist/chunk-WSJ7RO54.js.map +0 -1
  191. package/dist/chunk-WVNDEYXL.js.map +0 -1
  192. package/dist/chunk-XHHMM5HX.js.map +0 -1
  193. package/dist/chunk-XPJQXSC4.js.map +0 -1
  194. package/dist/chunk-YLYXW5PH.js.map +0 -1
  195. package/dist/chunk-ZCL7C5LH.js.map +0 -1
  196. package/dist/client-G2WGWPCA.js +0 -11
  197. package/dist/db-XKZYDCKM.js.map +0 -1
  198. package/dist/digest-7UBGUBRK.js.map +0 -1
  199. package/dist/digest-state-3KHZF7GD.js.map +0 -1
  200. package/dist/ensure-site-OAAE5YE2.js.map +0 -1
  201. package/dist/header-image-PSPT7CUM.js.map +0 -1
  202. package/dist/launch-UD36AHUN.js.map +0 -1
  203. package/dist/match-harness-QRG6XZAR.js.map +0 -1
  204. package/dist/migrate-Y7R4CESL.js +0 -7
  205. package/dist/prismic-ci-6GKKZGB5.js.map +0 -1
  206. package/dist/prospect-audit-PRHU53HS.js.map +0 -1
  207. package/dist/protection-audit-SJO53MEQ.js +0 -168
  208. package/dist/protection-audit-SJO53MEQ.js.map +0 -1
  209. package/dist/render-YGNGTX3W.js.map +0 -1
  210. package/dist/replay-PCSQOGCZ.js.map +0 -1
  211. package/dist/report-LQS4ECTM.js.map +0 -1
  212. package/dist/schema-A4WCPIF4.js.map +0 -1
  213. package/dist/site-mirror-WON4FTJ2.js.map +0 -1
  214. package/dist/submissions-3I3GAQAX.js.map +0 -1
  215. /package/dist/{airtable-JGCPS7JH.js.map → airtable-SJV4ZSMS.js.map} +0 -0
  216. /package/dist/{announce-CLQYX5I4.js.map → announce-45HVP4J4.js.map} +0 -0
  217. /package/dist/{blux-4KNXUP2K.js.map → blux-RUFMNOUJ.js.map} +0 -0
  218. /package/dist/{bump-deps-C5G2EFBB.js.map → bump-deps-EL3TKMIL.js.map} +0 -0
  219. /package/dist/{chunk-XN37VTDB.js.map → chunk-37B4LVB3.js.map} +0 -0
  220. /package/dist/{chunk-WMBLR5VA.js.map → chunk-44SDGPGQ.js.map} +0 -0
  221. /package/dist/{chunk-VSGMCCGQ.js.map → chunk-6INOBNRO.js.map} +0 -0
  222. /package/dist/{chunk-5GUJ36TL.js.map → chunk-74QRWE6G.js.map} +0 -0
  223. /package/dist/{chunk-S7RYFUIK.js.map → chunk-CA6ADGOC.js.map} +0 -0
  224. /package/dist/{chunk-KZRR4NFG.js.map → chunk-D3CTOCUR.js.map} +0 -0
  225. /package/dist/{chunk-BCKEP5S6.js.map → chunk-ECDJDHKT.js.map} +0 -0
  226. /package/dist/{chunk-BI2VI6EM.js.map → chunk-EQQ4H6L3.js.map} +0 -0
  227. /package/dist/{chunk-Z2K4JPQ3.js.map → chunk-H445EH33.js.map} +0 -0
  228. /package/dist/{chunk-FUWFLAWQ.js.map → chunk-IMYL67JV.js.map} +0 -0
  229. /package/dist/{chunk-R5IMFAFR.js.map → chunk-OTEVKRAH.js.map} +0 -0
  230. /package/dist/{chunk-2ZAZ7ETT.js.map → chunk-P4A6D6HO.js.map} +0 -0
  231. /package/dist/{chunk-4763KQ6O.js.map → chunk-PXRN2MER.js.map} +0 -0
  232. /package/dist/{chunk-2LYOAB2E.js.map → chunk-RRE74VWI.js.map} +0 -0
  233. /package/dist/{chunk-ZQV7UQNE.js.map → chunk-SQ4FSNYL.js.map} +0 -0
  234. /package/dist/{chunk-6R56OKJZ.js.map → chunk-UIITN37J.js.map} +0 -0
  235. /package/dist/{chunk-CJ2RMTT7.js.map → chunk-VEIIUCYL.js.map} +0 -0
  236. /package/dist/{chunk-HTABUCF4.js.map → chunk-Y5BVR4HW.js.map} +0 -0
  237. /package/dist/{chunk-5EEMTJMT.js.map → chunk-ZH5LOQET.js.map} +0 -0
  238. /package/dist/{client-G2WGWPCA.js.map → client-L7NAUG6J.js.map} +0 -0
  239. /package/dist/{convert-to-pnpm-YTFREUJK.js.map → convert-to-pnpm-4DON7IFS.js.map} +0 -0
  240. /package/dist/{digest-collectors-DJFN5ZDK.js.map → deadletter-G4M3HSVM.js.map} +0 -0
  241. /package/dist/{fleet-state-UEVZ3X3J.js.map → digest-collectors-MC5NWEUA.js.map} +0 -0
  242. /package/dist/{freeze-A6MUCZU4.js.map → fleet-state-GJWGKTLD.js.map} +0 -0
  243. /package/dist/{forms-notify-target-UC6PF3AE.js.map → forms-notify-target-4NPLX2UG.js.map} +0 -0
  244. /package/dist/{import-airtable-TAKSN4NV.js.map → freeze-GUQ43EAU.js.map} +0 -0
  245. /package/dist/{github-signals-TX5FPYP6.js.map → github-signals-DMU74SAX.js.map} +0 -0
  246. /package/dist/{health-mirror-XJAKA3EY.js.map → health-mirror-ONRT4DQ5.js.map} +0 -0
  247. /package/dist/{lighthouse-VHI77PFB.js.map → import-airtable-IIK2QUUF.js.map} +0 -0
  248. /package/dist/{lighthouse-airtable-YMH4UA7R.js.map → lighthouse-52WSPZ5D.js.map} +0 -0
  249. /package/dist/{migrate-Y7R4CESL.js.map → lighthouse-airtable-3NOFB42B.js.map} +0 -0
  250. /package/dist/{notify-IISTHNBR.js.map → migrate-NE3DUMTM.js.map} +0 -0
  251. /package/dist/{orchestrate-HYVQPRCE.js.map → notify-K3WVH3DM.js.map} +0 -0
  252. /package/dist/{onboard-TM2KESGF.js.map → onboard-CZYBWVWH.js.map} +0 -0
  253. /package/dist/{parity-CAJ477PS.js.map → orchestrate-TGDFGY52.js.map} +0 -0
  254. /package/dist/{pipeline-YROAY6DT.js.map → parity-AEKYSLN6.js.map} +0 -0
  255. /package/dist/{prospect-audits-R3LWF6U7.js.map → pipeline-UUPXOS2J.js.map} +0 -0
  256. /package/dist/{preflight-RYIP54NY.js.map → preflight-POZZI3GN.js.map} +0 -0
  257. /package/dist/{prismic-models-P6FWWVRS.js.map → prismic-models-URYGODNY.js.map} +0 -0
  258. /package/dist/{render-from-row-IFSBHWDH.js.map → prospect-audits-IZVQY3OQ.js.map} +0 -0
  259. /package/dist/{websites-TB4TCAWM.js.map → render-from-row-65DC6ZP3.js.map} +0 -0
  260. /package/dist/{renovate-dispatch-U5Q52SKT.js.map → renovate-dispatch-ONJMBUHJ.js.map} +0 -0
  261. /package/dist/{selftest-VXTHRPRN.js.map → selftest-AFLR7YV7.js.map} +0 -0
  262. /package/dist/{submissions-OVZPVBYG.js.map → submissions-II5J35QM.js.map} +0 -0
  263. /package/dist/{svelte-codemods-WSV2FGNG.js.map → svelte-codemods-QWGIRNN6.js.map} +0 -0
  264. /package/dist/{sync-K3PKHVLH.js.map → sync-NKPDCMYW.js.map} +0 -0
  265. /package/dist/{sync-configs-RRIHNLIJ.js.map → sync-configs-HGLQV55H.js.map} +0 -0
  266. /package/dist/{upgrade-VTD6V4HS.js.map → upgrade-WZ44IARG.js.map} +0 -0
  267. /package/dist/{webflow-IRKOIT7S.js.map → webflow-624FEBFD.js.map} +0 -0
  268. /package/dist/{write-audits-to-airtable-53EVXKMU.js.map → websites-6DHIHJ2D.js.map} +0 -0
@@ -109,7 +109,7 @@ async function runProspectAuditCommand(url, opts) {
109
109
  "No TURSO_DATABASE_URL, so the report cannot be saved or shared. Re-run with --out <file>, or set the Turso credentials."
110
110
  );
111
111
  }
112
- const { runProspectAudit } = await import("./pipeline-YROAY6DT.js");
112
+ const { runProspectAudit } = await import("./pipeline-UUPXOS2J.js");
113
113
  const onStage = (name, status, detail) => {
114
114
  if (status === "start") console.error(`\u2026 ${name}`);
115
115
  else if (status === "ok") console.error(`\u2713 ${name}`);
@@ -117,6 +117,9 @@ async function runProspectAuditCommand(url, opts) {
117
117
  };
118
118
  const business = opts.business?.trim();
119
119
  const goal = opts.goal?.trim();
120
+ const splitList = (v) => (v ?? "").split(",").map((x) => x.trim()).filter((x) => x !== "");
121
+ const chosenTerms = splitList(opts.terms);
122
+ const chosenQuestions = splitList(opts.questions);
120
123
  if (goal !== void 0 && goal !== "" && !isSiteGoal(goal)) {
121
124
  return fail(`--goal must be one of: ${GOALS.join(", ")}`);
122
125
  }
@@ -131,11 +134,13 @@ async function runProspectAuditCommand(url, opts) {
131
134
  ...opts.competitors ? {
132
135
  competitors: opts.competitors.split(",").map((c) => c.trim()).filter(Boolean)
133
136
  } : {},
134
- ...opts.probes === false ? { probes: false } : {}
137
+ ...opts.probes === false ? { probes: false } : {},
138
+ ...chosenTerms.length > 0 ? { terms: chosenTerms } : {},
139
+ ...chosenQuestions.length > 0 ? { questions: chosenQuestions } : {}
135
140
  },
136
141
  { ...opts.deps ?? {}, onStage }
137
142
  );
138
- const { renderProspectReport } = await import("./render-YGNGTX3W.js");
143
+ const { renderProspectReport } = await import("./render-MHUUHPLN.js");
139
144
  const html = renderProspectReport(result);
140
145
  const warnings = [];
141
146
  let file = null;
@@ -152,8 +157,8 @@ async function runProspectAuditCommand(url, opts) {
152
157
  let auditId = null;
153
158
  if (canPersist) {
154
159
  try {
155
- const { openDb, readDbConfig } = await import("./client-G2WGWPCA.js");
156
- const { createProspectAudit } = await import("./prospect-audits-R3LWF6U7.js");
160
+ const { openDb, readDbConfig } = await import("./client-L7NAUG6J.js");
161
+ const { createProspectAudit } = await import("./prospect-audits-IZVQY3OQ.js");
157
162
  const db = await openDb(readDbConfig());
158
163
  const created = await createProspectAudit(db, {
159
164
  url: result.url,
@@ -163,7 +168,11 @@ async function runProspectAuditCommand(url, opts) {
163
168
  // renaming a column needs its own migration for no benefit here.
164
169
  business: result.businessName,
165
170
  status: auditStatus(result),
166
- resultJson: JSON.stringify(result)
171
+ resultJson: JSON.stringify(result),
172
+ // #676: on the ROW, not only in result_json — a re-run reuses them and
173
+ // the /audits listing marks them without reading the blob.
174
+ chosenTerms: chosenTerms.length > 0 ? chosenTerms : null,
175
+ chosenQuestions: chosenQuestions.length > 0 ? chosenQuestions : null
167
176
  });
168
177
  auditId = created.id;
169
178
  token = created.token;
@@ -213,4 +222,4 @@ export {
213
222
  GOALS,
214
223
  runProspectAuditCommand
215
224
  };
216
- //# sourceMappingURL=prospect-audit-PRHU53HS.js.map
225
+ //# sourceMappingURL=prospect-audit-AJEE3Z4L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/prospect-audit.ts","../src/prospect/report-url.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { hostnameOf, isHttpUrl, isPrivateOrLoopbackHost } from \"../../util/url.js\";\nimport { reportUrl, reportPrintUrl } from \"../../prospect/report-url.js\";\nimport type { ProspectAuditStatus } from \"../../db/prospect-audits.js\";\nimport type { PipelineDeps, StageName } from \"../../prospect/pipeline.js\";\nimport type { ProspectAuditResult } from \"../../prospect/types.js\";\nimport type { SendAuditEmailResult } from \"../../prospect/email.js\";\nimport type { SiteGoal } from \"../../prospect/goals.js\";\n\n/** The operator-selectable goals, in the order the dispatch dropdown lists\n * them. `unknown` is deliberately absent: it is a finding the audit can reach\n * on its own, never something an operator would choose. */\nexport const GOALS = [\n \"book\",\n \"enquire\",\n \"call\",\n \"visit\",\n \"buy\",\n \"demo\",\n \"partner\",\n] as const satisfies readonly SiteGoal[];\n\nfunction isSiteGoal(value: string): value is SiteGoal {\n return (GOALS as readonly string[]).includes(value);\n}\n\nexport type ProspectAuditCliOptions = {\n business?: string;\n /**\n * The one action the site should produce from a visitor, chosen by the\n * operator instead of inferred from the site.\n *\n * Worth overriding because the operator knows things the site does not say.\n * A practice that wants online bookings but has never built one reads as\n * \"call\" from its own pages, and grading it as \"call\" would score it well for\n * doing the thing they are trying to stop doing.\n */\n goal?: string;\n /** Comma-separated competitor domains. */\n competitors?: string;\n /** cac sets this false for `--no-probes`. */\n probes?: boolean;\n out?: string;\n json?: boolean;\n /**\n * Email the internal sheet (scores, what wasn't measured and why, top\n * fixes, and the shareable link) to `PROSPECT_AUDIT_RECIPIENTS`, once the\n * report is rendered and the `--out`/Turso delivery attempts finish. A\n * missing/empty recipients var, or a Resend failure, is a warning — never a\n * crash — because by the time this runs the audit is already delivered (or\n * recovered) by the code above it.\n */\n email?: boolean;\n /**\n * #676. Search terms and buyer questions chosen by hand, comma-separated.\n *\n * Blank generates them, exactly as before — this is an override for a client\n * we already know, and for a comparison over time, where the terms have to\n * stay fixed between runs.\n */\n terms?: string;\n questions?: string;\n /** Test seam: injected pipeline deps. Never set from the CLI. */\n deps?: PipelineDeps;\n};\n\ntype RecoveryWrite = { htmlPath: string; jsonPath: string };\n\nfunction fail(message: string): { output: string; code: number } {\n return { output: message, code: 2 };\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction scoreLine(label: string, value: number | null): string {\n return `${label.padEnd(14)} ${value === null ? \"not measured\" : String(value).padStart(3)}`;\n}\n\n/** Item 3: the `status` column exists to answer, without deserializing the\n * whole result_json, whether this was a clean run or a degraded one. crawl\n * is excluded — it's the one fatal stage (see pipeline.ts), so by the time a\n * ProspectAuditResult exists it was always ok. Every other stage's\n * StageResult already collapses \"failed\" and \"deliberately skipped\"\n * (--no-probes, the checks→analyze cascade) into the same `ok: false` — both\n * mean the report has a \"not measured\" section, so both count as partial\n * here too. */\nfunction auditStatus(result: ProspectAuditResult): ProspectAuditStatus {\n const allStagesOk =\n result.checks.ok && result.lighthouse.ok && result.analyze.ok && result.probes.ok;\n return allStagesOk ? \"complete\" : \"partial\";\n}\n\n/** Filesystem-safe, never empty. Collapses anything outside [a-z0-9.-] to a\n * single dash so a hostname becomes a sane filename fragment. */\nfunction slugify(s: string): string {\n const slug = s\n .toLowerCase()\n .replace(/[^a-z0-9.-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return slug || \"prospect\";\n}\n\n/**\n * Last-resort write when NEITHER `--out` nor the database persist made it —\n * the one situation a paid, already-run audit must never end in silently. Lands\n * in `reports/`, mirroring header-image.ts's own convention for CLI-written\n * artifacts (mkdir -p, resolved against cwd), rather than a bare file dropped\n * into whatever directory the operator happened to be standing in when they\n * ran the command — often unrelated to this repo at all in fleet-tool usage.\n */\nasync function writeRecoveryFiles(\n result: ProspectAuditResult,\n html: string,\n): Promise<RecoveryWrite> {\n const dir = resolve(\"reports\");\n await mkdir(dir, { recursive: true });\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const base = `prospect-audit-${slugify(hostnameOf(result.url))}-${stamp}`;\n const htmlPath = resolve(dir, `${base}.html`);\n const jsonPath = resolve(dir, `${base}.json`);\n await writeFile(htmlPath, html, \"utf-8\");\n await writeFile(jsonPath, JSON.stringify(result, null, 2), \"utf-8\");\n return { htmlPath, jsonPath };\n}\n\nfunction summarize(\n result: ProspectAuditResult,\n link: string | null,\n file: string | null,\n warnings: string[],\n recovery: RecoveryWrite | null,\n email: SendAuditEmailResult | null,\n): string {\n const lines = [\n `Prospect audit — ${result.businessName ?? result.url}`,\n \"\",\n scoreLine(\"Findability\", result.scores.findability),\n scoreLine(\"Readability\", result.scores.readability),\n scoreLine(\"Answers\", result.scores.answers),\n // No AI Visibility line: the score was removed from every presentation\n // surface (web, print, email, here) — the measurement lives on as the\n // report's receipts section, not as a number that reads as ours to move.\n ];\n for (const [name, stage] of [\n [\"checks\", result.checks],\n [\"lighthouse\", result.lighthouse],\n [\"analyze\", result.analyze],\n [\"probes\", result.probes],\n ] as const) {\n if (!stage.ok) lines.push(` ! ${name} not measured — ${stage.error}`);\n }\n if (file) lines.push(\"\", `Report written to ${file}`);\n if (link) lines.push(\"\", `Shareable link: ${link}`);\n if (email?.sent) {\n lines.push(\"\", `Emailed to ${email.recipients.join(\", \")} (${email.messageId})`);\n }\n for (const w of warnings) lines.push(\"\", `! ${w}`);\n if (recovery) {\n lines.push(\n \"\",\n \"Neither --out nor the database took this report — wrote a recovery copy instead:\",\n ` ${recovery.htmlPath}`,\n ` ${recovery.jsonPath}`,\n );\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Run one prospect audit end to end. Progress goes to stderr so `--json` stdout\n * stays pipeable. Persistence needs Turso; without it `--out` is mandatory,\n * because an audit nobody can read afterwards is just a bill.\n *\n * By the time `runProspectAudit` resolves, the money is already spent — only\n * the crawl is fatal, every other stage degrades rather than throwing. So the\n * `--out` write and the database persist below are each wrapped in their own\n * try/catch and attempted independently: a bad `--out` path must not cost a\n * working Turso persist (or vice versa), and if BOTH fail — or persist fails\n * with no `--out` given at all — a recovery copy goes to `reports/` rather\n * than the run ending with nothing anywhere. `--json` only changes the SHAPE\n * of stdout; it still runs the same `--out`/persist/recovery logic beneath it.\n */\nexport async function runProspectAuditCommand(\n url: string,\n opts: ProspectAuditCliOptions,\n): Promise<{ output: string; code: number }> {\n if (!isHttpUrl(url)) {\n return fail(`\"${url}\" is not a URL. Pass the full address, e.g. https://example.com`);\n }\n // #612 review: this validated only the SHAPE. The sole private-address guard\n // was in `triggerProspectAudit` — one layer at the far end of the chain — so\n // anyone running the CLI directly, or any future second caller, had none.\n // `crawlSite` now refuses too; this keeps the refusal a clean message instead\n // of a thrown crawl error.\n if (isPrivateOrLoopbackHost(new URL(url).hostname)) {\n return fail(`\"${url}\" is a private or loopback address — refusing to audit it.`);\n }\n const canPersist = Boolean(process.env.TURSO_DATABASE_URL);\n if (!canPersist && !opts.out) {\n return fail(\n \"No TURSO_DATABASE_URL, so the report cannot be saved or shared. Re-run with --out <file>, or set the Turso credentials.\",\n );\n }\n\n const { runProspectAudit } = await import(\"../../prospect/pipeline.js\");\n const onStage = (name: StageName, status: \"start\" | \"ok\" | \"fail\", detail?: string): void => {\n if (status === \"start\") console.error(`… ${name}`);\n else if (status === \"ok\") console.error(`✓ ${name}`);\n else console.error(`! ${name} — ${detail ?? \"failed\"}`);\n };\n\n const business = opts.business?.trim();\n const goal = opts.goal?.trim();\n // #676. Comma-separated on a flag (a newline is not typeable in one); the\n // cockpit's textareas split on newlines instead. Both drop blanks, and both\n // treat \"nothing usable\" as \"generate\".\n const splitList = (v: string | undefined): string[] =>\n (v ?? \"\")\n .split(\",\")\n .map((x) => x.trim())\n .filter((x) => x !== \"\");\n const chosenTerms = splitList(opts.terms);\n const chosenQuestions = splitList(opts.questions);\n if (goal !== undefined && goal !== \"\" && !isSiteGoal(goal)) {\n return fail(`--goal must be one of: ${GOALS.join(\", \")}`);\n }\n\n const result = await runProspectAudit(\n url,\n {\n ...(business ? { business } : {}),\n // An empty string is what a blank dispatch field sends; it must read as\n // \"not supplied\" so the model's own inference still runs, rather than\n // overriding it with nothing.\n ...(goal ? { goal } : {}),\n ...(opts.competitors\n ? {\n competitors: opts.competitors\n .split(\",\")\n .map((c) => c.trim())\n .filter(Boolean),\n }\n : {}),\n ...(opts.probes === false ? { probes: false } : {}),\n ...(chosenTerms.length > 0 ? { terms: chosenTerms } : {}),\n ...(chosenQuestions.length > 0 ? { questions: chosenQuestions } : {}),\n },\n { ...(opts.deps ?? {}), onStage },\n );\n\n const { renderProspectReport } = await import(\"../../prospect/render.js\");\n const html = renderProspectReport(result);\n\n const warnings: string[] = [];\n\n let file: string | null = null;\n if (opts.out) {\n try {\n await writeFile(opts.out, html, \"utf-8\");\n file = opts.out;\n } catch (err) {\n warnings.push(`Could not write --out (${opts.out}): ${errorMessage(err)}`);\n }\n }\n\n let link: string | null = null;\n let token: string | null = null;\n let auditId: string | null = null;\n if (canPersist) {\n try {\n const { openDb, readDbConfig } = await import(\"../../db/client.js\");\n const { createProspectAudit } = await import(\"../../db/prospect-audits.js\");\n const db = await openDb(readDbConfig());\n const created = await createProspectAudit(db, {\n url: result.url,\n // Map at the boundary: ProspectAuditResult.businessName is the field\n // name (Item 2 — it's a resolved NAME, not a description); the\n // `prospect_audits.business` column keeps its existing name, since\n // renaming a column needs its own migration for no benefit here.\n business: result.businessName,\n status: auditStatus(result),\n resultJson: JSON.stringify(result),\n // #676: on the ROW, not only in result_json — a re-run reuses them and\n // the /audits listing marks them without reading the blob.\n chosenTerms: chosenTerms.length > 0 ? chosenTerms : null,\n chosenQuestions: chosenQuestions.length > 0 ? chosenQuestions : null,\n });\n auditId = created.id;\n token = created.token;\n link = reportUrl(token);\n } catch (err) {\n warnings.push(`Could not save to the database: ${errorMessage(err)}`);\n }\n }\n\n const delivered = file !== null || link !== null;\n const recovery = delivered ? null : await writeRecoveryFiles(result, html);\n const code = delivered ? 0 : 1;\n\n // The audit is already delivered (or recovered) above — an email failure\n // must never take that away. Same independent try/catch shape as the\n // --out write and the Turso persist: record a warning, keep going, never\n // touch `code`. Dynamically imported for the same reason db/client.js and\n // prospect/render.js are above: it reaches `resend` (a devDependency) and\n // must stay out of bin.js's static import graph (see bin.ts's\n // central-dep-blocker comment).\n // The PDF leave-behind, printed from the website's print route — a document\n // designed for paper, not the interactive report with its evidence folded\n // away behind disclosures.\n //\n // Best-effort by design. Every other stage in this pipeline degrades rather\n // than throwing, and an attachment is not worth losing a delivered report\n // over: if this fails the email still goes, with the link, and a warning says\n // the PDF is missing. It also needs a token — with no persisted report there\n // is no page to print.\n let pdf: Buffer | null = null;\n if (opts.email && token) {\n try {\n const { renderReportPdf } = await import(\"../../prospect/pdf.js\");\n pdf = await renderReportPdf(reportPrintUrl(token));\n } catch (err) {\n warnings.push(`Could not attach the PDF: ${errorMessage(err)}`);\n }\n }\n\n let email: SendAuditEmailResult | null = null;\n if (opts.email) {\n try {\n const { sendAuditEmail, parseProspectAuditRecipients } =\n await import(\"../../prospect/email.js\");\n const recipients = parseProspectAuditRecipients(process.env.PROSPECT_AUDIT_RECIPIENTS);\n email = await sendAuditEmail(result, { link, recipients, auditId, pdf });\n if (!email.sent) {\n warnings.push(`Could not email the audit sheet: ${email.reason}`);\n }\n } catch (err) {\n warnings.push(`Could not email the audit sheet: ${errorMessage(err)}`);\n }\n }\n\n if (opts.json) {\n return {\n output: JSON.stringify(\n { result, out: file, link, token, recovery, warnings, email },\n null,\n 2,\n ),\n code,\n };\n }\n\n return { output: summarize(result, link, file, warnings, recovery, email), code };\n}\n","/** Where the prospect-facing report lives now: a real, branded route on the\n * marketing site rather than generated HTML on the ops app's domain. */\nexport const DEFAULT_REPORT_BASE_URL = \"https://reddoorla.com\";\n\n/**\n * Resolve the public report origin.\n *\n * Deliberately its own variable rather than reusing `DASHBOARD_BASE_URL`. That\n * one addresses operators and points at the ops app; this one addresses a\n * prospect and points at reddoorla.com. They are different audiences on\n * different domains, and sharing a variable would mean one of them silently\n * moving the day the other is repointed.\n */\nexport function resolveReportBaseUrl(raw: string | undefined): string {\n return (raw?.trim() || DEFAULT_REPORT_BASE_URL).replace(/\\/$/, \"\");\n}\n\n/** The link a prospect opens. */\nexport function reportUrl(token: string, raw = process.env.REPORT_BASE_URL): string {\n return `${resolveReportBaseUrl(raw)}/audit/${token}`;\n}\n\n/** The print-designed variant, which the runner renders to a PDF. Same token,\n * same guards — a separate document, not a stylesheet over the page. */\nexport function reportPrintUrl(token: string, raw = process.env.REPORT_BASE_URL): string {\n return `${reportUrl(token, raw)}/print`;\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  MAX_RECENT_PROSPECT_AUDITS,
3
+ OPENED_AT_COALESCE_MS,
3
4
  OVERRIDES_MAX_LEN,
4
5
  createProspectAudit,
5
6
  generateToken,
@@ -10,9 +11,10 @@ import {
10
11
  setProspectAuditOverrides,
11
12
  siteKey,
12
13
  touchProspectAuditOpened
13
- } from "./chunk-XPJQXSC4.js";
14
+ } from "./chunk-P7323YVW.js";
14
15
  export {
15
16
  MAX_RECENT_PROSPECT_AUDITS,
17
+ OPENED_AT_COALESCE_MS,
16
18
  OVERRIDES_MAX_LEN,
17
19
  createProspectAudit,
18
20
  generateToken,
@@ -24,4 +26,4 @@ export {
24
26
  siteKey,
25
27
  touchProspectAuditOpened
26
28
  };
27
- //# sourceMappingURL=prospect-audits-R3LWF6U7.js.map
29
+ //# sourceMappingURL=prospect-audits-IZVQY3OQ.js.map
@@ -0,0 +1,413 @@
1
+ import {
2
+ rulesetGaps
3
+ } from "./chunk-MNDIED4M.js";
4
+ import {
5
+ makeGitHub
6
+ } from "./chunk-43SW3FGW.js";
7
+ import "./chunk-LBYOLBW7.js";
8
+
9
+ // src/audits/protection-coverage.ts
10
+ var RENOVATE_WORKFLOW_FILE = "renovate.yml";
11
+ var RENOVATE_STALE_AFTER_DAYS = 3;
12
+ var ACCEPTED_GAPS = [];
13
+ function partitionAcceptedGaps(repo, gaps, now, accepted = ACCEPTED_GAPS) {
14
+ const live = [];
15
+ const acceptedOut = [];
16
+ for (const gap of gaps) {
17
+ const match = accepted.find(
18
+ (a) => a.repo === repo && gap.startsWith(a.detailPrefix) && now < new Date(a.until)
19
+ );
20
+ if (match) acceptedOut.push(`${gap} [accepted until ${match.until}: ${match.reason}]`);
21
+ else live.push(gap);
22
+ }
23
+ return { live, accepted: acceptedOut };
24
+ }
25
+ function secretScanningGaps(repo, state, openAlerts) {
26
+ const gaps = [];
27
+ if (state.secretScanning !== "enabled")
28
+ gaps.push(`secret scanning ${state.secretScanning} (fleet floor is enabled)`);
29
+ if (state.pushProtection !== "enabled")
30
+ gaps.push(`push protection ${state.pushProtection} (fleet floor is enabled)`);
31
+ if (state.secretScanning === "enabled") {
32
+ if (openAlerts === "unavailable")
33
+ gaps.push(
34
+ `open secret-scanning alerts unreadable on ${repo} (token lacks security_events/admin read \u2014 unverified, not clean)`
35
+ );
36
+ else if (openAlerts > 0)
37
+ gaps.push(
38
+ `${openAlerts} open secret-scanning alert${openAlerts === 1 ? "" : "s"} on ${repo} \u2014 a leaked credential nobody has triaged. Fix: rotate or restrict the secret, then close the alert with a resolution at https://github.com/${repo}/security/secret-scanning (redacting the value at HEAD does NOT close it \u2014 the history is still public).`
39
+ );
40
+ }
41
+ return gaps;
42
+ }
43
+ function renovateGaps(health, now) {
44
+ if (!health.present) return ["no renovate workflow (dependency updates never run here)"];
45
+ if (health.state !== "active")
46
+ return [
47
+ // A disabled workflow REJECTS workflow_dispatch (HTTP 403) — it must be
48
+ // re-enabled first; "just dispatch it" is a dead-end runbook line here.
49
+ `renovate workflow is ${health.state} (re-enable it: \`gh workflow enable renovate.yml -R <repo>\`, then dispatch once)`
50
+ ];
51
+ if (health.lastSuccessAt === null)
52
+ return [
53
+ "renovate workflow has never succeeded (dispatch it once; if this gap returns, the schedule never registered \u2014 push a commit touching the workflow file)"
54
+ ];
55
+ const succeededAt = Date.parse(health.lastSuccessAt);
56
+ if (Number.isNaN(succeededAt))
57
+ return [`renovate last-success timestamp unreadable: ${health.lastSuccessAt}`];
58
+ const days = (now.getTime() - succeededAt) / 864e5;
59
+ if (days > RENOVATE_STALE_AFTER_DAYS)
60
+ return [`renovate last succeeded ${Math.floor(days)}d ago (cron is twice daily)`];
61
+ return [];
62
+ }
63
+ function renovateBlockedGaps(blocked) {
64
+ const orphaned = blocked.filter((b) => b.orphaned);
65
+ if (orphaned.length === 0) return [];
66
+ return [
67
+ `renovate is BLOCKED on ${orphaned.length} branch(es) \u2014 ${orphaned.map((b) => b.branch).join(", ")} \u2014 it runs and succeeds but refuses to update them, so every dependency grouped there is frozen. Usual cause: the branch tip was authored by a retired bot identity, which reads to Renovate as a human edit. Fix: confirm the tip author is a bot, then delete the branch and let Renovate recreate it (\`gh api -X DELETE repos/<repo>/git/refs/heads/<branch>\`).`
68
+ ];
69
+ }
70
+ function dashboardVocabularyGaps(dash) {
71
+ if (!dash.present || dash.unknownSections.length === 0) return [];
72
+ return [
73
+ `renovate dashboard has ${dash.unknownSections.length} section(s) this audit does not recognise \u2014 ${dash.unknownSections.join(", ")}. Nothing is necessarily wrong with the repo, but the blocked-branch check reads Renovate's own headings, so drift there means it can no longer be trusted. Fix: reconcile KNOWN_DASHBOARD_SECTIONS in src/github/gh.ts against Renovate's dependency-dashboard.ts.`
74
+ ];
75
+ }
76
+ function isFeatureUpdateBranch(headRef) {
77
+ if (!headRef.startsWith("renovate/")) return false;
78
+ if (headRef === "renovate/lock-file-maintenance") return false;
79
+ return !/^renovate\/npm-.+-vulnerability$/.test(headRef);
80
+ }
81
+ var RENOVATE_DROUGHT_WARN_DAYS = 21;
82
+ function renovateOutcome(window, now) {
83
+ const last = window.merges.filter((m) => isFeatureUpdateBranch(m.headRef))[0];
84
+ if (!last) {
85
+ return {
86
+ state: "unmeasured",
87
+ reason: window.truncated ? "no feature-update Renovate merge inside the scanned window, and the window is FULL (older history not read \u2014 this repo merges too many PRs to see back that far)" : "this repo has never merged a feature-update Renovate PR (no baseline to measure from)"
88
+ };
89
+ }
90
+ const days = Math.floor((now.getTime() - new Date(last.mergedAt).getTime()) / 864e5);
91
+ return {
92
+ state: days > RENOVATE_DROUGHT_WARN_DAYS ? "drought" : "delivering",
93
+ days,
94
+ lastBranch: last.headRef,
95
+ lastMergedAt: last.mergedAt
96
+ };
97
+ }
98
+ function renovateOutcomeLine(outcome) {
99
+ if (outcome.state === "delivering") return null;
100
+ if (outcome.state === "unmeasured") return `renovate outcome UNMEASURED \u2014 ${outcome.reason}`;
101
+ return `no feature update has LANDED in ${outcome.days}d (warns past ${RENOVATE_DROUGHT_WARN_DAYS}d; last was ${outcome.lastBranch} merged ${outcome.lastMergedAt.slice(0, 10)}). Security + lockfile Renovate PRs are excluded, so this is feature-version drift, not a security signal.`;
102
+ }
103
+ function renovateOutcomeSummary(outcomes) {
104
+ const n = (state) => outcomes.filter((o) => o.state === state).length;
105
+ return `RENOVATE_OUTCOME drought=${n("drought")} delivering=${n("delivering")} unmeasured=${n("unmeasured")} threshold=${RENOVATE_DROUGHT_WARN_DAYS}d`;
106
+ }
107
+ var BLOCKED_STALE_DAYS = 5;
108
+ async function resolveBlockedBranches(repo, dash, deps, now) {
109
+ if (!dash.present) return [];
110
+ const states = [];
111
+ for (const branch of dash.blockedBranches) {
112
+ const tip = await deps.branchTip(repo, branch);
113
+ if (!tip) continue;
114
+ const ageDays = (now.getTime() - new Date(tip.committedAt).getTime()) / 864e5;
115
+ states.push({ branch, orphaned: tip.authorIsMachine || ageDays > BLOCKED_STALE_DAYS });
116
+ }
117
+ return states;
118
+ }
119
+ async function collectProtectionCoverage(org, deps, now = /* @__PURE__ */ new Date(), accepted = ACCEPTED_GAPS) {
120
+ const rows = [];
121
+ for (const r of await deps.listOrgRepos(org)) {
122
+ const repo = `${org}/${r.name}`;
123
+ if (r.archived || r.visibility !== "public") {
124
+ rows.push({
125
+ repo,
126
+ status: "skipped",
127
+ detail: r.archived ? "archived" : `${r.visibility} (rulesets need a paid plan)`
128
+ });
129
+ continue;
130
+ }
131
+ try {
132
+ const gaps = [];
133
+ let coveredDetail = "";
134
+ const rulesets = await deps.listRepoRulesets(repo);
135
+ if (rulesets.length === 0) {
136
+ gaps.push("no repo rulesets at all");
137
+ } else {
138
+ const judged = await Promise.all(
139
+ rulesets.map(async (rs) => {
140
+ const full = await deps.getRuleset(repo, rs.id);
141
+ return { name: rs.name, full, gaps: rulesetGaps(full, null) };
142
+ })
143
+ );
144
+ const covering = judged.find((j) => j.gaps.length === 0);
145
+ if (covering) {
146
+ const ciGated = (covering.full.rules ?? []).some(
147
+ (rule) => rule.type === "required_status_checks"
148
+ );
149
+ coveredDetail = `"${covering.name}"${ciGated ? "" : " \u2014 NO CI gate (refs rules only)"}`;
150
+ } else {
151
+ gaps.push(judged.map((j) => `"${j.name}": ${j.gaps.join("; ")}`).join(" | "));
152
+ }
153
+ }
154
+ const openAlerts = r.secretScanning === "enabled" ? await deps.openSecretAlerts(repo) : "unavailable";
155
+ gaps.push(...secretScanningGaps(repo, r, openAlerts));
156
+ gaps.push(...renovateGaps(await deps.workflowHealth(repo, RENOVATE_WORKFLOW_FILE), now));
157
+ const dash = await deps.dependencyDashboard(repo);
158
+ gaps.push(...dashboardVocabularyGaps(dash));
159
+ gaps.push(...renovateBlockedGaps(await resolveBlockedBranches(repo, dash, deps, now)));
160
+ const outcome = renovateOutcome(await deps.renovateMergeWindow(repo), now);
161
+ const { live, accepted: acked } = partitionAcceptedGaps(repo, gaps, now, accepted);
162
+ if (live.length > 0) {
163
+ rows.push({
164
+ repo,
165
+ status: "gap",
166
+ detail: [...live, ...acked].join(" | "),
167
+ renovateOutcome: outcome
168
+ });
169
+ } else if (acked.length > 0) {
170
+ rows.push({
171
+ repo,
172
+ status: "skipped",
173
+ detail: acked.join(" | "),
174
+ renovateOutcome: outcome
175
+ });
176
+ } else {
177
+ rows.push({
178
+ repo,
179
+ status: "covered",
180
+ detail: coveredDetail,
181
+ renovateOutcome: outcome
182
+ });
183
+ }
184
+ } catch (e) {
185
+ rows.push({
186
+ repo,
187
+ status: "gap",
188
+ detail: `probe failed: ${e instanceof Error ? e.message : String(e)}`
189
+ });
190
+ }
191
+ }
192
+ return rows;
193
+ }
194
+
195
+ // src/github/package-manager-pin.ts
196
+ var PACKAGE_MANAGER_ACCEPTED_GAPS = [];
197
+ function parsePackageManagerField(packageJson) {
198
+ let parsed;
199
+ try {
200
+ parsed = JSON.parse(packageJson);
201
+ } catch (e) {
202
+ return {
203
+ state: "unreadable",
204
+ reason: `package.json is not valid JSON (${e instanceof Error ? e.message : String(e)})`
205
+ };
206
+ }
207
+ if (typeof parsed !== "object" || parsed === null) {
208
+ return { state: "unreadable", reason: "package.json is not a JSON object" };
209
+ }
210
+ const value = parsed.packageManager;
211
+ if (value === void 0) return { state: "absent" };
212
+ if (typeof value !== "string") {
213
+ return { state: "unreadable", reason: `packageManager is ${typeof value}, not a string` };
214
+ }
215
+ return { state: "present", value };
216
+ }
217
+ function parsePnpmActionSetupPins(workflow) {
218
+ const lines = workflow.split("\n");
219
+ const pins = [];
220
+ const indentOf = (line) => line.length - line.trimStart().length;
221
+ const isBlank = (line) => line.trim().length === 0 || /^\s*#/.test(line);
222
+ for (let i = 0; i < lines.length; i++) {
223
+ const line = lines[i];
224
+ const uses = /^(\s*)(-\s+)?uses:\s*['"]?pnpm\/action-setup(?=[@'"\s]|$)/.exec(line);
225
+ if (!uses) continue;
226
+ const contentIndent = uses[1].length + (uses[2] ? uses[2].length : 0);
227
+ for (let j = i + 1; j < lines.length; j++) {
228
+ const body = lines[j];
229
+ if (isBlank(body)) continue;
230
+ const indent = indentOf(body);
231
+ if (indent < contentIndent) break;
232
+ if (indent === contentIndent && /^\s*-\s/.test(body)) break;
233
+ if (indent !== contentIndent || !/^\s*with:\s*$/.test(body)) continue;
234
+ for (let k = j + 1; k < lines.length; k++) {
235
+ const input = lines[k];
236
+ if (isBlank(input)) continue;
237
+ if (indentOf(input) <= contentIndent) break;
238
+ const m = /^\s*version:\s*['"]?([^'"#\s]+)/.exec(input);
239
+ if (m) pins.push(m[1]);
240
+ }
241
+ break;
242
+ }
243
+ }
244
+ return pins;
245
+ }
246
+ function fleetPin(pins) {
247
+ if (pins.length === 0) return { state: "none" };
248
+ const tally = /* @__PURE__ */ new Map();
249
+ for (const p of pins) tally.set(p, (tally.get(p) ?? 0) + 1);
250
+ const counts = [...tally.entries()].map(([pin, count]) => ({ pin, count })).sort((a, b) => b.count - a.count || a.pin.localeCompare(b.pin));
251
+ const top = counts[0];
252
+ if (top.count * 2 > pins.length) {
253
+ return { state: "majority", pin: top.pin, count: top.count, total: pins.length };
254
+ }
255
+ return { state: "split", counts, total: pins.length };
256
+ }
257
+ function packageManagerGaps(facts, fleet) {
258
+ if (facts.packageJson === null) return [];
259
+ const field = parsePackageManagerField(facts.packageJson);
260
+ const gaps = [];
261
+ const pinnedWorkflows = facts.workflowPins.map((w) => `${w.file} (version: ${w.version})`).join(", ");
262
+ if (field.state === "unreadable") {
263
+ gaps.push(`packageManager unreadable \u2014 ${field.reason} (unverified, not clean)`);
264
+ return gaps;
265
+ }
266
+ if (field.state === "absent") {
267
+ const fleetSays = fleet.state === "majority" ? ` (the fleet pin is ${fleet.pin})` : " (fleet pin unsettled)";
268
+ gaps.push(
269
+ facts.workflowPins.length > 0 ? `no packageManager field in package.json${fleetSays}, and ${pinnedWorkflows} pins pnpm in a pnpm/action-setup step instead. Fix: add the field AND delete the workflow's version input in the SAME change \u2014 adding the field alone makes action-setup hard-error on the mismatch.` : `no packageManager field in package.json${fleetSays} \u2014 CI installs whatever pnpm the runner defaults to, so the version resolving this lockfile is GitHub's choice, not the repo's. Fix: add "packageManager" to package.json.`
270
+ );
271
+ return gaps;
272
+ }
273
+ if (fleet.state === "majority" && field.value !== fleet.pin) {
274
+ gaps.push(
275
+ `packageManager is ${field.value} but the fleet pin is ${fleet.pin} (${fleet.count}/${fleet.total} repos agree). Fix: bump the field, or say why this repo is deliberately apart.`
276
+ );
277
+ }
278
+ if (facts.workflowPins.length > 0) {
279
+ const agree = facts.workflowPins.every((w) => `pnpm@${w.version}` === field.value);
280
+ gaps.push(
281
+ `${pinnedWorkflows} pins a pnpm version input while package.json pins ${field.value}` + (agree ? " \u2014 they agree today, but nothing keeps them in step" : " \u2014 THEY DISAGREE") + `. pnpm/action-setup does not prefer either side, it hard-errors on a mismatch. Fix: delete the workflow's version input; packageManager is the single source of truth.`
282
+ );
283
+ }
284
+ return gaps;
285
+ }
286
+ async function collectPackageManagerPins(org, deps, now = /* @__PURE__ */ new Date(), accepted = PACKAGE_MANAGER_ACCEPTED_GAPS) {
287
+ const rows = [];
288
+ const facts = /* @__PURE__ */ new Map();
289
+ for (const r of await deps.listOrgRepos(org)) {
290
+ const repo = `${org}/${r.name}`;
291
+ if (r.archived || r.visibility !== "public") {
292
+ rows.push({
293
+ repo,
294
+ scope: "skipped",
295
+ gaps: [],
296
+ accepted: [],
297
+ detail: r.archived ? "archived" : `${r.visibility}`
298
+ });
299
+ continue;
300
+ }
301
+ try {
302
+ const packageJson = await deps.repoTextFile(repo, "package.json");
303
+ if (packageJson === null) {
304
+ rows.push({
305
+ repo,
306
+ scope: "out-of-scope",
307
+ gaps: [],
308
+ accepted: [],
309
+ detail: "no package.json"
310
+ });
311
+ continue;
312
+ }
313
+ const workflowPins = [];
314
+ for (const path of await deps.listWorkflowPaths(repo)) {
315
+ const contents = await deps.repoTextFile(repo, path);
316
+ if (contents === null) continue;
317
+ for (const version of parsePnpmActionSetupPins(contents)) {
318
+ workflowPins.push({ file: path.replace(/^\.github\/workflows\//, ""), version });
319
+ }
320
+ }
321
+ facts.set(repo, { packageJson, workflowPins });
322
+ rows.push({ repo, scope: "judged", gaps: [], accepted: [], detail: "" });
323
+ } catch (e) {
324
+ rows.push({
325
+ repo,
326
+ scope: "judged",
327
+ gaps: [`pnpm pin probe failed: ${e instanceof Error ? e.message : String(e)}`],
328
+ accepted: [],
329
+ detail: "probe failed"
330
+ });
331
+ }
332
+ }
333
+ const pinned = [];
334
+ for (const f of facts.values()) {
335
+ const field = parsePackageManagerField(f.packageJson);
336
+ if (field.state === "present") pinned.push(field.value);
337
+ }
338
+ const fleet = fleetPin(pinned);
339
+ return rows.map((row) => {
340
+ const f = facts.get(row.repo);
341
+ if (!f) return row;
342
+ const all = packageManagerGaps(f, fleet);
343
+ const split = partitionAcceptedGaps(row.repo, all, now, accepted);
344
+ const field = parsePackageManagerField(f.packageJson);
345
+ return {
346
+ ...row,
347
+ gaps: split.live,
348
+ accepted: split.accepted,
349
+ detail: field.state === "present" ? field.value : row.detail
350
+ };
351
+ });
352
+ }
353
+ function packageManagerPinSummary(rows) {
354
+ const judged = rows.filter((r) => r.scope === "judged");
355
+ const pins = judged.map((r) => r.detail).filter((d) => d.startsWith("pnpm@"));
356
+ const fleet = fleetPin(pins);
357
+ const pin = fleet.state === "majority" ? fleet.pin : fleet.state === "split" ? `SPLIT(${fleet.counts.map((c) => `${c.pin}x${c.count}`).join(",")})` : "none";
358
+ return `PACKAGE_MANAGER_PIN gaps=${judged.filter((r) => r.gaps.length > 0).length} pinned=${pins.length} judged=${judged.length} out-of-scope=${rows.filter((r) => r.scope === "out-of-scope").length} skipped=${rows.filter((r) => r.scope === "skipped").length} fleetPin=${pin}`;
359
+ }
360
+
361
+ // src/cli/commands/protection-audit.ts
362
+ function protectionAuditExitCode(gaps) {
363
+ return gaps > 0 ? 1 : 0;
364
+ }
365
+ async function runProtectionAuditCommand(opts, depsOverride) {
366
+ const org = opts.org?.trim();
367
+ if (!org) {
368
+ return { output: "protection-audit requires --org <org>", code: 2 };
369
+ }
370
+ const token = process.env.RENOVATE_TOKEN?.trim() || process.env.GH_TOKEN?.trim();
371
+ if (!depsOverride && !token) {
372
+ return {
373
+ output: "protection-audit skipped: no RENOVATE_TOKEN/GH_TOKEN (fleet read) configured.",
374
+ code: 0
375
+ };
376
+ }
377
+ const deps = depsOverride ?? makeGitHub({ token });
378
+ const coverage = await collectProtectionCoverage(org, deps);
379
+ const pinRows = await collectPackageManagerPins(org, deps);
380
+ const pinByRepo = new Map(pinRows.map((p) => [p.repo, p]));
381
+ const rows = coverage.map((row) => {
382
+ const pin = pinByRepo.get(row.repo);
383
+ if (!pin || pin.gaps.length === 0) return row;
384
+ return {
385
+ ...row,
386
+ status: "gap",
387
+ detail: [row.detail, ...pin.gaps].filter((d) => d.length > 0).join(" | ")
388
+ };
389
+ });
390
+ const gaps = rows.filter((r) => r.status === "gap");
391
+ const covered = rows.filter((r) => r.status === "covered");
392
+ const skipped = rows.filter((r) => r.status === "skipped");
393
+ const lines = rows.map((r) => `${r.status.toUpperCase().padEnd(7)} ${r.repo} \u2014 ${r.detail}`);
394
+ const measured = rows.map((r) => r.renovateOutcome).filter((o) => o !== void 0);
395
+ for (const r of rows) {
396
+ const line = r.renovateOutcome && renovateOutcomeLine(r.renovateOutcome);
397
+ if (line) lines.push(`WARN ${r.repo} \u2014 ${line}`);
398
+ }
399
+ for (const p of pinRows) {
400
+ for (const note of p.accepted) lines.push(`WARN ${p.repo} \u2014 ${note}`);
401
+ }
402
+ lines.push(renovateOutcomeSummary(measured));
403
+ lines.push(packageManagerPinSummary(pinRows));
404
+ lines.push(
405
+ `PROTECTION_AUDIT gaps=${gaps.length} covered=${covered.length} skipped=${skipped.length} total=${rows.length}`
406
+ );
407
+ return { output: lines.join("\n"), code: protectionAuditExitCode(gaps.length) };
408
+ }
409
+ export {
410
+ protectionAuditExitCode,
411
+ runProtectionAuditCommand
412
+ };
413
+ //# sourceMappingURL=protection-audit-3J3UEOVT.js.map