@reddoorla/maintenance 0.85.2 → 0.87.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 (265) hide show
  1. package/README.md +8 -0
  2. package/dist/airtable-ZBUPQJSC.js +9 -0
  3. package/dist/{announce-GIAYN67S.js → announce-I2Z4JN3I.js} +58 -30
  4. package/dist/announce-I2Z4JN3I.js.map +1 -0
  5. package/dist/attachments-NLZIJ5NU.js +9 -0
  6. package/dist/{bump-deps-SWJB6H5V.js → bump-deps-CGVELRLK.js} +9 -9
  7. package/dist/{chunk-SDUJIANJ.js → chunk-2NJQLSEU.js} +3 -3
  8. package/dist/chunk-2NQUCS7O.js +243 -0
  9. package/dist/chunk-2NQUCS7O.js.map +1 -0
  10. package/dist/{chunk-ZTPPCHDB.js → chunk-3G25KIWW.js} +4 -4
  11. package/dist/{chunk-DTTIFYAM.js → chunk-3JQ2FO3D.js} +44 -405
  12. package/dist/chunk-3JQ2FO3D.js.map +1 -0
  13. package/dist/chunk-3SF7D4DY.js +359 -0
  14. package/dist/chunk-3SF7D4DY.js.map +1 -0
  15. package/dist/{chunk-37S6L6G4.js → chunk-5UINJNAJ.js} +72 -2
  16. package/dist/chunk-5UINJNAJ.js.map +1 -0
  17. package/dist/{chunk-THEFTZ7F.js → chunk-62CMMGEO.js} +5 -5
  18. package/dist/{chunk-FDIFTIXC.js → chunk-6TWWY7ZU.js} +2 -2
  19. package/dist/{chunk-PBWDDDQH.js → chunk-7BZUOZPU.js} +2 -2
  20. package/dist/{chunk-577HCGZO.js → chunk-7NYN2P6I.js} +44 -26
  21. package/dist/chunk-7NYN2P6I.js.map +1 -0
  22. package/dist/{chunk-UYCAF77E.js → chunk-7RJ2HMMJ.js} +3 -3
  23. package/dist/{chunk-Q276MZ7E.js → chunk-7UAYMAKR.js} +2 -2
  24. package/dist/{chunk-MGR2H7F4.js → chunk-BBUSGEES.js} +58 -45
  25. package/dist/chunk-BBUSGEES.js.map +1 -0
  26. package/dist/chunk-BNM5POSJ.js +284 -0
  27. package/dist/chunk-BNM5POSJ.js.map +1 -0
  28. package/dist/{chunk-R6FPFMOS.js → chunk-BQRFQSGY.js} +3 -3
  29. package/dist/{chunk-NWB4O5EY.js → chunk-C6CLRKPV.js} +5 -5
  30. package/dist/chunk-C6CLRKPV.js.map +1 -0
  31. package/dist/{chunk-7J53SUT6.js → chunk-CROKUHJB.js} +4 -4
  32. package/dist/chunk-DEWJMMG3.js +274 -0
  33. package/dist/chunk-DEWJMMG3.js.map +1 -0
  34. package/dist/{chunk-SEEA6RXF.js → chunk-DJZWYIPP.js} +2 -2
  35. package/dist/chunk-DJZWYIPP.js.map +1 -0
  36. package/dist/chunk-EVBCK2KC.js +68 -0
  37. package/dist/chunk-EVBCK2KC.js.map +1 -0
  38. package/dist/chunk-F3JWOPTG.js +202 -0
  39. package/dist/chunk-F3JWOPTG.js.map +1 -0
  40. package/dist/{chunk-GZNBCHAZ.js → chunk-HEWSRXYV.js} +4 -4
  41. package/dist/chunk-HHMMUR4P.js +335 -0
  42. package/dist/chunk-HHMMUR4P.js.map +1 -0
  43. package/dist/chunk-HSI6Q7MI.js +1238 -0
  44. package/dist/chunk-HSI6Q7MI.js.map +1 -0
  45. package/dist/{chunk-MPBXOJ3N.js → chunk-IY6256GK.js} +33 -2
  46. package/dist/chunk-IY6256GK.js.map +1 -0
  47. package/dist/{chunk-LS6QPANO.js → chunk-IYSEC543.js} +2 -2
  48. package/dist/{chunk-DC5H7KEQ.js → chunk-JHGHLKFS.js} +2 -2
  49. package/dist/{chunk-4DHQCN5H.js → chunk-LBYOLBW7.js} +17 -2
  50. package/dist/chunk-LBYOLBW7.js.map +1 -0
  51. package/dist/{chunk-6QRLRT3P.js → chunk-MT2JINSS.js} +16 -265
  52. package/dist/chunk-MT2JINSS.js.map +1 -0
  53. package/dist/{chunk-TP5M2NQB.js → chunk-NCMZMLY7.js} +3 -3
  54. package/dist/{chunk-COZPL62X.js → chunk-NDWYJLRF.js} +24 -139
  55. package/dist/chunk-NDWYJLRF.js.map +1 -0
  56. package/dist/{chunk-3YXDSCM6.js → chunk-NXNPDCF3.js} +29 -11
  57. package/dist/chunk-NXNPDCF3.js.map +1 -0
  58. package/dist/chunk-NYUYNYQL.js +48 -0
  59. package/dist/chunk-NYUYNYQL.js.map +1 -0
  60. package/dist/chunk-P6AORG32.js +415 -0
  61. package/dist/chunk-P6AORG32.js.map +1 -0
  62. package/dist/chunk-PTDMVGKL.js +31 -0
  63. package/dist/chunk-PTDMVGKL.js.map +1 -0
  64. package/dist/{chunk-D4FVAIEJ.js → chunk-PZR4EBPY.js} +5 -2
  65. package/dist/chunk-PZR4EBPY.js.map +1 -0
  66. package/dist/{chunk-LTB6KVJW.js → chunk-SDDPUAWH.js} +13 -11
  67. package/dist/{chunk-LTB6KVJW.js.map → chunk-SDDPUAWH.js.map} +1 -1
  68. package/dist/{chunk-WYYQK4D3.js → chunk-TD2NNXLZ.js} +13 -13
  69. package/dist/{chunk-ZMIPG6TR.js → chunk-TVL5MVUF.js} +4 -4
  70. package/dist/chunk-UDRHAQT7.js +172 -0
  71. package/dist/chunk-UDRHAQT7.js.map +1 -0
  72. package/dist/chunk-VAD43MOR.js +72 -0
  73. package/dist/chunk-VAD43MOR.js.map +1 -0
  74. package/dist/chunk-VSBFM7DS.js +229 -0
  75. package/dist/chunk-VSBFM7DS.js.map +1 -0
  76. package/dist/{chunk-VRWUL4TR.js → chunk-XHHMM5HX.js} +2 -2
  77. package/dist/{chunk-RJB3QUSP.js → chunk-XIDUYLSR.js} +3 -3
  78. package/dist/{chunk-QG6OSL4M.js → chunk-XUGSZP7Z.js} +2 -2
  79. package/dist/{chunk-S56JQJ4F.js → chunk-Y53WOQIJ.js} +3 -3
  80. package/dist/{chunk-62VF32IC.js → chunk-YFAXOD7Z.js} +10 -6
  81. package/dist/chunk-YFAXOD7Z.js.map +1 -0
  82. package/dist/{chunk-ICXV25N7.js → chunk-Z3KYFUN6.js} +3 -3
  83. package/dist/chunk-ZBDZTJUO.js +129 -0
  84. package/dist/chunk-ZBDZTJUO.js.map +1 -0
  85. package/dist/chunk-ZGBEIWSC.js +146 -0
  86. package/dist/chunk-ZGBEIWSC.js.map +1 -0
  87. package/dist/cli/bin.js +51 -28
  88. package/dist/cli/bin.js.map +1 -1
  89. package/dist/cli/commands/audit.d.ts +30 -1
  90. package/dist/cli/commands/audit.js +69 -33
  91. package/dist/cli/commands/audit.js.map +1 -1
  92. package/dist/client-AUE3XUPO.js +11 -0
  93. package/dist/{convert-to-pnpm-AT52NWZB.js → convert-to-pnpm-QFJOEP5V.js} +9 -9
  94. package/dist/db-UXNIZ2RG.js +217 -0
  95. package/dist/db-UXNIZ2RG.js.map +1 -0
  96. package/dist/{digest-UVHYZUYE.js → digest-FW6QMZZJ.js} +26 -22
  97. package/dist/{digest-UVHYZUYE.js.map → digest-FW6QMZZJ.js.map} +1 -1
  98. package/dist/dump-P6EBSIOH.js +68 -0
  99. package/dist/dump-P6EBSIOH.js.map +1 -0
  100. package/dist/email-KJXASVH5.js +195 -0
  101. package/dist/email-KJXASVH5.js.map +1 -0
  102. package/dist/{ensure-site-S25MDDHW.js → ensure-site-4DCEBAPM.js} +25 -16
  103. package/dist/ensure-site-4DCEBAPM.js.map +1 -0
  104. package/dist/fleet-events-cF2Om88w.d.ts +24 -0
  105. package/dist/fleet-state-7TJ2GYT2.js +39 -0
  106. package/dist/{forms-notify-target-TWN4EH6K.js → forms-notify-target-2EAWCWBW.js} +42 -133
  107. package/dist/forms-notify-target-2EAWCWBW.js.map +1 -0
  108. package/dist/{github-signals-G4ILZAJ6.js → github-signals-E2WPYTBK.js} +42 -21
  109. package/dist/github-signals-E2WPYTBK.js.map +1 -0
  110. package/dist/{header-image-T7XD37JL.js → header-image-6IC53W27.js} +36 -13
  111. package/dist/header-image-6IC53W27.js.map +1 -0
  112. package/dist/header-images-SQBCE5OD.js +88 -0
  113. package/dist/header-images-SQBCE5OD.js.map +1 -0
  114. package/dist/{health-endpoint-242WG2OJ.js → health-endpoint-RFXL2F4A.js} +9 -9
  115. package/dist/health-mirror-L5JBEJVA.js +41 -0
  116. package/dist/health-mirror-L5JBEJVA.js.map +1 -0
  117. package/dist/import-airtable-OHVY344G.js +37 -0
  118. package/dist/index.d.ts +324 -21
  119. package/dist/index.js +215 -76
  120. package/dist/index.js.map +1 -1
  121. package/dist/{init-JN26NTWF.js → init-XRAQZYID.js} +21 -18
  122. package/dist/{init-JN26NTWF.js.map → init-XRAQZYID.js.map} +1 -1
  123. package/dist/{launch-JA7Z4CIM.js → launch-IX4Y5JJC.js} +65 -30
  124. package/dist/launch-IX4Y5JJC.js.map +1 -0
  125. package/dist/lighthouse-VHI77PFB.js +11 -0
  126. package/dist/{lighthouse-airtable-BIPM26SZ.js → lighthouse-airtable-3GUWHETW.js} +3 -3
  127. package/dist/mailchimp-XYCTEEJY.js +72 -0
  128. package/dist/mailchimp-XYCTEEJY.js.map +1 -0
  129. package/dist/migrate-GQH237A6.js +7 -0
  130. package/dist/migrate-GQH237A6.js.map +1 -0
  131. package/dist/notify-C645PGD5.js +22 -0
  132. package/dist/notify-C645PGD5.js.map +1 -0
  133. package/dist/{onboard-53CITIM7.js → onboard-O4IAHJMO.js} +9 -9
  134. package/dist/orchestrate-YLWQ6KK2.js +31 -0
  135. package/dist/orchestrate-YLWQ6KK2.js.map +1 -0
  136. package/dist/parity-56KA2DGR.js +12 -0
  137. package/dist/parity-56KA2DGR.js.map +1 -0
  138. package/dist/pipeline-3AANZFCR.js +12 -0
  139. package/dist/pipeline-3AANZFCR.js.map +1 -0
  140. package/dist/{preflight-3XPDUHFJ.js → preflight-TNORKGCX.js} +17 -13
  141. package/dist/{preflight-3XPDUHFJ.js.map → preflight-TNORKGCX.js.map} +1 -1
  142. package/dist/{prismic-ci-UTQ6LRR3.js → prismic-ci-G2AK53VL.js} +12 -12
  143. package/dist/{prismic-models-PVES6R5V.js → prismic-models-O6Z3OK3A.js} +18 -13
  144. package/dist/prismic-models-O6Z3OK3A.js.map +1 -0
  145. package/dist/prospect/types.d.ts +267 -0
  146. package/dist/prospect/types.js +1 -0
  147. package/dist/prospect/types.js.map +1 -0
  148. package/dist/prospect-audit-KSXUNQ5J.js +174 -0
  149. package/dist/prospect-audit-KSXUNQ5J.js.map +1 -0
  150. package/dist/prospect-audits-3PMIO73D.js +19 -0
  151. package/dist/prospect-audits-3PMIO73D.js.map +1 -0
  152. package/dist/{protection-audit-64PLLBLI.js → protection-audit-SJO53MEQ.js} +3 -3
  153. package/dist/recipes/sync-configs.js +3 -3
  154. package/dist/render-IFOGFB5T.js +10 -0
  155. package/dist/render-IFOGFB5T.js.map +1 -0
  156. package/dist/render-from-row-BWSOIYDS.js +17 -0
  157. package/dist/render-from-row-BWSOIYDS.js.map +1 -0
  158. package/dist/{renovate-dispatch-ICJGSFCE.js → renovate-dispatch-6GQWTRKX.js} +12 -12
  159. package/dist/{renovate-dispatch-ICJGSFCE.js.map → renovate-dispatch-6GQWTRKX.js.map} +1 -1
  160. package/dist/replay-C2LXMJZP.js +66 -0
  161. package/dist/replay-C2LXMJZP.js.map +1 -0
  162. package/dist/{report-6CKSWJNG.js → report-R3E3Y7HR.js} +100 -32
  163. package/dist/report-R3E3Y7HR.js.map +1 -0
  164. package/dist/report-mirror-4MNORYQ4.js +49 -0
  165. package/dist/report-mirror-4MNORYQ4.js.map +1 -0
  166. package/dist/reports/header-image/assets/headline-announcement.png +0 -0
  167. package/dist/reports/header-image/assets/headline-launch.png +0 -0
  168. package/dist/reports/header-image/assets/headline-maintenance.png +0 -0
  169. package/dist/reports/header-image/assets/headline-testing.png +0 -0
  170. package/dist/reports/header-image/assets/plate-clean.png +0 -0
  171. package/dist/rerender-3FM6DNRH.js +32 -0
  172. package/dist/rerender-3FM6DNRH.js.map +1 -0
  173. package/dist/{self-updating-PPQVV6UA.js → self-updating-UCIWESAX.js} +9 -9
  174. package/dist/{selftest-CQ6NOX52.js → selftest-VGHWPN72.js} +31 -24
  175. package/dist/selftest-VGHWPN72.js.map +1 -0
  176. package/dist/site-mirror-NIREX5ME.js +52 -0
  177. package/dist/site-mirror-NIREX5ME.js.map +1 -0
  178. package/dist/smoke-B2GQOSIZ.js +20 -0
  179. package/dist/smoke-B2GQOSIZ.js.map +1 -0
  180. package/dist/{smoke-suite-TMCSOYRK.js → smoke-suite-U3UKD3NS.js} +9 -9
  181. package/dist/{submissions-GJ6ITBRH.js → submissions-IQEJX6FK.js} +3 -3
  182. package/dist/{svelte-codemods-ZOBDHIES.js → svelte-codemods-GOKXTBHW.js} +9 -9
  183. package/dist/sync-CTUVXMCK.js +44 -0
  184. package/dist/sync-CTUVXMCK.js.map +1 -0
  185. package/dist/{sync-configs-CJXUX6MD.js → sync-configs-SPTLFJCC.js} +9 -9
  186. package/dist/{upgrade-WLX27UPC.js → upgrade-W6ZR3FJH.js} +9 -9
  187. package/dist/webhook-VOGOW3W6.js +38 -0
  188. package/dist/webhook-VOGOW3W6.js.map +1 -0
  189. package/dist/{websites-SADZR4XQ.js → websites-D6WYFAES.js} +6 -2
  190. package/dist/websites-D6WYFAES.js.map +1 -0
  191. package/dist/{write-audits-to-airtable-7PH75YWY.js → write-audits-to-airtable-CMKHGJNQ.js} +4 -4
  192. package/dist/write-audits-to-airtable-CMKHGJNQ.js.map +1 -0
  193. package/package.json +9 -2
  194. package/dist/airtable-HRKSA7V4.js +0 -9
  195. package/dist/announce-GIAYN67S.js.map +0 -1
  196. package/dist/chunk-37S6L6G4.js.map +0 -1
  197. package/dist/chunk-3YXDSCM6.js.map +0 -1
  198. package/dist/chunk-4DHQCN5H.js.map +0 -1
  199. package/dist/chunk-4SOCPK7A.js +0 -108
  200. package/dist/chunk-4SOCPK7A.js.map +0 -1
  201. package/dist/chunk-577HCGZO.js.map +0 -1
  202. package/dist/chunk-62VF32IC.js.map +0 -1
  203. package/dist/chunk-6QRLRT3P.js.map +0 -1
  204. package/dist/chunk-COZPL62X.js.map +0 -1
  205. package/dist/chunk-D4FVAIEJ.js.map +0 -1
  206. package/dist/chunk-DTTIFYAM.js.map +0 -1
  207. package/dist/chunk-MGR2H7F4.js.map +0 -1
  208. package/dist/chunk-MPBXOJ3N.js.map +0 -1
  209. package/dist/chunk-NWB4O5EY.js.map +0 -1
  210. package/dist/chunk-SEEA6RXF.js.map +0 -1
  211. package/dist/chunk-U5SZ4FOP.js +0 -26
  212. package/dist/chunk-U5SZ4FOP.js.map +0 -1
  213. package/dist/client-X5WUJWDE.js +0 -11
  214. package/dist/db-JSIZYT23.js +0 -23
  215. package/dist/db-JSIZYT23.js.map +0 -1
  216. package/dist/ensure-site-S25MDDHW.js.map +0 -1
  217. package/dist/forms-notify-target-TWN4EH6K.js.map +0 -1
  218. package/dist/github-signals-G4ILZAJ6.js.map +0 -1
  219. package/dist/header-image-T7XD37JL.js.map +0 -1
  220. package/dist/launch-JA7Z4CIM.js.map +0 -1
  221. package/dist/migrate-LJUFGXQZ.js +0 -7
  222. package/dist/orchestrate-KDNXZQX7.js +0 -27
  223. package/dist/prismic-models-PVES6R5V.js.map +0 -1
  224. package/dist/report-6CKSWJNG.js.map +0 -1
  225. package/dist/reports/header-image/assets/plate.png +0 -0
  226. package/dist/selftest-CQ6NOX52.js.map +0 -1
  227. /package/dist/{airtable-HRKSA7V4.js.map → airtable-ZBUPQJSC.js.map} +0 -0
  228. /package/dist/{client-X5WUJWDE.js.map → attachments-NLZIJ5NU.js.map} +0 -0
  229. /package/dist/{bump-deps-SWJB6H5V.js.map → bump-deps-CGVELRLK.js.map} +0 -0
  230. /package/dist/{chunk-SDUJIANJ.js.map → chunk-2NJQLSEU.js.map} +0 -0
  231. /package/dist/{chunk-ZTPPCHDB.js.map → chunk-3G25KIWW.js.map} +0 -0
  232. /package/dist/{chunk-THEFTZ7F.js.map → chunk-62CMMGEO.js.map} +0 -0
  233. /package/dist/{chunk-FDIFTIXC.js.map → chunk-6TWWY7ZU.js.map} +0 -0
  234. /package/dist/{chunk-PBWDDDQH.js.map → chunk-7BZUOZPU.js.map} +0 -0
  235. /package/dist/{chunk-UYCAF77E.js.map → chunk-7RJ2HMMJ.js.map} +0 -0
  236. /package/dist/{chunk-Q276MZ7E.js.map → chunk-7UAYMAKR.js.map} +0 -0
  237. /package/dist/{chunk-R6FPFMOS.js.map → chunk-BQRFQSGY.js.map} +0 -0
  238. /package/dist/{chunk-7J53SUT6.js.map → chunk-CROKUHJB.js.map} +0 -0
  239. /package/dist/{chunk-GZNBCHAZ.js.map → chunk-HEWSRXYV.js.map} +0 -0
  240. /package/dist/{chunk-LS6QPANO.js.map → chunk-IYSEC543.js.map} +0 -0
  241. /package/dist/{chunk-DC5H7KEQ.js.map → chunk-JHGHLKFS.js.map} +0 -0
  242. /package/dist/{chunk-TP5M2NQB.js.map → chunk-NCMZMLY7.js.map} +0 -0
  243. /package/dist/{chunk-WYYQK4D3.js.map → chunk-TD2NNXLZ.js.map} +0 -0
  244. /package/dist/{chunk-ZMIPG6TR.js.map → chunk-TVL5MVUF.js.map} +0 -0
  245. /package/dist/{chunk-VRWUL4TR.js.map → chunk-XHHMM5HX.js.map} +0 -0
  246. /package/dist/{chunk-RJB3QUSP.js.map → chunk-XIDUYLSR.js.map} +0 -0
  247. /package/dist/{chunk-QG6OSL4M.js.map → chunk-XUGSZP7Z.js.map} +0 -0
  248. /package/dist/{chunk-S56JQJ4F.js.map → chunk-Y53WOQIJ.js.map} +0 -0
  249. /package/dist/{chunk-ICXV25N7.js.map → chunk-Z3KYFUN6.js.map} +0 -0
  250. /package/dist/{lighthouse-airtable-BIPM26SZ.js.map → client-AUE3XUPO.js.map} +0 -0
  251. /package/dist/{convert-to-pnpm-AT52NWZB.js.map → convert-to-pnpm-QFJOEP5V.js.map} +0 -0
  252. /package/dist/{migrate-LJUFGXQZ.js.map → fleet-state-7TJ2GYT2.js.map} +0 -0
  253. /package/dist/{health-endpoint-242WG2OJ.js.map → health-endpoint-RFXL2F4A.js.map} +0 -0
  254. /package/dist/{orchestrate-KDNXZQX7.js.map → import-airtable-OHVY344G.js.map} +0 -0
  255. /package/dist/{websites-SADZR4XQ.js.map → lighthouse-VHI77PFB.js.map} +0 -0
  256. /package/dist/{write-audits-to-airtable-7PH75YWY.js.map → lighthouse-airtable-3GUWHETW.js.map} +0 -0
  257. /package/dist/{onboard-53CITIM7.js.map → onboard-O4IAHJMO.js.map} +0 -0
  258. /package/dist/{prismic-ci-UTQ6LRR3.js.map → prismic-ci-G2AK53VL.js.map} +0 -0
  259. /package/dist/{protection-audit-64PLLBLI.js.map → protection-audit-SJO53MEQ.js.map} +0 -0
  260. /package/dist/{self-updating-PPQVV6UA.js.map → self-updating-UCIWESAX.js.map} +0 -0
  261. /package/dist/{smoke-suite-TMCSOYRK.js.map → smoke-suite-U3UKD3NS.js.map} +0 -0
  262. /package/dist/{submissions-GJ6ITBRH.js.map → submissions-IQEJX6FK.js.map} +0 -0
  263. /package/dist/{svelte-codemods-ZOBDHIES.js.map → svelte-codemods-GOKXTBHW.js.map} +0 -0
  264. /package/dist/{sync-configs-CJXUX6MD.js.map → sync-configs-SPTLFJCC.js.map} +0 -0
  265. /package/dist/{upgrade-WLX27UPC.js.map → upgrade-W6ZR3FJH.js.map} +0 -0
