@reddoorla/maintenance 0.96.0 → 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 +5 -6
- package/dist/{announce-45HVP4J4.js → announce-5ZD2SBGD.js} +39 -31
- package/dist/announce-5ZD2SBGD.js.map +1 -0
- package/dist/attachments-JFGFQRXQ.js +10 -0
- package/dist/{blux-RUFMNOUJ.js → blux-NXQLMS4W.js} +5 -5
- package/dist/{bump-deps-EL3TKMIL.js → bump-deps-775VLDYB.js} +6 -6
- package/dist/{chunk-IMYL67JV.js → chunk-3RLDX3Q2.js} +5 -5
- package/dist/{chunk-CA6ADGOC.js → chunk-6PGXKN6F.js} +2 -2
- package/dist/{chunk-Y5BVR4HW.js → chunk-6THEZQLA.js} +3 -3
- package/dist/chunk-ASLDCWBF.js +41 -0
- package/dist/chunk-ASLDCWBF.js.map +1 -0
- package/dist/{chunk-DVJNYPFR.js → chunk-AYDQQBNG.js} +2 -2
- package/dist/{chunk-GEUC5VGM.js → chunk-BINFZQ2T.js} +49 -4
- package/dist/chunk-BINFZQ2T.js.map +1 -0
- package/dist/{chunk-72FJZ6LO.js → chunk-DRNZVORX.js} +26 -22
- package/dist/chunk-DRNZVORX.js.map +1 -0
- package/dist/{chunk-SHGIDTYF.js → chunk-DYTYPKFN.js} +2 -2
- package/dist/{chunk-NVLREB5R.js → chunk-EN6CTBUN.js} +7 -7
- package/dist/{chunk-ZH5LOQET.js → chunk-FHFXJ3EM.js} +7 -5
- package/dist/chunk-FHFXJ3EM.js.map +1 -0
- package/dist/{chunk-H445EH33.js → chunk-FP6HOW6J.js} +10 -7
- package/dist/chunk-FP6HOW6J.js.map +1 -0
- package/dist/{chunk-RRE74VWI.js → chunk-HSMN35LU.js} +2 -2
- package/dist/chunk-ICSRWW5Q.js +148 -0
- package/dist/chunk-ICSRWW5Q.js.map +1 -0
- package/dist/{chunk-XG2A6GIG.js → chunk-IDBPII6G.js} +4 -4
- package/dist/{chunk-XG2A6GIG.js.map → chunk-IDBPII6G.js.map} +1 -1
- package/dist/{chunk-T76MA2FH.js → chunk-IOTOOTII.js} +12 -1
- package/dist/{chunk-T76MA2FH.js.map → chunk-IOTOOTII.js.map} +1 -1
- package/dist/{chunk-ECDJDHKT.js → chunk-JHJNNJLG.js} +2 -2
- package/dist/{chunk-XY7YV2YQ.js → chunk-L3S3QZXL.js} +2 -2
- package/dist/{chunk-XED2DNAI.js → chunk-LAG6IP7Y.js} +7 -7
- package/dist/{chunk-P4A6D6HO.js → chunk-LDHRIQUW.js} +32 -18
- package/dist/chunk-LDHRIQUW.js.map +1 -0
- package/dist/{chunk-UIITN37J.js → chunk-LF2TJN4X.js} +2 -2
- package/dist/chunk-LF2TJN4X.js.map +1 -0
- package/dist/{chunk-SQ4FSNYL.js → chunk-LUF52EIY.js} +2 -2
- package/dist/{chunk-MZZDMBDT.js → chunk-LZMAG73U.js} +19 -19
- package/dist/chunk-LZMAG73U.js.map +1 -0
- package/dist/{chunk-6INOBNRO.js → chunk-MMSMZC7S.js} +2 -2
- package/dist/{chunk-CGQZFQAY.js → chunk-MVKNU4QD.js} +2 -3
- package/dist/chunk-MVKNU4QD.js.map +1 -0
- package/dist/{chunk-62YWOFVN.js → chunk-PARKXDXI.js} +3 -3
- package/dist/{chunk-62YWOFVN.js.map → chunk-PARKXDXI.js.map} +1 -1
- package/dist/{chunk-44SDGPGQ.js → chunk-PZZ6HM5T.js} +3 -3
- package/dist/chunk-RQJFGK3A.js +51 -0
- package/dist/chunk-RQJFGK3A.js.map +1 -0
- package/dist/{chunk-KN52PM63.js → chunk-TC4GANMX.js} +47 -149
- 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-4UTJ4G6P.js → chunk-TJLKWQOX.js} +2 -2
- package/dist/{chunk-VEIIUCYL.js → chunk-TV76A6GN.js} +2 -2
- package/dist/{chunk-D3CTOCUR.js → chunk-TVZ6QCOE.js} +3 -3
- package/dist/{chunk-3EA43S3R.js → chunk-U2ZI6G7H.js} +12 -12
- package/dist/chunk-UPE6D4AT.js +69 -0
- package/dist/chunk-UPE6D4AT.js.map +1 -0
- package/dist/{chunk-OTEVKRAH.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-EASYCD6F.js → chunk-WMF5S2MB.js} +1 -1
- package/dist/{chunk-EASYCD6F.js.map → chunk-WMF5S2MB.js.map} +1 -1
- package/dist/{chunk-37B4LVB3.js → chunk-X56LAKPR.js} +3 -3
- package/dist/chunk-X56LAKPR.js.map +1 -0
- package/dist/{chunk-IY6256GK.js → chunk-YJBSB4LP.js} +6 -1
- package/dist/chunk-YJBSB4LP.js.map +1 -0
- package/dist/{chunk-EQQ4H6L3.js → chunk-YXWBM26P.js} +2 -2
- package/dist/cli/bin.js +92 -50
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.d.ts +33 -3
- package/dist/cli/commands/audit.js +25 -22
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/client-DGTO65WB.js +11 -0
- package/dist/{convert-to-pnpm-4DON7IFS.js → convert-to-pnpm-KLWA53N5.js} +6 -6
- package/dist/{db-5K4CDEGJ.js → db-II5WAK4D.js} +13 -13
- package/dist/{digest-5AJGWS33.js → digest-CWKK2DMR.js} +28 -27
- package/dist/digest-CWKK2DMR.js.map +1 -0
- package/dist/{digest-collectors-MC5NWEUA.js → digest-collectors-CYTN2EUX.js} +15 -12
- 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-XESZHZEB.js → ensure-site-OO4UPWNQ.js} +19 -42
- package/dist/ensure-site-OO4UPWNQ.js.map +1 -0
- package/dist/fleet-events-CkOZpMVC.d.ts +435 -0
- package/dist/{fleet-state-GJWGKTLD.js → fleet-state-RJROYXKO.js} +17 -7
- package/dist/{forms-notify-target-4NPLX2UG.js → forms-notify-target-X6UEOI2M.js} +17 -13
- package/dist/forms-notify-target-X6UEOI2M.js.map +1 -0
- package/dist/{github-signals-DMU74SAX.js → github-signals-QIRTHHET.js} +20 -15
- package/dist/github-signals-QIRTHHET.js.map +1 -0
- package/dist/{header-image-BRTOLHJF.js → header-image-H7ZJAV26.js} +13 -17
- package/dist/header-image-H7ZJAV26.js.map +1 -0
- package/dist/{health-endpoint-UCLMSSCN.js → health-endpoint-BJ6BKRHF.js} +7 -7
- package/dist/{health-mirror-ONRT4DQ5.js → health-mirror-KU3G7V2Y.js} +10 -10
- package/dist/{import-airtable-IIK2QUUF.js → import-airtable-UBF5GYAL.js} +3 -3
- package/dist/index.d.ts +102 -494
- package/dist/index.js +60 -49
- package/dist/index.js.map +1 -1
- package/dist/{init-3LMZTCVD.js → init-GOWDA5QS.js} +15 -15
- package/dist/{launch-XTDMC6LD.js → launch-7N3X2MXB.js} +36 -34
- package/dist/launch-7N3X2MXB.js.map +1 -0
- package/dist/lighthouse-airtable-M45OPVRV.js +14 -0
- package/dist/{chunk-IHHAYXKY.js → match-harness-GBCDSVPS.js} +70 -4
- package/dist/match-harness-GBCDSVPS.js.map +1 -0
- package/dist/migrate-XZ7U5DFC.js +7 -0
- package/dist/notify-K3WVH3DM.js +1 -1
- package/dist/{onboard-CZYBWVWH.js → onboard-JQQIMQW7.js} +6 -6
- package/dist/{orchestrate-TGDFGY52.js → orchestrate-L2A5BZFY.js} +13 -10
- package/dist/parity-2JGVUCZD.js +14 -0
- package/dist/{preflight-POZZI3GN.js → preflight-O7Q6FWHU.js} +35 -11
- package/dist/preflight-O7Q6FWHU.js.map +1 -0
- package/dist/{prismic-ci-AHY5ULJR.js → prismic-ci-NAPWOXXD.js} +11 -11
- package/dist/prismic-ci-NAPWOXXD.js.map +1 -0
- package/dist/{prismic-models-URYGODNY.js → prismic-models-LL6ORTQ2.js} +19 -16
- package/dist/prismic-models-LL6ORTQ2.js.map +1 -0
- package/dist/{prospect-audit-AJEE3Z4L.js → prospect-audit-FMPGQIRQ.js} +2 -2
- package/dist/{protection-audit-3J3UEOVT.js → protection-audit-YXJKFU6O.js} +3 -3
- package/dist/{protection-audit-3J3UEOVT.js.map → protection-audit-YXJKFU6O.js.map} +1 -1
- package/dist/recipes/sync-configs.js +3 -3
- package/dist/{render-from-row-65DC6ZP3.js → render-from-row-75JTED2Z.js} +6 -4
- package/dist/{renovate-dispatch-ONJMBUHJ.js → renovate-dispatch-2IHAZDVA.js} +13 -8
- package/dist/{renovate-dispatch-ONJMBUHJ.js.map → renovate-dispatch-2IHAZDVA.js.map} +1 -1
- package/dist/{report-RC5LGPU3.js → report-IAHXN5CW.js} +102 -58
- package/dist/report-IAHXN5CW.js.map +1 -0
- package/dist/{report-mirror-KKZZ6RMZ.js → report-mirror-4QM57LMS.js} +28 -10
- 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/{self-updating-RKKIYNV4.js → self-updating-ZUWPGYUP.js} +7 -7
- package/dist/{selftest-AFLR7YV7.js → selftest-G4W3LIBQ.js} +21 -17
- package/dist/{selftest-AFLR7YV7.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-mirror-H2FYMN55.js → site-mirror-WR7ACXOV.js} +10 -10
- package/dist/site-mirror-WR7ACXOV.js.map +1 -0
- package/dist/{smoke-suite-BWHR3XX5.js → smoke-suite-WAUPI6EJ.js} +7 -7
- package/dist/{submissions-II5J35QM.js → submissions-2SHCFOKD.js} +11 -6
- package/dist/submissions-2SHCFOKD.js.map +1 -0
- package/dist/{svelte-codemods-QWGIRNN6.js → svelte-codemods-VOLOKKFI.js} +6 -6
- package/dist/{sync-NKPDCMYW.js → sync-F4USL4K5.js} +6 -6
- package/dist/{sync-configs-HGLQV55H.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-WZ44IARG.js → upgrade-EVAL3JTO.js} +6 -6
- package/dist/util/git.d.ts +1 -1
- package/dist/util/git.js +1 -1
- package/dist/{websites-6DHIHJ2D.js → websites-Y6FN2XFD.js} +26 -21
- package/dist/{write-audits-to-airtable-BIK6LNVC.js → write-audits-to-airtable-TI3JMWSY.js} +6 -4
- package/package.json +1 -1
- package/dist/airtable-SJV4ZSMS.js +0 -9
- package/dist/announce-45HVP4J4.js.map +0 -1
- package/dist/attachments-NLZIJ5NU.js +0 -9
- package/dist/chunk-37B4LVB3.js.map +0 -1
- package/dist/chunk-72FJZ6LO.js.map +0 -1
- package/dist/chunk-74QRWE6G.js +0 -53
- package/dist/chunk-74QRWE6G.js.map +0 -1
- package/dist/chunk-CGQZFQAY.js.map +0 -1
- package/dist/chunk-F3JWOPTG.js.map +0 -1
- package/dist/chunk-GEUC5VGM.js.map +0 -1
- package/dist/chunk-H445EH33.js.map +0 -1
- package/dist/chunk-IHHAYXKY.js.map +0 -1
- package/dist/chunk-IY6256GK.js.map +0 -1
- package/dist/chunk-KN52PM63.js.map +0 -1
- package/dist/chunk-MZZDMBDT.js.map +0 -1
- package/dist/chunk-OTEVKRAH.js.map +0 -1
- package/dist/chunk-P4A6D6HO.js.map +0 -1
- package/dist/chunk-UIITN37J.js.map +0 -1
- package/dist/chunk-YFAXOD7Z.js +0 -42
- package/dist/chunk-YFAXOD7Z.js.map +0 -1
- package/dist/chunk-ZH5LOQET.js.map +0 -1
- package/dist/client-L7NAUG6J.js +0 -11
- package/dist/digest-5AJGWS33.js.map +0 -1
- package/dist/ensure-site-XESZHZEB.js.map +0 -1
- package/dist/fleet-events-cF2Om88w.d.ts +0 -24
- package/dist/forms-notify-target-4NPLX2UG.js.map +0 -1
- package/dist/github-signals-DMU74SAX.js.map +0 -1
- package/dist/header-image-BRTOLHJF.js.map +0 -1
- package/dist/launch-XTDMC6LD.js.map +0 -1
- package/dist/lighthouse-airtable-3NOFB42B.js +0 -12
- package/dist/match-harness-OW45CB5H.js +0 -77
- package/dist/match-harness-OW45CB5H.js.map +0 -1
- package/dist/migrate-NE3DUMTM.js +0 -7
- package/dist/parity-AEKYSLN6.js +0 -14
- package/dist/preflight-POZZI3GN.js.map +0 -1
- package/dist/prismic-ci-AHY5ULJR.js.map +0 -1
- package/dist/prismic-models-URYGODNY.js.map +0 -1
- package/dist/report-RC5LGPU3.js.map +0 -1
- package/dist/report-mirror-KKZZ6RMZ.js.map +0 -1
- package/dist/site-mirror-H2FYMN55.js.map +0 -1
- package/dist/submissions-II5J35QM.js.map +0 -1
- package/dist/write-audits-to-airtable-BIK6LNVC.js.map +0 -1
- /package/dist/{airtable-SJV4ZSMS.js.map → attachments-JFGFQRXQ.js.map} +0 -0
- /package/dist/{blux-RUFMNOUJ.js.map → blux-NXQLMS4W.js.map} +0 -0
- /package/dist/{bump-deps-EL3TKMIL.js.map → bump-deps-775VLDYB.js.map} +0 -0
- /package/dist/{chunk-IMYL67JV.js.map → chunk-3RLDX3Q2.js.map} +0 -0
- /package/dist/{chunk-CA6ADGOC.js.map → chunk-6PGXKN6F.js.map} +0 -0
- /package/dist/{chunk-Y5BVR4HW.js.map → chunk-6THEZQLA.js.map} +0 -0
- /package/dist/{chunk-DVJNYPFR.js.map → chunk-AYDQQBNG.js.map} +0 -0
- /package/dist/{chunk-SHGIDTYF.js.map → chunk-DYTYPKFN.js.map} +0 -0
- /package/dist/{chunk-NVLREB5R.js.map → chunk-EN6CTBUN.js.map} +0 -0
- /package/dist/{chunk-RRE74VWI.js.map → chunk-HSMN35LU.js.map} +0 -0
- /package/dist/{chunk-ECDJDHKT.js.map → chunk-JHJNNJLG.js.map} +0 -0
- /package/dist/{chunk-XY7YV2YQ.js.map → chunk-L3S3QZXL.js.map} +0 -0
- /package/dist/{chunk-XED2DNAI.js.map → chunk-LAG6IP7Y.js.map} +0 -0
- /package/dist/{chunk-SQ4FSNYL.js.map → chunk-LUF52EIY.js.map} +0 -0
- /package/dist/{chunk-6INOBNRO.js.map → chunk-MMSMZC7S.js.map} +0 -0
- /package/dist/{chunk-44SDGPGQ.js.map → chunk-PZZ6HM5T.js.map} +0 -0
- /package/dist/{chunk-4UTJ4G6P.js.map → chunk-TJLKWQOX.js.map} +0 -0
- /package/dist/{chunk-VEIIUCYL.js.map → chunk-TV76A6GN.js.map} +0 -0
- /package/dist/{chunk-D3CTOCUR.js.map → chunk-TVZ6QCOE.js.map} +0 -0
- /package/dist/{chunk-3EA43S3R.js.map → chunk-U2ZI6G7H.js.map} +0 -0
- /package/dist/{chunk-EQQ4H6L3.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-4DON7IFS.js.map → convert-to-pnpm-KLWA53N5.js.map} +0 -0
- /package/dist/{db-5K4CDEGJ.js.map → db-II5WAK4D.js.map} +0 -0
- /package/dist/{client-L7NAUG6J.js.map → digest-collectors-CYTN2EUX.js.map} +0 -0
- /package/dist/{digest-collectors-MC5NWEUA.js.map → fleet-state-RJROYXKO.js.map} +0 -0
- /package/dist/{health-endpoint-UCLMSSCN.js.map → health-endpoint-BJ6BKRHF.js.map} +0 -0
- /package/dist/{health-mirror-ONRT4DQ5.js.map → health-mirror-KU3G7V2Y.js.map} +0 -0
- /package/dist/{fleet-state-GJWGKTLD.js.map → import-airtable-UBF5GYAL.js.map} +0 -0
- /package/dist/{init-3LMZTCVD.js.map → init-GOWDA5QS.js.map} +0 -0
- /package/dist/{import-airtable-IIK2QUUF.js.map → lighthouse-airtable-M45OPVRV.js.map} +0 -0
- /package/dist/{lighthouse-airtable-3NOFB42B.js.map → migrate-XZ7U5DFC.js.map} +0 -0
- /package/dist/{onboard-CZYBWVWH.js.map → onboard-JQQIMQW7.js.map} +0 -0
- /package/dist/{migrate-NE3DUMTM.js.map → orchestrate-L2A5BZFY.js.map} +0 -0
- /package/dist/{orchestrate-TGDFGY52.js.map → parity-2JGVUCZD.js.map} +0 -0
- /package/dist/{prospect-audit-AJEE3Z4L.js.map → prospect-audit-FMPGQIRQ.js.map} +0 -0
- /package/dist/{parity-AEKYSLN6.js.map → render-from-row-75JTED2Z.js.map} +0 -0
- /package/dist/{self-updating-RKKIYNV4.js.map → self-updating-ZUWPGYUP.js.map} +0 -0
- /package/dist/{smoke-suite-BWHR3XX5.js.map → smoke-suite-WAUPI6EJ.js.map} +0 -0
- /package/dist/{svelte-codemods-QWGIRNN6.js.map → svelte-codemods-VOLOKKFI.js.map} +0 -0
- /package/dist/{sync-NKPDCMYW.js.map → sync-F4USL4K5.js.map} +0 -0
- /package/dist/{sync-configs-HGLQV55H.js.map → sync-configs-OXSWKVZ2.js.map} +0 -0
- /package/dist/{upgrade-WZ44IARG.js.map → upgrade-EVAL3JTO.js.map} +0 -0
- /package/dist/{render-from-row-65DC6ZP3.js.map → websites-Y6FN2XFD.js.map} +0 -0
- /package/dist/{websites-6DHIHJ2D.js.map → write-audits-to-airtable-TI3JMWSY.js.map} +0 -0
package/README.md
CHANGED
|
@@ -158,7 +158,7 @@ Installs the matching harness used to rebuild a site against a live reference (a
|
|
|
158
158
|
```bash
|
|
159
159
|
reddoor-maint match-harness --ref https://client.webflow.io
|
|
160
160
|
reddoor-maint match-harness acme --ref https://acme.webflow.io --cand http://localhost:4173
|
|
161
|
-
reddoor-maint match-harness --fleet
|
|
161
|
+
reddoor-maint match-harness --fleet turso --ref https://client.webflow.io --matrix 1440,834,390
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
`--ref` is **required**. The harness gates against a live reference; with no reference it can only ever report an absence of errors, so the recipe refuses instead of installing something that goes green on nothing. `--cand` defaults to `http://localhost:5173` and `--matrix` to `1440,834,390`.
|
|
@@ -419,10 +419,10 @@ Nothing is written and nothing is sent.
|
|
|
419
419
|
0. **Prereq: refresh Lighthouse scores on each Websites row.** From each site's checkout:
|
|
420
420
|
|
|
421
421
|
```bash
|
|
422
|
-
reddoor-maint audit lighthouse --write-
|
|
422
|
+
reddoor-maint audit lighthouse --write-back
|
|
423
423
|
```
|
|
424
424
|
|
|
425
|
-
This runs Lighthouse and writes the 4 scores directly to the matching Websites row (slug auto-derived from `package.json#name`; pass `--write-
|
|
425
|
+
This runs Lighthouse and writes the 4 scores directly to the matching Websites row (slug auto-derived from `package.json#name`; pass `--write-back=<slug>` to override), along with a `Last lighthouse audit at` timestamp. The report orchestrator copies these into the new Reports row — drafting a report for a site missing scores fails with a clear error.
|
|
426
426
|
|
|
427
427
|
1. **Draft overdue reports**
|
|
428
428
|
|
|
@@ -481,10 +481,9 @@ This repo's Netlify site hosts the whole dashboard + forms surface: the Resend d
|
|
|
481
481
|
| `PROSPECT_EDIT_TOKEN` | Shared secret gating `POST /api/audit-report/:token/overrides`, where an operator's edits to a prospect report are saved — the SAME value is set on the marketing site, which proxies the save. Generate with `openssl rand -base64 32`. **Fails closed**: absent → every save 503s, which looks like a broken editor. Leading/trailing whitespace from a paste is tolerated, and warned about in the function logs |
|
|
482
482
|
| `DASHBOARD_PASSWORD` | Basic-auth password gating the cockpit, per-site dashboards, and every action endpoint — the dashboard is unauthable without it |
|
|
483
483
|
| `DASHBOARD_BASE_URL` | Public base URL of this deploy (e.g. `https://<your-site>.netlify.app`) — used to build absolute links such as report approve URLs |
|
|
484
|
-
| `
|
|
485
|
-
| `GH_TOKEN` | GitHub token for the dashboard's request-path REST calls (fleet-refresh dispatch + GitHub-sourced signals) |
|
|
484
|
+
| `GH_TOKEN` | GitHub token for the dashboard's request-path REST calls, with `actions:write` — the "Trigger Renovate" button, the fleet-refresh and prospect-audit dispatches (each answers 503 `not-configured` without it), plus GitHub-sourced signals. The only name these functions read: `RENOVATE_TOKEN` was retired with the operator PAT |
|
|
486
485
|
|
|
487
|
-
> The first seven vars cover the webhook + forms ingest (`TURNSTILE_SECRET_KEY` optional); `PROSPECT_EDIT_TOKEN` is needed only to SAVE edits to a prospect report (serving one needs no secret beyond its URL token); `DASHBOARD_PASSWORD` / `DASHBOARD_BASE_URL` / `
|
|
486
|
+
> The first seven vars cover the webhook + forms ingest (`TURNSTILE_SECRET_KEY` optional); `PROSPECT_EDIT_TOKEN` is needed only to SAVE edits to a prospect report (serving one needs no secret beyond its URL token); `DASHBOARD_PASSWORD` / `DASHBOARD_BASE_URL` / `GH_TOKEN` are additionally required by the operator **dashboard** this same deploy serves (cockpit at `/`, per-site at `/s/:slug`).
|
|
488
487
|
|
|
489
488
|
**3. Trigger a deploy** (Deploys → Trigger deploy → Deploy site). When it goes green, curl the health endpoint:
|
|
490
489
|
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
2
|
scoresFromRow
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PZZ6HM5T.js";
|
|
4
4
|
import {
|
|
5
5
|
fetchGaUsers,
|
|
6
6
|
fetchSearch,
|
|
7
7
|
readGaConfig,
|
|
8
8
|
refreshHeaderImage
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DRNZVORX.js";
|
|
10
10
|
import {
|
|
11
|
+
createReportDraft,
|
|
12
|
+
findReportForPeriod,
|
|
11
13
|
queueDraft
|
|
12
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-UPE6D4AT.js";
|
|
13
15
|
import {
|
|
14
|
-
createDraft,
|
|
15
|
-
findReportByPeriod,
|
|
16
16
|
updateReportScores
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-TEMQPSCM.js";
|
|
18
|
+
import "./chunk-ASLDCWBF.js";
|
|
18
19
|
import "./chunk-VAD43MOR.js";
|
|
19
20
|
import {
|
|
20
21
|
uploadAttachment
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-YJBSB4LP.js";
|
|
22
23
|
import {
|
|
23
24
|
openBase,
|
|
24
25
|
readAirtableConfig
|
|
@@ -32,23 +33,26 @@ import {
|
|
|
32
33
|
resolveCopy
|
|
33
34
|
} from "./chunk-KJGKZLCT.js";
|
|
34
35
|
import "./chunk-BGTYPVLT.js";
|
|
35
|
-
import "./chunk-
|
|
36
|
+
import "./chunk-LZMAG73U.js";
|
|
36
37
|
import {
|
|
37
|
-
listWebsites,
|
|
38
|
-
siteSlug,
|
|
39
38
|
updateAnalyticsHealth
|
|
40
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-TC4GANMX.js";
|
|
40
|
+
import {
|
|
41
|
+
siteSlug
|
|
42
|
+
} from "./chunk-ICSRWW5Q.js";
|
|
43
|
+
import "./chunk-RQJFGK3A.js";
|
|
41
44
|
import "./chunk-RARREDQE.js";
|
|
42
45
|
import "./chunk-6VAL7XJT.js";
|
|
43
46
|
|
|
44
47
|
// src/recipes/announce.ts
|
|
45
48
|
var GA_WINDOW_DAYS = 30;
|
|
46
49
|
async function announce(deps) {
|
|
47
|
-
const base = deps
|
|
48
|
-
const now = deps
|
|
49
|
-
const
|
|
50
|
+
const base = deps.base ?? openBase(readAirtableConfig());
|
|
51
|
+
const now = deps.now ?? /* @__PURE__ */ new Date();
|
|
52
|
+
const writer = deps.reportMirror;
|
|
53
|
+
const websites = await deps.roster();
|
|
50
54
|
let targets = websites.filter((w) => w.status === "maintained");
|
|
51
|
-
if (deps
|
|
55
|
+
if (deps.site) {
|
|
52
56
|
const wanted = siteSlug(deps.site);
|
|
53
57
|
targets = targets.filter((w) => siteSlug(w.name) === wanted);
|
|
54
58
|
}
|
|
@@ -61,8 +65,8 @@ async function announce(deps) {
|
|
|
61
65
|
results.push({ site: w.name, status: "skipped-no-scores" });
|
|
62
66
|
continue;
|
|
63
67
|
}
|
|
64
|
-
if (deps
|
|
65
|
-
await refreshHeaderImage(w, deps
|
|
68
|
+
if (deps.refreshHeader !== false) {
|
|
69
|
+
await refreshHeaderImage(w, deps.refreshHeader ?? {});
|
|
66
70
|
}
|
|
67
71
|
const periodEnd = now;
|
|
68
72
|
const periodStart = new Date(now.getTime() - GA_WINDOW_DAYS * 24 * 60 * 60 * 1e3);
|
|
@@ -82,17 +86,17 @@ async function announce(deps) {
|
|
|
82
86
|
w.id,
|
|
83
87
|
gaResult.softFailed || searchResult.softFailed ? now.toISOString() : null
|
|
84
88
|
);
|
|
85
|
-
await deps
|
|
89
|
+
await deps.siteMirror?.health(w.id, fields);
|
|
86
90
|
} catch (e) {
|
|
87
91
|
console.warn(`\u26A0 analytics-health write skipped for ${w.name}: ${e.message}`);
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
let report;
|
|
91
95
|
let statusKind;
|
|
92
|
-
const existing = await
|
|
96
|
+
const existing = await findReportForPeriod(writer, w.id, "Announcement", period);
|
|
93
97
|
if (existing) {
|
|
94
98
|
await updateReportScores(base, existing.id, scores, now, enrichment);
|
|
95
|
-
await
|
|
99
|
+
await writer.patch(existing.id, {
|
|
96
100
|
lighthouse_performance: scores.performance,
|
|
97
101
|
lighthouse_accessibility: scores.accessibility,
|
|
98
102
|
lighthouse_best_practices: scores.bestPractices,
|
|
@@ -106,11 +110,9 @@ async function announce(deps) {
|
|
|
106
110
|
report = existing;
|
|
107
111
|
statusKind = "reused";
|
|
108
112
|
} else {
|
|
109
|
-
report = await
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
deps?.reportMirror?.created
|
|
113
|
-
);
|
|
113
|
+
report = await createReportDraft(draftInputFor(w, scores, now, period, enrichment), {
|
|
114
|
+
create: writer.create
|
|
115
|
+
});
|
|
114
116
|
statusKind = "drafted";
|
|
115
117
|
}
|
|
116
118
|
const slug = siteSlug(w.name);
|
|
@@ -141,7 +143,7 @@ async function announce(deps) {
|
|
|
141
143
|
`${slug}-${now.toISOString().slice(0, 10)}.html`,
|
|
142
144
|
"text/html"
|
|
143
145
|
);
|
|
144
|
-
await
|
|
146
|
+
await writer.body(report.id, html);
|
|
145
147
|
} catch (uploadErr) {
|
|
146
148
|
console.warn(
|
|
147
149
|
`\u26A0 Announcement preview upload skipped for ${w.name}: ${uploadErr instanceof Error ? uploadErr.message : String(uploadErr)}`
|
|
@@ -150,7 +152,7 @@ async function announce(deps) {
|
|
|
150
152
|
const queue = await queueDraft(
|
|
151
153
|
base,
|
|
152
154
|
{ id: report.id, siteId: w.id, reportType: "Announcement" },
|
|
153
|
-
|
|
155
|
+
writer
|
|
154
156
|
);
|
|
155
157
|
const recipientMissing = !(w.reportRecipientsTo && w.reportRecipientsTo.trim());
|
|
156
158
|
results.push({
|
|
@@ -203,11 +205,17 @@ function formatAnnounceResult(result) {
|
|
|
203
205
|
return result.results.map(formatSiteResult).join("\n");
|
|
204
206
|
}
|
|
205
207
|
async function runAnnounceCommand(site, _opts) {
|
|
206
|
-
const { makeReportMirror } = await import("./report-mirror-
|
|
207
|
-
const { makeSiteMirror } = await import("./site-mirror-
|
|
208
|
+
const { makeReportMirror } = await import("./report-mirror-4QM57LMS.js");
|
|
209
|
+
const { makeSiteMirror } = await import("./site-mirror-WR7ACXOV.js");
|
|
208
210
|
const reportMirror = await makeReportMirror();
|
|
209
211
|
const siteMirror = await makeSiteMirror();
|
|
210
|
-
const
|
|
212
|
+
const { readFleetRoster } = await import("./roster-GDNSIRAK.js");
|
|
213
|
+
const result = await announce({
|
|
214
|
+
...site ? { site } : {},
|
|
215
|
+
roster: () => readFleetRoster(),
|
|
216
|
+
reportMirror,
|
|
217
|
+
siteMirror
|
|
218
|
+
});
|
|
211
219
|
const hadError = result.results.some((r) => r.status === "error");
|
|
212
220
|
return { output: formatAnnounceResult(result), code: hadError ? 1 : 0 };
|
|
213
221
|
}
|
|
@@ -215,4 +223,4 @@ export {
|
|
|
215
223
|
formatAnnounceResult,
|
|
216
224
|
runAnnounceCommand
|
|
217
225
|
};
|
|
218
|
-
//# sourceMappingURL=announce-
|
|
226
|
+
//# sourceMappingURL=announce-5ZD2SBGD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/recipes/announce.ts","../src/cli/commands/announce.ts"],"sourcesContent":["import { openBase, readAirtableConfig } from \"../reports/airtable/client.js\";\nimport type { AirtableBase } from \"../reports/airtable/client.js\";\nimport { siteSlug, updateAnalyticsHealth } from \"../reports/airtable/websites.js\";\nimport type { WebsiteRow } from \"../reports/airtable/websites.js\";\nimport { readGaConfig } from \"../reports/ga/config.js\";\nimport { updateReportScores, type ReportEnrichment } from \"../reports/airtable/reports.js\";\nimport { createReportDraft, findReportForPeriod } from \"../reports/create-report.js\";\nimport type { DraftInput } from \"../reports/draft-fields.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 { fetchGaUsers, fetchSearch, refreshHeaderImage } from \"../reports/draft.js\";\nimport type { RefreshHeaderDeps } from \"../reports/draft.js\";\nimport { announcementSiteExtras } from \"../reports/announcement-email/template.js\";\nimport type { LighthouseScores } from \"../reports/types.js\";\nimport { defaultReportSubject } from \"../reports/subject.js\";\nimport { scoresFromRow } from \"../reports/report-data.js\";\n\nexport type AnnounceSiteResult =\n | {\n site: string;\n status: \"drafted\" | \"reused\";\n reportId: string;\n recipientMissing: boolean;\n /** False when a higher-or-equal-tier report was already queued (single-queue rule). */\n queued: boolean;\n }\n | { site: string; status: \"skipped-no-scores\" }\n | { site: string; status: \"error\"; message: string };\n\nexport type AnnounceResult = { results: AnnounceSiteResult[] };\n\n/** The traffic/search lookback window (days) the announcement reports on. The trend compares it\n * against the equal-length prior window, and the email labels it \"vs the previous N days\". */\nconst GA_WINDOW_DAYS = 30;\n\nexport type AnnounceDeps = {\n /** Airtable handle. Defaults to opening the live base from credentials. */\n base?: AirtableBase;\n /** When set, restrict to the single site whose slug matches. Default: all maintenance sites. */\n site?: string;\n /** Single timestamp driving the period key, render, draft, and preview filename. */\n now?: Date;\n /**\n * Header-image refresh, matching `draftReportForSite`'s opt-out shape: UNSET means\n * refresh (the production default), `false` skips it so unit suites don't launch a\n * browser. Announce previously never refreshed at all, so a site whose stored header\n * predated a plate change kept announcing with the old one until its next\n * Maintenance/Testing draft happened to heal it.\n */\n refreshHeader?: RefreshHeaderDeps | false;\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 a site created\n * since step 3 was never announced. Required, not defaulted: every unit test\n * calls `announce` with a fake base, and a default would open a real libSQL\n * handle from inside the suite. The CLI composition root wires\n * `readFleetRoster`. */\n roster: () => Promise<WebsiteRow[]>;\n /** #539 Phase 5, and since #646 step 4 the store that MINTS and holds the\n * report row: the created row (or a reused row's refreshed scores), the\n * rendered body, and the queue flag. Required for the same reason `roster`\n * is — a default would open a real libSQL handle from inside the suite. */\n reportMirror: ReportMirror;\n /** #539 Phase 5: the Websites-row twin — announce stamps `Analytics soft-fail\n * at` on the SITE row, a different Turso table from the report. */\n siteMirror?: SiteMirror;\n};\n\n/**\n * Draft the monthly-report ANNOUNCEMENT email for every `maintenance` site (or one,\n * via `deps.site`). Airtable-driven and fleet-wide: unlike `launch`, it runs no audits\n * and takes no `Site`/inventory object — it reads the Lighthouse scores already stored\n * on each Websites row. DRAFTS ONLY; the M3 approve loop sends.\n *\n * One bad site must never abort the run: each site is wrapped in its own try/catch that\n * records an `error` result and continues.\n */\nexport async function announce(deps: AnnounceDeps): Promise<AnnounceResult> {\n const base = deps.base ?? openBase(readAirtableConfig());\n const now = deps.now ?? new Date();\n const writer = deps.reportMirror;\n\n const websites = await deps.roster();\n let targets = websites.filter((w) => w.status === \"maintained\");\n if (deps.site) {\n const wanted = siteSlug(deps.site);\n targets = targets.filter((w) => siteSlug(w.name) === wanted);\n }\n\n const period = now.toISOString().slice(0, 7);\n const results: AnnounceSiteResult[] = [];\n\n for (const w of targets) {\n try {\n const scores = scoresFromRow(w);\n if (scores === null) {\n results.push({ site: w.name, status: \"skipped-no-scores\" });\n continue;\n }\n\n // Refresh BEFORE the render so a queued announcement always has a current header\n // stored — the send re-reads the attachment, so a stale one here reaches the\n // client. Best-effort by construction: refreshHeaderImage returns false and never\n // throws, so a capture failure leaves the stored image and the draft continues.\n if (deps.refreshHeader !== false) {\n await refreshHeaderImage(w, deps.refreshHeader ?? {});\n }\n\n // Traffic + search snapshot over a ~30-day window ending now (fetchPeriodUsers derives\n // the equal-length previous window for the trend). Reuses the report pipeline's\n // soft-failing enrichment: GA/search unconfigured or an API error leaves the numbers\n // null and the email simply omits the traffic section — it never blocks the draft.\n const periodEnd = now;\n const periodStart = new Date(now.getTime() - GA_WINDOW_DAYS * 24 * 60 * 60 * 1000);\n const gaResult = await fetchGaUsers(w, periodStart, periodEnd);\n const searchResult = await fetchSearch(w, periodStart, periodEnd);\n const gaUsers = gaResult.value;\n const search = searchResult.value;\n const enrichment: ReportEnrichment = {\n ...(gaUsers ? { gaUsersCurrent: gaUsers.current, gaUsersPrevious: gaUsers.previous } : {}),\n ...(search ? { searchFoundPage1: search.foundOnPage1 } : {}),\n ...(search?.foundOnPage1 && search.position !== null\n ? { searchPosition: search.position }\n : {}),\n };\n\n // Record this site's GA/Search enrichment health for the per-site analytics-failure\n // signal (cockpit/digest) — exactly as the `--due` draft path does. Without this, an\n // announcement-time GA outage hides the traffic block with NO operator signal (it reads\n // identically to \"site has no GA configured\"). Set the timestamp on a soft-fail, clear\n // it on a clean enrichment so the signal self-heals. Best-effort: the column is\n // operator-added, so until it exists the write throws — which must not break the draft.\n if (readGaConfig() !== null && Boolean(w.ga4PropertyId || w.searchQuery)) {\n try {\n const fields = await updateAnalyticsHealth(\n base,\n w.id,\n gaResult.softFailed || searchResult.softFailed ? now.toISOString() : null,\n );\n // Mirror the EXACT FieldSet Airtable got — see draft.ts.\n await deps.siteMirror?.health(w.id, fields);\n } catch (e) {\n console.warn(`⚠ analytics-health write skipped for ${w.name}: ${(e as Error).message}`);\n }\n }\n\n // Dedupe: reuse an existing Announcement row for this (site, period) rather than\n // stacking a second draft. The reuse path refreshes the stored scores + traffic/search\n // (and Completed on) so the eventually-sent email — which reads the row — isn't stale.\n // The create path writes them via createDraft.\n let report;\n let statusKind: \"drafted\" | \"reused\";\n const existing = await findReportForPeriod(writer, w.id, \"Announcement\", period);\n if (existing) {\n await updateReportScores(base, existing.id, scores, now, enrichment);\n // Mirror the SAME refresh: the reuse path exists so the eventually-sent\n // email is not stale, and the console reads those numbers too.\n await writer.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: now.toISOString().slice(0, 10),\n ...(enrichment.gaUsersCurrent !== undefined\n ? { ga_users_current: enrichment.gaUsersCurrent }\n : {}),\n ...(enrichment.gaUsersPrevious !== undefined\n ? { ga_users_previous: enrichment.gaUsersPrevious }\n : {}),\n ...(enrichment.searchFoundPage1 !== undefined\n ? { search_found_page1: enrichment.searchFoundPage1 ? 1 : 0 }\n : {}),\n ...(enrichment.searchPosition !== undefined\n ? { search_position: enrichment.searchPosition }\n : {}),\n });\n report = existing;\n statusKind = \"reused\";\n } else {\n // #646 step 4: minted and written in Turso (`report_<ULID>`). The\n // Airtable Reports row this replaces is skipped, not written — see\n // `createReportDraft`.\n report = await createReportDraft(draftInputFor(w, scores, now, period, enrichment), {\n create: writer.create,\n });\n statusKind = \"drafted\";\n }\n\n const slug = siteSlug(w.name);\n const { html } = await renderReportHtml({\n siteName: w.name,\n siteUrl: w.url,\n reportType: \"Announcement\",\n completedOn: now,\n lighthouse: scores,\n gaUsersCurrent: gaUsers?.current,\n gaUsersPrevious: gaUsers?.previous,\n gaPeriodDays: GA_WINDOW_DAYS,\n searchPosition: search?.foundOnPage1 ? (search.position ?? undefined) : undefined,\n lastTestedDate: null,\n commentary: null,\n copy: resolveCopy(w),\n headerImageCid: `${slug}-header`,\n // cadence (the client's go-forward pace, \"None\" omitted) + default-on improvement\n // callouts. Shared with the send re-render via announcementSiteExtras so the sent\n // email matches this reviewed preview.\n ...announcementSiteExtras(w),\n });\n\n // A preview-upload hiccup must NOT fail the site — log and continue.\n try {\n await uploadAttachment(\n report.id,\n \"Rendered HTML\",\n html,\n `${slug}-${now.toISOString().slice(0, 10)}.html`,\n \"text/html\",\n );\n // Store the same body in Turso — the console's preview route reads it\n // there, not from the Airtable attachment (whose URL expires).\n await writer.body(report.id, html);\n } catch (uploadErr) {\n console.warn(\n `⚠ Announcement preview upload skipped for ${w.name}: ${\n uploadErr instanceof Error ? uploadErr.message : String(uploadErr)\n }`,\n );\n }\n\n // Critical: NOT wrapped — without queueing, the draft never enters the approve queue,\n // so a failure here must surface as an error result for the site. queueDraft also\n // supersedes any lower-tier (Maintenance/Testing) drafts queued for this site, and\n // stands down if an equal-or-higher report is already queued (single-queue rule).\n const queue = await queueDraft(\n base,\n { id: report.id, siteId: w.id, reportType: \"Announcement\" },\n writer,\n );\n\n const recipientMissing = !(w.reportRecipientsTo && w.reportRecipientsTo.trim());\n results.push({\n site: w.name,\n status: statusKind,\n reportId: report.id,\n recipientMissing,\n queued: queue.queued,\n });\n } catch (err) {\n results.push({\n site: w.name,\n status: \"error\",\n message: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n return { results };\n}\n\n/** Build the Announcement `DraftInput`. Announcements have no period window and no prior\n * maintenance test, so periodStart/periodEnd/completedOn all collapse to `now` and\n * `lastTestedDate` is null. The subject override gives the email a purpose-built line. */\nfunction draftInputFor(\n w: WebsiteRow,\n scores: LighthouseScores,\n now: Date,\n period: string,\n enrichment: ReportEnrichment,\n): DraftInput {\n return {\n reportId: `${w.name} — Announcement — ${now.toISOString().slice(0, 10)}`,\n siteId: w.id,\n reportType: \"Announcement\",\n period,\n periodStart: now,\n periodEnd: now,\n completedOn: now,\n lighthouse: scores,\n lastTestedDate: null,\n subjectOverride: defaultReportSubject({\n name: w.name,\n url: w.url,\n type: \"Announcement\",\n date: now,\n }),\n ...enrichment,\n };\n}\n","import { announce, type AnnounceResult, type AnnounceSiteResult } from \"../../recipes/announce.js\";\n\nexport type AnnounceCommandOptions = {\n cwd?: string;\n};\n\nfunction formatSiteResult(r: AnnounceSiteResult): string {\n if (r.status === \"skipped-no-scores\") return `[${r.site}] skipped-no-scores`;\n if (r.status === \"error\") return `[${r.site}] error: ${r.message}`;\n const note = r.recipientMissing ? \" ⚠ recipient missing\" : \"\";\n return `[${r.site}] ${r.status}${note}`;\n}\n\nexport function formatAnnounceResult(result: AnnounceResult): string {\n if (result.results.length === 0) return \"No maintenance sites to announce.\";\n return result.results.map(formatSiteResult).join(\"\\n\");\n}\n\n/**\n * `announce [site]` — Airtable-driven and fleet-wide. Draft the monthly-report\n * announcement email for every `maintenance` site (or one, when `site` is given) into\n * the M3 approve queue. Never sends; the operator approves each draft and the next send\n * run delivers it. Reads the Lighthouse scores already stored on each Websites row —\n * no audits are run.\n */\nexport async function runAnnounceCommand(\n site: string | undefined,\n _opts: AnnounceCommandOptions,\n): Promise<{ output: string; code: number }> {\n // #539 Phase 5: the create-side Turso dual-write is wired HERE rather than\n // inside `announce`, so a unit suite calling the recipe with a fake base can\n // never open a real libSQL handle (and, with TURSO_* exported locally, write\n // into production).\n const { makeReportMirror } = await import(\"../../reports/report-mirror.js\");\n const { makeSiteMirror } = await import(\"../../db/site-mirror.js\");\n const reportMirror = await makeReportMirror();\n const siteMirror = await makeSiteMirror();\n // #646 step 4: the fleet roster comes from Turso — an Airtable roster cannot\n // see a `site_<ULID>` site, so one created since step 3 was never announced.\n const { readFleetRoster } = await import(\"../../fleet/roster.js\");\n const result = await announce({\n ...(site ? { site } : {}),\n roster: () => readFleetRoster(),\n reportMirror,\n siteMirror,\n });\n const hadError = result.results.some((r) => r.status === \"error\");\n return { output: formatAnnounceResult(result), code: hadError ? 1 : 0 };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assetRef,
|
|
3
|
+
richText
|
|
4
|
+
} from "./chunk-UWJZXL2D.js";
|
|
1
5
|
import {
|
|
2
6
|
FROZEN_PAGE_TYPE,
|
|
3
7
|
extractMapConfig,
|
|
4
8
|
frozenPageCustomType,
|
|
5
9
|
makeIsMapMount
|
|
6
10
|
} from "./chunk-DRDAEELJ.js";
|
|
7
|
-
import {
|
|
8
|
-
assetRef,
|
|
9
|
-
richText
|
|
10
|
-
} from "./chunk-UWJZXL2D.js";
|
|
11
11
|
import {
|
|
12
12
|
CDN_HOSTS,
|
|
13
13
|
collectAssetUrls
|
|
@@ -4767,4 +4767,4 @@ ${lines.join("\n")}`,
|
|
|
4767
4767
|
export {
|
|
4768
4768
|
runBluxCommand
|
|
4769
4769
|
};
|
|
4770
|
-
//# sourceMappingURL=blux-
|
|
4770
|
+
//# sourceMappingURL=blux-NXQLMS4W.js.map
|
|
@@ -5,19 +5,19 @@ import {
|
|
|
5
5
|
appendSkipNotice,
|
|
6
6
|
fleetWorkdir,
|
|
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
|
bumpDeps
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
} from "./chunk-TV76A6GN.js";
|
|
15
|
+
import "./chunk-LUF52EIY.js";
|
|
16
16
|
import "./chunk-Z3JXLULD.js";
|
|
17
17
|
import "./chunk-LBYOLBW7.js";
|
|
18
18
|
import "./chunk-RARREDQE.js";
|
|
19
19
|
import "./chunk-XXTZBPUY.js";
|
|
20
|
-
import "./chunk-
|
|
20
|
+
import "./chunk-WMF5S2MB.js";
|
|
21
21
|
|
|
22
22
|
// src/cli/commands/bump-deps.ts
|
|
23
23
|
import { resolve } from "path";
|
|
@@ -57,4 +57,4 @@ async function runBumpDepsCommand(site, opts) {
|
|
|
57
57
|
export {
|
|
58
58
|
runBumpDepsCommand
|
|
59
59
|
};
|
|
60
|
-
//# sourceMappingURL=bump-deps-
|
|
60
|
+
//# sourceMappingURL=bump-deps-775VLDYB.js.map
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
withRecipe
|
|
3
|
-
} from "./chunk-SQ4FSNYL.js";
|
|
4
1
|
import {
|
|
5
2
|
ALL_TEMPLATES,
|
|
6
3
|
RENOVATE_ACTION_CONFIG,
|
|
@@ -8,10 +5,13 @@ import {
|
|
|
8
5
|
templatesByName,
|
|
9
6
|
withRenovatePinsFrom
|
|
10
7
|
} from "./chunk-SLFG7Y76.js";
|
|
8
|
+
import {
|
|
9
|
+
withRecipe
|
|
10
|
+
} from "./chunk-LUF52EIY.js";
|
|
11
11
|
import {
|
|
12
12
|
listTrackedFiles,
|
|
13
13
|
removeFromIndex
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-WMF5S2MB.js";
|
|
15
15
|
|
|
16
16
|
// src/recipes/sync-configs.ts
|
|
17
17
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -229,4 +229,4 @@ export {
|
|
|
229
229
|
planTemplateDiffs,
|
|
230
230
|
syncConfigs
|
|
231
231
|
};
|
|
232
|
-
//# sourceMappingURL=chunk-
|
|
232
|
+
//# sourceMappingURL=chunk-3RLDX3Q2.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
withRecipe
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LUF52EIY.js";
|
|
4
4
|
import {
|
|
5
5
|
bumpDep,
|
|
6
6
|
readPackageJson,
|
|
@@ -150,4 +150,4 @@ export {
|
|
|
150
150
|
selfCaretRange,
|
|
151
151
|
onboard
|
|
152
152
|
};
|
|
153
|
-
//# sourceMappingURL=chunk-
|
|
153
|
+
//# sourceMappingURL=chunk-6PGXKN6F.js.map
|
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
} from "./chunk-KJGKZLCT.js";
|
|
10
10
|
import {
|
|
11
11
|
applyReportTypeHeadline
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-LZMAG73U.js";
|
|
13
13
|
import {
|
|
14
14
|
siteSlug
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-ICSRWW5Q.js";
|
|
16
16
|
|
|
17
17
|
// src/reports/send/render-email.ts
|
|
18
18
|
function toInlineAttachment(a) {
|
|
@@ -143,4 +143,4 @@ export {
|
|
|
143
143
|
renderReportFromRow,
|
|
144
144
|
windowDays
|
|
145
145
|
};
|
|
146
|
-
//# sourceMappingURL=chunk-
|
|
146
|
+
//# sourceMappingURL=chunk-6THEZQLA.js.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/reports/report-row.ts
|
|
2
|
+
var REPORT_TYPES = ["Maintenance", "Testing", "Launch", "Announcement"];
|
|
3
|
+
function toReportType(raw) {
|
|
4
|
+
if (raw && REPORT_TYPES.includes(raw)) return raw;
|
|
5
|
+
if (raw)
|
|
6
|
+
console.warn(`[reports] unknown Report type ${JSON.stringify(raw)} \u2014 treating as Maintenance`);
|
|
7
|
+
return "Maintenance";
|
|
8
|
+
}
|
|
9
|
+
function isPendingApproval(r) {
|
|
10
|
+
return r.draftReady && !r.approvedToSend && r.sentAt === null;
|
|
11
|
+
}
|
|
12
|
+
function parseAutoEvidence(raw) {
|
|
13
|
+
if (typeof raw !== "string" || !raw.trim()) return null;
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = JSON.parse(raw);
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const [field, v] of Object.entries(parsed)) {
|
|
23
|
+
if (!v || typeof v !== "object") continue;
|
|
24
|
+
const o = v;
|
|
25
|
+
if (o.result !== "pass" && o.result !== "fail" && o.result !== "unknown" && o.result !== "n/a")
|
|
26
|
+
continue;
|
|
27
|
+
out[field] = {
|
|
28
|
+
result: o.result,
|
|
29
|
+
checkedAt: typeof o.checkedAt === "string" ? o.checkedAt : null,
|
|
30
|
+
note: typeof o.note === "string" ? o.note : ""
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return Object.keys(out).length > 0 ? out : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
toReportType,
|
|
38
|
+
isPendingApproval,
|
|
39
|
+
parseAutoEvidence
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=chunk-ASLDCWBF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/reports/report-row.ts"],"sourcesContent":["/**\n * The report-row model and its pure, vendor-neutral coercers.\n *\n * `ReportRow` is what BOTH readers return — `mapRow` over an Airtable record\n * (`src/reports/airtable/reports.ts`) and the Turso report reader in\n * `src/db/fleet-state.ts`. Moved out of the Airtable module in #539 Phase 6\n * step 1 (#646) so the Turso read path does not depend on the directory Phase 6\n * deletes. `src/reports/airtable/reports.ts` re-exports every name.\n */\nimport type { ReportType, LighthouseScores } from \"./types.js\";\nimport type { EvidenceRecord } from \"./auto-tick.js\";\n\nconst REPORT_TYPES: readonly ReportType[] = [\"Maintenance\", \"Testing\", \"Launch\", \"Announcement\"];\n\n/** Coerce the Airtable `Report type` (a single-select string) to a known\n * ReportType. A bare `as ReportType` cast is a compile-time lie: if the\n * single-select gains an unexpected option, the bad value flows to render.ts,\n * where `reportType === \"Launch\"` silently falls through to the Maintenance\n * template. Validate at the boundary; warn + default to \"Maintenance\" so an\n * unknown type is VISIBLE in the logs rather than silently mis-templated. */\nexport function toReportType(raw: string | undefined): ReportType {\n if (raw && (REPORT_TYPES as readonly string[]).includes(raw)) return raw as ReportType;\n if (raw)\n console.warn(`[reports] unknown Report type ${JSON.stringify(raw)} — treating as Maintenance`);\n return \"Maintenance\";\n}\n\nexport type DeliveryStatus = \"pending\" | \"delivered\" | \"bounced\" | \"complained\";\n\nexport type ReportRow = {\n id: string;\n reportId: string;\n siteId: string;\n reportType: ReportType;\n /** UTC `YYYY-MM` recurrence key (idempotency for search-before-create). Null on legacy rows. */\n period: string | null;\n periodStart: string | null;\n periodEnd: string | null;\n completedOn: string | null;\n lighthouse: LighthouseScores | null;\n gaUsersCurrent: number | null;\n gaUsersPrevious: number | null;\n searchFoundPage1: boolean | null;\n searchPosition: number | null;\n lastTestedDate: string | null;\n commentary: string | null;\n subjectOverride: string | null;\n draftReady: boolean;\n approvedToSend: boolean;\n sentAt: string | null;\n approvedAt: string | null;\n approvedBy: string | null;\n deliveryStatus: DeliveryStatus;\n renderedHtmlAttachment: { url: string; filename: string } | null;\n /** Read out of the Resend response and stored in a hidden field; needed for webhook reconciliation. */\n resendMessageId: string | null;\n /** The 12 operator-checklist checkboxes, keyed by their Airtable column name (ALL_CHECKLIST_FIELDS);\n * missing/false cells read false. Maintenance/Testing reports gate approve+send on the relevant\n * subset (see src/reports/checklist.ts). */\n checklist: Record<string, boolean>;\n /** Snapshot of the auto-tick evidence at draft time, keyed by checklist field → evidence\n * record. Null when the report predates auto-tick or carried no auto-checked items. This is the\n * health gate's source of truth: `isHealthGateClear`/`gatingHealth` (src/reports/checklist.ts)\n * read these evidence records, NOT the checklist booleans — and also drive the dashboard badges. */\n autoEvidence: Record<string, EvidenceRecord> | null;\n /** Logged send-anyway override (Phase 10). When `sendOverride` is true AND `overrideReason` is\n * non-empty, the health gate is bypassed for THIS report; `overrideBy`/`overrideAt` are the\n * audit trail (parallel to approvedBy/approvedAt). Missing cells read false/null. */\n sendOverride: boolean;\n overrideReason: string | null;\n overrideBy: string | null;\n overrideAt: string | null;\n};\n\n/**\n * The \"Ready for your yes\" gate: Draft ready ∧ ¬Approved to send ∧ Sent at BLANK.\n * The single source of truth for \"pending the operator's approval\" — `listPendingApproval`,\n * `runDigest`'s ready-list, the per-site dashboard, and the fleet cockpit all key off this\n * one predicate so the surfaces can't drift.\n */\nexport function isPendingApproval(r: ReportRow): boolean {\n return r.draftReady && !r.approvedToSend && r.sentAt === null;\n}\n\n/**\n * Parse the `Checklist auto-evidence` JSON field → a field→EvidenceRecord map, or null when\n * absent/malformed. Permissive on inner shape (it's display-only — a bad blob just yields null,\n * never throws), mirroring `parseNotifyRouting`.\n */\nexport function parseAutoEvidence(raw: unknown): Record<string, EvidenceRecord> | null {\n if (typeof raw !== \"string\" || !raw.trim()) return null;\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return null;\n // Validate each entry's inner shape (it drives display) — drop any record whose `result` isn't\n // one of the four literals, and coerce `note`/`checkedAt` to safe types. A garbage blob yields\n // null rather than a record that would render \"auto: undefined\" on the dashboard.\n const out: Record<string, EvidenceRecord> = {};\n for (const [field, v] of Object.entries(parsed as Record<string, unknown>)) {\n if (!v || typeof v !== \"object\") continue;\n const o = v as Record<string, unknown>;\n if (o.result !== \"pass\" && o.result !== \"fail\" && o.result !== \"unknown\" && o.result !== \"n/a\")\n continue;\n out[field] = {\n result: o.result,\n checkedAt: typeof o.checkedAt === \"string\" ? o.checkedAt : null,\n note: typeof o.note === \"string\" ? o.note : \"\",\n };\n }\n return Object.keys(out).length > 0 ? out : null;\n}\n"],"mappings":";AAYA,IAAM,eAAsC,CAAC,eAAe,WAAW,UAAU,cAAc;AAQxF,SAAS,aAAa,KAAqC;AAChE,MAAI,OAAQ,aAAmC,SAAS,GAAG,EAAG,QAAO;AACrE,MAAI;AACF,YAAQ,KAAK,iCAAiC,KAAK,UAAU,GAAG,CAAC,iCAA4B;AAC/F,SAAO;AACT;AAuDO,SAAS,kBAAkB,GAAuB;AACvD,SAAO,EAAE,cAAc,CAAC,EAAE,kBAAkB,EAAE,WAAW;AAC3D;AAOO,SAAS,kBAAkB,KAAqD;AACrF,MAAI,OAAO,QAAQ,YAAY,CAAC,IAAI,KAAK,EAAG,QAAO;AACnD,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AAI3E,QAAM,MAAsC,CAAC;AAC7C,aAAW,CAAC,OAAO,CAAC,KAAK,OAAO,QAAQ,MAAiC,GAAG;AAC1E,QAAI,CAAC,KAAK,OAAO,MAAM,SAAU;AACjC,UAAM,IAAI;AACV,QAAI,EAAE,WAAW,UAAU,EAAE,WAAW,UAAU,EAAE,WAAW,aAAa,EAAE,WAAW;AACvF;AACF,QAAI,KAAK,IAAI;AAAA,MACX,QAAQ,EAAE;AAAA,MACV,WAAW,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;AAAA,MAC3D,MAAM,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AAAA,IAC9C;AAAA,EACF;AACA,SAAO,OAAO,KAAK,GAAG,EAAE,SAAS,IAAI,MAAM;AAC7C;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
withRecipe
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LUF52EIY.js";
|
|
4
4
|
import {
|
|
5
5
|
readPackageJson,
|
|
6
6
|
writePackageJson
|
|
@@ -96,4 +96,4 @@ async function convertToPnpm(site, opts = {}) {
|
|
|
96
96
|
export {
|
|
97
97
|
convertToPnpm
|
|
98
98
|
};
|
|
99
|
-
//# sourceMappingURL=chunk-
|
|
99
|
+
//# sourceMappingURL=chunk-AYDQQBNG.js.map
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
mapWebsiteRecord,
|
|
6
6
|
scheduleColumnFor,
|
|
7
7
|
siteValueFor
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LF2TJN4X.js";
|
|
9
9
|
import {
|
|
10
10
|
parseAutoEvidence,
|
|
11
11
|
toReportType
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-ASLDCWBF.js";
|
|
13
13
|
import {
|
|
14
14
|
MAINTENANCE_CHECKLIST,
|
|
15
15
|
TESTING_CHECKLIST
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
toPrismicModelsVerdict,
|
|
23
23
|
toVerdict,
|
|
24
24
|
trimToNull
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-ICSRWW5Q.js";
|
|
26
26
|
|
|
27
27
|
// src/db/fleet-state.ts
|
|
28
28
|
import { sql } from "kysely";
|
|
@@ -199,6 +199,35 @@ async function mirrorHealthFields(db, siteId, fields) {
|
|
|
199
199
|
const res = await db.updateTable("site_health").set(patch).where("site_id", "=", siteId).executeTakeFirst();
|
|
200
200
|
return res.numUpdatedRows > 0n;
|
|
201
201
|
}
|
|
202
|
+
async function insertSiteRows(db, site, computedAt) {
|
|
203
|
+
await db.insertInto("sites").values({
|
|
204
|
+
id: site.id,
|
|
205
|
+
slug: site.slug,
|
|
206
|
+
name: site.name,
|
|
207
|
+
status: site.status,
|
|
208
|
+
url: site.url,
|
|
209
|
+
point_of_contact: site.pointOfContact,
|
|
210
|
+
git_repo: site.gitRepo,
|
|
211
|
+
require_turnstile: 0
|
|
212
|
+
}).execute();
|
|
213
|
+
await db.insertInto("site_health").values({ site_id: site.id }).execute();
|
|
214
|
+
await db.insertInto("site_schedule").values({
|
|
215
|
+
site_id: site.id,
|
|
216
|
+
next_maintenance_at: null,
|
|
217
|
+
next_testing_at: null,
|
|
218
|
+
computed_at: computedAt
|
|
219
|
+
}).execute();
|
|
220
|
+
}
|
|
221
|
+
async function updateSiteIdentity(db, siteId, patch) {
|
|
222
|
+
const set = {};
|
|
223
|
+
if (patch.name !== void 0) set.name = patch.name;
|
|
224
|
+
if (patch.url !== void 0) set.url = patch.url;
|
|
225
|
+
if (patch.pointOfContact !== void 0) set.point_of_contact = patch.pointOfContact;
|
|
226
|
+
if (patch.gitRepo !== void 0) set.git_repo = patch.gitRepo;
|
|
227
|
+
if (Object.keys(set).length === 0) return true;
|
|
228
|
+
const res = await db.updateTable("sites").set(set).where("id", "=", siteId).executeTakeFirst();
|
|
229
|
+
return res.numUpdatedRows > 0n;
|
|
230
|
+
}
|
|
202
231
|
async function mirrorScheduleFields(db, siteId, fields, computedAt) {
|
|
203
232
|
const patch = {};
|
|
204
233
|
for (const [field, value] of Object.entries(fields)) {
|
|
@@ -332,6 +361,10 @@ async function listReportsForSite(db, siteId) {
|
|
|
332
361
|
const rows = await db.selectFrom("reports").select(REPORT_LIST_COLUMNS).select(HAS_RENDERED_HTML).where("site_id", "=", siteId).orderBy("period_start", "desc").orderBy("id").execute();
|
|
333
362
|
return rows.map((r) => reportRowFromDb(r, r.has_rendered_html !== 0));
|
|
334
363
|
}
|
|
364
|
+
async function listSendableReports(db) {
|
|
365
|
+
const rows = await db.selectFrom("reports").select(REPORT_LIST_COLUMNS).select(HAS_RENDERED_HTML).where("draft_ready", "=", 1).where("approved_to_send", "=", 1).where("sent_at", "is", null).orderBy("period_start", "desc").orderBy("id").execute();
|
|
366
|
+
return rows.map((r) => reportRowFromDb(r, r.has_rendered_html !== 0));
|
|
367
|
+
}
|
|
335
368
|
async function mirrorReportPatch(db, reportId, patch) {
|
|
336
369
|
if (Object.keys(patch).length === 0) return true;
|
|
337
370
|
const res = await db.updateTable("reports").set(patch).where("id", "=", reportId).executeTakeFirst();
|
|
@@ -343,6 +376,9 @@ async function mirrorReportInsert(db, rec) {
|
|
|
343
376
|
void _rh;
|
|
344
377
|
await db.insertInto("reports").values(row).onConflict((oc) => oc.column("id").doUpdateSet(rowSansHtml)).execute();
|
|
345
378
|
}
|
|
379
|
+
async function insertReportRow(db, rec) {
|
|
380
|
+
await db.insertInto("reports").values(mapReportRecord(rec, null)).execute();
|
|
381
|
+
}
|
|
346
382
|
async function storeRenderedHtml(db, reportId, html) {
|
|
347
383
|
await db.updateTable("reports").set({ rendered_html: html }).where("id", "=", reportId).execute();
|
|
348
384
|
}
|
|
@@ -350,6 +386,10 @@ async function getReportById(db, id) {
|
|
|
350
386
|
const r = await db.selectFrom("reports").selectAll().where("id", "=", id).executeTakeFirst();
|
|
351
387
|
return r ? reportRowFromDb(r, r.rendered_html !== null) : null;
|
|
352
388
|
}
|
|
389
|
+
async function findReportByMessageId(db, messageId) {
|
|
390
|
+
const r = await db.selectFrom("reports").select(REPORT_LIST_COLUMNS).select(HAS_RENDERED_HTML).where("resend_message_id", "=", messageId).limit(1).executeTakeFirst();
|
|
391
|
+
return r ? reportRowFromDb(r, r.has_rendered_html !== 0) : null;
|
|
392
|
+
}
|
|
353
393
|
async function getReportHtml(db, id) {
|
|
354
394
|
const r = await db.selectFrom("reports").select(["rendered_html", "report_id"]).where("id", "=", id).executeTakeFirst();
|
|
355
395
|
if (!r || r.rendered_html === null) return null;
|
|
@@ -361,6 +401,8 @@ export {
|
|
|
361
401
|
mirrorSiteFields,
|
|
362
402
|
mirrorSiteInsert,
|
|
363
403
|
mirrorHealthFields,
|
|
404
|
+
insertSiteRows,
|
|
405
|
+
updateSiteIdentity,
|
|
364
406
|
mirrorScheduleFields,
|
|
365
407
|
getSiteBySlug,
|
|
366
408
|
siteRowExists,
|
|
@@ -369,10 +411,13 @@ export {
|
|
|
369
411
|
REPORT_LIST_COLUMNS,
|
|
370
412
|
listAllReports,
|
|
371
413
|
listReportsForSite,
|
|
414
|
+
listSendableReports,
|
|
372
415
|
mirrorReportPatch,
|
|
373
416
|
mirrorReportInsert,
|
|
417
|
+
insertReportRow,
|
|
374
418
|
storeRenderedHtml,
|
|
375
419
|
getReportById,
|
|
420
|
+
findReportByMessageId,
|
|
376
421
|
getReportHtml
|
|
377
422
|
};
|
|
378
|
-
//# sourceMappingURL=chunk-
|
|
423
|
+
//# sourceMappingURL=chunk-BINFZQ2T.js.map
|