@reddoorla/maintenance 0.87.0 → 0.88.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/dist/airtable-QG5ICI3Y.js +9 -0
  2. package/dist/{announce-I2Z4JN3I.js → announce-GKD5YJNB.js} +9 -9
  3. package/dist/{blux-ZGDSHI3U.js → blux-DN4MI532.js} +5 -5
  4. package/dist/{bump-deps-CGVELRLK.js → bump-deps-KGH5UYSF.js} +4 -4
  5. package/dist/{chunk-SDDPUAWH.js → chunk-6I6L3CMX.js} +2 -2
  6. package/dist/{chunk-ZBDZTJUO.js → chunk-7FFUBF4I.js} +5 -2
  7. package/dist/chunk-7FFUBF4I.js.map +1 -0
  8. package/dist/{chunk-HSI6Q7MI.js → chunk-7TMWBXX7.js} +159 -29
  9. package/dist/chunk-7TMWBXX7.js.map +1 -0
  10. package/dist/{chunk-DEWJMMG3.js → chunk-A2UNJIKH.js} +15 -7
  11. package/dist/chunk-A2UNJIKH.js.map +1 -0
  12. package/dist/{chunk-NCMZMLY7.js → chunk-ANJNTAMM.js} +3 -69
  13. package/dist/chunk-ANJNTAMM.js.map +1 -0
  14. package/dist/{chunk-3JQ2FO3D.js → chunk-B23FFWU4.js} +2 -2
  15. package/dist/{chunk-DJZWYIPP.js → chunk-BGTYPVLT.js} +1 -1
  16. package/dist/chunk-BGTYPVLT.js.map +1 -0
  17. package/dist/chunk-BRYXFH77.js +70 -0
  18. package/dist/chunk-BRYXFH77.js.map +1 -0
  19. package/dist/{chunk-NDWYJLRF.js → chunk-DGZWFI2G.js} +3 -3
  20. package/dist/{chunk-HHMMUR4P.js → chunk-GQDDWYU2.js} +45 -8
  21. package/dist/chunk-GQDDWYU2.js.map +1 -0
  22. package/dist/{chunk-BBUSGEES.js → chunk-J3WEZGIN.js} +6 -6
  23. package/dist/{chunk-BNM5POSJ.js → chunk-JRHIFA5R.js} +2 -2
  24. package/dist/{chunk-TD2NNXLZ.js → chunk-K6SLF6GI.js} +5 -5
  25. package/dist/{chunk-MT2JINSS.js → chunk-KJGKZLCT.js} +3 -3
  26. package/dist/{chunk-NXNPDCF3.js → chunk-KMCZA7CH.js} +11 -1
  27. package/dist/chunk-KMCZA7CH.js.map +1 -0
  28. package/dist/{chunk-3SF7D4DY.js → chunk-MFHS7IQ2.js} +36 -1
  29. package/dist/{chunk-3SF7D4DY.js.map → chunk-MFHS7IQ2.js.map} +1 -1
  30. package/dist/{chunk-SNSTJLQW.js → chunk-NO74DXDP.js} +48 -2
  31. package/dist/chunk-NO74DXDP.js.map +1 -0
  32. package/dist/{chunk-C6B4RHXR.js → chunk-O3GT46R2.js} +8 -2
  33. package/dist/chunk-O3GT46R2.js.map +1 -0
  34. package/dist/{chunk-C6CLRKPV.js → chunk-PR343K7F.js} +2 -2
  35. package/dist/{chunk-CROKUHJB.js → chunk-Q47DU255.js} +4 -4
  36. package/dist/{chunk-EVBCK2KC.js → chunk-RARREDQE.js} +2 -1
  37. package/dist/{chunk-EVBCK2KC.js.map → chunk-RARREDQE.js.map} +1 -1
  38. package/dist/chunk-RUPPWAD6.js +7 -0
  39. package/dist/chunk-RUPPWAD6.js.map +1 -0
  40. package/dist/{chunk-VSBFM7DS.js → chunk-SJOXWJ5Y.js} +3 -3
  41. package/dist/{chunk-HEWSRXYV.js → chunk-ULHPALI5.js} +3 -3
  42. package/dist/{chunk-XUGSZP7Z.js → chunk-W45FZEGE.js} +2 -2
  43. package/dist/{chunk-JHGHLKFS.js → chunk-WS6NU475.js} +2 -2
  44. package/dist/{chunk-ZGBEIWSC.js → chunk-XP5WM2LV.js} +5 -5
  45. package/dist/{chunk-6TWWY7ZU.js → chunk-Z3JXLULD.js} +2 -2
  46. package/dist/cli/bin.js +27 -27
  47. package/dist/cli/bin.js.map +1 -1
  48. package/dist/cli/commands/audit.d.ts +4 -0
  49. package/dist/cli/commands/audit.js +16 -13
  50. package/dist/cli/commands/audit.js.map +1 -1
  51. package/dist/client-JYKPVCJX.js +11 -0
  52. package/dist/configs/playwright-a11y.js +1 -1
  53. package/dist/{convert-to-pnpm-QFJOEP5V.js → convert-to-pnpm-NWB5FCQ2.js} +4 -4
  54. package/dist/{db-UXNIZ2RG.js → db-V6UIS433.js} +121 -26
  55. package/dist/db-V6UIS433.js.map +1 -0
  56. package/dist/{digest-FW6QMZZJ.js → digest-2JEAEJK6.js} +53 -29
  57. package/dist/digest-2JEAEJK6.js.map +1 -0
  58. package/dist/digest-state-CRZRNGDI.js +25 -0
  59. package/dist/digest-state-CRZRNGDI.js.map +1 -0
  60. package/dist/digest-state-F37XUCS5.js +13 -0
  61. package/dist/{dump-P6EBSIOH.js → dump-EPL7AZFR.js} +38 -13
  62. package/dist/dump-EPL7AZFR.js.map +1 -0
  63. package/dist/{email-KJXASVH5.js → email-SL7464YI.js} +16 -6
  64. package/dist/{email-KJXASVH5.js.map → email-SL7464YI.js.map} +1 -1
  65. package/dist/{ensure-site-4DCEBAPM.js → ensure-site-FU6527OU.js} +2 -2
  66. package/dist/{fleet-state-7TJ2GYT2.js → fleet-state-R5LU7IC3.js} +4 -2
  67. package/dist/{forms-notify-target-2EAWCWBW.js → forms-notify-target-SGDGXXK5.js} +6 -6
  68. package/dist/{github-signals-E2WPYTBK.js → github-signals-GWGCKEE2.js} +7 -6
  69. package/dist/{github-signals-E2WPYTBK.js.map → github-signals-GWGCKEE2.js.map} +1 -1
  70. package/dist/{header-image-6IC53W27.js → header-image-7A7XLVEU.js} +2 -2
  71. package/dist/{health-endpoint-RFXL2F4A.js → health-endpoint-UVX6R5N5.js} +4 -4
  72. package/dist/{health-mirror-L5JBEJVA.js → health-mirror-GCDN7BJL.js} +11 -6
  73. package/dist/health-mirror-GCDN7BJL.js.map +1 -0
  74. package/dist/index.js +128 -88
  75. package/dist/index.js.map +1 -1
  76. package/dist/{init-XRAQZYID.js → init-FGTYNGUI.js} +9 -9
  77. package/dist/{launch-IX4Y5JJC.js → launch-5H7LPEGP.js} +12 -11
  78. package/dist/{launch-IX4Y5JJC.js.map → launch-5H7LPEGP.js.map} +1 -1
  79. package/dist/migrate-YITCXBLS.js +7 -0
  80. package/dist/{notify-C645PGD5.js → notify-4HP4GZ2H.js} +5 -5
  81. package/dist/{onboard-O4IAHJMO.js → onboard-BKCYBAVW.js} +4 -4
  82. package/dist/{orchestrate-YLWQ6KK2.js → orchestrate-JNKCWU5K.js} +9 -9
  83. package/dist/{parity-56KA2DGR.js → parity-NBYZUBFU.js} +4 -2
  84. package/dist/pdf-N5QLQ5ZQ.js +38 -0
  85. package/dist/pdf-N5QLQ5ZQ.js.map +1 -0
  86. package/dist/{pipeline-3AANZFCR.js → pipeline-WYTXSLHX.js} +3 -3
  87. package/dist/{preflight-TNORKGCX.js → preflight-IMTYDDD4.js} +10 -10
  88. package/dist/{prismic-ci-G2AK53VL.js → prismic-ci-MY2U6NN7.js} +4 -4
  89. package/dist/{prismic-models-O6Z3OK3A.js → prismic-models-NJN5JORF.js} +6 -6
  90. package/dist/prospect/types.d.ts +14 -0
  91. package/dist/{prospect-audit-KSXUNQ5J.js → prospect-audit-JMBRVXSG.js} +32 -13
  92. package/dist/prospect-audit-JMBRVXSG.js.map +1 -0
  93. package/dist/render-VX72AESY.js +10 -0
  94. package/dist/{render-from-row-BWSOIYDS.js → render-from-row-DOYJDO2L.js} +5 -5
  95. package/dist/{replay-C2LXMJZP.js → replay-4S6ENYCH.js} +3 -3
  96. package/dist/{report-R3E3Y7HR.js → report-TALY5P3L.js} +17 -17
  97. package/dist/{report-mirror-4MNORYQ4.js → report-mirror-AAK3MXHY.js} +12 -6
  98. package/dist/report-mirror-AAK3MXHY.js.map +1 -0
  99. package/dist/schema-75PJECAE.js +18 -0
  100. package/dist/schema-75PJECAE.js.map +1 -0
  101. package/dist/{self-updating-UCIWESAX.js → self-updating-NTCCWJYZ.js} +4 -4
  102. package/dist/{selftest-VGHWPN72.js → selftest-NZTR4SM2.js} +11 -11
  103. package/dist/{site-mirror-NIREX5ME.js → site-mirror-Q763PLGG.js} +17 -7
  104. package/dist/site-mirror-Q763PLGG.js.map +1 -0
  105. package/dist/{smoke-suite-U3UKD3NS.js → smoke-suite-4JFZZYVW.js} +4 -4
  106. package/dist/{spam-classifier-XLBFCO7G.js → spam-classifier-T5H4VTP6.js} +4 -2
  107. package/dist/{submissions-IQEJX6FK.js → submissions-TEHPBSZJ.js} +4 -4
  108. package/dist/{svelte-codemods-GOKXTBHW.js → svelte-codemods-QAPDZC6F.js} +4 -4
  109. package/dist/{sync-CTUVXMCK.js → sync-GBFV45WX.js} +4 -2
  110. package/dist/{sync-CTUVXMCK.js.map → sync-GBFV45WX.js.map} +1 -1
  111. package/dist/{sync-configs-SPTLFJCC.js → sync-configs-34JIA6KZ.js} +4 -4
  112. package/dist/{upgrade-W6ZR3FJH.js → upgrade-IR2OO3KP.js} +4 -4
  113. package/dist/{webhook-VOGOW3W6.js → webhook-XM6EV4XT.js} +2 -2
  114. package/dist/{write-audits-to-airtable-CMKHGJNQ.js → write-audits-to-airtable-OBHGR7DQ.js} +5 -2
  115. package/dist/write-audits-to-airtable-OBHGR7DQ.js.map +1 -0
  116. package/package.json +1 -1
  117. package/dist/airtable-ZBUPQJSC.js +0 -9
  118. package/dist/chunk-C6B4RHXR.js.map +0 -1
  119. package/dist/chunk-DEWJMMG3.js.map +0 -1
  120. package/dist/chunk-DJZWYIPP.js.map +0 -1
  121. package/dist/chunk-HHMMUR4P.js.map +0 -1
  122. package/dist/chunk-HSI6Q7MI.js.map +0 -1
  123. package/dist/chunk-NCMZMLY7.js.map +0 -1
  124. package/dist/chunk-NXNPDCF3.js.map +0 -1
  125. package/dist/chunk-SNSTJLQW.js.map +0 -1
  126. package/dist/chunk-ZBDZTJUO.js.map +0 -1
  127. package/dist/client-AUE3XUPO.js +0 -11
  128. package/dist/db-UXNIZ2RG.js.map +0 -1
  129. package/dist/digest-FW6QMZZJ.js.map +0 -1
  130. package/dist/dump-P6EBSIOH.js.map +0 -1
  131. package/dist/health-mirror-L5JBEJVA.js.map +0 -1
  132. package/dist/migrate-GQH237A6.js +0 -7
  133. package/dist/prospect-audit-KSXUNQ5J.js.map +0 -1
  134. package/dist/render-IFOGFB5T.js +0 -10
  135. package/dist/report-mirror-4MNORYQ4.js.map +0 -1
  136. package/dist/site-mirror-NIREX5ME.js.map +0 -1
  137. /package/dist/{airtable-ZBUPQJSC.js.map → airtable-QG5ICI3Y.js.map} +0 -0
  138. /package/dist/{announce-I2Z4JN3I.js.map → announce-GKD5YJNB.js.map} +0 -0
  139. /package/dist/{blux-ZGDSHI3U.js.map → blux-DN4MI532.js.map} +0 -0
  140. /package/dist/{bump-deps-CGVELRLK.js.map → bump-deps-KGH5UYSF.js.map} +0 -0
  141. /package/dist/{chunk-SDDPUAWH.js.map → chunk-6I6L3CMX.js.map} +0 -0
  142. /package/dist/{chunk-3JQ2FO3D.js.map → chunk-B23FFWU4.js.map} +0 -0
  143. /package/dist/{chunk-NDWYJLRF.js.map → chunk-DGZWFI2G.js.map} +0 -0
  144. /package/dist/{chunk-BBUSGEES.js.map → chunk-J3WEZGIN.js.map} +0 -0
  145. /package/dist/{chunk-BNM5POSJ.js.map → chunk-JRHIFA5R.js.map} +0 -0
  146. /package/dist/{chunk-TD2NNXLZ.js.map → chunk-K6SLF6GI.js.map} +0 -0
  147. /package/dist/{chunk-MT2JINSS.js.map → chunk-KJGKZLCT.js.map} +0 -0
  148. /package/dist/{chunk-C6CLRKPV.js.map → chunk-PR343K7F.js.map} +0 -0
  149. /package/dist/{chunk-CROKUHJB.js.map → chunk-Q47DU255.js.map} +0 -0
  150. /package/dist/{chunk-VSBFM7DS.js.map → chunk-SJOXWJ5Y.js.map} +0 -0
  151. /package/dist/{chunk-HEWSRXYV.js.map → chunk-ULHPALI5.js.map} +0 -0
  152. /package/dist/{chunk-XUGSZP7Z.js.map → chunk-W45FZEGE.js.map} +0 -0
  153. /package/dist/{chunk-JHGHLKFS.js.map → chunk-WS6NU475.js.map} +0 -0
  154. /package/dist/{chunk-ZGBEIWSC.js.map → chunk-XP5WM2LV.js.map} +0 -0
  155. /package/dist/{chunk-6TWWY7ZU.js.map → chunk-Z3JXLULD.js.map} +0 -0
  156. /package/dist/{client-AUE3XUPO.js.map → client-JYKPVCJX.js.map} +0 -0
  157. /package/dist/{convert-to-pnpm-QFJOEP5V.js.map → convert-to-pnpm-NWB5FCQ2.js.map} +0 -0
  158. /package/dist/{fleet-state-7TJ2GYT2.js.map → digest-state-F37XUCS5.js.map} +0 -0
  159. /package/dist/{ensure-site-4DCEBAPM.js.map → ensure-site-FU6527OU.js.map} +0 -0
  160. /package/dist/{migrate-GQH237A6.js.map → fleet-state-R5LU7IC3.js.map} +0 -0
  161. /package/dist/{forms-notify-target-2EAWCWBW.js.map → forms-notify-target-SGDGXXK5.js.map} +0 -0
  162. /package/dist/{header-image-6IC53W27.js.map → header-image-7A7XLVEU.js.map} +0 -0
  163. /package/dist/{health-endpoint-RFXL2F4A.js.map → health-endpoint-UVX6R5N5.js.map} +0 -0
  164. /package/dist/{init-XRAQZYID.js.map → init-FGTYNGUI.js.map} +0 -0
  165. /package/dist/{notify-C645PGD5.js.map → migrate-YITCXBLS.js.map} +0 -0
  166. /package/dist/{orchestrate-YLWQ6KK2.js.map → notify-4HP4GZ2H.js.map} +0 -0
  167. /package/dist/{onboard-O4IAHJMO.js.map → onboard-BKCYBAVW.js.map} +0 -0
  168. /package/dist/{parity-56KA2DGR.js.map → orchestrate-JNKCWU5K.js.map} +0 -0
  169. /package/dist/{pipeline-3AANZFCR.js.map → parity-NBYZUBFU.js.map} +0 -0
  170. /package/dist/{render-IFOGFB5T.js.map → pipeline-WYTXSLHX.js.map} +0 -0
  171. /package/dist/{preflight-TNORKGCX.js.map → preflight-IMTYDDD4.js.map} +0 -0
  172. /package/dist/{prismic-ci-G2AK53VL.js.map → prismic-ci-MY2U6NN7.js.map} +0 -0
  173. /package/dist/{prismic-models-O6Z3OK3A.js.map → prismic-models-NJN5JORF.js.map} +0 -0
  174. /package/dist/{render-from-row-BWSOIYDS.js.map → render-VX72AESY.js.map} +0 -0
  175. /package/dist/{spam-classifier-XLBFCO7G.js.map → render-from-row-DOYJDO2L.js.map} +0 -0
  176. /package/dist/{replay-C2LXMJZP.js.map → replay-4S6ENYCH.js.map} +0 -0
  177. /package/dist/{report-R3E3Y7HR.js.map → report-TALY5P3L.js.map} +0 -0
  178. /package/dist/{self-updating-UCIWESAX.js.map → self-updating-NTCCWJYZ.js.map} +0 -0
  179. /package/dist/{selftest-VGHWPN72.js.map → selftest-NZTR4SM2.js.map} +0 -0
  180. /package/dist/{smoke-suite-U3UKD3NS.js.map → smoke-suite-4JFZZYVW.js.map} +0 -0
  181. /package/dist/{write-audits-to-airtable-CMKHGJNQ.js.map → spam-classifier-T5H4VTP6.js.map} +0 -0
  182. /package/dist/{submissions-IQEJX6FK.js.map → submissions-TEHPBSZJ.js.map} +0 -0
  183. /package/dist/{svelte-codemods-GOKXTBHW.js.map → svelte-codemods-QAPDZC6F.js.map} +0 -0
  184. /package/dist/{sync-configs-SPTLFJCC.js.map → sync-configs-34JIA6KZ.js.map} +0 -0
  185. /package/dist/{upgrade-W6ZR3FJH.js.map → upgrade-IR2OO3KP.js.map} +0 -0
  186. /package/dist/{webhook-VOGOW3W6.js.map → webhook-XM6EV4XT.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli/commands/prospect-audit.ts","../src/prospect/report-url.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport { hostnameOf, isHttpUrl, isPrivateOrLoopbackHost } from \"../../util/url.js\";\nimport { reportUrl, reportPrintUrl } from \"../../prospect/report-url.js\";\nimport type { ProspectAuditStatus } from \"../../db/prospect-audits.js\";\nimport type { PipelineDeps, StageName } from \"../../prospect/pipeline.js\";\nimport type { ProspectAuditResult } from \"../../prospect/types.js\";\nimport type { SendAuditEmailResult } from \"../../prospect/email.js\";\n\nexport type ProspectAuditCliOptions = {\n business?: string;\n /** Comma-separated competitor domains. */\n competitors?: string;\n /** cac sets this false for `--no-probes`. */\n probes?: boolean;\n out?: string;\n json?: boolean;\n /**\n * Email the internal sheet (scores, what wasn't measured and why, top\n * fixes, and the shareable link) to `PROSPECT_AUDIT_RECIPIENTS`, once the\n * report is rendered and the `--out`/Turso delivery attempts finish. A\n * missing/empty recipients var, or a Resend failure, is a warning — never a\n * crash — because by the time this runs the audit is already delivered (or\n * recovered) by the code above it.\n */\n email?: boolean;\n /** Test seam: injected pipeline deps. Never set from the CLI. */\n deps?: PipelineDeps;\n};\n\ntype RecoveryWrite = { htmlPath: string; jsonPath: string };\n\nfunction fail(message: string): { output: string; code: number } {\n return { output: message, code: 2 };\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\nfunction scoreLine(label: string, value: number | null): string {\n return `${label.padEnd(14)} ${value === null ? \"not measured\" : String(value).padStart(3)}`;\n}\n\n/** Item 3: the `status` column exists to answer, without deserializing the\n * whole result_json, whether this was a clean run or a degraded one. crawl\n * is excluded — it's the one fatal stage (see pipeline.ts), so by the time a\n * ProspectAuditResult exists it was always ok. Every other stage's\n * StageResult already collapses \"failed\" and \"deliberately skipped\"\n * (--no-probes, the checks→analyze cascade) into the same `ok: false` — both\n * mean the report has a \"not measured\" section, so both count as partial\n * here too. */\nfunction auditStatus(result: ProspectAuditResult): ProspectAuditStatus {\n const allStagesOk =\n result.checks.ok && result.lighthouse.ok && result.analyze.ok && result.probes.ok;\n return allStagesOk ? \"complete\" : \"partial\";\n}\n\n/** Filesystem-safe, never empty. Collapses anything outside [a-z0-9.-] to a\n * single dash so a hostname becomes a sane filename fragment. */\nfunction slugify(s: string): string {\n const slug = s\n .toLowerCase()\n .replace(/[^a-z0-9.-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return slug || \"prospect\";\n}\n\n/**\n * Last-resort write when NEITHER `--out` nor the database persist made it —\n * the one situation a paid, already-run audit must never end in silently. Lands\n * in `reports/`, mirroring header-image.ts's own convention for CLI-written\n * artifacts (mkdir -p, resolved against cwd), rather than a bare file dropped\n * into whatever directory the operator happened to be standing in when they\n * ran the command — often unrelated to this repo at all in fleet-tool usage.\n */\nasync function writeRecoveryFiles(\n result: ProspectAuditResult,\n html: string,\n): Promise<RecoveryWrite> {\n const dir = resolve(\"reports\");\n await mkdir(dir, { recursive: true });\n const stamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const base = `prospect-audit-${slugify(hostnameOf(result.url))}-${stamp}`;\n const htmlPath = resolve(dir, `${base}.html`);\n const jsonPath = resolve(dir, `${base}.json`);\n await writeFile(htmlPath, html, \"utf-8\");\n await writeFile(jsonPath, JSON.stringify(result, null, 2), \"utf-8\");\n return { htmlPath, jsonPath };\n}\n\nfunction summarize(\n result: ProspectAuditResult,\n link: string | null,\n file: string | null,\n warnings: string[],\n recovery: RecoveryWrite | null,\n email: SendAuditEmailResult | null,\n): string {\n const lines = [\n `Prospect audit — ${result.businessName ?? result.url}`,\n \"\",\n scoreLine(\"Findability\", result.scores.findability),\n scoreLine(\"Readability\", result.scores.readability),\n scoreLine(\"Answers\", result.scores.answers),\n scoreLine(\"AI Visibility\", result.scores.aiVisibility),\n ];\n for (const [name, stage] of [\n [\"checks\", result.checks],\n [\"lighthouse\", result.lighthouse],\n [\"analyze\", result.analyze],\n [\"probes\", result.probes],\n ] as const) {\n if (!stage.ok) lines.push(` ! ${name} not measured — ${stage.error}`);\n }\n if (file) lines.push(\"\", `Report written to ${file}`);\n if (link) lines.push(\"\", `Shareable link: ${link}`);\n if (email?.sent) {\n lines.push(\"\", `Emailed to ${email.recipients.join(\", \")} (${email.messageId})`);\n }\n for (const w of warnings) lines.push(\"\", `! ${w}`);\n if (recovery) {\n lines.push(\n \"\",\n \"Neither --out nor the database took this report — wrote a recovery copy instead:\",\n ` ${recovery.htmlPath}`,\n ` ${recovery.jsonPath}`,\n );\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Run one prospect audit end to end. Progress goes to stderr so `--json` stdout\n * stays pipeable. Persistence needs Turso; without it `--out` is mandatory,\n * because an audit nobody can read afterwards is just a bill.\n *\n * By the time `runProspectAudit` resolves, the money is already spent — only\n * the crawl is fatal, every other stage degrades rather than throwing. So the\n * `--out` write and the database persist below are each wrapped in their own\n * try/catch and attempted independently: a bad `--out` path must not cost a\n * working Turso persist (or vice versa), and if BOTH fail — or persist fails\n * with no `--out` given at all — a recovery copy goes to `reports/` rather\n * than the run ending with nothing anywhere. `--json` only changes the SHAPE\n * of stdout; it still runs the same `--out`/persist/recovery logic beneath it.\n */\nexport async function runProspectAuditCommand(\n url: string,\n opts: ProspectAuditCliOptions,\n): Promise<{ output: string; code: number }> {\n if (!isHttpUrl(url)) {\n return fail(`\"${url}\" is not a URL. Pass the full address, e.g. https://example.com`);\n }\n // #612 review: this validated only the SHAPE. The sole private-address guard\n // was in `triggerProspectAudit` — one layer at the far end of the chain — so\n // anyone running the CLI directly, or any future second caller, had none.\n // `crawlSite` now refuses too; this keeps the refusal a clean message instead\n // of a thrown crawl error.\n if (isPrivateOrLoopbackHost(new URL(url).hostname)) {\n return fail(`\"${url}\" is a private or loopback address — refusing to audit it.`);\n }\n const canPersist = Boolean(process.env.TURSO_DATABASE_URL);\n if (!canPersist && !opts.out) {\n return fail(\n \"No TURSO_DATABASE_URL, so the report cannot be saved or shared. Re-run with --out <file>, or set the Turso credentials.\",\n );\n }\n\n const { runProspectAudit } = await import(\"../../prospect/pipeline.js\");\n const onStage = (name: StageName, status: \"start\" | \"ok\" | \"fail\", detail?: string): void => {\n if (status === \"start\") console.error(`… ${name}`);\n else if (status === \"ok\") console.error(`✓ ${name}`);\n else console.error(`! ${name} — ${detail ?? \"failed\"}`);\n };\n\n const business = opts.business?.trim();\n const result = await runProspectAudit(\n url,\n {\n ...(business ? { business } : {}),\n ...(opts.competitors\n ? {\n competitors: opts.competitors\n .split(\",\")\n .map((c) => c.trim())\n .filter(Boolean),\n }\n : {}),\n ...(opts.probes === false ? { probes: false } : {}),\n },\n { ...(opts.deps ?? {}), onStage },\n );\n\n const { renderProspectReport } = await import(\"../../prospect/render.js\");\n const html = renderProspectReport(result);\n\n const warnings: string[] = [];\n\n let file: string | null = null;\n if (opts.out) {\n try {\n await writeFile(opts.out, html, \"utf-8\");\n file = opts.out;\n } catch (err) {\n warnings.push(`Could not write --out (${opts.out}): ${errorMessage(err)}`);\n }\n }\n\n let link: string | null = null;\n let token: string | null = null;\n let auditId: string | null = null;\n if (canPersist) {\n try {\n const { openDb, readDbConfig } = await import(\"../../db/client.js\");\n const { createProspectAudit } = await import(\"../../db/prospect-audits.js\");\n const db = await openDb(readDbConfig());\n const created = await createProspectAudit(db, {\n url: result.url,\n // Map at the boundary: ProspectAuditResult.businessName is the field\n // name (Item 2 — it's a resolved NAME, not a description); the\n // `prospect_audits.business` column keeps its existing name, since\n // renaming a column needs its own migration for no benefit here.\n business: result.businessName,\n status: auditStatus(result),\n resultJson: JSON.stringify(result),\n });\n auditId = created.id;\n token = created.token;\n link = reportUrl(token);\n } catch (err) {\n warnings.push(`Could not save to the database: ${errorMessage(err)}`);\n }\n }\n\n const delivered = file !== null || link !== null;\n const recovery = delivered ? null : await writeRecoveryFiles(result, html);\n const code = delivered ? 0 : 1;\n\n // The audit is already delivered (or recovered) above — an email failure\n // must never take that away. Same independent try/catch shape as the\n // --out write and the Turso persist: record a warning, keep going, never\n // touch `code`. Dynamically imported for the same reason db/client.js and\n // prospect/render.js are above: it reaches `resend` (a devDependency) and\n // must stay out of bin.js's static import graph (see bin.ts's\n // central-dep-blocker comment).\n // The PDF leave-behind, printed from the website's print route — a document\n // designed for paper, not the interactive report with its evidence folded\n // away behind disclosures.\n //\n // Best-effort by design. Every other stage in this pipeline degrades rather\n // than throwing, and an attachment is not worth losing a delivered report\n // over: if this fails the email still goes, with the link, and a warning says\n // the PDF is missing. It also needs a token — with no persisted report there\n // is no page to print.\n let pdf: Buffer | null = null;\n if (opts.email && token) {\n try {\n const { renderReportPdf } = await import(\"../../prospect/pdf.js\");\n pdf = await renderReportPdf(reportPrintUrl(token));\n } catch (err) {\n warnings.push(`Could not attach the PDF: ${errorMessage(err)}`);\n }\n }\n\n let email: SendAuditEmailResult | null = null;\n if (opts.email) {\n try {\n const { sendAuditEmail, parseProspectAuditRecipients } =\n await import(\"../../prospect/email.js\");\n const recipients = parseProspectAuditRecipients(process.env.PROSPECT_AUDIT_RECIPIENTS);\n email = await sendAuditEmail(result, { link, recipients, auditId, pdf });\n if (!email.sent) {\n warnings.push(`Could not email the audit sheet: ${email.reason}`);\n }\n } catch (err) {\n warnings.push(`Could not email the audit sheet: ${errorMessage(err)}`);\n }\n }\n\n if (opts.json) {\n return {\n output: JSON.stringify(\n { result, out: file, link, token, recovery, warnings, email },\n null,\n 2,\n ),\n code,\n };\n }\n\n return { output: summarize(result, link, file, warnings, recovery, email), code };\n}\n","/** Where the prospect-facing report lives now: a real, branded route on the\n * marketing site rather than generated HTML on the ops app's domain. */\nexport const DEFAULT_REPORT_BASE_URL = \"https://reddoorla.com\";\n\n/**\n * Resolve the public report origin.\n *\n * Deliberately its own variable rather than reusing `DASHBOARD_BASE_URL`. That\n * one addresses operators and points at the ops app; this one addresses a\n * prospect and points at reddoorla.com. They are different audiences on\n * different domains, and sharing a variable would mean one of them silently\n * moving the day the other is repointed.\n */\nexport function resolveReportBaseUrl(raw: string | undefined): string {\n return (raw?.trim() || DEFAULT_REPORT_BASE_URL).replace(/\\/$/, \"\");\n}\n\n/** The link a prospect opens. */\nexport function reportUrl(token: string, raw = process.env.REPORT_BASE_URL): string {\n return `${resolveReportBaseUrl(raw)}/audit/${token}`;\n}\n\n/** The print-designed variant, which the runner renders to a PDF. Same token,\n * same guards — a separate document, not a stylesheet over the page. */\nexport function reportPrintUrl(token: string, raw = process.env.REPORT_BASE_URL): string {\n return `${reportUrl(token, raw)}/print`;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,10 @@
1
+ import {
2
+ renderProspectReport
3
+ } from "./chunk-A2UNJIKH.js";
4
+ import "./chunk-7TMWBXX7.js";
5
+ import "./chunk-BGTYPVLT.js";
6
+ import "./chunk-RARREDQE.js";
7
+ export {
8
+ renderProspectReport
9
+ };
10
+ //# sourceMappingURL=render-VX72AESY.js.map
@@ -2,16 +2,16 @@ import {
2
2
  renderReportFromRow,
3
3
  requireLighthouse,
4
4
  windowDays
5
- } from "./chunk-ZGBEIWSC.js";
5
+ } from "./chunk-XP5WM2LV.js";
6
6
  import "./chunk-GECESMVS.js";