@@ -0,0 +1,267 @@
1
+ import { a as AuditResult } from '../types-OIQZ5hWN.js';
2
+
3
+ /** Every pipeline stage resolves to this. A failed stage degrades its report
4
+ * section to "not measured" — it never kills the run (spec: error handling). */
5
+ type StageResult<T> = {
6
+ ok: true;
7
+ data: T;
8
+ } | {
9
+ ok: false;
10
+ error: string;
11
+ };
12
+ type RobotsAgentAccess = {
13
+ agent: string;
14
+ /** May this agent fetch "/" per robots.txt? (No robots.txt → allowed.) */
15
+ allowed: boolean;
16
+ /** The deciding rule, e.g. "User-agent: GPTBot → Disallow: /". Null = no rule matched. */
17
+ matchedRule: string | null;
18
+ };
19
+ type PageExtract = {
20
+ title: string | null;
21
+ metaDescription: string | null;
22
+ canonical: string | null;
23
+ /** property/name → content for og:* and twitter:* metas. */
24
+ social: Record<string, string>;
25
+ /** `level` is 1-6 (H1-H6). */
26
+ headings: {
27
+ level: number;
28
+ text: string;
29
+ }[];
30
+ /** Raw text of each <script type="application/ld+json"> block. */
31
+ jsonLd: string[];
32
+ images: {
33
+ total: number;
34
+ withAlt: number;
35
+ };
36
+ hasViewportMeta: boolean;
37
+ /** Visible text, whitespace-collapsed. */
38
+ text: string;
39
+ };
40
+ type PageCapture = {
41
+ url: string;
42
+ /** HTTP status of the raw fetch. Null = the fetch itself failed. */
43
+ status: number | null;
44
+ /** Extract of the raw HTTP HTML (what non-JS crawlers see). */
45
+ raw: PageExtract | null;
46
+ /** Extract of the Playwright-rendered DOM (what a browser sees). */
47
+ rendered: PageExtract | null;
48
+ error: string | null;
49
+ };
50
+ type CrawlResult = {
51
+ /** Normalized origin, e.g. "https://example.com". */
52
+ origin: string;
53
+ robotsTxt: string | null;
54
+ /** One entry per agent in crawl.ts's ALL_AGENTS (6 AI + 2 classical). */
55
+ agentAccess: RobotsAgentAccess[];
56
+ sitemap: {
57
+ present: boolean;
58
+ urlCount: number;
59
+ };
60
+ llmsTxt: {
61
+ present: boolean;
62
+ firstLine: string | null;
63
+ };
64
+ /** Per sidecar, the error that stopped us fetching it, or null. A fetch that
65
+ * FAILED must never be reported as "the site has no robots.txt" — that would
66
+ * claim the crawlers are unrestricted when we simply did not look. */
67
+ sidecarErrors: {
68
+ robots: string | null;
69
+ llms: string | null;
70
+ sitemap: string | null;
71
+ };
72
+ /** Lower-cased homepage response headers (security-header check input). */
73
+ homeHeaders: Record<string, string>;
74
+ pages: PageCapture[];
75
+ };
76
+ type ChecksResult = {
77
+ /** False when the robots.txt fetch itself failed, so "no AI crawler is
78
+ * blocked" would be our silence rather than the site's answer. The report
79
+ * must say "not measured" for crawler access when this is false. */
80
+ crawlerAccessMeasured: boolean;
81
+ crawlerAccess: {
82
+ blockedAi: string[];
83
+ allowedAi: string[];
84
+ blockedClassical: string[];
85
+ };
86
+ jsDependence: {
87
+ /** 0..1 — fraction of rendered words absent from the raw HTML, weighted by
88
+ * page size (total missing words / total rendered words across every
89
+ * comparable page) — or null when no page produced a comparable pair.
90
+ * Never 0 for "nothing measured": a page whose raw fetch was blocked, or
91
+ * whose rendered text tokenizes to nothing, drops out of the average
92
+ * rather than forcing it toward a false "perfectly crawlable". */
93
+ avgMissing: number | null;
94
+ perPage: {
95
+ url: string;
96
+ /** 0..1 — this page's own missing fraction (unweighted). */
97
+ missing: number;
98
+ /** Token count wordSet(rendered.text) produced for this page — the
99
+ * weight avgMissing gives it, and why a "coming soon" stub can't swing
100
+ * the headline number the way a full page does. */
101
+ renderedWords: number;
102
+ }[];
103
+ };
104
+ schema: {
105
+ typesFound: string[];
106
+ missingExpected: string[];
107
+ invalidBlocks: number;
108
+ };
109
+ meta: {
110
+ pageCount: number;
111
+ missingTitle: number;
112
+ missingDescription: number;
113
+ missingCanonical: number;
114
+ missingSocial: number;
115
+ /** Pages where neither the raw nor the rendered fetch produced an extract
116
+ * — excluded from pageCount and every other meta denominator above, so a
117
+ * report reading "1 of 2 pages missing a description" doesn't silently
118
+ * hide the third page that produced nothing at all. */
119
+ pagesWithoutExtract: number;
120
+ };
121
+ headings: {
122
+ pagesWithoutH1: number;
123
+ pagesWithLevelSkips: number;
124
+ };
125
+ securityHeaders: {
126
+ present: string[];
127
+ missing: string[];
128
+ };
129
+ /** False when the sitemap.xml fetch itself failed (crawl.sidecarErrors.sitemap
130
+ * !== null), so `sitemapPresent: false` would otherwise read as "confirmed
131
+ * absent" rather than "we never got an answer". The report must say "not
132
+ * measured" for sitemap presence when this is false, and computeScores must
133
+ * treat it as neutral rather than penalizing the technical component for a
134
+ * fetch we could not complete. */
135
+ sitemapMeasured: boolean;
136
+ sitemapPresent: boolean;
137
+ /** Same as sitemapMeasured, for the llms.txt fetch (crawl.sidecarErrors.llms). */
138
+ llmsTxtMeasured: boolean;
139
+ llmsTxtPresent: boolean;
140
+ viewportOk: boolean;
141
+ };
142
+ type BuyerQuestion = {
143
+ question: string;
144
+ answered: "yes" | "partial" | "no";
145
+ /** Is there a passage an AI answer could quote verbatim? */
146
+ quotable: boolean;
147
+ page: string | null;
148
+ evidence: string | null;
149
+ };
150
+ type Fix = {
151
+ title: string;
152
+ why: string;
153
+ impact: "high" | "medium" | "low";
154
+ effort: "low" | "medium" | "high";
155
+ tier: "crawl" | "content" | "technical";
156
+ };
157
+ type AnalyzeResult = {
158
+ /** The business's proper name, as a searchable proper noun — "Acme Roofing",
159
+ * not a description of it. Empty string when the site never states one,
160
+ * which is itself a finding. This is what the visibility probes query and
161
+ * brand-match against; `business` below is prose and unsearchable. */
162
+ businessName: string;
163
+ /** The model's read of what this company does, for whom, where. */
164
+ business: string;
165
+ entityClarity: {
166
+ score: number;
167
+ missing: string[];
168
+ };
169
+ buyerQuestions: BuyerQuestion[];
170
+ /** Standalone searches for the visibility probes — what a buyer types before
171
+ * they know this company exists. Distinct from `buyerQuestions`, which are
172
+ * phrased about this site and are unanswerable on their own; see the schema
173
+ * comment in analyze.ts for what that cost us. Never mentions the business. */
174
+ categoryQueries: string[];
175
+ fixes: Fix[];
176
+ narrative: {
177
+ findability: string;
178
+ readability: string;
179
+ answers: string;
180
+ };
181
+ };
182
+ type ProbeAnswer = {
183
+ engine: string;
184
+ query: string;
185
+ /** "branded" ("who is X" / "X reviews") always echoes the name back even when
186
+ * an engine knows nothing real, so it cannot be trusted as a discoverability
187
+ * signal. "category" is a real buyer question — the one the score is built
188
+ * from. "competitor" is a head-to-head comparison query. */
189
+ kind: "branded" | "category" | "competitor";
190
+ domainCited: boolean;
191
+ brandMentioned: boolean;
192
+ citedDomains: string[];
193
+ /** First ~300 chars of the engine's answer — the report's receipt. */
194
+ snippet: string;
195
+ /** True when `snippet` is a truncated prefix of a longer answer — set
196
+ * right where the truncation happens (probes.ts's SNIPPET_CHARS), so the
197
+ * renderer never has to re-derive "was this cut short?" from a length
198
+ * check of its own against a constant it can't see. */
199
+ truncated: boolean;
200
+ /** ISO-8601, set when the answer came back — this stage's whole output is
201
+ * quotable claims about a live third party, and answer engines' answers
202
+ * drift, so a disputed line in a report needs a date attached. */
203
+ askedAt: string;
204
+ };
205
+ type ProbesResult = {
206
+ answers: ProbeAnswer[];
207
+ /** 0..100 — fraction of CATEGORY answers (real buyer questions) where the
208
+ * prospect was cited or mentioned. Null — never 0 — when no category query
209
+ * ran at all, e.g. the analyze stage failed and supplied no buyer questions.
210
+ * Deliberately excludes branded answers: "who is X"/"X reviews" echo the name
211
+ * back even when the engine knows nothing real, which would put a floor
212
+ * under the score that has nothing to do with discoverability. */
213
+ visibilityScore: number | null;
214
+ /** Did any branded query produce a real citation of the prospect's own
215
+ * domain — not merely an echo of the name in prose. Reported separately from
216
+ * visibilityScore because it answers a different question ("does the engine
217
+ * know this business exists at all") from the one the score answers ("would
218
+ * it surface this business to someone who didn't already name it"). */
219
+ brandedRecognized: boolean;
220
+ competitorsSeen: {
221
+ domain: string;
222
+ count: number;
223
+ }[];
224
+ };
225
+ type LighthouseScores = {
226
+ performance: number | null;
227
+ accessibility: number | null;
228
+ bestPractices: number | null;
229
+ seo: number | null;
230
+ summary: string;
231
+ status: AuditResult["status"];
232
+ };
233
+ type Scores = {
234
+ findability: number | null;
235
+ readability: number | null;
236
+ answers: number | null;
237
+ aiVisibility: number | null;
238
+ };
239
+ type ProspectAuditResult = {
240
+ url: string;
241
+ /** The resolved searchable proper NOUN — "Acme Roofing", not a description
242
+ * of it (mirrors `AnalyzeResult.businessName`, the value this is usually
243
+ * copied from). Distinct from `AnalyzeResult.business` (prose, nested
244
+ * under `analyze.data`) — the two share a root name only because they're
245
+ * one level apart, not because they hold the same kind of value. Null
246
+ * when no name was ever resolved (operator override empty, model returned
247
+ * none) — itself a finding, not an error. Persisted verbatim into the
248
+ * database's `business` COLUMN (kept as-is; a rename there would need its
249
+ * own migration for no benefit) — map at that one boundary, not here. */
250
+ businessName: string | null;
251
+ generatedAt: string;
252
+ scores: Scores;
253
+ /** The crawl is the one fatal stage (see pipeline.ts): a crawl failure
254
+ * throws before a ProspectAuditResult is ever built, so the `ok:false`
255
+ * branch of StageResult can never appear here. Narrowed to the success
256
+ * shape only, so consumers never handle the impossible branch. */
257
+ crawl: {
258
+ ok: true;
259
+ data: CrawlResult;
260
+ };
261
+ checks: StageResult<ChecksResult>;
262
+ lighthouse: StageResult<LighthouseScores>;
263
+ analyze: StageResult<AnalyzeResult>;
264
+ probes: StageResult<ProbesResult>;
265
+ };
266
+
267
+ export type { AnalyzeResult, BuyerQuestion, ChecksResult, CrawlResult, Fix, LighthouseScores, PageCapture, PageExtract, ProbeAnswer, ProbesResult, ProspectAuditResult, RobotsAgentAccess, Scores, StageResult };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,174 @@
1
+ import {
2
+ hostnameOf,
3
+ isHttpUrl
4
+ } from "./chunk-EVBCK2KC.js";
5
+
6
+ // src/cli/commands/prospect-audit.ts
7
+ import { mkdir, writeFile } from "fs/promises";
8
+ import { resolve } from "path";
9
+
10
+ // src/dashboard/handler-helpers.ts
11
+ var DEFAULT_DASHBOARD_BASE_URL = "https://reddoor-maintenance.netlify.app";
12
+ function resolveDashboardBaseUrl(raw) {
13
+ return (raw?.trim() || DEFAULT_DASHBOARD_BASE_URL).replace(/\/$/, "");
14
+ }
15
+
16
+ // src/cli/commands/prospect-audit.ts
17
+ function fail(message) {
18
+ return { output: message, code: 2 };
19
+ }
20
+ function errorMessage(err) {
21
+ return err instanceof Error ? err.message : String(err);
22
+ }
23
+ function scoreLine(label, value) {
24
+ return `${label.padEnd(14)} ${value === null ? "not measured" : String(value).padStart(3)}`;
25
+ }
26
+ function auditStatus(result) {
27
+ const allStagesOk = result.checks.ok && result.lighthouse.ok && result.analyze.ok && result.probes.ok;
28
+ return allStagesOk ? "complete" : "partial";
29
+ }
30
+ function slugify(s) {
31
+ const slug = s.toLowerCase().replace(/[^a-z0-9.-]+/g, "-").replace(/^-+|-+$/g, "");
32
+ return slug || "prospect";
33
+ }
34
+ async function writeRecoveryFiles(result, html) {
35
+ const dir = resolve("reports");
36
+ await mkdir(dir, { recursive: true });
37
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
38
+ const base = `prospect-audit-${slugify(hostnameOf(result.url))}-${stamp}`;
39
+ const htmlPath = resolve(dir, `${base}.html`);
40
+ const jsonPath = resolve(dir, `${base}.json`);
41
+ await writeFile(htmlPath, html, "utf-8");
42
+ await writeFile(jsonPath, JSON.stringify(result, null, 2), "utf-8");
43
+ return { htmlPath, jsonPath };
44
+ }
45
+ function summarize(result, link, file, warnings, recovery, email) {
46
+ const lines = [
47
+ `Prospect audit \u2014 ${result.businessName ?? result.url}`,
48
+ "",
49
+ scoreLine("Findability", result.scores.findability),
50
+ scoreLine("Readability", result.scores.readability),
51
+ scoreLine("Answers", result.scores.answers),
52
+ scoreLine("AI Visibility", result.scores.aiVisibility)
53
+ ];
54
+ for (const [name, stage] of [
55
+ ["checks", result.checks],
56
+ ["lighthouse", result.lighthouse],
57
+ ["analyze", result.analyze],
58
+ ["probes", result.probes]
59
+ ]) {
60
+ if (!stage.ok) lines.push(` ! ${name} not measured \u2014 ${stage.error}`);
61
+ }
62
+ if (file) lines.push("", `Report written to ${file}`);
63
+ if (link) lines.push("", `Shareable link: ${link}`);
64
+ if (email?.sent) {
65
+ lines.push("", `Emailed to ${email.recipients.join(", ")} (${email.messageId})`);
66
+ }
67
+ for (const w of warnings) lines.push("", `! ${w}`);
68
+ if (recovery) {
69
+ lines.push(
70
+ "",
71
+ "Neither --out nor the database took this report \u2014 wrote a recovery copy instead:",
72
+ ` ${recovery.htmlPath}`,
73
+ ` ${recovery.jsonPath}`
74
+ );
75
+ }
76
+ return lines.join("\n");
77
+ }
78
+ async function runProspectAuditCommand(url, opts) {
79
+ if (!isHttpUrl(url)) {
80
+ return fail(`"${url}" is not a URL. Pass the full address, e.g. https://example.com`);
81
+ }
82
+ const canPersist = Boolean(process.env.TURSO_DATABASE_URL);
83
+ if (!canPersist && !opts.out) {
84
+ return fail(
85
+ "No TURSO_DATABASE_URL, so the report cannot be saved or shared. Re-run with --out <file>, or set the Turso credentials."
86
+ );
87
+ }
88
+ const { runProspectAudit } = await import("./pipeline-3AANZFCR.js");
89
+ const onStage = (name, status, detail) => {
90
+ if (status === "start") console.error(`\u2026 ${name}`);
91
+ else if (status === "ok") console.error(`\u2713 ${name}`);
92
+ else console.error(`! ${name} \u2014 ${detail ?? "failed"}`);
93
+ };
94
+ const business = opts.business?.trim();
95
+ const result = await runProspectAudit(
96
+ url,
97
+ {
98
+ ...business ? { business } : {},
99
+ ...opts.competitors ? {
100
+ competitors: opts.competitors.split(",").map((c) => c.trim()).filter(Boolean)
101
+ } : {},
102
+ ...opts.probes === false ? { probes: false } : {}
103
+ },
104
+ { ...opts.deps ?? {}, onStage }
105
+ );
106
+ const { renderProspectReport } = await import("./render-IFOGFB5T.js");
107
+ const html = renderProspectReport(result);
108
+ const warnings = [];
109
+ let file = null;
110
+ if (opts.out) {
111
+ try {
112
+ await writeFile(opts.out, html, "utf-8");
113
+ file = opts.out;
114
+ } catch (err) {
115
+ warnings.push(`Could not write --out (${opts.out}): ${errorMessage(err)}`);
116
+ }
117
+ }
118
+ let link = null;
119
+ let token = null;
120
+ let auditId = null;
121
+ if (canPersist) {
122
+ try {
123
+ const { openDb, readDbConfig } = await import("./client-AUE3XUPO.js");
124
+ const { createProspectAudit } = await import("./prospect-audits-3PMIO73D.js");
125
+ const db = await openDb(readDbConfig());
126
+ const created = await createProspectAudit(db, {
127
+ url: result.url,
128
+ // Map at the boundary: ProspectAuditResult.businessName is the field
129
+ // name (Item 2 — it's a resolved NAME, not a description); the
130
+ // `prospect_audits.business` column keeps its existing name, since
131
+ // renaming a column needs its own migration for no benefit here.
132
+ business: result.businessName,
133
+ status: auditStatus(result),
134
+ resultJson: JSON.stringify(result)
135
+ });
136
+ auditId = created.id;
137
+ token = created.token;
138
+ link = `${resolveDashboardBaseUrl(process.env.DASHBOARD_BASE_URL)}/r/${token}`;
139
+ } catch (err) {
140
+ warnings.push(`Could not save to the database: ${errorMessage(err)}`);
141
+ }
142
+ }
143
+ const delivered = file !== null || link !== null;
144
+ const recovery = delivered ? null : await writeRecoveryFiles(result, html);
145
+ const code = delivered ? 0 : 1;
146
+ let email = null;
147
+ if (opts.email) {
148
+ try {
149
+ const { sendAuditEmail, parseProspectAuditRecipients } = await import("./email-KJXASVH5.js");
150
+ const recipients = parseProspectAuditRecipients(process.env.PROSPECT_AUDIT_RECIPIENTS);
151
+ email = await sendAuditEmail(result, { link, recipients, auditId });
152
+ if (!email.sent) {
153
+ warnings.push(`Could not email the audit sheet: ${email.reason}`);
154
+ }
155
+ } catch (err) {
156
+ warnings.push(`Could not email the audit sheet: ${errorMessage(err)}`);
157
+ }
158
+ }
159
+ if (opts.json) {
160
+ return {
161
+ output: JSON.stringify(
162
+ { result, out: file, link, token, recovery, warnings, email },
163
+ null,
164
+ 2
165
+ ),
166
+ code
167
+ };
168
+ }
169
+ return { output: summarize(result, link, file, warnings, recovery, email), code };
170
+ }
171
+ export {
172
+ runProspectAuditCommand
173
+ };
174
+ //# sourceMappingURL=prospect-audit-KSXUNQ5J.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/prospect-audit.ts","../src/dashboard/handler-helpers.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { hostnameOf, isHttpUrl } from \"../../util/url.js\";\nimport { resolveDashboardBaseUrl } from \"../../dashboard/handler-helpers.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\";\n\nexport type ProspectAuditCliOptions = {\n business?: 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 /** 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 scoreLine(\"AI Visibility\", result.scores.aiVisibility),\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 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 result = await runProspectAudit(\n url,\n {\n ...(business ? { business } : {}),\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 },\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 });\n auditId = created.id;\n token = created.token;\n link = `${resolveDashboardBaseUrl(process.env.DASHBOARD_BASE_URL)}/r/${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 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 });\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","/**\n * Small pure helpers extracted from the Netlify dashboard handlers\n * (fleet-homepage.mts, site-dashboard.mts) so their non-glue logic is\n * unit-testable without booting a Netlify function.\n */\n\nconst DEFAULT_DASHBOARD_BASE_URL = \"https://reddoor-maintenance.netlify.app\";\n\n/**\n * Resolve the dashboard's public base URL from the optional DASHBOARD_BASE_URL\n * env var: trim it, fall back to the canonical Netlify URL when unset/blank,\n * and strip a trailing slash so callers can append `/s/:slug` cleanly.\n */\nexport function resolveDashboardBaseUrl(raw: string | undefined): string {\n return (raw?.trim() || DEFAULT_DASHBOARD_BASE_URL).replace(/\\/$/, \"\");\n}\n\n/**\n * Resolve the requested site slug from the path param (Netlify\n * `ctx.params.slug`) or, lacking it, the `?slug=` query param. Null when\n * neither is present (→ the handler's presence-only health check).\n */\nexport function resolveSlug(\n paramSlug: string | undefined,\n querySlug: string | null,\n): string | null {\n return paramSlug ?? querySlug ?? null;\n}\n\n/**\n * Build a 502 for an UNEXPECTED handler failure (an Airtable 429/500, a network\n * timeout). Logs the real error server-side so the operator sees it in the\n * function logs, but returns a generic, retry-able body — never the error/stack\n * itself — so a transient backend hiccup degrades to a clean \"try again\" rather\n * than an unhandled rejection (whose surfaced status/body we don't control).\n * Use in each handler's top-level catch around the Airtable + render section.\n */\nexport function handlerError(service: string, err: unknown): Response {\n const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);\n console.error(`[${service}] unexpected failure: ${detail}`);\n return new Response(\"Temporarily unavailable — please retry in a moment.\", {\n status: 502,\n headers: { \"content-type\": \"text/plain; charset=utf-8\" },\n });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,19 @@
1
+ import {
2
+ MAX_RECENT_PROSPECT_AUDITS,
3
+ createProspectAudit,
4
+ generateToken,
5
+ getProspectAuditByToken,
6
+ isValidToken,
7
+ listRecentProspectAudits,
8
+ newProspectAuditId
9
+ } from "./chunk-NYUYNYQL.js";
10
+ export {
11
+ MAX_RECENT_PROSPECT_AUDITS,
12
+ createProspectAudit,
13
+ generateToken,
14
+ getProspectAuditByToken,
15
+ isValidToken,
16
+ listRecentProspectAudits,
17
+ newProspectAuditId
18
+ };
19
+ //# sourceMappingURL=prospect-audits-3PMIO73D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -3,8 +3,8 @@ import {
3
3
  } from "./chunk-MNDIED4M.js";
