@reddoorla/maintenance 0.86.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 (233) hide show
  1. package/dist/airtable-QG5ICI3Y.js +9 -0
  2. package/dist/{announce-MXTUQ6C7.js → announce-GKD5YJNB.js} +49 -28
  3. package/dist/announce-GKD5YJNB.js.map +1 -0
  4. package/dist/attachments-NLZIJ5NU.js +9 -0
  5. package/dist/{blux-ZGDSHI3U.js → blux-DN4MI532.js} +5 -5
  6. package/dist/{bump-deps-EC74CPLS.js → bump-deps-KGH5UYSF.js} +4 -4
  7. package/dist/{chunk-N45M3FDK.js → chunk-6I6L3CMX.js} +4 -4
  8. package/dist/{chunk-KTH5ZODA.js → chunk-7BZUOZPU.js} +2 -2
  9. package/dist/{chunk-2EVEFOZY.js → chunk-7FFUBF4I.js} +6 -3
  10. package/dist/chunk-7FFUBF4I.js.map +1 -0
  11. package/dist/{chunk-LDWZSLSU.js → chunk-7NYN2P6I.js} +7 -4
  12. package/dist/{chunk-LDWZSLSU.js.map → chunk-7NYN2P6I.js.map} +1 -1
  13. package/dist/{chunk-X3B723J4.js → chunk-7TMWBXX7.js} +184 -25
  14. package/dist/chunk-7TMWBXX7.js.map +1 -0
  15. package/dist/{chunk-3VQWSE3F.js → chunk-7UAYMAKR.js} +4 -4
  16. package/dist/{chunk-W7Z3BGCI.js → chunk-A2UNJIKH.js} +15 -7
  17. package/dist/chunk-A2UNJIKH.js.map +1 -0
  18. package/dist/{chunk-F5KM4LEQ.js → chunk-ANJNTAMM.js} +4 -70
  19. package/dist/chunk-ANJNTAMM.js.map +1 -0
  20. package/dist/{chunk-2ZPT7GLV.js → chunk-B23FFWU4.js} +4 -4
  21. package/dist/chunk-B23FFWU4.js.map +1 -0
  22. package/dist/{chunk-DJZWYIPP.js → chunk-BGTYPVLT.js} +1 -1
  23. package/dist/chunk-BGTYPVLT.js.map +1 -0
  24. package/dist/chunk-BRYXFH77.js +70 -0
  25. package/dist/chunk-BRYXFH77.js.map +1 -0
  26. package/dist/{chunk-2NDRHXMR.js → chunk-DGZWFI2G.js} +18 -141
  27. package/dist/chunk-DGZWFI2G.js.map +1 -0
  28. package/dist/{chunk-ACBRLLFT.js → chunk-F3JWOPTG.js} +7 -3
  29. package/dist/chunk-F3JWOPTG.js.map +1 -0
  30. package/dist/chunk-GQDDWYU2.js +372 -0
  31. package/dist/chunk-GQDDWYU2.js.map +1 -0
  32. package/dist/{chunk-Z7AXYKQB.js → chunk-IY6256GK.js} +1 -1
  33. package/dist/{chunk-BYK66EWO.js → chunk-J3WEZGIN.js} +52 -46
  34. package/dist/chunk-J3WEZGIN.js.map +1 -0
  35. package/dist/{chunk-BNM5POSJ.js → chunk-JRHIFA5R.js} +2 -2
  36. package/dist/{chunk-ZKXSX4LO.js → chunk-K6SLF6GI.js} +6 -6
  37. package/dist/{chunk-CQR4LIM7.js → chunk-KJGKZLCT.js} +7 -3
  38. package/dist/{chunk-CQR4LIM7.js.map → chunk-KJGKZLCT.js.map} +1 -1
  39. package/dist/{chunk-PPE5KZJK.js → chunk-KMCZA7CH.js} +13 -3
  40. package/dist/chunk-KMCZA7CH.js.map +1 -0
  41. package/dist/{chunk-3SF7D4DY.js → chunk-MFHS7IQ2.js} +36 -1
  42. package/dist/{chunk-3SF7D4DY.js.map → chunk-MFHS7IQ2.js.map} +1 -1
  43. package/dist/{chunk-SNSTJLQW.js → chunk-NO74DXDP.js} +48 -2
  44. package/dist/chunk-NO74DXDP.js.map +1 -0
  45. package/dist/{chunk-C6B4RHXR.js → chunk-O3GT46R2.js} +8 -2
  46. package/dist/chunk-O3GT46R2.js.map +1 -0
  47. package/dist/{chunk-ITIOFN7A.js → chunk-P6AORG32.js} +29 -10
  48. package/dist/chunk-P6AORG32.js.map +1 -0
  49. package/dist/{chunk-SLUQ7R6I.js → chunk-PR343K7F.js} +5 -5
  50. package/dist/{chunk-P2FE5AI6.js → chunk-Q47DU255.js} +4 -4
  51. package/dist/{chunk-EVBCK2KC.js → chunk-RARREDQE.js} +2 -1
  52. package/dist/{chunk-EVBCK2KC.js.map → chunk-RARREDQE.js.map} +1 -1
  53. package/dist/chunk-RUPPWAD6.js +7 -0
  54. package/dist/chunk-RUPPWAD6.js.map +1 -0
  55. package/dist/{chunk-VSBFM7DS.js → chunk-SJOXWJ5Y.js} +3 -3
  56. package/dist/{chunk-TERE5SDF.js → chunk-ULHPALI5.js} +4 -4
  57. package/dist/{chunk-XUGSZP7Z.js → chunk-W45FZEGE.js} +2 -2
  58. package/dist/{chunk-JHGHLKFS.js → chunk-WS6NU475.js} +2 -2
  59. package/dist/chunk-XP5WM2LV.js +146 -0
  60. package/dist/chunk-XP5WM2LV.js.map +1 -0
  61. package/dist/{chunk-ILUNZHYV.js → chunk-YFAXOD7Z.js} +10 -6
  62. package/dist/chunk-YFAXOD7Z.js.map +1 -0
  63. package/dist/{chunk-6TWWY7ZU.js → chunk-Z3JXLULD.js} +2 -2
  64. package/dist/cli/bin.js +34 -29
  65. package/dist/cli/bin.js.map +1 -1
  66. package/dist/cli/commands/audit.d.ts +4 -0
  67. package/dist/cli/commands/audit.js +25 -18
  68. package/dist/cli/commands/audit.js.map +1 -1
  69. package/dist/client-JYKPVCJX.js +11 -0
  70. package/dist/configs/playwright-a11y.js +1 -1
  71. package/dist/{convert-to-pnpm-6L6P74WR.js → convert-to-pnpm-NWB5FCQ2.js} +4 -4
  72. package/dist/{db-B35OGV3G.js → db-V6UIS433.js} +124 -29
  73. package/dist/db-V6UIS433.js.map +1 -0
  74. package/dist/{digest-BL6UJYKH.js → digest-2JEAEJK6.js} +60 -35
  75. package/dist/digest-2JEAEJK6.js.map +1 -0
  76. package/dist/digest-state-CRZRNGDI.js +25 -0
  77. package/dist/digest-state-CRZRNGDI.js.map +1 -0
  78. package/dist/digest-state-F37XUCS5.js +13 -0
  79. package/dist/{dump-P6EBSIOH.js → dump-EPL7AZFR.js} +38 -13
  80. package/dist/dump-EPL7AZFR.js.map +1 -0
  81. package/dist/{email-VTTTKKL5.js → email-SL7464YI.js} +16 -6
  82. package/dist/{email-VTTTKKL5.js.map → email-SL7464YI.js.map} +1 -1
  83. package/dist/{ensure-site-DFTB7MN3.js → ensure-site-FU6527OU.js} +19 -11
  84. package/dist/ensure-site-FU6527OU.js.map +1 -0
  85. package/dist/fleet-state-R5LU7IC3.js +41 -0
  86. package/dist/{forms-notify-target-75U6OU7L.js → forms-notify-target-SGDGXXK5.js} +10 -7
  87. package/dist/forms-notify-target-SGDGXXK5.js.map +1 -0
  88. package/dist/{github-signals-IJUPGCUX.js → github-signals-GWGCKEE2.js} +13 -12
  89. package/dist/{github-signals-IJUPGCUX.js.map → github-signals-GWGCKEE2.js.map} +1 -1
  90. package/dist/{header-image-AWLLQA4L.js → header-image-7A7XLVEU.js} +8 -8
  91. package/dist/{header-images-335L2AKB.js → header-images-SQBCE5OD.js} +21 -1
  92. package/dist/header-images-SQBCE5OD.js.map +1 -0
  93. package/dist/{health-endpoint-WJC2UMYS.js → health-endpoint-UVX6R5N5.js} +4 -4
  94. package/dist/health-mirror-GCDN7BJL.js +46 -0
  95. package/dist/health-mirror-GCDN7BJL.js.map +1 -0
  96. package/dist/{import-airtable-HE43FB4H.js → import-airtable-OHVY344G.js} +7 -5
  97. package/dist/index.d.ts +149 -0
  98. package/dist/index.js +245 -108
  99. package/dist/index.js.map +1 -1
  100. package/dist/{init-NJEOEO7I.js → init-FGTYNGUI.js} +11 -11
  101. package/dist/{launch-XNAX427U.js → launch-5H7LPEGP.js} +59 -29
  102. package/dist/launch-5H7LPEGP.js.map +1 -0
  103. package/dist/{lighthouse-airtable-LBVIYYP5.js → lighthouse-airtable-3GUWHETW.js} +3 -3
  104. package/dist/migrate-YITCXBLS.js +7 -0
  105. package/dist/{notify-C645PGD5.js → notify-4HP4GZ2H.js} +5 -5
  106. package/dist/{onboard-GK4PHPNI.js → onboard-BKCYBAVW.js} +4 -4
  107. package/dist/{orchestrate-77UO4BYJ.js → orchestrate-JNKCWU5K.js} +14 -13
  108. package/dist/parity-NBYZUBFU.js +14 -0
  109. package/dist/pdf-N5QLQ5ZQ.js +38 -0
  110. package/dist/pdf-N5QLQ5ZQ.js.map +1 -0
  111. package/dist/{pipeline-S6CVHDGB.js → pipeline-WYTXSLHX.js} +3 -3
  112. package/dist/{preflight-6WBNU2GZ.js → preflight-IMTYDDD4.js} +15 -14
  113. package/dist/{preflight-6WBNU2GZ.js.map → preflight-IMTYDDD4.js.map} +1 -1
  114. package/dist/{prismic-ci-3RHOZUUT.js → prismic-ci-MY2U6NN7.js} +7 -7
  115. package/dist/{prismic-models-QBGUT2Q3.js → prismic-models-NJN5JORF.js} +15 -10
  116. package/dist/prismic-models-NJN5JORF.js.map +1 -0
  117. package/dist/prospect/types.d.ts +281 -0
  118. package/dist/prospect/types.js +1 -0
  119. package/dist/{prospect-audit-IUNCJ2LV.js → prospect-audit-JMBRVXSG.js} +32 -13
  120. package/dist/prospect-audit-JMBRVXSG.js.map +1 -0
  121. package/dist/recipes/sync-configs.js +2 -2
  122. package/dist/render-VX72AESY.js +10 -0
  123. package/dist/render-VX72AESY.js.map +1 -0
  124. package/dist/render-from-row-DOYJDO2L.js +17 -0
  125. package/dist/render-from-row-DOYJDO2L.js.map +1 -0
  126. package/dist/{renovate-dispatch-QJY75ODR.js → renovate-dispatch-6GQWTRKX.js} +10 -10
  127. package/dist/{renovate-dispatch-QJY75ODR.js.map → renovate-dispatch-6GQWTRKX.js.map} +1 -1
  128. package/dist/{replay-C2LXMJZP.js → replay-4S6ENYCH.js} +3 -3
  129. package/dist/{report-4EVNDHOZ.js → report-TALY5P3L.js} +63 -21
  130. package/dist/report-TALY5P3L.js.map +1 -0
  131. package/dist/report-mirror-AAK3MXHY.js +55 -0
  132. package/dist/report-mirror-AAK3MXHY.js.map +1 -0
  133. package/dist/rerender-3FM6DNRH.js +32 -0
  134. package/dist/rerender-3FM6DNRH.js.map +1 -0
  135. package/dist/schema-75PJECAE.js +18 -0
  136. package/dist/schema-75PJECAE.js.map +1 -0
  137. package/dist/{self-updating-6DWI2Z56.js → self-updating-NTCCWJYZ.js} +4 -4
  138. package/dist/{selftest-RC36MTKP.js → selftest-NZTR4SM2.js} +26 -24
  139. package/dist/{selftest-RC36MTKP.js.map → selftest-NZTR4SM2.js.map} +1 -1
  140. package/dist/site-mirror-Q763PLGG.js +62 -0
  141. package/dist/site-mirror-Q763PLGG.js.map +1 -0
  142. package/dist/{smoke-suite-VYY2LXM2.js → smoke-suite-4JFZZYVW.js} +4 -4
  143. package/dist/{spam-classifier-XLBFCO7G.js → spam-classifier-T5H4VTP6.js} +4 -2
  144. package/dist/spam-classifier-T5H4VTP6.js.map +1 -0
  145. package/dist/{submissions-Q7DRFOCD.js → submissions-TEHPBSZJ.js} +5 -5
  146. package/dist/{svelte-codemods-25J2FO22.js → svelte-codemods-QAPDZC6F.js} +4 -4
  147. package/dist/{sync-34HNZZ56.js → sync-GBFV45WX.js} +6 -4
  148. package/dist/{sync-34HNZZ56.js.map → sync-GBFV45WX.js.map} +1 -1
  149. package/dist/{sync-configs-3SE6PSCO.js → sync-configs-34JIA6KZ.js} +6 -6
  150. package/dist/{upgrade-WBRQ62FX.js → upgrade-IR2OO3KP.js} +4 -4
  151. package/dist/{webflow-IRKOIT7S.js → webflow-624FEBFD.js} +4 -4
  152. package/dist/{webhook-VOGOW3W6.js → webhook-XM6EV4XT.js} +2 -2
  153. package/dist/{websites-SXWTP72S.js → websites-D6WYFAES.js} +2 -2
  154. package/dist/websites-D6WYFAES.js.map +1 -0
  155. package/dist/write-audits-to-airtable-OBHGR7DQ.js +17 -0
  156. package/dist/write-audits-to-airtable-OBHGR7DQ.js.map +1 -0
  157. package/package.json +5 -1
  158. package/dist/airtable-G7WBQQMI.js +0 -9
  159. package/dist/announce-MXTUQ6C7.js.map +0 -1
  160. package/dist/chunk-2EVEFOZY.js.map +0 -1
  161. package/dist/chunk-2NDRHXMR.js.map +0 -1
  162. package/dist/chunk-2ZPT7GLV.js.map +0 -1
  163. package/dist/chunk-ACBRLLFT.js.map +0 -1
  164. package/dist/chunk-BYK66EWO.js.map +0 -1
  165. package/dist/chunk-C6B4RHXR.js.map +0 -1
  166. package/dist/chunk-DJZWYIPP.js.map +0 -1
  167. package/dist/chunk-F5KM4LEQ.js.map +0 -1
  168. package/dist/chunk-ILUNZHYV.js.map +0 -1
  169. package/dist/chunk-ITIOFN7A.js.map +0 -1
  170. package/dist/chunk-PPE5KZJK.js.map +0 -1
  171. package/dist/chunk-SNSTJLQW.js.map +0 -1
  172. package/dist/chunk-W7Z3BGCI.js.map +0 -1
  173. package/dist/chunk-X3B723J4.js.map +0 -1
  174. package/dist/client-AUE3XUPO.js +0 -11
  175. package/dist/db-B35OGV3G.js.map +0 -1
  176. package/dist/digest-BL6UJYKH.js.map +0 -1
  177. package/dist/dump-P6EBSIOH.js.map +0 -1
  178. package/dist/ensure-site-DFTB7MN3.js.map +0 -1
  179. package/dist/forms-notify-target-75U6OU7L.js.map +0 -1
  180. package/dist/header-images-335L2AKB.js.map +0 -1
  181. package/dist/health-mirror-4B2L7T2Y.js +0 -75
  182. package/dist/health-mirror-4B2L7T2Y.js.map +0 -1
  183. package/dist/launch-XNAX427U.js.map +0 -1
  184. package/dist/migrate-GQH237A6.js +0 -7
  185. package/dist/parity-MWS24A3H.js +0 -12
  186. package/dist/prismic-models-QBGUT2Q3.js.map +0 -1
  187. package/dist/prospect-audit-IUNCJ2LV.js.map +0 -1
  188. package/dist/render-AQAVQDPZ.js +0 -10
  189. package/dist/report-4EVNDHOZ.js.map +0 -1
  190. package/dist/write-audits-to-airtable-MBEILLPU.js +0 -14
  191. /package/dist/{airtable-G7WBQQMI.js.map → airtable-QG5ICI3Y.js.map} +0 -0
  192. /package/dist/{client-AUE3XUPO.js.map → attachments-NLZIJ5NU.js.map} +0 -0
  193. /package/dist/{blux-ZGDSHI3U.js.map → blux-DN4MI532.js.map} +0 -0
  194. /package/dist/{bump-deps-EC74CPLS.js.map → bump-deps-KGH5UYSF.js.map} +0 -0
  195. /package/dist/{chunk-N45M3FDK.js.map → chunk-6I6L3CMX.js.map} +0 -0
  196. /package/dist/{chunk-KTH5ZODA.js.map → chunk-7BZUOZPU.js.map} +0 -0
  197. /package/dist/{chunk-3VQWSE3F.js.map → chunk-7UAYMAKR.js.map} +0 -0
  198. /package/dist/{chunk-Z7AXYKQB.js.map → chunk-IY6256GK.js.map} +0 -0
  199. /package/dist/{chunk-BNM5POSJ.js.map → chunk-JRHIFA5R.js.map} +0 -0
  200. /package/dist/{chunk-ZKXSX4LO.js.map → chunk-K6SLF6GI.js.map} +0 -0
  201. /package/dist/{chunk-SLUQ7R6I.js.map → chunk-PR343K7F.js.map} +0 -0
  202. /package/dist/{chunk-P2FE5AI6.js.map → chunk-Q47DU255.js.map} +0 -0
  203. /package/dist/{chunk-VSBFM7DS.js.map → chunk-SJOXWJ5Y.js.map} +0 -0
  204. /package/dist/{chunk-TERE5SDF.js.map → chunk-ULHPALI5.js.map} +0 -0
  205. /package/dist/{chunk-XUGSZP7Z.js.map → chunk-W45FZEGE.js.map} +0 -0
  206. /package/dist/{chunk-JHGHLKFS.js.map → chunk-WS6NU475.js.map} +0 -0
  207. /package/dist/{chunk-6TWWY7ZU.js.map → chunk-Z3JXLULD.js.map} +0 -0
  208. /package/dist/{import-airtable-HE43FB4H.js.map → client-JYKPVCJX.js.map} +0 -0
  209. /package/dist/{convert-to-pnpm-6L6P74WR.js.map → convert-to-pnpm-NWB5FCQ2.js.map} +0 -0
  210. /package/dist/{lighthouse-airtable-LBVIYYP5.js.map → digest-state-F37XUCS5.js.map} +0 -0
  211. /package/dist/{migrate-GQH237A6.js.map → fleet-state-R5LU7IC3.js.map} +0 -0
  212. /package/dist/{header-image-AWLLQA4L.js.map → header-image-7A7XLVEU.js.map} +0 -0
  213. /package/dist/{health-endpoint-WJC2UMYS.js.map → health-endpoint-UVX6R5N5.js.map} +0 -0
  214. /package/dist/{notify-C645PGD5.js.map → import-airtable-OHVY344G.js.map} +0 -0
  215. /package/dist/{init-NJEOEO7I.js.map → init-FGTYNGUI.js.map} +0 -0
  216. /package/dist/{orchestrate-77UO4BYJ.js.map → lighthouse-airtable-3GUWHETW.js.map} +0 -0
  217. /package/dist/{parity-MWS24A3H.js.map → migrate-YITCXBLS.js.map} +0 -0
  218. /package/dist/{pipeline-S6CVHDGB.js.map → notify-4HP4GZ2H.js.map} +0 -0
  219. /package/dist/{onboard-GK4PHPNI.js.map → onboard-BKCYBAVW.js.map} +0 -0
  220. /package/dist/{render-AQAVQDPZ.js.map → orchestrate-JNKCWU5K.js.map} +0 -0
  221. /package/dist/{spam-classifier-XLBFCO7G.js.map → parity-NBYZUBFU.js.map} +0 -0
  222. /package/dist/{websites-SXWTP72S.js.map → pipeline-WYTXSLHX.js.map} +0 -0
  223. /package/dist/{prismic-ci-3RHOZUUT.js.map → prismic-ci-MY2U6NN7.js.map} +0 -0
  224. /package/dist/{write-audits-to-airtable-MBEILLPU.js.map → prospect/types.js.map} +0 -0
  225. /package/dist/{replay-C2LXMJZP.js.map → replay-4S6ENYCH.js.map} +0 -0
  226. /package/dist/{self-updating-6DWI2Z56.js.map → self-updating-NTCCWJYZ.js.map} +0 -0
  227. /package/dist/{smoke-suite-VYY2LXM2.js.map → smoke-suite-4JFZZYVW.js.map} +0 -0
  228. /package/dist/{submissions-Q7DRFOCD.js.map → submissions-TEHPBSZJ.js.map} +0 -0
  229. /package/dist/{svelte-codemods-25J2FO22.js.map → svelte-codemods-QAPDZC6F.js.map} +0 -0
  230. /package/dist/{sync-configs-3SE6PSCO.js.map → sync-configs-34JIA6KZ.js.map} +0 -0
  231. /package/dist/{upgrade-WBRQ62FX.js.map → upgrade-IR2OO3KP.js.map} +0 -0
  232. /package/dist/{webflow-IRKOIT7S.js.map → webflow-624FEBFD.js.map} +0 -0
  233. /package/dist/{webhook-VOGOW3W6.js.map → webhook-XM6EV4XT.js.map} +0 -0
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isPrivateOrLoopbackHost
3
- } from "./chunk-EVBCK2KC.js";
3
+ } from "./chunk-RARREDQE.js";
4
4
 
