@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.
- package/dist/airtable-QG5ICI3Y.js +9 -0
- package/dist/{announce-MXTUQ6C7.js → announce-GKD5YJNB.js} +49 -28
- package/dist/announce-GKD5YJNB.js.map +1 -0
- package/dist/attachments-NLZIJ5NU.js +9 -0
- package/dist/{blux-ZGDSHI3U.js → blux-DN4MI532.js} +5 -5
- package/dist/{bump-deps-EC74CPLS.js → bump-deps-KGH5UYSF.js} +4 -4
- package/dist/{chunk-N45M3FDK.js → chunk-6I6L3CMX.js} +4 -4
- package/dist/{chunk-KTH5ZODA.js → chunk-7BZUOZPU.js} +2 -2
- package/dist/{chunk-2EVEFOZY.js → chunk-7FFUBF4I.js} +6 -3
- package/dist/chunk-7FFUBF4I.js.map +1 -0
- package/dist/{chunk-LDWZSLSU.js → chunk-7NYN2P6I.js} +7 -4
- package/dist/{chunk-LDWZSLSU.js.map → chunk-7NYN2P6I.js.map} +1 -1
- package/dist/{chunk-X3B723J4.js → chunk-7TMWBXX7.js} +184 -25
- package/dist/chunk-7TMWBXX7.js.map +1 -0
- package/dist/{chunk-3VQWSE3F.js → chunk-7UAYMAKR.js} +4 -4
- package/dist/{chunk-W7Z3BGCI.js → chunk-A2UNJIKH.js} +15 -7
- package/dist/chunk-A2UNJIKH.js.map +1 -0
- package/dist/{chunk-F5KM4LEQ.js → chunk-ANJNTAMM.js} +4 -70
- package/dist/chunk-ANJNTAMM.js.map +1 -0
- package/dist/{chunk-2ZPT7GLV.js → chunk-B23FFWU4.js} +4 -4
- package/dist/chunk-B23FFWU4.js.map +1 -0
- package/dist/{chunk-DJZWYIPP.js → chunk-BGTYPVLT.js} +1 -1
- package/dist/chunk-BGTYPVLT.js.map +1 -0
- package/dist/chunk-BRYXFH77.js +70 -0
- package/dist/chunk-BRYXFH77.js.map +1 -0
- package/dist/{chunk-2NDRHXMR.js → chunk-DGZWFI2G.js} +18 -141
- package/dist/chunk-DGZWFI2G.js.map +1 -0
- package/dist/{chunk-ACBRLLFT.js → chunk-F3JWOPTG.js} +7 -3
- package/dist/chunk-F3JWOPTG.js.map +1 -0
- package/dist/chunk-GQDDWYU2.js +372 -0
- package/dist/chunk-GQDDWYU2.js.map +1 -0
- package/dist/{chunk-Z7AXYKQB.js → chunk-IY6256GK.js} +1 -1
- package/dist/{chunk-BYK66EWO.js → chunk-J3WEZGIN.js} +52 -46
- package/dist/chunk-J3WEZGIN.js.map +1 -0
- package/dist/{chunk-BNM5POSJ.js → chunk-JRHIFA5R.js} +2 -2
- package/dist/{chunk-ZKXSX4LO.js → chunk-K6SLF6GI.js} +6 -6
- package/dist/{chunk-CQR4LIM7.js → chunk-KJGKZLCT.js} +7 -3
- package/dist/{chunk-CQR4LIM7.js.map → chunk-KJGKZLCT.js.map} +1 -1
- package/dist/{chunk-PPE5KZJK.js → chunk-KMCZA7CH.js} +13 -3
- package/dist/chunk-KMCZA7CH.js.map +1 -0
- package/dist/{chunk-3SF7D4DY.js → chunk-MFHS7IQ2.js} +36 -1
- package/dist/{chunk-3SF7D4DY.js.map → chunk-MFHS7IQ2.js.map} +1 -1
- package/dist/{chunk-SNSTJLQW.js → chunk-NO74DXDP.js} +48 -2
- package/dist/chunk-NO74DXDP.js.map +1 -0
- package/dist/{chunk-C6B4RHXR.js → chunk-O3GT46R2.js} +8 -2
- package/dist/chunk-O3GT46R2.js.map +1 -0
- package/dist/{chunk-ITIOFN7A.js → chunk-P6AORG32.js} +29 -10
- package/dist/chunk-P6AORG32.js.map +1 -0
- package/dist/{chunk-SLUQ7R6I.js → chunk-PR343K7F.js} +5 -5
- package/dist/{chunk-P2FE5AI6.js → chunk-Q47DU255.js} +4 -4
- package/dist/{chunk-EVBCK2KC.js → chunk-RARREDQE.js} +2 -1
- package/dist/{chunk-EVBCK2KC.js.map → chunk-RARREDQE.js.map} +1 -1
- package/dist/chunk-RUPPWAD6.js +7 -0
- package/dist/chunk-RUPPWAD6.js.map +1 -0
- package/dist/{chunk-VSBFM7DS.js → chunk-SJOXWJ5Y.js} +3 -3
- package/dist/{chunk-TERE5SDF.js → chunk-ULHPALI5.js} +4 -4
- package/dist/{chunk-XUGSZP7Z.js → chunk-W45FZEGE.js} +2 -2
- package/dist/{chunk-JHGHLKFS.js → chunk-WS6NU475.js} +2 -2
- package/dist/chunk-XP5WM2LV.js +146 -0
- package/dist/chunk-XP5WM2LV.js.map +1 -0
- package/dist/{chunk-ILUNZHYV.js → chunk-YFAXOD7Z.js} +10 -6
- package/dist/chunk-YFAXOD7Z.js.map +1 -0
- package/dist/{chunk-6TWWY7ZU.js → chunk-Z3JXLULD.js} +2 -2
- package/dist/cli/bin.js +34 -29
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.d.ts +4 -0
- package/dist/cli/commands/audit.js +25 -18
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/client-JYKPVCJX.js +11 -0
- package/dist/configs/playwright-a11y.js +1 -1
- package/dist/{convert-to-pnpm-6L6P74WR.js → convert-to-pnpm-NWB5FCQ2.js} +4 -4
- package/dist/{db-B35OGV3G.js → db-V6UIS433.js} +124 -29
- package/dist/db-V6UIS433.js.map +1 -0
- package/dist/{digest-BL6UJYKH.js → digest-2JEAEJK6.js} +60 -35
- package/dist/digest-2JEAEJK6.js.map +1 -0
- package/dist/digest-state-CRZRNGDI.js +25 -0
- package/dist/digest-state-CRZRNGDI.js.map +1 -0
- package/dist/digest-state-F37XUCS5.js +13 -0
- package/dist/{dump-P6EBSIOH.js → dump-EPL7AZFR.js} +38 -13
- package/dist/dump-EPL7AZFR.js.map +1 -0
- package/dist/{email-VTTTKKL5.js → email-SL7464YI.js} +16 -6
- package/dist/{email-VTTTKKL5.js.map → email-SL7464YI.js.map} +1 -1
- package/dist/{ensure-site-DFTB7MN3.js → ensure-site-FU6527OU.js} +19 -11
- package/dist/ensure-site-FU6527OU.js.map +1 -0
- package/dist/fleet-state-R5LU7IC3.js +41 -0
- package/dist/{forms-notify-target-75U6OU7L.js → forms-notify-target-SGDGXXK5.js} +10 -7
- package/dist/forms-notify-target-SGDGXXK5.js.map +1 -0
- package/dist/{github-signals-IJUPGCUX.js → github-signals-GWGCKEE2.js} +13 -12
- package/dist/{github-signals-IJUPGCUX.js.map → github-signals-GWGCKEE2.js.map} +1 -1
- package/dist/{header-image-AWLLQA4L.js → header-image-7A7XLVEU.js} +8 -8
- package/dist/{header-images-335L2AKB.js → header-images-SQBCE5OD.js} +21 -1
- package/dist/header-images-SQBCE5OD.js.map +1 -0
- package/dist/{health-endpoint-WJC2UMYS.js → health-endpoint-UVX6R5N5.js} +4 -4
- package/dist/health-mirror-GCDN7BJL.js +46 -0
- package/dist/health-mirror-GCDN7BJL.js.map +1 -0
- package/dist/{import-airtable-HE43FB4H.js → import-airtable-OHVY344G.js} +7 -5
- package/dist/index.d.ts +149 -0
- package/dist/index.js +245 -108
- package/dist/index.js.map +1 -1
- package/dist/{init-NJEOEO7I.js → init-FGTYNGUI.js} +11 -11
- package/dist/{launch-XNAX427U.js → launch-5H7LPEGP.js} +59 -29
- package/dist/launch-5H7LPEGP.js.map +1 -0
- package/dist/{lighthouse-airtable-LBVIYYP5.js → lighthouse-airtable-3GUWHETW.js} +3 -3
- package/dist/migrate-YITCXBLS.js +7 -0
- package/dist/{notify-C645PGD5.js → notify-4HP4GZ2H.js} +5 -5
- package/dist/{onboard-GK4PHPNI.js → onboard-BKCYBAVW.js} +4 -4
- package/dist/{orchestrate-77UO4BYJ.js → orchestrate-JNKCWU5K.js} +14 -13
- package/dist/parity-NBYZUBFU.js +14 -0
- package/dist/pdf-N5QLQ5ZQ.js +38 -0
- package/dist/pdf-N5QLQ5ZQ.js.map +1 -0
- package/dist/{pipeline-S6CVHDGB.js → pipeline-WYTXSLHX.js} +3 -3
- package/dist/{preflight-6WBNU2GZ.js → preflight-IMTYDDD4.js} +15 -14
- package/dist/{preflight-6WBNU2GZ.js.map → preflight-IMTYDDD4.js.map} +1 -1
- package/dist/{prismic-ci-3RHOZUUT.js → prismic-ci-MY2U6NN7.js} +7 -7
- package/dist/{prismic-models-QBGUT2Q3.js → prismic-models-NJN5JORF.js} +15 -10
- package/dist/prismic-models-NJN5JORF.js.map +1 -0
- package/dist/prospect/types.d.ts +281 -0
- package/dist/prospect/types.js +1 -0
- package/dist/{prospect-audit-IUNCJ2LV.js → prospect-audit-JMBRVXSG.js} +32 -13
- package/dist/prospect-audit-JMBRVXSG.js.map +1 -0
- package/dist/recipes/sync-configs.js +2 -2
- package/dist/render-VX72AESY.js +10 -0
- package/dist/render-VX72AESY.js.map +1 -0
- package/dist/render-from-row-DOYJDO2L.js +17 -0
- package/dist/render-from-row-DOYJDO2L.js.map +1 -0
- package/dist/{renovate-dispatch-QJY75ODR.js → renovate-dispatch-6GQWTRKX.js} +10 -10
- package/dist/{renovate-dispatch-QJY75ODR.js.map → renovate-dispatch-6GQWTRKX.js.map} +1 -1
- package/dist/{replay-C2LXMJZP.js → replay-4S6ENYCH.js} +3 -3
- package/dist/{report-4EVNDHOZ.js → report-TALY5P3L.js} +63 -21
- package/dist/report-TALY5P3L.js.map +1 -0
- package/dist/report-mirror-AAK3MXHY.js +55 -0
- package/dist/report-mirror-AAK3MXHY.js.map +1 -0
- package/dist/rerender-3FM6DNRH.js +32 -0
- package/dist/rerender-3FM6DNRH.js.map +1 -0
- package/dist/schema-75PJECAE.js +18 -0
- package/dist/schema-75PJECAE.js.map +1 -0
- package/dist/{self-updating-6DWI2Z56.js → self-updating-NTCCWJYZ.js} +4 -4
- package/dist/{selftest-RC36MTKP.js → selftest-NZTR4SM2.js} +26 -24
- package/dist/{selftest-RC36MTKP.js.map → selftest-NZTR4SM2.js.map} +1 -1
- package/dist/site-mirror-Q763PLGG.js +62 -0
- package/dist/site-mirror-Q763PLGG.js.map +1 -0
- package/dist/{smoke-suite-VYY2LXM2.js → smoke-suite-4JFZZYVW.js} +4 -4
- package/dist/{spam-classifier-XLBFCO7G.js → spam-classifier-T5H4VTP6.js} +4 -2
- package/dist/spam-classifier-T5H4VTP6.js.map +1 -0
- package/dist/{submissions-Q7DRFOCD.js → submissions-TEHPBSZJ.js} +5 -5
- package/dist/{svelte-codemods-25J2FO22.js → svelte-codemods-QAPDZC6F.js} +4 -4
- package/dist/{sync-34HNZZ56.js → sync-GBFV45WX.js} +6 -4
- package/dist/{sync-34HNZZ56.js.map → sync-GBFV45WX.js.map} +1 -1
- package/dist/{sync-configs-3SE6PSCO.js → sync-configs-34JIA6KZ.js} +6 -6
- package/dist/{upgrade-WBRQ62FX.js → upgrade-IR2OO3KP.js} +4 -4
- package/dist/{webflow-IRKOIT7S.js → webflow-624FEBFD.js} +4 -4
- package/dist/{webhook-VOGOW3W6.js → webhook-XM6EV4XT.js} +2 -2
- package/dist/{websites-SXWTP72S.js → websites-D6WYFAES.js} +2 -2
- package/dist/websites-D6WYFAES.js.map +1 -0
- package/dist/write-audits-to-airtable-OBHGR7DQ.js +17 -0
- package/dist/write-audits-to-airtable-OBHGR7DQ.js.map +1 -0
- package/package.json +5 -1
- package/dist/airtable-G7WBQQMI.js +0 -9
- package/dist/announce-MXTUQ6C7.js.map +0 -1
- package/dist/chunk-2EVEFOZY.js.map +0 -1
- package/dist/chunk-2NDRHXMR.js.map +0 -1
- package/dist/chunk-2ZPT7GLV.js.map +0 -1
- package/dist/chunk-ACBRLLFT.js.map +0 -1
- package/dist/chunk-BYK66EWO.js.map +0 -1
- package/dist/chunk-C6B4RHXR.js.map +0 -1
- package/dist/chunk-DJZWYIPP.js.map +0 -1
- package/dist/chunk-F5KM4LEQ.js.map +0 -1
- package/dist/chunk-ILUNZHYV.js.map +0 -1
- package/dist/chunk-ITIOFN7A.js.map +0 -1
- package/dist/chunk-PPE5KZJK.js.map +0 -1
- package/dist/chunk-SNSTJLQW.js.map +0 -1
- package/dist/chunk-W7Z3BGCI.js.map +0 -1
- package/dist/chunk-X3B723J4.js.map +0 -1
- package/dist/client-AUE3XUPO.js +0 -11
- package/dist/db-B35OGV3G.js.map +0 -1
- package/dist/digest-BL6UJYKH.js.map +0 -1
- package/dist/dump-P6EBSIOH.js.map +0 -1
- package/dist/ensure-site-DFTB7MN3.js.map +0 -1
- package/dist/forms-notify-target-75U6OU7L.js.map +0 -1
- package/dist/header-images-335L2AKB.js.map +0 -1
- package/dist/health-mirror-4B2L7T2Y.js +0 -75
- package/dist/health-mirror-4B2L7T2Y.js.map +0 -1
- package/dist/launch-XNAX427U.js.map +0 -1
- package/dist/migrate-GQH237A6.js +0 -7
- package/dist/parity-MWS24A3H.js +0 -12
- package/dist/prismic-models-QBGUT2Q3.js.map +0 -1
- package/dist/prospect-audit-IUNCJ2LV.js.map +0 -1
- package/dist/render-AQAVQDPZ.js +0 -10
- package/dist/report-4EVNDHOZ.js.map +0 -1
- package/dist/write-audits-to-airtable-MBEILLPU.js +0 -14
- /package/dist/{airtable-G7WBQQMI.js.map → airtable-QG5ICI3Y.js.map} +0 -0
- /package/dist/{client-AUE3XUPO.js.map → attachments-NLZIJ5NU.js.map} +0 -0
- /package/dist/{blux-ZGDSHI3U.js.map → blux-DN4MI532.js.map} +0 -0
- /package/dist/{bump-deps-EC74CPLS.js.map → bump-deps-KGH5UYSF.js.map} +0 -0
- /package/dist/{chunk-N45M3FDK.js.map → chunk-6I6L3CMX.js.map} +0 -0
- /package/dist/{chunk-KTH5ZODA.js.map → chunk-7BZUOZPU.js.map} +0 -0
- /package/dist/{chunk-3VQWSE3F.js.map → chunk-7UAYMAKR.js.map} +0 -0
- /package/dist/{chunk-Z7AXYKQB.js.map → chunk-IY6256GK.js.map} +0 -0
- /package/dist/{chunk-BNM5POSJ.js.map → chunk-JRHIFA5R.js.map} +0 -0
- /package/dist/{chunk-ZKXSX4LO.js.map → chunk-K6SLF6GI.js.map} +0 -0
- /package/dist/{chunk-SLUQ7R6I.js.map → chunk-PR343K7F.js.map} +0 -0
- /package/dist/{chunk-P2FE5AI6.js.map → chunk-Q47DU255.js.map} +0 -0
- /package/dist/{chunk-VSBFM7DS.js.map → chunk-SJOXWJ5Y.js.map} +0 -0
- /package/dist/{chunk-TERE5SDF.js.map → chunk-ULHPALI5.js.map} +0 -0
- /package/dist/{chunk-XUGSZP7Z.js.map → chunk-W45FZEGE.js.map} +0 -0
- /package/dist/{chunk-JHGHLKFS.js.map → chunk-WS6NU475.js.map} +0 -0
- /package/dist/{chunk-6TWWY7ZU.js.map → chunk-Z3JXLULD.js.map} +0 -0
- /package/dist/{import-airtable-HE43FB4H.js.map → client-JYKPVCJX.js.map} +0 -0
- /package/dist/{convert-to-pnpm-6L6P74WR.js.map → convert-to-pnpm-NWB5FCQ2.js.map} +0 -0
- /package/dist/{lighthouse-airtable-LBVIYYP5.js.map → digest-state-F37XUCS5.js.map} +0 -0
- /package/dist/{migrate-GQH237A6.js.map → fleet-state-R5LU7IC3.js.map} +0 -0
- /package/dist/{header-image-AWLLQA4L.js.map → header-image-7A7XLVEU.js.map} +0 -0
- /package/dist/{health-endpoint-WJC2UMYS.js.map → health-endpoint-UVX6R5N5.js.map} +0 -0
- /package/dist/{notify-C645PGD5.js.map → import-airtable-OHVY344G.js.map} +0 -0
- /package/dist/{init-NJEOEO7I.js.map → init-FGTYNGUI.js.map} +0 -0
- /package/dist/{orchestrate-77UO4BYJ.js.map → lighthouse-airtable-3GUWHETW.js.map} +0 -0
- /package/dist/{parity-MWS24A3H.js.map → migrate-YITCXBLS.js.map} +0 -0
- /package/dist/{pipeline-S6CVHDGB.js.map → notify-4HP4GZ2H.js.map} +0 -0
- /package/dist/{onboard-GK4PHPNI.js.map → onboard-BKCYBAVW.js.map} +0 -0
- /package/dist/{render-AQAVQDPZ.js.map → orchestrate-JNKCWU5K.js.map} +0 -0
- /package/dist/{spam-classifier-XLBFCO7G.js.map → parity-NBYZUBFU.js.map} +0 -0
- /package/dist/{websites-SXWTP72S.js.map → pipeline-WYTXSLHX.js.map} +0 -0
- /package/dist/{prismic-ci-3RHOZUUT.js.map → prismic-ci-MY2U6NN7.js.map} +0 -0
- /package/dist/{write-audits-to-airtable-MBEILLPU.js.map → prospect/types.js.map} +0 -0
- /package/dist/{replay-C2LXMJZP.js.map → replay-4S6ENYCH.js.map} +0 -0
- /package/dist/{self-updating-6DWI2Z56.js.map → self-updating-NTCCWJYZ.js.map} +0 -0
- /package/dist/{smoke-suite-VYY2LXM2.js.map → smoke-suite-4JFZZYVW.js.map} +0 -0
- /package/dist/{submissions-Q7DRFOCD.js.map → submissions-TEHPBSZJ.js.map} +0 -0
- /package/dist/{svelte-codemods-25J2FO22.js.map → svelte-codemods-QAPDZC6F.js.map} +0 -0
- /package/dist/{sync-configs-3SE6PSCO.js.map → sync-configs-34JIA6KZ.js.map} +0 -0
- /package/dist/{upgrade-WBRQ62FX.js.map → upgrade-IR2OO3KP.js.map} +0 -0
- /package/dist/{webflow-IRKOIT7S.js.map → webflow-624FEBFD.js.map} +0 -0
- /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-
|
|
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
|
-
|
|
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":
|
|
724
|
-
//
|
|
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
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
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 =
|
|
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
|
|
927
|
-
|
|
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
|
-
|
|
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 =
|
|
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.
|
|
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:
|
|
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
|
-
|
|
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-
|
|
1368
|
+
//# sourceMappingURL=chunk-7TMWBXX7.js.map
|