4
4
  import {
5
5
  makeGitHub
6
- } from "./chunk-VRWUL4TR.js";
7
- import "./chunk-4DHQCN5H.js";
6
+ } from "./chunk-XHHMM5HX.js";
7
+ import "./chunk-LBYOLBW7.js";
8
8
 
9
9
  // src/audits/protection-coverage.ts
10
10
  var RENOVATE_WORKFLOW_FILE = "renovate.yml";
@@ -165,4 +165,4 @@ export {
165
165
  protectionAuditExitCode,
166
166
  runProtectionAuditCommand
167
167
  };
168
- //# sourceMappingURL=protection-audit-64PLLBLI.js.map
168
+ //# sourceMappingURL=protection-audit-SJO53MEQ.js.map
@@ -2,11 +2,11 @@ import {
2
2
  ALL_CONFIG_NAMES,
3
3
  isConfigName,
4
4
  syncConfigs
5
- } from "../chunk-Q276MZ7E.js";
5
+ } from "../chunk-7UAYMAKR.js";
6
6
  import "../chunk-73UFRZKY.js";
7
- import "../chunk-ZTPPCHDB.js";
8
- import "../chunk-XXTZBPUY.js";
7
+ import "../chunk-3G25KIWW.js";
9
8
  import "../chunk-XTK5VIQB.js";
