@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/commands/launch.ts","../src/recipes/launch.ts"],"sourcesContent":["import { resolve } from \"node:path\";\nimport { launch, type LaunchResult, type LaunchStepResult } from \"../../recipes/launch.js\";\nimport { resolveSites } from \"../fleet/resolve-sites.js\";\n\nexport type LaunchCommandOptions = {\n cwd?: string;\n};\n\nexport function formatStep(name: string, r: LaunchStepResult): string {\n if (r.kind === \"error\") return `${name.padEnd(20)} error: ${r.message}`;\n if (r.kind === \"audit\") {\n const s = r.scores;\n return `${name.padEnd(20)} audited (P=${s.performance} A=${s.accessibility} BP=${s.bestPractices} SEO=${s.seo})`;\n }\n if (r.kind === \"draft\") {\n return `${name.padEnd(20)} drafted ${r.report.reportId}`;\n }\n if (r.kind === \"probe\") return `${name.padEnd(20)} ok — ${r.message}`;\n const rec = r.result;\n if (rec.status === \"noop\") return `${name.padEnd(20)} noop${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n if (rec.status === \"failed\")\n return `${name.padEnd(20)} failed${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n return `${name.padEnd(20)} applied (${rec.commits.length} commit${rec.commits.length === 1 ? \"\" : \"s\"})${rec.notes ? ` — ${rec.notes}` : \"\"}`;\n}\n\nfunction formatResult(r: LaunchResult): string {\n const header = `[${r.site}] launch — ${r.complete ? \"drafted (awaiting approval)\" : \"STOPPED\"}`;\n const body = r.steps.map((s) => formatStep(s.name, s.result)).join(\"\\n\");\n return `${header}\\n${body}`;\n}\n\n/**\n * `launch <site>` — single-site only. Bootstrap (Renovate + protection), first-audit\n * the site, and DRAFT its launch email into the M3 approve queue. Never sends;\n * the operator approves the draft and the next send run delivers the go-live\n * email (flipping Status → maintenance + stamping Launched at).\n */\nexport async function runLaunchCommand(\n site: string,\n opts: LaunchCommandOptions,\n): Promise<{ output: string; code: number }> {\n const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();\n const sites = await resolveSites({ site, cwd });\n const target = sites[0];\n if (!target) {\n return { output: `No site resolved for \"${site}\".`, code: 1 };\n }\n\n // #539 Phase 5: create-side Turso dual-write, wired at the composition root\n // (see runAnnounceCommand for why it is not defaulted inside the recipe).\n const { makeReportMirror } = await import(\"../../reports/report-mirror.js\");\n const { makeSiteMirror } = await import(\"../../db/site-mirror.js\");\n // #646 step 4: the fleet roster comes from Turso — an Airtable roster cannot\n // see a `site_<ULID>` site, so launching one failed at \"no site row matched\".\n const { readFleetRoster } = await import(\"../../fleet/roster.js\");\n const result = await launch(target, {\n roster: () => readFleetRoster(),\n reportMirror: await makeReportMirror(),\n siteMirror: await makeSiteMirror(),\n });\n return { output: formatResult(result), code: result.complete ? 0 : 1 };\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { AuditResult, RecipeResult, Site } from \"../types.js\";\nimport { siteLabel } from \"../util/site.js\";\nimport { selfUpdating } from \"./self-updating/index.js\";\nimport { HARNESS_JSON_RELATIVE, UNGUARDED_TWIN_TELL } from \"./match-harness/template.js\";\nimport { runAudits } from \"../audits/index.js\";\nimport { hasRealScores, lighthouseScoresFromResult } from \"../audits/lighthouse-airtable.js\";\nimport { writeAuditsToAirtable } from \"../audits/write-audits-to-airtable.js\";\nimport { openBase, readAirtableConfig } from \"../reports/airtable/client.js\";\nimport type { AirtableBase } from \"../reports/airtable/client.js\";\nimport { siteSlug } from \"../reports/airtable/websites.js\";\nimport type { WebsiteRow } from \"../reports/airtable/websites.js\";\nimport { updateReportScores } from \"../reports/airtable/reports.js\";\nimport { createReportDraft, findReportForPeriod } from \"../reports/create-report.js\";\nimport type { DraftInput } from \"../reports/draft-fields.js\";\nimport type { ReportRow } from \"../reports/airtable/reports.js\";\nimport type { ReportMirror } from \"../reports/report-mirror.js\";\nimport type { SiteMirror } from \"../db/site-mirror.js\";\nimport { queueDraft } from \"../reports/queue.js\";\nimport { uploadAttachment } from \"../reports/airtable/attachments.js\";\nimport { renderReportHtml } from \"../reports/render.js\";\nimport { resolveCopy } from \"../reports/copy.js\";\nimport type { LighthouseScores } from \"../reports/types.js\";\n\nexport type LaunchStepResult =\n | { kind: \"recipe\"; result: RecipeResult }\n | { kind: \"audit\"; results: AuditResult[]; scores: LighthouseScores }\n | { kind: \"draft\"; report: ReportRow }\n | { kind: \"probe\"; message: string }\n | { kind: \"error\"; message: string };\n\nexport type LaunchResult = {\n site: string;\n steps: Array<{ name: string; result: LaunchStepResult }>;\n /** True if every step ran (bootstrap + audit + draft); false if a step\n * errored or a recipe `failed` short-circuited the chain. */\n complete: boolean;\n};\n\nexport type LaunchDeps = {\n /** Bootstrap step (Renovate + protection; ci.yml comes from the starter). Defaults to the real `selfUpdating`. */\n bootstrap?: (site: Site) => Promise<RecipeResult>;\n /** Audit step. Defaults to the real `runAudits`. */\n audit?: (site: Site) => Promise<AuditResult[]>;\n /** Airtable handle. Defaults to opening the live base from credentials. */\n base?: AirtableBase;\n /** Every site in the fleet, read from TURSO (#646 step 4) — the Airtable\n * roster this replaces could not see a `site_<ULID>` site, so launching one\n * failed at \"no Websites row matched\". Required, not defaulted: the unit\n * suite calls `launch` with a fake base and must not open a real libSQL\n * handle. The CLI composition root wires `readFleetRoster`. */\n roster: () => Promise<WebsiteRow[]>;\n /** #539 Phase 5, and since #646 step 4 the store that MINTS and holds the\n * Launch row: the row itself (or a re-run's refreshed scores), the rendered\n * body, and the queue flag. Required for the same reason `roster` is. */\n reportMirror: ReportMirror;\n /** #539 Phase 5: the Websites-row twin — launch writes the site's FIRST audit\n * results and (on send) its launched status. Injected at the CLI root. */\n siteMirror?: SiteMirror;\n /** HTTP probe for `dev-guard`. Defaults to global fetch. INJECTED in tests:\n * the suite stubs `global.fetch` for the Airtable attachment upload and that\n * stub answers 200 to everything — which is precisely the state this step\n * exists to fail on. */\n probe?: (url: string) => Promise<{ status: number; body: string }>;\n};\n\n/** The site's OWN 404. `src/routes/+error.svelte` renders `<h1>{page.status}</h1>`,\n * so a real SvelteKit 404 from this app carries an `<h1>404</h1>`; a parked\n * domain, a CDN 404 and a dead host do not. \"The route is gone\" and \"the site\n * is gone\" must not look the same to this gate. */\nconst SITE_404_MARKER = /<h1[^>]*>\\s*404\\s*<\\/h1>/i;\n\n/** The UNGUARDED twin's OWN refusal. `src/routes/dev/match/[uid]/+page.server.ts`\n * 404s for any uid absent from its assembly map — and that message renders\n * through the very same `+error.svelte` the guard's 404 uses, so it carries an\n * `<h1>404</h1>` too. On a site whose uid set lacks \"home\", a LIVE twin and a\n * guarded one are byte-indistinguishable to SITE_404_MARKER, and the check\n * could never fail. Verified on disk: beachfront-dentistry's copy of that route\n * imports `$app/environment` zero times.\n *\n * The contract is the MACHINE tell `UNGUARDED_TWIN_TELL`, imported from the\n * template that installs the route rather than re-typed here: the installed\n * twin puts it ahead of its human-readable message. Matching the prose was\n * the defect of #719 — reword the message (\"no document for\", \"unknown uid\")\n * and the guard silently stopped denying, i.e. failed OPEN with fixtures\n * public. The two prose wordings are STILL accepted, on purpose and for now:\n * beachfront-dentistry's on-disk twin predates the tell and says \"no matching\n * assembly for\", and any site installed before the tell says \"no assembly\n * for\". Drop the prose alternates once beachfront's twin has been\n * re-installed from the harness; until then they only ever widen the deny.\n *\n * This is a DENY clause and only ever a deny — it can refuse a green, never\n * grant one. Widening it can only ever refuse a launch that would otherwise\n * have proceeded, so widening is always the safe direction here. */\nexport const UNGUARDED_TWIN_MARKER = new RegExp(\n `${UNGUARDED_TWIN_TELL.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")}|no (matching )?assembly for`,\n \"i\",\n);\n\nconst MATCH_ROUTE_DIR = \"src/routes/dev/match\";\n\n/** Positive evidence that `sitePath` is a SvelteKit checkout at all. Without it,\n * the absence of MATCH_ROUTE_DIR is evidence of nothing: a path never cloned, a\n * stale clone, a mistyped site name and the wrong working directory all produce\n * exactly the same `false`, and `resolveSites` builds the path with\n * `localPath(resolve(cwd, site))` and no existence check of its own. This is\n * the filesystem twin of the `/health` control on the deployed gate — an absent\n * error must never be allowed to grant a green. */\nconst CHECKOUT_MARKER = \"src/routes\";\n\n/** Tokens after which a `/` opens a REGEX literal rather than a division. This\n * is the usual JS ambiguity, answered by lookback at the last significant\n * character — a scanner's answer, not a parser's.\n *\n * `)` is deliberately NOT here. `if (x) /re/.test(y)` is legal and is read as\n * division, which costs nothing (the `/` is copied through unchanged, exactly\n * as before this function existed) and keeps `(a + b) / 2` right, which is far\n * commoner in that position. That is the residual: a regex literal opening\n * directly after `)` AND containing a quote falls back to the old behaviour and\n * therefore DENIES. Deny is the safe direction here — see `carriesGuard`. */\nconst REGEX_PRECEDERS = /[(,=:[!&|?{};+\\-*%~^<>]/;\nconst REGEX_KEYWORDS = new Set([\n \"return\",\n \"typeof\",\n \"case\",\n \"in\",\n \"of\",\n \"new\",\n \"delete\",\n \"void\",\n \"instanceof\",\n \"do\",\n \"else\",\n \"yield\",\n \"await\",\n]);\n\nfunction opensRegex(prevChar: string, prevWord: string): boolean {\n if (prevChar === \"\") return true;\n if (REGEX_PRECEDERS.test(prevChar)) return true;\n return REGEX_KEYWORDS.has(prevWord);\n}\n\n/** The last significant character emitted so far, and the identifier ending at\n * it. Both scanners look back over their own OUTPUT, which is why masking is\n * safe to interleave: masked bodies are spaces and the delimiters survive, so\n * the structural lookback reads the same either way. */\nfunction lookback(out: string): { char: string; word: string } {\n let i = out.length - 1;\n while (i >= 0 && /\\s/.test(out[i]!)) i--;\n if (i < 0) return { char: \"\", word: \"\" };\n let j = i;\n while (j >= 0 && /[A-Za-z0-9_$]/.test(out[j]!)) j--;\n return { char: out[i]!, word: out.slice(j + 1, i + 1) };\n}\n\n/** Index of a regex literal's CLOSING `/`, or -1 if `source[at]` does not in\n * fact open one. `[...]` character classes are tracked because a `/` inside one\n * does not close the literal (`/[a-z/]/`). A newline or an unterminated run\n * means this was never a regex, and the caller then treats the `/` as an\n * ordinary character — the behaviour that predates regex-awareness. */\nfunction regexLiteralEnd(source: string, at: number): number {\n let inClass = false;\n for (let i = at + 1; i < source.length; i++) {\n const c = source[i]!;\n if (c === \"\\\\\") {\n i++;\n continue;\n }\n if (c === \"\\n\") return -1;\n if (inClass) {\n if (c === \"]\") inClass = false;\n continue;\n }\n if (c === \"[\") {\n inClass = true;\n continue;\n }\n if (c === \"/\") return i;\n }\n return -1;\n}\n\n/** Strip line and block comments, so the COMMON commented-out guard cannot\n * satisfy the match — an import left in place above `// if (!dev) error(404);`\n * is the likeliest real-world state, and it used to pass. String and template\n * literals are skipped so a `//` inside a URL is not read as a comment.\n *\n * Regex literals are recognised (#726). Without that, a quote character inside\n * one — `/[a-z0-9']+/` — opened a string that never closed, and everything to\n * the next matching quote was preserved as if it were string content. On a\n * CORRECTLY guarded file that swallowed the live `if (!dev) error(404)` and the\n * twin read as unguarded, so a launch was blocked on a site that was fine. The\n * literal is now copied through verbatim, which is what a comment stripper\n * should do with one. */\nfunction stripComments(source: string): string {\n let out = \"\";\n for (let i = 0; i < source.length;) {\n const ch = source[i]!;\n const next = source[i + 1];\n if (ch === \"/\" && next === \"/\") {\n while (i < source.length && source[i] !== \"\\n\") i++;\n continue;\n }\n if (ch === \"/\" && next === \"*\") {\n i += 2;\n while (i < source.length && !(source[i] === \"*\" && source[i + 1] === \"/\")) i++;\n i += 2;\n continue;\n }\n if (ch === \"/\") {\n const { char, word } = lookback(out);\n if (opensRegex(char, word)) {\n const end = regexLiteralEnd(source, i);\n if (end !== -1) {\n out += source.slice(i, end + 1);\n i = end + 1;\n continue;\n }\n }\n }\n if (ch === '\"' || ch === \"'\" || ch === \"`\") {\n out += ch;\n i++;\n while (i < source.length) {\n if (source[i] === \"\\\\\") {\n out += source.slice(i, i + 2);\n i += 2;\n continue;\n }\n out += source[i]!;\n i++;\n if (source[i - 1] === ch) break;\n }\n continue;\n }\n out += ch;\n i++;\n }\n return out;\n}\n\n/** Blank the CONTENTS of string and template literals, keeping the quote\n * characters, the newlines and the overall length. Only the guard scan uses\n * this — never the `$app/environment` check, whose whole subject is an import\n * specifier and therefore is itself a string literal.\n *\n * It exists because `if (!dev) console.warn(\"this should throw error(404)\")`\n * read as a refusal. Masking can only ever REMOVE candidate refusal tokens, so\n * it can only ever deny.\n * Regex BODIES are masked too, for the same reason and with the same effect: a\n * regex is data, so `if (!dev) RE = /throw/` must not be read as a refusal. It\n * was, before #726. */\nfunction maskLiterals(code: string): string {\n let out = \"\";\n for (let i = 0; i < code.length;) {\n const ch = code[i]!;\n if (ch === \"/\") {\n const { char, word } = lookback(out);\n if (opensRegex(char, word)) {\n const end = regexLiteralEnd(code, i);\n if (end !== -1) {\n // Delimiters and length kept, body blanked — exactly the string rule.\n out += \"/\" + \" \".repeat(end - i - 1) + \"/\";\n i = end + 1;\n continue;\n }\n }\n }\n if (ch !== '\"' && ch !== \"'\" && ch !== \"`\") {\n out += ch;\n i++;\n continue;\n }\n out += ch;\n i++;\n while (i < code.length) {\n const c = code[i]!;\n if (c === \"\\\\\") {\n out += \" \";\n i += 2;\n continue;\n }\n i++;\n if (c === ch) {\n out += c;\n break;\n }\n out += c === \"\\n\" ? \"\\n\" : \" \";\n }\n }\n return out;\n}\n\n/** The statement or block the `if (...)` ending at `from` actually governs: a\n * braced block, or the single statement up to its terminating `;` — or, under\n * ASI, the end of its line. Bracket depth is tracked so\n * `error(404, {\\n message: \"…\"\\n});` is ONE statement rather than three\n * fragments. Expects `code` to be comment-stripped and literal-masked, so every\n * bracket it sees is a real one. */\nfunction consequentAt(code: string, from: number): string {\n let i = from;\n while (i < code.length && /\\s/.test(code[i]!)) i++;\n const start = i;\n if (code[i] === \"{\") {\n let depth = 0;\n for (; i < code.length; i++) {\n if (code[i] === \"{\") depth++;\n else if (code[i] === \"}\" && --depth === 0) return code.slice(start, i + 1);\n }\n return code.slice(start);\n }\n let depth = 0;\n for (; i < code.length; i++) {\n const ch = code[i]!;\n if (ch === \"(\" || ch === \"[\" || ch === \"{\") depth++;\n else if (ch === \")\" || ch === \"]\" || ch === \"}\") {\n if (depth === 0) return code.slice(start, i);\n depth--;\n } else if (depth === 0 && (ch === \";\" || ch === \"\\n\")) return code.slice(start, i);\n }\n return code.slice(start);\n}\n\n/** An actual refusal: SvelteKit's `error(404, …)`, or any `throw`. */\nconst REFUSAL = /\\berror\\s*\\(\\s*404|\\bthrow\\b/;\n\n/** A guard is `$app/environment` + an `if (!dev)` **whose own branch refuses**.\n *\n * The three conditions used to be ANDed across the WHOLE file, with nothing\n * tying the refusal to the `if`. Two shapes passed that should not have: a\n * warn-only `if (!dev)` alongside the route's own unrelated `throw` (the exact\n * half-fix this comment used to claim was closed), and a refusal occurring only\n * inside a string. Reading the guard's consequent, on literal-masked source, is\n * strictly narrower than the old whole-file grep — every input it rejects, the\n * old predicate also had to reject on `$app/environment` or `if (!dev)`, or was\n * a false pass. It can therefore only deny, never grant.\n *\n * It is a contract with plan BC's `ROUTE_SERVER` template, which BC installs\n * and this pre-flight must accept; tests/recipes/launch.test.ts lifts that\n * template out of the plan and runs this predicate over it rather than\n * restating the pattern. */\nfunction carriesGuard(source: string): boolean {\n const code = stripComments(source);\n if (!code.includes(\"$app/environment\")) return false;\n const masked = maskLiterals(code);\n const guards = /if\\s*\\(\\s*!\\s*dev\\s*\\)/g;\n for (let m = guards.exec(masked); m; m = guards.exec(masked)) {\n if (REFUSAL.test(consequentAt(masked, m.index + m[0].length))) return true;\n }\n return false;\n}\n\nconst DEV_ROUTE_DIR = \"src/routes/dev\";\n\n/** Every SvelteKit route file at or below `relDir`, site-root-relative. The\n * gates used to read a FIXED list of three paths, which is why a `+server.ts`\n * endpoint and a `/dev/match/frozen` sibling were inspected by neither of them\n * (#723). What exists under the twin is a question for the filesystem, not for\n * a constant. */\nasync function routeFilesUnder(sitePath: string, relDir: string): Promise<string[]> {\n let entries;\n try {\n entries = await readdir(join(sitePath, relDir), { withFileTypes: true });\n } catch {\n return [];\n }\n const out: string[] = [];\n for (const e of entries) {\n const rel = `${relDir}/${e.name}`;\n if (e.isDirectory()) out.push(...(await routeFilesUnder(sitePath, rel)));\n else if (e.name.startsWith(\"+\")) out.push(rel);\n }\n return out;\n}\n\n/** `carriesGuard` for one file, memoised — a layout is asked about once per page\n * directory below it. An unreadable file is not a guard. */\nasync function guardAt(\n sitePath: string,\n rel: string,\n cache: Map<string, boolean>,\n): Promise<boolean> {\n const hit = cache.get(rel);\n if (hit !== undefined) return hit;\n let ok: boolean;\n try {\n ok = carriesGuard(await readFile(join(sitePath, rel), \"utf-8\"));\n } catch {\n ok = false;\n }\n cache.set(rel, ok);\n return ok;\n}\n\n/** The nearest guarded `+layout.server.ts` at or above `dir`, up to and\n * including `src/routes/dev`, or null.\n *\n * `src/routes/dev/+layout.server.ts` is the STRONGEST placement and the\n * fleet-wide class-fix for \"dev routes ship in production\" (#717): one `load`\n * doing `if (!dev) error(404)` runs for every `/dev/*` page, `/dev/match/[uid]`\n * included. An earlier version read only inside `/dev/match` and so reported\n * such a site as \"the matching twin would ship\" — untrue, and it penalised the\n * better fix. It also contradicted the deployed half of this feature, which was\n * deliberately redesigned to survive that class-fix: see the `/health` comment\n * at the dev-guard step for why the 200 control moved off `/dev/a11y-fixtures`.\n *\n * Layouts cover PAGES only. A `+server.ts` endpoint runs no layout `load` at\n * all, which is why `matchingDisposition` checks endpoints separately and never\n * lets a layout vouch for one (#723). */\nasync function coveringLayout(\n sitePath: string,\n dir: string,\n cache: Map<string, boolean>,\n): Promise<string | null> {\n let d = dir;\n for (;;) {\n const rel = `${d}/+layout.server.ts`;\n if (await guardAt(sitePath, rel, cache)) return rel;\n if (d === DEV_ROUTE_DIR) return null;\n const up = d.slice(0, d.lastIndexOf(\"/\"));\n if (up.length < DEV_ROUTE_DIR.length) return null;\n d = up;\n }\n}\n\n/**\n * Pre-flight: a checkout that still carries the matching twin must carry the\n * dev guard the `match-harness` recipe installs with it. Nothing is deleted at\n * launch — the harness stays for the next round — so the guard is the whole\n * mechanism keeping `/dev/match/*` off the production build.\n *\n * Filesystem only, and it runs BEFORE any GitHub write: a site that will fail\n * the deployed check should not first acquire branch protection and an audit.\n *\n * Liveness first. `src/routes` has to be there before a missing\n * `src/routes/dev/match` may be read as \"this site has no twin\" — otherwise the\n * pass is granted by an absence, and four separate operator errors produce that\n * same absence. See CHECKOUT_MARKER.\n */\nexport async function matchingDisposition(\n sitePath: string,\n): Promise<{ ok: boolean; message: string }> {\n if (!existsSync(join(sitePath, CHECKOUT_MARKER))) {\n return {\n ok: false,\n message: `${sitePath} has no ${CHECKOUT_MARKER} — this is not a SvelteKit checkout (never cloned, a stale clone, a mistyped site name, or the wrong working directory), so the twin's disposition cannot be established`,\n };\n }\n if (!existsSync(join(sitePath, MATCH_ROUTE_DIR))) {\n return {\n ok: true,\n message: `no ${MATCH_ROUTE_DIR} in this checkout (${CHECKOUT_MARKER} is present, so the absence is a real one)`,\n };\n }\n const files = await routeFilesUnder(sitePath, MATCH_ROUTE_DIR);\n if (files.length === 0) {\n return {\n ok: false,\n message: `${MATCH_ROUTE_DIR} exists but carries no route files (+page/+server/+layout) — the twin's disposition cannot be established`,\n };\n }\n\n const cache = new Map<string, boolean>();\n const unguarded: string[] = [];\n const covers: string[] = [];\n\n // Endpoints FIRST, and never covered by a layout: SvelteKit layout `load` does\n // not run for `+server.ts`, so the #717 class-fix at src/routes/dev that\n // legitimately covers every page leaves an endpoint fully live (#723).\n for (const ep of files.filter((f) => f.endsWith(\"/+server.ts\"))) {\n if (await guardAt(sitePath, ep, cache)) covers.push(ep);\n else\n unguarded.push(\n `${ep} (a \\`+server.ts\\` endpoint — layout \\`load\\` does not run for it, so it needs its own guard)`,\n );\n }\n\n // Then every directory holding a page, each covered by its own guarded\n // +page.server.ts or by a +layout.server.ts at or above it.\n const pageDirs = [\n ...new Set(\n files\n .filter((f) => /\\/\\+page\\.(server\\.ts|ts|svelte)$/.test(f))\n .map((f) => f.slice(0, f.lastIndexOf(\"/\"))),\n ),\n ];\n for (const dir of pageDirs) {\n const layout = await coveringLayout(sitePath, dir, cache);\n if (layout) {\n covers.push(layout);\n continue;\n }\n const own = `${dir}/+page.server.ts`;\n if (await guardAt(sitePath, own, cache)) {\n covers.push(own);\n continue;\n }\n unguarded.push(`${dir}/ (no guarded +page.server.ts and no covering +layout.server.ts)`);\n }\n\n if (unguarded.length > 0) {\n return {\n ok: false,\n message: `${MATCH_ROUTE_DIR} is not fully guarded — ${unguarded.join(\"; \")} — an \\`if (!dev)\\` guard on \\`$app/environment\\` whose own branch refuses (\\`error(404\\` or \\`throw\\`) is required, once comments are stripped and string and regex contents masked; the matching twin would ship`,\n };\n }\n // Deliberately NOT \"the twin is guarded in production\". This function reads\n // source text on disk; it cannot observe the deployed build, and a field that\n // can only see configuration must not be named after the thing it cannot see.\n // The deployed behaviour is the `dev-guard` step's job.\n return {\n ok: true,\n message: `every route under ${MATCH_ROUTE_DIR} is covered by dev guard source — ${[...new Set(covers)].join(\", \")} (source text only; the deployed build is checked at the dev-guard step)`,\n };\n}\n\n/** The uids whose `/dev/match` twin must be proved dead, read from the harness's\n * OWN page table — the file `match-harness` installs and every gate already\n * reads — so the gate and the harness cannot disagree about which pages exist.\n * Probing a hardcoded \"home\" left every sibling uid unprobed (#723).\n *\n * A page whose `uid` is null has no twin (harness.mjs says so) and is skipped.\n * An absent or unparseable harness.json falls back to \"home\": not every\n * launched site carries the harness, so a missing file must not become a\n * refusal — and must not yield an EMPTY list either, which would prove nothing\n * while looking like a pass. */\nasync function twinUids(sitePath: string): Promise<string[]> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(await readFile(join(sitePath, HARNESS_JSON_RELATIVE), \"utf-8\"));\n } catch {\n return [\"home\"];\n }\n const pages = (parsed as { pages?: Record<string, { uid?: unknown } | null> })?.pages;\n if (!pages || typeof pages !== \"object\") return [\"home\"];\n const uids = Object.values(pages)\n .map((p) => p?.uid)\n .filter((u): u is string => typeof u === \"string\" && u.length > 0);\n return uids.length > 0 ? [...new Set(uids)] : [\"home\"];\n}\n\n/** Undici's defaults are 300s headers + 300s body, and the body timeout is an\n * INACTIVITY timer — so a trickling origin can hold this open for ~10 minutes,\n * AFTER the GitHub writes and a full Lighthouse audit have already run.\n *\n * 15s is NOT the house number, and this comment used to imply it was by citing\n * `audits/function-health.ts`, which is 10s. The 15s call sites are\n * `audits/netlify-deploy.ts`, `github/gh-rest.ts`, `prismic/models/remote.ts`\n * and the inline ones in `prospect/pipeline.ts` and `prospect/http-probes.ts`;\n * `audits/function-health.ts`, `audits/form-e2e.ts`, `audits/browser.ts` and\n * `prospect/dns.ts` are 10s, `prospect/ownership.ts` 12s, `prospect/crawl.ts`\n * 20s. Each is set for its own workload — see the note on PRISMIC_TIMEOUT_MS.\n * 15s here is a bound on a two-request pre-send check, not a convention.\n *\n * An abort rejects, and dev-guard treats a rejection as a refusal, which is the\n * correct reading: an origin that will not answer has proved nothing. */\nconst PROBE_TIMEOUT_MS = 15_000;\n\nconst defaultProbe = async (url: string): Promise<{ status: number; body: string }> => {\n const res = await fetch(url, {\n redirect: \"follow\",\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n return { status: res.status, body: await res.text() };\n};\n\n/**\n * Launch a site: bootstrap → first-audit → DRAFT a launch email. The M3\n * approve loop is what actually sends; `launch` never sends — it stops at a\n * dashboard-queued draft (`reportType: \"Launch\"`) carrying the just-audited\n * Lighthouse scores, so `sendOne`'s `report.lighthouse` guard passes.\n *\n * EXECUTION order, stopping on the first error or `failed` recipe:\n * 0. matchingDisposition — filesystem pre-flight, BEFORE any GitHub write.\n * 1. selfUpdating — Renovate + protection (platform auto-merge OFF; ci.yml is the starter's).\n * 2. audit — the Lighthouse scores that feed the draft are collected here.\n * 3. the Websites-row lookup, which supplies the url the next step probes.\n * 3b. dev-guard — the same twin, checked against the DEPLOYED url. It sits\n * BETWEEN collecting the scores and writing them, so a site that fails it\n * has been audited but leaves its Websites row untouched.\n * 4. writeAuditsToAirtable — the `audit --write-back` writer.\n * 5. createDraft — reportType \"Launch\", today's period, the audited scores.\n *\n * The REPORTED chain is deliberately not that order. The `audit` step is only\n * pushed once its write succeeds (:261), so the emitted steps read\n * `matching-disposition -> self-updating -> dev-guard -> audit -> draft`, which\n * is what the CLI prints and what tests/recipes/launch.test.ts asserts. Do not\n * renumber either list to match the other: one says when work happens, the\n * other says what has been proved. Only step 0 precedes a GitHub write —\n * dev-guard cannot, because it needs the row from step 3 to know the url.\n */\nexport async function launch(site: Site, deps: LaunchDeps): Promise<LaunchResult> {\n const label = siteLabel(site);\n const bootstrap = deps.bootstrap ?? selfUpdating;\n const audit = deps.audit ?? runAudits;\n const base = deps.base ?? openBase(readAirtableConfig());\n const probe = deps.probe ?? defaultProbe;\n\n const steps: Array<{ name: string; result: LaunchStepResult }> = [];\n const stop = (): LaunchResult => ({ site: label, steps, complete: false });\n\n // 0. Matching disposition — filesystem, before any GitHub write.\n const disposition = await matchingDisposition(site.path);\n steps.push({\n name: \"matching-disposition\",\n result: disposition.ok\n ? { kind: \"probe\", message: disposition.message }\n : { kind: \"error\", message: disposition.message },\n });\n if (!disposition.ok) return stop();\n\n // 1. Bootstrap.\n let recipe: RecipeResult;\n try {\n recipe = await bootstrap(site);\n } catch (err) {\n steps.push({ name: \"self-updating\", result: errorOf(err) });\n return stop();\n }\n steps.push({ name: \"self-updating\", result: { kind: \"recipe\", result: recipe } });\n if (recipe.status === \"failed\") return stop();\n\n // 2. Audit + write scores back to Airtable.\n let results: AuditResult[];\n try {\n results = await audit(site);\n } catch (err) {\n steps.push({ name: \"audit\", result: errorOf(err) });\n return stop();\n }\n const lhResult = results.find((r) => r.audit === \"lighthouse\");\n if (!lhResult || !hasRealScores(lhResult)) {\n steps.push({\n name: \"audit\",\n result: { kind: \"error\", message: \"lighthouse audit produced no real scores\" },\n });\n return stop();\n }\n // The launch announcement renders a numeric score per category; a metric that\n // errored this run (now null from lighthouseScoresFromResult) keeps the prior\n // 0 behavior here rather than propagating null into the launch-email path. The\n // Airtable write path (write-audits-to-airtable) keeps the null → shows \"—\".\n const rawScores = lighthouseScoresFromResult(lhResult);\n const scores: LighthouseScores = {\n performance: rawScores.performance ?? 0,\n accessibility: rawScores.accessibility ?? 0,\n bestPractices: rawScores.bestPractices ?? 0,\n seo: rawScores.seo ?? 0,\n };\n\n const websites = await deps.roster();\n const target = websites.find((w) => siteSlug(w.name) === siteSlug(label));\n if (!target) {\n steps.push({\n name: \"audit\",\n result: { kind: \"error\", message: `no site row matched site \"${label}\"` },\n });\n return stop();\n }\n\n // 2b. The dev guard, on the DEPLOYED build. Two halves, both required:\n // /dev/match/home must 404 WITH this site's own error page, and /health\n // must answer 200. Without the second, a dead host, a wrong url and a\n // parked domain all \"pass\" the first — an absent error granting a green,\n // which is the one shape this fleet does not allow. Reading the deployed\n // url makes this a production-build check by construction; no local build\n // can substitute.\n //\n // The control is /health, NOT a /dev/* route. It used to be\n // /dev/a11y-fixtures, which made this gate require an unguarded dev page\n // to be publicly reachable in production — so the obvious class-fix for\n // \"dev routes ship in production\" (one src/routes/dev/+layout.server.ts\n // doing `if (!dev) error(404)`) would have taken the control down with it\n // and failed every correctly-guarded site. /health is a real production\n // endpoint shipped by the starter and present in all 23 starter-derived\n // repos, each declaring `prerender = false` — so a 200 from it is still a\n // live render/function path, not a static file served by the CDN.\n const origin = target.url.replace(/\\/+$/, \"\");\n const uids = await twinUids(site.path);\n const twins: Array<{ uid: string; res: { status: number; body: string } }> = [];\n let control: { status: number; body: string };\n try {\n for (const uid of uids) twins.push({ uid, res: await probe(`${origin}/dev/match/${uid}`) });\n control = await probe(`${origin}/health`);\n } catch (err) {\n steps.push({ name: \"dev-guard\", result: errorOf(err) });\n return stop();\n }\n if (control.status !== 200) {\n steps.push({\n name: \"dev-guard\",\n result: {\n kind: \"error\",\n message: `${origin}/health answered ${control.status}, not 200 — the twin's 404 proves nothing (host down, wrong url, or the site's /health function is not deploying, in which case the function-health audit is failing too)`,\n },\n });\n return stop();\n }\n for (const { uid, res } of twins) {\n const unguardedTell = res.body.match(UNGUARDED_TWIN_MARKER);\n if (unguardedTell) {\n steps.push({\n name: \"dev-guard\",\n result: {\n kind: \"error\",\n message: `${origin}/dev/match/${uid} answered ${res.status} carrying the twin route's OWN \"${unguardedTell[0]}\" message — the twin is LIVE and merely has no assembly for the uid \"${uid}\". That is not evidence of a dev guard.`,\n },\n });\n return stop();\n }\n if (res.status !== 404 || !SITE_404_MARKER.test(res.body)) {\n steps.push({\n name: \"dev-guard\",\n result: {\n kind: \"error\",\n message:\n res.status === 404\n ? `${origin}/dev/match/${uid} 404s, but not with this site's own error page — cannot tell the guard from a dead route`\n : `${origin}/dev/match/${uid} answered ${res.status} — the matching twin is live in production`,\n },\n });\n return stop();\n }\n }\n steps.push({\n name: \"dev-guard\",\n result: {\n kind: \"probe\",\n message: `${uids.map((u) => `${origin}/dev/match/${u}`).join(\", \")} 404 (site error page), /health 200`,\n },\n });\n\n try {\n const auditWrite = await writeAuditsToAirtable({\n base,\n websites,\n slug: siteSlug(target.name),\n results,\n });\n // #539 Phase 5: mirror the first-audit write-back. Launch is the ONE path\n // that writes a brand-new site's health, so without this its row reads empty\n // in the console until the next hourly sync.\n if (auditWrite.siteId && auditWrite.fields) {\n await deps.siteMirror?.health(auditWrite.siteId, auditWrite.fields);\n }\n } catch (err) {\n steps.push({ name: \"audit\", result: errorOf(err) });\n return stop();\n }\n steps.push({ name: \"audit\", result: { kind: \"audit\", results, scores } });\n\n // 3. Draft the launch email (reuses draft.ts's reportId/period scheme). DRAFTS\n // ONLY — the M3 approve loop sends it and flips Status on send.\n const today = new Date();\n const period = today.toISOString().slice(0, 7);\n const slug = siteSlug(target.name);\n\n let report: ReportRow;\n try {\n // Re-run dedupe: reuse an existing Launch row for this (site, period) instead\n // of stacking a second draft. findReportByPeriod is the same idempotency\n // lookup draft.ts documents (dashboard/digest point lookup).\n const existing = await findReportForPeriod(deps.reportMirror, target.id, \"Launch\", period);\n if (existing) {\n // Reuse path: the row was created on a prior run with THAT run's scores. This\n // re-run just produced fresh audit scores AND will re-render the preview from\n // them — so refresh the row's Lighthouse cells (+ Completed on) to match,\n // otherwise the sent email (which reads the row) ships stale scores. The\n // create path already writes fresh scores via createDraft.\n await updateReportScores(base, existing.id, scores, today);\n // Mirror the same refresh — see the announce reuse path for why.\n await deps.reportMirror.patch(existing.id, {\n lighthouse_performance: scores.performance,\n lighthouse_accessibility: scores.accessibility,\n lighthouse_best_practices: scores.bestPractices,\n lighthouse_seo: scores.seo,\n completed_on: today.toISOString().slice(0, 10),\n });\n report = existing;\n } else {\n // #646 step 4: minted and written in Turso (`report_<ULID>`). The Airtable\n // Reports row this replaces is skipped, not written — see `createReportDraft`.\n report = await createReportDraft(draftInputFor(target, scores, today, period), {\n create: deps.reportMirror.create,\n });\n }\n } catch (err) {\n steps.push({ name: \"draft\", result: errorOf(err) });\n return stop();\n }\n\n // Mirror draft.ts:135-154 — render → upload \"Rendered HTML\" preview → flip\n // Draft ready. Without setDraftReady the draft never enters the approve queue\n // (every pending-approval gate requires draftReady true), so it can never be\n // approved or sent. The upload is a review convenience; the ready flag is the\n // critical step.\n try {\n const { html } = await renderReportHtml({\n siteName: target.name,\n siteUrl: target.url,\n reportType: \"Launch\",\n completedOn: today,\n lighthouse: scores,\n lastTestedDate: null,\n commentary: null,\n copy: resolveCopy(target),\n headerImageCid: `${slug}-header`,\n });\n // A preview-upload hiccup must NOT fail the launch — log and continue.\n try {\n await uploadAttachment(\n report.id,\n \"Rendered HTML\",\n html,\n `${slug}-${today.toISOString().slice(0, 10)}.html`,\n \"text/html\",\n );\n // The console preview reads the body from Turso, not the attachment.\n await deps.reportMirror.body(report.id, html);\n } catch (uploadErr) {\n console.warn(\n `⚠ Launch preview upload skipped for ${target.name}: ${\n uploadErr instanceof Error ? uploadErr.message : String(uploadErr)\n }`,\n );\n }\n // Critical: NOT wrapped — a failure here must surface as a failed launch. queueDraft\n // supersedes any lower-tier (Maintenance/Testing) drafts queued for this site; a Launch is\n // top tier so it only stands down if another Launch/Announcement is already queued.\n await queueDraft(\n base,\n { id: report.id, siteId: target.id, reportType: \"Launch\" },\n deps.reportMirror,\n );\n } catch (err) {\n steps.push({ name: \"draft\", result: errorOf(err) });\n return stop();\n }\n\n steps.push({ name: \"draft\", result: { kind: \"draft\", report } });\n\n return { site: label, steps, complete: true };\n}\n\n/** Build the Launch `DraftInput`. reportId/period mirror `draftReportForSite`\n * (draft.ts) — do not invent a new id scheme. `today`/`period` are threaded in\n * from `launch()` so a single timestamp drives the render, the draft, the\n * dedupe lookup, and the preview filename. Launch reports have no period window\n * and no prior maintenance test, so periodStart/periodEnd/completedOn all\n * collapse to \"today\" and `lastTestedDate` is null. */\nfunction draftInputFor(\n target: WebsiteRow,\n scores: LighthouseScores,\n today: Date,\n period: string,\n): DraftInput {\n const reportType = \"Launch\" as const;\n const reportId = `${target.name} — ${reportType} — ${today.toISOString().slice(0, 10)}`;\n return {\n reportId,\n siteId: target.id,\n reportType,\n period,\n periodStart: today,\n periodEnd: today,\n completedOn: today,\n lighthouse: scores,\n lastTestedDate: null,\n };\n}\n\nfunction errorOf(err: unknown): LaunchStepResult {\n return { kind: \"error\", message: err instanceof Error ? err.message : String(err) };\n}\n"],"mappings":"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","names":["depth"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
lighthouseAudit
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BIYQN7FH.js";
|
|
4
4
|
import "./chunk-PTDMVGKL.js";
|
|
5
5
|
import "./chunk-LBYOLBW7.js";
|
|
6
6
|
import "./chunk-XXTZBPUY.js";
|
|
@@ -8,4 +8,4 @@ import "./chunk-NMAWXBJA.js";
|
|
|
8
8
|
export {
|
|
9
9
|
lighthouseAudit
|
|
10
10
|
};
|
|
11
|
-
//# sourceMappingURL=lighthouse-
|
|
11
|
+
//# sourceMappingURL=lighthouse-52WSPZ5D.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hasRealScores,
|
|
3
|
+
lighthouseScoresFromResult,
|
|
4
|
+
resolveSlugFromCwd
|
|
5
|
+
} from "./chunk-X56LAKPR.js";
|
|
6
|
+
import "./chunk-TC4GANMX.js";
|
|
7
|
+
import "./chunk-ICSRWW5Q.js";
|
|
8
|
+
import "./chunk-RQJFGK3A.js";
|
|
9
|
+
export {
|
|
10
|
+
hasRealScores,
|
|
11
|
+
lighthouseScoresFromResult,
|
|
12
|
+
resolveSlugFromCwd
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=lighthouse-airtable-M45OPVRV.js.map
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLAUDE_MD_BLOCK,
|
|
3
|
+
CLAUDE_MD_MARKER,
|
|
4
|
+
GITIGNORE_BLOCK,
|
|
5
|
+
GITIGNORE_MARKER,
|
|
6
|
+
HARNESS_JSON_RELATIVE,
|
|
7
|
+
MATCH_HARNESS_COUPLED,
|
|
8
|
+
MATCH_HARNESS_FILES,
|
|
9
|
+
MATCH_HARNESS_PREVIOUS,
|
|
10
|
+
PRETTIERIGNORE_BLOCK,
|
|
11
|
+
PRETTIERIGNORE_MARKER
|
|
12
|
+
} from "./chunk-Y6FCXRIC.js";
|
|
13
|
+
import {
|
|
14
|
+
runRecipeOverSites
|
|
15
|
+
} from "./chunk-VPJ7N6P7.js";
|
|
16
|
+
import {
|
|
17
|
+
appendSkipNotice,
|
|
18
|
+
fleetWorkdir,
|
|
19
|
+
prepareFleetSites
|
|
20
|
+
} from "./chunk-DYTYPKFN.js";
|
|
21
|
+
import {
|
|
22
|
+
resolveSites
|
|
23
|
+
} from "./chunk-FP6HOW6J.js";
|
|
24
|
+
import {
|
|
25
|
+
PRETTIER_FLAG_NOTE,
|
|
26
|
+
formatWithPrettier,
|
|
27
|
+
resolveTargetPrettier
|
|
28
|
+
} from "./chunk-5CJVUPJU.js";
|
|
29
|
+
import {
|
|
30
|
+
withRecipe
|
|
31
|
+
} from "./chunk-LUF52EIY.js";
|
|
32
|
+
import "./chunk-Z3JXLULD.js";
|
|
33
|
+
import {
|
|
34
|
+
defaultSpawn
|
|
35
|
+
} from "./chunk-LBYOLBW7.js";
|
|
36
|
+
import "./chunk-RARREDQE.js";
|
|
37
|
+
import "./chunk-XXTZBPUY.js";
|
|
38
|
+
import {
|
|
39
|
+
ignoreRulesFor,
|
|
40
|
+
pathsMissingFromHead
|
|
41
|
+
} from "./chunk-WMF5S2MB.js";
|
|
42
|
+
|
|
43
|
+
// src/cli/commands/match-harness.ts
|
|
44
|
+
import { resolve } from "path";
|
|
45
|
+
|
|
46
|
+
// src/recipes/match-harness/index.ts
|
|
47
|
+
import { mkdir, readFile, rm, rmdir, writeFile } from "fs/promises";
|
|
48
|
+
import { dirname, join } from "path";
|
|
49
|
+
|
|
50
|
+
// src/recipes/match-harness/previous.ts
|
|
51
|
+
var CLAUDE_MD_BLOCK_0_95_1 = `
|
|
52
|
+
The \`matching-a-page\` skill governs a live-reference rebuild. These five rules
|
|
53
|
+
exist because the skill alone did not hold on the project this harness came
|
|
54
|
+
from \u2014 each one is a drift that actually happened, with the mechanical check
|
|
55
|
+
that now catches it. \`matching/harness.json\` is this site's configuration;
|
|
56
|
+
\`matching/LEDGER.md\` is the dated record of every deviation, floor and mask.
|
|
57
|
+
|
|
58
|
+
### 1. Source prescribes, rects only verify
|
|
59
|
+
|
|
60
|
+
Every geometry fix must cite the rule it came from: a line in the captured
|
|
61
|
+
reference stylesheet under \`matching/spec/\`, or the reference's HTML. "The probe
|
|
62
|
+
says the gap is 40px" is not a source. If you cannot name the line you are
|
|
63
|
+
guessing \u2014 go read the stylesheet first.
|
|
64
|
+
|
|
65
|
+
**Check:** the commit body must name the file:line for each fix.
|
|
66
|
+
**Operator's challenge:** _"which line of the reference stylesheet says that?"_
|
|
67
|
+
|
|
68
|
+
### 2. Phase 1 before Phase 4
|
|
69
|
+
|
|
70
|
+
A page gets its section census and per-section spec in \`matching/SPEC.md\` BEFORE
|
|
71
|
+
its geometry is touched. No SPEC section, no geometry round. The census is the
|
|
72
|
+
coverage denominator; skipping it is how a reference's root-font ladder and its
|
|
73
|
+
per-component height ladders get discovered reactively, after the region has
|
|
74
|
+
already failed several rounds.
|
|
75
|
+
|
|
76
|
+
**Check:** \`matching/gate.sh\` refuses to run a page with no \`SPEC.md\` section.
|
|
77
|
+
**Operator's challenge:** _"show me the SPEC section for that region."_
|
|
78
|
+
|
|
79
|
+
### 3. Three strikes, then stop
|
|
80
|
+
|
|
81
|
+
A failing region that has not improved across 3+ gate runs does not get a fourth
|
|
82
|
+
attempt. Present the attempts. Never widen the threshold, add a mask, or
|
|
83
|
+
reclassify it as a floor to make it go away.
|
|
84
|
+
|
|
85
|
+
**Check:** \`node matching/strikes.mjs <page>\` \u2014 exits 1 while any region is
|
|
86
|
+
stalled, and is the first thing a geometry round runs.
|
|
87
|
+
**Operator's challenge:** _"how many runs has that region been flat?"_
|
|
88
|
+
|
|
89
|
+
### 4. A gate closes an item, nothing else
|
|
90
|
+
|
|
91
|
+
No fix is "done" because the code changed. Paste the gate header \u2014 it is
|
|
92
|
+
self-describing, so a nonstandard threshold or an undisclosed mask is visible.
|
|
93
|
+
The threshold and the matrix are whatever \`matching/harness.json\` says, on every
|
|
94
|
+
page, never a subset.
|
|
95
|
+
|
|
96
|
+
**Operator's challenge:** _"paste the gate header."_
|
|
97
|
+
|
|
98
|
+
### 5. A commit is a checkpoint, not a stopping point
|
|
99
|
+
|
|
100
|
+
Do not hand control back between rounds. After committing, run
|
|
101
|
+
\`node matching/next.mjs\`: while it exits 1 there is a named next action, and the
|
|
102
|
+
round continues. Report when the backlog is empty, when a decision is genuinely
|
|
103
|
+
the operator's (three strikes, a novel floor, a threshold change), or when
|
|
104
|
+
asked \u2014 not because a commit felt like a natural place to summarise.
|
|
105
|
+
|
|
106
|
+
This exists because the failure was habitual, not deliberate: a turn ends when
|
|
107
|
+
user-facing prose gets written, and "I just committed something good" is the
|
|
108
|
+
moment that invites writing it. The gate stops incorrect work; this stops
|
|
109
|
+
premature stopping.
|
|
110
|
+
|
|
111
|
+
**Pausing:** create \`matching/PAUSED\` holding a note that says what the pause
|
|
112
|
+
means; \`next.mjs\` and \`strikes.mjs\` then exit 0 while it exists. This rule says
|
|
113
|
+
"do not stop while work is named"; it never said "find work to name". An empty
|
|
114
|
+
agenda and a paused one end the round the same way. Deleting the switch is the
|
|
115
|
+
operator's call.
|
|
116
|
+
|
|
117
|
+
**Check:** \`node matching/next.mjs\` \u2014 exits 1 while any non-floor region fails.
|
|
118
|
+
**Operator's challenge:** _"what does next.mjs say?"_
|
|
119
|
+
|
|
120
|
+
### Round protocol
|
|
121
|
+
|
|
122
|
+
1. \`node matching/strikes.mjs <page>\` \u2014 if it exits 1, the stalled regions are
|
|
123
|
+
the agenda, and stalled ones get escalated rather than re-attempted.
|
|
124
|
+
2. Confirm the page has a \`matching/SPEC.md\` section; write
|
|
125
|
+
\`matching/spec-sections/<page>.md\` and run \`node matching/build-spec.mjs\` if
|
|
126
|
+
not.
|
|
127
|
+
3. Fix, each change citing its source line.
|
|
128
|
+
4. \`bash matching/gate.sh <tag> <page>\` \u2014 paste the header.
|
|
129
|
+
5. Append to \`matching/LEDGER.md\` at the moment a deviation, floor or mask is
|
|
130
|
+
decided, not reconstructed at the end.
|
|
131
|
+
6. \`pnpm verify\`, then commit and push.
|
|
132
|
+
`;
|
|
133
|
+
var MATCH_HARNESS_BLOCK_PREVIOUS = {
|
|
134
|
+
"CLAUDE.md": [CLAUDE_MD_BLOCK_0_95_1]
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/recipes/match-harness/index.ts
|
|
138
|
+
var PRETTIER_TIMEOUT_MS = 6e4;
|
|
139
|
+
async function readIfExists(path) {
|
|
140
|
+
try {
|
|
141
|
+
return await readFile(path, "utf-8");
|
|
142
|
+
} catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function normalize(s) {
|
|
147
|
+
return s.replace(/\r\n/g, "\n").replace(/[ \t]+$/gm, "").trim();
|
|
148
|
+
}
|
|
149
|
+
function planBlockWrite(existing, marker, endMarker, block, previous) {
|
|
150
|
+
const region = `${marker}
|
|
151
|
+
${block}
|
|
152
|
+
${endMarker}`;
|
|
153
|
+
if (existing === null) return { action: "write", content: `${region}
|
|
154
|
+
` };
|
|
155
|
+
const start = existing.indexOf(marker);
|
|
156
|
+
if (start === -1) {
|
|
157
|
+
const base = existing.endsWith("\n") ? existing : `${existing}
|
|
158
|
+
`;
|
|
159
|
+
return { action: "write", content: `${base}
|
|
160
|
+
${region}
|
|
161
|
+
` };
|
|
162
|
+
}
|
|
163
|
+
const head = existing.slice(0, start);
|
|
164
|
+
const bodyStart = start + marker.length + 1;
|
|
165
|
+
const endAt = existing.indexOf(endMarker, bodyStart);
|
|
166
|
+
if (endAt !== -1) {
|
|
167
|
+
const body = existing.slice(bodyStart, endAt);
|
|
168
|
+
if (normalize(body) === normalize(block)) return { action: "skip" };
|
|
169
|
+
if (previous.some((p) => normalize(body) === normalize(p)))
|
|
170
|
+
return {
|
|
171
|
+
action: "replace",
|
|
172
|
+
content: head + region + existing.slice(endAt + endMarker.length)
|
|
173
|
+
};
|
|
174
|
+
return { action: "flag" };
|
|
175
|
+
}
|
|
176
|
+
for (const candidate of [block, ...previous]) {
|
|
177
|
+
if (!existing.startsWith(candidate, bodyStart)) continue;
|
|
178
|
+
const content = `${head}${region}
|
|
179
|
+
${existing.slice(bodyStart + candidate.length)}`;
|
|
180
|
+
return candidate === block ? { action: "terminate", content } : { action: "replace", content };
|
|
181
|
+
}
|
|
182
|
+
return { action: "flag" };
|
|
183
|
+
}
|
|
184
|
+
function commitMessage(installed, upgraded) {
|
|
185
|
+
const what = "(/dev/match route + matching/ gate scripts)";
|
|
186
|
+
if (upgraded.length === 0) return `feat: install the matching harness ${what}`;
|
|
187
|
+
const subject = installed.length > 0 ? `feat: install and upgrade the matching harness ${what}` : `chore: upgrade the matching harness (${upgraded.length} file${upgraded.length === 1 ? "" : "s"} from a previously shipped version)`;
|
|
188
|
+
const body = [
|
|
189
|
+
installed.length > 0 ? `installed: ${installed.join(", ")}` : "",
|
|
190
|
+
`upgraded from a previously shipped version: ${upgraded.join(", ")}`
|
|
191
|
+
].filter(Boolean).join("\n");
|
|
192
|
+
return `${subject}
|
|
193
|
+
|
|
194
|
+
${body}`;
|
|
195
|
+
}
|
|
196
|
+
function planFileWrite(existing, template, owner, previous) {
|
|
197
|
+
if (existing === null) return "write";
|
|
198
|
+
if (normalize(existing) === normalize(template)) return "skip";
|
|
199
|
+
if (owner === "site") return "skip";
|
|
200
|
+
if (previous.some((p) => normalize(existing) === normalize(p))) return "replace";
|
|
201
|
+
return "flag";
|
|
202
|
+
}
|
|
203
|
+
var GITIGNORE_END_MARKER = "# end reddoor-maint match-harness";
|
|
204
|
+
var PRETTIERIGNORE_END_MARKER = "# end reddoor-maint match-harness";
|
|
205
|
+
var CLAUDE_MD_END_MARKER = "<!-- end reddoor-maint match-harness -->";
|
|
206
|
+
var APPENDED_BLOCKS = [
|
|
207
|
+
[".gitignore", GITIGNORE_MARKER, GITIGNORE_END_MARKER, GITIGNORE_BLOCK],
|
|
208
|
+
[".prettierignore", PRETTIERIGNORE_MARKER, PRETTIERIGNORE_END_MARKER, PRETTIERIGNORE_BLOCK],
|
|
209
|
+
["CLAUDE.md", CLAUDE_MD_MARKER, CLAUDE_MD_END_MARKER, CLAUDE_MD_BLOCK]
|
|
210
|
+
];
|
|
211
|
+
var MATCH_HARNESS_INSTALLED_PATHS = [
|
|
212
|
+
...MATCH_HARNESS_FILES.map((f) => f.rel),
|
|
213
|
+
...APPENDED_BLOCKS.map(([rel]) => rel)
|
|
214
|
+
];
|
|
215
|
+
async function matchHarness(site, opts, deps = { spawn: defaultSpawn }) {
|
|
216
|
+
return withRecipe({
|
|
217
|
+
name: "match-harness",
|
|
218
|
+
site,
|
|
219
|
+
plan: async () => {
|
|
220
|
+
if (!opts.ref || !/^https?:\/\//.test(opts.ref)) {
|
|
221
|
+
return {
|
|
222
|
+
kind: "failed",
|
|
223
|
+
notes: "--ref <url> is required: the harness gates against a live reference, and a harness with no reference can only ever report an absence of errors"
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
kind: "apply",
|
|
228
|
+
plan: {
|
|
229
|
+
ref: opts.ref.replace(/\/$/, ""),
|
|
230
|
+
cand: opts.cand ?? "http://localhost:5173",
|
|
231
|
+
matrix: opts.matrix ?? [1440, 834, 390]
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
apply: async (planned, { commit, cwd }) => {
|
|
236
|
+
const notes = [];
|
|
237
|
+
const written = [];
|
|
238
|
+
const installed = [];
|
|
239
|
+
const upgraded = [];
|
|
240
|
+
const before = /* @__PURE__ */ new Map();
|
|
241
|
+
const previousAll = deps.previous ?? MATCH_HARNESS_PREVIOUS;
|
|
242
|
+
const blockPreviousAll = deps.blockPrevious ?? MATCH_HARNESS_BLOCK_PREVIOUS;
|
|
243
|
+
const plans = [];
|
|
244
|
+
for (const f of MATCH_HARNESS_FILES) {
|
|
245
|
+
const target = join(cwd, f.rel);
|
|
246
|
+
let template = f.template;
|
|
247
|
+
if (f.rel === HARNESS_JSON_RELATIVE) {
|
|
248
|
+
const seed = JSON.parse(template);
|
|
249
|
+
seed.ref = planned.ref;
|
|
250
|
+
seed.cand = planned.cand;
|
|
251
|
+
seed.matrix = planned.matrix;
|
|
252
|
+
template = JSON.stringify(seed, null, 2) + "\n";
|
|
253
|
+
}
|
|
254
|
+
const existing = await readIfExists(target);
|
|
255
|
+
plans.push({
|
|
256
|
+
f,
|
|
257
|
+
template,
|
|
258
|
+
existing,
|
|
259
|
+
action: planFileWrite(existing, template, f.owner, previousAll[f.rel] ?? [])
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const coupled = new Set(MATCH_HARNESS_COUPLED);
|
|
263
|
+
const blockedBy = plans.filter((p) => coupled.has(p.f.rel) && p.action === "flag").map((p) => p.f.rel);
|
|
264
|
+
const demoted = plans.filter(
|
|
265
|
+
(p) => coupled.has(p.f.rel) && p.action === "replace" && !blockedBy.includes(p.f.rel)
|
|
266
|
+
);
|
|
267
|
+
if (blockedBy.length > 0 && demoted.length > 0) {
|
|
268
|
+
for (const p of demoted) p.action = "flag";
|
|
269
|
+
notes.push(
|
|
270
|
+
`${blockedBy.join(", ")} differs from the shipped template, so the whole coupled set was left alone: ${MATCH_HARNESS_COUPLED.join(", ")} upgrade together. matching/gate.sh calls \`harness.mjs --check-run\` and matching/next.mjs imports from harness.mjs, so upgrading one without the others leaves a harness that cannot run.`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
const demotedRels = new Set(demoted.map((p) => p.f.rel));
|
|
274
|
+
for (const { f, template, existing, action } of plans) {
|
|
275
|
+
if (action === "flag") {
|
|
276
|
+
if (demotedRels.has(f.rel)) continue;
|
|
277
|
+
notes.push(
|
|
278
|
+
`${f.rel} differs from the shipped template and was left alone (hand-edited?)`
|
|
279
|
+
);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (action === "skip") continue;
|
|
283
|
+
const target = join(cwd, f.rel);
|
|
284
|
+
await mkdir(dirname(target), { recursive: true });
|
|
285
|
+
before.set(f.rel, existing);
|
|
286
|
+
await writeFile(target, template, "utf-8");
|
|
287
|
+
written.push(f.rel);
|
|
288
|
+
(action === "write" ? installed : upgraded).push(f.rel);
|
|
289
|
+
if (action === "replace") notes.push(`${f.rel} upgraded from a previous version`);
|
|
290
|
+
}
|
|
291
|
+
for (const [rel, marker, endMarker, block] of APPENDED_BLOCKS) {
|
|
292
|
+
const path = join(cwd, rel);
|
|
293
|
+
const existing = await readIfExists(path);
|
|
294
|
+
const plan = planBlockWrite(
|
|
295
|
+
existing,
|
|
296
|
+
marker,
|
|
297
|
+
endMarker,
|
|
298
|
+
block,
|
|
299
|
+
blockPreviousAll[rel] ?? []
|
|
300
|
+
);
|
|
301
|
+
if (plan.action === "skip") continue;
|
|
302
|
+
if (plan.action === "flag") {
|
|
303
|
+
notes.push(
|
|
304
|
+
`${rel} carries a match-harness block that differs from every block this recipe has shipped, and was left alone (hand-edited?)`
|
|
305
|
+
);
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
before.set(rel, existing);
|
|
309
|
+
await writeFile(path, plan.content, "utf-8");
|
|
310
|
+
written.push(rel);
|
|
311
|
+
(plan.action === "write" ? installed : upgraded).push(`${rel} (match-harness block)`);
|
|
312
|
+
if (plan.action === "replace")
|
|
313
|
+
notes.push(`${rel}'s match-harness block upgraded from a previous version`);
|
|
314
|
+
if (plan.action === "terminate")
|
|
315
|
+
notes.push(
|
|
316
|
+
`${rel}'s match-harness block region was terminated so a future version can update it`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
const siteOwned = new Set(
|
|
320
|
+
MATCH_HARNESS_FILES.filter((f) => f.owner === "site").map((f) => f.rel)
|
|
321
|
+
);
|
|
322
|
+
const toFormat = written.filter((p) => siteOwned.has(p) || p === "CLAUDE.md");
|
|
323
|
+
if (toFormat.length > 0) {
|
|
324
|
+
const bin = await (deps.resolvePrettier ?? resolveTargetPrettier)(cwd);
|
|
325
|
+
if (bin === null) {
|
|
326
|
+
notes.push(PRETTIER_FLAG_NOTE);
|
|
327
|
+
} else if (!await formatWithPrettier(deps.spawn, cwd, toFormat, {
|
|
328
|
+
bin,
|
|
329
|
+
timeoutMs: PRETTIER_TIMEOUT_MS
|
|
330
|
+
})) {
|
|
331
|
+
notes.push(PRETTIER_FLAG_NOTE);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
await commit(commitMessage(installed, upgraded));
|
|
335
|
+
const missing = await pathsMissingFromHead(cwd, MATCH_HARNESS_INSTALLED_PATHS);
|
|
336
|
+
if (missing.length > 0) {
|
|
337
|
+
const rules = await ignoreRulesFor(cwd, missing);
|
|
338
|
+
const sources = [...new Set(rules.map((r) => r.split(" ")[0]))];
|
|
339
|
+
const explained = new Set(rules.map((r) => r.split(" ")[1] ?? ""));
|
|
340
|
+
const unexplained = missing.filter((p) => !explained.has(p));
|
|
341
|
+
const removed = [];
|
|
342
|
+
for (const rel of written.filter((p) => missing.includes(p))) {
|
|
343
|
+
const prev = before.get(rel);
|
|
344
|
+
if (prev === void 0) continue;
|
|
345
|
+
if (prev === null) {
|
|
346
|
+
await rm(join(cwd, rel), { force: true });
|
|
347
|
+
removed.push(rel);
|
|
348
|
+
} else {
|
|
349
|
+
await writeFile(join(cwd, rel), prev, "utf-8");
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
for (const d of [...new Set(removed.map(dirname))].sort((a, b) => b.length - a.length)) {
|
|
353
|
+
if (d !== ".") await rmdir(join(cwd, d)).catch(() => void 0);
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
kind: "failed",
|
|
357
|
+
notes: `the harness was NOT installed: ${missing.length} of ${MATCH_HARNESS_INSTALLED_PATHS.length} paths git refused to track, so a fresh clone, CI and the next agent get a harness that does not exist. Absent from the commit: ${missing.join(", ")}. ` + (sources.length > 0 ? `Excluded by ${sources.join(", ")} \u2014 narrow or remove those rules and re-run. ` : "") + (unexplained.length > 0 ? `No ignore rule matched ${unexplained.join(", ")} \u2014 check .git/info/exclude, core.excludesFile, and whether the directory is a nested repository. ` : "") + "Everything this run wrote to those paths has been put back as it was."
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return notes.length > 0 ? { kind: "ok", notes: notes.join("; ") } : { kind: "ok" };
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/cli/commands/match-harness.ts
|
|
366
|
+
function formatResult(r) {
|
|
367
|
+
if (r.status === "noop") return `[${r.site}] noop: ${r.notes ?? ""}`;
|
|
368
|
+
if (r.status === "failed") return `[${r.site}] failed: ${r.notes ?? ""}`;
|
|
369
|
+
return `[${r.site}] applied: ${r.commits.length} commit(s)
|
|
370
|
+
${r.notes ?? ""}`;
|
|
371
|
+
}
|
|
372
|
+
async function runMatchHarnessCommand(site, opts) {
|
|
373
|
+
const cwd = opts.cwd ? resolve(opts.cwd) : process.cwd();
|
|
374
|
+
let sites = await resolveSites({
|
|
375
|
+
...site !== void 0 ? { site } : {},
|
|
376
|
+
...opts.fleet !== void 0 ? { fleet: opts.fleet } : {},
|
|
377
|
+
...opts.workdir !== void 0 ? { workdir: opts.workdir } : {},
|
|
378
|
+
cwd
|
|
379
|
+
});
|
|
380
|
+
if (sites.length === 0) {
|
|
381
|
+
return {
|
|
382
|
+
output: `the inventory resolved NO SITES, so the matching harness was installed nowhere. This is not an installed fleet \u2014 check the inventory (an Airtable view filter, an empty JSON file, a dynamic inventory returning []). Do NOT read this exit as a rollout.`,
|
|
383
|
+
code: 1
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
let skipped = [];
|
|
387
|
+
if (opts.fleet) {
|
|
388
|
+
const workdir = opts.workdir ?? fleetWorkdir();
|
|
389
|
+
const prep = await prepareFleetSites(sites, { workdir });
|
|
390
|
+
sites = prep.prepared;
|
|
391
|
+
skipped = prep.skipped;
|
|
392
|
+
}
|
|
393
|
+
const rawMatrix = opts.matrix === void 0 ? void 0 : String(opts.matrix);
|
|
394
|
+
const matrix = rawMatrix === void 0 ? void 0 : rawMatrix.split(",").map((v) => Number(v.trim()));
|
|
395
|
+
if (matrix !== void 0 && matrix.some((v) => !Number.isFinite(v) || v <= 0)) {
|
|
396
|
+
return {
|
|
397
|
+
output: `match-harness: --matrix "${rawMatrix}" is not a list of viewports`,
|
|
398
|
+
code: 1
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
const results = await runRecipeOverSites(
|
|
402
|
+
"match-harness",
|
|
403
|
+
sites,
|
|
404
|
+
(s) => matchHarness(s, {
|
|
405
|
+
ref: opts.ref ?? "",
|
|
406
|
+
...opts.cand !== void 0 ? { cand: opts.cand } : {},
|
|
407
|
+
...matrix !== void 0 ? { matrix } : {}
|
|
408
|
+
})
|
|
409
|
+
);
|
|
410
|
+
const output = results.map(formatResult).join("\n");
|
|
411
|
+
const code = results.some((r) => r.status === "failed") ? 1 : 0;
|
|
412
|
+
return { output: appendSkipNotice(output, skipped), code };
|
|
413
|
+
}
|
|
414
|
+
export {
|
|
415
|
+
runMatchHarnessCommand
|
|
416
|
+
};
|
|
417
|
+
//# sourceMappingURL=match-harness-GBCDSVPS.js.map
|