@reddoorla/maintenance 0.97.0 → 0.98.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/dist/{announce-5ZD2SBGD.js → announce-2FH2KBQQ.js} +4 -4
  2. package/dist/{bump-deps-775VLDYB.js → bump-deps-54K4ECP3.js} +2 -2
  3. package/dist/{chunk-YXWBM26P.js → chunk-3YALLSBF.js} +2 -2
  4. package/dist/{chunk-LAJ3FF54.js → chunk-5UI4FERC.js} +16 -5
  5. package/dist/chunk-5UI4FERC.js.map +1 -0
  6. package/dist/{chunk-VMGHQUTV.js → chunk-B2BRFWJY.js} +2 -2
  7. package/dist/{chunk-IOTOOTII.js → chunk-CP6MVZ5X.js} +16 -1
  8. package/dist/{chunk-IOTOOTII.js.map → chunk-CP6MVZ5X.js.map} +1 -1
  9. package/dist/{chunk-L3S3QZXL.js → chunk-DXZRWW2F.js} +64 -9
  10. package/dist/chunk-DXZRWW2F.js.map +1 -0
  11. package/dist/chunk-DZZ5KRTB.js +23 -0
  12. package/dist/chunk-DZZ5KRTB.js.map +1 -0
  13. package/dist/{chunk-U2ZI6G7H.js → chunk-GRF2RYEO.js} +2 -2
  14. package/dist/{chunk-QFSL2L2B.js → chunk-L5F3WYIY.js} +81 -31
  15. package/dist/chunk-L5F3WYIY.js.map +1 -0
  16. package/dist/{chunk-MMSMZC7S.js → chunk-MNRWHSFS.js} +2 -2
  17. package/dist/{chunk-PARKXDXI.js → chunk-ORTOMEYR.js} +2 -2
  18. package/dist/{chunk-FP6HOW6J.js → chunk-S2PLX4XK.js} +2 -2
  19. package/dist/{chunk-A5QKNOXW.js → chunk-TTT6HYPW.js} +47 -10
  20. package/dist/chunk-TTT6HYPW.js.map +1 -0
  21. package/dist/{chunk-BIYQN7FH.js → chunk-VCPTOZ56.js} +31 -1
  22. package/dist/{chunk-BIYQN7FH.js.map → chunk-VCPTOZ56.js.map} +1 -1
  23. package/dist/{chunk-LDHRIQUW.js → chunk-ZEE5WQYU.js} +2 -2
  24. package/dist/cli/bin.js +27 -27
  25. package/dist/cli/commands/audit.js +10 -10
  26. package/dist/client-3XGSZMLU.js +11 -0
  27. package/dist/{convert-to-pnpm-KLWA53N5.js → convert-to-pnpm-U2CRNKNY.js} +2 -2
  28. package/dist/{db-II5WAK4D.js → db-7XYTBIPH.js} +20 -13
  29. package/dist/db-7XYTBIPH.js.map +1 -0
  30. package/dist/{deadletter-G4M3HSVM.js → deadletter-A7RQVNG3.js} +8 -4
  31. package/dist/{digest-CWKK2DMR.js → digest-AMXRGWZA.js} +20 -20
  32. package/dist/{digest-collectors-CYTN2EUX.js → digest-collectors-CX2NCGS7.js} +7 -7
  33. package/dist/{ensure-site-2QZHFVQ3.js → ensure-site-F2WFCWWA.js} +2 -2
  34. package/dist/{forms-notify-target-X6UEOI2M.js → forms-notify-target-DK5R326N.js} +3 -3
  35. package/dist/{github-signals-QIRTHHET.js → github-signals-RGRPYTUB.js} +6 -6
  36. package/dist/{header-image-H7ZJAV26.js → header-image-43ZIEJKP.js} +3 -3
  37. package/dist/{health-endpoint-BJ6BKRHF.js → health-endpoint-K5LXDPGE.js} +2 -2
  38. package/dist/{health-mirror-KU3G7V2Y.js → health-mirror-WZUHWCCV.js} +3 -3
  39. package/dist/index.js +10 -11
  40. package/dist/index.js.map +1 -1
  41. package/dist/{init-GOWDA5QS.js → init-OFITGMOP.js} +5 -5
  42. package/dist/{launch-7N3X2MXB.js → launch-AJY3TGTQ.js} +7 -7
  43. package/dist/{lighthouse-52WSPZ5D.js → lighthouse-RFMFREEX.js} +2 -2
  44. package/dist/{match-harness-GBCDSVPS.js → match-harness-CFELJA57.js} +2 -2
  45. package/dist/migrate-EZL55AWA.js +7 -0
  46. package/dist/{onboard-JQQIMQW7.js → onboard-F7RTLMDZ.js} +2 -2
  47. package/dist/{orchestrate-L2A5BZFY.js → orchestrate-SAIFXZIW.js} +5 -5
  48. package/dist/{pipeline-UUPXOS2J.js → pipeline-TCSNVDQZ.js} +3 -3
  49. package/dist/{preflight-O7Q6FWHU.js → preflight-6XGVVO2U.js} +7 -7
  50. package/dist/{prismic-ci-NAPWOXXD.js → prismic-ci-XMX7A5E4.js} +2 -2
  51. package/dist/{prismic-models-LL6ORTQ2.js → prismic-models-7GKFAAFA.js} +4 -4
  52. package/dist/prospect/types.d.ts +44 -2
  53. package/dist/prospect/types.js +1 -1
  54. package/dist/{prospect-audit-FMPGQIRQ.js → prospect-audit-KWP5P5U2.js} +37 -5
  55. package/dist/prospect-audit-KWP5P5U2.js.map +1 -0
  56. package/dist/{render-MHUUHPLN.js → render-NWUAX6RN.js} +3 -3
  57. package/dist/{renovate-dispatch-2IHAZDVA.js → renovate-dispatch-ZZQXT7DA.js} +2 -2
  58. package/dist/replay-HB44TAII.js +77 -0
  59. package/dist/replay-HB44TAII.js.map +1 -0
  60. package/dist/{report-IAHXN5CW.js → report-MVPWS7WP.js} +13 -13
  61. package/dist/{report-mirror-4QM57LMS.js → report-mirror-O6IDFDWU.js} +3 -3
  62. package/dist/{roster-GDNSIRAK.js → roster-EJ3ILR52.js} +2 -2
  63. package/dist/{screenouts-7P5GOZMV.js → screenouts-7NQUWHOJ.js} +14 -1
  64. package/dist/screenouts-7NQUWHOJ.js.map +1 -0
  65. package/dist/{self-updating-ZUWPGYUP.js → self-updating-KX5FMGEO.js} +2 -2
  66. package/dist/{selftest-G4W3LIBQ.js → selftest-KABY2CYJ.js} +5 -5
  67. package/dist/{site-mirror-WR7ACXOV.js → site-mirror-TKTDWYP3.js} +3 -3
  68. package/dist/{smoke-suite-WAUPI6EJ.js → smoke-suite-QQMV7TNA.js} +2 -2
  69. package/dist/{submissions-JDZP35JM.js → submissions-4I66W6QK.js} +11 -1
  70. package/dist/{submissions-JDZP35JM.js.map → submissions-4I66W6QK.js.map} +1 -1
  71. package/dist/{submissions-2SHCFOKD.js → submissions-K5UXGZUN.js} +4 -4
  72. package/dist/{svelte-codemods-VOLOKKFI.js → svelte-codemods-YFN4TEGV.js} +2 -2
  73. package/dist/{sync-configs-OXSWKVZ2.js → sync-configs-HBXS3D7C.js} +2 -2
  74. package/dist/{upgrade-EVAL3JTO.js → upgrade-XOJMDIT4.js} +2 -2
  75. package/package.json +1 -1
  76. package/dist/chunk-A5QKNOXW.js.map +0 -1
  77. package/dist/chunk-L3S3QZXL.js.map +0 -1
  78. package/dist/chunk-LAJ3FF54.js.map +0 -1
  79. package/dist/chunk-QFSL2L2B.js.map +0 -1
  80. package/dist/client-DGTO65WB.js +0 -11
  81. package/dist/db-II5WAK4D.js.map +0 -1
  82. package/dist/migrate-XZ7U5DFC.js +0 -7
  83. package/dist/prospect-audit-FMPGQIRQ.js.map +0 -1
  84. package/dist/replay-4FVFOMHH.js +0 -54
  85. package/dist/replay-4FVFOMHH.js.map +0 -1
  86. package/dist/screenouts-7P5GOZMV.js.map +0 -1
  87. /package/dist/{announce-5ZD2SBGD.js.map → announce-2FH2KBQQ.js.map} +0 -0
  88. /package/dist/{bump-deps-775VLDYB.js.map → bump-deps-54K4ECP3.js.map} +0 -0
  89. /package/dist/{chunk-YXWBM26P.js.map → chunk-3YALLSBF.js.map} +0 -0
  90. /package/dist/{chunk-VMGHQUTV.js.map → chunk-B2BRFWJY.js.map} +0 -0
  91. /package/dist/{chunk-U2ZI6G7H.js.map → chunk-GRF2RYEO.js.map} +0 -0
  92. /package/dist/{chunk-MMSMZC7S.js.map → chunk-MNRWHSFS.js.map} +0 -0
  93. /package/dist/{chunk-PARKXDXI.js.map → chunk-ORTOMEYR.js.map} +0 -0
  94. /package/dist/{chunk-FP6HOW6J.js.map → chunk-S2PLX4XK.js.map} +0 -0
  95. /package/dist/{chunk-LDHRIQUW.js.map → chunk-ZEE5WQYU.js.map} +0 -0
  96. /package/dist/{client-DGTO65WB.js.map → client-3XGSZMLU.js.map} +0 -0
  97. /package/dist/{convert-to-pnpm-KLWA53N5.js.map → convert-to-pnpm-U2CRNKNY.js.map} +0 -0
  98. /package/dist/{deadletter-G4M3HSVM.js.map → deadletter-A7RQVNG3.js.map} +0 -0
  99. /package/dist/{digest-CWKK2DMR.js.map → digest-AMXRGWZA.js.map} +0 -0
  100. /package/dist/{digest-collectors-CYTN2EUX.js.map → digest-collectors-CX2NCGS7.js.map} +0 -0
  101. /package/dist/{ensure-site-2QZHFVQ3.js.map → ensure-site-F2WFCWWA.js.map} +0 -0
  102. /package/dist/{forms-notify-target-X6UEOI2M.js.map → forms-notify-target-DK5R326N.js.map} +0 -0
  103. /package/dist/{github-signals-QIRTHHET.js.map → github-signals-RGRPYTUB.js.map} +0 -0
  104. /package/dist/{header-image-H7ZJAV26.js.map → header-image-43ZIEJKP.js.map} +0 -0
  105. /package/dist/{health-endpoint-BJ6BKRHF.js.map → health-endpoint-K5LXDPGE.js.map} +0 -0
  106. /package/dist/{health-mirror-KU3G7V2Y.js.map → health-mirror-WZUHWCCV.js.map} +0 -0
  107. /package/dist/{init-GOWDA5QS.js.map → init-OFITGMOP.js.map} +0 -0
  108. /package/dist/{launch-7N3X2MXB.js.map → launch-AJY3TGTQ.js.map} +0 -0
  109. /package/dist/{lighthouse-52WSPZ5D.js.map → lighthouse-RFMFREEX.js.map} +0 -0
  110. /package/dist/{match-harness-GBCDSVPS.js.map → match-harness-CFELJA57.js.map} +0 -0
  111. /package/dist/{migrate-XZ7U5DFC.js.map → migrate-EZL55AWA.js.map} +0 -0
  112. /package/dist/{onboard-JQQIMQW7.js.map → onboard-F7RTLMDZ.js.map} +0 -0
  113. /package/dist/{orchestrate-L2A5BZFY.js.map → orchestrate-SAIFXZIW.js.map} +0 -0
  114. /package/dist/{pipeline-UUPXOS2J.js.map → pipeline-TCSNVDQZ.js.map} +0 -0
  115. /package/dist/{preflight-O7Q6FWHU.js.map → preflight-6XGVVO2U.js.map} +0 -0
  116. /package/dist/{prismic-ci-NAPWOXXD.js.map → prismic-ci-XMX7A5E4.js.map} +0 -0
  117. /package/dist/{prismic-models-LL6ORTQ2.js.map → prismic-models-7GKFAAFA.js.map} +0 -0
  118. /package/dist/{render-MHUUHPLN.js.map → render-NWUAX6RN.js.map} +0 -0
  119. /package/dist/{renovate-dispatch-2IHAZDVA.js.map → renovate-dispatch-ZZQXT7DA.js.map} +0 -0
  120. /package/dist/{report-IAHXN5CW.js.map → report-MVPWS7WP.js.map} +0 -0
  121. /package/dist/{report-mirror-4QM57LMS.js.map → report-mirror-O6IDFDWU.js.map} +0 -0
  122. /package/dist/{roster-GDNSIRAK.js.map → roster-EJ3ILR52.js.map} +0 -0
  123. /package/dist/{self-updating-ZUWPGYUP.js.map → self-updating-KX5FMGEO.js.map} +0 -0
  124. /package/dist/{selftest-G4W3LIBQ.js.map → selftest-KABY2CYJ.js.map} +0 -0
  125. /package/dist/{site-mirror-WR7ACXOV.js.map → site-mirror-TKTDWYP3.js.map} +0 -0
  126. /package/dist/{smoke-suite-WAUPI6EJ.js.map → smoke-suite-QQMV7TNA.js.map} +0 -0
  127. /package/dist/{submissions-2SHCFOKD.js.map → submissions-K5UXGZUN.js.map} +0 -0
  128. /package/dist/{svelte-codemods-VOLOKKFI.js.map → svelte-codemods-YFN4TEGV.js.map} +0 -0
  129. /package/dist/{sync-configs-OXSWKVZ2.js.map → sync-configs-HBXS3D7C.js.map} +0 -0
  130. /package/dist/{upgrade-EVAL3JTO.js.map → upgrade-XOJMDIT4.js.map} +0 -0
