@canonry/canonry 7.76.0 → 7.76.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.
@@ -3549,9 +3549,9 @@ function resolveOurPage(cq, inventory) {
3549
3549
  if (cq.gscPage && cq.gscPosition !== null) {
3550
3550
  return { url: cq.gscPage, position: cq.gscPosition, source: "gsc" };
3551
3551
  }
3552
- for (const page3 of inventory) {
3553
- if (slugMatchesQuery(page3.url, cq.query)) {
3554
- return { url: page3.url, position: 100, source: "inventory" };
3552
+ for (const page2 of inventory) {
3553
+ if (slugMatchesQuery(page2.url, cq.query)) {
3554
+ return { url: page2.url, position: 100, source: "inventory" };
3555
3555
  }
3556
3556
  }
3557
3557
  return null;
@@ -7392,16 +7392,16 @@ var SentimentService = class {
7392
7392
  return { ...summary3, attemptCount: attemptCounts.get(row.id) ?? 0 };
7393
7393
  }) };
7394
7394
  }
7395
- job(projectId, jobId, page3) {
7395
+ job(projectId, jobId, page2) {
7396
7396
  const row = this.repository.getJob(projectId, jobId);
7397
7397
  if (!row) throw notFound("Sentiment job", jobId);
7398
7398
  const tally = this.jobTallies(projectId, [jobId], this.disabled(projectId)).get(jobId);
7399
7399
  const scope = and9(eq15(sentimentJobItems.projectId, projectId), eq15(sentimentJobItems.jobId, jobId));
7400
- if (!page3) return jobReceipt(row, tally, this.db.select({ attempt: sentimentAttempts }).from(sentimentAttempts).innerJoin(sentimentJobItems, jobAttemptJoin).where(scope).orderBy(sentimentAttempts.dispatchedAt, sentimentAttempts.id).all().map(({ attempt }) => attempt));
7401
- const limit = Math.max(1, page3.limit ?? SENTIMENT_ATTEMPT_PAGE_DEFAULT);
7400
+ if (!page2) return jobReceipt(row, tally, this.db.select({ attempt: sentimentAttempts }).from(sentimentAttempts).innerJoin(sentimentJobItems, jobAttemptJoin).where(scope).orderBy(sentimentAttempts.dispatchedAt, sentimentAttempts.id).all().map(({ attempt }) => attempt));
7401
+ const limit = Math.max(1, page2.limit ?? SENTIMENT_ATTEMPT_PAGE_DEFAULT);
7402
7402
  let after = null;
7403
- if (page3.cursor) {
7404
- const token = this.verify(page3.cursor);
7403
+ if (page2.cursor) {
7404
+ const token = this.verify(page2.cursor);
7405
7405
  if (token.kind !== "attempt-page" || token.projectId !== projectId || token.jobId !== jobId) throw validationError("Attempt cursor does not match this sentiment job.");
7406
7406
  after = token.after;
7407
7407
  }
@@ -7532,9 +7532,9 @@ var SentimentService = class {
7532
7532
  const ordered = [...groups.values()].sort(compareRows);
7533
7533
  const start = rows.after ? ordered.findIndex((group) => compareRows(group, rows.after) > 0) : 0;
7534
7534
  const from = start === -1 ? ordered.length : start;
7535
- const page3 = rows.limit === null ? ordered.slice(from) : ordered.slice(from, from + rows.limit);
7535
+ const page2 = rows.limit === null ? ordered.slice(from) : ordered.slice(from, from + rows.limit);
7536
7536
  const measuredBySourceSubject = new Map(items.map((item2) => [`${item2.work.snapshotId}\0${item2.member.subject.key}`, item2]));
7537
- const queries2 = page3.map((group) => {
7537
+ const queries2 = page2.map((group) => {
7538
7538
  const key = queryRowKey(group);
7539
7539
  const inRow = (edge) => queryRowKey(edge) === key;
7540
7540
  const assessments = !rows.include.has("assessments") ? [] : group.eligible.map((member) => {
@@ -7567,7 +7567,7 @@ var SentimentService = class {
7567
7567
  })();
7568
7568
  return { ...scoreAssessments(group.measured, group.eligible, options), queryId: group.queryId, queryText: group.queryText, queryClass: selection.queryClass, sourceSnapshotIds: [...new Set(group.eligible.map((item2) => item2.snapshotId))].sort(), assessments, locations, executionNodeKey: group.executionNodeKey };
7569
7569
  });
7570
- const next = rows.limit !== null && from + rows.limit < ordered.length ? rowKey(page3.at(-1)) : null;
7570
+ const next = rows.limit !== null && from + rows.limit < ordered.length ? rowKey(page2.at(-1)) : null;
7571
7571
  const criticizedProperties = selection.queryClass === "branded" && !disabled ? rankCriticizedProperties(breakdowns) : void 0;
7572
7572
  return { summary: { ...base, breakdowns, ...criticizedProperties ? { criticizedProperties } : {} }, queries: queries2, total: ordered.length, next };
7573
7573
  }
@@ -7648,8 +7648,8 @@ var SentimentService = class {
7648
7648
  const state = this.present(read, settings, null).summary.state;
7649
7649
  if (disabled) return { state, selection, items: [], nextCursor: null };
7650
7650
  const items = read.items.filter((item2) => (!assessmentId || item2.work.id === assessmentId) && (!outcomes || outcomes.has(item2.outcome))).sort((a, b) => a.work.id.localeCompare(b.work.id)).filter((item2) => item2.work.id > after);
7651
- const page3 = items.slice(0, limit);
7652
- return { state, selection, items: page3.map((item2) => item2.evidence), nextCursor: items.length > limit ? this.sign({ projectId, selection, fingerprint: read.fingerprint, after: page3.at(-1).work.id }) : null };
7651
+ const page2 = items.slice(0, limit);
7652
+ return { state, selection, items: page2.map((item2) => item2.evidence), nextCursor: items.length > limit ? this.sign({ projectId, selection, fingerprint: read.fingerprint, after: page2.at(-1).work.id }) : null };
7653
7653
  }
7654
7654
  previousRatedRun(projectId, query, toRunId) {
7655
7655
  const target = this.db.select().from(runs).where(and9(eq15(runs.projectId, projectId), eq15(runs.id, toRunId))).get();
@@ -7910,6 +7910,130 @@ function createOutcomeEmitter(sink) {
7910
7910
  // ../api-routes/src/oauth.ts
7911
7911
  import crypto10 from "crypto";
7912
7912
  import { and as and10, eq as eq16, inArray as inArray7, isNull as isNull2, lt as lt2 } from "drizzle-orm";
7913
+
7914
+ // ../api-routes/src/oauth-pages.ts
7915
+ var CANONRY_MARK = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="154.7 160.4 692.7 692.7" width="28" height="28">
7916
+ <rect x="154.7" y="160.4" width="692.7" height="692.7" rx="155" fill="#FFFFFF"/>
7917
+ <g fill="#000000">
7918
+ <path d="M643.280273,349.521912 C663.564758,358.227753 677.878296,372.354462 685.971619,392.573181 C686.972107,395.072723 688.558105,396.657318 690.965576,397.793091 C700.154480,402.128113 709.299805,406.557098 718.418152,411.038940 C720.137024,411.883789 722.297424,412.287781 723.082764,414.505646 C722.436829,416.736572 720.315369,417.297180 718.672974,418.283936 C710.677063,423.087982 702.737610,428.007751 694.536316,432.438995 C690.292847,434.731812 689.623474,437.601929 690.721252,441.873505 C694.409424,456.224579 697.080322,470.783447 698.648254,485.516296 C706.980408,563.808472 674.667969,637.691406 599.837036,675.696777 C591.244507,680.060791 589.348999,684.706055 588.762085,693.643799 C587.802551,708.253723 591.334656,719.604431 603.608582,728.042114 C606.993469,730.369080 609.692200,733.711975 612.633240,736.661011 C614.834778,738.868713 616.390259,741.451965 615.484070,744.714661 C614.463074,748.390442 611.040588,748.530701 608.186829,748.873535 C602.282593,749.582764 596.495117,748.300415 591.085144,746.121216 C584.405823,743.430725 578.196350,743.029907 571.347473,745.981567 C565.596313,748.460205 559.291748,749.906616 552.809509,748.608948 C546.415894,747.329041 544.679260,742.387268 549.100769,737.268677 C553.124512,732.610596 557.478271,728.232239 561.777283,723.819031 C565.233093,720.271423 566.768433,716.091919 566.706421,711.171570 C566.618774,704.216675 566.684204,697.259827 566.684204,689.673462 C558.493774,691.342041 551.083862,693.058167 543.598328,694.328552 C532.268250,696.251221 520.852844,697.742981 509.332672,697.280762 C504.396545,697.082764 503.395782,699.117798 502.988739,703.669495 C502.131439,713.256897 504.788086,720.890442 512.407776,726.972046 C516.164429,729.970459 519.393433,733.638672 522.797852,737.066711 C525.003601,739.287781 526.529358,741.943420 525.592102,745.147461 C524.603699,748.526367 521.390869,748.625854 518.629883,748.986389 C512.858459,749.740051 507.260590,748.525085 502.034332,746.295715 C496.310394,743.854187 491.006897,743.716125 485.176880,746.123047 C479.977631,748.269592 474.388336,749.726379 468.595459,748.899902 C462.113007,747.975098 460.216949,743.495361 464.340210,738.341492 C467.662201,734.189087 471.266022,730.214600 475.101440,726.532776 C479.828003,721.995667 481.496887,716.581360 481.076111,710.263123 C480.976654,708.769592 481.041229,707.264771 481.039337,705.765137 C481.027771,696.497192 481.037323,696.410034 471.694427,695.478516 C442.248657,692.542664 414.247101,684.167725 387.215302,672.526550 C348.882019,656.018433 314.611755,633.294312 284.843445,603.941284 C282.757477,601.884460 280.240143,600.093811 279.139984,596.988037 C281.286224,594.889709 283.922913,595.734070 286.249481,595.797302 C308.058136,596.389587 329.499695,594.198181 350.292328,587.321228 C397.447968,571.724976 433.482452,542.002075 460.186523,500.492340 C473.036499,480.517883 482.051941,458.583374 492.496124,437.377869 C503.334290,415.372559 514.369690,393.433014 532.379700,376.153564 C552.092468,357.240356 575.247620,345.211548 602.822083,343.025940 C616.616638,341.932617 630.063782,344.054810 643.280273,349.521912 M495.527954,631.939941 C547.588440,610.400635 576.816711,571.974487 581.564331,515.495239 C581.785034,512.869202 582.262817,510.040649 580.342407,507.036652 C576.384949,514.126831 573.027527,520.802063 568.784912,526.969849 C554.886353,547.175476 536.622559,562.492676 515.187866,573.967163 C493.818420,585.406677 473.139191,597.697327 455.509399,614.599426 C445.589752,624.109680 434.727051,632.323120 421.726410,637.308838 C420.815826,637.658081 419.832977,638.096313 420.111389,640.036011 C445.648468,644.186218 470.547882,641.495972 495.527954,631.939941 M620.682800,404.632080 C621.568542,406.034393 622.304260,407.563599 623.365356,408.817505 C628.476868,414.857666 636.107910,416.365417 643.242798,412.856628 C649.758179,409.652466 653.593445,402.235199 652.212341,395.509857 C650.181763,385.621887 641.324646,379.919281 631.735962,382.326355 C622.488525,384.647736 617.546265,394.065552 620.682800,404.632080 z"/>
7919
+ <path d="M609.415771,298.999939 C609.404480,308.826233 609.236938,318.156067 609.453552,327.476990 C609.549316,331.600281 608.532349,333.592590 603.946533,333.514648 C599.994507,333.447479 595.979919,334.127533 592.078735,334.906799 C588.809875,335.559723 586.920410,334.955017 585.414673,331.663605 C576.860962,312.965210 563.870667,298.161072 545.954712,287.865753 C542.771973,286.036835 540.935547,284.239990 545.001099,281.431763 C562.686279,269.215973 582.051392,262.950745 603.739685,264.480255 C607.911316,264.774414 609.185059,266.794464 609.195984,270.523499 C609.223389,279.849091 609.336914,289.174438 609.415771,298.999939 z"/>
7920
+ <path d="M528.548218,295.440735 C530.920166,292.833984 533.166809,292.846558 535.939697,294.247742 C553.777527,303.261475 567.022461,316.798035 576.192078,334.477356 C577.604675,337.200958 577.081482,338.818573 574.231934,339.850098 C565.409119,343.044037 557.316101,347.609619 549.694580,353.041199 C548.369812,353.985291 547.054626,354.269806 545.502991,353.559906 C532.158264,347.454254 518.989014,348.832764 505.922913,354.526489 C505.487671,354.716187 504.953705,354.679352 504.485840,354.745270 C498.829224,345.102753 512.282654,311.787689 528.548218,295.440735 z"/>
7921
+ <path d="M627.180786,334.941315 C618.853943,333.440460 618.957336,333.440582 618.967651,325.463226 C618.989685,308.494110 618.886902,291.524811 618.848572,274.555603 C618.835571,268.806610 619.716431,268.037811 625.258728,269.238525 C643.614075,273.215179 658.819092,282.436798 671.065857,296.672333 C673.732483,299.771912 674.003357,301.755615 670.442383,304.695953 C660.370789,313.012299 655.150024,324.168915 653.311096,336.971527 C652.435242,343.069092 652.024658,343.353271 646.585510,340.962372 C640.477112,338.277283 634.187866,336.164337 627.180786,334.941315 z"/>
7922
+ </g>
7923
+ </svg>`;
7924
+ var FONT_PATH = /^\/[\w\-./]*\.woff2$/;
7925
+ var styles = `
7926
+ :root {
7927
+ color-scheme: dark;
7928
+ --bg: hsl(0 0% 4%); --surface: hsl(0 0% 4%); --hover: hsl(0 0% 10%);
7929
+ --border: hsl(0 0% 18%); --border-strong: hsl(0 0% 53%);
7930
+ --text: hsl(0 0% 93%); --secondary: hsl(0 0% 63%);
7931
+ --accent-hover: hsl(0 0% 80%); --focus: hsl(210 100% 66%);
7932
+ --negative: hsl(358 100% 69%);
7933
+ --caution: hsl(39 90% 50%); --caution-surface: hsl(35 100% 8%); --caution-border: hsl(35 100% 17%);
7934
+ font-family: 'Geist', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
7935
+ font-synthesis: none; -webkit-font-smoothing: antialiased;
7936
+ background: var(--bg); color: var(--text);
7937
+ }
7938
+ * { box-sizing: border-box; }
7939
+ body { margin: 0; font-size: 14px; line-height: 20px; }
7940
+ main { min-height: 100vh; min-height: 100svh; display: flex; align-items: center; justify-content: center; padding: 48px 24px; }
7941
+ .gate { width: 100%; max-width: 448px; min-width: 0; }
7942
+ .brand { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 24px; font-size: 18px; line-height: 24px; font-weight: 600; }
7943
+ .brand-icon, .brand-icon svg { display: block; width: 28px; height: 28px; }
7944
+ .panel { background: var(--surface); border: 1px solid var(--border); border-radius: 6px; padding: 32px; }
7945
+ h1 { margin: 0; font-size: 24px; line-height: 32px; font-weight: 600; letter-spacing: -.96px; }
7946
+ p { margin: 0; overflow-wrap: anywhere; }
7947
+ .intro { color: var(--secondary); margin-top: 8px; }
7948
+ strong { color: var(--text); font-weight: 550; }
7949
+ .err { margin-top: 16px; color: var(--negative); }
7950
+ .warn { margin-top: 16px; padding: 12px; border: 1px solid var(--caution-border); border-radius: 6px; background: var(--caution-surface); color: var(--caution); font-size: 13px; line-height: 20px; }
7951
+ .grant { margin: 24px 0 0; padding: 16px 0 20px; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); font-size: 13px; line-height: 20px; }
7952
+ dt { color: var(--secondary); }
7953
+ dd { margin: 4px 0 0; overflow-wrap: anywhere; }
7954
+ dd + dt { margin-top: 12px; }
7955
+ form { margin-top: 28px; }
7956
+ .grant + form, .warn + form { margin-top: 0; }
7957
+ label { display: block; margin-bottom: 8px; font-weight: 500; }
7958
+ .field + .field { margin-top: 16px; }
7959
+ input, button { font: inherit; }
7960
+ input:is([type="text"], [type="password"]) { display: block; width: 100%; min-width: 0; height: 40px; padding: 0 12px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); }
7961
+ input:is([type="text"], [type="password"]):hover { border-color: var(--border-strong); }
7962
+ .actions { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 10px; margin-top: 24px; }
7963
+ .actions.single { grid-template-columns: minmax(0, 1fr); }
7964
+ button { min-height: 36px; border: 1px solid transparent; border-radius: 6px; padding: 7px 14px; font-size: 14px; line-height: 20px; font-weight: 500; cursor: pointer; transition: background-color 150ms ease-out, border-color 150ms ease-out; }
7965
+ .primary { background: var(--text); color: var(--bg); }
7966
+ .primary:hover { background: var(--accent-hover); }
7967
+ .primary:active { background: var(--secondary); }
7968
+ .secondary { border-color: var(--border); background: var(--surface); color: var(--text); }
7969
+ .secondary:hover { background: var(--hover); border-color: var(--border-strong); }
7970
+ .secondary:active { background: var(--border); }
7971
+ :where(input, button):focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
7972
+ @media (max-width: 480px) {
7973
+ main { padding: 32px 16px; align-items: flex-start; }
7974
+ .panel { padding: 24px; }
7975
+ .actions { grid-template-columns: minmax(0, 1fr); }
7976
+ input:is([type="text"], [type="password"]) { height: 48px; font-size: 16px; }
7977
+ button { min-height: 44px; padding: 11px 14px; }
7978
+ }
7979
+ @media (pointer: coarse) { input:is([type="text"], [type="password"]), button { min-height: 44px; } }
7980
+ @media (prefers-reduced-motion: reduce) { button { transition: none; } }
7981
+ @media (forced-colors: active) { .primary, .secondary, input:is([type="text"], [type="password"]) { border: 1px solid ButtonText; } }
7982
+ `;
7983
+ function escapeHtml(value) {
7984
+ return value.replace(/[&<>"']/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[char] ?? char);
7985
+ }
7986
+ function breakableUrl(value) {
7987
+ return escapeHtml(value).replace(/(?<!\/)\/(?!\/)/g, "/<wbr>");
7988
+ }
7989
+ function fontFace(fontUrl) {
7990
+ if (!fontUrl || !FONT_PATH.test(fontUrl)) return "";
7991
+ return `@font-face {
7992
+ font-family: 'Geist'; font-style: normal; font-weight: 100 900; font-display: swap;
7993
+ src: url('${fontUrl}') format('woff2');
7994
+ }`;
7995
+ }
7996
+ function gatePage(title, body, fontUrl) {
7997
+ return `<!doctype html>
7998
+ <html lang="en"><head>
7999
+ <meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
8000
+ <meta name="color-scheme" content="dark"><meta name="theme-color" content="#0a0a0a">
8001
+ <title>${title} \xB7 Canonry</title>
8002
+ <style>${fontFace(fontUrl)}${styles}</style>
8003
+ </head><body><main><div class="gate">
8004
+ <div class="brand"><span class="brand-icon" aria-hidden="true">${CANONRY_MARK}</span><span>Canonry</span></div>
8005
+ <section class="panel" aria-labelledby="gate-title">
8006
+ ${body}
8007
+ </section>
8008
+ </div></main></body></html>`;
8009
+ }
8010
+ function renderSignInPage(o) {
8011
+ return gatePage("Sign in", `<h1 id="gate-title">Sign in to continue</h1>
8012
+ ${o.error ? `<p class="err" role="alert">${escapeHtml(o.error)}</p>` : ""}
8013
+ <form method="post" action="${escapeHtml(o.action)}">
8014
+ <div class="field"><label for="name">Name</label><input id="name" type="text" name="name" autocomplete="username" autocapitalize="none" spellcheck="false" autofocus></div>
8015
+ <div class="field"><label for="password">Password</label><input id="password" type="password" name="password" autocomplete="current-password"></div>
8016
+ <div class="actions single"><button class="primary" type="submit">Sign in</button></div>
8017
+ </form>`, o.fontUrl);
8018
+ }
8019
+ function renderConsentPage(o) {
8020
+ const cut = o.action.indexOf("?");
8021
+ const consentAction = cut === -1 ? `${o.action}/consent` : `${o.action.slice(0, cut)}/consent${o.action.slice(cut)}`;
8022
+ return gatePage("Allow access", `<h1 id="gate-title">Allow access?</h1>
8023
+ <p class="intro"><strong>${escapeHtml(o.clientName)}</strong> wants to access Canonry as <strong>${escapeHtml(o.userName)}</strong>.</p>
8024
+ ${o.selfRegistered ? '<p class="warn">This application chose its own name and has not been verified by anyone. Check the address below before allowing it.</p>' : ""}
8025
+ <dl class="grant">
8026
+ <dt>Permissions</dt><dd>${escapeHtml(o.scope)}</dd>
8027
+ <dt>Redirects to</dt><dd>${breakableUrl(o.redirectUri)}</dd>
8028
+ </dl>
8029
+ ${o.scope.split(/\s+/).includes(RESEARCH_RUN_SCOPE) ? `<p class="warn">Run research queries using the operator's provider quota, subject to the shared daily research limit. This does not grant permission to change tracking or settings.</p>` : ""}
8030
+ <form method="post" action="${escapeHtml(consentAction)}">
8031
+ <input type="hidden" name="csrf" value="${escapeHtml(o.csrf)}">
8032
+ <div class="actions"><button class="primary" type="submit" name="approve" value="yes">Allow</button><button class="secondary" type="submit" name="approve" value="no">Deny</button></div>
8033
+ </form>`, o.fontUrl);
8034
+ }
8035
+
8036
+ // ../api-routes/src/oauth.ts
7913
8037
  var CODE_TTL_MS = 6e4;
7914
8038
  var ACCESS_TTL_MS = 60 * 60 * 1e3;
7915
8039
  var REFRESH_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
@@ -7959,66 +8083,11 @@ function resolveRequestedScope(requested) {
7959
8083
  function badRequest(reply, error, description) {
7960
8084
  return reply.status(400).send({ error, error_description: description });
7961
8085
  }
7962
- function escapeHtml(value) {
7963
- return value.replace(/[&<>"']/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[char] ?? char);
7964
- }
7965
- function signInPage(action, error) {
7966
- return page(`<h1>Sign in to continue</h1>
7967
- ${error ? `<p class="err">${escapeHtml(error)}</p>` : ""}
7968
- <label for="name">Name</label><input id="name" name="name" autocomplete="username" autofocus>
7969
- <label for="password">Password</label><input id="password" name="password" type="password" autocomplete="current-password">
7970
- <button type="submit">Sign in</button>`, action);
7971
- }
7972
- function consentPage(o) {
7973
- const cut = o.action.indexOf("?");
7974
- const consentAction = cut === -1 ? `${o.action}/consent` : `${o.action.slice(0, cut)}/consent${o.action.slice(cut)}`;
7975
- return page(`<h1>Allow access?</h1>
7976
- <p class="who"><strong>${escapeHtml(o.clientName)}</strong> wants to access Canonry as
7977
- <strong>${escapeHtml(o.userName)}</strong>.</p>
7978
- ${o.selfRegistered ? `<p class="warn">This application chose its own name and has not been verified by anyone.
7979
- Check the address below before allowing it.</p>` : ""}
7980
- <dl>
7981
- <dt>Permissions</dt><dd>${escapeHtml(o.scope)}</dd>
7982
- <dt>Redirects to</dt><dd class="uri">${escapeHtml(o.redirectUri)}</dd>
7983
- </dl>
7984
- ${o.scope.split(/\s+/).includes(RESEARCH_RUN_SCOPE) ? `<p class="warn">Run research queries using the operator's provider quota, subject to the shared daily research limit. This does not grant permission to change tracking or settings.</p>` : ""}
7985
- <input type="hidden" name="csrf" value="${escapeHtml(o.csrf)}">
7986
- <div class="row">
7987
- <button type="submit" name="approve" value="no" class="ghost">Deny</button>
7988
- <button type="submit" name="approve" value="yes">Allow</button>
7989
- </div>`, consentAction);
7990
- }
7991
8086
  function sendGatePage(reply, html) {
7992
8087
  return reply.header(
7993
8088
  "content-security-policy",
7994
- "frame-ancestors 'none'; default-src 'none'; style-src 'unsafe-inline'; form-action 'self'"
7995
- ).header("x-frame-options", "DENY").header("referrer-policy", "no-referrer").header("cache-control", "no-store").type("text/html").send(html);
7996
- }
7997
- function page(inner, action) {
7998
- return `<!doctype html>
7999
- <html lang="en"><head><meta charset="utf-8">
8000
- <meta name="viewport" content="width=device-width,initial-scale=1">
8001
- <title>Canonry</title>
8002
- <style>
8003
- body{font:16px/1.5 system-ui,sans-serif;background:#10141c;color:#e8eaed;display:grid;place-items:center;min-height:100vh;margin:0}
8004
- form{background:#171c26;padding:2rem;border-radius:12px;width:min(26rem,90vw);display:grid;gap:.75rem}
8005
- h1{font-size:1.1rem;margin:0 0 .5rem}
8006
- label,dt{font-size:.85rem;color:#9aa4b2}
8007
- dl{margin:0;display:grid;gap:.5rem}
8008
- dd{margin:0}
8009
- .uri{word-break:break-all;font-family:ui-monospace,monospace;font-size:.85rem}
8010
- input{padding:.6rem;border-radius:6px;border:1px solid #2a3140;background:#0d1117;color:inherit;font:inherit}
8011
- input[type=hidden]{display:none}
8012
- button{padding:.6rem 1rem;border-radius:6px;border:0;background:#3b82f6;color:#fff;font:inherit;cursor:pointer}
8013
- .ghost{background:#2a3140}
8014
- .row{display:flex;gap:.5rem;justify-content:flex-end}
8015
- .err{color:#f87171;font-size:.85rem}
8016
- .who{margin:0}
8017
- .warn{margin:0;padding:.6rem;border-radius:6px;background:#3a2a12;color:#fbbf24;font-size:.85rem}
8018
- </style></head>
8019
- <body><form method="post" action="${escapeHtml(action)}">
8020
- ${inner}
8021
- </form></body></html>`;
8089
+ "frame-ancestors 'none'; default-src 'none'; style-src 'unsafe-inline'; font-src 'self'; form-action 'self'"
8090
+ ).header("x-frame-options", "DENY").header("referrer-policy", "same-origin").header("cache-control", "no-store").type("text/html").send(html);
8022
8091
  }
8023
8092
  var CONSENT_SECRET = crypto10.randomBytes(32);
8024
8093
  function registerOAuthRoutes(app, opts) {
@@ -8116,16 +8185,17 @@ function registerOAuthRoutesIn(app, opts) {
8116
8185
  }
8117
8186
  const user = opts.resolveUser(request, reply);
8118
8187
  if (!user) {
8119
- return sendGatePage(reply, signInPage(request.url, null));
8188
+ return sendGatePage(reply, renderSignInPage({ action: request.url, error: null, fontUrl: opts.fontUrl }));
8120
8189
  }
8121
- return sendGatePage(reply, consentPage({
8190
+ return sendGatePage(reply, renderConsentPage({
8122
8191
  action: request.url,
8123
8192
  clientName: client.name,
8124
8193
  selfRegistered: client.registration === "dynamic",
8125
8194
  redirectUri,
8126
8195
  scope: grantedScope,
8127
8196
  userName: user.name,
8128
- csrf: consentToken(user.id, clientId, redirectUri, grantedScope)
8197
+ csrf: consentToken(user.id, clientId, redirectUri, grantedScope),
8198
+ fontUrl: opts.fontUrl
8129
8199
  }));
8130
8200
  });
8131
8201
  app.post(`${authorizationPath}/consent`, async (request, reply) => {
@@ -8244,11 +8314,11 @@ function registerOAuthRoutesIn(app, opts) {
8244
8314
  const name = body.name?.trim();
8245
8315
  const password = body.password;
8246
8316
  if (!name || !password) {
8247
- return sendGatePage(reply, signInPage(request.url, "Enter a name and password."));
8317
+ return sendGatePage(reply, renderSignInPage({ action: request.url, error: "Enter a name and password.", fontUrl: opts.fontUrl }));
8248
8318
  }
8249
8319
  const result = await opts.credentials.verify(request, name, password);
8250
8320
  if (!result.ok) {
8251
- return sendGatePage(reply, signInPage(request.url, result.message));
8321
+ return sendGatePage(reply, renderSignInPage({ action: request.url, error: result.message, fontUrl: opts.fontUrl }));
8252
8322
  }
8253
8323
  void reply.header("set-cookie", opts.startSession(result.user.id));
8254
8324
  return reply.redirect(request.url);
@@ -13552,7 +13622,7 @@ function runProgress(db, displayed, plan) {
13552
13622
  }
13553
13623
  function planV1Overview(db, projectId, active, query, scope) {
13554
13624
  const displayed = selectDisplayedRun(db, projectId, active, query);
13555
- const { page: page3, outcomes } = pageOf(
13625
+ const { page: page2, outcomes } = pageOf(
13556
13626
  propertyLabels(active.plan, scope).filter((row) => matchesSearch(row, query.search)).map((row) => ({
13557
13627
  ...row,
13558
13628
  mentionCoverage: unavailable2("plan_v1"),
@@ -13585,7 +13655,7 @@ function planV1Overview(db, projectId, active, query, scope) {
13585
13655
  brandPresence: unavailable2("plan_v1"),
13586
13656
  sov: unavailable2("plan_v1")
13587
13657
  },
13588
- properties: page3,
13658
+ properties: page2,
13589
13659
  outcomes,
13590
13660
  flags: { total: 0 }
13591
13661
  };
@@ -13597,7 +13667,7 @@ function planV2Overview(db, projectId, active, plan, query, scope, cache2) {
13597
13667
  const currentDto = current ? { currentRunId: current.id } : {};
13598
13668
  const locate = propertyLocations(plan);
13599
13669
  if (!displayed) {
13600
- const { page: page4, outcomes: outcomes2 } = pageOf(
13670
+ const { page: page3, outcomes: outcomes2 } = pageOf(
13601
13671
  propertyLabels(plan, scope).filter((row) => matchesSearch(row, query.search)).map((row) => ({
13602
13672
  ...row,
13603
13673
  ...metroFields(locate(row.targetKey)),
@@ -13629,7 +13699,7 @@ function planV2Overview(db, projectId, active, plan, query, scope, cache2) {
13629
13699
  brandPresence: unavailable2("no_completed_run"),
13630
13700
  sov: unavailable2("no_completed_run")
13631
13701
  },
13632
- properties: page4,
13702
+ properties: page3,
13633
13703
  outcomes: outcomes2,
13634
13704
  flags: { total: 0 }
13635
13705
  };
@@ -13659,7 +13729,7 @@ function planV2Overview(db, projectId, active, plan, query, scope, cache2) {
13659
13729
  const measured = new Map(
13660
13730
  overview.properties.map((row) => [row.targetId, row])
13661
13731
  );
13662
- const { page: page3, outcomes } = pageOf(
13732
+ const { page: page2, outcomes } = pageOf(
13663
13733
  propertyLabels(plan, scope).filter((row) => matchesSearch(row, query.search)).map((row) => {
13664
13734
  const property = measured.get(row.targetKey);
13665
13735
  return {
@@ -13713,7 +13783,7 @@ function planV2Overview(db, projectId, active, plan, query, scope, cache2) {
13713
13783
  // the browser migrates off it (§0.2). The two must never diverge.
13714
13784
  sov: brandPresence
13715
13785
  },
13716
- properties: page3,
13786
+ properties: page2,
13717
13787
  outcomes,
13718
13788
  flags: { total: overview.flags },
13719
13789
  ...namedShareOfVoice === void 0 ? {} : { namedShareOfVoice }
@@ -22789,7 +22859,7 @@ function compactPortfolioPage(response, query, versionId, evidence, cursor) {
22789
22859
  })).toString("base64url") : null
22790
22860
  };
22791
22861
  };
22792
- const fits = (page4) => JSON.stringify(page4, null, 2).length <= 18e3;
22862
+ const fits = (page3) => JSON.stringify(page3, null, 2).length <= 18e3;
22793
22863
  const initialComplete = build(requested, initialSummaryLimit);
22794
22864
  if (fits(initialComplete)) return measurementPortfolioSummaryResponseSchema.parse(initialComplete);
22795
22865
  const minimum = Math.min(1, requested);
@@ -22805,19 +22875,19 @@ function compactPortfolioPage(response, query, versionId, evidence, cursor) {
22805
22875
  }
22806
22876
  const complete = build(requested, summaryLimit);
22807
22877
  if (fits(complete)) return measurementPortfolioSummaryResponseSchema.parse(complete);
22808
- let page3 = build(minimum, summaryLimit);
22809
- if (!fits(page3)) throw validationError("One portfolio row or its metadata exceeds the compact page budget. Read a narrower scope without compact=true.");
22878
+ let page2 = build(minimum, summaryLimit);
22879
+ if (!fits(page2)) throw validationError("One portfolio row or its metadata exceeds the compact page budget. Read a narrower scope without compact=true.");
22810
22880
  let low = minimum + 1;
22811
22881
  let high = requested;
22812
22882
  while (low <= high) {
22813
22883
  const count10 = Math.floor((low + high) / 2);
22814
22884
  const candidate = build(count10, summaryLimit);
22815
22885
  if (fits(candidate)) {
22816
- page3 = candidate;
22886
+ page2 = candidate;
22817
22887
  low = count10 + 1;
22818
22888
  } else high = count10 - 1;
22819
22889
  }
22820
- return measurementPortfolioSummaryResponseSchema.parse(page3);
22890
+ return measurementPortfolioSummaryResponseSchema.parse(page2);
22821
22891
  }
22822
22892
  function portfolioAnswerEvidence(plan, materialized, filters, targetKeys) {
22823
22893
  const targets = new Set(targetKeys);
@@ -24036,7 +24106,7 @@ function parseCursor3(value) {
24036
24106
  return null;
24037
24107
  }
24038
24108
  }
24039
- function page2(rows, keyOf, kind, queryClass, input, run) {
24109
+ function page(rows, keyOf, kind, queryClass, input, run) {
24040
24110
  const selectionFingerprint = fingerprint({
24041
24111
  mode: input.mode,
24042
24112
  activeRevision: input.activeRevision,
@@ -24176,8 +24246,8 @@ function emptyPopulation(queryClass, definition2, input) {
24176
24246
  queryClass,
24177
24247
  summary: summary2(candidates, definition2, populationTargetKeys),
24178
24248
  trend: [],
24179
- queries: page2([], () => "", "queries", queryClass, input, void 0),
24180
- evidence: page2([], () => "", "evidence", queryClass, input, void 0),
24249
+ queries: page([], () => "", "queries", queryClass, input, void 0),
24250
+ evidence: page([], () => "", "evidence", queryClass, input, void 0),
24181
24251
  competitorAvailability: definition2.competitorAvailability,
24182
24252
  competitors: [],
24183
24253
  observedCompetitors: [],
@@ -24235,8 +24305,8 @@ function buildVisibilityReport(input) {
24235
24305
  summary: selectedSummary,
24236
24306
  trend,
24237
24307
  ...basis === void 0 ? {} : { comparison: populationComparison(basis, selectedSummary, input.selection, queryClass) },
24238
- queries: page2(rows, (row) => [row.queryKey, row.provider, row.model ?? "", row.location ?? ""].join("\0"), "queries", queryClass, input, selectedRun),
24239
- evidence: page2(evidence, (row) => [row.answerId, row.runId].join("\0"), "evidence", queryClass, input, selectedRun),
24308
+ queries: page(rows, (row) => [row.queryKey, row.provider, row.model ?? "", row.location ?? ""].join("\0"), "queries", queryClass, input, selectedRun),
24309
+ evidence: page(evidence, (row) => [row.answerId, row.runId].join("\0"), "evidence", queryClass, input, selectedRun),
24240
24310
  competitorAvailability: definition2.competitorAvailability,
24241
24311
  competitors: competitorRows(selected),
24242
24312
  observedCompetitors: observedCompetitorRows(selected),
@@ -29858,9 +29928,9 @@ async function journeyRoutes(app) {
29858
29928
  const visitor = app.db.select().from(journeyVisitors).where(and42(eq50(journeyVisitors.projectId, project.id), eq50(journeyVisitors.visitorKey, request.params.visitorKey))).get();
29859
29929
  if (!visitor) throw notFound("Journey", request.params.visitorKey);
29860
29930
  const visits = visitsByVisitor(app.db, project.id, [visitor.visitorKey]).get(visitor.visitorKey) ?? [];
29861
- const page3 = visits.slice(offset, offset + limit);
29931
+ const page2 = visits.slice(offset, offset + limit);
29862
29932
  const events = /* @__PURE__ */ new Map();
29863
- for (const part of chunks(page3.map((v) => v.id))) {
29933
+ for (const part of chunks(page2.map((v) => v.id))) {
29864
29934
  for (const row of app.db.select().from(journeyEvents).where(inArray28(journeyEvents.visitId, part)).orderBy(asc10(journeyEvents.ts)).all()) {
29865
29935
  const list = events.get(row.visitId);
29866
29936
  if (list) list.push(row);
@@ -29873,7 +29943,7 @@ async function journeyRoutes(app) {
29873
29943
  totalVisits: visits.length,
29874
29944
  limit,
29875
29945
  offset,
29876
- visits: page3.map((v) => ({
29946
+ visits: page2.map((v) => ({
29877
29947
  ...visitDto(v),
29878
29948
  events: (events.get(v.id) ?? []).map((e) => ({ ts: e.ts, method: e.method, path: e.path, status: e.status, kind: e.kind === "data" ? "data" : "page", aiTouch: e.aiTouch }))
29879
29949
  }))
@@ -31190,9 +31260,9 @@ function buildOrganicEvidence(db, projectName, periodDays) {
31190
31260
  return created;
31191
31261
  };
31192
31262
  for (const row of pageSearchRows) {
31193
- const page3 = ensurePage(row.page);
31194
- page3.gsc.clicks += row.clicks;
31195
- page3.gsc.impressions += row.impressions;
31263
+ const page2 = ensurePage(row.page);
31264
+ page2.gsc.clicks += row.clicks;
31265
+ page2.gsc.impressions += row.impressions;
31196
31266
  }
31197
31267
  if (nativeAcquisition.status === "never-synced") for (const row of gaWindow) {
31198
31268
  ensurePage(row.landingPageNormalized ?? row.landingPage).ga4OrganicSessions += row.organicSessions;
@@ -33549,12 +33619,12 @@ function aggregateGscByQuery(rows, queryTotals = []) {
33549
33619
  const accurateByQuery = new Map(queryTotals.map((t) => [t.query, t]));
33550
33620
  const accumulators = /* @__PURE__ */ new Map();
33551
33621
  for (const r of rows) {
33552
- const page3 = extractPath2(r.page);
33622
+ const page2 = extractPath2(r.page);
33553
33623
  const position = Number(r.position) || 0;
33554
33624
  const existing = accumulators.get(r.query);
33555
33625
  if (!existing) {
33556
33626
  accumulators.set(r.query, {
33557
- bestPage: page3,
33627
+ bestPage: page2,
33558
33628
  bestPageImpressions: r.impressions,
33559
33629
  totalClicks: r.clicks,
33560
33630
  totalImpressions: r.impressions,
@@ -33566,7 +33636,7 @@ function aggregateGscByQuery(rows, queryTotals = []) {
33566
33636
  existing.totalImpressions += r.impressions;
33567
33637
  existing.weightedPositionSum += position * r.impressions;
33568
33638
  if (r.impressions > existing.bestPageImpressions) {
33569
- existing.bestPage = page3;
33639
+ existing.bestPage = page2;
33570
33640
  existing.bestPageImpressions = r.impressions;
33571
33641
  }
33572
33642
  }
@@ -43227,21 +43297,21 @@ async function operationalLogsRoutes(app, opts = {}) {
43227
43297
  if (!opts.listOperationalLogs) {
43228
43298
  throw notImplemented("Operational logs are not available in this deployment.");
43229
43299
  }
43230
- const page3 = operationalLogListDtoSchema.parse(await opts.listOperationalLogs(parsed.data));
43300
+ const page2 = operationalLogListDtoSchema.parse(await opts.listOperationalLogs(parsed.data));
43231
43301
  reply.header("vary", OPERATIONAL_LOG_FIELDS_HEADER);
43232
- return withoutUnrequestedFields(page3, requestedFields(request.headers[OPERATIONAL_LOG_FIELDS_HEADER]));
43302
+ return withoutUnrequestedFields(page2, requestedFields(request.headers[OPERATIONAL_LOG_FIELDS_HEADER]));
43233
43303
  });
43234
43304
  }
43235
43305
  function requestedFields(header2) {
43236
43306
  const names = (Array.isArray(header2) ? header2 : [header2 ?? ""]).flatMap((value) => value.split(",")).map((name) => name.trim());
43237
43307
  return new Set(OPERATIONAL_LOG_OPT_IN_CONTEXT_FIELDS.filter((field) => names.includes(field)));
43238
43308
  }
43239
- function withoutUnrequestedFields(page3, requested) {
43309
+ function withoutUnrequestedFields(page2, requested) {
43240
43310
  const omitted = OPERATIONAL_LOG_OPT_IN_CONTEXT_FIELDS.filter((field) => !requested.has(field));
43241
- if (omitted.length === 0) return page3;
43311
+ if (omitted.length === 0) return page2;
43242
43312
  return {
43243
- ...page3,
43244
- entries: page3.entries.map((entry) => ({
43313
+ ...page2,
43314
+ entries: page2.entries.map((entry) => ({
43245
43315
  ...entry,
43246
43316
  context: Object.fromEntries(Object.entries(entry.context).filter(([key]) => !omitted.includes(key)))
43247
43317
  }))
@@ -43877,7 +43947,7 @@ async function createLocalPost(token, account, location, content) {
43877
43947
  async function listAccounts(accessToken, opts = {}) {
43878
43948
  const collected = [];
43879
43949
  let pageToken;
43880
- let page3 = 0;
43950
+ let page2 = 0;
43881
43951
  do {
43882
43952
  const url = new URL(`${GBP_ACCOUNT_MANAGEMENT_BASE}/accounts`);
43883
43953
  url.searchParams.set("pageSize", String(GBP_DEFAULT_PAGE_SIZE));
@@ -43885,8 +43955,8 @@ async function listAccounts(accessToken, opts = {}) {
43885
43955
  const res = await gbpFetchGet(url.toString(), accessToken, opts);
43886
43956
  if (res.accounts) collected.push(...res.accounts);
43887
43957
  pageToken = res.nextPageToken;
43888
- page3++;
43889
- } while (pageToken && page3 < GBP_MAX_PAGES);
43958
+ page2++;
43959
+ } while (pageToken && page2 < GBP_MAX_PAGES);
43890
43960
  return collected;
43891
43961
  }
43892
43962
 
@@ -43895,7 +43965,7 @@ async function listLocations(accessToken, accountName, opts = {}) {
43895
43965
  const readMask = opts.readMask ?? GBP_LOCATIONS_DEFAULT_READ_MASK;
43896
43966
  const collected = [];
43897
43967
  let pageToken;
43898
- let page3 = 0;
43968
+ let page2 = 0;
43899
43969
  do {
43900
43970
  const url = new URL(`${GBP_BUSINESS_INFO_BASE}/${accountName}/locations`);
43901
43971
  url.searchParams.set("readMask", readMask);
@@ -43904,8 +43974,8 @@ async function listLocations(accessToken, accountName, opts = {}) {
43904
43974
  const res = await gbpFetchGet(url.toString(), accessToken, opts);
43905
43975
  if (res.locations) collected.push(...res.locations);
43906
43976
  pageToken = res.nextPageToken;
43907
- page3++;
43908
- } while (pageToken && page3 < GBP_MAX_PAGES);
43977
+ page2++;
43978
+ } while (pageToken && page2 < GBP_MAX_PAGES);
43909
43979
  return collected;
43910
43980
  }
43911
43981
  function formatStorefrontAddress(loc) {
@@ -43976,7 +44046,7 @@ async function fetchDailyMetrics(accessToken, locationName, opts) {
43976
44046
  async function listMonthlyKeywords(accessToken, locationName, opts) {
43977
44047
  const collected = [];
43978
44048
  let pageToken;
43979
- let page3 = 0;
44049
+ let page2 = 0;
43980
44050
  do {
43981
44051
  const params = new URLSearchParams();
43982
44052
  params.set("monthlyRange.startMonth.year", String(opts.startMonth.year));
@@ -43997,8 +44067,8 @@ async function listMonthlyKeywords(accessToken, locationName, opts) {
43997
44067
  });
43998
44068
  }
43999
44069
  pageToken = res.nextPageToken;
44000
- page3++;
44001
- } while (pageToken && page3 < GBP_MAX_PAGES);
44070
+ page2++;
44071
+ } while (pageToken && page2 < GBP_MAX_PAGES);
44002
44072
  return collected;
44003
44073
  }
44004
44074
 
@@ -44006,7 +44076,7 @@ async function listMonthlyKeywords(accessToken, locationName, opts) {
44006
44076
  async function listPlaceActionLinks(accessToken, locationName, opts = {}) {
44007
44077
  const collected = [];
44008
44078
  let pageToken;
44009
- let page3 = 0;
44079
+ let page2 = 0;
44010
44080
  do {
44011
44081
  const url = new URL(`${GBP_PLACE_ACTIONS_BASE}/${locationName}/placeActionLinks`);
44012
44082
  url.searchParams.set("pageSize", String(GBP_DEFAULT_PAGE_SIZE));
@@ -44022,8 +44092,8 @@ async function listPlaceActionLinks(accessToken, locationName, opts = {}) {
44022
44092
  });
44023
44093
  }
44024
44094
  pageToken = res.nextPageToken;
44025
- page3++;
44026
- } while (pageToken && page3 < GBP_MAX_PAGES);
44095
+ page2++;
44096
+ } while (pageToken && page2 < GBP_MAX_PAGES);
44027
44097
  return collected;
44028
44098
  }
44029
44099
 
@@ -44143,14 +44213,14 @@ async function listReviews(accessToken, accountName, locationName, opts = {}) {
44143
44213
  let totalReviewCount = null;
44144
44214
  let stoppedEarly = false;
44145
44215
  let pageToken;
44146
- let page3 = 0;
44216
+ let page2 = 0;
44147
44217
  do {
44148
44218
  const url = new URL(`${GBP_LEGACY_V4_BASE}/${accountName}/${locationName}/reviews`);
44149
44219
  url.searchParams.set("pageSize", String(GBP_REVIEWS_PAGE_SIZE));
44150
44220
  url.searchParams.set("orderBy", "updateTime desc");
44151
44221
  if (pageToken) url.searchParams.set("pageToken", pageToken);
44152
44222
  const res = await gbpFetchGet(url.toString(), accessToken, opts);
44153
- if (page3 === 0) {
44223
+ if (page2 === 0) {
44154
44224
  averageRating = typeof res.averageRating === "number" ? res.averageRating : null;
44155
44225
  totalReviewCount = typeof res.totalReviewCount === "number" ? res.totalReviewCount : null;
44156
44226
  }
@@ -44160,13 +44230,13 @@ async function listReviews(accessToken, accountName, locationName, opts = {}) {
44160
44230
  const rows = mapped.filter((row) => row !== null);
44161
44231
  reviews.push(...rows);
44162
44232
  pageToken = res.nextPageToken;
44163
- page3++;
44233
+ page2++;
44164
44234
  const oldest = rows.at(-1);
44165
44235
  if (pageToken && opts.stopBefore && oldest && isNewestFirst(rows) && oldest.updateTime < opts.stopBefore) {
44166
44236
  stoppedEarly = true;
44167
44237
  break;
44168
44238
  }
44169
- } while (pageToken && page3 < maxPages);
44239
+ } while (pageToken && page2 < maxPages);
44170
44240
  if (pageToken && !stoppedEarly) throw new Error("Review pagination is incomplete");
44171
44241
  if (opts.strict && (new Set(reviews.map((row) => row.reviewName)).size !== reviews.length || totalReviewCount !== null && totalReviewCount !== reviews.length)) throw new Error("Review coverage is incomplete or changed during pagination");
44172
44242
  return { reviews, averageRating, totalReviewCount, stoppedEarly };
@@ -45429,12 +45499,12 @@ async function gbpLocalPostsRoutes(app, options) {
45429
45499
  request.query.accountName,
45430
45500
  request.query.locationName
45431
45501
  );
45432
- const page3 = request.query.pageToken;
45433
- if (page3 !== void 0 && (page3.length > 2e3 || /[\p{Cc}\p{Cf}]/u.test(page3)))
45502
+ const page2 = request.query.pageToken;
45503
+ if (page2 !== void 0 && (page2.length > 2e3 || /[\p{Cc}\p{Cf}]/u.test(page2)))
45434
45504
  throw validationError("Invalid GBP page token.");
45435
45505
  return readPostResource(
45436
45506
  project.canonicalDomain,
45437
- (token) => listLocalPosts(token, location.accountName, location.locationName, page3),
45507
+ (token) => listLocalPosts(token, location.accountName, location.locationName, page2),
45438
45508
  "Google Local Posts could not be read; coverage is unknown."
45439
45509
  );
45440
45510
  });
@@ -46187,13 +46257,13 @@ function measurementDateRange(days) {
46187
46257
  async function fetchPagedReport(accessToken, propertyId, baseRequest, pageSize = 1e4) {
46188
46258
  const rows = [];
46189
46259
  let offset = 0;
46190
- for (let page3 = 0; page3 < GA4_MAX_PAGES; page3++) {
46260
+ for (let page2 = 0; page2 < GA4_MAX_PAGES; page2++) {
46191
46261
  const response = await runReport(accessToken, propertyId, { ...baseRequest, limit: pageSize, offset });
46192
46262
  const pageRows = response.rows ?? [];
46193
46263
  rows.push(...pageRows);
46194
46264
  offset += pageRows.length;
46195
46265
  if (pageRows.length < pageSize || response.rowCount !== void 0 && offset >= response.rowCount) break;
46196
- if (page3 === GA4_MAX_PAGES - 1) {
46266
+ if (page2 === GA4_MAX_PAGES - 1) {
46197
46267
  throw new Error(`GA4 report truncated: page limit (${GA4_MAX_PAGES}) reached before all rows were fetched`);
46198
46268
  }
46199
46269
  }
@@ -46206,7 +46276,7 @@ async function fetchPagedReportWithTotals(accessToken, propertyId, baseRequest,
46206
46276
  let firstPage;
46207
46277
  let reportedRowCount;
46208
46278
  let offset = 0;
46209
- for (let page3 = 0; page3 < GA4_MAX_PAGES; page3++) {
46279
+ for (let page2 = 0; page2 < GA4_MAX_PAGES; page2++) {
46210
46280
  const limit = Math.min(pageSize, maxRows - rows.length);
46211
46281
  const response = await runReport(accessToken, propertyId, { ...baseRequest, limit, offset });
46212
46282
  firstPage ??= response;
@@ -47604,7 +47674,7 @@ async function googleRoutes(app, opts) {
47604
47674
  });
47605
47675
  app.get("/projects/:name/google/gsc/performance", async (request) => {
47606
47676
  const project = resolveProject(app.db, request.params.name);
47607
- const { startDate, endDate, query, page: page3, limit, offset } = request.query;
47677
+ const { startDate, endDate, query, page: page2, limit, offset } = request.query;
47608
47678
  const parsedOrderBy = gscPerformanceOrderBySchema.safeParse(request.query.orderBy ?? "clicks");
47609
47679
  if (!parsedOrderBy.success) {
47610
47680
  throw validationError(
@@ -47626,7 +47696,7 @@ async function googleRoutes(app, opts) {
47626
47696
  else if (cutoffDate) conditions.push(sql21`${gscSearchData.date} >= ${cutoffDate}`);
47627
47697
  if (effectiveEndDate) conditions.push(sql21`${gscSearchData.date} <= ${effectiveEndDate}`);
47628
47698
  if (query) conditions.push(sql21`${gscSearchData.query} LIKE ${"%" + escapeLikePattern(query) + "%"} ESCAPE '\\'`);
47629
- if (page3) conditions.push(sql21`${gscSearchData.page} LIKE ${"%" + escapeLikePattern(page3) + "%"} ESCAPE '\\'`);
47699
+ if (page2) conditions.push(sql21`${gscSearchData.page} LIKE ${"%" + escapeLikePattern(page2) + "%"} ESCAPE '\\'`);
47630
47700
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
47631
47701
  const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
47632
47702
  const orderColumns = {
@@ -50252,11 +50322,11 @@ async function googleMarketingRoutes(app, opts) {
50252
50322
  }
50253
50323
  const limit = query.limit ?? 50;
50254
50324
  const rows = app.db.select().from(googleAdsRawSnapshots).where(and55(...conditions)).orderBy(desc32(googleAdsRawSnapshots.capturedAt), desc32(googleAdsRawSnapshots.id)).limit(limit + 1).all();
50255
- const page3 = rows.slice(0, limit);
50325
+ const page2 = rows.slice(0, limit);
50256
50326
  const total = app.db.select({ total: sql22`COUNT(*)`.mapWith(Number).as("total") }).from(googleAdsRawSnapshots).where(eq67(googleAdsRawSnapshots.projectId, project.id)).get();
50257
50327
  return {
50258
- snapshots: page3.map(toGoogleAdsSnapshotMetadata),
50259
- nextCursor: rows.length > limit && page3.length > 0 ? encodeSnapshotCursor({ capturedAt: page3.at(-1).capturedAt, id: page3.at(-1).id }) : null,
50328
+ snapshots: page2.map(toGoogleAdsSnapshotMetadata),
50329
+ nextCursor: rows.length > limit && page2.length > 0 ? encodeSnapshotCursor({ capturedAt: page2.at(-1).capturedAt, id: page2.at(-1).id }) : null,
50260
50330
  total: total?.total ?? 0
50261
50331
  };
50262
50332
  });
@@ -50284,11 +50354,11 @@ async function googleMarketingRoutes(app, opts) {
50284
50354
  }
50285
50355
  const limit = query.limit ?? 50;
50286
50356
  const rows = app.db.select().from(gtmRawSnapshots).where(and55(...conditions)).orderBy(desc32(gtmRawSnapshots.capturedAt), desc32(gtmRawSnapshots.id)).limit(limit + 1).all();
50287
- const page3 = rows.slice(0, limit);
50357
+ const page2 = rows.slice(0, limit);
50288
50358
  const total = app.db.select({ total: sql22`COUNT(*)`.mapWith(Number).as("total") }).from(gtmRawSnapshots).where(eq67(gtmRawSnapshots.projectId, project.id)).get();
50289
50359
  return {
50290
- snapshots: page3.map(toGtmSnapshotMetadata),
50291
- nextCursor: rows.length > limit && page3.length > 0 ? encodeSnapshotCursor({ capturedAt: page3.at(-1).capturedAt, id: page3.at(-1).id }) : null,
50360
+ snapshots: page2.map(toGtmSnapshotMetadata),
50361
+ nextCursor: rows.length > limit && page2.length > 0 ? encodeSnapshotCursor({ capturedAt: page2.at(-1).capturedAt, id: page2.at(-1).id }) : null,
50292
50362
  total: total?.total ?? 0
50293
50363
  };
50294
50364
  });
@@ -54733,11 +54803,11 @@ async function adsRoutes(app, opts) {
54733
54803
  ) : void 0
54734
54804
  )).orderBy(asc15(adsOperations.createdAt), asc15(adsOperations.id)).limit(parsed.data.limit + 1).all();
54735
54805
  const hasMore = rows.length > parsed.data.limit;
54736
- const page3 = hasMore ? rows.slice(0, parsed.data.limit) : rows;
54806
+ const page2 = hasMore ? rows.slice(0, parsed.data.limit) : rows;
54737
54807
  const response = {
54738
- operations: page3.map(operationDto),
54739
- count: page3.length,
54740
- nextCursor: hasMore && page3.length > 0 ? encodeAdsOperationCursor(project.id, parsed.data.state, page3[page3.length - 1]) : null
54808
+ operations: page2.map(operationDto),
54809
+ count: page2.length,
54810
+ nextCursor: hasMore && page2.length > 0 ? encodeAdsOperationCursor(project.id, parsed.data.state, page2[page2.length - 1]) : null
54741
54811
  };
54742
54812
  return response;
54743
54813
  });
@@ -58863,9 +58933,9 @@ function contentHash(content) {
58863
58933
  return crypto48.createHash("sha256").update(content).digest("hex");
58864
58934
  }
58865
58935
  function buildAmbiguousSlugMessage(slug, pages) {
58866
- const candidates = pages.map((page3) => {
58867
- const title = stripHtml(page3.title?.rendered ?? "") || "(untitled)";
58868
- return `#${page3.id} "${title}"`;
58936
+ const candidates = pages.map((page2) => {
58937
+ const title = stripHtml(page2.title?.rendered ?? "") || "(untitled)";
58938
+ return `#${page2.id} "${title}"`;
58869
58939
  }).join(", ");
58870
58940
  return `Multiple pages matched slug "${slug}". Candidates: ${candidates}.`;
58871
58941
  }
@@ -58899,9 +58969,9 @@ function resolveSeoWriteTargets(meta, plugins) {
58899
58969
  }
58900
58970
  return [...new Set(targets)];
58901
58971
  }
58902
- function buildSeoState(page3, html, plugins) {
58972
+ function buildSeoState(page2, html, plugins) {
58903
58973
  const renderedSeo = html ? summarizeSeoFromHtml(html) : { title: null, description: null, noindex: null };
58904
- const writeTargets = resolveSeoWriteTargets(page3.meta, plugins);
58974
+ const writeTargets = resolveSeoWriteTargets(page2.meta, plugins);
58905
58975
  return {
58906
58976
  title: renderedSeo.title,
58907
58977
  description: renderedSeo.description,
@@ -58970,17 +59040,17 @@ async function listActivePlugins(connection, env) {
58970
59040
  const site = resolveEnvironment(connection, env);
58971
59041
  try {
58972
59042
  const allPlugins = [];
58973
- let page3 = 1;
59043
+ let page2 = 1;
58974
59044
  let totalPages = 1;
58975
- while (page3 <= totalPages) {
59045
+ while (page2 <= totalPages) {
58976
59046
  const { body, response } = await fetchJson(
58977
59047
  connection,
58978
59048
  site.siteUrl,
58979
- `/wp-json/wp/v2/plugins?per_page=100&page=${page3}&_fields=plugin,status`
59049
+ `/wp-json/wp/v2/plugins?per_page=100&page=${page2}&_fields=plugin,status`
58980
59050
  );
58981
59051
  totalPages = Number.parseInt(response.headers.get("x-wp-totalpages") ?? "1", 10) || 1;
58982
59052
  allPlugins.push(...body);
58983
- page3 += 1;
59053
+ page2 += 1;
58984
59054
  }
58985
59055
  return allPlugins.filter((plugin) => plugin.status === "active").map((plugin) => plugin.plugin).sort();
58986
59056
  } catch (error) {
@@ -58993,13 +59063,13 @@ async function listActivePlugins(connection, env) {
58993
59063
  async function listPages(connection, env) {
58994
59064
  const site = resolveEnvironment(connection, env);
58995
59065
  const pages = [];
58996
- let page3 = 1;
59066
+ let page2 = 1;
58997
59067
  let totalPages = 1;
58998
- while (page3 <= totalPages) {
59068
+ while (page2 <= totalPages) {
58999
59069
  const { body, response } = await fetchJson(
59000
59070
  connection,
59001
59071
  site.siteUrl,
59002
- `/wp-json/wp/v2/pages?per_page=100&page=${page3}&context=edit&_fields=${PAGE_LIST_FIELDS}`
59072
+ `/wp-json/wp/v2/pages?per_page=100&page=${page2}&context=edit&_fields=${PAGE_LIST_FIELDS}`
59003
59073
  );
59004
59074
  totalPages = Number.parseInt(response.headers.get("x-wp-totalpages") ?? "1", 10) || 1;
59005
59075
  pages.push(...body.map((entry) => ({
@@ -59010,7 +59080,7 @@ async function listPages(connection, env) {
59010
59080
  modifiedAt: entry.modified ?? entry.modified_gmt ?? null,
59011
59081
  link: entry.link ?? null
59012
59082
  })));
59013
- page3 += 1;
59083
+ page2 += 1;
59014
59084
  }
59015
59085
  return pages;
59016
59086
  }
@@ -59024,7 +59094,7 @@ async function getPageBySlug(connection, slug, env) {
59024
59094
  if (body.length === 0) {
59025
59095
  throw new WordpressApiError("NOT_FOUND", `No WordPress page found for slug "${slug}"`, 404);
59026
59096
  }
59027
- const exact = body.filter((page3) => page3.slug === slug);
59097
+ const exact = body.filter((page2) => page2.slug === slug);
59028
59098
  if (exact.length === 1) return exact[0];
59029
59099
  if (exact.length > 1) {
59030
59100
  throw new WordpressApiError("VALIDATION_ERROR", buildAmbiguousSlugMessage(slug, exact), 400);
@@ -59041,19 +59111,19 @@ async function fetchRenderedPage(connection, link) {
59041
59111
  async function getPageDetail(connection, slug, env, plugins) {
59042
59112
  const site = resolveEnvironment(connection, env);
59043
59113
  const resolvedPlugins = plugins === void 0 ? await listActivePlugins(connection, site.env) : plugins;
59044
- const page3 = await getPageBySlug(connection, slug, site.env);
59045
- const html = await fetchRenderedPage(connection, page3.link);
59114
+ const page2 = await getPageBySlug(connection, slug, site.env);
59115
+ const html = await fetchRenderedPage(connection, page2.link);
59046
59116
  const schemaBlocks = html ? extractSchemaBlocks(html) : [];
59047
- const seo = buildSeoState(page3, html, resolvedPlugins);
59117
+ const seo = buildSeoState(page2, html, resolvedPlugins);
59048
59118
  return {
59049
- id: page3.id,
59050
- slug: page3.slug,
59051
- title: stripHtml(page3.title?.rendered ?? ""),
59052
- status: page3.status,
59053
- modifiedAt: page3.modified ?? page3.modified_gmt ?? null,
59054
- link: page3.link ?? null,
59119
+ id: page2.id,
59120
+ slug: page2.slug,
59121
+ title: stripHtml(page2.title?.rendered ?? ""),
59122
+ status: page2.status,
59123
+ modifiedAt: page2.modified ?? page2.modified_gmt ?? null,
59124
+ link: page2.link ?? null,
59055
59125
  env: site.env,
59056
- content: page3.content?.raw ?? page3.content?.rendered ?? "",
59126
+ content: page2.content?.raw ?? page2.content?.rendered ?? "",
59057
59127
  seo,
59058
59128
  schemaBlocks
59059
59129
  };
@@ -59078,11 +59148,11 @@ async function createPage(connection, body, env) {
59078
59148
  }
59079
59149
  async function updatePageBySlug(connection, slug, body, env) {
59080
59150
  const site = resolveEnvironment(connection, env);
59081
- const page3 = await getPageBySlug(connection, slug, site.env);
59151
+ const page2 = await getPageBySlug(connection, slug, site.env);
59082
59152
  const { body: updated } = await fetchJson(
59083
59153
  connection,
59084
59154
  site.siteUrl,
59085
- `/wp-json/wp/v2/pages/${page3.id}`,
59155
+ `/wp-json/wp/v2/pages/${page2.id}`,
59086
59156
  {
59087
59157
  method: "POST",
59088
59158
  body: JSON.stringify(body)
@@ -59099,9 +59169,9 @@ function encodeNoindexValue(key, value) {
59099
59169
  async function setSeoMeta(connection, slug, body, env) {
59100
59170
  const site = resolveEnvironment(connection, env);
59101
59171
  const plugins = await listActivePlugins(connection, site.env);
59102
- const page3 = await getPageBySlug(connection, slug, site.env);
59103
- const writeTargets = resolveSeoWriteTargets(page3.meta, plugins);
59104
- if (writeTargets.length === 0 || !page3.meta) {
59172
+ const page2 = await getPageBySlug(connection, slug, site.env);
59173
+ const writeTargets = resolveSeoWriteTargets(page2.meta, plugins);
59174
+ if (writeTargets.length === 0 || !page2.meta) {
59105
59175
  throw new WordpressApiError("UNSUPPORTED", "This WordPress site does not expose writable SEO meta fields through REST. Update the meta manually in WordPress.", 400);
59106
59176
  }
59107
59177
  const patch = {};
@@ -59118,12 +59188,12 @@ async function setSeoMeta(connection, slug, body, env) {
59118
59188
  await fetchJson(
59119
59189
  connection,
59120
59190
  site.siteUrl,
59121
- `/wp-json/wp/v2/pages/${page3.id}`,
59191
+ `/wp-json/wp/v2/pages/${page2.id}`,
59122
59192
  {
59123
59193
  method: "POST",
59124
59194
  body: JSON.stringify({
59125
59195
  meta: {
59126
- ...page3.meta ?? {},
59196
+ ...page2.meta ?? {},
59127
59197
  ...patch
59128
59198
  }
59129
59199
  })
@@ -59171,7 +59241,7 @@ async function bulkSetSeoMeta(connection, entries, env) {
59171
59241
  3,
59172
59242
  async (entry) => {
59173
59243
  try {
59174
- const page3 = await getPageBySlug(connection, entry.slug, site.env);
59244
+ const page2 = await getPageBySlug(connection, entry.slug, site.env);
59175
59245
  if (strategy === "manual") {
59176
59246
  return {
59177
59247
  slug: entry.slug,
@@ -59179,20 +59249,20 @@ async function bulkSetSeoMeta(connection, entries, env) {
59179
59249
  manualAssist: buildManualMetaAssist(
59180
59250
  site.siteUrl,
59181
59251
  entry.slug,
59182
- page3.link,
59252
+ page2.link,
59183
59253
  entry
59184
59254
  )
59185
59255
  };
59186
59256
  }
59187
- const writeTargets = resolveSeoWriteTargets(page3.meta, plugins);
59188
- if (writeTargets.length === 0 || !page3.meta) {
59257
+ const writeTargets = resolveSeoWriteTargets(page2.meta, plugins);
59258
+ if (writeTargets.length === 0 || !page2.meta) {
59189
59259
  return {
59190
59260
  slug: entry.slug,
59191
59261
  status: "manual",
59192
59262
  manualAssist: buildManualMetaAssist(
59193
59263
  site.siteUrl,
59194
59264
  entry.slug,
59195
- page3.link,
59265
+ page2.link,
59196
59266
  entry
59197
59267
  )
59198
59268
  };
@@ -59212,7 +59282,7 @@ async function bulkSetSeoMeta(connection, entries, env) {
59212
59282
  manualAssist: buildManualMetaAssist(
59213
59283
  site.siteUrl,
59214
59284
  entry.slug,
59215
- page3.link,
59285
+ page2.link,
59216
59286
  entry
59217
59287
  )
59218
59288
  };
@@ -59220,11 +59290,11 @@ async function bulkSetSeoMeta(connection, entries, env) {
59220
59290
  await fetchJson(
59221
59291
  connection,
59222
59292
  site.siteUrl,
59223
- `/wp-json/wp/v2/pages/${page3.id}`,
59293
+ `/wp-json/wp/v2/pages/${page2.id}`,
59224
59294
  {
59225
59295
  method: "POST",
59226
59296
  body: JSON.stringify({
59227
- meta: { ...page3.meta ?? {}, ...patch }
59297
+ meta: { ...page2.meta ?? {}, ...patch }
59228
59298
  })
59229
59299
  }
59230
59300
  );
@@ -59264,20 +59334,20 @@ ${CANONRY_SCHEMA_END}`;
59264
59334
  return stripped + injection;
59265
59335
  }
59266
59336
  async function verifySchemaInjection(connection, slug, env) {
59267
- const page3 = await getPageBySlug(connection, slug, env);
59268
- const raw = page3.content?.raw ?? page3.content?.rendered ?? "";
59337
+ const page2 = await getPageBySlug(connection, slug, env);
59338
+ const raw = page2.content?.raw ?? page2.content?.rendered ?? "";
59269
59339
  return raw.includes(CANONRY_SCHEMA_START);
59270
59340
  }
59271
59341
  async function deploySchema(connection, slug, schemas, env) {
59272
59342
  const site = resolveEnvironment(connection, env);
59273
59343
  try {
59274
- const page3 = await getPageBySlug(connection, slug, site.env);
59275
- const currentContent = page3.content?.raw ?? page3.content?.rendered ?? "";
59344
+ const page2 = await getPageBySlug(connection, slug, site.env);
59345
+ const currentContent = page2.content?.raw ?? page2.content?.rendered ?? "";
59276
59346
  const updatedContent = injectCanonrySchema(currentContent, schemas);
59277
59347
  await fetchJson(
59278
59348
  connection,
59279
59349
  site.siteUrl,
59280
- `/wp-json/wp/v2/pages/${page3.id}`,
59350
+ `/wp-json/wp/v2/pages/${page2.id}`,
59281
59351
  {
59282
59352
  method: "POST",
59283
59353
  body: JSON.stringify({ content: updatedContent })
@@ -59291,7 +59361,7 @@ async function deploySchema(connection, slug, schemas, env) {
59291
59361
  schemasInjected: schemas.map((s) => typeof s["@type"] === "string" ? s["@type"] : "Unknown"),
59292
59362
  manualAssist: {
59293
59363
  manualRequired: true,
59294
- targetUrl: page3.link ?? `${site.siteUrl}/${slug}`,
59364
+ targetUrl: page2.link ?? `${site.siteUrl}/${slug}`,
59295
59365
  adminUrl: `${site.siteUrl}/wp-admin/`,
59296
59366
  content: schemas.map((s) => JSON.stringify(s, null, 2)).join("\n\n"),
59297
59367
  nextSteps: [
@@ -59346,12 +59416,12 @@ async function getSchemaStatus(connection, env) {
59346
59416
  const details = await mapWithConcurrency2(
59347
59417
  pages,
59348
59418
  5,
59349
- async (page3) => getPageDetail(connection, page3.slug, site.env)
59419
+ async (page2) => getPageDetail(connection, page2.slug, site.env)
59350
59420
  );
59351
- const statusPages = details.map((page3) => {
59352
- const rawContent = page3.content;
59421
+ const statusPages = details.map((page2) => {
59422
+ const rawContent = page2.content;
59353
59423
  const hasCanonryMarker = rawContent.includes(CANONRY_SCHEMA_START);
59354
- const allSchemaTypes = page3.schemaBlocks.map((b) => b.type);
59424
+ const allSchemaTypes = page2.schemaBlocks.map((b) => b.type);
59355
59425
  const canonrySchemas = [];
59356
59426
  const thirdPartySchemas = [];
59357
59427
  if (hasCanonryMarker) {
@@ -59384,8 +59454,8 @@ async function getSchemaStatus(connection, env) {
59384
59454
  }
59385
59455
  }
59386
59456
  return {
59387
- slug: page3.slug,
59388
- title: page3.title,
59457
+ slug: page2.slug,
59458
+ title: page2.title,
59389
59459
  canonrySchemas,
59390
59460
  thirdPartySchemas,
59391
59461
  hasCanonrySchema: hasCanonryMarker
@@ -59427,14 +59497,14 @@ async function getPageSchema(connection, slug, env) {
59427
59497
  }
59428
59498
  async function buildManualSchemaUpdate(connection, slug, body, env) {
59429
59499
  const site = resolveEnvironment(connection, env);
59430
- const page3 = await getPageBySlug(connection, slug, site.env);
59500
+ const page2 = await getPageBySlug(connection, slug, site.env);
59431
59501
  return {
59432
59502
  manualRequired: true,
59433
- targetUrl: page3.link ?? `${site.siteUrl}/${slug}`,
59503
+ targetUrl: page2.link ?? `${site.siteUrl}/${slug}`,
59434
59504
  adminUrl: `${site.siteUrl}/wp-admin/`,
59435
59505
  content: body.json,
59436
59506
  nextSteps: [
59437
- `Open the WordPress editor or theme/plugin settings for ${page3.slug}.`,
59507
+ `Open the WordPress editor or theme/plugin settings for ${page2.slug}.`,
59438
59508
  `Add the ${body.type ?? "custom"} JSON-LD block to the page or the site-level schema tool you use.`,
59439
59509
  "Publish/update the page and refresh the public URL to confirm the JSON-LD block is rendered."
59440
59510
  ]
@@ -59447,33 +59517,33 @@ async function runAudit(connection, env) {
59447
59517
  const details = await mapWithConcurrency2(
59448
59518
  pages,
59449
59519
  5,
59450
- async (page3) => getPageDetail(connection, page3.slug, site.env, plugins)
59520
+ async (page2) => getPageDetail(connection, page2.slug, site.env, plugins)
59451
59521
  );
59452
59522
  const auditPages = [];
59453
59523
  const allIssues = [];
59454
- for (const page3 of details) {
59524
+ for (const page2 of details) {
59455
59525
  const issues = [];
59456
- const wordCount = computeWordCount(page3.content);
59457
- const schemaPresent = page3.schemaBlocks.length > 0;
59458
- if (page3.status === "publish" && page3.seo.noindex === true) {
59526
+ const wordCount = computeWordCount(page2.content);
59527
+ const schemaPresent = page2.schemaBlocks.length > 0;
59528
+ if (page2.status === "publish" && page2.seo.noindex === true) {
59459
59529
  issues.push({
59460
- slug: page3.slug,
59530
+ slug: page2.slug,
59461
59531
  severity: "high",
59462
59532
  code: "noindex",
59463
59533
  message: "Published page is marked noindex."
59464
59534
  });
59465
59535
  }
59466
- if (!page3.seo.title) {
59536
+ if (!page2.seo.title) {
59467
59537
  issues.push({
59468
- slug: page3.slug,
59538
+ slug: page2.slug,
59469
59539
  severity: "medium",
59470
59540
  code: "missing-seo-title",
59471
59541
  message: "Rendered page title is missing."
59472
59542
  });
59473
59543
  }
59474
- if (!page3.seo.description) {
59544
+ if (!page2.seo.description) {
59475
59545
  issues.push({
59476
- slug: page3.slug,
59546
+ slug: page2.slug,
59477
59547
  severity: "medium",
59478
59548
  code: "missing-meta-description",
59479
59549
  message: "Rendered meta description is missing."
@@ -59481,7 +59551,7 @@ async function runAudit(connection, env) {
59481
59551
  }
59482
59552
  if (!schemaPresent) {
59483
59553
  issues.push({
59484
- slug: page3.slug,
59554
+ slug: page2.slug,
59485
59555
  severity: "medium",
59486
59556
  code: "missing-schema",
59487
59557
  message: "No JSON-LD schema was detected on the rendered page."
@@ -59489,18 +59559,18 @@ async function runAudit(connection, env) {
59489
59559
  }
59490
59560
  if (wordCount < THIN_CONTENT_WORD_COUNT) {
59491
59561
  issues.push({
59492
- slug: page3.slug,
59562
+ slug: page2.slug,
59493
59563
  severity: "low",
59494
59564
  code: "thin-content",
59495
59565
  message: `Page content is thin (${wordCount} words; target at least ${THIN_CONTENT_WORD_COUNT}).`
59496
59566
  });
59497
59567
  }
59498
59568
  auditPages.push({
59499
- slug: page3.slug,
59500
- title: page3.title,
59501
- status: page3.status,
59569
+ slug: page2.slug,
59570
+ title: page2.title,
59571
+ status: page2.status,
59502
59572
  wordCount,
59503
- seo: page3.seo,
59573
+ seo: page2.seo,
59504
59574
  schemaPresent,
59505
59575
  issues
59506
59576
  });
@@ -60085,19 +60155,19 @@ async function wordpressRoutes(app, opts) {
60085
60155
  for (const issue of auditIssues) {
60086
60156
  if (issue.code === "missing-meta-description" || issue.code === "missing-seo-title") {
60087
60157
  const existing = metaEntries.find((e) => e.slug === issue.slug);
60088
- const page3 = auditPages.find((p) => p.slug === issue.slug);
60158
+ const page2 = auditPages.find((p) => p.slug === issue.slug);
60089
60159
  if (!existing) {
60090
60160
  metaEntries.push({
60091
60161
  slug: issue.slug,
60092
- title: issue.code === "missing-seo-title" ? page3?.title ?? issue.slug : void 0,
60093
- description: issue.code === "missing-meta-description" ? page3?.title ?? issue.slug : void 0
60162
+ title: issue.code === "missing-seo-title" ? page2?.title ?? issue.slug : void 0,
60163
+ description: issue.code === "missing-meta-description" ? page2?.title ?? issue.slug : void 0
60094
60164
  });
60095
60165
  } else {
60096
60166
  if (issue.code === "missing-seo-title" && !existing.title) {
60097
- existing.title = page3?.title ?? issue.slug;
60167
+ existing.title = page2?.title ?? issue.slug;
60098
60168
  }
60099
60169
  if (issue.code === "missing-meta-description" && !existing.description) {
60100
- existing.description = page3?.title ?? issue.slug;
60170
+ existing.description = page2?.title ?? issue.slug;
60101
60171
  }
60102
60172
  }
60103
60173
  }
@@ -61310,7 +61380,7 @@ async function listCloudRunTrafficEvents(accessToken, options) {
61310
61380
  let skippedEntryCount = 0;
61311
61381
  const events = [];
61312
61382
  const orderBy = options.orderBy ?? (options.firstSync ? "timestamp desc" : "timestamp asc");
61313
- for (let page3 = 0; page3 < maxPages; page3 += 1) {
61383
+ for (let page2 = 0; page2 < maxPages; page2 += 1) {
61314
61384
  const requestBody = {
61315
61385
  resourceNames: [`projects/${options.gcpProjectId}`],
61316
61386
  filter,
@@ -61551,7 +61621,7 @@ async function listWordpressTrafficEvents(options) {
61551
61621
  let hasMore = false;
61552
61622
  const events = [];
61553
61623
  const fetchImpl = options.fetchImpl ?? fetch;
61554
- for (let page3 = 0; page3 < maxPages; page3 += 1) {
61624
+ for (let page2 = 0; page2 < maxPages; page2 += 1) {
61555
61625
  const url = new URL(endpoint);
61556
61626
  url.searchParams.set("limit", String(pageSize));
61557
61627
  if (cursor !== void 0 && cursor !== "") {
@@ -61789,12 +61859,12 @@ async function listVercelTrafficEvents(options) {
61789
61859
  let skippedEntryCount = 0;
61790
61860
  let hasMore = false;
61791
61861
  const events = [];
61792
- for (let page3 = 0; page3 < maxPages; page3 += 1) {
61862
+ for (let page2 = 0; page2 < maxPages; page2 += 1) {
61793
61863
  const url = new URL(VERCEL_REQUEST_LOGS_URL);
61794
61864
  url.searchParams.set("projectId", projectId);
61795
61865
  url.searchParams.set("ownerId", teamId);
61796
61866
  url.searchParams.set("teamId", teamId);
61797
- url.searchParams.set("page", String(page3));
61867
+ url.searchParams.set("page", String(page2));
61798
61868
  url.searchParams.set("startDate", startDate);
61799
61869
  url.searchParams.set("endDate", endDate);
61800
61870
  url.searchParams.set("environment", environment);
@@ -61926,9 +61996,9 @@ async function drainVercelTrafficEvents(options) {
61926
61996
  const subSpanMs = subEndMs - cursorMs;
61927
61997
  const useFloorPageBudget = subSpanMs <= MIN_SUB_WINDOW_MS && floorPageBudgetCountdown > 0;
61928
61998
  const pageBudget = useFloorPageBudget ? FLOOR_SLICE_MAX_PAGES : options.pagesPerSubWindow;
61929
- let page3;
61999
+ let page2;
61930
62000
  try {
61931
- page3 = await options.pull({
62001
+ page2 = await options.pull({
61932
62002
  token: options.token,
61933
62003
  projectId: options.projectId,
61934
62004
  teamId: options.teamId,
@@ -61950,7 +62020,7 @@ async function drainVercelTrafficEvents(options) {
61950
62020
  throw error;
61951
62021
  }
61952
62022
  subWindowCount += 1;
61953
- if (page3.hasMore) {
62023
+ if (page2.hasMore) {
61954
62024
  if (subSpanMs > MIN_SUB_WINDOW_MS) {
61955
62025
  spanMs = Math.max(Math.floor(subSpanMs / 2), MIN_SUB_WINDOW_MS);
61956
62026
  if (spanMs === MIN_SUB_WINDOW_MS) {
@@ -61959,7 +62029,7 @@ async function drainVercelTrafficEvents(options) {
61959
62029
  continue;
61960
62030
  }
61961
62031
  if (pageBudget < FLOOR_SLICE_MAX_PAGES) {
61962
- page3 = await options.pull({
62032
+ page2 = await options.pull({
61963
62033
  token: options.token,
61964
62034
  projectId: options.projectId,
61965
62035
  teamId: options.teamId,
@@ -61972,7 +62042,7 @@ async function drainVercelTrafficEvents(options) {
61972
62042
  floorSpanProbeCountdown = FLOOR_CONGESTION_PROBE_INTERVAL;
61973
62043
  floorPageBudgetCountdown = FLOOR_CONGESTION_PROBE_INTERVAL;
61974
62044
  }
61975
- if (page3.hasMore) {
62045
+ if (page2.hasMore) {
61976
62046
  if (options.abortOnTruncation) {
61977
62047
  throw new Error(
61978
62048
  `Vercel ${MIN_SUB_WINDOW_MS / 1e3}-second slice starting ${new Date(cursorMs).toISOString()} holds more than ${FLOOR_SLICE_MAX_PAGES} pages and cannot be drained further`
@@ -61982,7 +62052,7 @@ async function drainVercelTrafficEvents(options) {
61982
62052
  truncatedSliceStartsMs.push(cursorMs);
61983
62053
  }
61984
62054
  }
61985
- for (const event of page3.events) {
62055
+ for (const event of page2.events) {
61986
62056
  if (!seenEventIds.has(event.eventId)) {
61987
62057
  seenEventIds.add(event.eventId);
61988
62058
  events.push(event);
@@ -64965,7 +65035,7 @@ async function trafficRoutes(app, opts) {
64965
65035
  }
64966
65036
  const isFirstSync = !sourceRow2.lastSyncedAt;
64967
65037
  try {
64968
- const page3 = await pullEvents(accessToken, {
65038
+ const page2 = await pullEvents(accessToken, {
64969
65039
  gcpProjectId,
64970
65040
  serviceName,
64971
65041
  location,
@@ -64976,7 +65046,7 @@ async function trafficRoutes(app, opts) {
64976
65046
  firstSync: isFirstSync,
64977
65047
  requestUrlSubstrings: [project.canonicalDomain]
64978
65048
  });
64979
- allEvents = page3.events;
65049
+ allEvents = page2.events;
64980
65050
  } catch (e) {
64981
65051
  const msg = describeError(e);
64982
65052
  markFailed(msg, "PROVIDER_PULL");
@@ -65055,7 +65125,7 @@ async function trafficRoutes(app, opts) {
65055
65125
  const collected = [];
65056
65126
  let cursor = sourceRow2.lastCursor ?? void 0;
65057
65127
  try {
65058
- for (let page3 = 0; page3 < wpMaxPages; page3 += 1) {
65128
+ for (let page2 = 0; page2 < wpMaxPages; page2 += 1) {
65059
65129
  renewWordpressLease();
65060
65130
  const pageResult = await pullWordpressEvents({
65061
65131
  baseUrl: credential.baseUrl,
@@ -65599,7 +65669,7 @@ async function trafficRoutes(app, opts) {
65599
65669
  pullErrorPrefix = "Cloud Run pull failed";
65600
65670
  pullForBackfill = async () => {
65601
65671
  const accessToken = await resolveAccessToken2(credential);
65602
- const page3 = await pullEvents(accessToken, {
65672
+ const page2 = await pullEvents(accessToken, {
65603
65673
  gcpProjectId,
65604
65674
  serviceName,
65605
65675
  location,
@@ -65615,7 +65685,7 @@ async function trafficRoutes(app, opts) {
65615
65685
  orderBy: "timestamp asc",
65616
65686
  requestUrlSubstrings: [project.canonicalDomain]
65617
65687
  });
65618
- return page3.events;
65688
+ return page2.events;
65619
65689
  };
65620
65690
  } else {
65621
65691
  const credentialStore = opts.vercelTrafficCredentialStore;
@@ -71982,12 +72052,12 @@ function readSiteAuditIssues(input) {
71982
72052
  snapshot.detailsAvailable,
71983
72053
  issueId ?? null
71984
72054
  ])).update(crossJson);
71985
- const addMember = (group, page3) => {
72055
+ const addMember = (group, page2) => {
71986
72056
  group.affectedPages += 1;
71987
- if (group.examples.length < 3) group.examples.push(page3);
72057
+ if (group.examples.length < 3) group.examples.push(page2);
71988
72058
  else group.examplesTruncated = true;
71989
72059
  if (group.id === issueId) {
71990
- if (selectedMembers >= offset && members.length < limit + 1) members.push(page3);
72060
+ if (selectedMembers >= offset && members.length < limit + 1) members.push(page2);
71991
72061
  selectedMembers += 1;
71992
72062
  }
71993
72063
  };
@@ -72040,12 +72110,12 @@ function readSiteAuditIssues(input) {
72040
72110
  auditScore: siteCrawlPages.auditScore,
72041
72111
  auditFields: siteCrawlPages.auditFields
72042
72112
  }).from(siteCrawlPages).where(and82(scope, after === void 0 ? void 0 : gt10(siteCrawlPages.nodeKey, after))).orderBy(asc21(siteCrawlPages.nodeKey)).limit(200).all();
72043
- for (const page3 of pages) {
72044
- evidenceHash.update(JSON.stringify(page3));
72045
- const fields = record(page3.auditFields);
72046
- if (page3.auditScore !== null || fields?.schemaVersion === "1.0") coverage.auditedPages += 1;
72113
+ for (const page2 of pages) {
72114
+ evidenceHash.update(JSON.stringify(page2));
72115
+ const fields = record(page2.auditFields);
72116
+ if (page2.auditScore !== null || fields?.schemaVersion === "1.0") coverage.auditedPages += 1;
72047
72117
  if (fields?.schemaVersion !== "1.0") {
72048
- if (page3.auditScore !== null) coverage.pagesWithoutFindingEvidence += 1;
72118
+ if (page2.auditScore !== null) coverage.pagesWithoutFindingEvidence += 1;
72049
72119
  continue;
72050
72120
  }
72051
72121
  let pageComplete = Array.isArray(fields.factors) && Array.isArray(fields.criticalDefects);
@@ -72087,7 +72157,7 @@ function readSiteAuditIssues(input) {
72087
72157
  examples: [],
72088
72158
  examplesTruncated: false
72089
72159
  });
72090
- addMember(group, pageEvidence(page3.nodeKey, page3.url, finding2.message, factor));
72160
+ addMember(group, pageEvidence(page2.nodeKey, page2.url, finding2.message, factor));
72091
72161
  }
72092
72162
  }
72093
72163
  for (const rawDefect of Array.isArray(fields.criticalDefects) ? fields.criticalDefects : []) {
@@ -72117,7 +72187,7 @@ function readSiteAuditIssues(input) {
72117
72187
  });
72118
72188
  if (defect.severity === "critical") group.severity = "critical";
72119
72189
  addMember(group, {
72120
- ...pageEvidence(page3.nodeKey, page3.url, defect.detail),
72190
+ ...pageEvidence(page2.nodeKey, page2.url, defect.detail),
72121
72191
  recommendations: [defect.recommendation.slice(0, 400)],
72122
72192
  recommendationsScope: "issue",
72123
72193
  recommendationsTruncated: defect.recommendation.length > 400
@@ -73804,14 +73874,14 @@ async function technicalAeoRoutes(app, opts) {
73804
73874
  };
73805
73875
  const scope = detailScopeFor(project.id, snapshot);
73806
73876
  if (!scope) return { state: "details-unavailable", ...provenance };
73807
- const page3 = pageInScope(scope, nodeKey ? { nodeKey } : { url });
73808
- if (!page3) return { state: "not-found", ...provenance };
73877
+ const page2 = pageInScope(scope, nodeKey ? { nodeKey } : { url });
73878
+ if (!page2) return { state: "not-found", ...provenance };
73809
73879
  const identity = {
73810
- nodeKey: page3.nodeKey,
73811
- url: page3.url,
73812
- auditState: page3.auditState
73880
+ nodeKey: page2.nodeKey,
73881
+ url: page2.url,
73882
+ auditState: page2.auditState
73813
73883
  };
73814
- if (page3.auditScore == null) {
73884
+ if (page2.auditScore == null) {
73815
73885
  return {
73816
73886
  state: "not-audited",
73817
73887
  ...provenance,
@@ -73825,8 +73895,8 @@ async function technicalAeoRoutes(app, opts) {
73825
73895
  state: "ready",
73826
73896
  ...provenance,
73827
73897
  ...identity,
73828
- auditScore: page3.auditScore,
73829
- ...mapCrawlPageAuditEvidence(page3)
73898
+ auditScore: page2.auditScore,
73899
+ ...mapCrawlPageAuditEvidence(page2)
73830
73900
  };
73831
73901
  });
73832
73902
  app.get("/projects/:name/technical-aeo/crawl/pages", async (request) => {
@@ -73995,9 +74065,9 @@ async function technicalAeoRoutes(app, opts) {
73995
74065
  const limit = parseBoundedLimit(request.query.limit, 50, 100);
73996
74066
  const offset = decodeCursor(request.query.cursor);
73997
74067
  const sortedChildren = [...children.entries()].sort(([left], [right]) => left.localeCompare(right));
73998
- const page3 = sortedChildren.slice(offset, offset + limit + 1);
73999
- const hasMore = page3.length > limit;
74000
- const visible = hasMore ? page3.slice(0, limit) : page3;
74068
+ const page2 = sortedChildren.slice(offset, offset + limit + 1);
74069
+ const hasMore = page2.length > limit;
74070
+ const visible = hasMore ? page2.slice(0, limit) : page2;
74001
74071
  return {
74002
74072
  project: project.name,
74003
74073
  hasCrawlData: true,