@reddoorla/maintenance 0.95.1 → 0.97.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.
- package/README.md +41 -7
- package/dist/{announce-CLQYX5I4.js → announce-5ZD2SBGD.js} +39 -31
- package/dist/announce-5ZD2SBGD.js.map +1 -0
- package/dist/attachments-JFGFQRXQ.js +10 -0
- package/dist/{blux-4KNXUP2K.js → blux-NXQLMS4W.js} +1 -1
- package/dist/{bump-deps-C5G2EFBB.js → bump-deps-775VLDYB.js} +6 -6
- package/dist/{chunk-FUWFLAWQ.js → chunk-3RLDX3Q2.js} +5 -5
- package/dist/{chunk-XHHMM5HX.js → chunk-43SW3FGW.js} +63 -1
- package/dist/chunk-43SW3FGW.js.map +1 -0
- package/dist/{chunk-R53VL3RI.js → chunk-5CJVUPJU.js} +1 -1
- package/dist/{chunk-R53VL3RI.js.map → chunk-5CJVUPJU.js.map} +1 -1
- package/dist/{chunk-S7RYFUIK.js → chunk-6PGXKN6F.js} +2 -2
- package/dist/{chunk-HTABUCF4.js → chunk-6THEZQLA.js} +3 -3
- package/dist/chunk-A5QKNOXW.js +72 -0
- package/dist/chunk-A5QKNOXW.js.map +1 -0
- package/dist/chunk-ASLDCWBF.js +41 -0
- package/dist/chunk-ASLDCWBF.js.map +1 -0
- package/dist/{chunk-CWXTNPNZ.js → chunk-AYDQQBNG.js} +3 -3
- package/dist/chunk-AYDQQBNG.js.map +1 -0
- package/dist/{chunk-WVNDEYXL.js → chunk-BINFZQ2T.js} +57 -6
- package/dist/chunk-BINFZQ2T.js.map +1 -0
- package/dist/{chunk-2NQUCS7O.js → chunk-BIYQN7FH.js} +3 -1
- package/dist/{chunk-2NQUCS7O.js.map → chunk-BIYQN7FH.js.map} +1 -1
- package/dist/{chunk-YLYXW5PH.js → chunk-CXICCLNM.js} +34 -4
- package/dist/chunk-CXICCLNM.js.map +1 -0
- package/dist/{chunk-BYGPUUCT.js → chunk-DRNZVORX.js} +66 -30
- package/dist/chunk-DRNZVORX.js.map +1 -0
- package/dist/{chunk-LM2NHRCJ.js → chunk-DYTYPKFN.js} +4 -4
- package/dist/chunk-DYTYPKFN.js.map +1 -0
- package/dist/{chunk-DFKBTBO3.js → chunk-EN6CTBUN.js} +35 -6
- package/dist/chunk-EN6CTBUN.js.map +1 -0
- package/dist/{chunk-5EEMTJMT.js → chunk-FHFXJ3EM.js} +8 -6
- package/dist/chunk-FHFXJ3EM.js.map +1 -0
- package/dist/{chunk-Z2K4JPQ3.js → chunk-FP6HOW6J.js} +10 -7
- package/dist/chunk-FP6HOW6J.js.map +1 -0
- package/dist/{chunk-2LYOAB2E.js → chunk-HSMN35LU.js} +2 -2
- package/dist/chunk-ICSRWW5Q.js +148 -0
- package/dist/chunk-ICSRWW5Q.js.map +1 -0
- package/dist/{chunk-FSGICFXB.js → chunk-IDBPII6G.js} +17 -5
- package/dist/chunk-IDBPII6G.js.map +1 -0
- package/dist/{chunk-C3IQQKXL.js → chunk-IOTOOTII.js} +121 -1
- package/dist/{chunk-C3IQQKXL.js.map → chunk-IOTOOTII.js.map} +1 -1
- package/dist/{chunk-BCKEP5S6.js → chunk-JHJNNJLG.js} +2 -2
- package/dist/{chunk-TO7DJYLZ.js → chunk-L3S3QZXL.js} +178 -39
- package/dist/chunk-L3S3QZXL.js.map +1 -0
- package/dist/{chunk-WSJ7RO54.js → chunk-LAG6IP7Y.js} +23 -5
- package/dist/chunk-LAG6IP7Y.js.map +1 -0
- package/dist/{chunk-WJMPCVOR.js → chunk-LAJ3FF54.js} +12 -4
- package/dist/chunk-LAJ3FF54.js.map +1 -0
- package/dist/{chunk-2ZAZ7ETT.js → chunk-LDHRIQUW.js} +32 -18
- package/dist/chunk-LDHRIQUW.js.map +1 -0
- package/dist/{chunk-6R56OKJZ.js → chunk-LF2TJN4X.js} +2 -2
- package/dist/chunk-LF2TJN4X.js.map +1 -0
- package/dist/{chunk-ZQV7UQNE.js → chunk-LUF52EIY.js} +2 -2
- package/dist/{chunk-5UINJNAJ.js → chunk-LZMAG73U.js} +34 -21
- package/dist/chunk-LZMAG73U.js.map +1 -0
- package/dist/{chunk-VSGMCCGQ.js → chunk-MMSMZC7S.js} +2 -2
- package/dist/chunk-MVKNU4QD.js +37 -0
- package/dist/chunk-MVKNU4QD.js.map +1 -0
- package/dist/{chunk-XPJQXSC4.js → chunk-P7323YVW.js} +27 -5
- package/dist/chunk-P7323YVW.js.map +1 -0
- package/dist/{chunk-KJRVMMM3.js → chunk-PARKXDXI.js} +36 -5
- package/dist/chunk-PARKXDXI.js.map +1 -0
- package/dist/{chunk-4763KQ6O.js → chunk-PXRN2MER.js} +2 -2
- package/dist/{chunk-WMBLR5VA.js → chunk-PZZ6HM5T.js} +3 -3
- package/dist/{chunk-TR3HFH6J.js → chunk-QFSL2L2B.js} +67 -10
- package/dist/chunk-QFSL2L2B.js.map +1 -0
- package/dist/chunk-RQJFGK3A.js +51 -0
- package/dist/chunk-RQJFGK3A.js.map +1 -0
- package/dist/{chunk-3Z6X3BHU.js → chunk-TC4GANMX.js} +53 -150
- package/dist/chunk-TC4GANMX.js.map +1 -0
- package/dist/{chunk-F3JWOPTG.js → chunk-TEMQPSCM.js} +45 -105
- package/dist/chunk-TEMQPSCM.js.map +1 -0
- package/dist/chunk-TJLKWQOX.js +52 -0
- package/dist/chunk-TJLKWQOX.js.map +1 -0
- package/dist/{chunk-CJ2RMTT7.js → chunk-TV76A6GN.js} +2 -2
- package/dist/{chunk-KZRR4NFG.js → chunk-TVZ6QCOE.js} +3 -3
- package/dist/{chunk-BVEDKMNG.js → chunk-U2ZI6G7H.js} +29 -11
- package/dist/chunk-U2ZI6G7H.js.map +1 -0
- package/dist/chunk-UPE6D4AT.js +69 -0
- package/dist/chunk-UPE6D4AT.js.map +1 -0
- package/dist/{chunk-R5IMFAFR.js → chunk-VMGHQUTV.js} +7 -16
- package/dist/chunk-VMGHQUTV.js.map +1 -0
- package/dist/chunk-VQMAAQ5M.js +53 -0
- package/dist/chunk-VQMAAQ5M.js.map +1 -0
- package/dist/{chunk-AVEQUR44.js → chunk-WMF5S2MB.js} +60 -17
- package/dist/chunk-WMF5S2MB.js.map +1 -0
- package/dist/{chunk-SVAAONYB.js → chunk-WQLNUEZL.js} +12 -2
- package/dist/{chunk-SVAAONYB.js.map → chunk-WQLNUEZL.js.map} +1 -1
- package/dist/{chunk-XN37VTDB.js → chunk-X56LAKPR.js} +3 -3
- package/dist/chunk-X56LAKPR.js.map +1 -0
- package/dist/{chunk-ZCL7C5LH.js → chunk-Y6FCXRIC.js} +1661 -387
- package/dist/chunk-Y6FCXRIC.js.map +1 -0
- package/dist/{chunk-2SLED3NB.js → chunk-YDZ7XGPZ.js} +1 -1
- package/dist/{chunk-2SLED3NB.js.map → chunk-YDZ7XGPZ.js.map} +1 -1
- package/dist/{chunk-IY6256GK.js → chunk-YJBSB4LP.js} +6 -1
- package/dist/chunk-YJBSB4LP.js.map +1 -0
- package/dist/{chunk-BI2VI6EM.js → chunk-YXWBM26P.js} +2 -2
- package/dist/{chunk-QF6IQQFP.js → chunk-Z7FDJ35L.js} +18 -12
- package/dist/chunk-Z7FDJ35L.js.map +1 -0
- package/dist/cli/bin.js +111 -51
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.d.ts +33 -3
- package/dist/cli/commands/audit.js +28 -25
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/client-DGTO65WB.js +11 -0
- package/dist/configs/playwright-a11y.d.ts +11 -1
- package/dist/configs/playwright-a11y.js +3 -1
- package/dist/{convert-to-pnpm-YTFREUJK.js → convert-to-pnpm-KLWA53N5.js} +6 -6
- package/dist/{db-XKZYDCKM.js → db-II5WAK4D.js} +45 -20
- package/dist/db-II5WAK4D.js.map +1 -0
- package/dist/deadletter-G4M3HSVM.js +17 -0
- package/dist/{digest-7UBGUBRK.js → digest-CWKK2DMR.js} +56 -32
- package/dist/digest-CWKK2DMR.js.map +1 -0
- package/dist/{digest-collectors-DJFN5ZDK.js → digest-collectors-CYTN2EUX.js} +17 -12
- package/dist/{digest-state-3KHZF7GD.js → digest-state-ZDPKR7MW.js} +18 -2
- package/dist/digest-state-ZDPKR7MW.js.map +1 -0
- package/dist/ensure-site-2QZHFVQ3.js +70 -0
- package/dist/ensure-site-2QZHFVQ3.js.map +1 -0
- package/dist/ensure-site-JEKV2ODE.js +105 -0
- package/dist/ensure-site-JEKV2ODE.js.map +1 -0
- package/dist/{ensure-site-OAAE5YE2.js → ensure-site-OO4UPWNQ.js} +49 -44
- package/dist/ensure-site-OO4UPWNQ.js.map +1 -0
- package/dist/fleet-events-CkOZpMVC.d.ts +435 -0
- package/dist/{fleet-state-UEVZ3X3J.js → fleet-state-RJROYXKO.js} +19 -7
- package/dist/{forms-notify-target-UC6PF3AE.js → forms-notify-target-X6UEOI2M.js} +19 -15
- package/dist/forms-notify-target-X6UEOI2M.js.map +1 -0
- package/dist/{freeze-A6MUCZU4.js → freeze-GUQ43EAU.js} +2 -2
- package/dist/{github-signals-TX5FPYP6.js → github-signals-QIRTHHET.js} +22 -17
- package/dist/github-signals-QIRTHHET.js.map +1 -0
- package/dist/{header-image-PSPT7CUM.js → header-image-H7ZJAV26.js} +15 -18
- package/dist/header-image-H7ZJAV26.js.map +1 -0
- package/dist/{health-endpoint-KVCJYWET.js → health-endpoint-BJ6BKRHF.js} +8 -7
- package/dist/{health-endpoint-KVCJYWET.js.map → health-endpoint-BJ6BKRHF.js.map} +1 -1
- package/dist/{health-mirror-XJAKA3EY.js → health-mirror-KU3G7V2Y.js} +10 -10
- package/dist/{import-airtable-TAKSN4NV.js → import-airtable-UBF5GYAL.js} +3 -3
- package/dist/index.d.ts +158 -419
- package/dist/index.js +91 -46
- package/dist/index.js.map +1 -1
- package/dist/{init-FFE4FCO2.js → init-GOWDA5QS.js} +19 -18
- package/dist/{init-FFE4FCO2.js.map → init-GOWDA5QS.js.map} +1 -1
- package/dist/{launch-UD36AHUN.js → launch-7N3X2MXB.js} +229 -79
- package/dist/launch-7N3X2MXB.js.map +1 -0
- package/dist/{lighthouse-VHI77PFB.js → lighthouse-52WSPZ5D.js} +2 -2
- package/dist/lighthouse-airtable-M45OPVRV.js +14 -0
- package/dist/match-harness-GBCDSVPS.js +417 -0
- package/dist/match-harness-GBCDSVPS.js.map +1 -0
- package/dist/migrate-XZ7U5DFC.js +7 -0
- package/dist/{notify-IISTHNBR.js → notify-K3WVH3DM.js} +4 -4
- package/dist/{onboard-TM2KESGF.js → onboard-JQQIMQW7.js} +6 -6
- package/dist/{orchestrate-HYVQPRCE.js → orchestrate-L2A5BZFY.js} +13 -10
- package/dist/parity-2JGVUCZD.js +14 -0
- package/dist/{pipeline-YROAY6DT.js → pipeline-UUPXOS2J.js} +3 -3
- package/dist/{preflight-RYIP54NY.js → preflight-O7Q6FWHU.js} +35 -11
- package/dist/preflight-O7Q6FWHU.js.map +1 -0
- package/dist/{prismic-ci-6GKKZGB5.js → prismic-ci-NAPWOXXD.js} +39 -9
- package/dist/prismic-ci-NAPWOXXD.js.map +1 -0
- package/dist/{prismic-models-P6FWWVRS.js → prismic-models-LL6ORTQ2.js} +20 -17
- package/dist/prismic-models-LL6ORTQ2.js.map +1 -0
- package/dist/prospect/types.d.ts +72 -0
- package/dist/prospect/types.js +1 -1
- package/dist/{prospect-audit-PRHU53HS.js → prospect-audit-FMPGQIRQ.js} +16 -7
- package/dist/prospect-audit-FMPGQIRQ.js.map +1 -0
- package/dist/{prospect-audits-R3LWF6U7.js → prospect-audits-IZVQY3OQ.js} +4 -2
- package/dist/protection-audit-YXJKFU6O.js +413 -0
- package/dist/protection-audit-YXJKFU6O.js.map +1 -0
- package/dist/recipes/sync-configs.js +3 -3
- package/dist/{render-YGNGTX3W.js → render-MHUUHPLN.js} +36 -9
- package/dist/render-MHUUHPLN.js.map +1 -0
- package/dist/{render-from-row-IFSBHWDH.js → render-from-row-75JTED2Z.js} +6 -4
- package/dist/{renovate-dispatch-U5Q52SKT.js → renovate-dispatch-2IHAZDVA.js} +14 -9
- package/dist/{renovate-dispatch-U5Q52SKT.js.map → renovate-dispatch-2IHAZDVA.js.map} +1 -1
- package/dist/{replay-PCSQOGCZ.js → replay-4FVFOMHH.js} +10 -23
- package/dist/replay-4FVFOMHH.js.map +1 -0
- package/dist/{report-LQS4ECTM.js → report-IAHXN5CW.js} +103 -57
- package/dist/report-IAHXN5CW.js.map +1 -0
- package/dist/{report-mirror-HDTOJE64.js → report-mirror-4QM57LMS.js} +34 -12
- package/dist/report-mirror-4QM57LMS.js.map +1 -0
- package/dist/roster-GDNSIRAK.js +19 -0
- package/dist/roster-GDNSIRAK.js.map +1 -0
- package/dist/{schema-A4WCPIF4.js → schema-KWUQEZ5O.js} +1 -1
- package/dist/schema-KWUQEZ5O.js.map +1 -0
- package/dist/{self-updating-MWXIZLL7.js → self-updating-ZUWPGYUP.js} +8 -7
- package/dist/{self-updating-MWXIZLL7.js.map → self-updating-ZUWPGYUP.js.map} +1 -1
- package/dist/{selftest-VXTHRPRN.js → selftest-G4W3LIBQ.js} +21 -17
- package/dist/{selftest-VXTHRPRN.js.map → selftest-G4W3LIBQ.js.map} +1 -1
- package/dist/site-create-ZU3ACBTO.js +45 -0
- package/dist/site-create-ZU3ACBTO.js.map +1 -0
- package/dist/site-lookup-DTOLJ4EA.js +27 -0
- package/dist/site-lookup-DTOLJ4EA.js.map +1 -0
- package/dist/{site-mirror-WON4FTJ2.js → site-mirror-WR7ACXOV.js} +26 -12
- package/dist/site-mirror-WR7ACXOV.js.map +1 -0
- package/dist/{smoke-suite-7DGITCTZ.js → smoke-suite-WAUPI6EJ.js} +8 -7
- package/dist/{smoke-suite-7DGITCTZ.js.map → smoke-suite-WAUPI6EJ.js.map} +1 -1
- package/dist/{submissions-OVZPVBYG.js → submissions-2SHCFOKD.js} +12 -7
- package/dist/submissions-2SHCFOKD.js.map +1 -0
- package/dist/{submissions-3I3GAQAX.js → submissions-JDZP35JM.js} +49 -9
- package/dist/submissions-JDZP35JM.js.map +1 -0
- package/dist/{svelte-codemods-WSV2FGNG.js → svelte-codemods-VOLOKKFI.js} +6 -6
- package/dist/{sync-K3PKHVLH.js → sync-F4USL4K5.js} +6 -6
- package/dist/{sync-configs-RRIHNLIJ.js → sync-configs-OXSWKVZ2.js} +6 -6
- package/dist/turso-KQKBBQCK.js +24 -0
- package/dist/turso-KQKBBQCK.js.map +1 -0
- package/dist/{upgrade-VTD6V4HS.js → upgrade-EVAL3JTO.js} +6 -6
- package/dist/util/git.d.ts +49 -10
- package/dist/util/git.js +1 -1
- package/dist/{webflow-IRKOIT7S.js → webflow-624FEBFD.js} +4 -4
- package/dist/{websites-TB4TCAWM.js → websites-Y6FN2XFD.js} +27 -20
- package/dist/{write-audits-to-airtable-53EVXKMU.js → write-audits-to-airtable-TI3JMWSY.js} +7 -5
- package/package.json +3 -2
- package/dist/airtable-JGCPS7JH.js +0 -9
- package/dist/announce-CLQYX5I4.js.map +0 -1
- package/dist/attachments-NLZIJ5NU.js +0 -9
- package/dist/chunk-2ZAZ7ETT.js.map +0 -1
- package/dist/chunk-3Z6X3BHU.js.map +0 -1
- package/dist/chunk-5EEMTJMT.js.map +0 -1
- package/dist/chunk-5GUJ36TL.js +0 -53
- package/dist/chunk-5GUJ36TL.js.map +0 -1
- package/dist/chunk-5UINJNAJ.js.map +0 -1
- package/dist/chunk-6R56OKJZ.js.map +0 -1
- package/dist/chunk-AVEQUR44.js.map +0 -1
- package/dist/chunk-BVEDKMNG.js.map +0 -1
- package/dist/chunk-BYGPUUCT.js.map +0 -1
- package/dist/chunk-CWXTNPNZ.js.map +0 -1
- package/dist/chunk-DFKBTBO3.js.map +0 -1
- package/dist/chunk-F3JWOPTG.js.map +0 -1
- package/dist/chunk-FSGICFXB.js.map +0 -1
- package/dist/chunk-GASBX52O.js +0 -12
- package/dist/chunk-GASBX52O.js.map +0 -1
- package/dist/chunk-IY6256GK.js.map +0 -1
- package/dist/chunk-KJRVMMM3.js.map +0 -1
- package/dist/chunk-LM2NHRCJ.js.map +0 -1
- package/dist/chunk-QF6IQQFP.js.map +0 -1
- package/dist/chunk-R5IMFAFR.js.map +0 -1
- package/dist/chunk-TO7DJYLZ.js.map +0 -1
- package/dist/chunk-TR3HFH6J.js.map +0 -1
- package/dist/chunk-WJMPCVOR.js.map +0 -1
- package/dist/chunk-WSJ7RO54.js.map +0 -1
- package/dist/chunk-WVNDEYXL.js.map +0 -1
- package/dist/chunk-XHHMM5HX.js.map +0 -1
- package/dist/chunk-XN37VTDB.js.map +0 -1
- package/dist/chunk-XPJQXSC4.js.map +0 -1
- package/dist/chunk-YFAXOD7Z.js +0 -42
- package/dist/chunk-YFAXOD7Z.js.map +0 -1
- package/dist/chunk-YLYXW5PH.js.map +0 -1
- package/dist/chunk-Z2K4JPQ3.js.map +0 -1
- package/dist/chunk-ZCL7C5LH.js.map +0 -1
- package/dist/client-G2WGWPCA.js +0 -11
- package/dist/db-XKZYDCKM.js.map +0 -1
- package/dist/digest-7UBGUBRK.js.map +0 -1
- package/dist/digest-state-3KHZF7GD.js.map +0 -1
- package/dist/ensure-site-OAAE5YE2.js.map +0 -1
- package/dist/fleet-events-cF2Om88w.d.ts +0 -24
- package/dist/forms-notify-target-UC6PF3AE.js.map +0 -1
- package/dist/github-signals-TX5FPYP6.js.map +0 -1
- package/dist/header-image-PSPT7CUM.js.map +0 -1
- package/dist/launch-UD36AHUN.js.map +0 -1
- package/dist/lighthouse-airtable-YMH4UA7R.js +0 -12
- package/dist/match-harness-QRG6XZAR.js +0 -70
- package/dist/match-harness-QRG6XZAR.js.map +0 -1
- package/dist/migrate-Y7R4CESL.js +0 -7
- package/dist/parity-CAJ477PS.js +0 -14
- package/dist/preflight-RYIP54NY.js.map +0 -1
- package/dist/prismic-ci-6GKKZGB5.js.map +0 -1
- package/dist/prismic-models-P6FWWVRS.js.map +0 -1
- package/dist/prospect-audit-PRHU53HS.js.map +0 -1
- package/dist/protection-audit-SJO53MEQ.js +0 -168
- package/dist/protection-audit-SJO53MEQ.js.map +0 -1
- package/dist/render-YGNGTX3W.js.map +0 -1
- package/dist/replay-PCSQOGCZ.js.map +0 -1
- package/dist/report-LQS4ECTM.js.map +0 -1
- package/dist/report-mirror-HDTOJE64.js.map +0 -1
- package/dist/schema-A4WCPIF4.js.map +0 -1
- package/dist/site-mirror-WON4FTJ2.js.map +0 -1
- package/dist/submissions-3I3GAQAX.js.map +0 -1
- package/dist/submissions-OVZPVBYG.js.map +0 -1
- /package/dist/{airtable-JGCPS7JH.js.map → attachments-JFGFQRXQ.js.map} +0 -0
- /package/dist/{blux-4KNXUP2K.js.map → blux-NXQLMS4W.js.map} +0 -0
- /package/dist/{bump-deps-C5G2EFBB.js.map → bump-deps-775VLDYB.js.map} +0 -0
- /package/dist/{chunk-FUWFLAWQ.js.map → chunk-3RLDX3Q2.js.map} +0 -0
- /package/dist/{chunk-S7RYFUIK.js.map → chunk-6PGXKN6F.js.map} +0 -0
- /package/dist/{chunk-HTABUCF4.js.map → chunk-6THEZQLA.js.map} +0 -0
- /package/dist/{chunk-2LYOAB2E.js.map → chunk-HSMN35LU.js.map} +0 -0
- /package/dist/{chunk-BCKEP5S6.js.map → chunk-JHJNNJLG.js.map} +0 -0
- /package/dist/{chunk-ZQV7UQNE.js.map → chunk-LUF52EIY.js.map} +0 -0
- /package/dist/{chunk-VSGMCCGQ.js.map → chunk-MMSMZC7S.js.map} +0 -0
- /package/dist/{chunk-4763KQ6O.js.map → chunk-PXRN2MER.js.map} +0 -0
- /package/dist/{chunk-WMBLR5VA.js.map → chunk-PZZ6HM5T.js.map} +0 -0
- /package/dist/{chunk-CJ2RMTT7.js.map → chunk-TV76A6GN.js.map} +0 -0
- /package/dist/{chunk-KZRR4NFG.js.map → chunk-TVZ6QCOE.js.map} +0 -0
- /package/dist/{chunk-BI2VI6EM.js.map → chunk-YXWBM26P.js.map} +0 -0
- /package/dist/{attachments-NLZIJ5NU.js.map → client-DGTO65WB.js.map} +0 -0
- /package/dist/{convert-to-pnpm-YTFREUJK.js.map → convert-to-pnpm-KLWA53N5.js.map} +0 -0
- /package/dist/{client-G2WGWPCA.js.map → deadletter-G4M3HSVM.js.map} +0 -0
- /package/dist/{digest-collectors-DJFN5ZDK.js.map → digest-collectors-CYTN2EUX.js.map} +0 -0
- /package/dist/{fleet-state-UEVZ3X3J.js.map → fleet-state-RJROYXKO.js.map} +0 -0
- /package/dist/{freeze-A6MUCZU4.js.map → freeze-GUQ43EAU.js.map} +0 -0
- /package/dist/{health-mirror-XJAKA3EY.js.map → health-mirror-KU3G7V2Y.js.map} +0 -0
- /package/dist/{import-airtable-TAKSN4NV.js.map → import-airtable-UBF5GYAL.js.map} +0 -0
- /package/dist/{lighthouse-VHI77PFB.js.map → lighthouse-52WSPZ5D.js.map} +0 -0
- /package/dist/{lighthouse-airtable-YMH4UA7R.js.map → lighthouse-airtable-M45OPVRV.js.map} +0 -0
- /package/dist/{migrate-Y7R4CESL.js.map → migrate-XZ7U5DFC.js.map} +0 -0
- /package/dist/{notify-IISTHNBR.js.map → notify-K3WVH3DM.js.map} +0 -0
- /package/dist/{onboard-TM2KESGF.js.map → onboard-JQQIMQW7.js.map} +0 -0
- /package/dist/{orchestrate-HYVQPRCE.js.map → orchestrate-L2A5BZFY.js.map} +0 -0
- /package/dist/{parity-CAJ477PS.js.map → parity-2JGVUCZD.js.map} +0 -0
- /package/dist/{pipeline-YROAY6DT.js.map → pipeline-UUPXOS2J.js.map} +0 -0
- /package/dist/{prospect-audits-R3LWF6U7.js.map → prospect-audits-IZVQY3OQ.js.map} +0 -0
- /package/dist/{render-from-row-IFSBHWDH.js.map → render-from-row-75JTED2Z.js.map} +0 -0
- /package/dist/{svelte-codemods-WSV2FGNG.js.map → svelte-codemods-VOLOKKFI.js.map} +0 -0
- /package/dist/{sync-K3PKHVLH.js.map → sync-F4USL4K5.js.map} +0 -0
- /package/dist/{sync-configs-RRIHNLIJ.js.map → sync-configs-OXSWKVZ2.js.map} +0 -0
- /package/dist/{upgrade-VTD6V4HS.js.map → upgrade-EVAL3JTO.js.map} +0 -0
- /package/dist/{webflow-IRKOIT7S.js.map → webflow-624FEBFD.js.map} +0 -0
- /package/dist/{websites-TB4TCAWM.js.map → websites-Y6FN2XFD.js.map} +0 -0
- /package/dist/{write-audits-to-airtable-53EVXKMU.js.map → write-audits-to-airtable-TI3JMWSY.js.map} +0 -0
package/dist/cli/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/bin.ts","../../src/cli/version.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { cac } from \"cac\";\nimport type { AuditName, RecipeName } from \"../types.js\";\nimport { loadCredentialsIntoEnv } from \"../util/credentials.js\";\nimport { resolvePackageVersion } from \"./version.js\";\n\n// Command modules are loaded LAZILY (dynamic `import()` inside each `.action()`),\n// never eagerly at the top. An eager `import { runReportCommand } from\n// \"./commands/report.js\"` would pull EVERY command's transitive dependency chain\n// into the CLI's startup graph — report/announce/launch drag in mjml + resend +\n// @google-analytics/data, `db` drags in the libSQL/kysely stack, etc. Those heavy\n// packages are `devDependencies` (this repo's CLI/functions/audits use them); a\n// consuming fleet site installs @reddoorla/maintenance only for `./forms` +\n// `./configs/*` and runs just `reddoor-maint audit --only a11y` in CI. Lazy\n// loading keeps that path (and `--help`/`--version`) free of the report/db\n// chains, so those packages never land in a consumer's node_modules. The\n// smoke-dist gate asserts bin.js's STATIC import closure stays free of them.\n\n// Load credentials from ~/.config/reddoor-maint/credentials.env before any\n// command runs, so AIRTABLE_PAT/AIRTABLE_BASE_ID/RESEND_API_KEY/etc. are\n// available from any cwd. Shell-exported env vars still win. Silent on\n// missing file — commands that need the credentials will fail with their\n// own clear error.\nloadCredentialsIntoEnv();\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst version = resolvePackageVersion(here);\n\nconst AUDIT_DESCRIPTIONS: Record<AuditName, string> = {\n deps: \"Diff site package.json against the bundled baseline version map.\",\n lighthouse: \"Run @lhci/cli autorun using the canonical lighthouserc.\",\n a11y: \"Playwright + axe against the canonical a11y routes.\",\n security:\n \"GitHub Dependabot alerts (prod+dev) for repo-backed sites; falls back to pnpm/npm audit (prod-deps) with no repo/token.\",\n lint: \"ESLint + Prettier using the canonical configs.\",\n domain: \"DNS resolve + TLS cert expiry against the deployed URL (checkout-free).\",\n browser:\n \"Playwright across desktop engines + mobile devices + link-check against the deployed URL (checkout-free).\",\n \"netlify-deploy\":\n \"Latest production deploy health via the Netlify API by site id (checkout-free; needs NETLIFY_PAT).\",\n \"function-health\":\n \"GET {deployedUrl}/health and read its ok/prismic/forms status (checkout-free).\",\n smoke: \"Run the site's own `pnpm test:smoke` suite in its checkout (clone-based).\",\n \"form-e2e\":\n \"Submit the real production contact form in test-mode against the deployed URL (checkout-free).\",\n};\n\nconst RECIPE_DESCRIPTIONS: Record<RecipeName, string> = {\n \"sync-configs\": \"Overwrite a site's canonical configs to match @reddoorla/maintenance.\",\n \"bump-deps\": \"Bump dependencies and commit the lockfile change.\",\n \"svelte-4-to-5\": \"Run the 7-commit Svelte 4 → 5 upgrade recipe.\",\n \"svelte-codemods\":\n \"Apply Svelte 5 gotcha codemods to an already-migrated site (state_referenced_locally, etc.).\",\n \"convert-to-pnpm\": \"Convert an npm/yarn site to pnpm (lockfile, packageManager, scripts).\",\n onboard: \"Install @reddoorla/maintenance + audit deps on a site (preferred first step).\",\n \"a11y-fixtures-page\":\n \"Write src/routes/dev/a11y-fixtures/+page.svelte (stub for lhci + axe targets).\",\n \"health-endpoint\":\n \"Write src/routes/health/+server.ts (function-health probe for the report gate).\",\n \"smoke-suite\": \"Add the smoke suite (test:smoke + playwright config + /health smoke routes).\",\n \"self-updating\":\n \"Bootstrap Renovate + repo protection (writes renovate configs via PR, wires ruleset, disables platform auto-merge; ci.yml comes from the starter and is deliberately not managed).\",\n \"prismic-ci\":\n \"Add the Prismic model delivery workflow to a site via PR (dry delta comment on PRs, push to Prismic on merge to main).\",\n \"match-harness\":\n \"Install the matching-a-page gate harness (dev-guarded /dev/match/[uid] route + matching/ scripts) for a live-reference rebuild.\",\n init: \"Run the full onboarding chain (convert-to-pnpm → onboard → sync-configs → svelte-codemods → a11y-fixtures-page → health-endpoint → smoke-suite → audit).\",\n};\n\n/** Run a command thunk and surface its result, falling back to a clean error\n * message on throw. Wraps the ~10-line try/catch every `.action()` used to\n * duplicate. `verbose` flips between full stack and message-only.\n *\n * On success it sets `process.exitCode` and RETURNS rather than calling\n * `process.exit()` right after `console.log()`. `process.exit()` does not wait\n * for stdout to flush when stdout is a pipe, so a large `--json` payload piped\n * to another process would get truncated mid-write. Setting `exitCode` and\n * returning lets Node drain stdout and exit naturally with the right code. A\n * non-zero `code` still yields a non-zero process exit.\n *\n * The error path keeps `process.exit()` — error messages are small (one line),\n * always go to stderr, and exiting immediately is the desired fail-fast. */\nexport async function runOrExit(\n fn: () => Promise<{ output: string; code: number }>,\n opts: { verbose?: boolean },\n): Promise<void> {\n try {\n const { output, code } = await fn();\n console.log(output);\n process.exitCode = code;\n return;\n } catch (err) {\n const e = err as { exitCode?: number; message?: string; stack?: string };\n console.error(opts.verbose ? (e.stack ?? e.message) : (e.message ?? String(err)));\n process.exit(e.exitCode ?? 1);\n }\n}\n\nconst cli = cac(\"reddoor-maint\");\n\ncli.option(\"--cwd <path>\", \"Override working directory (default: process.cwd())\");\ncli.option(\"--verbose\", \"Verbose output (full stack on errors)\");\n\ncli.command(\"list-audits\", \"Print the available audits.\").action(() => {\n for (const [name, desc] of Object.entries(AUDIT_DESCRIPTIONS)) {\n console.log(`${name.padEnd(12)} ${desc}`);\n }\n});\n\ncli.command(\"list-recipes\", \"Print the available recipes.\").action(() => {\n for (const [name, desc] of Object.entries(RECIPE_DESCRIPTIONS)) {\n console.log(`${name.padEnd(16)} ${desc}`);\n }\n});\n\ncli\n .command(\"audit [site]\", \"Run audits against a site (default: cwd).\")\n .option(\"--only <names>\", \"Comma-separated audit names (e.g. deps,lighthouse)\")\n .option(\"--json\", \"Machine-readable JSON output\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .option(\n \"--write-airtable [slug]\",\n \"After lighthouse runs, write pScore/rScore/bpScore/seoScore + timestamp to the matching Websites row. Slug defaults to cwd's package.json#name.\",\n )\n .option(\"--fail-on-violations\", \"Exit non-zero if any a11y violations are found (for CI gates)\")\n .option(\n \"--url <url>\",\n \"Audit this deployed URL with lighthouse (no dev server); single-site. Pair with --only lighthouse — other audits still use the local checkout.\",\n )\n .option(\n \"--concurrency <n>\",\n \"Max sites to audit in parallel in --fleet mode (default: all at once). Use 1 for sequential (CI).\",\n )\n .action(\n async (\n site,\n opts: {\n only?: string;\n json?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n writeAirtable?: string | boolean;\n failOnViolations?: boolean;\n url?: string;\n concurrency?: string;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/audit.js\")).runAuditCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"sync-configs [site]\", \"Sync canonical configs into a site.\")\n .option(\"--only <names>\", \"Comma-separated config names (e.g. eslint,prettier)\")\n .option(\"--dry\", \"Print diff without writing\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n only?: string;\n dry?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/sync-configs.js\")).runSyncConfigsCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"bump-deps [site]\", \"Bump dependencies.\")\n .option(\"--group <group>\", \"patch | minor | major\", { default: \"minor\" })\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n group?: string;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/bump-deps.js\")).runBumpDepsCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"self-updating [site]\",\n \"Bootstrap a repo to keep itself updated (Renovate + protection; platform auto-merge off; ci.yml is the starter's, not managed here).\",\n )\n .option(\"--dry\", \"List what would be enabled without writing or opening PRs\")\n .option(\"--fleet <inventory>\", 'Inventory file (.json or .mjs/.js), or \"airtable\"')\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n dry?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/self-updating.js\")).runSelfUpdatingCommand(site, opts),\n opts,\n ),\n );\n\n// Fleet-wide, this opens ONE PULL REQUEST PER REPOSITORY and never pushes to a\n// client's main. `--dry` exists because that is the only way to see the blast\n// radius before firing it. Both directions of the flag ↔ option link are pinned\n// by tests/cli/prismic-ci-command.test.ts.\ncli\n .command(\n \"prismic-ci [site]\",\n \"Add the Prismic model delivery workflow to a site via PR (one PR per repo; never pushes to main).\",\n )\n .option(\"--dry\", \"List the sites that would be offered the workflow, without opening any PR\")\n .option(\"--fleet <inventory>\", 'Inventory file (.json or .mjs/.js), or \"airtable\"')\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n dry?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/prismic-ci.js\")).runPrismicCiCommand(site, opts),\n opts,\n ),\n );\n\n// EVERY flag below must be one the command actually reads, and every option the\n// command reads must appear below. cac rejects an unknown option outright, so a\n// missing line here is a hard error in whatever workflow types the flag; a line\n// here with no handler is the silent no-op — it parses, it lands in `opts`, and\n// nothing ever looks at it. Both directions are pinned by\n// tests/cli/prismic-models-registration.test.ts, the second one at the type level\n// against `PrismicModelsCommandOptions`.\ncli\n .command(\n \"prismic-models [site]\",\n \"Compare a site's Prismic models against its repo (dry by default).\",\n )\n .option(\n \"--apply\",\n \"Push local models to Prismic. Never deletes — remote-only models are reported.\",\n )\n .option(\"--pull\", \"Write remote-only models into the repo (single-site; review + PR the result).\")\n .option(\"--tokens\", \"Print the per-site write-token doctor: which env var, present?, reads?\")\n .option(\"--fleet <inventory>\", 'Inventory file (.json or .mjs/.js), or \"airtable\". Read-only.')\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .option(\"--write-airtable\", \"Fleet mode: persist each site's verdict to its Websites row\")\n .option(\n \"--comment-file <path>\",\n \"Write the report to this file (the CI workflow posts it as a PR comment)\",\n )\n .action(\n async (\n site,\n opts: {\n apply?: boolean;\n pull?: boolean;\n tokens?: boolean;\n fleet?: string;\n workdir?: string;\n writeAirtable?: boolean;\n commentFile?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/prismic-models.js\")).runPrismicModelsCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"upgrade <upgrade> [site]\", \"Run a named upgrade recipe (svelte-4-to-5).\")\n .example(\"reddoor-maint upgrade svelte-4-to-5 ./my-site\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n upgrade: string,\n site: string | undefined,\n opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/upgrade.js\")).runUpgradeCommand(upgrade, site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"convert-to-pnpm [site]\",\n \"Convert an npm/yarn site to pnpm (lockfile, packageManager, scripts).\",\n )\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/convert-to-pnpm.js\")).runConvertToPnpmCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"svelte-codemods [site]\", \"Apply Svelte 5 gotcha codemods to an already-migrated site.\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/svelte-codemods.js\")).runSvelteCodemodsCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"health-endpoint [site]\",\n \"Write src/routes/health/+server.ts (function-health probe for the report gate).\",\n )\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/health-endpoint.js\")).runHealthEndpointCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"match-harness [site]\",\n \"Install the matching-a-page gate harness (dev-guarded /dev/match/[uid] route + matching/ scripts) for a live-reference rebuild.\",\n )\n .option(\"--ref <url>\", \"Reference (live) site URL the harness gates against — required\")\n .option(\"--cand <url>\", \"Candidate origin (default http://localhost:5173)\")\n .option(\"--matrix <list>\", \"Breakpoint matrix, comma-separated (default 1440,834,390)\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n ref?: string;\n cand?: string;\n matrix?: string | number;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/match-harness.js\")).runMatchHarnessCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"smoke-suite [site]\",\n \"Add the smoke suite (test:smoke + playwright config + /health smoke routes).\",\n )\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () => (await import(\"./commands/smoke-suite.js\")).runSmokeSuiteCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"onboard [site]\",\n \"Install @reddoorla/maintenance + audit deps on a site (run after convert-to-pnpm).\",\n )\n .option(\"--audits <names>\", \"Comma-separated audit subset: lighthouse,a11y (default: both)\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n audits?: string;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/onboard.js\")).runOnboardCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"init [site]\",\n \"One-shot guided onboarding: convert-to-pnpm → onboard → sync-configs → svelte-codemods → a11y-fixtures-page → health-endpoint → smoke-suite → audit.\",\n )\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"airtable\" to read from Websites table',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(async () => (await import(\"./commands/init.js\")).runInitCommand(site, opts), opts),\n );\n\ncli\n .command(\n \"launch <site>\",\n \"Bootstrap + first-audit a site, then draft its launch email for approval.\",\n )\n .action(async (site: string, opts: { cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () => (await import(\"./commands/launch.js\")).runLaunchCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"header-image [site]\", \"Generate a site's report header image from its live homepage.\")\n .option(\"--all\", \"Every live site with no Header image yet (backfill)\")\n .option(\"--force\", \"With --all, regenerate sites that already have one\")\n .option(\"--write-airtable\", \"Upload to the Websites row instead of writing a local file\")\n .option(\"--out-dir <path>\", \"Directory for local output (default: reports/)\")\n .option(\"--settle-ms <ms>\", \"Override the post-load settle delay for slow/animated homepages\")\n .action(\n async (\n site: string | undefined,\n opts: {\n all?: boolean;\n force?: boolean;\n writeAirtable?: boolean;\n outDir?: string;\n settleMs?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/header-image.js\")).runHeaderImageCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"announce [site]\",\n \"Draft the monthly-report announcement email for maintenance sites (all, or one) for approval.\",\n )\n .action(async (site: string | undefined, opts: { cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () => (await import(\"./commands/announce.js\")).runAnnounceCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"forms-notify-target [site]\",\n \"Show who a form submission would email, and optionally flip the pre-launch guard (read-back confirmed).\",\n )\n .option(\"--set <on|off>\", \"Flip the guard: on = route to operator, off = restore.\")\n .option(\"--restore <status>\", \"Status to restore with --set off. Required; never inferred.\")\n .action(\n async (\n site: string | undefined,\n opts: { set?: string; restore?: string; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/forms-notify-target.js\")).runFormsNotifyTargetCommand(\n site,\n opts,\n ),\n opts,\n ),\n );\n\ncli\n .command(\n \"selftest <kind> [site]\",\n \"Operator self-tests. kind=email: preview a report email for a site (or --all) to yourself.\",\n )\n .option(\"--type <type>\", \"Report type: announcement (default) | maintenance | testing | launch\")\n .option(\"--to <addr>\", \"Recipient(s), comma-separated. Default: OPERATOR_EMAIL.\")\n .option(\"--all\", \"Send a preview for every maintenance site (to --to/operator).\")\n .option(\"--dry-run\", \"Render only; write reports/<slug>/selftest-<type>.html; do not send.\")\n .action(\n async (\n kind: string,\n site: string | undefined,\n opts: {\n type?: string;\n to?: string;\n all?: boolean;\n dryRun?: boolean;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/selftest.js\")).runSelftestCommand(kind, site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"preflight [site]\",\n \"Read-only pre-send checks: recipients, header image, pending drafts, schedule hygiene.\",\n )\n .option(\"--type <type>\", \"Report type: announcement (default) | maintenance | testing\")\n .option(\n \"--all\",\n \"Fleet mode: announcement→maintenance sites; maintenance/testing→all schedulable sites.\",\n )\n .action(\n async (\n site: string | undefined,\n opts: { type?: string; all?: boolean; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/preflight.js\")).runPreflightCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"ensure-site <slug>\", \"Create/verify the Airtable Websites row for a new site.\")\n .option(\"--name <display name>\", \"Human Name for the row (client-facing copy uses it verbatim).\")\n .option(\"--url <url>\", \"Deployed URL (e.g. the Netlify site URL).\")\n .option(\"--contact <email>\", \"point of contact — the client address reports resolve to.\")\n .option(\"--git-repo <owner/repo>\", \"GitHub identity. Default on create: reddoorla/<slug>.\")\n .action(\n async (\n slug: string,\n opts: {\n name?: string;\n url?: string;\n contact?: string;\n gitRepo?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/ensure-site.js\")).runEnsureSiteCommand(slug, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"prospect-audit <url>\",\n \"AEO/SEO audit an external prospect's site and publish the report.\",\n )\n .option(\n \"--business <name>\",\n \"The prospect's business name (defaults to what the model reads off the site).\",\n )\n .option(\n \"--competitors <list>\",\n \"Comma-separated competitor domains to add comparison probes for.\",\n )\n .option(\n \"--goal <goal>\",\n \"What the site should get a visitor to do: book, enquire, call, visit, buy, demo or partner. Defaults to what the model reads off the site.\",\n )\n .option(\"--no-probes\", \"Skip the live AI-visibility probes (tier 3).\")\n .option(\"--out <file>\", \"Also write the rendered report to this file.\")\n .option(\"--json\", \"Print the raw result JSON instead of the report summary.\")\n .option(\n \"--email\",\n \"Email the internal sheet (scores, what wasn't measured, top fixes, the link) to PROSPECT_AUDIT_RECIPIENTS.\",\n )\n .action(\n async (\n url: string,\n opts: {\n business?: string;\n goal?: string;\n competitors?: string;\n probes?: boolean;\n out?: string;\n json?: boolean;\n email?: boolean;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/prospect-audit.js\")).runProspectAuditCommand(url, opts),\n opts,\n ),\n );\n\ncli\n .command(\"report [site]\", \"Draft or send maintenance/testing reports.\")\n .option(\"--due\", \"Scan all Websites and draft overdue reports.\")\n .option(\"--type <type>\", \"Single-site draft report type: Maintenance (default) or Testing.\")\n .option(\n \"--preview\",\n \"Single-site dry run; writes reports/<slug>/draft.html, never touches Airtable.\",\n )\n .option(\n \"--enrich\",\n \"With --preview: also fetch GA + Search Console, so the render exercises those credentials for real. Still writes nothing.\",\n )\n .option(\n \"--send-ready\",\n \"Send all Reports with Draft ready=true AND Approved to send=true AND Sent at IS NULL.\",\n )\n .option(\n \"--digest\",\n \"Email the operator one daily digest of reports ready for approval (skips when empty).\",\n )\n .option(\n \"--rerender <reportId>\",\n \"Regenerate one UNSENT report's stored HTML from its current row, so the console preview shows edited commentary. Refuses a sent report.\",\n )\n .action(\n async (\n site,\n opts: {\n due?: boolean;\n type?: string;\n preview?: boolean;\n enrich?: boolean;\n sendReady?: boolean;\n digest?: boolean;\n rerender?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/report.js\")).runReportCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"github-signals\",\n \"Sweep the fleet for GitHub signals (Renovate-failing/CI/last-commit) and write Airtable.\",\n )\n .option(\"--fleet\", \"Run across every site in the Airtable inventory.\")\n .option(\"--write-airtable\", \"Write each site's signals back to its Websites row.\")\n .action(\n async (opts: { fleet?: boolean; writeAirtable?: boolean; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/github-signals.js\")).runGitHubSignalsCommand({\n fleet: opts.fleet,\n writeAirtable: opts.writeAirtable,\n }),\n opts,\n ),\n );\n\ncli\n .command(\n \"protection-audit\",\n \"Verify every public org repo carries a sound branch ruleset (empty bypass, PR-required).\",\n )\n .option(\"--org <org>\", \"GitHub org to sweep (enumerated from the API, never a hand list).\")\n .action(async (opts: { org?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/protection-audit.js\")).runProtectionAuditCommand({\n org: opts.org,\n }),\n opts,\n ),\n );\n\ncli\n .command(\n \"db <action>\",\n \"Operate the libSQL store (migrate | replay-deadletters | import-airtable | parity | sync | backfill-header-images | backfill-digest-state | dump | verify-dump | restore | usage).\",\n )\n .option(\"--file <path>\", \"verify-dump / restore: the dump file to load\")\n .option(\n \"--org <slug>\",\n \"usage: Turso org slug (default: TURSO_ORG, else discovered from the token). The token itself comes from TURSO_FLEET_USAGE — never a flag, since argv is readable from ps.\",\n )\n .option(\n \"--url <url>\",\n \"restore: the TARGET database to load into. Required, never defaulted — a restore \" +\n \"that could fall back to the ambient TURSO_DATABASE_URL is one keystroke from \" +\n \"overwriting production.\",\n )\n .option(\n \"--force\",\n \"import-airtable / sync: run despite the freeze (#643) — a deliberate rollback-window \" +\n \"converge from the frozen Airtable shadow. Without it both refuse while Turso is \" +\n \"authoritative, because an import overwrites authoritative rows.\",\n )\n .action(\n async (\n action: string,\n opts: {\n file?: string;\n url?: string;\n org?: string;\n force?: boolean;\n cwd?: string;\n verbose?: boolean;\n },\n ) => runOrExit(async () => (await import(\"./commands/db.js\")).runDbCommand(action, opts), opts),\n );\n\ncli\n .command(\n \"submissions <action>\",\n \"Operate on stored form submissions. rescore: re-run the CURRENT spam classifier over the status='new' backlog (dry-run by default; only 'new' rows are ever touched).\",\n )\n .option(\"--dry-run\", \"Print the would-be re-buckets without writing (the default).\")\n .option(\n \"--apply\",\n \"Write the re-buckets: status → spam_auto, spam_score/spam_reason replaced with the new verdict + 'retro-rescore' marker.\",\n )\n .action(\n async (\n action: string,\n opts: { dryRun?: boolean; apply?: boolean; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/submissions.js\")).runSubmissionsCommand(action, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"renovate-dispatch\",\n \"Trigger Renovate on fleet sites the security sweep flagged with critical/high vulns.\",\n )\n .option(\"--fleet\", \"Run across every active, repo-backed site in the Airtable inventory.\")\n .action(async (opts: { fleet?: boolean; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/renovate-dispatch.js\")).runRenovateDispatchCommand({\n fleet: opts.fleet,\n // #539 Phase 5: mirror the auto-fix counter into site_health.\n siteMirror: await (await import(\"../db/site-mirror.js\")).makeSiteMirror(),\n }),\n opts,\n ),\n );\n\ncli\n .command(\n \"blux <action> [dir]\",\n \"Blux conversion pipeline. emit: export dir → migration plan + custom types + theme + review manifest. migrate: emitted plan → live Prismic (needs PRISMIC_REPOSITORY_NAME + PRISMIC_WRITE_TOKEN). validate: content coverage of a rendered site against the export answer key. grid: parse rendered index.html → grid-tree.json (layout tree). convert: parse+classify index.html + assemble IR → migration-plan.json + blux-presentation.json + theme.css (+ map-config.json), offline. catalog: parse each page + classify every band to a blux_section → migration-plan.json only (no sidecar), offline.\",\n )\n .option(\n \"--out <dir>\",\n \"Output directory for emit/grid/convert/catalog (default: <exportDir>/blux-out)\",\n )\n .option(\"--converted-base <url>\", \"Converted-site base URL for the review manifest\")\n .option(\"--blux-base <url>\", \"Original Blux site base URL (default: https://<site.json domain>)\")\n .option(\n \"--probe\",\n \"Reconstruct + HEAD-probe CDN URLs for used assets the HTML scrape missed (network)\",\n )\n .option(\n \"--against <target>\",\n \"validate: rendered HTML of the converted site — a file path or http(s) URL\",\n )\n .option(\n \"--site <slug>\",\n \"freeze/migrate-frozen: site slug naming <slug>.slots.json (default: slugified dir name)\",\n )\n .option(\n \"--extra-slots <path>\",\n \"freeze: JSON declaring slots the template has no token for (video poster, a panel rebuilt from a baked image). Keys must start with 'x.'\",\n )\n .action(\n async (\n action: string,\n dir: string | undefined,\n opts: {\n out?: string;\n convertedBase?: string;\n bluxBase?: string;\n probe?: boolean;\n against?: string;\n site?: string;\n extraSlots?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/blux.js\")).runBluxCommand(action, dir, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"webflow <action> [target]\",\n \"Webflow import pipeline. capture: live-crawl a base url → <out>/ir.json, progress on stderr. docs: convert a saved ir.json → docs.json + assets.json (offline). migrate: push a capture dir's docs.json + assets.json to Prismic via the shared runner (needs PRISMIC_REPOSITORY_NAME + PRISMIC_WRITE_TOKEN).\",\n )\n .option(\n \"--out <dir>\",\n \"Output directory for capture (default: webflow-out) / docs (default: dirname of the ir.json)\",\n )\n .action(\n async (\n action: string,\n target: string | undefined,\n opts: { out?: string; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/webflow.js\")).runWebflowCommand(action, target, opts),\n opts,\n ),\n );\n\ncli.help();\ncli.version(version);\n\n// A typo'd / unrecognized subcommand (e.g. `reddoor-maint auditt`) otherwise\n// falls through cac with no matched command and exits 0 — a cron/CI typo would\n// \"succeed\" silently. cac emits `command:*` at the end of parse() exactly when\n// a positional arg was given but matched no command (a bare `reddoor-maint`,\n// `--help`, and `--version` do NOT trigger it: they have no leading positional\n// or are handled before this fires). Turn that into a clear stderr error +\n// non-zero exit. process.argv[2] is the first positional, i.e. the bad command.\ncli.on(\"command:*\", () => {\n const unknown = cli.args[0] ?? process.argv[2] ?? \"\";\n console.error(\n `error: unknown command '${unknown}'. Run 'reddoor-maint --help' to see available commands.`,\n );\n process.exit(1);\n});\n\ncli.parse();\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Read the @reddoorla/maintenance package version, given the directory of a\n * file that lives inside the package. Walks UP looking for the first\n * `package.json` whose `name` matches. Defensive against bundling-layout\n * changes — the older \"two levels up\" assumption held for `dist/cli/bin.js`\n * but would silently mis-read (or return \"unknown\") if tsup ever moved bin\n * to a different depth. Same walk-up pattern as the self-version helper.\n *\n * Returns \"unknown\" when no matching package.json is reachable (Yarn PnP\n * setups stash manifests inside .zip caches; the readFileSync there fails\n * before any name check).\n */\nexport function resolvePackageVersion(fromDir: string): string {\n try {\n let dir = fromDir;\n while (true) {\n const candidate = join(dir, \"package.json\");\n if (existsSync(candidate)) {\n const raw = readFileSync(candidate, \"utf-8\");\n const pkg = JSON.parse(raw) as { name?: string; version?: string };\n if (pkg.name === \"@reddoorla/maintenance\") {\n return pkg.version ?? \"unknown\";\n }\n }\n const parent = dirname(dir);\n if (parent === dir) return \"unknown\";\n dir = parent;\n }\n } catch {\n return \"unknown\";\n }\n}\n"],"mappings":"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","names":["dirname","dirname"]}
|
|
1
|
+
{"version":3,"sources":["../../src/cli/bin.ts","../../src/cli/retired-flags.ts","../../src/cli/version.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { cac } from \"cac\";\nimport type { AuditName, RecipeName } from \"../types.js\";\nimport { loadCredentialsIntoEnv } from \"../util/credentials.js\";\nimport { rewriteRetiredFlags } from \"./retired-flags.js\";\nimport { resolvePackageVersion } from \"./version.js\";\n\n// Command modules are loaded LAZILY (dynamic `import()` inside each `.action()`),\n// never eagerly at the top. An eager `import { runReportCommand } from\n// \"./commands/report.js\"` would pull EVERY command's transitive dependency chain\n// into the CLI's startup graph — report/announce/launch drag in mjml + resend +\n// @google-analytics/data, `db` drags in the libSQL/kysely stack, etc. Those heavy\n// packages are `devDependencies` (this repo's CLI/functions/audits use them); a\n// consuming fleet site installs @reddoorla/maintenance only for `./forms` +\n// `./configs/*` and runs just `reddoor-maint audit --only a11y` in CI. Lazy\n// loading keeps that path (and `--help`/`--version`) free of the report/db\n// chains, so those packages never land in a consumer's node_modules. The\n// smoke-dist gate asserts bin.js's STATIC import closure stays free of them.\n\n// Load credentials from ~/.config/reddoor-maint/credentials.env before any\n// command runs, so AIRTABLE_PAT/AIRTABLE_BASE_ID/RESEND_API_KEY/etc. are\n// available from any cwd. Shell-exported env vars still win. Silent on\n// missing file — commands that need the credentials will fail with their\n// own clear error.\nloadCredentialsIntoEnv();\n\nconst here = dirname(fileURLToPath(import.meta.url));\nconst version = resolvePackageVersion(here);\n\nconst AUDIT_DESCRIPTIONS: Record<AuditName, string> = {\n deps: \"Diff site package.json against the bundled baseline version map.\",\n lighthouse: \"Run @lhci/cli autorun using the canonical lighthouserc.\",\n a11y: \"Playwright + axe against the canonical a11y routes.\",\n security:\n \"GitHub Dependabot alerts (prod+dev) for repo-backed sites; falls back to pnpm/npm audit (prod-deps) with no repo/token.\",\n lint: \"ESLint + Prettier using the canonical configs.\",\n domain: \"DNS resolve + TLS cert expiry against the deployed URL (checkout-free).\",\n browser:\n \"Playwright across desktop engines + mobile devices + link-check against the deployed URL (checkout-free).\",\n \"netlify-deploy\":\n \"Latest production deploy health via the Netlify API by site id (checkout-free; needs NETLIFY_PAT).\",\n \"function-health\":\n \"GET {deployedUrl}/health and read its ok/prismic/forms status (checkout-free).\",\n smoke: \"Run the site's own `pnpm test:smoke` suite in its checkout (clone-based).\",\n \"form-e2e\":\n \"Submit the real production contact form in test-mode against the deployed URL (checkout-free).\",\n};\n\nconst RECIPE_DESCRIPTIONS: Record<RecipeName, string> = {\n \"sync-configs\": \"Overwrite a site's canonical configs to match @reddoorla/maintenance.\",\n \"bump-deps\": \"Bump dependencies and commit the lockfile change.\",\n \"svelte-4-to-5\": \"Run the 7-commit Svelte 4 → 5 upgrade recipe.\",\n \"svelte-codemods\":\n \"Apply Svelte 5 gotcha codemods to an already-migrated site (state_referenced_locally, etc.).\",\n \"convert-to-pnpm\": \"Convert an npm/yarn site to pnpm (lockfile, packageManager, scripts).\",\n onboard: \"Install @reddoorla/maintenance + audit deps on a site (preferred first step).\",\n \"a11y-fixtures-page\":\n \"Write src/routes/dev/a11y-fixtures/+page.svelte (stub for lhci + axe targets).\",\n \"health-endpoint\":\n \"Write src/routes/health/+server.ts (function-health probe for the report gate).\",\n \"smoke-suite\": \"Add the smoke suite (test:smoke + playwright config + /health smoke routes).\",\n \"self-updating\":\n \"Bootstrap Renovate + repo protection (writes renovate configs via PR, wires ruleset, disables platform auto-merge; ci.yml comes from the starter and is deliberately not managed).\",\n \"prismic-ci\":\n \"Add the Prismic model delivery workflow to a site via PR (dry delta comment on PRs, push to Prismic on merge to main).\",\n \"match-harness\":\n \"Install the matching-a-page gate harness (dev-guarded /dev/match/[uid] route + matching/ scripts) for a live-reference rebuild.\",\n init: \"Run the full onboarding chain (convert-to-pnpm → onboard → sync-configs → svelte-codemods → a11y-fixtures-page → health-endpoint → smoke-suite → audit).\",\n};\n\n/** Run a command thunk and surface its result, falling back to a clean error\n * message on throw. Wraps the ~10-line try/catch every `.action()` used to\n * duplicate. `verbose` flips between full stack and message-only.\n *\n * On success it sets `process.exitCode` and RETURNS rather than calling\n * `process.exit()` right after `console.log()`. `process.exit()` does not wait\n * for stdout to flush when stdout is a pipe, so a large `--json` payload piped\n * to another process would get truncated mid-write. Setting `exitCode` and\n * returning lets Node drain stdout and exit naturally with the right code. A\n * non-zero `code` still yields a non-zero process exit.\n *\n * The error path keeps `process.exit()` — error messages are small (one line),\n * always go to stderr, and exiting immediately is the desired fail-fast. */\nexport async function runOrExit(\n fn: () => Promise<{ output: string; code: number }>,\n opts: { verbose?: boolean },\n): Promise<void> {\n try {\n const { output, code } = await fn();\n console.log(output);\n process.exitCode = code;\n return;\n } catch (err) {\n const e = err as { exitCode?: number; message?: string; stack?: string };\n console.error(opts.verbose ? (e.stack ?? e.message) : (e.message ?? String(err)));\n process.exit(e.exitCode ?? 1);\n }\n}\n\nconst cli = cac(\"reddoor-maint\");\n\ncli.option(\"--cwd <path>\", \"Override working directory (default: process.cwd())\");\ncli.option(\"--verbose\", \"Verbose output (full stack on errors)\");\n\ncli.command(\"list-audits\", \"Print the available audits.\").action(() => {\n for (const [name, desc] of Object.entries(AUDIT_DESCRIPTIONS)) {\n console.log(`${name.padEnd(12)} ${desc}`);\n }\n});\n\ncli.command(\"list-recipes\", \"Print the available recipes.\").action(() => {\n for (const [name, desc] of Object.entries(RECIPE_DESCRIPTIONS)) {\n console.log(`${name.padEnd(16)} ${desc}`);\n }\n});\n\ncli\n .command(\"audit [site]\", \"Run audits against a site (default: cwd).\")\n .option(\"--only <names>\", \"Comma-separated audit names (e.g. deps,lighthouse)\")\n .option(\"--json\", \"Machine-readable JSON output\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .option(\n \"--write-back [slug]\",\n \"After lighthouse runs, write pScore/rScore/bpScore/seoScore + timestamp to the matching Websites row. Slug defaults to cwd's package.json#name.\",\n )\n .option(\"--fail-on-violations\", \"Exit non-zero if any a11y violations are found (for CI gates)\")\n .option(\n \"--url <url>\",\n \"Audit this deployed URL with lighthouse (no dev server); single-site. Pair with --only lighthouse — other audits still use the local checkout.\",\n )\n .option(\n \"--concurrency <n>\",\n \"Max sites to audit in parallel in --fleet mode (default: all at once). Use 1 for sequential (CI).\",\n )\n .action(\n async (\n site,\n opts: {\n only?: string;\n json?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n writeBack?: string | boolean;\n failOnViolations?: boolean;\n url?: string;\n concurrency?: string;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/audit.js\")).runAuditCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"sync-configs [site]\", \"Sync canonical configs into a site.\")\n .option(\"--only <names>\", \"Comma-separated config names (e.g. eslint,prettier)\")\n .option(\"--dry\", \"Print diff without writing\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n only?: string;\n dry?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/sync-configs.js\")).runSyncConfigsCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"bump-deps [site]\", \"Bump dependencies.\")\n .option(\"--group <group>\", \"patch | minor | major\", { default: \"minor\" })\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n group?: string;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/bump-deps.js\")).runBumpDepsCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"self-updating [site]\",\n \"Bootstrap a repo to keep itself updated (Renovate + protection; platform auto-merge off; ci.yml is the starter's, not managed here).\",\n )\n .option(\"--dry\", \"List what would be enabled without writing or opening PRs\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n dry?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/self-updating.js\")).runSelfUpdatingCommand(site, opts),\n opts,\n ),\n );\n\n// Fleet-wide, this opens ONE PULL REQUEST PER REPOSITORY and never pushes to a\n// client's main. `--dry` exists because that is the only way to see the blast\n// radius before firing it. Both directions of the flag ↔ option link are pinned\n// by tests/cli/prismic-ci-command.test.ts.\ncli\n .command(\n \"prismic-ci [site]\",\n \"Add the Prismic model delivery workflow to a site via PR (one PR per repo; never pushes to main).\",\n )\n .option(\"--dry\", \"List the sites that would be offered the workflow, without opening any PR\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n dry?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/prismic-ci.js\")).runPrismicCiCommand(site, opts),\n opts,\n ),\n );\n\n// EVERY flag below must be one the command actually reads, and every option the\n// command reads must appear below. cac rejects an unknown option outright, so a\n// missing line here is a hard error in whatever workflow types the flag; a line\n// here with no handler is the silent no-op — it parses, it lands in `opts`, and\n// nothing ever looks at it. Both directions are pinned by\n// tests/cli/prismic-models-registration.test.ts, the second one at the type level\n// against `PrismicModelsCommandOptions`.\ncli\n .command(\n \"prismic-models [site]\",\n \"Compare a site's Prismic models against its repo (dry by default).\",\n )\n .option(\n \"--apply\",\n \"Push local models to Prismic. Never deletes — remote-only models are reported.\",\n )\n .option(\"--pull\", \"Write remote-only models into the repo (single-site; review + PR the result).\")\n .option(\"--tokens\", \"Print the per-site write-token doctor: which env var, present?, reads?\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" (\"airtable\" is a deprecated alias). Read-only.',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .option(\"--write-back\", \"Fleet mode: persist each site's verdict to its Websites row\")\n .option(\n \"--comment-file <path>\",\n \"Write the report to this file (the CI workflow posts it as a PR comment)\",\n )\n .action(\n async (\n site,\n opts: {\n apply?: boolean;\n pull?: boolean;\n tokens?: boolean;\n fleet?: string;\n workdir?: string;\n writeBack?: boolean;\n commentFile?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/prismic-models.js\")).runPrismicModelsCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"upgrade <upgrade> [site]\", \"Run a named upgrade recipe (svelte-4-to-5).\")\n .example(\"reddoor-maint upgrade svelte-4-to-5 ./my-site\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n upgrade: string,\n site: string | undefined,\n opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/upgrade.js\")).runUpgradeCommand(upgrade, site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"convert-to-pnpm [site]\",\n \"Convert an npm/yarn site to pnpm (lockfile, packageManager, scripts).\",\n )\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/convert-to-pnpm.js\")).runConvertToPnpmCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"svelte-codemods [site]\", \"Apply Svelte 5 gotcha codemods to an already-migrated site.\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/svelte-codemods.js\")).runSvelteCodemodsCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"health-endpoint [site]\",\n \"Write src/routes/health/+server.ts (function-health probe for the report gate).\",\n )\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/health-endpoint.js\")).runHealthEndpointCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"match-harness [site]\",\n \"Install the matching-a-page gate harness (dev-guarded /dev/match/[uid] route + matching/ scripts) for a live-reference rebuild.\",\n )\n .option(\"--ref <url>\", \"Reference (live) site URL the harness gates against — required\")\n .option(\"--cand <url>\", \"Candidate origin (default http://localhost:5173)\")\n .option(\"--matrix <list>\", \"Breakpoint matrix, comma-separated (default 1440,834,390)\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n ref?: string;\n cand?: string;\n matrix?: string | number;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/match-harness.js\")).runMatchHarnessCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"smoke-suite [site]\",\n \"Add the smoke suite (test:smoke + playwright config + /health smoke routes).\",\n )\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () => (await import(\"./commands/smoke-suite.js\")).runSmokeSuiteCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"onboard [site]\",\n \"Install @reddoorla/maintenance + audit deps on a site (run after convert-to-pnpm).\",\n )\n .option(\"--audits <names>\", \"Comma-separated audit subset: lighthouse,a11y (default: both)\")\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (\n site,\n opts: {\n audits?: string;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/onboard.js\")).runOnboardCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"init [site]\",\n \"One-shot guided onboarding: convert-to-pnpm → onboard → sync-configs → svelte-codemods → a11y-fixtures-page → health-endpoint → smoke-suite → audit.\",\n )\n .option(\n \"--fleet <inventory>\",\n 'Inventory file (.json or .mjs/.js), or \"turso\" to read the fleet roster from the database (\"airtable\" is a deprecated alias)',\n )\n .option(\"--workdir <path>\", \"Clone target for fleet mode (default ~/.reddoor-maint/sites)\")\n .action(\n async (site, opts: { fleet?: string; workdir?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(async () => (await import(\"./commands/init.js\")).runInitCommand(site, opts), opts),\n );\n\ncli\n .command(\n \"launch <site>\",\n \"Bootstrap + first-audit a site, then draft its launch email for approval.\",\n )\n .action(async (site: string, opts: { cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () => (await import(\"./commands/launch.js\")).runLaunchCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\"header-image [site]\", \"Generate a site's report header image from its live homepage.\")\n .option(\"--all\", \"Every live site with no Header image yet (backfill)\")\n .option(\"--force\", \"With --all, regenerate sites that already have one\")\n .option(\"--write-back\", \"Upload to the Websites row instead of writing a local file\")\n .option(\"--out-dir <path>\", \"Directory for local output (default: reports/)\")\n .option(\"--settle-ms <ms>\", \"Override the post-load settle delay for slow/animated homepages\")\n .option(\n \"--consent-selector <css>\",\n \"Extra CSS selector(s) to hide before the shutter (consent UI the heuristic misses)\",\n )\n .action(\n async (\n site: string | undefined,\n opts: {\n all?: boolean;\n force?: boolean;\n writeBack?: boolean;\n outDir?: string;\n settleMs?: string;\n consentSelector?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/header-image.js\")).runHeaderImageCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"announce [site]\",\n \"Draft the monthly-report announcement email for maintenance sites (all, or one) for approval.\",\n )\n .action(async (site: string | undefined, opts: { cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () => (await import(\"./commands/announce.js\")).runAnnounceCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"forms-notify-target [site]\",\n \"Show who a form submission would email, and optionally flip the pre-launch guard (read-back confirmed).\",\n )\n .option(\"--set <on|off>\", \"Flip the guard: on = route to operator, off = restore.\")\n .option(\"--restore <status>\", \"Status to restore with --set off. Required; never inferred.\")\n .action(\n async (\n site: string | undefined,\n opts: { set?: string; restore?: string; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/forms-notify-target.js\")).runFormsNotifyTargetCommand(\n site,\n opts,\n ),\n opts,\n ),\n );\n\ncli\n .command(\n \"selftest <kind> [site]\",\n \"Operator self-tests. kind=email: preview a report email for a site (or --all) to yourself.\",\n )\n .option(\"--type <type>\", \"Report type: announcement (default) | maintenance | testing | launch\")\n .option(\"--to <addr>\", \"Recipient(s), comma-separated. Default: OPERATOR_EMAIL.\")\n .option(\"--all\", \"Send a preview for every maintenance site (to --to/operator).\")\n .option(\"--dry-run\", \"Render only; write reports/<slug>/selftest-<type>.html; do not send.\")\n .action(\n async (\n kind: string,\n site: string | undefined,\n opts: {\n type?: string;\n to?: string;\n all?: boolean;\n dryRun?: boolean;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/selftest.js\")).runSelftestCommand(kind, site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"preflight [site]\",\n \"Read-only pre-send checks: recipients, header image, pending drafts, schedule hygiene.\",\n )\n .option(\"--type <type>\", \"Report type: announcement (default) | maintenance | testing\")\n .option(\n \"--all\",\n \"Fleet mode: announcement→maintenance sites; maintenance/testing→all schedulable sites.\",\n )\n .action(\n async (\n site: string | undefined,\n opts: { type?: string; all?: boolean; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/preflight.js\")).runPreflightCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"ensure-site <slug>\",\n \"Create/verify a site's fleet row in Turso (new sites get a site_<ULID> id).\",\n )\n .option(\"--name <display name>\", \"Human Name for the row (client-facing copy uses it verbatim).\")\n .option(\"--url <url>\", \"Deployed URL (e.g. the Netlify site URL).\")\n .option(\"--contact <email>\", \"point of contact — the client address reports resolve to.\")\n .option(\"--git-repo <owner/repo>\", \"GitHub identity. Default on create: reddoorla/<slug>.\")\n .action(\n async (\n slug: string,\n opts: {\n name?: string;\n url?: string;\n contact?: string;\n gitRepo?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/ensure-site.js\")).runEnsureSiteCommand(slug, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"prospect-audit <url>\",\n \"AEO/SEO audit an external prospect's site and publish the report.\",\n )\n .option(\n \"--business <name>\",\n \"The prospect's business name (defaults to what the model reads off the site).\",\n )\n .option(\n \"--competitors <list>\",\n \"Comma-separated competitor domains to add comparison probes for.\",\n )\n .option(\n \"--goal <goal>\",\n \"What the site should get a visitor to do: book, enquire, call, visit, buy, demo or partner. Defaults to what the model reads off the site.\",\n )\n .option(\"--no-probes\", \"Skip the live AI-visibility probes (tier 3).\")\n .option(\"--out <file>\", \"Also write the rendered report to this file.\")\n .option(\"--json\", \"Print the raw result JSON instead of the report summary.\")\n .option(\n \"--email\",\n \"Email the internal sheet (scores, what wasn't measured, top fixes, the link) to PROSPECT_AUDIT_RECIPIENTS.\",\n )\n .option(\n \"--terms <list>\",\n \"Comma-separated search terms for the visibility section, chosen by hand. Blank generates them from the site, as before.\",\n )\n .option(\n \"--questions <list>\",\n \"Comma-separated buyer questions, chosen by hand. Blank asks the goal's fixed set. A chosen set gets its own version key, so comparisons never mix sets.\",\n )\n .action(\n async (\n url: string,\n opts: {\n business?: string;\n goal?: string;\n competitors?: string;\n probes?: boolean;\n out?: string;\n json?: boolean;\n email?: boolean;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () =>\n (await import(\"./commands/prospect-audit.js\")).runProspectAuditCommand(url, opts),\n opts,\n ),\n );\n\ncli\n .command(\"report [site]\", \"Draft or send maintenance/testing reports.\")\n .option(\"--due\", \"Scan all Websites and draft overdue reports.\")\n .option(\"--type <type>\", \"Single-site draft report type: Maintenance (default) or Testing.\")\n .option(\n \"--preview\",\n \"Single-site dry run; writes reports/<slug>/draft.html, never touches Airtable.\",\n )\n .option(\n \"--enrich\",\n \"With --preview: also fetch GA + Search Console, so the render exercises those credentials for real. Still writes nothing.\",\n )\n .option(\n \"--send-ready\",\n \"Send all Reports with Draft ready=true AND Approved to send=true AND Sent at IS NULL.\",\n )\n .option(\n \"--digest\",\n \"Email the operator one daily digest of reports ready for approval (skips when empty).\",\n )\n .option(\n \"--rerender <reportId>\",\n \"Regenerate one UNSENT report's stored HTML from its current row, so the console preview shows edited commentary. Refuses a sent report.\",\n )\n .action(\n async (\n site,\n opts: {\n due?: boolean;\n type?: string;\n preview?: boolean;\n enrich?: boolean;\n sendReady?: boolean;\n digest?: boolean;\n rerender?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/report.js\")).runReportCommand(site, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"github-signals\",\n \"Sweep the fleet for GitHub signals (Renovate-failing/CI/last-commit) and write each site's row.\",\n )\n .option(\"--fleet\", \"Run across every site in the Airtable inventory.\")\n .option(\"--write-back\", \"Write each site's signals back to its Websites row.\")\n .action(async (opts: { fleet?: boolean; writeBack?: boolean; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/github-signals.js\")).runGitHubSignalsCommand({\n fleet: opts.fleet,\n writeBack: opts.writeBack,\n }),\n opts,\n ),\n );\n\ncli\n .command(\n \"protection-audit\",\n \"Verify every public org repo carries a sound branch ruleset (empty bypass, PR-required).\",\n )\n .option(\"--org <org>\", \"GitHub org to sweep (enumerated from the API, never a hand list).\")\n .action(async (opts: { org?: string; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/protection-audit.js\")).runProtectionAuditCommand({\n org: opts.org,\n }),\n opts,\n ),\n );\n\ncli\n .command(\n \"db <action>\",\n \"Operate the libSQL store (migrate | replay-deadletters | import-airtable | parity | sync | backfill-header-images | backfill-digest-state | dump | verify-dump | restore | usage).\",\n )\n .option(\"--file <path>\", \"verify-dump / restore: the dump file to load\")\n .option(\n \"--org <slug>\",\n \"usage: Turso org slug (default: TURSO_ORG, else discovered from the token). The token itself comes from TURSO_FLEET_USAGE — never a flag, since argv is readable from ps.\",\n )\n .option(\n \"--url <url>\",\n \"restore: the TARGET database to load into. Required, never defaulted — a restore \" +\n \"that could fall back to the ambient TURSO_DATABASE_URL is one keystroke from \" +\n \"overwriting production.\",\n )\n .option(\n \"--force\",\n \"import-airtable / sync: run despite the freeze (#643) — a deliberate rollback-window \" +\n \"converge from the frozen Airtable shadow. Without it both refuse while Turso is \" +\n \"authoritative, because an import overwrites authoritative rows.\",\n )\n .option(\n \"--abandon <slug-or-id>\",\n \"replay-deadletters: mark a slug's queued leads (or one dl_… row) abandoned — terminal \" +\n \"by decision, dropped from the replay queue and the cockpit alarm, row and payload kept.\",\n )\n .option(\n \"--reason <text>\",\n \"replay-deadletters --abandon: why the leads are being written off. Required.\",\n )\n .option(\n \"--by <who>\",\n \"replay-deadletters --abandon: who decided (default: OPERATOR_EMAIL, else 'operator').\",\n )\n .action(\n async (\n action: string,\n opts: {\n file?: string;\n url?: string;\n org?: string;\n force?: boolean;\n cwd?: string;\n verbose?: boolean;\n },\n ) => runOrExit(async () => (await import(\"./commands/db.js\")).runDbCommand(action, opts), opts),\n );\n\ncli\n .command(\n \"submissions <action>\",\n \"Operate on stored form submissions. rescore: re-run the CURRENT spam classifier over the status='new' backlog (dry-run by default; only 'new' rows are ever touched).\",\n )\n .option(\"--dry-run\", \"Print the would-be re-buckets without writing (the default).\")\n .option(\n \"--apply\",\n \"Write the re-buckets: status → spam_auto, spam_score/spam_reason replaced with the new verdict + 'retro-rescore' marker.\",\n )\n .action(\n async (\n action: string,\n opts: { dryRun?: boolean; apply?: boolean; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/submissions.js\")).runSubmissionsCommand(action, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"renovate-dispatch\",\n \"Trigger Renovate on fleet sites the security sweep flagged with critical/high vulns.\",\n )\n .option(\"--fleet\", \"Run across every active, repo-backed site in the Airtable inventory.\")\n .action(async (opts: { fleet?: boolean; cwd?: string; verbose?: boolean }) =>\n runOrExit(\n async () =>\n (await import(\"./commands/renovate-dispatch.js\")).runRenovateDispatchCommand({\n fleet: opts.fleet,\n // #539 Phase 5: mirror the auto-fix counter into site_health.\n siteMirror: await (await import(\"../db/site-mirror.js\")).makeSiteMirror(),\n }),\n opts,\n ),\n );\n\ncli\n .command(\n \"blux <action> [dir]\",\n \"Blux conversion pipeline. emit: export dir → migration plan + custom types + theme + review manifest. migrate: emitted plan → live Prismic (needs PRISMIC_REPOSITORY_NAME + PRISMIC_WRITE_TOKEN). validate: content coverage of a rendered site against the export answer key. grid: parse rendered index.html → grid-tree.json (layout tree). convert: parse+classify index.html + assemble IR → migration-plan.json + blux-presentation.json + theme.css (+ map-config.json), offline. catalog: parse each page + classify every band to a blux_section → migration-plan.json only (no sidecar), offline.\",\n )\n .option(\n \"--out <dir>\",\n \"Output directory for emit/grid/convert/catalog (default: <exportDir>/blux-out)\",\n )\n .option(\"--converted-base <url>\", \"Converted-site base URL for the review manifest\")\n .option(\"--blux-base <url>\", \"Original Blux site base URL (default: https://<site.json domain>)\")\n .option(\n \"--probe\",\n \"Reconstruct + HEAD-probe CDN URLs for used assets the HTML scrape missed (network)\",\n )\n .option(\n \"--against <target>\",\n \"validate: rendered HTML of the converted site — a file path or http(s) URL\",\n )\n .option(\n \"--site <slug>\",\n \"freeze/migrate-frozen: site slug naming <slug>.slots.json (default: slugified dir name)\",\n )\n .option(\n \"--extra-slots <path>\",\n \"freeze: JSON declaring slots the template has no token for (video poster, a panel rebuilt from a baked image). Keys must start with 'x.'\",\n )\n .action(\n async (\n action: string,\n dir: string | undefined,\n opts: {\n out?: string;\n convertedBase?: string;\n bluxBase?: string;\n probe?: boolean;\n against?: string;\n site?: string;\n extraSlots?: string;\n cwd?: string;\n verbose?: boolean;\n },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/blux.js\")).runBluxCommand(action, dir, opts),\n opts,\n ),\n );\n\ncli\n .command(\n \"webflow <action> [target]\",\n \"Webflow import pipeline. capture: live-crawl a base url → <out>/ir.json, progress on stderr. docs: convert a saved ir.json → docs.json + assets.json (offline). migrate: push a capture dir's docs.json + assets.json to Prismic via the shared runner (needs PRISMIC_REPOSITORY_NAME + PRISMIC_WRITE_TOKEN).\",\n )\n .option(\n \"--out <dir>\",\n \"Output directory for capture (default: webflow-out) / docs (default: dirname of the ir.json)\",\n )\n .action(\n async (\n action: string,\n target: string | undefined,\n opts: { out?: string; cwd?: string; verbose?: boolean },\n ) =>\n runOrExit(\n async () => (await import(\"./commands/webflow.js\")).runWebflowCommand(action, target, opts),\n opts,\n ),\n );\n\ncli.help();\ncli.version(version);\n\n// A typo'd / unrecognized subcommand (e.g. `reddoor-maint auditt`) otherwise\n// falls through cac with no matched command and exits 0 — a cron/CI typo would\n// \"succeed\" silently. cac emits `command:*` at the end of parse() exactly when\n// a positional arg was given but matched no command (a bare `reddoor-maint`,\n// `--help`, and `--version` do NOT trigger it: they have no leading positional\n// or are handled before this fires). Turn that into a clear stderr error +\n// non-zero exit. process.argv[2] is the first positional, i.e. the bad command.\ncli.on(\"command:*\", () => {\n const unknown = cli.args[0] ?? process.argv[2] ?? \"\";\n console.error(\n `error: unknown command '${unknown}'. Run 'reddoor-maint --help' to see available commands.`,\n );\n process.exit(1);\n});\n\n// Retired flag spellings (`--write-airtable` → `--write-back`, #698) are rewritten\n// before cac sees them, so the old names keep working without appearing in --help.\ncli.parse(rewriteRetiredFlags(process.argv));\n","/**\n * Retired CLI flag spellings, and the one place they are translated (#698).\n *\n * `--write-airtable` named the store the flag once wrote to. Since the #539 flip\n * Turso is authoritative and Airtable is the shadow, so the name described the\n * wrong store — and agents believed it. The flag is now `--write-back`: it writes\n * the site's row, in whichever stores the write path owns, which stays true after\n * Phase 6 removes the shadow.\n *\n * The old spelling still works, because it is typed by runbooks, shell history\n * and anything outside this repository that cannot be updated in the same commit.\n * It is rewritten in argv BEFORE cac parses rather than registered as a second\n * `.option()`: cac has no hidden options, so a second registration would keep\n * advertising the retired name in every `--help`.\n *\n * Kept free of imports: bin.ts loads it statically, and the smoke-dist gate holds\n * bin.js's static import closure to its own light dependencies.\n */\nexport const RETIRED_FLAGS: Readonly<Record<string, string>> = {\n \"--write-airtable\": \"--write-back\",\n};\n\n/** Return `argv` with every retired flag replaced by its current name. `argv[0]`\n * and `argv[1]` (node and the script) are never touched, nor is anything after a\n * `--` terminator. Each retired spelling seen is reported once through `note`. */\nexport function rewriteRetiredFlags(\n argv: readonly string[],\n note: (message: string) => void = (message) => console.error(message),\n): string[] {\n const seen = new Set<string>();\n const out = argv.slice(0, 2);\n let terminated = false;\n for (const token of argv.slice(2)) {\n if (terminated || token === \"--\") {\n terminated = true;\n out.push(token);\n continue;\n }\n const eq = token.indexOf(\"=\");\n const name = eq === -1 ? token : token.slice(0, eq);\n const next = Object.hasOwn(RETIRED_FLAGS, name) ? RETIRED_FLAGS[name] : undefined;\n if (next === undefined) {\n out.push(token);\n continue;\n }\n if (!seen.has(name)) {\n seen.add(name);\n note(`note: ${name} is now ${next}; the old spelling still works.`);\n }\n out.push(eq === -1 ? next : `${next}${token.slice(eq)}`);\n }\n return out;\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Read the @reddoorla/maintenance package version, given the directory of a\n * file that lives inside the package. Walks UP looking for the first\n * `package.json` whose `name` matches. Defensive against bundling-layout\n * changes — the older \"two levels up\" assumption held for `dist/cli/bin.js`\n * but would silently mis-read (or return \"unknown\") if tsup ever moved bin\n * to a different depth. Same walk-up pattern as the self-version helper.\n *\n * Returns \"unknown\" when no matching package.json is reachable (Yarn PnP\n * setups stash manifests inside .zip caches; the readFileSync there fails\n * before any name check).\n */\nexport function resolvePackageVersion(fromDir: string): string {\n try {\n let dir = fromDir;\n while (true) {\n const candidate = join(dir, \"package.json\");\n if (existsSync(candidate)) {\n const raw = readFileSync(candidate, \"utf-8\");\n const pkg = JSON.parse(raw) as { name?: string; version?: string };\n if (pkg.name === \"@reddoorla/maintenance\") {\n return pkg.version ?? \"unknown\";\n }\n }\n const parent = dirname(dir);\n if (parent === dir) return \"unknown\";\n dir = parent;\n }\n } catch {\n return \"unknown\";\n }\n}\n"],"mappings":"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","names":["dirname","dirname"]}
|
|
@@ -1,7 +1,34 @@
|
|
|
1
1
|
import { S as Site, a as AuditResult, A as AuditName } from '../../types-D_bpy89i.js';
|
|
2
|
-
import { A as AirtableBase, F as FleetEvent } from '../../fleet-events-
|
|
2
|
+
import { W as WebsiteRow, A as AirtableBase, F as FleetEvent } from '../../fleet-events-CkOZpMVC.js';
|
|
3
3
|
import 'airtable/lib/airtable_base.js';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The fleet roster for batch jobs — every site row, read from Turso (#646 step 4).
|
|
7
|
+
*
|
|
8
|
+
* Until step 4 each batch job (the audit write-back, `github-signals`,
|
|
9
|
+
* `renovate-dispatch`, `header-image`, the Prismic verdict sink) listed sites
|
|
10
|
+
* with the Airtable `listWebsites` and matched its results against that list.
|
|
11
|
+
* Since step 3 a site created by `ensure-site` has a `site_<ULID>` id and no
|
|
12
|
+
* Airtable record, so an Airtable roster is silently short: the site is never
|
|
13
|
+
* swept, or it is swept and then fails its write-back with "no Websites row
|
|
14
|
+
* matched". Turso holds every site.
|
|
15
|
+
*
|
|
16
|
+
* Same contract as `listWebsites` — every site, one `WebsiteRow` each — and the
|
|
17
|
+
* rows are the ones the reader-equivalence instrument pins field-for-field to
|
|
18
|
+
* Airtable's `mapRow` (tests/db/fleet-state.test.ts).
|
|
19
|
+
*
|
|
20
|
+
* The Airtable SHADOW writes these jobs still make are untouched: each Airtable
|
|
21
|
+
* writer skips a non-`rec` id itself (`AIRTABLE_SHADOW skipped=non-rec-id`).
|
|
22
|
+
*
|
|
23
|
+
* `open` defaults to `TURSO_DATABASE_URL`/`TURSO_AUTH_TOKEN`; tests inject a temp
|
|
24
|
+
* `file:` db or a whole `FleetRoster`. The connection this opens is closed before
|
|
25
|
+
* returning — the roster is a snapshot, not a handle. The db stack is imported
|
|
26
|
+
* lazily: `kysely` and the libSQL client are devDependencies.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/** A source of the fleet's site rows. Injected by tests; defaulted to Turso. */
|
|
30
|
+
type FleetRoster = () => Promise<WebsiteRow[]>;
|
|
31
|
+
|
|
5
32
|
/** One site's just-written Airtable FieldSet, mirrored into site_health.
|
|
6
33
|
* Resolves true when a site_health row matched; false when the UPDATE touched
|
|
7
34
|
* 0 rows (site created in Airtable after the last hourly import) — callers
|
|
@@ -19,7 +46,7 @@ type AuditCommandOptions = {
|
|
|
19
46
|
* in Airtable. `true` (no value) = derive slug from cwd/package.json#name;
|
|
20
47
|
* string = explicit slug (e.g. "med-solutions-of-texas").
|
|
21
48
|
*/
|
|
22
|
-
|
|
49
|
+
writeBack?: string | boolean;
|
|
23
50
|
/** Exit non-zero if any a11y violations are found (overrides warn). For CI gates. */
|
|
24
51
|
failOnViolations?: boolean;
|
|
25
52
|
/** Audit this deployed URL directly (lighthouse only; single-site). */
|
|
@@ -54,7 +81,7 @@ declare function runAuditCommand(site: string | undefined, opts: AuditCommandOpt
|
|
|
54
81
|
output: string;
|
|
55
82
|
code: number;
|
|
56
83
|
}>;
|
|
57
|
-
/** The fleet `--write-
|
|
84
|
+
/** The fleet `--write-back` step: write every site's audits back to its
|
|
58
85
|
* Websites row, dual-write each just-written FieldSet into Turso (#539
|
|
59
86
|
* Phase 3), and record fleet-activity events. Extracted from runAuditCommand
|
|
60
87
|
* so the mirror WIRING itself is pinned by test — the mutation this seam
|
|
@@ -68,6 +95,9 @@ declare function runFleetWriteBack(args: {
|
|
|
68
95
|
which: AuditName[];
|
|
69
96
|
deps?: {
|
|
70
97
|
openBase?: () => AirtableBase;
|
|
98
|
+
/** #646 step 4: the rows results are matched against. Defaults to Turso
|
|
99
|
+
* (`readFleetRoster`); tests inject. */
|
|
100
|
+
roster?: FleetRoster;
|
|
71
101
|
makeMirror?: () => Promise<HealthMirror | null>;
|
|
72
102
|
recordEvents?: (events: FleetEvent[], now: Date) => Promise<void>;
|
|
73
103
|
/** #612. `true` = post-freeze, where a mirror failure, a missed row or an
|
|
@@ -5,37 +5,37 @@ import {
|
|
|
5
5
|
fleetWorkdir,
|
|
6
6
|
formatSkippedNotice,
|
|
7
7
|
prepareFleetSites
|
|
8
|
-
} from "../../chunk-
|
|
8
|
+
} from "../../chunk-DYTYPKFN.js";
|
|
9
9
|
import {
|
|
10
10
|
resolveSites
|
|
11
|
-
} from "../../chunk-
|
|
11
|
+
} from "../../chunk-FP6HOW6J.js";
|
|
12
12
|
import {
|
|
13
13
|
ALL_AUDIT_NAMES,
|
|
14
14
|
runOneAudit
|
|
15
|
-
} from "../../chunk-
|
|
15
|
+
} from "../../chunk-L3S3QZXL.js";
|
|
16
16
|
import "../../chunk-LKGVSM2O.js";
|
|
17
|
-
import "../../chunk-
|
|
18
|
-
import "../../chunk-
|
|
17
|
+
import "../../chunk-MVKNU4QD.js";
|
|
18
|
+
import "../../chunk-43SW3FGW.js";
|
|
19
19
|
import "../../chunk-Z3JXLULD.js";
|
|
20
20
|
import {
|
|
21
21
|
recordFleetEventsBestEffort
|
|
22
|
-
} from "../../chunk-
|
|
23
|
-
import "../../chunk-
|
|
24
|
-
import "../../chunk-
|
|
25
|
-
import "../../chunk-2NQUCS7O.js";
|
|
22
|
+
} from "../../chunk-YXWBM26P.js";
|
|
23
|
+
import "../../chunk-MMSMZC7S.js";
|
|
24
|
+
import "../../chunk-BIYQN7FH.js";
|
|
26
25
|
import "../../chunk-UDRHAQT7.js";
|
|
27
26
|
import "../../chunk-PTDMVGKL.js";
|
|
28
27
|
import "../../chunk-LBYOLBW7.js";
|
|
28
|
+
import "../../chunk-IOTOOTII.js";
|
|
29
29
|
import "../../chunk-A3WDWNN5.js";
|
|
30
30
|
import "../../chunk-2OP4JKIQ.js";
|
|
31
31
|
import {
|
|
32
32
|
isHttpUrl
|
|
33
33
|
} from "../../chunk-RARREDQE.js";
|
|
34
34
|
import "../../chunk-XXTZBPUY.js";
|
|
35
|
-
import "../../chunk-
|
|
35
|
+
import "../../chunk-WMF5S2MB.js";
|
|
36
36
|
import "../../chunk-6VAL7XJT.js";
|
|
37
37
|
import "../../chunk-NMAWXBJA.js";
|
|
38
|
-
import "../../chunk-
|
|
38
|
+
import "../../chunk-CXICCLNM.js";
|
|
39
39
|
|
|
40
40
|
// src/cli/commands/audit.ts
|
|
41
41
|
import { resolve } from "path";
|
|
@@ -194,10 +194,10 @@ function applyDeployedUrl(sites, url) {
|
|
|
194
194
|
async function runAuditCommand(site, opts) {
|
|
195
195
|
const which = parseOnly(opts.only) ?? ALL_AUDIT_NAMES;
|
|
196
196
|
const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();
|
|
197
|
-
if (typeof opts.
|
|
197
|
+
if (typeof opts.writeBack === "string" && opts.fleet !== void 0) {
|
|
198
198
|
throw Object.assign(
|
|
199
199
|
new Error(
|
|
200
|
-
"--write-
|
|
200
|
+
"--write-back=<slug> is single-site; with --fleet each site's slug comes from the inventory. Use --write-back (no slug) + --fleet."
|
|
201
201
|
),
|
|
202
202
|
{ exitCode: 2 }
|
|
203
203
|
);
|
|
@@ -242,9 +242,8 @@ ${skipNotice}`;
|
|
|
242
242
|
${formatUnmeasuredSmokeSummary(results)}`;
|
|
243
243
|
}
|
|
244
244
|
let writeBackFailed = false;
|
|
245
|
-
if (opts.
|
|
245
|
+
if (opts.writeBack !== void 0) {
|
|
246
246
|
const { openBase, readAirtableConfig } = await import("../../client-EPVO3FEH.js");
|
|
247
|
-
const { listWebsites } = await import("../../websites-TB4TCAWM.js");
|
|
248
247
|
if (opts.fleet !== void 0) {
|
|
249
248
|
const wb = await runFleetWriteBack({ results, which });
|
|
250
249
|
if (wb.anyFailed) writeBackFailed = true;
|
|
@@ -252,9 +251,9 @@ ${formatUnmeasuredSmokeSummary(results)}`;
|
|
|
252
251
|
|
|
253
252
|
${wb.summary}`;
|
|
254
253
|
} else {
|
|
255
|
-
const { resolveSlugFromCwd } = await import("../../lighthouse-airtable-
|
|
256
|
-
const { writeAuditsToAirtable } = await import("../../write-audits-to-airtable-
|
|
257
|
-
const slug = typeof opts.
|
|
254
|
+
const { resolveSlugFromCwd } = await import("../../lighthouse-airtable-M45OPVRV.js");
|
|
255
|
+
const { writeAuditsToAirtable } = await import("../../write-audits-to-airtable-TI3JMWSY.js");
|
|
256
|
+
const slug = typeof opts.writeBack === "string" && opts.writeBack.length > 0 ? opts.writeBack : await resolveSlugFromCwd(cwd);
|
|
258
257
|
let writeSummary = null;
|
|
259
258
|
await new Listr(
|
|
260
259
|
[
|
|
@@ -262,12 +261,13 @@ ${wb.summary}`;
|
|
|
262
261
|
title: `Write to Airtable[${slug}]`,
|
|
263
262
|
task: async (_ctx, task) => {
|
|
264
263
|
const base = openBase(readAirtableConfig());
|
|
265
|
-
task.output = "loading
|
|
266
|
-
const
|
|
264
|
+
task.output = "loading the fleet roster\u2026";
|
|
265
|
+
const { readFleetRoster } = await import("../../roster-GDNSIRAK.js");
|
|
266
|
+
const websites = await readFleetRoster();
|
|
267
267
|
task.output = "writing scores\u2026";
|
|
268
268
|
writeSummary = await writeAuditsToAirtable({ base, websites, slug, results });
|
|
269
269
|
if (writeSummary.siteId && writeSummary.fields) {
|
|
270
|
-
const { makeSiteMirror } = await import("../../site-mirror-
|
|
270
|
+
const { makeSiteMirror } = await import("../../site-mirror-WR7ACXOV.js");
|
|
271
271
|
await (await makeSiteMirror()).health(writeSummary.siteId, writeSummary.fields);
|
|
272
272
|
}
|
|
273
273
|
task.title = `Wrote to Websites[${writeSummary.siteName}] (${writeSummary.writes.length} audit type${writeSummary.writes.length === 1 ? "" : "s"})`;
|
|
@@ -293,7 +293,7 @@ ${notice}`;
|
|
|
293
293
|
}
|
|
294
294
|
async function runFleetWriteBack(args) {
|
|
295
295
|
const { results, which, deps = {} } = args;
|
|
296
|
-
const { writeFleetAuditsToAirtable, formatFleetWriteSummary, fleetWriteFailed } = await import("../../write-audits-to-airtable-
|
|
296
|
+
const { writeFleetAuditsToAirtable, formatFleetWriteSummary, fleetWriteFailed } = await import("../../write-audits-to-airtable-TI3JMWSY.js");
|
|
297
297
|
let base;
|
|
298
298
|
if (deps.openBase) {
|
|
299
299
|
base = deps.openBase();
|
|
@@ -301,10 +301,13 @@ async function runFleetWriteBack(args) {
|
|
|
301
301
|
const { openBase, readAirtableConfig } = await import("../../client-EPVO3FEH.js");
|
|
302
302
|
base = openBase(readAirtableConfig());
|
|
303
303
|
}
|
|
304
|
-
const
|
|
305
|
-
|
|
304
|
+
const roster = deps.roster ?? (async () => {
|
|
305
|
+
const { readFleetRoster } = await import("../../roster-GDNSIRAK.js");
|
|
306
|
+
return readFleetRoster();
|
|
307
|
+
});
|
|
308
|
+
const websites = await roster();
|
|
306
309
|
const makeMirror = deps.makeMirror ?? (async () => {
|
|
307
|
-
const { makeHealthMirrorBestEffort } = await import("../../health-mirror-
|
|
310
|
+
const { makeHealthMirrorBestEffort } = await import("../../health-mirror-KU3G7V2Y.js");
|
|
308
311
|
return makeHealthMirrorBestEffort();
|
|
309
312
|
});
|
|
310
313
|
const mirror = await makeMirror();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/cli/commands/audit.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { Listr } from \"listr2\";\nimport { runOneAudit, ALL_AUDIT_NAMES } from \"../../audits/index.js\";\nimport type { AuditName, AuditResult, Site } from \"../../types.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport {\n prepareFleetSites,\n formatSkippedNotice,\n type SkippedSite,\n} from \"../fleet/prepare-sites.js\";\nimport { isHttpUrl } from \"../../util/url.js\";\nimport { fleetWorkdir } from \"../../util/fleet-workdir.js\";\nimport { recordFleetEventsBestEffort } from \"../../audits/fleet-events-writer.js\";\nimport { fleetSweptEvent } from \"../../audits/fleet-event-detectors.js\";\nimport type { AirtableBase } from \"../../reports/airtable/client.js\";\nimport type { HealthMirror } from \"../../audits/health-mirror.js\";\nimport type { FleetEvent } from \"../../db/fleet-events.js\";\n\nexport type AuditCommandOptions = {\n only?: string;\n json?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n /**\n * After running, push the lighthouse scores to the matching Websites row\n * in Airtable. `true` (no value) = derive slug from cwd/package.json#name;\n * string = explicit slug (e.g. \"med-solutions-of-texas\").\n */\n writeAirtable?: string | boolean;\n /** Exit non-zero if any a11y violations are found (overrides warn). For CI gates. */\n failOnViolations?: boolean;\n /** Audit this deployed URL directly (lighthouse only; single-site). */\n url?: string;\n /** Max sites to audit in parallel (fleet mode). Unset = all at once;\n * `1` = sequential (used by the nightly CI workflow). */\n concurrency?: string;\n};\n\nfunction parseOnly(value: string | undefined): AuditName[] | undefined {\n if (!value) return undefined;\n const names = value.split(\",\").map((s) => s.trim());\n for (const n of names) {\n if (!ALL_AUDIT_NAMES.includes(n as AuditName)) {\n throw Object.assign(new Error(`unknown audit in --only: ${n}`), { exitCode: 2 });\n }\n }\n return names as AuditName[];\n}\n\nfunction formatTable(results: AuditResult[]): string {\n return results\n .map((r) => `${r.audit.padEnd(12)} ${r.status.padEnd(5)} ${r.site}\\n ${r.summary}`)\n .join(\"\\n\");\n}\n\nexport function auditExitCode(results: AuditResult[], failOnViolations: boolean): number {\n if (results.some((r) => r.status === \"fail\")) return 1;\n if (failOnViolations) {\n const a11yViolations = results\n .filter((r) => r.audit === \"a11y\")\n .reduce(\n (n, r) =>\n n + ((r.details as { totalViolations?: number } | undefined)?.totalViolations ?? 0),\n 0,\n );\n if (a11yViolations > 0) return 1;\n }\n return 0;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1_000) return `${ms}ms`;\n const totalSeconds = Math.round(ms / 1_000);\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const m = Math.floor(totalSeconds / 60);\n const s = totalSeconds % 60;\n return `${m}m${s.toString().padStart(2, \"0\")}s`;\n}\n\ntype Renderer = \"default\" | \"silent\";\n\n/** Parse the `--concurrency <n>` flag into a Listr `concurrent` value. Unset →\n * `true` (all sites in parallel, the interactive default). A positive integer\n * bounds how many sites audit at once — `--concurrency 1` runs sequentially,\n * which is what the nightly CI workflow uses so ~10 deployed-Lighthouse runs\n * don't saturate a 2-core runner (one flaked locally at full parallelism). */\nexport function parseConcurrency(value: string | undefined): boolean | number {\n if (value === undefined) return true;\n const n = Number(value);\n if (!Number.isInteger(n) || n < 1) {\n throw Object.assign(new Error(`--concurrency must be a positive integer, got \"${value}\"`), {\n exitCode: 2,\n });\n }\n return n;\n}\n\n/** Build the audit-progress task list. Single-site → each audit is a sibling\n * task. Fleet → each site is a task whose `output` shows X/N audits done as\n * they complete (audits-per-site still run in parallel). Results are pushed\n * into the shared `results` array; tasks throw on `fail` status so listr2\n * paints them red, but `exitOnError: false` keeps other tasks running. */\nfunction buildAuditTasks(\n sites: Site[],\n which: AuditName[],\n results: AuditResult[],\n renderer: Renderer,\n concurrency: boolean | number,\n) {\n const singleSite = sites.length === 1;\n\n if (singleSite) {\n const site = sites[0]!;\n return new Listr(\n which.map((name) => ({\n title: name,\n task: async (_ctx, task) => {\n const start = Date.now();\n const result = await runOneAudit(site, name);\n results.push(result);\n const elapsed = formatDuration(Date.now() - start);\n task.title = `${name}: ${result.summary} (${elapsed})`;\n if (result.status === \"fail\") throw new Error(result.summary);\n },\n })),\n { concurrent: true, exitOnError: false, renderer },\n );\n }\n\n return new Listr(\n sites.map((site) => {\n const label = site.name || site.path; // `||`: empty slug must fall back to path, not blank\n return {\n title: label,\n task: async (_ctx, task) => {\n const start = Date.now();\n let done = 0;\n task.output = `0/${which.length} audits`;\n const settled = await Promise.all(\n which.map(async (name) => {\n const r = await runOneAudit(site, name);\n results.push(r);\n done += 1;\n task.output = `${done}/${which.length} audits`;\n return r;\n }),\n );\n const elapsed = formatDuration(Date.now() - start);\n const failed = settled.filter((r) => r.status === \"fail\").length;\n const warned = settled.filter((r) => r.status === \"warn\").length;\n const note =\n failed > 0\n ? `${failed} failed`\n : warned > 0\n ? `${warned} warning${warned === 1 ? \"\" : \"s\"}`\n : \"all green\";\n task.title = `${label}: ${note} (${elapsed})`;\n if (failed > 0) throw new Error(`${label}: ${failed} audit(s) failed`);\n },\n };\n }),\n { concurrent: concurrency, exitOnError: false, renderer },\n );\n}\n\ntype WriteSummary = Awaited<\n ReturnType<typeof import(\"../../audits/write-audits-to-airtable.js\").writeAuditsToAirtable>\n>;\n\nfunction formatWriteSummary(summary: WriteSummary): string {\n const lines = summary.writes.map((w) => {\n if (w.audit === \"lighthouse\") {\n const s = w.counts as {\n performance: number;\n accessibility: number;\n bestPractices: number;\n seo: number;\n };\n return ` lighthouse: P=${s.performance} A=${s.accessibility} BP=${s.bestPractices} SEO=${s.seo}`;\n }\n if (w.audit === \"a11y\") {\n return ` a11y: ${(w.counts as { violations: number }).violations} violations`;\n }\n if (w.audit === \"deps\") {\n const c = w.counts as { drifted: number; majorBehind: number };\n return ` deps: ${c.drifted} drifted (${c.majorBehind} major)`;\n }\n const c = w.counts as { critical: number; high: number; moderate: number; low: number };\n return ` security: ${c.critical}C/${c.high}H/${c.moderate}M/${c.low}L`;\n });\n return `→ wrote to Websites[${summary.siteName}]:\\n${lines.join(\"\\n\")}`;\n}\n\n/** Listr renderer choice. `--json` → silent so stdout stays clean for piping.\n * Otherwise listr's `default` renderer auto-falls back to `simple` in\n * non-TTY contexts (CI, log capture, our own integration tests). */\nfunction rendererFor(json: boolean | undefined): Renderer {\n return json ? \"silent\" : \"default\";\n}\n\n/** When `--url` is set but audits other than lighthouse also ran (those use the\n * local checkout, not the deployed URL), return a one-line operator notice;\n * null when there's nothing to warn about. Keeps the mixed-provenance result\n * table from silently confusing the operator. */\nexport function deployedUrlNotice(\n which: AuditName[],\n url: string | undefined,\n cwd: string,\n): string | null {\n if (url === undefined) return null;\n const others = which.filter((n) => n !== \"lighthouse\");\n if (others.length === 0) return null;\n return `note: --url only affects lighthouse; ${others.join(\", \")} ran against the local checkout at ${cwd}`;\n}\n\n/** Audits that need no repo checkout — they probe an external resource. lighthouse hits the\n * live URL; domain probes DNS/TLS of the URL's host; browser drives the deployed URL;\n * netlify-deploy queries the Netlify API by site id; function-health fetches the deployed\n * `/health` endpoint. */\nconst CHECKOUT_FREE_AUDITS: ReadonlySet<AuditName> = new Set<AuditName>([\n \"lighthouse\",\n \"domain\",\n \"browser\",\n \"netlify-deploy\",\n \"function-health\",\n \"form-e2e\",\n]);\n\n/** Audits whose checkout-free run is keyed off `site.netlifyId` (the Netlify API) rather than\n * the deployed URL. Everything else in CHECKOUT_FREE_AUDITS reads `site.deployedUrl`. */\nconst NETLIFY_ID_AUDITS: ReadonlySet<AuditName> = new Set<AuditName>([\"netlify-deploy\"]);\n\n/** A fleet site needs a local checkout unless every requested audit is checkout-free AND the site\n * has the external resource each one runs against (`deployedUrl` for lighthouse/domain/browser,\n * `netlifyId` for netlify-deploy). */\nexport function auditNeedsCheckout(site: Site, which: AuditName[]): boolean {\n const deployedCapable = which.every((n) => {\n if (!CHECKOUT_FREE_AUDITS.has(n)) return false;\n return NETLIFY_ID_AUDITS.has(n) ? site.netlifyId !== undefined : site.deployedUrl !== undefined;\n });\n return !deployedCapable;\n}\n\n/** Apply a single-site `--url` to the resolved sites. Returns the input\n * untouched when no url is given; otherwise requires exactly one site and\n * stamps `deployedUrl` on it so the lighthouse audit takes its deployed path.\n * The `--url`+`--fleet` combination is rejected earlier in `runAuditCommand`;\n * this length guard also covers any future multi-site single-run resolver. */\nexport function applyDeployedUrl(sites: Site[], url: string | undefined): Site[] {\n if (url === undefined) return sites;\n if (sites.length !== 1) {\n throw Object.assign(\n new Error(`--url expects exactly one site, but ${sites.length} resolved.`),\n { exitCode: 2 },\n );\n }\n // Scheme-allowlist: the URL is handed straight to Chrome/lhci, so only\n // http(s) is safe (a file:///gopher:// value would be a local-file read /\n // SSRF). Same predicate the inventory paths use.\n if (!isHttpUrl(url)) {\n throw Object.assign(new Error(`--url must be an http(s) URL (got: ${JSON.stringify(url)})`), {\n exitCode: 2,\n });\n }\n return [{ ...sites[0]!, deployedUrl: url }];\n}\n\nexport async function runAuditCommand(\n site: string | undefined,\n opts: AuditCommandOptions,\n): Promise<{ output: string; code: number }> {\n const which = parseOnly(opts.only) ?? ALL_AUDIT_NAMES;\n const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();\n\n // A literal --write-airtable=<slug> is single-site (the slug names one row).\n // Boolean --write-airtable + --fleet is fine: each site's slug comes from the\n // inventory, so there's no cwd-derived-slug ambiguity.\n if (typeof opts.writeAirtable === \"string\" && opts.fleet !== undefined) {\n throw Object.assign(\n new Error(\n \"--write-airtable=<slug> is single-site; with --fleet each site's slug comes from the inventory. Use --write-airtable (no slug) + --fleet.\",\n ),\n { exitCode: 2 },\n );\n }\n\n if (opts.url !== undefined && opts.fleet !== undefined) {\n throw Object.assign(\n new Error(\n \"--url is single-site only and cannot be combined with --fleet. Audit a single site instead.\",\n ),\n { exitCode: 2 },\n );\n }\n\n let sites = await resolveSites({\n ...(site !== undefined ? { site } : {}),\n ...(opts.fleet !== undefined ? { fleet: opts.fleet } : {}),\n ...(opts.workdir !== undefined ? { workdir: opts.workdir } : {}),\n cwd,\n });\n\n sites = applyDeployedUrl(sites, opts.url);\n\n let skippedPrep: SkippedSite[] = [];\n if (opts.fleet) {\n const workdir = opts.workdir ?? fleetWorkdir();\n const prep = await prepareFleetSites(sites, {\n workdir,\n needsCheckout: (s) => auditNeedsCheckout(s, which),\n });\n sites = prep.prepared;\n skippedPrep = prep.skipped;\n }\n\n const results: AuditResult[] = [];\n const renderer = rendererFor(opts.json);\n await buildAuditTasks(sites, which, results, renderer, parseConcurrency(opts.concurrency)).run();\n\n let output = opts.json ? JSON.stringify(results, null, 2) : formatTable(results);\n\n // Surface any site that couldn't be prepared (no auditable target, clone\n // failure) — visibly, but without reding the run. One misconfigured inventory\n // row is an operator fix, not an outage; the other sites still audited and\n // wrote back. \"No silent caps\": a dropped site is never invisible.\n const skipNotice = formatSkippedNotice(skippedPrep);\n if (skipNotice && !opts.json) output += `\\n\\n${skipNotice}`;\n\n // \"Did every site actually get measured?\" — emitted for any fleet sweep that ran\n // `smoke`, independent of --write-airtable, because it is a statement about the\n // RUN, not about persistence. Deliberately not folded into FLEET_WRITE_SUMMARY:\n // an unmeasured site still writes (it just writes nothing new), so the write gate\n // counts it as a success. That gap is exactly what hid four nights of timeouts.\n if (opts.fleet !== undefined && which.includes(\"smoke\") && !opts.json) {\n const { formatUnmeasuredSmokeSummary } = await import(\"../../audits/smoke.js\");\n output += `\\n\\n${formatUnmeasuredSmokeSummary(results)}`;\n }\n\n // Did any site fail to write back to Airtable? The fleet writer collects\n // per-site failures instead of throwing, so without this the command would\n // exit 0 while rows silently failed to persist — automation keying on `$?`\n // would see a clean run. (The single-site writer throws on failure, so it's\n // already non-zero via the propagated error.)\n let writeBackFailed = false;\n if (opts.writeAirtable !== undefined) {\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n const { listWebsites } = await import(\"../../reports/airtable/websites.js\");\n\n if (opts.fleet !== undefined) {\n const wb = await runFleetWriteBack({ results, which });\n if (wb.anyFailed) writeBackFailed = true;\n // Gate on !json: the write-summary is human text; appending it after the\n // results array would corrupt `--json` output (the other notices already\n // guard this way). The write itself still happens regardless of --json.\n if (!opts.json) output += `\\n\\n${wb.summary}`;\n } else {\n const { resolveSlugFromCwd } = await import(\"../../audits/lighthouse-airtable.js\");\n const { writeAuditsToAirtable } = await import(\"../../audits/write-audits-to-airtable.js\");\n const slug =\n typeof opts.writeAirtable === \"string\" && opts.writeAirtable.length > 0\n ? opts.writeAirtable\n : await resolveSlugFromCwd(cwd);\n let writeSummary: WriteSummary | null = null;\n await new Listr(\n [\n {\n title: `Write to Airtable[${slug}]`,\n task: async (_ctx, task) => {\n const base = openBase(readAirtableConfig());\n task.output = \"loading Websites…\";\n const websites = await listWebsites(base);\n task.output = \"writing scores…\";\n writeSummary = await writeAuditsToAirtable({ base, websites, slug, results });\n // #539 Phase 5: the FLEET path has mirrored since Phase 3, this\n // single-site one never did — same write, same columns, reaching\n // Turso only via the hourly sync. The summary already carries the\n // exact FieldSet Airtable got, so no signature change is needed.\n if (writeSummary.siteId && writeSummary.fields) {\n const { makeSiteMirror } = await import(\"../../db/site-mirror.js\");\n await (await makeSiteMirror()).health(writeSummary.siteId, writeSummary.fields);\n }\n task.title = `Wrote to Websites[${writeSummary.siteName}] (${writeSummary.writes.length} audit type${writeSummary.writes.length === 1 ? \"\" : \"s\"})`;\n },\n },\n ],\n { renderer },\n ).run();\n if (writeSummary && !opts.json) output += `\\n\\n${formatWriteSummary(writeSummary)}`;\n }\n }\n\n const notice = deployedUrlNotice(which, opts.url, cwd);\n if (notice && !opts.json) output += `\\n\\n${notice}`;\n\n const code = Math.max(\n auditExitCode(results, opts.failOnViolations === true),\n writeBackFailed ? 1 : 0,\n );\n return { output, code };\n}\n\n/** The fleet `--write-airtable` step: write every site's audits back to its\n * Websites row, dual-write each just-written FieldSet into Turso (#539\n * Phase 3), and record fleet-activity events. Extracted from runAuditCommand\n * so the mirror WIRING itself is pinned by test — the mutation this seam\n * kills is dropping the `...(mirror ? { mirror } : {})` pass-through, which\n * would silently stop all five nightly sweeps from mirroring while every\n * sweep stayed green (adversarial review of #566, finding 6). Deps default\n * to the real fleet wiring; dynamic imports keep the no-write paths from\n * loading Airtable/db clients. */\nexport async function runFleetWriteBack(args: {\n results: AuditResult[];\n which: AuditName[];\n deps?: {\n openBase?: () => AirtableBase;\n makeMirror?: () => Promise<HealthMirror | null>;\n recordEvents?: (events: FleetEvent[], now: Date) => Promise<void>;\n /** #612. `true` = post-freeze, where a mirror failure, a missed row or an\n * absent mirror are each fatal. Injected so both sides stay proven and the\n * freeze commit stays a one-line change. */\n strict?: boolean;\n };\n}): Promise<{ summary: string; anyFailed: boolean }> {\n const { results, which, deps = {} } = args;\n const { writeFleetAuditsToAirtable, formatFleetWriteSummary, fleetWriteFailed } =\n await import(\"../../audits/write-audits-to-airtable.js\");\n let base: AirtableBase;\n if (deps.openBase) {\n base = deps.openBase();\n } else {\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n base = openBase(readAirtableConfig());\n }\n const { listWebsites } = await import(\"../../reports/airtable/websites.js\");\n const websites = await listWebsites(base);\n // Phase 3 dual-write (#539): mirror each site's written FieldSet into\n // site_health. Null when libSQL creds are absent — Airtable write-back\n // proceeds exactly as before.\n const makeMirror =\n deps.makeMirror ??\n (async () => {\n const { makeHealthMirrorBestEffort } = await import(\"../../audits/health-mirror.js\");\n return makeHealthMirrorBestEffort();\n });\n const mirror = await makeMirror();\n const fleetWrite = await writeFleetAuditsToAirtable({\n base,\n websites,\n results,\n ...(mirror ? { mirror } : {}),\n });\n // Record fleet-activity events (vuln_cleared / cert_renewed rode along on each\n // WriteSummary) plus a per-sweep rollup. Best-effort: a missing Turso cred no-ops.\n const sweep = which.includes(\"security\") ? \"security\" : \"lighthouse\";\n const now = new Date();\n const auditEvents = fleetWrite.written.flatMap((w) => w.events ?? []);\n await (deps.recordEvents ?? recordFleetEventsBestEffort)(\n [...auditEvents, fleetSweptEvent(sweep, fleetWrite.written.length, now.toISOString())],\n now,\n );\n // #612: post-freeze a mirror failure, a missed row, or an absent mirror all\n // become fatal — there is no hourly import left to converge them, so a sweep\n // that wrote nothing into the only store would otherwise finish green.\n return {\n summary: formatFleetWriteSummary(fleetWrite),\n anyFailed:\n deps.strict === undefined\n ? fleetWriteFailed(fleetWrite)\n : fleetWriteFailed(fleetWrite, deps.strict),\n };\n}\n"],"mappings":"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","names":["c"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/cli/commands/audit.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { Listr } from \"listr2\";\nimport { runOneAudit, ALL_AUDIT_NAMES } from \"../../audits/index.js\";\nimport type { AuditName, AuditResult, Site } from \"../../types.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\nimport {\n prepareFleetSites,\n formatSkippedNotice,\n type SkippedSite,\n} from \"../fleet/prepare-sites.js\";\nimport { isHttpUrl } from \"../../util/url.js\";\nimport { fleetWorkdir } from \"../../util/fleet-workdir.js\";\nimport { recordFleetEventsBestEffort } from \"../../audits/fleet-events-writer.js\";\nimport { fleetSweptEvent } from \"../../audits/fleet-event-detectors.js\";\nimport type { AirtableBase } from \"../../reports/airtable/client.js\";\nimport type { FleetRoster } from \"../../fleet/roster.js\";\nimport type { HealthMirror } from \"../../audits/health-mirror.js\";\nimport type { FleetEvent } from \"../../db/fleet-events.js\";\n\nexport type AuditCommandOptions = {\n only?: string;\n json?: boolean;\n fleet?: string;\n workdir?: string;\n cwd?: string;\n /**\n * After running, push the lighthouse scores to the matching Websites row\n * in Airtable. `true` (no value) = derive slug from cwd/package.json#name;\n * string = explicit slug (e.g. \"med-solutions-of-texas\").\n */\n writeBack?: string | boolean;\n /** Exit non-zero if any a11y violations are found (overrides warn). For CI gates. */\n failOnViolations?: boolean;\n /** Audit this deployed URL directly (lighthouse only; single-site). */\n url?: string;\n /** Max sites to audit in parallel (fleet mode). Unset = all at once;\n * `1` = sequential (used by the nightly CI workflow). */\n concurrency?: string;\n};\n\nfunction parseOnly(value: string | undefined): AuditName[] | undefined {\n if (!value) return undefined;\n const names = value.split(\",\").map((s) => s.trim());\n for (const n of names) {\n if (!ALL_AUDIT_NAMES.includes(n as AuditName)) {\n throw Object.assign(new Error(`unknown audit in --only: ${n}`), { exitCode: 2 });\n }\n }\n return names as AuditName[];\n}\n\nfunction formatTable(results: AuditResult[]): string {\n return results\n .map((r) => `${r.audit.padEnd(12)} ${r.status.padEnd(5)} ${r.site}\\n ${r.summary}`)\n .join(\"\\n\");\n}\n\nexport function auditExitCode(results: AuditResult[], failOnViolations: boolean): number {\n if (results.some((r) => r.status === \"fail\")) return 1;\n if (failOnViolations) {\n const a11yViolations = results\n .filter((r) => r.audit === \"a11y\")\n .reduce(\n (n, r) =>\n n + ((r.details as { totalViolations?: number } | undefined)?.totalViolations ?? 0),\n 0,\n );\n if (a11yViolations > 0) return 1;\n }\n return 0;\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1_000) return `${ms}ms`;\n const totalSeconds = Math.round(ms / 1_000);\n if (totalSeconds < 60) return `${totalSeconds}s`;\n const m = Math.floor(totalSeconds / 60);\n const s = totalSeconds % 60;\n return `${m}m${s.toString().padStart(2, \"0\")}s`;\n}\n\ntype Renderer = \"default\" | \"silent\";\n\n/** Parse the `--concurrency <n>` flag into a Listr `concurrent` value. Unset →\n * `true` (all sites in parallel, the interactive default). A positive integer\n * bounds how many sites audit at once — `--concurrency 1` runs sequentially,\n * which is what the nightly CI workflow uses so ~10 deployed-Lighthouse runs\n * don't saturate a 2-core runner (one flaked locally at full parallelism). */\nexport function parseConcurrency(value: string | undefined): boolean | number {\n if (value === undefined) return true;\n const n = Number(value);\n if (!Number.isInteger(n) || n < 1) {\n throw Object.assign(new Error(`--concurrency must be a positive integer, got \"${value}\"`), {\n exitCode: 2,\n });\n }\n return n;\n}\n\n/** Build the audit-progress task list. Single-site → each audit is a sibling\n * task. Fleet → each site is a task whose `output` shows X/N audits done as\n * they complete (audits-per-site still run in parallel). Results are pushed\n * into the shared `results` array; tasks throw on `fail` status so listr2\n * paints them red, but `exitOnError: false` keeps other tasks running. */\nfunction buildAuditTasks(\n sites: Site[],\n which: AuditName[],\n results: AuditResult[],\n renderer: Renderer,\n concurrency: boolean | number,\n) {\n const singleSite = sites.length === 1;\n\n if (singleSite) {\n const site = sites[0]!;\n return new Listr(\n which.map((name) => ({\n title: name,\n task: async (_ctx, task) => {\n const start = Date.now();\n const result = await runOneAudit(site, name);\n results.push(result);\n const elapsed = formatDuration(Date.now() - start);\n task.title = `${name}: ${result.summary} (${elapsed})`;\n if (result.status === \"fail\") throw new Error(result.summary);\n },\n })),\n { concurrent: true, exitOnError: false, renderer },\n );\n }\n\n return new Listr(\n sites.map((site) => {\n const label = site.name || site.path; // `||`: empty slug must fall back to path, not blank\n return {\n title: label,\n task: async (_ctx, task) => {\n const start = Date.now();\n let done = 0;\n task.output = `0/${which.length} audits`;\n const settled = await Promise.all(\n which.map(async (name) => {\n const r = await runOneAudit(site, name);\n results.push(r);\n done += 1;\n task.output = `${done}/${which.length} audits`;\n return r;\n }),\n );\n const elapsed = formatDuration(Date.now() - start);\n const failed = settled.filter((r) => r.status === \"fail\").length;\n const warned = settled.filter((r) => r.status === \"warn\").length;\n const note =\n failed > 0\n ? `${failed} failed`\n : warned > 0\n ? `${warned} warning${warned === 1 ? \"\" : \"s\"}`\n : \"all green\";\n task.title = `${label}: ${note} (${elapsed})`;\n if (failed > 0) throw new Error(`${label}: ${failed} audit(s) failed`);\n },\n };\n }),\n { concurrent: concurrency, exitOnError: false, renderer },\n );\n}\n\ntype WriteSummary = Awaited<\n ReturnType<typeof import(\"../../audits/write-audits-to-airtable.js\").writeAuditsToAirtable>\n>;\n\nfunction formatWriteSummary(summary: WriteSummary): string {\n const lines = summary.writes.map((w) => {\n if (w.audit === \"lighthouse\") {\n const s = w.counts as {\n performance: number;\n accessibility: number;\n bestPractices: number;\n seo: number;\n };\n return ` lighthouse: P=${s.performance} A=${s.accessibility} BP=${s.bestPractices} SEO=${s.seo}`;\n }\n if (w.audit === \"a11y\") {\n return ` a11y: ${(w.counts as { violations: number }).violations} violations`;\n }\n if (w.audit === \"deps\") {\n const c = w.counts as { drifted: number; majorBehind: number };\n return ` deps: ${c.drifted} drifted (${c.majorBehind} major)`;\n }\n const c = w.counts as { critical: number; high: number; moderate: number; low: number };\n return ` security: ${c.critical}C/${c.high}H/${c.moderate}M/${c.low}L`;\n });\n return `→ wrote to Websites[${summary.siteName}]:\\n${lines.join(\"\\n\")}`;\n}\n\n/** Listr renderer choice. `--json` → silent so stdout stays clean for piping.\n * Otherwise listr's `default` renderer auto-falls back to `simple` in\n * non-TTY contexts (CI, log capture, our own integration tests). */\nfunction rendererFor(json: boolean | undefined): Renderer {\n return json ? \"silent\" : \"default\";\n}\n\n/** When `--url` is set but audits other than lighthouse also ran (those use the\n * local checkout, not the deployed URL), return a one-line operator notice;\n * null when there's nothing to warn about. Keeps the mixed-provenance result\n * table from silently confusing the operator. */\nexport function deployedUrlNotice(\n which: AuditName[],\n url: string | undefined,\n cwd: string,\n): string | null {\n if (url === undefined) return null;\n const others = which.filter((n) => n !== \"lighthouse\");\n if (others.length === 0) return null;\n return `note: --url only affects lighthouse; ${others.join(\", \")} ran against the local checkout at ${cwd}`;\n}\n\n/** Audits that need no repo checkout — they probe an external resource. lighthouse hits the\n * live URL; domain probes DNS/TLS of the URL's host; browser drives the deployed URL;\n * netlify-deploy queries the Netlify API by site id; function-health fetches the deployed\n * `/health` endpoint. */\nconst CHECKOUT_FREE_AUDITS: ReadonlySet<AuditName> = new Set<AuditName>([\n \"lighthouse\",\n \"domain\",\n \"browser\",\n \"netlify-deploy\",\n \"function-health\",\n \"form-e2e\",\n]);\n\n/** Audits whose checkout-free run is keyed off `site.netlifyId` (the Netlify API) rather than\n * the deployed URL. Everything else in CHECKOUT_FREE_AUDITS reads `site.deployedUrl`. */\nconst NETLIFY_ID_AUDITS: ReadonlySet<AuditName> = new Set<AuditName>([\"netlify-deploy\"]);\n\n/** A fleet site needs a local checkout unless every requested audit is checkout-free AND the site\n * has the external resource each one runs against (`deployedUrl` for lighthouse/domain/browser,\n * `netlifyId` for netlify-deploy). */\nexport function auditNeedsCheckout(site: Site, which: AuditName[]): boolean {\n const deployedCapable = which.every((n) => {\n if (!CHECKOUT_FREE_AUDITS.has(n)) return false;\n return NETLIFY_ID_AUDITS.has(n) ? site.netlifyId !== undefined : site.deployedUrl !== undefined;\n });\n return !deployedCapable;\n}\n\n/** Apply a single-site `--url` to the resolved sites. Returns the input\n * untouched when no url is given; otherwise requires exactly one site and\n * stamps `deployedUrl` on it so the lighthouse audit takes its deployed path.\n * The `--url`+`--fleet` combination is rejected earlier in `runAuditCommand`;\n * this length guard also covers any future multi-site single-run resolver. */\nexport function applyDeployedUrl(sites: Site[], url: string | undefined): Site[] {\n if (url === undefined) return sites;\n if (sites.length !== 1) {\n throw Object.assign(\n new Error(`--url expects exactly one site, but ${sites.length} resolved.`),\n { exitCode: 2 },\n );\n }\n // Scheme-allowlist: the URL is handed straight to Chrome/lhci, so only\n // http(s) is safe (a file:///gopher:// value would be a local-file read /\n // SSRF). Same predicate the inventory paths use.\n if (!isHttpUrl(url)) {\n throw Object.assign(new Error(`--url must be an http(s) URL (got: ${JSON.stringify(url)})`), {\n exitCode: 2,\n });\n }\n return [{ ...sites[0]!, deployedUrl: url }];\n}\n\nexport async function runAuditCommand(\n site: string | undefined,\n opts: AuditCommandOptions,\n): Promise<{ output: string; code: number }> {\n const which = parseOnly(opts.only) ?? ALL_AUDIT_NAMES;\n const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();\n\n // A literal --write-back=<slug> is single-site (the slug names one row).\n // Boolean --write-back + --fleet is fine: each site's slug comes from the\n // inventory, so there's no cwd-derived-slug ambiguity.\n if (typeof opts.writeBack === \"string\" && opts.fleet !== undefined) {\n throw Object.assign(\n new Error(\n \"--write-back=<slug> is single-site; with --fleet each site's slug comes from the inventory. Use --write-back (no slug) + --fleet.\",\n ),\n { exitCode: 2 },\n );\n }\n\n if (opts.url !== undefined && opts.fleet !== undefined) {\n throw Object.assign(\n new Error(\n \"--url is single-site only and cannot be combined with --fleet. Audit a single site instead.\",\n ),\n { exitCode: 2 },\n );\n }\n\n let sites = await resolveSites({\n ...(site !== undefined ? { site } : {}),\n ...(opts.fleet !== undefined ? { fleet: opts.fleet } : {}),\n ...(opts.workdir !== undefined ? { workdir: opts.workdir } : {}),\n cwd,\n });\n\n sites = applyDeployedUrl(sites, opts.url);\n\n let skippedPrep: SkippedSite[] = [];\n if (opts.fleet) {\n const workdir = opts.workdir ?? fleetWorkdir();\n const prep = await prepareFleetSites(sites, {\n workdir,\n needsCheckout: (s) => auditNeedsCheckout(s, which),\n });\n sites = prep.prepared;\n skippedPrep = prep.skipped;\n }\n\n const results: AuditResult[] = [];\n const renderer = rendererFor(opts.json);\n await buildAuditTasks(sites, which, results, renderer, parseConcurrency(opts.concurrency)).run();\n\n let output = opts.json ? JSON.stringify(results, null, 2) : formatTable(results);\n\n // Surface any site that couldn't be prepared (no auditable target, clone\n // failure) — visibly, but without reding the run. One misconfigured inventory\n // row is an operator fix, not an outage; the other sites still audited and\n // wrote back. \"No silent caps\": a dropped site is never invisible.\n const skipNotice = formatSkippedNotice(skippedPrep);\n if (skipNotice && !opts.json) output += `\\n\\n${skipNotice}`;\n\n // \"Did every site actually get measured?\" — emitted for any fleet sweep that ran\n // `smoke`, independent of --write-back, because it is a statement about the\n // RUN, not about persistence. Deliberately not folded into FLEET_WRITE_SUMMARY:\n // an unmeasured site still writes (it just writes nothing new), so the write gate\n // counts it as a success. That gap is exactly what hid four nights of timeouts.\n if (opts.fleet !== undefined && which.includes(\"smoke\") && !opts.json) {\n const { formatUnmeasuredSmokeSummary } = await import(\"../../audits/smoke.js\");\n output += `\\n\\n${formatUnmeasuredSmokeSummary(results)}`;\n }\n\n // Did any site fail to write back to Airtable? The fleet writer collects\n // per-site failures instead of throwing, so without this the command would\n // exit 0 while rows silently failed to persist — automation keying on `$?`\n // would see a clean run. (The single-site writer throws on failure, so it's\n // already non-zero via the propagated error.)\n let writeBackFailed = false;\n if (opts.writeBack !== undefined) {\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n\n if (opts.fleet !== undefined) {\n const wb = await runFleetWriteBack({ results, which });\n if (wb.anyFailed) writeBackFailed = true;\n // Gate on !json: the write-summary is human text; appending it after the\n // results array would corrupt `--json` output (the other notices already\n // guard this way). The write itself still happens regardless of --json.\n if (!opts.json) output += `\\n\\n${wb.summary}`;\n } else {\n const { resolveSlugFromCwd } = await import(\"../../audits/lighthouse-airtable.js\");\n const { writeAuditsToAirtable } = await import(\"../../audits/write-audits-to-airtable.js\");\n const slug =\n typeof opts.writeBack === \"string\" && opts.writeBack.length > 0\n ? opts.writeBack\n : await resolveSlugFromCwd(cwd);\n let writeSummary: WriteSummary | null = null;\n await new Listr(\n [\n {\n title: `Write to Airtable[${slug}]`,\n task: async (_ctx, task) => {\n const base = openBase(readAirtableConfig());\n // #646 step 4: the row to write is found in Turso, which holds every\n // site — a `site_<ULID>` site has no Airtable record to find. The\n // Airtable write below stays as the shadow and skips non-`rec` ids.\n task.output = \"loading the fleet roster…\";\n const { readFleetRoster } = await import(\"../../fleet/roster.js\");\n const websites = await readFleetRoster();\n task.output = \"writing scores…\";\n writeSummary = await writeAuditsToAirtable({ base, websites, slug, results });\n // #539 Phase 5: the FLEET path has mirrored since Phase 3, this\n // single-site one never did — same write, same columns, reaching\n // Turso only via the hourly sync. The summary already carries the\n // exact FieldSet Airtable got, so no signature change is needed.\n if (writeSummary.siteId && writeSummary.fields) {\n const { makeSiteMirror } = await import(\"../../db/site-mirror.js\");\n await (await makeSiteMirror()).health(writeSummary.siteId, writeSummary.fields);\n }\n task.title = `Wrote to Websites[${writeSummary.siteName}] (${writeSummary.writes.length} audit type${writeSummary.writes.length === 1 ? \"\" : \"s\"})`;\n },\n },\n ],\n { renderer },\n ).run();\n if (writeSummary && !opts.json) output += `\\n\\n${formatWriteSummary(writeSummary)}`;\n }\n }\n\n const notice = deployedUrlNotice(which, opts.url, cwd);\n if (notice && !opts.json) output += `\\n\\n${notice}`;\n\n const code = Math.max(\n auditExitCode(results, opts.failOnViolations === true),\n writeBackFailed ? 1 : 0,\n );\n return { output, code };\n}\n\n/** The fleet `--write-back` step: write every site's audits back to its\n * Websites row, dual-write each just-written FieldSet into Turso (#539\n * Phase 3), and record fleet-activity events. Extracted from runAuditCommand\n * so the mirror WIRING itself is pinned by test — the mutation this seam\n * kills is dropping the `...(mirror ? { mirror } : {})` pass-through, which\n * would silently stop all five nightly sweeps from mirroring while every\n * sweep stayed green (adversarial review of #566, finding 6). Deps default\n * to the real fleet wiring; dynamic imports keep the no-write paths from\n * loading Airtable/db clients. */\nexport async function runFleetWriteBack(args: {\n results: AuditResult[];\n which: AuditName[];\n deps?: {\n openBase?: () => AirtableBase;\n /** #646 step 4: the rows results are matched against. Defaults to Turso\n * (`readFleetRoster`); tests inject. */\n roster?: FleetRoster;\n makeMirror?: () => Promise<HealthMirror | null>;\n recordEvents?: (events: FleetEvent[], now: Date) => Promise<void>;\n /** #612. `true` = post-freeze, where a mirror failure, a missed row or an\n * absent mirror are each fatal. Injected so both sides stay proven and the\n * freeze commit stays a one-line change. */\n strict?: boolean;\n };\n}): Promise<{ summary: string; anyFailed: boolean }> {\n const { results, which, deps = {} } = args;\n const { writeFleetAuditsToAirtable, formatFleetWriteSummary, fleetWriteFailed } =\n await import(\"../../audits/write-audits-to-airtable.js\");\n let base: AirtableBase;\n if (deps.openBase) {\n base = deps.openBase();\n } else {\n const { openBase, readAirtableConfig } = await import(\"../../reports/airtable/client.js\");\n base = openBase(readAirtableConfig());\n }\n // #646 step 4: match results against the TURSO roster. Every site is there,\n // including a `site_<ULID>` site with no Airtable record — matched against\n // Airtable it failed with \"No Websites row matched\". Airtable stays the\n // shadow: updateAuditFields skips a non-`rec` id and logs the skip.\n const roster =\n deps.roster ??\n (async () => {\n const { readFleetRoster } = await import(\"../../fleet/roster.js\");\n return readFleetRoster();\n });\n const websites = await roster();\n // Phase 3 dual-write (#539): mirror each site's written FieldSet into\n // site_health. Null when libSQL creds are absent — Airtable write-back\n // proceeds exactly as before.\n const makeMirror =\n deps.makeMirror ??\n (async () => {\n const { makeHealthMirrorBestEffort } = await import(\"../../audits/health-mirror.js\");\n return makeHealthMirrorBestEffort();\n });\n const mirror = await makeMirror();\n const fleetWrite = await writeFleetAuditsToAirtable({\n base,\n websites,\n results,\n ...(mirror ? { mirror } : {}),\n });\n // Record fleet-activity events (vuln_cleared / cert_renewed rode along on each\n // WriteSummary) plus a per-sweep rollup. Best-effort: a missing Turso cred no-ops.\n const sweep = which.includes(\"security\") ? \"security\" : \"lighthouse\";\n const now = new Date();\n const auditEvents = fleetWrite.written.flatMap((w) => w.events ?? []);\n await (deps.recordEvents ?? recordFleetEventsBestEffort)(\n [...auditEvents, fleetSweptEvent(sweep, fleetWrite.written.length, now.toISOString())],\n now,\n );\n // #612: post-freeze a mirror failure, a missed row, or an absent mirror all\n // become fatal — there is no hourly import left to converge them, so a sweep\n // that wrote nothing into the only store would otherwise finish green.\n return {\n summary: formatFleetWriteSummary(fleetWrite),\n anyFailed:\n deps.strict === undefined\n ? fleetWriteFailed(fleetWrite)\n : fleetWriteFailed(fleetWrite, deps.strict),\n };\n}\n"],"mappings":"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","names":["c"]}
|