@@ -0,0 +1,23 @@
1
+ // src/prospect/daily-cap.ts
2
+ var PROSPECT_AUDIT_DAILY_CAP = 25;
3
+ var DAILY_CAP_LOOKBACK = PROSPECT_AUDIT_DAILY_CAP * 2;
4
+ var DAY_MS = 24 * 60 * 60 * 1e3;
5
+ function countAuditsInDailyWindow(recent, now) {
6
+ const dayAgo = now.getTime() - DAY_MS;
7
+ return recent.filter((r) => Date.parse(r.created_at) >= dayAgo).length;
8
+ }
9
+ function isOverDailyCap(count) {
10
+ return count >= PROSPECT_AUDIT_DAILY_CAP;
11
+ }
12
+ function dailyCapMessage(count, cap = PROSPECT_AUDIT_DAILY_CAP) {
13
+ return `${count} audits have run in the last 24 hours (cap ${cap}). This is a runaway brake \u2014 if the run is genuinely needed, raise PROSPECT_AUDIT_DAILY_CAP.`;
14
+ }
15
+
16
+ export {
17
+ PROSPECT_AUDIT_DAILY_CAP,
18
+ DAILY_CAP_LOOKBACK,
19
+ countAuditsInDailyWindow,
20
+ isOverDailyCap,
21
+ dailyCapMessage
22
+ };
23
+ //# sourceMappingURL=chunk-DZZ5KRTB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/prospect/daily-cap.ts"],"sourcesContent":["/**\n * The prospect-audit runaway brake — the numbers and the arithmetic, in one\n * place both callers reach.\n *\n * It lived inside `src/dashboard/prospect-audit-trigger.ts`, which meant it\n * guarded the dashboard dispatch path and nothing else, while every batch to\n * date — including the 29-site corpus — went through `prospect-audit` on the\n * CLI, which had no cap at all (MED-15 of the 2026-09-02 review).\n *\n * That is the same shape #618 already fixed once in this feature, and said so\n * in `src/cli/commands/prospect-audit.ts` while fixing it: *\"The sole private-\n * address guard was in `triggerProspectAudit` — one layer at the far end of the\n * chain — so anyone running the CLI directly, or any future second caller, had\n * none.\"* Identical sentence, different guard.\n *\n * Dependency-free on purpose: the CLI's command modules are lazily loaded and\n * must not drag the GitHub/dispatch layer in behind a constant, and the count\n * needs nothing from a row but its timestamp.\n */\n\n/** Anything with a creation timestamp — `ProspectAuditListItem` satisfies it.\n * Structural so this module needs no import from `src/db`. */\nexport type DatedAudit = { created_at: string };\n\n/** Most audits that may be STARTED in any rolling 24 hours (#612 review).\n *\n * The duplicate window stops the SAME url being re-run; nothing stopped\n * DISTINCT urls. One authenticated session could dispatch ~30/minute against\n * 30 hostnames indefinitely, and one audit is structurally an Opus call plus\n * up to 28 Sonnet calls with up to 112 billed web searches, a 20-page double\n * crawl, a 3-pass Lighthouse and a PDF render, inside a billed Actions job.\n *\n * 25/day is far above real use (this is one operator clicking a button) and far\n * below a number that could quietly cost hundreds. A cap that never binds in\n * normal operation is the point: it is a runaway brake, not a quota. */\nexport const PROSPECT_AUDIT_DAILY_CAP = 25;\n\n/** Lookback for the daily cap. Must exceed the cap so the count can actually\n * reach it — a lookback at or below the cap would make the limit unreachable\n * and the brake permanently disengaged, which is exactly the kind of guard\n * that reads as working while doing nothing. */\nexport const DAILY_CAP_LOOKBACK = PROSPECT_AUDIT_DAILY_CAP * 2;\n\n/** The rolling window itself. */\nconst DAY_MS = 24 * 60 * 60 * 1000;\n\n/** How many of `recent` were created in the 24 hours before `now`.\n *\n * `recent` is expected to be the newest `DAILY_CAP_LOOKBACK` audits; the\n * caller does the fetching so this stays pure and testable from either side. */\nexport function countAuditsInDailyWindow(recent: readonly DatedAudit[], now: Date): number {\n const dayAgo = now.getTime() - DAY_MS;\n return recent.filter((r) => Date.parse(r.created_at) >= dayAgo).length;\n}\n\n/** Whether that count trips the brake. */\nexport function isOverDailyCap(count: number): boolean {\n return count >= PROSPECT_AUDIT_DAILY_CAP;\n}\n\n/** The refusal, worded once so the dashboard's 429 body and the CLI's stderr\n * say the same thing. Carries both numbers: a bare \"refused\" leaves the\n * operator guessing whether they hit a limit or broke something. */\nexport function dailyCapMessage(count: number, cap: number = PROSPECT_AUDIT_DAILY_CAP): string {\n return `${count} audits have run in the last 24 hours (cap ${cap}). This is a runaway brake — if the run is genuinely needed, raise PROSPECT_AUDIT_DAILY_CAP.`;\n}\n"],"mappings":";AAmCO,IAAM,2BAA2B;AAMjC,IAAM,qBAAqB,2BAA2B;AAG7D,IAAM,SAAS,KAAK,KAAK,KAAK;AAMvB,SAAS,yBAAyB,QAA+B,KAAmB;AACzF,QAAM,SAAS,IAAI,QAAQ,IAAI;AAC/B,SAAO,OAAO,OAAO,CAAC,MAAM,KAAK,MAAM,EAAE,UAAU,KAAK,MAAM,EAAE;AAClE;AAGO,SAAS,eAAe,OAAwB;AACrD,SAAO,SAAS;AAClB;AAKO,SAAS,gBAAgB,OAAe,MAAc,0BAAkC;AAC7F,SAAO,GAAG,KAAK,8CAA8C,GAAG;AAClE;","names":[]}
@@ -26,7 +26,7 @@ import {
26
26
  } from "./chunk-LUF52EIY.js";