9
+ import "../chunk-XXTZBPUY.js";
10
10
  export {
11
11
  ALL_CONFIG_NAMES,
12
12
  isConfigName,
@@ -0,0 +1,10 @@
1
+ import {
2
+ renderProspectReport
3
+ } from "./chunk-DEWJMMG3.js";
4
+ import "./chunk-HSI6Q7MI.js";
5
+ import "./chunk-DJZWYIPP.js";
6
+ import "./chunk-EVBCK2KC.js";
7
+ export {
8
+ renderProspectReport
9
+ };
10
+ //# sourceMappingURL=render-IFOGFB5T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,17 @@
1
+ import {
2
+ renderReportFromRow,
3
+ requireLighthouse,
4
+ windowDays
5
+ } from "./chunk-ZGBEIWSC.js";
6
+ import "./chunk-GECESMVS.js";
7
+ import "./chunk-MT2JINSS.js";
8
+ import "./chunk-DJZWYIPP.js";
9
+ import "./chunk-5UINJNAJ.js";
10
+ import "./chunk-7NYN2P6I.js";
11
+ import "./chunk-EVBCK2KC.js";
12
+ export {
13
+ renderReportFromRow,
14
+ requireLighthouse,
15
+ windowDays
16
+ };
17
+ //# sourceMappingURL=render-from-row-BWSOIYDS.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -2,19 +2,19 @@ import {
2
2
  isRenovatePR
3
3
  } from "./chunk-PZQ3WED2.js";
