@reddoorla/maintenance 0.87.0 → 0.88.3
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-I2Z4JN3I.js → announce-GKD5YJNB.js} +9 -9
- package/dist/{blux-ZGDSHI3U.js → blux-EATHOHKK.js} +5 -5
- package/dist/{bump-deps-CGVELRLK.js → bump-deps-KGH5UYSF.js} +4 -4
- package/dist/{chunk-SDDPUAWH.js → chunk-6I6L3CMX.js} +2 -2
- package/dist/{chunk-NXNPDCF3.js → chunk-6UCVWYDA.js} +11 -1
- package/dist/chunk-6UCVWYDA.js.map +1 -0
- package/dist/{chunk-ZBDZTJUO.js → chunk-7FFUBF4I.js} +5 -2
- package/dist/chunk-7FFUBF4I.js.map +1 -0
- package/dist/{chunk-3JQ2FO3D.js → chunk-B23FFWU4.js} +2 -2
- 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-HSI6Q7MI.js → chunk-CEW5PEHZ.js} +168 -31
- package/dist/chunk-CEW5PEHZ.js.map +1 -0
- package/dist/{chunk-NDWYJLRF.js → chunk-DGZWFI2G.js} +3 -3
- package/dist/{chunk-HHMMUR4P.js → chunk-GQDDWYU2.js} +45 -8
- package/dist/chunk-GQDDWYU2.js.map +1 -0
- package/dist/{chunk-BBUSGEES.js → chunk-J3WEZGIN.js} +6 -6
- package/dist/{chunk-BNM5POSJ.js → chunk-JRHIFA5R.js} +2 -2
- package/dist/{chunk-MT2JINSS.js → chunk-KJGKZLCT.js} +3 -3
- 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-C6CLRKPV.js → chunk-PR343K7F.js} +2 -2
- package/dist/{chunk-CROKUHJB.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-HEWSRXYV.js → chunk-ULHPALI5.js} +3 -3
- package/dist/{chunk-XUGSZP7Z.js → chunk-W45FZEGE.js} +2 -2
- package/dist/{chunk-NCMZMLY7.js → chunk-WPL5GERE.js} +5 -69
- package/dist/chunk-WPL5GERE.js.map +1 -0
- package/dist/{chunk-JHGHLKFS.js → chunk-WS6NU475.js} +2 -2
- package/dist/{chunk-DEWJMMG3.js → chunk-XKDCR4UE.js} +19 -8
- package/dist/chunk-XKDCR4UE.js.map +1 -0
- package/dist/{chunk-ZGBEIWSC.js → chunk-XP5WM2LV.js} +5 -5
- package/dist/{chunk-TD2NNXLZ.js → chunk-XSOINOPY.js} +8 -8
- package/dist/{chunk-6TWWY7ZU.js → chunk-Z3JXLULD.js} +2 -2
- package/dist/cli/bin.js +31 -28
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/audit.d.ts +4 -0
- package/dist/cli/commands/audit.js +16 -13
- 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-QFJOEP5V.js → convert-to-pnpm-NWB5FCQ2.js} +4 -4
- package/dist/{db-UXNIZ2RG.js → db-DXUUAMXT.js} +121 -26
- package/dist/db-DXUUAMXT.js.map +1 -0
- package/dist/{digest-FW6QMZZJ.js → digest-NBLECFM7.js} +110 -29
- package/dist/digest-NBLECFM7.js.map +1 -0
- package/dist/digest-collectors-NM7WUWQP.js +50 -0
- package/dist/digest-state-3KHZF7GD.js +50 -0
- package/dist/digest-state-3KHZF7GD.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-KJXASVH5.js → email-FMMEUPX6.js} +16 -6
- package/dist/{email-KJXASVH5.js.map → email-FMMEUPX6.js.map} +1 -1
- package/dist/{ensure-site-4DCEBAPM.js → ensure-site-FU6527OU.js} +2 -2
- package/dist/{fleet-state-7TJ2GYT2.js → fleet-state-R5LU7IC3.js} +4 -2
- package/dist/{forms-notify-target-2EAWCWBW.js → forms-notify-target-SGDGXXK5.js} +6 -6
- package/dist/{github-signals-E2WPYTBK.js → github-signals-DB25UWY4.js} +7 -6
- package/dist/{github-signals-E2WPYTBK.js.map → github-signals-DB25UWY4.js.map} +1 -1
- package/dist/{header-image-6IC53W27.js → header-image-7A7XLVEU.js} +2 -2
- package/dist/{health-endpoint-RFXL2F4A.js → health-endpoint-UVX6R5N5.js} +4 -4
- package/dist/{health-mirror-L5JBEJVA.js → health-mirror-GCDN7BJL.js} +11 -6
- package/dist/health-mirror-GCDN7BJL.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +136 -95
- package/dist/index.js.map +1 -1
- package/dist/{init-XRAQZYID.js → init-32AWPS7A.js} +9 -9
- package/dist/{launch-IX4Y5JJC.js → launch-52WUE4TZ.js} +12 -11
- package/dist/{launch-IX4Y5JJC.js.map → launch-52WUE4TZ.js.map} +1 -1
- package/dist/migrate-YITCXBLS.js +7 -0
- package/dist/{notify-C645PGD5.js → notify-4HP4GZ2H.js} +5 -5
- package/dist/{onboard-O4IAHJMO.js → onboard-BKCYBAVW.js} +4 -4
- package/dist/{orchestrate-YLWQ6KK2.js → orchestrate-JNKCWU5K.js} +9 -9
- package/dist/{parity-56KA2DGR.js → parity-NBYZUBFU.js} +4 -2
- package/dist/pdf-N5QLQ5ZQ.js +38 -0
- package/dist/pdf-N5QLQ5ZQ.js.map +1 -0
- package/dist/{pipeline-3AANZFCR.js → pipeline-YIBIQ7XK.js} +3 -3
- package/dist/{preflight-TNORKGCX.js → preflight-IMTYDDD4.js} +10 -10
- package/dist/{prismic-ci-G2AK53VL.js → prismic-ci-MY2U6NN7.js} +4 -4
- package/dist/{prismic-models-O6Z3OK3A.js → prismic-models-IJWZW2EO.js} +6 -6
- package/dist/prospect/types.d.ts +33 -0
- package/dist/{prospect-audit-KSXUNQ5J.js → prospect-audit-WJQI7V5V.js} +32 -13
- package/dist/prospect-audit-WJQI7V5V.js.map +1 -0
- package/dist/render-CVKE7IWC.js +10 -0
- package/dist/{render-from-row-BWSOIYDS.js → render-from-row-DOYJDO2L.js} +5 -5
- package/dist/{replay-C2LXMJZP.js → replay-4S6ENYCH.js} +3 -3
- package/dist/{report-R3E3Y7HR.js → report-2LM2SX67.js} +20 -20
- package/dist/{report-mirror-4MNORYQ4.js → report-mirror-AAK3MXHY.js} +12 -6
- package/dist/report-mirror-AAK3MXHY.js.map +1 -0
- package/dist/schema-75PJECAE.js +18 -0
- package/dist/schema-75PJECAE.js.map +1 -0
- package/dist/{self-updating-UCIWESAX.js → self-updating-NTCCWJYZ.js} +4 -4
- package/dist/{selftest-VGHWPN72.js → selftest-DIZ5JD2X.js} +12 -12
- package/dist/{site-mirror-NIREX5ME.js → site-mirror-Q763PLGG.js} +17 -7
- package/dist/site-mirror-Q763PLGG.js.map +1 -0
- package/dist/{smoke-suite-U3UKD3NS.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-IQEJX6FK.js → submissions-TEHPBSZJ.js} +4 -4
- package/dist/{svelte-codemods-GOKXTBHW.js → svelte-codemods-QAPDZC6F.js} +4 -4
- package/dist/{sync-CTUVXMCK.js → sync-GBFV45WX.js} +4 -2
- package/dist/{sync-CTUVXMCK.js.map → sync-GBFV45WX.js.map} +1 -1
- package/dist/{sync-configs-SPTLFJCC.js → sync-configs-34JIA6KZ.js} +4 -4
- package/dist/{upgrade-W6ZR3FJH.js → upgrade-IR2OO3KP.js} +4 -4
- package/dist/{webhook-VOGOW3W6.js → webhook-XM6EV4XT.js} +2 -2
- package/dist/{write-audits-to-airtable-CMKHGJNQ.js → write-audits-to-airtable-OPBJXGS4.js} +5 -2
- package/dist/write-audits-to-airtable-OPBJXGS4.js.map +1 -0
- package/package.json +1 -1
- package/dist/airtable-ZBUPQJSC.js +0 -9
- package/dist/chunk-C6B4RHXR.js.map +0 -1
- package/dist/chunk-DEWJMMG3.js.map +0 -1
- package/dist/chunk-DJZWYIPP.js.map +0 -1
- package/dist/chunk-HHMMUR4P.js.map +0 -1
- package/dist/chunk-HSI6Q7MI.js.map +0 -1
- package/dist/chunk-NCMZMLY7.js.map +0 -1
- package/dist/chunk-NXNPDCF3.js.map +0 -1
- package/dist/chunk-SNSTJLQW.js.map +0 -1
- package/dist/chunk-ZBDZTJUO.js.map +0 -1
- package/dist/client-AUE3XUPO.js +0 -11
- package/dist/db-UXNIZ2RG.js.map +0 -1
- package/dist/digest-FW6QMZZJ.js.map +0 -1
- package/dist/dump-P6EBSIOH.js.map +0 -1
- package/dist/health-mirror-L5JBEJVA.js.map +0 -1
- package/dist/migrate-GQH237A6.js +0 -7
- package/dist/prospect-audit-KSXUNQ5J.js.map +0 -1
- package/dist/render-IFOGFB5T.js +0 -10
- package/dist/report-mirror-4MNORYQ4.js.map +0 -1
- package/dist/site-mirror-NIREX5ME.js.map +0 -1
- /package/dist/{airtable-ZBUPQJSC.js.map → airtable-QG5ICI3Y.js.map} +0 -0
- /package/dist/{announce-I2Z4JN3I.js.map → announce-GKD5YJNB.js.map} +0 -0
- /package/dist/{blux-ZGDSHI3U.js.map → blux-EATHOHKK.js.map} +0 -0
- /package/dist/{bump-deps-CGVELRLK.js.map → bump-deps-KGH5UYSF.js.map} +0 -0
- /package/dist/{chunk-SDDPUAWH.js.map → chunk-6I6L3CMX.js.map} +0 -0
- /package/dist/{chunk-3JQ2FO3D.js.map → chunk-B23FFWU4.js.map} +0 -0
- /package/dist/{chunk-NDWYJLRF.js.map → chunk-DGZWFI2G.js.map} +0 -0
- /package/dist/{chunk-BBUSGEES.js.map → chunk-J3WEZGIN.js.map} +0 -0
- /package/dist/{chunk-BNM5POSJ.js.map → chunk-JRHIFA5R.js.map} +0 -0
- /package/dist/{chunk-MT2JINSS.js.map → chunk-KJGKZLCT.js.map} +0 -0
- /package/dist/{chunk-C6CLRKPV.js.map → chunk-PR343K7F.js.map} +0 -0
- /package/dist/{chunk-CROKUHJB.js.map → chunk-Q47DU255.js.map} +0 -0
- /package/dist/{chunk-VSBFM7DS.js.map → chunk-SJOXWJ5Y.js.map} +0 -0
- /package/dist/{chunk-HEWSRXYV.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-ZGBEIWSC.js.map → chunk-XP5WM2LV.js.map} +0 -0
- /package/dist/{chunk-TD2NNXLZ.js.map → chunk-XSOINOPY.js.map} +0 -0
- /package/dist/{chunk-6TWWY7ZU.js.map → chunk-Z3JXLULD.js.map} +0 -0
- /package/dist/{client-AUE3XUPO.js.map → client-JYKPVCJX.js.map} +0 -0
- /package/dist/{convert-to-pnpm-QFJOEP5V.js.map → convert-to-pnpm-NWB5FCQ2.js.map} +0 -0
- /package/dist/{fleet-state-7TJ2GYT2.js.map → digest-collectors-NM7WUWQP.js.map} +0 -0
- /package/dist/{migrate-GQH237A6.js.map → digest-state-F37XUCS5.js.map} +0 -0
- /package/dist/{ensure-site-4DCEBAPM.js.map → ensure-site-FU6527OU.js.map} +0 -0
- /package/dist/{notify-C645PGD5.js.map → fleet-state-R5LU7IC3.js.map} +0 -0
- /package/dist/{forms-notify-target-2EAWCWBW.js.map → forms-notify-target-SGDGXXK5.js.map} +0 -0
- /package/dist/{header-image-6IC53W27.js.map → header-image-7A7XLVEU.js.map} +0 -0
- /package/dist/{health-endpoint-RFXL2F4A.js.map → health-endpoint-UVX6R5N5.js.map} +0 -0
- /package/dist/{init-XRAQZYID.js.map → init-32AWPS7A.js.map} +0 -0
- /package/dist/{orchestrate-YLWQ6KK2.js.map → migrate-YITCXBLS.js.map} +0 -0
- /package/dist/{parity-56KA2DGR.js.map → notify-4HP4GZ2H.js.map} +0 -0
- /package/dist/{onboard-O4IAHJMO.js.map → onboard-BKCYBAVW.js.map} +0 -0
- /package/dist/{pipeline-3AANZFCR.js.map → orchestrate-JNKCWU5K.js.map} +0 -0
- /package/dist/{render-IFOGFB5T.js.map → parity-NBYZUBFU.js.map} +0 -0
- /package/dist/{render-from-row-BWSOIYDS.js.map → pipeline-YIBIQ7XK.js.map} +0 -0
- /package/dist/{preflight-TNORKGCX.js.map → preflight-IMTYDDD4.js.map} +0 -0
- /package/dist/{prismic-ci-G2AK53VL.js.map → prismic-ci-MY2U6NN7.js.map} +0 -0
- /package/dist/{prismic-models-O6Z3OK3A.js.map → prismic-models-IJWZW2EO.js.map} +0 -0
- /package/dist/{spam-classifier-XLBFCO7G.js.map → render-CVKE7IWC.js.map} +0 -0
- /package/dist/{write-audits-to-airtable-CMKHGJNQ.js.map → render-from-row-DOYJDO2L.js.map} +0 -0
- /package/dist/{replay-C2LXMJZP.js.map → replay-4S6ENYCH.js.map} +0 -0
- /package/dist/{report-R3E3Y7HR.js.map → report-2LM2SX67.js.map} +0 -0
- /package/dist/{self-updating-UCIWESAX.js.map → self-updating-NTCCWJYZ.js.map} +0 -0
- /package/dist/{selftest-VGHWPN72.js.map → selftest-DIZ5JD2X.js.map} +0 -0
- /package/dist/{smoke-suite-U3UKD3NS.js.map → smoke-suite-4JFZZYVW.js.map} +0 -0
- /package/dist/{submissions-IQEJX6FK.js.map → submissions-TEHPBSZJ.js.map} +0 -0
- /package/dist/{svelte-codemods-GOKXTBHW.js.map → svelte-codemods-QAPDZC6F.js.map} +0 -0
- /package/dist/{sync-configs-SPTLFJCC.js.map → sync-configs-34JIA6KZ.js.map} +0 -0
- /package/dist/{upgrade-W6ZR3FJH.js.map → upgrade-IR2OO3KP.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());
|
|
@@ -787,10 +807,16 @@ Return:
|
|
|
787
807
|
the site answers it (yes/partial/no), whether there is a passage an AI could quote verbatim, the page
|
|
788
808
|
it lives on, and the evidence quote. evidence must be an EXACT substring of that page's quoted text \u2014
|
|
789
809
|
copied verbatim, never paraphrased or invented \u2014 or null when no exact quote supports the answer.
|
|
790
|
-
- categoryQueries:
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
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").
|
|
794
820
|
Never refer to the company \u2014 not by name, and not as "this agency", "they", "them" or "you". A query
|
|
795
821
|
that names the company measures nothing (the engine just echoes the name back); a query that points
|
|
796
822
|
at it with a pronoun has no antecedent and the engine will answer that it does not know who is meant.
|
|
@@ -902,16 +928,19 @@ function verifyEvidence(result, crawl) {
|
|
|
902
928
|
if (view) textByUrl.set(p.url, view.text);
|
|
903
929
|
}
|
|
904
930
|
const buyerQuestions = result.buyerQuestions.map((q) => {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
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;
|
|
915
944
|
});
|
|
916
945
|
return { ...result, buyerQuestions };
|
|
917
946
|
}
|
|
@@ -922,7 +951,7 @@ async function analyzeSite(url, crawl, checks, deps = defaultAnalyzeDeps()) {
|
|
|
922
951
|
}
|
|
923
952
|
|
|
924
953
|
// src/prospect/probes.ts
|
|
925
|
-
var MAX_QUERIES =
|
|
954
|
+
var MAX_QUERIES = 9;
|
|
926
955
|
var SNIPPET_CHARS = 300;
|
|
927
956
|
var PROBE_MODEL = "claude-sonnet-5";
|
|
928
957
|
function domainOf(raw) {
|
|
@@ -937,11 +966,13 @@ function isSameSite(cited, prospect) {
|
|
|
937
966
|
return cited === prospect || cited.endsWith(`.${prospect}`) || prospect.endsWith(`.${cited}`);
|
|
938
967
|
}
|
|
939
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;
|
|
940
971
|
function resolveBusinessName(business, url) {
|
|
941
972
|
const trimmed = business.trim();
|
|
942
|
-
if (!trimmed || trimmed.length > MAX_NAME_CHARS
|
|
943
|
-
|
|
944
|
-
|
|
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);
|
|
945
976
|
return trimmed;
|
|
946
977
|
}
|
|
947
978
|
function buildQueries(input) {
|
|
@@ -949,7 +980,11 @@ function buildQueries(input) {
|
|
|
949
980
|
const candidates = [
|
|
950
981
|
{ query: `who is ${name}`, kind: "branded" },
|
|
951
982
|
{ query: `${name} reviews`, kind: "branded" },
|
|
952
|
-
|
|
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" })),
|
|
953
988
|
...input.competitors.slice(0, 2).map((c) => ({ query: `${name} vs ${c}`, kind: "competitor" }))
|
|
954
989
|
];
|
|
955
990
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -964,12 +999,106 @@ function buildQueries(input) {
|
|
|
964
999
|
function escapeRegExp(s) {
|
|
965
1000
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
966
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
|
+
}
|
|
967
1006
|
function mentionsBrand(answer, brand) {
|
|
968
|
-
|
|
969
|
-
|
|
1007
|
+
const needle = normalizeForMatch(brand);
|
|
1008
|
+
if (!needle) return false;
|
|
1009
|
+
return new RegExp(`(^| )${escapeRegExp(needle)}( |$)`).test(normalizeForMatch(answer));
|
|
970
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
|
+
]);
|
|
971
1097
|
function isDistinctiveName(brand) {
|
|
972
|
-
|
|
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));
|
|
973
1102
|
}
|
|
974
1103
|
function isRateLimited(err) {
|
|
975
1104
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -983,13 +1112,18 @@ async function runVisibilityProbes(input, engines, opts = {}) {
|
|
|
983
1112
|
const queries = buildQueries(input);
|
|
984
1113
|
const prospect = domainOf(input.url);
|
|
985
1114
|
const brand = resolveBusinessName(input.business, input.url).toLowerCase();
|
|
1115
|
+
const nameIsDistinctive = isDistinctiveName(brand);
|
|
986
1116
|
const answers = [];
|
|
987
1117
|
const competitorCounts = /* @__PURE__ */ new Map();
|
|
1118
|
+
const perEngine = /* @__PURE__ */ new Map();
|
|
988
1119
|
for (const engine of engines) {
|
|
1120
|
+
const tally = { categoryAttempted: 0, answeredAny: false };
|
|
1121
|
+
perEngine.set(engine.name, tally);
|
|
989
1122
|
await pacedEach(
|
|
990
1123
|
queries,
|
|
991
1124
|
delayMs,
|
|
992
1125
|
async ({ query, kind }) => {
|
|
1126
|
+
if (kind === "category") tally.categoryAttempted += 1;
|
|
993
1127
|
let reply;
|
|
994
1128
|
try {
|
|
995
1129
|
reply = await engine.ask(query);
|
|
@@ -1002,6 +1136,7 @@ async function runVisibilityProbes(input, engines, opts = {}) {
|
|
|
1002
1136
|
return;
|
|
1003
1137
|
}
|
|
1004
1138
|
}
|
|
1139
|
+
tally.answeredAny = true;
|
|
1005
1140
|
const citedDomains = reply.citedDomains.map(domainOf);
|
|
1006
1141
|
const domainCited = citedDomains.some((d) => isSameSite(d, prospect));
|
|
1007
1142
|
const brandMentioned = mentionsBrand(reply.answer.toLowerCase(), brand);
|
|
@@ -1015,6 +1150,8 @@ async function runVisibilityProbes(input, engines, opts = {}) {
|
|
|
1015
1150
|
kind,
|
|
1016
1151
|
domainCited,
|
|
1017
1152
|
brandMentioned,
|
|
1153
|
+
// The same expression the score uses below — written once, read twice.
|
|
1154
|
+
countedAsVisible: domainCited || brandMentioned && nameIsDistinctive,
|
|
1018
1155
|
citedDomains,
|
|
1019
1156
|
snippet: reply.answer.slice(0, SNIPPET_CHARS),
|
|
1020
1157
|
truncated: reply.answer.length > SNIPPET_CHARS,
|
|
@@ -1028,17 +1165,16 @@ async function runVisibilityProbes(input, engines, opts = {}) {
|
|
|
1028
1165
|
throw new Error("no visibility engine returned an answer");
|
|
1029
1166
|
}
|
|
1030
1167
|
const categoryAnswers = answers.filter((a) => a.kind === "category");
|
|
1031
|
-
const
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
).length;
|
|
1035
|
-
const visibilityScore = categoryAnswers.length === 0 ? null : Math.round(visibleCategory / categoryAnswers.length * 100);
|
|
1168
|
+
const visibleCategory = categoryAnswers.filter((a) => a.countedAsVisible).length;
|
|
1169
|
+
const categoryAttempted = [...perEngine.values()].filter((t) => t.answeredAny).reduce((n, t) => n + t.categoryAttempted, 0);
|
|
1170
|
+
const visibilityScore = categoryAttempted === 0 || categoryAnswers.length === 0 ? null : Math.round(visibleCategory / categoryAttempted * 100);
|
|
1036
1171
|
const brandedRecognized = answers.some((a) => a.kind === "branded" && a.domainCited);
|
|
1037
1172
|
return {
|
|
1038
1173
|
answers,
|
|
1039
1174
|
visibilityScore,
|
|
1040
1175
|
brandedRecognized,
|
|
1041
|
-
competitorsSeen: [...competitorCounts.entries()].map(([domain, count]) => ({ domain, count })).sort((a, b) => b.count - a.count).slice(0, 8)
|
|
1176
|
+
competitorsSeen: [...competitorCounts.entries()].map(([domain, count]) => ({ domain, count })).sort((a, b) => b.count - a.count).slice(0, 8),
|
|
1177
|
+
categoryProbes: { attempted: categoryAttempted, answered: categoryAnswers.length }
|
|
1042
1178
|
};
|
|
1043
1179
|
}
|
|
1044
1180
|
function perplexityEngine(apiKey, fetchImpl = fetch) {
|
|
@@ -1231,8 +1367,9 @@ async function runProspectAudit(url, opts, deps = {}) {
|
|
|
1231
1367
|
}
|
|
1232
1368
|
|
|
1233
1369
|
export {
|
|
1370
|
+
resolveBusinessName,
|
|
1234
1371
|
PROBES_SKIPPED,
|
|
1235
1372
|
ANALYZE_SKIPPED,
|
|
1236
1373
|
runProspectAudit
|
|
1237
1374
|
};
|
|
1238
|
-
//# sourceMappingURL=chunk-
|
|
1375
|
+
//# sourceMappingURL=chunk-CEW5PEHZ.js.map
|