27
27
  import {
28
28
  runAudits
29
- } from "./chunk-L3S3QZXL.js";
29
+ } from "./chunk-DXZRWW2F.js";
30
30
  import {
31
31
  siteLabel
32
32
  } from "./chunk-XXTZBPUY.js";
@@ -161,4 +161,4 @@ export {
161
161
  DEFAULT_INIT_STEPS,
162
162
  init
163
163
  };
164
- //# sourceMappingURL=chunk-U2ZI6G7H.js.map
164
+ //# sourceMappingURL=chunk-GRF2RYEO.js.map
@@ -2,6 +2,7 @@ import {
2
2
  AI_AGENTS,
3
3
  CLASSICAL_AGENTS,
4
4
  USER_AGENT,
5
+ anchorsTruncated,
5
6
  buildJourney,
6
7
  canonicalizeUrl,
7
8
  crawlSite,
@@ -16,7 +17,7 @@ import {
16
17
  sleep,
17
18
  summarizeAccessibility,
18
19
  usablePages
19
- } from "./chunk-LAJ3FF54.js";
20
+ } from "./chunk-5UI4FERC.js";
20
21
  import {
21
22
  checkGoal
22
23
  } from "./chunk-T7ZUUOZ2.js";
@@ -26,8 +27,12 @@ import {
26
27
 
27
28
  // src/prospect/consistency.ts
28
29
  var PHONE_IN_TEXT = /(?<!\d)(?:\+?1[\s.-]?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}(?!\d)/g;
29
- var COPYRIGHT_YEAR = /(?:©|&copy;|copyright)([^\d<>]{0,30}?)(?:(\d{4})\s*[-–—]\s*)?(\d{4})/gi;
30
- var SENTENCE_BREAK = /[.!?]\s/;
30
+ var NAME_GAP = "(?:[\\s.,&'\u2019-]*[A-Z][A-Za-z.'\u2019&-]{0,19}){0,6}[\\s.,&'\u2019-]*";
31
+ var COPYRIGHT_YEAR = new RegExp(
32
+ `(?:\xA9|&[Cc]opy;|&COPY;|[Cc]opyright|COPYRIGHT)(${NAME_GAP})(?:(\\d{4})\\s*[-\u2013\u2014]\\s*)?(\\d{4})`,
33
+ "g"
34
+ );
35
+ var SENTENCE_BREAK = /[.!?]\s+[A-Z]/;
31
36
  function normalizePhone(raw) {
32
37
  const digits = raw.replace(/\D/g, "");
33
38
  const national = digits.length === 11 && digits.startsWith("1") ? digits.slice(1) : digits;
@@ -66,9 +71,12 @@ function checkConsistency(pages) {
66
71
  record(emails, address.toLowerCase(), address, page.url, true);
67
72
  continue;
68
73
  }
69
- hrefs.add(href);
74
+ const abs = resolveNavigable(href, page.url);
75
+ if (!abs) continue;
76
+ const key = canonicalizeUrl(abs);
77
+ if (key) hrefs.add(key);
70
78
  }
71
- linkSets.push({ url: page.url, hrefs });
79
+ linkSets.push({ url: page.url, hrefs, truncated: anchorsTruncated(extract) });
72
80
  for (const match of extract.text.matchAll(PHONE_IN_TEXT)) {
73
81
  const raw = match[0];
74
82
  if (!raw) continue;
@@ -81,24 +89,26 @@ function checkConsistency(pages) {
81
89
  if (year >= 1990 && year <= 2100) years.add(year);
82
90
  }
83
91
  }
92
+ const templateSets = linkSets.filter((p) => !p.truncated);
84
93
  const counts = /* @__PURE__ */ new Map();
85
- for (const { hrefs } of linkSets) {
94
+ for (const { hrefs } of templateSets) {
86
95
  for (const href of hrefs) counts.set(href, (counts.get(href) ?? 0) + 1);
87
96
  }
88
- const threshold = Math.ceil(linkSets.length * 0.6);
97
+ const threshold = Math.ceil(templateSets.length * 0.6);
89
98
  const sharedNav = new Set(
90
99
  [...counts.entries()].filter(([, n]) => n >= threshold).map(([href]) => href)
91
100
  );
92
- const canJudgeTemplate = linkSets.length >= 3 && sharedNav.size > 0;
101
+ const canJudgeTemplate = templateSets.length >= 3 && sharedNav.size > 0;
93
102
  const sortedYears = [...years].sort((a, b) => a - b);
94
103
  return {
95
104
  phones: [...phones.values()],
96
105
  emails: [...emails.values()],
97
106
  copyrightYears: sortedYears,
98
107
  newestCopyrightYear: sortedYears.at(-1) ?? null,
99
- pagesOffTemplate: canJudgeTemplate ? linkSets.filter((p) => ![...sharedNav].some((h) => p.hrefs.has(h))).map((p) => p.url) : [],
108
+ pagesOffTemplate: canJudgeTemplate ? templateSets.filter((p) => ![...sharedNav].some((h) => p.hrefs.has(h))).map((p) => p.url) : [],
100
109
  sharedNavLinks: sharedNav.size,
101
- pagesExamined: linkSets.length
110
+ pagesExamined: linkSets.length,
111
+ pagesWithTruncatedAnchors: linkSets.filter((p) => p.truncated).map((p) => p.url)
102
112
  };
103
113
  }
104
114
 
@@ -734,7 +744,7 @@ function ownHost(url) {
734
744
  return null;
735
745
  }
736
746
  }