4
4
  import {
5
- makeGitHub
6
- } from "./chunk-VRWUL4TR.js";
7
- import "./chunk-4DHQCN5H.js";
5
+ openBase,
6
+ readAirtableConfig
7
+ } from "./chunk-HN6I2XWI.js";
8
8
  import {
9
9
  allActionableVulnsTransitive,
10
10
  isDashboardVisible,
11
11
  listWebsites,
12
12
  updateAutoFixAttempts
13
- } from "./chunk-577HCGZO.js";
13
+ } from "./chunk-7NYN2P6I.js";
14
14
  import {
15
- openBase,
16
- readAirtableConfig
17
- } from "./chunk-HN6I2XWI.js";
15
+ makeGitHub
16
+ } from "./chunk-XHHMM5HX.js";
17
+ import "./chunk-LBYOLBW7.js";
18
18
  import "./chunk-6VAL7XJT.js";
19
19
 
20
20
  // src/github/renovate-dispatch.ts
@@ -130,10 +130,10 @@ async function runRenovateDispatchCommand(opts) {
130
130
  for (const f of result.failed) lines.push(` \u21B3 ${f.repo}: ${f.error}`);
131
131
  }
132
132
  const attemptUpdates = computeAutoFixAttemptUpdates(websites, result);
133
- const attemptTally = await applyAutoFixAttemptUpdates(
134
- attemptUpdates,
135
- (id, attempts) => updateAutoFixAttempts(base, id, attempts)
136
- );
133
+ const attemptTally = await applyAutoFixAttemptUpdates(attemptUpdates, async (id, attempts) => {
134
+ const fields = await updateAutoFixAttempts(base, id, attempts);
135
+ await opts.siteMirror?.health(id, fields);
136
+ });
137
137
  lines.push(formatRenovateDispatchSummary(result));
138
138
  lines.push(formatAutoFixAttemptsSummary(attemptTally));
139
139
  return { output: lines.join("\n"), code: 0 };
@@ -141,4 +141,4 @@ async function runRenovateDispatchCommand(opts) {
141
141
  export {
142
142
  runRenovateDispatchCommand
143
143
  };
144
- //# sourceMappingURL=renovate-dispatch-ICJGSFCE.js.map
144
+ //# sourceMappingURL=renovate-dispatch-6GQWTRKX.js.map