5
5
  // src/prospect/crawl.ts
6
6
  import { parse as parse2, NodeType as NodeType2 } from "node-html-parser";
@@ -264,6 +264,17 @@ function decodeXmlText(s) {
264
264
  function parseSitemapLocs(xml) {
265
265
  return [...xml.matchAll(/<(?:[\w-]+:)?loc\b[^>]*>([\s\S]*?)<\/(?:[\w-]+:)?loc>/gi)].map((m) => decodeXmlText(m[1] ?? "").trim()).filter(Boolean);
266
266
  }
267
+ function isSafeNestedSitemap(child, origin) {
268
+ let url;
269
+ try {
270
+ url = new URL(child, origin);
271
+ } catch {
272
+ return false;
273
+ }
274
+ if (url.protocol !== "https:" && url.protocol !== "http:") return false;
275
+ if (url.origin !== origin) return false;
276
+ return !isPrivateOrLoopbackHost(url.hostname);
277
+ }
267
278
  function isSitemapIndex(xml) {
268
279
  return /<(?:[\w-]+:)?sitemapindex[\s>]/i.test(xml);
269
280
  }
@@ -347,7 +358,8 @@ async function fetchSidecars(origin, deps) {
347
358
  if (sitemap.res && /<(urlset|sitemapindex)[\s>]/i.test(sitemap.res.body)) {
348
359
  sitemapPresent = true;
349
360
  if (isSitemapIndex(sitemap.res.body)) {
350
- for (const child of parseSitemapLocs(sitemap.res.body).slice(0, 3)) {
361
+ const children = parseSitemapLocs(sitemap.res.body).filter((child) => isSafeNestedSitemap(child, origin)).slice(0, 3);
362
+ for (const child of children) {
351
363
  const nested = await optional(deps, child);
352
364
  if (nested.res) sitemapUrls.push(...parseSitemapLocs(nested.res.body));
353
365
  }
@@ -369,6 +381,14 @@ async function crawlSite(rawUrl, deps) {
369
381
  start.hash = "";
370
382
  start.username = "";
371
383
  start.password = "";
384
+ if (isPrivateOrLoopbackHost(start.hostname)) {
385
+ throw Object.assign(
386
+ new Error(
387
+ `${start.toString()} is a private address (${start.hostname}) \u2014 refusing to crawl it.`
388
+ ),
389
+ { exitCode: 1 }
390
+ );
391
+ }
372
392
  let home;
373
393
  try {
374
394
  home = await deps.fetchUrl(start.toString());
@@ -720,9 +740,8 @@ var AnalyzeSchema = z.object({
720
740
  businessName: z.string(),
721
741
  business: z.string(),
722
742
  entityClarity: z.object({ score: z.number().min(0).max(100), missing: z.array(z.string()) }),
723
- // 6-10, not just "an array": this also seeds the live-search probes in the next
724
- // stage, so a thin or empty response must fail loudly here rather than quietly
725
- // starving that stage of questions to ask.
743
+ // 6-10, not just "an array": a thin or empty response must fail loudly here
744
+ // rather than quietly starving the report's Answers section.
726
745
  buyerQuestions: z.array(
727
746
  z.object({
728
747
  question: z.string(),
@@ -732,6 +751,14 @@ var AnalyzeSchema = z.object({
732
751
  evidence: z.string().nullable()
733
752
  })
734
753
  ).min(6).max(10),
754
+ // Seeds the live-search probes in the next stage. Deliberately NOT the same
755
+ // strings as buyerQuestions: those are written about THIS site and read
756
+ // correctly only beside it ("What services does this agency offer?"), so as
757
+ // standalone searches they are unanswerable — proved in production, where an
758
+ // engine handed one replied "I don't have any context about who 'they'
759
+ // refers to" and the category score collapsed to a measurement of our own
760
+ // malformed prompt. A probe query must stand alone with no antecedent.
761
+ categoryQueries: z.array(z.string()).min(3).max(5),
735
762
  // No documented floor (a clean site may legitimately need none), but an
736
763
  // unbounded array had no cost/context ceiling either — a report's fix list
737
764
  // is a prioritized top set, not an exhaustive audit, so it's bounded the
@@ -780,6 +807,20 @@ Return:
780
807
  the site answers it (yes/partial/no), whether there is a passage an AI could quote verbatim, the page
781
808
  it lives on, and the evidence quote. evidence must be an EXACT substring of that page's quoted text \u2014
782
809
  copied verbatim, never paraphrased or invented \u2014 or null when no exact quote supports the answer.
810
+ - categoryQueries: 5 searches a buyer types BEFORE they have heard of this company, chosen so that this
811
+ company could PLAUSIBLY RANK for them today \u2014 not ones it arguably deserves. A broad head term
812
+ ("branding agency Los Angeles") returns directories and listicles, which is where small firms are
813
+ aggregated rather than surfaced, so a query like that measures nothing about this company. Give a
814
+ spread: at most ONE head term, and at least THREE that are long-tail \u2014 a specific service, a
815
+ narrower niche or industry, a smaller locality, or a question phrased the way a buyer types it.
816
+ Prefer the specific over the impressive. Each one is sent verbatim to a live answer engine on its
817
+ own, with no other context, so it must stand alone: name the service and the place or the qualifier
818
+ a buyer would use ("trade show booth design for medical device companies", "how much does a rebrand
819
+ cost for a B2B company", "packaging design studio San Antonio").
820
+ Never refer to the company \u2014 not by name, and not as "this agency", "they", "them" or "you". A query
821
+ that names the company measures nothing (the engine just echoes the name back); a query that points
822
+ at it with a pronoun has no antecedent and the engine will answer that it does not know who is meant.
823
+ These are searches, not conversational questions, and they are not the buyerQuestions above.
783
824
  - fixes: prioritized, concrete, specific to this site. No generic SEO advice.
784
825
  - narrative: two or three plain sentences per report section, addressed to the business owner. No
785
826
  jargon, no hedging.`;
@@ -887,16 +928,19 @@ function verifyEvidence(result, crawl) {
887
928
  if (view) textByUrl.set(p.url, view.text);
888
929
  }
889
930
  const buyerQuestions = result.buyerQuestions.map((q) => {
890
- if (q.page === null) {
891
- return q.evidence === null ? q : { ...q, evidence: null };
892
- }
893
- if (!crawledUrls.has(q.page)) {
894
- return { ...q, page: null, evidence: null };
895
- }
896
- if (q.evidence === null) return q;
897
- const pageText = textByUrl.get(q.page) ?? "";
898
- const verified = normalizeWhitespace(pageText).includes(normalizeWhitespace(q.evidence));
899
- return verified ? q : { ...q, evidence: null };
931
+ const verified = (() => {
932
+ if (q.page === null) {
933
+ return q.evidence === null ? q : { ...q, evidence: null };
934
+ }
935
+ if (!crawledUrls.has(q.page)) {
936
+ return { ...q, page: null, evidence: null };
937
+ }
938
+ if (q.evidence === null) return q;
939
+ const pageText = textByUrl.get(q.page) ?? "";
940
+ const quoted = normalizeWhitespace(pageText).includes(normalizeWhitespace(q.evidence));
941
+ return quoted ? q : { ...q, evidence: null };
942
+ })();
943
+ return verified.evidence === null && verified.answered !== "no" ? { ...verified, answered: "no" } : verified;
900
944
  });
901
945
  return { ...result, buyerQuestions };
902
946
  }
@@ -907,8 +951,9 @@ async function analyzeSite(url, crawl, checks, deps = defaultAnalyzeDeps()) {
907
951
  }
908
952
 
909
953
  // src/prospect/probes.ts
910
- var MAX_QUERIES = 8;
954
+ var MAX_QUERIES = 9;
911
955
  var SNIPPET_CHARS = 300;
956
+ var PROBE_MODEL = "claude-sonnet-5";
912
957
  function domainOf(raw) {
913
958
  const withScheme = /^https?:\/\//i.test(raw) ? raw : `https://${raw}`;
914
959
  try {
@@ -921,11 +966,13 @@ function isSameSite(cited, prospect) {
921
966
  return cited === prospect || cited.endsWith(`.${prospect}`) || prospect.endsWith(`.${cited}`);
922
967
  }
923
968
  var MAX_NAME_CHARS = 60;
969
+ var NAME_ABBREVIATIONS = /\b(st|mt|ft|dr|mr|mrs|ms|jr|sr|co|inc|ltd|llc|corp|assoc|bros|dept|ave|blvd|rd)\.\s/gi;
970
+ var INITIALS = /\b[a-z]\.\s/gi;
924
971
  function resolveBusinessName(business, url) {
925
972
  const trimmed = business.trim();
926
- if (!trimmed || trimmed.length > MAX_NAME_CHARS || /\.\s/.test(trimmed)) {
927
- return domainOf(url);
928
- }
973
+ if (!trimmed || trimmed.length > MAX_NAME_CHARS) return domainOf(url);
974
+ const withoutAbbreviations = trimmed.replace(NAME_ABBREVIATIONS, "").replace(INITIALS, "");
975
+ if (/\.\s/.test(withoutAbbreviations)) return domainOf(url);
929
976
  return trimmed;
930
977
  }
931
978
  function buildQueries(input) {
@@ -933,7 +980,11 @@ function buildQueries(input) {
933
980
  const candidates = [
934
981
  { query: `who is ${name}`, kind: "branded" },
935
982
  { query: `${name} reviews`, kind: "branded" },
936
- ...input.buyerQuestions.slice(0, 3).map((query) => ({ query, kind: "category" })),
983
+ // All five, not three. The schema asks the model for up to five and we were
984
+ // paying to generate them, then discarding two — which also pinned the
985
+ // denominator at 3, making visibilityScore a four-valued {0,33,67,100}
986
+ // rendered on a 0-100 card. Five halves the step to 20 points.
987
+ ...input.categoryQueries.slice(0, 5).map((query) => ({ query, kind: "category" })),
937
988
  ...input.competitors.slice(0, 2).map((c) => ({ query: `${name} vs ${c}`, kind: "competitor" }))
938
989
  ];
939
990
  const seen = /* @__PURE__ */ new Set();
@@ -945,6 +996,110 @@ function buildQueries(input) {
945
996
  }
946
997
  return deduped.slice(0, MAX_QUERIES);
947
998
  }
999
+ function escapeRegExp(s) {
1000
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1001
+ }
1002
+ var LEGAL_SUFFIX = /\b(llc|inc|incorporated|ltd|limited|corp|corporation|plc|llp|lp|pllc|pc)\b/g;
1003
+ function normalizeForMatch(s) {
1004
+ return s.toLowerCase().replace(/&/g, " and ").replace(/[‐-―]/g, " ").replace(/[^a-z0-9]+/g, " ").replace(LEGAL_SUFFIX, " ").replace(/\s+/g, " ").trim();
1005
+ }
1006
+ function mentionsBrand(answer, brand) {
1007
+ const needle = normalizeForMatch(brand);
1008
+ if (!needle) return false;
1009
+ return new RegExp(`(^| )${escapeRegExp(needle)}( |$)`).test(normalizeForMatch(answer));
1010
+ }
1011
+ var CATEGORY_WORDS = /* @__PURE__ */ new Set([
1012
+ // articles and joiners
1013
+ "the",
1014
+ "a",
1015
+ "an",
1016
+ "and",
1017
+ "of",
1018
+ "for",
1019
+ "at",
1020
+ "by",
1021
+ // company words
1022
+ "co",
1023
+ "company",
1024
+ "group",
1025
+ "collective",
1026
+ "partners",
1027
+ "associates",
1028
+ "works",
1029
+ "lab",
1030
+ "labs",
1031
+ "studio",
1032
+ "studios",
1033
+ "agency",
1034
+ "agencies",
1035
+ "firm",
1036
+ "practice",
1037
+ "shop",
1038
+ "house",
1039
+ "office",
1040
+ // sector words
1041
+ "design",
1042
+ "designs",
1043
+ "creative",
1044
+ "creatives",
1045
+ "brand",
1046
+ "branding",
1047
+ "marketing",
1048
+ "media",
1049
+ "digital",
1050
+ "solutions",
1051
+ "services",
1052
+ "consulting",
1053
+ "consultants",
1054
+ "advisors",
1055
+ "strategy",
1056
+ "dental",
1057
+ "dentistry",
1058
+ "orthodontics",
1059
+ "law",
1060
+ "legal",
1061
+ "clinic",
1062
+ "care",
1063
+ "health",
1064
+ "medical",
1065
+ "roofing",
1066
+ "construction",
1067
+ "builders",
1068
+ "contracting",
1069
+ "plumbing",
1070
+ "electric",
1071
+ "landscaping",
1072
+ "interiors",
1073
+ "architects",
1074
+ "photography",
1075
+ "films",
1076
+ "productions",
1077
+ "printing",
1078
+ "packaging",
1079
+ // adjectives that market rather than identify
1080
+ "modern",
1081
+ "premier",
1082
+ "elite",
1083
+ "first",
1084
+ "best",
1085
+ "local",
1086
+ "family",
1087
+ "advanced",
1088
+ "complete",
1089
+ "quality",
1090
+ "professional",
1091
+ "trusted",
1092
+ "expert",
1093
+ "affordable",
1094
+ "custom",
1095
+ "creative"
1096
+ ]);
1097
+ function isDistinctiveName(brand) {
1098
+ if (brand.includes(".")) return true;
1099
+ const tokens = normalizeForMatch(brand).split(" ").filter(Boolean);
1100
+ if (tokens.length < 2) return false;
1101
+ return tokens.some((t) => !CATEGORY_WORDS.has(t));
1102
+ }
948
1103
  function isRateLimited(err) {
949
1104
  const message = err instanceof Error ? err.message : String(err);
950
1105
  return /429|529/.test(message);
@@ -957,6 +1112,7 @@ async function runVisibilityProbes(input, engines, opts = {}) {
957
1112
  const queries = buildQueries(input);
958
1113
  const prospect = domainOf(input.url);
959
1114
  const brand = resolveBusinessName(input.business, input.url).toLowerCase();
1115
+ const nameIsDistinctive = isDistinctiveName(brand);
960
1116
  const answers = [];
961
1117
  const competitorCounts = /* @__PURE__ */ new Map();
962
1118
  for (const engine of engines) {
@@ -978,7 +1134,7 @@ async function runVisibilityProbes(input, engines, opts = {}) {
978
1134
  }
979
1135
  const citedDomains = reply.citedDomains.map(domainOf);
980
1136
  const domainCited = citedDomains.some((d) => isSameSite(d, prospect));
981
- const brandMentioned = brand.length > 0 && reply.answer.toLowerCase().includes(brand);
1137
+ const brandMentioned = mentionsBrand(reply.answer.toLowerCase(), brand);
982
1138
  for (const d of citedDomains) {
983
1139
  if (isSameSite(d, prospect)) continue;
984
1140
  competitorCounts.set(d, (competitorCounts.get(d) ?? 0) + 1);
@@ -989,6 +1145,8 @@ async function runVisibilityProbes(input, engines, opts = {}) {
989
1145
  kind,
990
1146
  domainCited,
991
1147
  brandMentioned,
1148
+ // The same expression the score uses below — written once, read twice.
1149
+ countedAsVisible: domainCited || brandMentioned && nameIsDistinctive,
992
1150
  citedDomains,
993
1151
  snippet: reply.answer.slice(0, SNIPPET_CHARS),
994
1152
  truncated: reply.answer.length > SNIPPET_CHARS,
@@ -1002,7 +1160,7 @@ async function runVisibilityProbes(input, engines, opts = {}) {
1002
1160
  throw new Error("no visibility engine returned an answer");
1003
1161
  }
1004
1162
  const categoryAnswers = answers.filter((a) => a.kind === "category");
1005
- const visibleCategory = categoryAnswers.filter((a) => a.domainCited || a.brandMentioned).length;
1163
+ const visibleCategory = categoryAnswers.filter((a) => a.countedAsVisible).length;
1006
1164
  const visibilityScore = categoryAnswers.length === 0 ? null : Math.round(visibleCategory / categoryAnswers.length * 100);
1007
1165
  const brandedRecognized = answers.some((a) => a.kind === "branded" && a.domainCited);
1008
1166
  return {
@@ -1050,7 +1208,7 @@ function claudeWebSearchEngine(createMessage) {
1050
1208
  const collected = [];
1051
1209
  for (let turn = 0; turn < MAX_CLAUDE_TURNS; turn++) {
1052
1210
  const res = await create({
1053
- model: "claude-opus-5",
1211
+ model: PROBE_MODEL,
1054
1212
  max_tokens: 4e3,
1055
1213
  tools: [{ type: "web_search_20260209", name: "web_search", max_uses: 4 }],
1056
1214
  messages
@@ -1175,7 +1333,7 @@ async function runProspectAudit(url, opts, deps = {}) {
1175
1333
  {
1176
1334
  url,
1177
1335
  business: businessName ?? "",
1178
- buyerQuestions: analyze.ok ? analyze.data.buyerQuestions.map((q) => q.question) : [],
1336
+ categoryQueries: analyze.ok ? analyze.data.categoryQueries : [],
1179
1337
  competitors: opts.competitors ?? []
1180
1338
  },
1181
1339
  deps.engines ?? defaultEngines(),
@@ -1202,8 +1360,9 @@ async function runProspectAudit(url, opts, deps = {}) {
1202
1360
  }
1203
1361
 
1204
1362
  export {
1363
+ resolveBusinessName,
1205
1364
  PROBES_SKIPPED,
1206
1365
  ANALYZE_SKIPPED,
1207
1366
  runProspectAudit
1208
1367
  };
1209
- //# sourceMappingURL=chunk-X3B723J4.js.map
1368
+ //# sourceMappingURL=chunk-7TMWBXX7.js.map