7
- import "./chunk-MT2JINSS.js";
8
- import "./chunk-DJZWYIPP.js";
9
7
  import "./chunk-5UINJNAJ.js";
8
+ import "./chunk-KJGKZLCT.js";
9
+ import "./chunk-BGTYPVLT.js";
10
10
  import "./chunk-7NYN2P6I.js";
11
- import "./chunk-EVBCK2KC.js";
11
+ import "./chunk-RARREDQE.js";
12
12
  export {
13
13
  renderReportFromRow,
14
14
  requireLighthouse,
15
15
  windowDays
16
16
  };
17
- //# sourceMappingURL=render-from-row-BWSOIYDS.js.map
17
+ //# sourceMappingURL=render-from-row-DOYJDO2L.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ingestSubmission
3
- } from "./chunk-BNM5POSJ.js";
4
- import "./chunk-SNSTJLQW.js";
3
+ } from "./chunk-JRHIFA5R.js";
4
+ import "./chunk-NO74DXDP.js";
5
5
  import "./chunk-2OP4JKIQ.js";
6
6
 
7
7
  // src/db/deadletter.ts
@@ -63,4 +63,4 @@ async function replayOne(db, deps, row) {
63
63
  export {
64
64
  replayDeadLetters
65
65
  };
66
- //# sourceMappingURL=replay-C2LXMJZP.js.map
66
+ //# sourceMappingURL=replay-4S6ENYCH.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  draftReportForSite,
6
6
  readGaConfig
7
- } from "./chunk-BBUSGEES.js";
7
+ } from "./chunk-J3WEZGIN.js";
8
8
  import {
9
9
  findDueReports,
10
10
  nextDueDate,
@@ -22,17 +22,17 @@ import {
22
22
  openBase,
23
23
  readAirtableConfig
24
24
  } from "./chunk-HN6I2XWI.js";
25
- import "./chunk-MT2JINSS.js";
25
+ import "./chunk-5UINJNAJ.js";
26
+ import "./chunk-KJGKZLCT.js";
26
27
  import {
27
28
  escapeHtml
28
- } from "./chunk-DJZWYIPP.js";
29
- import "./chunk-5UINJNAJ.js";
29
+ } from "./chunk-BGTYPVLT.js";
30
30
  import {
31
31
  listWebsites,
32
32
  siteSlug,
33
33
  updateNextDueDates
34
34
  } from "./chunk-7NYN2P6I.js";