737
- function analyzeAnswerSpace(answers, prospectUrl) {
747
+ function analyzeAnswerSpace(answers, prospectUrl, categoryAttempted) {
738
748
  const category = answers.filter((a) => a.kind === "category");
739
749
  const host = ownHost(prospectUrl);
740
750
  const counts = /* @__PURE__ */ new Map();
@@ -774,7 +784,7 @@ function analyzeAnswerSpace(answers, prospectUrl) {
774
784
  const own = ownIndex === -1 ? void 0 : topSources[ownIndex];
775
785
  return {
776
786
  answersWithCitations,
777
- queriesAsked: category.length,
787
+ queriesAsked: categoryAttempted,
778
788
  citationsTotal,
779
789
  distinctDomains: topSources.length,
780
790
  topSources,
@@ -1045,7 +1055,7 @@ async function runVisibilityProbes(input, engines, opts = {}) {
1045
1055
  // a pure read of citations already recorded, so computing it here keeps the
1046
1056
  // ask loop to one job and lets the same function be re-run over a stored
1047
1057
  // report if we ever backfill.
1048
- answerSpace: analyzeAnswerSpace(answers, input.url)
1058
+ answerSpace: analyzeAnswerSpace(answers, input.url, categoryAttempted)
1049
1059
  };
1050
1060
  }
1051
1061
  function perplexityEngine(apiKey, fetchImpl = fetch) {
@@ -1455,12 +1465,15 @@ ${a.fullAnswer}
1455
1465
  function normalize(s) {
1456
1466
  return s.replace(/\s+/g, " ").trim().toLowerCase();
1457
1467
  }
1458
- function distinctive(terms) {
1468
+ function distinctive(terms, claim) {
1469
+ const namesInClaim = new Set(
1470
+ scanWords(claim).filter((w) => w.capitalised && !w.opensSentence).map((w) => w.lower)
1471
+ );
1459
1472
  return terms.map((t) => t.trim()).filter((t) => {
1460
1473
  if (t.length < 5) return false;
1461
1474
  const multiWord = /\s/.test(t);
1462
1475
  const hasNumber = /\d/.test(t);
1463
- const properNoun = /^[A-Z]/.test(t);
1476
+ const properNoun = namesInClaim.has(t.toLowerCase());
1464
1477
  return multiWord || hasNumber || properNoun;
1465
1478
  });
1466
1479
  }
@@ -1477,7 +1490,7 @@ function backstopAbsent(assertions, siteText, searchTermsByClaim, siteFullyRead)
1477
1490
  if (a.verdict !== "absent") return a;
1478
1491
  const flat = siteText.replace(/\s+/g, " ");
1479
1492
  let scattered = null;
1480
- for (const term of distinctive(searchTermsByClaim.get(a.claim) ?? [])) {
1493
+ for (const term of distinctive(searchTermsByClaim.get(a.claim) ?? [], a.claim)) {
1481
1494
  const found = findTerm(haystack, term);
1482
1495
  if (found === null) continue;
1483
1496
  if (found === "exact") {
@@ -1538,21 +1551,53 @@ var WEAK_WORDS = /* @__PURE__ */ new Set([
1538
1551
  "based"
1539
1552
  ]);
1540
1553
  function quoteSupportsClaim(claim, quote) {
1541
- const words = (t) => {
1554
+ const tokens = (t) => {
1542
1555
  const out = /* @__PURE__ */ new Set();
1543
- for (const w of t.split(/[^A-Za-z0-9]+/)) {
1544
- if (w === "") continue;
1545
- const lower = w.toLowerCase();
1546
- if (WEAK_WORDS.has(lower)) continue;
1547
- const properNoun = /^[A-Z]/.test(w) && w.length >= 3;
1548
- if (properNoun || lower.length >= 4) out.add(lower);
1556
+ const names = /* @__PURE__ */ new Set();
1557
+ const run = [];
1558
+ const flushRun = () => {
1559
+ if (run.length > 0) names.add(run.join(" "));
1560
+ run.length = 0;
1561
+ };
1562
+ for (const w of scanWords(t)) {
1563
+ if (WEAK_WORDS.has(w.lower)) {
1564
+ flushRun();
1565
+ continue;
1566
+ }
1567
+ const properNoun = w.capitalised && !w.opensSentence && w.raw.length >= 2;
1568
+ if (properNoun) run.push(w.lower);
1569
+ else flushRun();
1570
+ if (properNoun || w.lower.length >= 4) out.add(w.lower);
1549
1571
  }
1550
- return out;
1572
+ flushRun();
1573
+ return { words: out, names };
1551
1574
  };
1552
- const inQuote = words(quote);
1575
+ const c = tokens(claim);
1576
+ const q = tokens(quote);
1553
1577
  let shared = 0;
1554
- for (const w of words(claim)) if (inQuote.has(w)) shared += 1;
1555
- return shared >= 2;
1578
+ for (const w of c.words) if (q.words.has(w)) shared += 1;
1579
+ if (shared >= 2) return true;
1580
+ for (const n of c.names) if (q.names.has(n)) return true;
1581
+ return false;
1582
+ }
1583
+ function scanWords(text) {
1584
+ const out = [];
1585
+ let opensSentence = true;
1586
+ let index = 0;
1587
+ for (const m of text.matchAll(/[A-Za-z0-9]+/g)) {
1588
+ const raw = m[0];
1589
+ const gap = text.slice(index, m.index);
1590
+ if (out.length > 0 && /[.!?\n\r]/.test(gap)) opensSentence = true;
1591
+ index = m.index + raw.length;
1592
+ out.push({
1593
+ raw,
1594
+ lower: raw.toLowerCase(),
1595
+ capitalised: /^[A-Z]/.test(raw),
1596
+ opensSentence
1597
+ });
1598
+ opensSentence = false;
1599
+ }
1600
+ return out;
1556
1601
  }
1557
1602
  function verifyQuotes(raw, answerText, siteText, answerOf, prospectDomain, searchTermsByClaim = /* @__PURE__ */ new Map()) {
1558
1603
  const answers = normalize(answerText);
@@ -1564,7 +1609,7 @@ function verifyQuotes(raw, answerText, siteText, answerOf, prospectDomain, searc
1564
1609
  let verdict = a.verdict;
1565
1610
  let siteQuote = a.siteQuote;
1566
1611
  if (a.verdict === "contradicted") {
1567
- for (const term of distinctive(searchTermsByClaim.get(a.claim) ?? [])) {
1612
+ for (const term of distinctive(searchTermsByClaim.get(a.claim) ?? [], a.claim)) {
1568
1613
  if (findTerm(site, term) !== "exact") continue;
1569
1614
  const at = site.indexOf(normalize(term));
1570
1615
  verdict = "confirmed";
@@ -1970,7 +2015,7 @@ function claudeCodeEngine(run = defaultClaudeCodeRun) {
1970
2015
  function defaultLighthouseDeps() {
1971
2016
  return {
1972
2017
  async audit(site) {
1973
- const { lighthouseAudit } = await import("./lighthouse-52WSPZ5D.js");
2018
+ const { lighthouseAudit } = await import("./lighthouse-RFMFREEX.js");
1974
2019
  return lighthouseAudit({ site });
1975
2020
  }
1976
2021
  };
@@ -2442,7 +2487,12 @@ async function checkBasics(crawl, deps) {
2442
2487
  // Only meaningful on an https site, and only over the images the extract
2443
2488
  // records — stylesheets and scripts are not captured, so the report must
2444
2489
  // not claim to have checked them.
2445
- measured: isHttps && usable.pages.length > 0,
2490
+ //
2491
+ // `imageSrcsMeasured`, not `pages.length > 0`: the loop above reads
2492
+ // `extract.imageSrcs ?? []`, and that field is absent on every report
2493
+ // stored before it existed. Counting those as measured published "we
2494
+ // checked and found none" from a measurement never taken.
2495
+ measured: isHttps && usable.imageSrcsMeasured,
2446
2496
  imageUrls: [...insecureImages].slice(0, 12),
2447
2497
  imagesSeen
2448
2498
  },
@@ -3014,4 +3064,4 @@ export {
3014
3064
  mergeFixes,
3015
3065
  runProspectAudit
3016
3066
  };
3017
- //# sourceMappingURL=chunk-QFSL2L2B.js.map
3067
+ //# sourceMappingURL=chunk-L5F3WYIY.js.map