35
- import "./chunk-EVBCK2KC.js";
35
+ import "./chunk-RARREDQE.js";
36
36
  import "./chunk-6VAL7XJT.js";
37
37
 
38
38
  // src/alerts/analytics-health.ts
@@ -78,19 +78,19 @@ function dashboardBaseUrl() {
78
78
  }
79
79
  async function runReportCommand(slug, opts) {
80
80
  if (opts.digest) {
81
- const { runDigest } = await import("./digest-FW6QMZZJ.js");
81
+ const { runDigest } = await import("./digest-2JEAEJK6.js");
82
82
  return runDigest({ baseUrl: dashboardBaseUrl() });
83
83
  }
84
84
  if (opts.sendReady) {
85
- const { sendApprovedReports } = await import("./orchestrate-YLWQ6KK2.js");
86
- const { makeSiteMirror } = await import("./site-mirror-NIREX5ME.js");
85
+ const { sendApprovedReports } = await import("./orchestrate-JNKCWU5K.js");
86
+ const { makeSiteMirror } = await import("./site-mirror-Q763PLGG.js");
87
87
  return sendApprovedReports({ siteMirror: await makeSiteMirror() });
88
88
  }
89
89
  if (opts.rerender) {
90
90
  const { rerenderReport, formatRerenderResult } = await import("./rerender-3FM6DNRH.js");
91
- const { renderReportFromRow } = await import("./render-from-row-BWSOIYDS.js");
92
- const { openDb, readDbConfig } = await import("./client-AUE3XUPO.js");
93
- const { getReportById, getSiteById, storeRenderedHtml } = await import("./fleet-state-7TJ2GYT2.js");
91
+ const { renderReportFromRow } = await import("./render-from-row-DOYJDO2L.js");
92
+ const { openDb, readDbConfig } = await import("./client-JYKPVCJX.js");
93
+ const { getReportById, getSiteById, storeRenderedHtml } = await import("./fleet-state-R5LU7IC3.js");
94
94
  const { loadHeaderImage } = await import("./header-images-SQBCE5OD.js");
95
95
  const { fetchAttachmentBytes } = await import("./attachments-NLZIJ5NU.js");
96
96
  const db = await openDb(readDbConfig());
@@ -129,9 +129,9 @@ async function runReportCommand(slug, opts) {
129
129
  }
130
130
  async function runDueDraft() {
131
131
  const base = openBase(readAirtableConfig());
132
- const { makeScheduleMirrorBestEffort } = await import("./health-mirror-L5JBEJVA.js");
133
- const { makeReportMirror } = await import("./report-mirror-4MNORYQ4.js");
134
- const { makeSiteMirror } = await import("./site-mirror-NIREX5ME.js");
132
+ const { makeScheduleMirrorBestEffort } = await import("./health-mirror-GCDN7BJL.js");
133
+ const { makeReportMirror } = await import("./report-mirror-AAK3MXHY.js");
134
+ const { makeSiteMirror } = await import("./site-mirror-Q763PLGG.js");
135
135
  const result = await draftDueReports(
136
136
  base,
137
137
  /* @__PURE__ */ new Date(),
@@ -321,8 +321,8 @@ async function runSingleSiteDraft(slug, opts) {
321
321
  if (!site) {
322
322
  throw Object.assign(new Error(`No Websites row matched slug "${slug}"`), { exitCode: 2 });
323
323
  }
324
- const reportMirror = opts.previewOnly ? null : await (await import("./report-mirror-4MNORYQ4.js")).makeReportMirror();
325
- const siteMirror = opts.previewOnly ? null : await (await import("./site-mirror-NIREX5ME.js")).makeSiteMirror();
324
+ const reportMirror = opts.previewOnly ? null : await (await import("./report-mirror-AAK3MXHY.js")).makeReportMirror();
325
+ const siteMirror = opts.previewOnly ? null : await (await import("./site-mirror-Q763PLGG.js")).makeSiteMirror();
326
326
  const result = await draftReportForSite(opts.previewOnly ? null : base, site, opts.reportType, {
327
327
  previewOnly: opts.previewOnly,
328
328
  // Only forced on. Left undefined, draftReportForSite keeps its default
@@ -350,4 +350,4 @@ export {
350
350
  runReportCommand,
351
351
  writeNextDueDates
352
352
  };
353
- //# sourceMappingURL=report-R3E3Y7HR.js.map
353
+ //# sourceMappingURL=report-TALY5P3L.js.map
@@ -1,8 +1,11 @@
1
+ import {
2
+ TURSO_IS_AUTHORITATIVE
3
+ } from "./chunk-RUPPWAD6.js";
1
4
  import {
2
5
  mirrorReportInsert,
3
6
  mirrorReportPatch,
4
7
  storeRenderedHtml
5
- } from "./chunk-HHMMUR4P.js";
8
+ } from "./chunk-GQDDWYU2.js";
6
9
  import "./chunk-P6AORG32.js";
7
10
  import "./chunk-F3JWOPTG.js";
8
11
  import "./chunk-VAD43MOR.js";
@@ -10,12 +13,12 @@ import "./chunk-7NYN2P6I.js";
10
13
  import {
11
14
  openDb,
12
15
  readDbConfig
13
- } from "./chunk-JHGHLKFS.js";
14
- import "./chunk-3SF7D4DY.js";
16
+ } from "./chunk-WS6NU475.js";
17
+ import "./chunk-MFHS7IQ2.js";
15
18
  import "./chunk-6VAL7XJT.js";
16
19
 
17
20
  // src/reports/report-mirror.ts
18
- async function makeReportMirror(open = () => openDb(readDbConfig())) {
21
+ async function makeReportMirror(open = () => openDb(readDbConfig()), strict = TURSO_IS_AUTHORITATIVE) {
19
22
  let db = null;
20
23
  let why = "";
21
24
  try {
@@ -23,6 +26,7 @@ async function makeReportMirror(open = () => openDb(readDbConfig())) {
23
26
  } catch (e) {
24
27
  why = e.message;
25
28
  }
29
+ if (strict && !db) throw new Error(`REPORT_MIRROR unavailable: ${why}`);
26
30
  const run = async (reportId, op, work) => {
27
31
  if (!db) {
28
32
  console.log(`REPORT_MIRROR report=${reportId} op=${op} mirrored=absent reason=${why}`);
@@ -30,12 +34,14 @@ async function makeReportMirror(open = () => openDb(readDbConfig())) {
30
34
  }
31
35
  try {
32
36
  await work(db);
33
- console.log(`REPORT_MIRROR report=${reportId} op=${op} mirrored=1`);
34
37
  } catch (e) {
35
38
  console.log(
36
39
  `REPORT_MIRROR report=${reportId} op=${op} mirrored=0 error=${e.message}`
37
40
  );
41
+ if (strict) throw e;
42
+ return;
38
43
  }
44
+ console.log(`REPORT_MIRROR report=${reportId} op=${op} mirrored=1`);
39
45
  };
40
46
  return {
41
47
  created: (rec) => run(rec.id, "created", (d) => mirrorReportInsert(d, rec)),
@@ -46,4 +52,4 @@ async function makeReportMirror(open = () => openDb(readDbConfig())) {
46
52
  export {
47
53
  makeReportMirror
48
54
  };
49
- //# sourceMappingURL=report-mirror-4MNORYQ4.js.map
55
+ //# sourceMappingURL=report-mirror-AAK3MXHY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/reports/report-mirror.ts"],"sourcesContent":["/** #539 Phase 5: the Turso write-through for the Reports write surface.\n *\n * Before this, the only report mirrors were UPDATEs on the request path\n * (approve, override, delivery status, commentary). Everything the DRAFTING\n * path writes — the row itself, the rendered body, the queue flag, a re-run's\n * refreshed scores — reached Turso only via the hourly sync. Two of those are\n * visible to the operator today: a fresh draft's row does not exist, and its\n * preview route answers \"No rendered body stored\" for up to an hour. At the\n * freeze they stop being windows.\n *\n * Deliberately UNLIKE `makeHealthMirrorBestEffort`, this never returns null.\n * #585 is the reason: Phase 3's next-due mirror silently no-opped in production\n * for weeks because the factory returned null without creds, and a dead\n * dual-write then looked exactly like a healthy one — the only tell was an\n * ABSENT log suffix nobody was watching for. Here creds-absent is a state the\n * mirror REPORTS, so every write emits one REPORT_MIRROR line and an absent\n * line means the wiring is gone, not that conditions were quiet.\n */\nimport { openDb, readDbConfig, type Db } from \"../db/client.js\";\nimport {\n mirrorReportInsert,\n mirrorReportPatch,\n storeRenderedHtml,\n type ReportMirrorPatch,\n} from \"../db/fleet-state.js\";\nimport type { CreatedDraftMirror } from \"./airtable/reports.js\";\nimport { TURSO_IS_AUTHORITATIVE } from \"../db/freeze.js\";\n\n/** The three shapes a report write takes. Injected as ONE object rather than\n * three parameters because they share a db handle and always travel together:\n * a caller holding `created` but not `body` produces exactly the half-mirrored\n * state (row present, preview 404) this module exists to prevent. */\nexport type ReportMirror = {\n /** A row Airtable just CREATED, as Airtable echoed it back. */\n created: CreatedDraftMirror;\n /** A freshly rendered body for an existing row. */\n body: (reportId: string, html: string) => Promise<void>;\n /** Columns just written to an existing row. */\n patch: (reportId: string, patch: ReportMirrorPatch) => Promise<void>;\n};\n\n/** Build the drafting-path mirror. Never throws and never returns null:\n * Airtable is still authoritative through Phase 5, so a mirror problem must\n * not cost a draft the operator is waiting on — the hourly sync converges\n * whatever this misses. `open` is injectable for tests. */\nexport async function makeReportMirror(\n open: () => Promise<Db> = () => openDb(readDbConfig()),\n /** #612. `true` = Turso is the store that must succeed, so every failure\n * throws instead of being logged and swallowed. Defaulted from the shipped\n * constant and injected by tests, so both sides stay proven. */\n strict: boolean = TURSO_IS_AUTHORITATIVE,\n): Promise<ReportMirror> {\n let db: Db | null = null;\n let why = \"\";\n try {\n db = await open();\n } catch (e) {\n why = (e as Error).message;\n }\n // Frozen: refuse to hand back a mirror that cannot write — see makeSiteMirror\n // for why this fails at construction rather than per write.\n if (strict && !db) throw new Error(`REPORT_MIRROR unavailable: ${why}`);\n\n const run = async (reportId: string, op: string, work: (db: Db) => Promise<void>) => {\n if (!db) {\n console.log(`REPORT_MIRROR report=${reportId} op=${op} mirrored=absent reason=${why}`);\n return;\n }\n try {\n await work(db);\n } catch (e) {\n console.log(\n `REPORT_MIRROR report=${reportId} op=${op} mirrored=0 error=${(e as Error).message}`,\n );\n if (strict) throw e;\n return;\n }\n console.log(`REPORT_MIRROR report=${reportId} op=${op} mirrored=1`);\n };\n\n return {\n created: (rec) => run(rec.id, \"created\", (d) => mirrorReportInsert(d, rec)),\n body: (reportId, html) => run(reportId, \"body\", (d) => storeRenderedHtml(d, reportId, html)),\n patch: (reportId, patch) =>\n run(reportId, \"patch\", (d) => mirrorReportPatch(d, reportId, patch)),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA6CA,eAAsB,iBACpB,OAA0B,MAAM,OAAO,aAAa,CAAC,GAIrD,SAAkB,wBACK;AACvB,MAAI,KAAgB;AACpB,MAAI,MAAM;AACV,MAAI;AACF,SAAK,MAAM,KAAK;AAAA,EAClB,SAAS,GAAG;AACV,UAAO,EAAY;AAAA,EACrB;AAGA,MAAI,UAAU,CAAC,GAAI,OAAM,IAAI,MAAM,8BAA8B,GAAG,EAAE;AAEtE,QAAM,MAAM,OAAO,UAAkB,IAAY,SAAoC;AACnF,QAAI,CAAC,IAAI;AACP,cAAQ,IAAI,wBAAwB,QAAQ,OAAO,EAAE,2BAA2B,GAAG,EAAE;AACrF;AAAA,IACF;AACA,QAAI;AACF,YAAM,KAAK,EAAE;AAAA,IACf,SAAS,GAAG;AACV,cAAQ;AAAA,QACN,wBAAwB,QAAQ,OAAO,EAAE,qBAAsB,EAAY,OAAO;AAAA,MACpF;AACA,UAAI,OAAQ,OAAM;AAClB;AAAA,IACF;AACA,YAAQ,IAAI,wBAAwB,QAAQ,OAAO,EAAE,aAAa;AAAA,EACpE;AAEA,SAAO;AAAA,IACL,SAAS,CAAC,QAAQ,IAAI,IAAI,IAAI,WAAW,CAAC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAAA,IAC1E,MAAM,CAAC,UAAU,SAAS,IAAI,UAAU,QAAQ,CAAC,MAAM,kBAAkB,GAAG,UAAU,IAAI,CAAC;AAAA,IAC3F,OAAO,CAAC,UAAU,UAChB,IAAI,UAAU,SAAS,CAAC,MAAM,kBAAkB,GAAG,UAAU,KAAK,CAAC;AAAA,EACvE;AACF;","names":[]}
@@ -0,0 +1,18 @@
1
+ // src/db/schema.ts
2
+ var DATABASE_TABLES = [
3
+ "submissions",
4
+ "spam_screenouts",
5
+ "fleet_events",
6
+ "_migrations",
7
+ "submission_deadletter",
8
+ "sites",
9
+ "site_health",
10
+ "site_schedule",
11
+ "reports",
12
+ "prospect_audits",
13
+ "digest_state"
14
+ ];
15
+ export {
16
+ DATABASE_TABLES
17
+ };
18
+ //# sourceMappingURL=schema-75PJECAE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db/schema.ts"],"sourcesContent":["// The hand-written shape Kysely types every query from. SQLite stores TEXT/INTEGER\n// only, so these are the column *shapes* — the value domains (form_type, status,\n// notify_status) are still narrowed at read time by the validators in\n// src/reports/submission-row.ts. Keep this in lockstep with src/db/migrations.ts;\n// drift is caught by the in-memory tests in tests/db/, not by codegen.\n\nexport interface SubmissionsTable {\n id: string;\n submission_id: number | null;\n site_id: string;\n form_type: string;\n name: string;\n email: string;\n phone: string | null;\n message: string | null;\n extra_fields: string | null;\n source_url: string | null;\n utm: string | null;\n submitted_at: string | null;\n status: string;\n notify_status: string;\n resend_message_id: string | null;\n spam_score: number | null;\n spam_reason: string | null;\n fanout_status: string | null;\n}\n\nexport interface SpamScreenoutsTable {\n site_id: string;\n date: string;\n honeypot: number;\n too_fast: number;\n marked_spam: number;\n}\n\nexport interface FleetEventsTable {\n id: string;\n ts: string;\n type: string;\n site_id: string | null;\n site_name: string | null;\n summary: string;\n data: string | null;\n created_at: string;\n}\n\nexport interface MigrationsTable {\n id: string;\n applied_at: string;\n}\n\n/** A lead whose site lookup threw mid-ingest (migration 0006). `payload` and\n * `turnstile` are JSON — the raw wire body and the verification computed at\n * receipt — exactly what `db replay-deadletters` feeds back through\n * `ingestSubmission` once the lookup recovers. */\nexport interface SubmissionDeadletterTable {\n id: string;\n site_slug: string;\n payload: string;\n turnstile: string;\n error: string;\n received_at: string;\n replayed_at: string | null;\n replay_outcome: string | null;\n replay_submission_id: string | null;\n}\n\n/** Operator-owned fleet config (migration 0007). PK = Airtable rec id (D1).\n * `legacy` holds the 33 populated-but-code-unreferenced Airtable columns as a\n * JSON object keyed by original column name; the plaintext DNS/cms credential\n * cells never migrate (operator ruling 2026-08-23). */\nexport interface SitesTable {\n id: string;\n slug: string;\n name: string;\n url: string | null;\n status: string | null;\n point_of_contact: string | null;\n maintenance_freq: string | null;\n testing_freq: string | null;\n maintenance_day: string | null;\n testing_day: string | null;\n ga4_property_id: string | null;\n search_query: string | null;\n search_console_property: string | null;\n git_repo: string | null;\n netlify_id: string | null;\n report_recipients_to: string | null;\n report_recipients_cc: string | null;\n copy_intro: string | null;\n copy_contact: string | null;\n copy_footer: string | null;\n newsletter_webhook: string | null;\n mailchimp_api_key: string | null;\n mailchimp_audience_id: string | null;\n notify_routing: string | null;\n require_turnstile: number;\n accepted_watch_conditions: string | null;\n prismic_ack_until: string | null;\n launched_at: string | null;\n header_image: Uint8Array | null;\n header_image_filename: string | null;\n header_image_type: string | null;\n header_image_generated_at: string | null;\n legacy: string | null;\n}\n\n/** Nightly-cron-owned health/telemetry (migration 0007) — one row per site,\n * written as one batched upsert per sweep family. `analytics_soft_fail_at` is\n * the \"Analytics soft-fail at\" column code always wrote best-effort but no\n * operator ever created in Airtable. */\nexport interface SiteHealthTable {\n site_id: string;\n p_score: number | null;\n r_score: number | null;\n bp_score: number | null;\n seo_score: number | null;\n lighthouse_at: string | null;\n a11y_violations: number | null;\n deps_drifted: number | null;\n deps_major_behind: number | null;\n deps_outdated: number | null;\n deps_major_outdated: number | null;\n vulns_critical: number | null;\n vulns_high: number | null;\n vulns_moderate: number | null;\n vulns_low: number | null;\n security_audit_at: string | null;\n security_advisories: string | null;\n auto_fix_attempts: number | null;\n analytics_soft_fail_at: string | null;\n cert_days_remaining: number | null;\n domain_checked_at: string | null;\n deploy_status: string | null;\n last_deploy_at: string | null;\n deploy_log_url: string | null;\n deploy_checked_at: string | null;\n function_health: string | null;\n cms_reachable: string | null;\n turnstile_widget: string | null;\n function_health_checked_at: string | null;\n crossbrowser_ok: number | null;\n mobile_ok: number | null;\n links_ok: number | null;\n broken_links: number | null;\n browser_checked_at: string | null;\n uptime_reachable: string | null;\n titles_meta_ok: string | null;\n smoke_ok: string | null;\n last_smoke_at: string | null;\n form_e2e_ok: string | null;\n form_e2e_checked_at: string | null;\n renovate_failing_cis: number | null;\n default_branch_ci: string | null;\n last_commit_at: string | null;\n github_signals_at: string | null;\n prismic_models: string | null;\n prismic_models_checked_at: string | null;\n prismic_models_drift: string | null;\n}\n\n/** Code-derived schedule (migration 0007) — written by updateNextDueDates. */\nexport interface SiteScheduleTable {\n site_id: string;\n next_maintenance_at: string | null;\n next_testing_at: string | null;\n computed_at: string | null;\n}\n\n/** Report rows (migration 0007). PK = Airtable rec id. `checklist` is JSON\n * keyed by the STABLE checklist key (src/reports/checklist.ts), not the\n * Airtable column name; `rendered_html` is the downloaded attachment body. */\nexport interface ReportsTable {\n id: string;\n site_id: string | null;\n report_id: string | null;\n report_type: string | null;\n period: string | null;\n period_start: string | null;\n period_end: string | null;\n completed_on: string | null;\n lighthouse_performance: number | null;\n lighthouse_accessibility: number | null;\n lighthouse_best_practices: number | null;\n lighthouse_seo: number | null;\n ga_users_current: number | null;\n ga_users_previous: number | null;\n search_found_page1: number | null;\n search_position: number | null;\n last_tested_date: string | null;\n commentary: string | null;\n subject_override: string | null;\n draft_ready: number;\n approved_to_send: number;\n approved_at: string | null;\n approved_by: string | null;\n send_override: number;\n override_reason: string | null;\n override_by: string | null;\n override_at: string | null;\n sent_at: string | null;\n delivery_status: string | null;\n resend_message_id: string | null;\n checklist: string | null;\n checklist_auto_evidence: string | null;\n rendered_html: string | null;\n}\n\n/** One prospect-audit run (migration 0009). `token` is the 128-bit unguessable\n * public handle for GET /r/{token}; `result_json` holds the full\n * ProspectAuditResult (src/prospect/types.ts). */\nexport interface ProspectAuditsTable {\n id: string;\n token: string;\n url: string;\n business: string | null;\n created_at: string;\n status: string;\n result_json: string;\n}\n\n/** The digest's prior-run snapshot (migration 0011). One row, `id` = the\n * constant singleton key; `snapshot` is the whole DigestSnapshot as JSON. */\nexport interface DigestStateTable {\n id: string;\n snapshot: string;\n updated_at: string | null;\n}\n\n/** Every table the app owns, as a RUNTIME value (#612 review).\n *\n * The `Database` interface below is types-only, so nothing could assert that a\n * backup actually covered it — `tables=N` was printed by the verifier and never\n * checked, meaning a table a migration failed to create would ride green\n * forever. The compile-time check under this list makes forgetting an entry a\n * build error naming the missing table. */\nexport const DATABASE_TABLES = [\n \"submissions\",\n \"spam_screenouts\",\n \"fleet_events\",\n \"_migrations\",\n \"submission_deadletter\",\n \"sites\",\n \"site_health\",\n \"site_schedule\",\n \"reports\",\n \"prospect_audits\",\n \"digest_state\",\n] as const;\n\nexport type DatabaseTable = (typeof DATABASE_TABLES)[number];\n\nexport interface Database {\n submissions: SubmissionsTable;\n spam_screenouts: SpamScreenoutsTable;\n fleet_events: FleetEventsTable;\n _migrations: MigrationsTable;\n submission_deadletter: SubmissionDeadletterTable;\n sites: SitesTable;\n site_health: SiteHealthTable;\n site_schedule: SiteScheduleTable;\n reports: ReportsTable;\n prospect_audits: ProspectAuditsTable;\n digest_state: DigestStateTable;\n}\n\n/** Compile-time exhaustiveness: if a table is added to `Database` and not to\n * `DATABASE_TABLES`, this errors and NAMES it. */\ntype MissingFromDatabaseTables = Exclude<keyof Database, DatabaseTable>;\nconst _everyTableIsListed: MissingFromDatabaseTables extends never\n ? true\n : MissingFromDatabaseTables = true;\nvoid _everyTableIsListed;\n"],"mappings":";AA4OO,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":[]}
@@ -12,12 +12,12 @@ import {
12
12
  } from "./chunk-TVL5MVUF.js";
13
13
  import {
14
14
  resolveSites
15
- } from "./chunk-CROKUHJB.js";
15
+ } from "./chunk-Q47DU255.js";
16
16
  import "./chunk-73UFRZKY.js";
17
17
  import "./chunk-GASBX52O.js";
18
18
  import "./chunk-XHHMM5HX.js";
19
- import "./chunk-6TWWY7ZU.js";
20
- import "./chunk-EVBCK2KC.js";
19
+ import "./chunk-Z3JXLULD.js";
20
+ import "./chunk-RARREDQE.js";
21
21
  import "./chunk-XTK5VIQB.js";
22
22
  import "./chunk-LBYOLBW7.js";
23
23
  import "./chunk-XXTZBPUY.js";
@@ -62,4 +62,4 @@ async function runSelfUpdatingCommand(site, opts) {
62
62
  export {
63
63
  runSelfUpdatingCommand
64
64
  };
65
- //# sourceMappingURL=self-updating-UCIWESAX.js.map
65
+ //# sourceMappingURL=self-updating-NTCCWJYZ.js.map
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  buildReportDataForSite,
3
3
  scoresFromRow
4
- } from "./chunk-HEWSRXYV.js";
4
+ } from "./chunk-ULHPALI5.js";
5
5
  import {
6
6
  operatorEmail
7
7
  } from "./chunk-5ML7FMBD.js";
8
- import "./chunk-BBUSGEES.js";
8
+ import "./chunk-J3WEZGIN.js";
9
9
  import {
10
10
  ELIGIBLE_STATUSES
11
11
  } from "./chunk-PZR4EBPY.js";
@@ -13,7 +13,7 @@ import "./chunk-YFAXOD7Z.js";
13
13
  import {
14
14
  isProbablyEmail,
15
15
  parseAddresses
16
- } from "./chunk-NDWYJLRF.js";
16
+ } from "./chunk-DGZWFI2G.js";
17
17
  import "./chunk-F3JWOPTG.js";
18
18
  import "./chunk-VAD43MOR.js";
19
19
  import {
@@ -26,13 +26,13 @@ import {
26
26
  import {
27
27
  prepareHeaderImage,
28
28
  renderReportEmail
29
- } from "./chunk-ZGBEIWSC.js";
29
+ } from "./chunk-XP5WM2LV.js";
30
30
  import "./chunk-GECESMVS.js";
31
- import "./chunk-MT2JINSS.js";
32
- import "./chunk-DJZWYIPP.js";
33
31
  import {
34
32
  applyReportTypeHeadline
35
33
  } from "./chunk-5UINJNAJ.js";
34
+ import "./chunk-KJGKZLCT.js";
35
+ import "./chunk-BGTYPVLT.js";
36
36
  import {
37
37
  listWebsites,
38
38
  siteSlug
@@ -40,10 +40,10 @@ import {
40
40
  import {
41
41
  defaultResendClient
42
42
  } from "./chunk-WEKL2HYJ.js";
43
- import "./chunk-XUGSZP7Z.js";
44
- import "./chunk-EVBCK2KC.js";
45
- import "./chunk-JHGHLKFS.js";
46
- import "./chunk-3SF7D4DY.js";
43
+ import "./chunk-W45FZEGE.js";
44
+ import "./chunk-RARREDQE.js";
45
+ import "./chunk-WS6NU475.js";
46
+ import "./chunk-MFHS7IQ2.js";
47
47
  import "./chunk-6VAL7XJT.js";
48
48
 
49
49
  // src/recipes/selftest-email.ts
@@ -172,4 +172,4 @@ async function runSelftestCommand(kind, site, opts) {
172
172
  export {
173
173
  runSelftestCommand
174
174
  };
175
- //# sourceMappingURL=selftest-VGHWPN72.js.map
175
+ //# sourceMappingURL=selftest-NZTR4SM2.js.map
@@ -1,8 +1,11 @@
1
+ import {
2
+ TURSO_IS_AUTHORITATIVE
3
+ } from "./chunk-RUPPWAD6.js";
1
4
  import {
2
5
  mirrorHealthFields,
3
6
  mirrorSiteFields,
4
7
  mirrorSiteInsert
5
- } from "./chunk-HHMMUR4P.js";
8
+ } from "./chunk-GQDDWYU2.js";
6
9
  import "./chunk-P6AORG32.js";
7
10
  import "./chunk-F3JWOPTG.js";
8
11
  import "./chunk-VAD43MOR.js";
@@ -10,12 +13,12 @@ import "./chunk-7NYN2P6I.js";
10
13
  import {
11
14
  openDb,
12
15
  readDbConfig
13
- } from "./chunk-JHGHLKFS.js";
14
- import "./chunk-3SF7D4DY.js";
16
+ } from "./chunk-WS6NU475.js";
17
+ import "./chunk-MFHS7IQ2.js";
15
18
  import "./chunk-6VAL7XJT.js";
16
19
 
17
20
  // src/db/site-mirror.ts
18
- async function makeSiteMirror(open = () => openDb(readDbConfig())) {
21
+ async function makeSiteMirror(open = () => openDb(readDbConfig()), strict = TURSO_IS_AUTHORITATIVE) {
19
22
  let db = null;
20
23
  let why = "";
21
24
  try {
@@ -23,16 +26,23 @@ async function makeSiteMirror(open = () => openDb(readDbConfig())) {
23
26
  } catch (e) {
24
27
  why = e.message;
25
28
  }
29
+ if (strict && !db) throw new Error(`SITE_MIRROR unavailable: ${why}`);
26
30
  const run = async (siteId, op, work) => {
27
31
  if (!db) {
28
32
  console.log(`SITE_MIRROR site=${siteId} op=${op} mirrored=absent reason=${why}`);
29
33
  return;
30
34
  }
35
+ let matched;
31
36
  try {
32
- const matched = await work(db);
33
- console.log(`SITE_MIRROR site=${siteId} op=${op} mirrored=${matched ? "1" : "missed"}`);
37
+ matched = await work(db);
34
38
  } catch (e) {
35
39
  console.log(`SITE_MIRROR site=${siteId} op=${op} mirrored=0 error=${e.message}`);
40
+ if (strict) throw e;
41
+ return;
42
+ }
43
+ console.log(`SITE_MIRROR site=${siteId} op=${op} mirrored=${matched ? "1" : "missed"}`);
44
+ if (strict && !matched) {
45
+ throw new Error(`SITE_MIRROR site=${siteId} op=${op}: no such row in Turso`);
36
46
  }
37
47
  };
38
48
  return {
@@ -49,4 +59,4 @@ async function makeSiteMirror(open = () => openDb(readDbConfig())) {
49
59
  export {
50
60
  makeSiteMirror
51
61
  };
52
- //# sourceMappingURL=site-mirror-NIREX5ME.js.map
62
+ //# sourceMappingURL=site-mirror-Q763PLGG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/db/site-mirror.ts"],"sourcesContent":["/** #539 Phase 5: the Turso write-through for the ONE-OFF Websites writers.\n *\n * Phase 3 mirrored the nightly sweep — the audit write-back, github-signals and\n * the next-due dates. It did not touch the writers that run on their own\n * schedule or on demand: the analytics soft-fail stamp, the Renovate\n * auto-fix counter, the prismic-models verdict, `updateLaunched`, the\n * forms notify target, and the SINGLE-SITE audit write-back (only the fleet\n * path ever passed a mirror). Each of those reached Turso solely via the hourly\n * sync, which stops existing at the freeze.\n *\n * It lives in `src/db` rather than a feature folder because its callers span\n * audits, recipes, CLI commands and the send path — no one feature owns it.\n *\n * Deliberately UNLIKE `makeHealthMirrorBestEffort`, this never returns null.\n * #585 is the reason: that factory returned null without creds and the\n * dual-write silently no-opped in production for weeks, because a dead mirror\n * and a healthy one produced identical output — the only tell was an ABSENT log\n * suffix nobody was watching for. Here creds-absent is a state the mirror\n * REPORTS, so a missing SITE_MIRROR line means the wiring is gone.\n */\nimport { openDb, readDbConfig, type Db } from \"./client.js\";\nimport { mirrorHealthFields, mirrorSiteFields, mirrorSiteInsert } from \"./fleet-state.js\";\nimport { TURSO_IS_AUTHORITATIVE } from \"./freeze.js\";\n\n/** Two ops because the Websites row is split across two Turso tables. Callers\n * pass the EXACT FieldSet the Airtable writer returned, so the mirror can never\n * carry a different payload than the write it shadows. */\nexport type SiteMirror = {\n /** A row Airtable just CREATED, as Airtable echoed it back (`ensure-site`).\n * Every other op is an UPDATE, which does nothing for a row that does not\n * exist yet — so without this a bootstrapped site is invisible to Turso and\n * every mirror the rest of the bootstrap fires reports `mirrored=missed`. */\n created: (rec: { id: string; fields: Record<string, unknown> }) => Promise<void>;\n /** Columns that live in `site_health`. */\n health: (siteId: string, fields: Record<string, unknown>) => Promise<void>;\n /** Columns that live in `sites`. */\n site: (siteId: string, fields: Record<string, unknown>) => Promise<void>;\n};\n\n/** Build the one-off writers' mirror. Never throws and never returns null:\n * Airtable is still authoritative through Phase 5, so a mirror problem must not\n * cost the write it shadows — the hourly sync converges whatever this misses.\n * `open` is injectable for tests. */\nexport async function makeSiteMirror(\n open: () => Promise<Db> = () => openDb(readDbConfig()),\n /** #612. `true` = Turso is the store that must succeed, so every failure\n * throws instead of being logged and swallowed. Defaulted from the shipped\n * constant and injected by tests, so both sides stay proven. */\n strict: boolean = TURSO_IS_AUTHORITATIVE,\n): Promise<SiteMirror> {\n let db: Db | null = null;\n let why = \"\";\n try {\n db = await open();\n } catch (e) {\n why = (e as Error).message;\n }\n // Frozen: refuse to hand back a mirror that cannot write. Failing at\n // CONSTRUCTION rather than per write matters — a caller holding a\n // working-looking mirror would run its whole batch before anyone noticed that\n // nothing had persisted.\n if (strict && !db) throw new Error(`SITE_MIRROR unavailable: ${why}`);\n\n const run = async (siteId: string, op: string, work: (db: Db) => Promise<boolean>) => {\n if (!db) {\n console.log(`SITE_MIRROR site=${siteId} op=${op} mirrored=absent reason=${why}`);\n return;\n }\n // Every path below logs EXACTLY ONE line, then strict adds a throw. Logging\n // first matters: a frozen run that fails should still leave the same trace\n // in the run log that an unfrozen one would, not just a stack.\n let matched: boolean;\n try {\n matched = await work(db);\n } catch (e) {\n console.log(`SITE_MIRROR site=${siteId} op=${op} mirrored=0 error=${(e as Error).message}`);\n if (strict) throw e;\n return;\n }\n // `missed` is its own outcome: the UPDATE matched no row. Before the freeze\n // that means the hourly sync has not imported this site yet — a transient,\n // and reporting it as mirrored=1 would claim a write that never landed.\n // After the freeze no importer exists, so an absent row stays absent: it is\n // a bug, not a wait.\n console.log(`SITE_MIRROR site=${siteId} op=${op} mirrored=${matched ? \"1\" : \"missed\"}`);\n if (strict && !matched) {\n throw new Error(`SITE_MIRROR site=${siteId} op=${op}: no such row in Turso`);\n }\n };\n\n return {\n // An INSERT always lands, so it reports 1 rather than routing a row count\n // through `missed` — there was nothing to match in the first place.\n created: (rec) =>\n run(rec.id, \"created\", async (d) => {\n await mirrorSiteInsert(d, rec, new Date().toISOString());\n return true;\n }),\n health: (siteId, fields) => run(siteId, \"health\", (d) => mirrorHealthFields(d, siteId, fields)),\n site: (siteId, fields) => run(siteId, \"site\", (d) => mirrorSiteFields(d, siteId, fields)),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2CA,eAAsB,eACpB,OAA0B,MAAM,OAAO,aAAa,CAAC,GAIrD,SAAkB,wBACG;AACrB,MAAI,KAAgB;AACpB,MAAI,MAAM;AACV,MAAI;AACF,SAAK,MAAM,KAAK;AAAA,EAClB,SAAS,GAAG;AACV,UAAO,EAAY;AAAA,EACrB;AAKA,MAAI,UAAU,CAAC,GAAI,OAAM,IAAI,MAAM,4BAA4B,GAAG,EAAE;AAEpE,QAAM,MAAM,OAAO,QAAgB,IAAY,SAAuC;AACpF,QAAI,CAAC,IAAI;AACP,cAAQ,IAAI,oBAAoB,MAAM,OAAO,EAAE,2BAA2B,GAAG,EAAE;AAC/E;AAAA,IACF;AAIA,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,KAAK,EAAE;AAAA,IACzB,SAAS,GAAG;AACV,cAAQ,IAAI,oBAAoB,MAAM,OAAO,EAAE,qBAAsB,EAAY,OAAO,EAAE;AAC1F,UAAI,OAAQ,OAAM;AAClB;AAAA,IACF;AAMA,YAAQ,IAAI,oBAAoB,MAAM,OAAO,EAAE,aAAa,UAAU,MAAM,QAAQ,EAAE;AACtF,QAAI,UAAU,CAAC,SAAS;AACtB,YAAM,IAAI,MAAM,oBAAoB,MAAM,OAAO,EAAE,wBAAwB;AAAA,IAC7E;AAAA,EACF;AAEA,SAAO;AAAA;AAAA;AAAA,IAGL,SAAS,CAAC,QACR,IAAI,IAAI,IAAI,WAAW,OAAO,MAAM;AAClC,YAAM,iBAAiB,GAAG,MAAK,oBAAI,KAAK,GAAE,YAAY,CAAC;AACvD,aAAO;AAAA,IACT,CAAC;AAAA,IACH,QAAQ,CAAC,QAAQ,WAAW,IAAI,QAAQ,UAAU,CAAC,MAAM,mBAAmB,GAAG,QAAQ,MAAM,CAAC;AAAA,IAC9F,MAAM,CAAC,QAAQ,WAAW,IAAI,QAAQ,QAAQ,CAAC,MAAM,iBAAiB,GAAG,QAAQ,MAAM,CAAC;AAAA,EAC1F;AACF;","names":[]}
@@ -8,14 +8,14 @@ import {
8
8
  } from "./chunk-TVL5MVUF.js";
9
9
  import {
10
10
  resolveSites
11
- } from "./chunk-CROKUHJB.js";
11
+ } from "./chunk-Q47DU255.js";
12
12
  import {
13
13
  smokeSuite
14
14
  } from "./chunk-Y53WOQIJ.js";
15
15
  import "./chunk-R53VL3RI.js";
16
16
  import "./chunk-3G25KIWW.js";
17
- import "./chunk-6TWWY7ZU.js";
18
- import "./chunk-EVBCK2KC.js";
17
+ import "./chunk-Z3JXLULD.js";
18
+ import "./chunk-RARREDQE.js";
19
19
  import "./chunk-XTK5VIQB.js";
20
20
  import "./chunk-LBYOLBW7.js";
21
21
  import "./chunk-XXTZBPUY.js";
@@ -51,4 +51,4 @@ async function runSmokeSuiteCommand(site, opts) {
51
51
  export {
52
52
  runSmokeSuiteCommand
53
53
  };
54
- //# sourceMappingURL=smoke-suite-U3UKD3NS.js.map
54
+ //# sourceMappingURL=smoke-suite-4JFZZYVW.js.map
@@ -1,15 +1,17 @@
1
1
  import {
2
+ BLOCKED_EMAIL_DOMAINS,
2
3
  BUYER_KEYWORDS,
3
4
  DISPOSABLE_EMAIL_DOMAINS,
4
5
  SPAM_KEYWORDS,
5
6
  SPAM_THRESHOLD,
6
7
  classifySpam
7
- } from "./chunk-SNSTJLQW.js";
8
+ } from "./chunk-NO74DXDP.js";
8
9
  export {
10
+ BLOCKED_EMAIL_DOMAINS,
9
11
  BUYER_KEYWORDS,
10
12
  DISPOSABLE_EMAIL_DOMAINS,
11
13
  SPAM_KEYWORDS,
12
14
  SPAM_THRESHOLD,
13
15
  classifySpam
14
16
  };
15
- //# sourceMappingURL=spam-classifier-XLBFCO7G.js.map
17
+ //# sourceMappingURL=spam-classifier-T5H4VTP6.js.map
@@ -11,7 +11,7 @@ function parseExtraFields(raw) {
11
11
  }
12
12
  async function rescoreNewSubmissions(db, opts) {
13
13
  const { listSubmissionsFiltered, rescoreSubmissionSpam } = await import("./submissions-3I3GAQAX.js");
14
- const { classifySpam, SPAM_THRESHOLD } = await import("./spam-classifier-XLBFCO7G.js");
14
+ const { classifySpam, SPAM_THRESHOLD } = await import("./spam-classifier-T5H4VTP6.js");
15
15
  const rows = [];
16
16
  const PAGE = 500;
17
17
  for (let offset = 0; ; offset += PAGE) {
@@ -78,8 +78,8 @@ async function runSubmissionsCommand(action, opts) {
78
78
  return { output: "--apply and --dry-run conflict. Pick one.", code: 2 };
79
79
  }
80
80
  const apply = opts.apply === true;
81
- const { openDb, readDbConfig } = await import("./client-AUE3XUPO.js");
82
- const { SPAM_THRESHOLD } = await import("./spam-classifier-XLBFCO7G.js");
81
+ const { openDb, readDbConfig } = await import("./client-JYKPVCJX.js");
82
+ const { SPAM_THRESHOLD } = await import("./spam-classifier-T5H4VTP6.js");
83
83
  const cfg = opts.url ? { url: opts.url } : readDbConfig();
84
84
  const db = await openDb(cfg);
85
85
  const { scanned, flagged } = await rescoreNewSubmissions(db, { apply });
@@ -119,4 +119,4 @@ export {
119
119
  rescoreNewSubmissions,
120
120
  runSubmissionsCommand
121
121
  };
122
- //# sourceMappingURL=submissions-IQEJX6FK.js.map
122
+ //# sourceMappingURL=submissions-TEHPBSZJ.js.map
@@ -8,14 +8,14 @@ import {
8
8
  } from "./chunk-TVL5MVUF.js";
9
9
  import {
10
10
  resolveSites
11
- } from "./chunk-CROKUHJB.js";
11
+ } from "./chunk-Q47DU255.js";
12
12
  import {
13
13
  svelteCodemods
14
14
  } from "./chunk-IYSEC543.js";
15
15
  import "./chunk-J7SZQUCW.js";
16
16
  import "./chunk-3G25KIWW.js";
17
- import "./chunk-6TWWY7ZU.js";
18
- import "./chunk-EVBCK2KC.js";
17
+ import "./chunk-Z3JXLULD.js";
18
+ import "./chunk-RARREDQE.js";
19
19
  import "./chunk-XTK5VIQB.js";
20
20
  import "./chunk-LBYOLBW7.js";
21
21
  import "./chunk-XXTZBPUY.js";
@@ -51,4 +51,4 @@ async function runSvelteCodemodsCommand(site, opts) {
51
51
  export {
52
52
  runSvelteCodemodsCommand
53
53
  };
54
- //# sourceMappingURL=svelte-codemods-GOKXTBHW.js.map
54
+ //# sourceMappingURL=svelte-codemods-QAPDZC6F.js.map
@@ -1,11 +1,13 @@
1
1
  import {
2
2
  checkFleetParity,
3
3
  formatParityResult
4
- } from "./chunk-ZBDZTJUO.js";
4
+ } from "./chunk-7FFUBF4I.js";
5
+ import "./chunk-GQDDWYU2.js";
5
6
  import {
6
7
  formatReapSummary,
7
8
  importFleetState
8
9
  } from "./chunk-P6AORG32.js";
10
+ import "./chunk-F3JWOPTG.js";
9
11
  import "./chunk-VAD43MOR.js";
10
12
  import "./chunk-7NYN2P6I.js";
11
13
 
@@ -41,4 +43,4 @@ export {
41
43
  formatSyncResult,
42
44
  syncFleetState
43
45
  };
44
- //# sourceMappingURL=sync-CTUVXMCK.js.map
46
+ //# sourceMappingURL=sync-GBFV45WX.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/db/sync.ts"],"sourcesContent":["/** Phase 2 backbone of the Airtable → Turso migration (#539): the hourly sync.\n *\n * In Phase 2 readers move to Turso while writers still write Airtable, so\n * Turso is only as fresh as the last import. One sync pass = import (with\n * reportHtml \"when-missing\", so hourly runs don't re-download every report\n * attachment) followed by the parity check, so every pass both refreshes the\n * copy and PROVES it converged — the same instrument the whole cutover trusts.\n *\n * The retry exists for a specific race, not for flakiness: the importer and\n * the parity check each read Airtable separately, so a write landing between\n * those two reads makes parity flag rows the import genuinely never saw. One\n * re-import + re-check converges unless Airtable is being written continuously\n * — a persistent mismatch after the retry is real drift and must red the run.\n */\nimport type { Db } from \"./client.js\";\nimport {\n importFleetState,\n formatReapSummary,\n type ImportIo,\n type ImportSummary,\n} from \"./import-airtable.js\";\nimport { checkFleetParity, formatParityResult, type ParityResult } from \"./parity.js\";\n\nexport type SyncResult = {\n importSummary: ImportSummary;\n parity: ParityResult;\n /** True when the first parity check mismatched and the pass re-imported. */\n retried: boolean;\n};\n\nexport async function syncFleetState(db: Db, io: ImportIo): Promise<SyncResult> {\n let importSummary = await importFleetState(db, io, { reportHtml: \"when-missing\" });\n let parity = await checkFleetParity(db, io);\n let retried = false;\n if (parity.mismatches.length > 0) {\n retried = true;\n importSummary = await importFleetState(db, io, { reportHtml: \"when-missing\" });\n parity = await checkFleetParity(db, io);\n }\n return { importSummary, parity, retried };\n}\n\n/** Human lines plus the machine gate line, emitted on EVERY run (count=0\n * included) — an absent FLEET_SYNC line means the sync crashed, never that it\n * was clean. */\nexport function formatSyncResult(r: SyncResult): string {\n const lines: string[] = [];\n if (r.importSummary.renderedHtmlMisses.length > 0) {\n lines.push(\n `⚠ ${r.importSummary.renderedHtmlMisses.length} report(s) imported WITHOUT Rendered HTML ` +\n `(fetch failed / URL expired): ${r.importSummary.renderedHtmlMisses.join(\", \")}`,\n );\n }\n // The reap is the only destructive step, so every removal is NAMED and every\n // refusal is quoted in full. FLEET_REAP rides its own line rather than as new\n // keys on FLEET_SYNC: the workflow gate greps `^FLEET_SYNC .* mismatches=0$`,\n // anchored at the end, so extending that line is a contract change waiting to\n // be got wrong. A separate always-emitted line costs nothing and, like\n // FLEET_PARITY, means an absent line reads as \"never ran\", not as \"clean\".\n lines.push(...formatReapSummary(r.importSummary.reaped));\n if (r.parity.mismatches.length > 0) {\n lines.push(formatParityResult(r.parity));\n }\n lines.push(\n `FLEET_SYNC sites=${r.importSummary.sites} reports=${r.importSummary.reports} ` +\n `html_fetched=${r.importSummary.renderedHtmlFetched} ` +\n `html_skipped=${r.importSummary.renderedHtmlSkipped} ` +\n `retried=${r.retried ? 1 : 0} mismatches=${r.parity.mismatches.length}`,\n );\n return lines.join(\"\\n\");\n}\n"],"mappings":";;;;;;;;;;;;AA8BA,eAAsB,eAAe,IAAQ,IAAmC;AAC9E,MAAI,gBAAgB,MAAM,iBAAiB,IAAI,IAAI,EAAE,YAAY,eAAe,CAAC;AACjF,MAAI,SAAS,MAAM,iBAAiB,IAAI,EAAE;AAC1C,MAAI,UAAU;AACd,MAAI,OAAO,WAAW,SAAS,GAAG;AAChC,cAAU;AACV,oBAAgB,MAAM,iBAAiB,IAAI,IAAI,EAAE,YAAY,eAAe,CAAC;AAC7E,aAAS,MAAM,iBAAiB,IAAI,EAAE;AAAA,EACxC;AACA,SAAO,EAAE,eAAe,QAAQ,QAAQ;AAC1C;AAKO,SAAS,iBAAiB,GAAuB;AACtD,QAAM,QAAkB,CAAC;AACzB,MAAI,EAAE,cAAc,mBAAmB,SAAS,GAAG;AACjD,UAAM;AAAA,MACJ,UAAK,EAAE,cAAc,mBAAmB,MAAM,2EACX,EAAE,cAAc,mBAAmB,KAAK,IAAI,CAAC;AAAA,IAClF;AAAA,EACF;AAOA,QAAM,KAAK,GAAG,kBAAkB,EAAE,cAAc,MAAM,CAAC;AACvD,MAAI,EAAE,OAAO,WAAW,SAAS,GAAG;AAClC,UAAM,KAAK,mBAAmB,EAAE,MAAM,CAAC;AAAA,EACzC;AACA,QAAM;AAAA,IACJ,oBAAoB,EAAE,cAAc,KAAK,YAAY,EAAE,cAAc,OAAO,iBAC1D,EAAE,cAAc,mBAAmB,iBACnC,EAAE,cAAc,mBAAmB,YACxC,EAAE,UAAU,IAAI,CAAC,eAAe,EAAE,OAAO,WAAW,MAAM;AAAA,EACzE;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;","names":[]}
1
+ {"version":3,"sources":["../src/db/sync.ts"],"sourcesContent":["/** Phase 2 backbone of the Airtable → Turso migration (#539): the hourly sync.\n *\n * In Phase 2 readers move to Turso while writers still write Airtable, so\n * Turso is only as fresh as the last import. One sync pass = import (with\n * reportHtml \"when-missing\", so hourly runs don't re-download every report\n * attachment) followed by the parity check, so every pass both refreshes the\n * copy and PROVES it converged — the same instrument the whole cutover trusts.\n *\n * The retry exists for a specific race, not for flakiness: the importer and\n * the parity check each read Airtable separately, so a write landing between\n * those two reads makes parity flag rows the import genuinely never saw. One\n * re-import + re-check converges unless Airtable is being written continuously\n * — a persistent mismatch after the retry is real drift and must red the run.\n */\nimport type { Db } from \"./client.js\";\nimport {\n importFleetState,\n formatReapSummary,\n type ImportIo,\n type ImportSummary,\n} from \"./import-airtable.js\";\nimport { checkFleetParity, formatParityResult, type ParityResult } from \"./parity.js\";\n\nexport type SyncResult = {\n importSummary: ImportSummary;\n parity: ParityResult;\n /** True when the first parity check mismatched and the pass re-imported. */\n retried: boolean;\n};\n\nexport async function syncFleetState(db: Db, io: ImportIo): Promise<SyncResult> {\n let importSummary = await importFleetState(db, io, { reportHtml: \"when-missing\" });\n let parity = await checkFleetParity(db, io);\n let retried = false;\n if (parity.mismatches.length > 0) {\n retried = true;\n importSummary = await importFleetState(db, io, { reportHtml: \"when-missing\" });\n parity = await checkFleetParity(db, io);\n }\n return { importSummary, parity, retried };\n}\n\n/** Human lines plus the machine gate line, emitted on EVERY run (count=0\n * included) — an absent FLEET_SYNC line means the sync crashed, never that it\n * was clean. */\nexport function formatSyncResult(r: SyncResult): string {\n const lines: string[] = [];\n if (r.importSummary.renderedHtmlMisses.length > 0) {\n lines.push(\n `⚠ ${r.importSummary.renderedHtmlMisses.length} report(s) imported WITHOUT Rendered HTML ` +\n `(fetch failed / URL expired): ${r.importSummary.renderedHtmlMisses.join(\", \")}`,\n );\n }\n // The reap is the only destructive step, so every removal is NAMED and every\n // refusal is quoted in full. FLEET_REAP rides its own line rather than as new\n // keys on FLEET_SYNC: the workflow gate greps `^FLEET_SYNC .* mismatches=0$`,\n // anchored at the end, so extending that line is a contract change waiting to\n // be got wrong. A separate always-emitted line costs nothing and, like\n // FLEET_PARITY, means an absent line reads as \"never ran\", not as \"clean\".\n lines.push(...formatReapSummary(r.importSummary.reaped));\n if (r.parity.mismatches.length > 0) {\n lines.push(formatParityResult(r.parity));\n }\n lines.push(\n `FLEET_SYNC sites=${r.importSummary.sites} reports=${r.importSummary.reports} ` +\n `html_fetched=${r.importSummary.renderedHtmlFetched} ` +\n `html_skipped=${r.importSummary.renderedHtmlSkipped} ` +\n `retried=${r.retried ? 1 : 0} mismatches=${r.parity.mismatches.length}`,\n );\n return lines.join(\"\\n\");\n}\n"],"mappings":";;;;;;;;;;;;;;AA8BA,eAAsB,eAAe,IAAQ,IAAmC;AAC9E,MAAI,gBAAgB,MAAM,iBAAiB,IAAI,IAAI,EAAE,YAAY,eAAe,CAAC;AACjF,MAAI,SAAS,MAAM,iBAAiB,IAAI,EAAE;AAC1C,MAAI,UAAU;AACd,MAAI,OAAO,WAAW,SAAS,GAAG;AAChC,cAAU;AACV,oBAAgB,MAAM,iBAAiB,IAAI,IAAI,EAAE,YAAY,eAAe,CAAC;AAC7E,aAAS,MAAM,iBAAiB,IAAI,EAAE;AAAA,EACxC;AACA,SAAO,EAAE,eAAe,QAAQ,QAAQ;AAC1C;AAKO,SAAS,iBAAiB,GAAuB;AACtD,QAAM,QAAkB,CAAC;AACzB,MAAI,EAAE,cAAc,mBAAmB,SAAS,GAAG;AACjD,UAAM;AAAA,MACJ,UAAK,EAAE,cAAc,mBAAmB,MAAM,2EACX,EAAE,cAAc,mBAAmB,KAAK,IAAI,CAAC;AAAA,IAClF;AAAA,EACF;AAOA,QAAM,KAAK,GAAG,kBAAkB,EAAE,cAAc,MAAM,CAAC;AACvD,MAAI,EAAE,OAAO,WAAW,SAAS,GAAG;AAClC,UAAM,KAAK,mBAAmB,EAAE,MAAM,CAAC;AAAA,EACzC;AACA,QAAM;AAAA,IACJ,oBAAoB,EAAE,cAAc,KAAK,YAAY,EAAE,cAAc,OAAO,iBAC1D,EAAE,cAAc,mBAAmB,iBACnC,EAAE,cAAc,mBAAmB,YACxC,EAAE,UAAU,IAAI,CAAC,eAAe,EAAE,OAAO,WAAW,MAAM;AAAA,EACzE;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;","names":[]}
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-TVL5MVUF.js";
9
9
  import {
10
10
  resolveSites
11
- } from "./chunk-CROKUHJB.js";
11
+ } from "./chunk-Q47DU255.js";
12
12
  import {
13
13
  ALL_CONFIG_NAMES,
14
14
  CANONICAL_GITIGNORE_ENTRIES,
@@ -21,8 +21,8 @@ import {
21
21
  templatesByName
22
22
  } from "./chunk-73UFRZKY.js";
23
23
  import "./chunk-3G25KIWW.js";
24
- import "./chunk-6TWWY7ZU.js";
25
- import "./chunk-EVBCK2KC.js";
24
+ import "./chunk-Z3JXLULD.js";
25
+ import "./chunk-RARREDQE.js";
26
26
  import "./chunk-XTK5VIQB.js";
27
27
  import "./chunk-LBYOLBW7.js";
28
28
  import "./chunk-XXTZBPUY.js";
@@ -113,4 +113,4 @@ async function runSyncConfigsCommand(site, opts) {
113
113
  export {
114
114
  runSyncConfigsCommand
115
115
  };
116
- //# sourceMappingURL=sync-configs-SPTLFJCC.js.map
116
+ //# sourceMappingURL=sync-configs-34JIA6KZ.js.map