@canonry/canonry 4.153.0 → 4.154.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 (29) hide show
  1. package/assets/assets/{AuditHistoryPanel-CcoXjMxm.js → AuditHistoryPanel-WWT1n8jp.js} +1 -1
  2. package/assets/assets/{BacklinksPage-Cij_A80K.js → BacklinksPage-DIjUnsVL.js} +1 -1
  3. package/assets/assets/{HistoryPage-CdxhPEHF.js → HistoryPage-CrIBhGuW.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-CJaqCeMr.js → MeasurementPropertyPage-uN4BWigS.js} +1 -1
  5. package/assets/assets/ProjectPage-BR5ECwUP.js +12 -0
  6. package/assets/assets/{RunRow-xfuUNQ2O.js → RunRow-OE5tiuih.js} +1 -1
  7. package/assets/assets/{RunsPage-BirDqVe_.js → RunsPage-C6xBmjps.js} +1 -1
  8. package/assets/assets/{SettingsPage-BzMoD-42.js → SettingsPage-CUjjr8U0.js} +1 -1
  9. package/assets/assets/{TrafficPage-Du-7BL_j.js → TrafficPage-DrEfsHiE.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-Dk6-TuNw.js → TrafficSourceDetailPage-B3DX5BEk.js} +1 -1
  11. package/assets/assets/{arrow-left-HyrLx9li.js → arrow-left-CLmqvCqU.js} +1 -1
  12. package/assets/assets/{extract-error-message-Db0s9Qon.js → extract-error-message-BEBd1a5v.js} +1 -1
  13. package/assets/assets/{index-Bio0Z1kJ.js → index-CsM-Xxia.js} +55 -55
  14. package/assets/assets/{index-CaYn5phz.css → index-DyL54s8x.css} +1 -1
  15. package/assets/assets/{refresh-cw-BFnkrZE9.js → refresh-cw-DYSS2Wd-.js} +1 -1
  16. package/assets/assets/{trash-2-CQtsaJ-3.js → trash-2-VoiF_ckU.js} +1 -1
  17. package/assets/assets/v2-overview-adapter-COMP5UGt.js +1 -0
  18. package/assets/index.html +2 -2
  19. package/dist/{chunk-LNJF7LL5.js → chunk-ANSQ6JNO.js} +1 -1
  20. package/dist/{chunk-VTL2WOIO.js → chunk-IFZS2TXV.js} +110 -70
  21. package/dist/{chunk-SKTXCZZD.js → chunk-NNW7TJUZ.js} +6 -1
  22. package/dist/{chunk-YV4VKT4M.js → chunk-X5I7CAXH.js} +172 -77
  23. package/dist/cli.js +4 -4
  24. package/dist/index.js +4 -4
  25. package/dist/{intelligence-service-APPDGUH6.js → intelligence-service-B5AETQ5N.js} +2 -2
  26. package/dist/mcp.js +2 -2
  27. package/package.json +7 -7
  28. package/assets/assets/ProjectPage-BJPVmD_-.js +0 -12
  29. package/assets/assets/v2-overview-adapter-D81QVAhK.js +0 -1
@@ -1 +1 @@
1
- import{c as e}from"./index-Bio0Z1kJ.js";const o=[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]],a=e("chevron-left",o);const t=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],h=e("refresh-cw",t);export{a as C,h as R};
1
+ import{c as e}from"./index-CsM-Xxia.js";const o=[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]],a=e("chevron-left",o);const t=[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]],h=e("refresh-cw",t);export{a as C,h as R};
@@ -1 +1 @@
1
- import{c as a}from"./index-Bio0Z1kJ.js";const t=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],h=a("circle-question-mark",t);const e=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],d=a("download",e);const o=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],p=a("external-link",o);const c=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],s=a("trash-2",c);export{h as C,d as D,p as E,s as T};
1
+ import{c as a}from"./index-CsM-Xxia.js";const t=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]],h=a("circle-question-mark",t);const e=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],d=a("download",e);const o=[["path",{d:"M15 3h6v6",key:"1q9fwt"}],["path",{d:"M10 14 21 3",key:"gplh6r"}],["path",{d:"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",key:"a6xqqp"}]],p=a("external-link",o);const c=[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]],s=a("trash-2",c);export{h as C,d as D,p as E,s as T};
@@ -0,0 +1 @@
1
+ function R(e){const r=e[0];return r?e.every(a=>a.mode===r.mode&&a.scope.kind===r.scope.kind&&a.scope.key===r.scope.key&&a.queryClass===r.queryClass&&a.measurement.displayedRunId===r.measurement.displayedRunId):!0}function u(e){return e.state==="unavailable"?{numerator:null,denominator:null,reason:e.reason}:e.numerator===void 0||e.denominator===void 0||e.denominator<=0?{numerator:null,denominator:null,reason:"not_applicable"}:{numerator:e.numerator,denominator:e.denominator}}function K(e){return e.kind==="exact"?e.url:e.kind==="prefix"?`https://${e.host}${e.pathPrefix}/*`:`https://${e.host}/*`}function $(e){return e==="assigned"?"this-property":e==="sibling"?"another-property":e==="ownedUnmapped"?"owned-unassigned":e==="external"?"external":e==="ambiguous"?"multiple-properties":"invalid-url"}function I(e){return e==="assigned"?"positive":e==="external"?"neutral":e==="invalid"?"negative":"caution"}function C(e){return`target:${e.targetKey}:${e.queryId}:${e.executionNodeKey}`}function T(e,r,a){const l=new Map;if(!r)return l;const m=new Map(e.usageEdges.map(n=>[C(n),n.targetKey])),i=new Map(e.assignments.map(n=>[`${n.targetKey}:${n.queryId}`,n.queryClass])),g=new Map(e.usageEdges.map(n=>[C(n),i.get(`${n.targetKey}:${n.queryId}`)]));for(const n of r.evidence){if(a&&g.get(n.usageEdgeId)!==a)continue;const s=m.get(n.usageEdgeId);if(!s)continue;const d=l.get(s)??[];d.push({id:`${n.observationId}:${n.expectedSlotId}:${n.usageEdgeId}:${n.sourceUrl}`,kind:$(n.classification),query:n.queryText,provider:n.provider,location:n.location,url:n.sourceUrl,tone:I(n.classification),historical:n.historical||n.bridged}),l.set(s,d)}return l}function M(e){if(e.flags>0)return{label:"Review",tone:"caution"};const r=[e.mentionCoverage,e.citationCoverage].filter(a=>a.state==="unavailable").map(a=>a.reason);return r.includes("no_completed_run")?{label:"Not measured",tone:"neutral"}:r.includes("plan_v1")?{label:"Update setup",tone:"caution"}:r.includes("evidence_incomplete")?{label:"Evidence incomplete",tone:"caution"}:r.includes("no_population")?{label:"No queries",tone:"neutral"}:r.includes("not_applicable")?{label:"Not applicable",tone:"neutral"}:{label:"Complete",tone:"positive"}}function S(e){return e==="complete"?{label:"Complete",tone:"positive"}:e==="partial"?{label:"Partial",tone:"caution"}:e==="queued"?{label:"Queued",tone:"neutral"}:e==="running"?{label:"Running",tone:"neutral"}:e==="failed"?{label:"Failed",tone:"negative"}:{label:"Not measured",tone:"neutral"}}function E(e){if(!e)return"";const r=new Date(e);return Number.isFinite(r.getTime())?r.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric",timeZone:"UTC"}):""}function x(e){const r=e.nextAction.count??e.flags.total;if(e.nextAction.kind==="review_flags")return`${r} flagged ${r===1?"result needs":"results need"} review.`;if(e.nextAction.kind==="complete_setup")return"Finish setup.";if(e.nextAction.kind==="republish_setup")return"Setup update required.";if(e.nextAction.kind==="run_measurement")return"Ready to measure."}function _({overview:e,activePlan:r,report:a,reportState:l=a?"ready":"loading",sort:m}){if(r.plan.schemaVersion!==2||e.mode!=="active-v2")throw new Error("The current overview requires an active version-two setup.");if(e.scope.kind!=="all"&&e.scope.kind!=="group")throw new Error("The current overview requires an All Properties or group scope.");const i=r.plan,g=new Map(i.targets.map(t=>[t.stableKey,t])),n=new Map(i.querySnapshots.map(t=>[t.queryId,t.queryText])),s=new Map;for(const t of i.assignments){if(e.queryClass!=="all"&&t.queryClass!==e.queryClass)continue;const o=n.get(t.queryId);if(!o)continue;const c=s.get(t.targetKey)??new Set;c.add(o),s.set(t.targetKey,c)}const d=a?.revision===r.revision&&a.run?.id===e.measurement.displayedRunId?a:null,h=e.measurement.displayedRunId===void 0||d?"ready":l==="error"||a!=null?"error":"loading",q=T(i,d,e.queryClass==="all"?void 0:e.queryClass),f=new Map;for(const t of i.groups)for(const o of t.targetKeys)f.has(o)||f.set(o,t.label);const y=e.properties.items.map(t=>{const o=g.get(t.targetKey),c=q.get(t.targetKey)??[];return{id:t.targetKey,name:t.label,mentionCoverage:u(t.mentionCoverage),citationCoverage:u(t.citationCoverage),status:M(t),providers:t.providers?.map(p=>({provider:p.provider,mentionCoverage:u(p.mentionCoverage),citationCoverage:u(p.citationCoverage)})),market:f.get(t.targetKey),assignedQueries:[...s.get(t.targetKey)??[]],urls:o?.urlMatchers.map(K)??[],evidence:c,evidenceState:h,historical:c.some(p=>p.historical)}}),b=e.namedShareOfVoice?.entries.map(t=>({name:t.label,coverage:{numerator:t.credits,denominator:e.namedShareOfVoice.denominator}})),k=e.properties.items.flatMap(t=>t.flags>0?[{id:`property:${t.targetKey}`,property:t.label,summary:`${t.flags} ${t.flags===1?"result needs":"results need"} review.`,tone:"caution",count:t.flags}]:[]);return{classReporting:"available",latestMeasurement:{status:S(e.measurement.state),completedSlots:e.measurement.completed,totalSlots:e.measurement.expected,date:E(e.measurement.completedAt),includesBridgedHistory:e.measurement.includesHistoricalData===!0},currentView:{scope:e.scope.kind==="group"?{kind:"group",...e.scope.key?{key:e.scope.key}:{}}:{kind:"all"},queryClass:e.queryClass,aggregate:{metrics:{propertiesMentioned:u(e.metrics.propertiesMentioned),mentionCoverage:u(e.metrics.mentionCoverage),citationCoverage:u(e.metrics.citationCoverage)},properties:y,...b?{shareOfVoice:b}:{}},propertyTotal:e.properties.totalEstimate??y.length,outcomes:e.outcomes,nextCursor:e.properties.nextCursor,...m?{sort:m}:{}},availableGroups:i.groups.map(t=>({id:t.stableKey,label:t.label})),...x(e)?{nextActionText:x(e)}:{},flaggedResultsTotal:e.flags.total,flaggedResults:k}}export{R as a,_ as b,K as m};
package/assets/index.html CHANGED
@@ -12,12 +12,12 @@
12
12
  <link rel="icon" type="image/png" sizes="32x32" href="./favicon-32.png" />
13
13
  <link rel="apple-touch-icon" href="./apple-touch-icon.png" />
14
14
  <title>Canonry</title>
15
- <script type="module" crossorigin src="./assets/index-Bio0Z1kJ.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-CsM-Xxia.js"></script>
16
16
  <link rel="modulepreload" crossorigin href="./assets/vendor-tanstack-BjS7iY3g.js">
17
17
  <link rel="modulepreload" crossorigin href="./assets/vendor-radix-DnzzbllM.js">
18
18
  <link rel="modulepreload" crossorigin href="./assets/vendor-recharts-33ACLxsD.js">
19
19
  <link rel="modulepreload" crossorigin href="./assets/vendor-markdown-CpDJch4G.js">
20
- <link rel="stylesheet" crossorigin href="./assets/index-CaYn5phz.css">
20
+ <link rel="stylesheet" crossorigin href="./assets/index-DyL54s8x.css">
21
21
  </head>
22
22
  <body>
23
23
  <div id="root"></div>
@@ -81,7 +81,7 @@ import {
81
81
  trafficConnectWordpressRequestSchema,
82
82
  trafficEventKindSchema,
83
83
  trafficSeriesGranularitySchema
84
- } from "./chunk-SKTXCZZD.js";
84
+ } from "./chunk-NNW7TJUZ.js";
85
85
 
86
86
  // src/config.ts
87
87
  import fs from "fs";
@@ -521,7 +521,7 @@ import {
521
521
  wordpressSchemaDeployResultDtoSchema,
522
522
  wordpressSchemaStatusResultDtoSchema,
523
523
  wordpressStatusDtoSchema
524
- } from "./chunk-SKTXCZZD.js";
524
+ } from "./chunk-NNW7TJUZ.js";
525
525
 
526
526
  // src/intelligence-service.ts
527
527
  import { eq as eq57, desc as desc26, asc as asc11, and as and45, ne as ne7, or as or11, inArray as inArray19, gte as gte13, lte as lte10 } from "drizzle-orm";
@@ -35501,8 +35501,9 @@ function isSitemapOwnedByProperty(sitemapUrl, propertyId, canonicalDomain) {
35501
35501
  function signState(payload, secret) {
35502
35502
  return crypto29.createHmac("sha256", secret).update(payload).digest("hex");
35503
35503
  }
35504
+ var OAUTH_STATE_MAX_AGE_MS = 15 * 60 * 1e3;
35504
35505
  function buildSignedState(data, secret) {
35505
- const payload = JSON.stringify(data);
35506
+ const payload = JSON.stringify({ ...data, issuedAt: Date.now() });
35506
35507
  const sig = signState(payload, secret);
35507
35508
  return Buffer.from(JSON.stringify({ payload, sig })).toString("base64url");
35508
35509
  }
@@ -35511,7 +35512,10 @@ function verifySignedState(encoded, secret) {
35511
35512
  const { payload, sig } = JSON.parse(Buffer.from(encoded, "base64url").toString());
35512
35513
  const expected = signState(payload, secret);
35513
35514
  if (!crypto29.timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"))) return null;
35514
- return JSON.parse(payload);
35515
+ const parsed = JSON.parse(payload);
35516
+ const issuedAt = typeof parsed.issuedAt === "number" ? parsed.issuedAt : null;
35517
+ if (issuedAt === null || Date.now() - issuedAt > OAUTH_STATE_MAX_AGE_MS) return null;
35518
+ return parsed;
35515
35519
  } catch {
35516
35520
  return null;
35517
35521
  }
@@ -41797,9 +41801,12 @@ async function adsRoutes(app, opts) {
41797
41801
  }
41798
41802
 
41799
41803
  // ../api-routes/src/bing.ts
41800
- import crypto33 from "crypto";
41804
+ import crypto34 from "crypto";
41801
41805
  import { eq as eq42, and as and33, desc as desc19 } from "drizzle-orm";
41802
41806
 
41807
+ // ../integration-bing/src/bing-client.ts
41808
+ import crypto33 from "crypto";
41809
+
41803
41810
  // ../integration-bing/src/constants.ts
41804
41811
  var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
41805
41812
  var BING_SUBMIT_URL_BATCH_LIMIT = 500;
@@ -41808,6 +41815,7 @@ var BING_REQUEST_TIMEOUT_MS = 3e4;
41808
41815
  var BING_MAX_RETRIES = 4;
41809
41816
  var BING_RETRY_BASE_DELAY_MS = 2e3;
41810
41817
  var BING_RETRY_MAX_DELAY_MS = 3e4;
41818
+ var BING_THROTTLE_COOLDOWN_MS = 10 * 60 * 1e3;
41811
41819
 
41812
41820
  // ../integration-bing/src/types.ts
41813
41821
  var BING_THROTTLE_ERROR_CODES = /* @__PURE__ */ new Set([4, 5]);
@@ -41947,26 +41955,58 @@ async function bingFetchOnce(apiKey, endpoint, opts) {
41947
41955
  throw new BingApiError("Bing API returned invalid JSON", 502);
41948
41956
  }
41949
41957
  }
41958
+ var throttleCooldownUntil = /* @__PURE__ */ new Map();
41959
+ var cooldownNow = () => Date.now();
41960
+ function cooldownKey(apiKey) {
41961
+ return crypto33.createHash("sha256").update(apiKey).digest("hex").slice(0, 12);
41962
+ }
41963
+ function throttleCooldownRemainingMs(apiKey) {
41964
+ const until = throttleCooldownUntil.get(cooldownKey(apiKey));
41965
+ if (until == null) return 0;
41966
+ const remaining = until - cooldownNow();
41967
+ if (remaining <= 0) {
41968
+ throttleCooldownUntil.delete(cooldownKey(apiKey));
41969
+ return 0;
41970
+ }
41971
+ return remaining;
41972
+ }
41973
+ function openThrottleCooldown(apiKey) {
41974
+ throttleCooldownUntil.set(cooldownKey(apiKey), cooldownNow() + BING_THROTTLE_COOLDOWN_MS);
41975
+ bingClientLog("error", "http.cooldown-open", { cooldownMs: BING_THROTTLE_COOLDOWN_MS });
41976
+ }
41950
41977
  async function bingFetch(apiKey, endpoint, opts) {
41951
41978
  const isIdempotent = (opts?.method ?? "GET") === "GET";
41952
- return withRetry(() => bingFetchOnce(apiKey, endpoint, opts), {
41953
- maxRetries: BING_MAX_RETRIES,
41954
- baseDelayMs: BING_RETRY_BASE_DELAY_MS,
41955
- maxDelayMs: BING_RETRY_MAX_DELAY_MS,
41956
- isRetryable: (err) => {
41957
- if (err instanceof BingApiError && err.isThrottle) return true;
41958
- return isIdempotent && isRetryableHttpError(err);
41959
- },
41960
- computeDelayMs: (_attempt, err, defaultMs) => retryAfterDelayMs(err) ?? defaultMs,
41961
- onRetry: ({ attempt, err, delayMs }) => {
41962
- bingClientLog("warn", "http.retry", {
41963
- endpoint,
41964
- attempt,
41965
- delayMs: Math.round(delayMs),
41966
- throttled: err instanceof BingApiError ? err.isThrottle : false
41967
- });
41968
- }
41969
- });
41979
+ const cooling = throttleCooldownRemainingMs(apiKey);
41980
+ if (cooling > 0) {
41981
+ bingClientLog("warn", "http.cooldown-skip", { endpoint, remainingMs: cooling });
41982
+ throw new BingApiError(
41983
+ `Bing API key is in a throttle cooldown for another ${Math.ceil(cooling / 1e3)}s; the account was still throttled after a full retry budget, so this call was not attempted.`,
41984
+ 429
41985
+ );
41986
+ }
41987
+ try {
41988
+ return await withRetry(() => bingFetchOnce(apiKey, endpoint, opts), {
41989
+ maxRetries: BING_MAX_RETRIES,
41990
+ baseDelayMs: BING_RETRY_BASE_DELAY_MS,
41991
+ maxDelayMs: BING_RETRY_MAX_DELAY_MS,
41992
+ isRetryable: (err) => {
41993
+ if (err instanceof BingApiError && err.isThrottle) return true;
41994
+ return isIdempotent && isRetryableHttpError(err);
41995
+ },
41996
+ computeDelayMs: (_attempt, err, defaultMs) => retryAfterDelayMs(err) ?? defaultMs,
41997
+ onRetry: ({ attempt, err, delayMs }) => {
41998
+ bingClientLog("warn", "http.retry", {
41999
+ endpoint,
42000
+ attempt,
42001
+ delayMs: Math.round(delayMs),
42002
+ throttled: err instanceof BingApiError ? err.isThrottle : false
42003
+ });
42004
+ }
42005
+ });
42006
+ } catch (err) {
42007
+ if (err instanceof BingApiError && err.isThrottle) openThrottleCooldown(apiKey);
42008
+ throw err;
42009
+ }
41970
42010
  }
41971
42011
  async function getSites(apiKey) {
41972
42012
  validateApiKey(apiKey);
@@ -42237,7 +42277,7 @@ async function bingRoutes(app, opts) {
42237
42277
  const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
42238
42278
  const now = (/* @__PURE__ */ new Date()).toISOString();
42239
42279
  app.db.insert(bingCoverageSnapshots).values({
42240
- id: crypto33.randomUUID(),
42280
+ id: crypto34.randomUUID(),
42241
42281
  projectId: project.id,
42242
42282
  syncRunId: snapshotRunId,
42243
42283
  date: snapshotDate,
@@ -42308,7 +42348,7 @@ async function bingRoutes(app, opts) {
42308
42348
  throw validationError("url is required");
42309
42349
  }
42310
42350
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
42311
- const runId = crypto33.randomUUID();
42351
+ const runId = crypto34.randomUUID();
42312
42352
  app.db.insert(runs).values({
42313
42353
  id: runId,
42314
42354
  projectId: project.id,
@@ -42329,7 +42369,7 @@ async function bingRoutes(app, opts) {
42329
42369
  discoveryDate: result.DiscoveryDate ?? null
42330
42370
  });
42331
42371
  const now = (/* @__PURE__ */ new Date()).toISOString();
42332
- const id = crypto33.randomUUID();
42372
+ const id = crypto34.randomUUID();
42333
42373
  const httpCode = result.HttpStatus ?? result.HttpCode ?? null;
42334
42374
  const lastCrawledDate = parseBingDate(result.LastCrawledDate);
42335
42375
  const inIndexDate = parseBingDate(result.InIndexDate);
@@ -42399,7 +42439,7 @@ async function bingRoutes(app, opts) {
42399
42439
  throw validationError('No Bing site configured. Run "canonry bing set-site <project> <url>" first.');
42400
42440
  }
42401
42441
  const now = (/* @__PURE__ */ new Date()).toISOString();
42402
- const runId = crypto33.randomUUID();
42442
+ const runId = crypto34.randomUUID();
42403
42443
  app.db.insert(runs).values({
42404
42444
  id: runId,
42405
42445
  projectId: project.id,
@@ -42684,7 +42724,7 @@ async function cdpRoutes(app, opts) {
42684
42724
  }
42685
42725
 
42686
42726
  // ../api-routes/src/ga.ts
42687
- import crypto34 from "crypto";
42727
+ import crypto35 from "crypto";
42688
42728
  import { eq as eq44, desc as desc20, and as and35, sql as sql16 } from "drizzle-orm";
42689
42729
 
42690
42730
  // ../api-routes/src/ga-session-history.ts
@@ -42886,7 +42926,7 @@ function persistAcquisitionMeasurement(db, input) {
42886
42926
  )).run();
42887
42927
  for (const row of input.report.rows) {
42888
42928
  tx.insert(gaAcquisitionDaily).values({
42889
- id: crypto34.randomUUID(),
42929
+ id: crypto35.randomUUID(),
42890
42930
  projectId: input.projectId,
42891
42931
  date: row.date,
42892
42932
  channelGroup: row.channelGroup,
@@ -42928,7 +42968,7 @@ function persistLeadMeasurement(db, input) {
42928
42968
  )).run();
42929
42969
  for (const row of input.report.rows) {
42930
42970
  tx.insert(gaLeadEventsDaily).values({
42931
- id: crypto34.randomUUID(),
42971
+ id: crypto35.randomUUID(),
42932
42972
  projectId: input.projectId,
42933
42973
  date: row.date,
42934
42974
  eventName: row.eventName,
@@ -43208,7 +43248,7 @@ async function ga4Routes(app, opts) {
43208
43248
  const leadDays = measurementState?.leadSyncedAt != null ? Math.min(Math.max(1, days), 90) : 90;
43209
43249
  const leadEventNames = project.measurement.leadEventNames;
43210
43250
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
43211
- const runId = crypto34.randomUUID();
43251
+ const runId = crypto35.randomUUID();
43212
43252
  app.db.insert(runs).values({
43213
43253
  id: runId,
43214
43254
  projectId: project.id,
@@ -43262,7 +43302,7 @@ async function ga4Routes(app, opts) {
43262
43302
  ).run();
43263
43303
  for (const row of rows) {
43264
43304
  tx.insert(gaTrafficSnapshots).values({
43265
- id: crypto34.randomUUID(),
43305
+ id: crypto35.randomUUID(),
43266
43306
  projectId: project.id,
43267
43307
  date: row.date,
43268
43308
  landingPage: row.landingPage,
@@ -43285,7 +43325,7 @@ async function ga4Routes(app, opts) {
43285
43325
  ).run();
43286
43326
  for (const row of dailyTotals) {
43287
43327
  tx.insert(gaDailyTotals).values({
43288
- id: crypto34.randomUUID(),
43328
+ id: crypto35.randomUUID(),
43289
43329
  projectId: project.id,
43290
43330
  date: row.date,
43291
43331
  sessions: row.sessions,
@@ -43311,7 +43351,7 @@ async function ga4Routes(app, opts) {
43311
43351
  ).run();
43312
43352
  for (const row of aiReferrals) {
43313
43353
  tx.insert(gaAiReferrals).values({
43314
- id: crypto34.randomUUID(),
43354
+ id: crypto35.randomUUID(),
43315
43355
  projectId: project.id,
43316
43356
  date: row.date,
43317
43357
  source: row.source,
@@ -43343,7 +43383,7 @@ async function ga4Routes(app, opts) {
43343
43383
  ).run();
43344
43384
  for (const row of socialReferrals) {
43345
43385
  tx.insert(gaSocialReferrals).values({
43346
- id: crypto34.randomUUID(),
43386
+ id: crypto35.randomUUID(),
43347
43387
  projectId: project.id,
43348
43388
  date: row.date,
43349
43389
  source: row.source,
@@ -43359,7 +43399,7 @@ async function ga4Routes(app, opts) {
43359
43399
  if (syncSummary) {
43360
43400
  tx.delete(gaTrafficSummaries).where(eq44(gaTrafficSummaries.projectId, project.id)).run();
43361
43401
  tx.insert(gaTrafficSummaries).values({
43362
- id: crypto34.randomUUID(),
43402
+ id: crypto35.randomUUID(),
43363
43403
  projectId: project.id,
43364
43404
  periodStart: summary.periodStart,
43365
43405
  periodEnd: summary.periodEnd,
@@ -43372,7 +43412,7 @@ async function ga4Routes(app, opts) {
43372
43412
  tx.delete(gaTrafficWindowSummaries).where(eq44(gaTrafficWindowSummaries.projectId, project.id)).run();
43373
43413
  for (const ws of windowSummaries) {
43374
43414
  tx.insert(gaTrafficWindowSummaries).values({
43375
- id: crypto34.randomUUID(),
43415
+ id: crypto35.randomUUID(),
43376
43416
  projectId: project.id,
43377
43417
  windowKey: ws.windowKey,
43378
43418
  periodStart: ws.periodStart,
@@ -44053,7 +44093,7 @@ function parseSchemaPageEntry(entry) {
44053
44093
  }
44054
44094
 
44055
44095
  // ../integration-wordpress/src/wordpress-client.ts
44056
- import crypto35 from "crypto";
44096
+ import crypto36 from "crypto";
44057
44097
  function validateUsername(username) {
44058
44098
  if (!username || typeof username !== "string" || username.trim().length === 0) {
44059
44099
  throw new WordpressApiError("AUTH_INVALID", "Username is required and must be a non-empty string", 400);
@@ -44273,7 +44313,7 @@ function buildSnippet(content) {
44273
44313
  return `${text2.slice(0, 157)}...`;
44274
44314
  }
44275
44315
  function contentHash(content) {
44276
- return crypto35.createHash("sha256").update(content).digest("hex");
44316
+ return crypto36.createHash("sha256").update(content).digest("hex");
44277
44317
  }
44278
44318
  function buildAmbiguousSlugMessage(slug, pages) {
44279
44319
  const candidates = pages.map((page) => {
@@ -45573,7 +45613,7 @@ async function wordpressRoutes(app, opts) {
45573
45613
  }
45574
45614
 
45575
45615
  // ../api-routes/src/backlinks.ts
45576
- import crypto36 from "crypto";
45616
+ import crypto37 from "crypto";
45577
45617
  import { and as and37, asc as asc9, desc as desc21, eq as eq45, sql as sql17 } from "drizzle-orm";
45578
45618
 
45579
45619
  // ../integration-commoncrawl/src/constants.ts
@@ -46210,7 +46250,7 @@ async function backlinksRoutes(app, opts) {
46210
46250
  const refreshed = app.db.select().from(ccReleaseSyncs).where(eq45(ccReleaseSyncs.id, existing.id)).get();
46211
46251
  return reply.status(200).send(mapSyncRow(refreshed));
46212
46252
  }
46213
- const id = crypto36.randomUUID();
46253
+ const id = crypto37.randomUUID();
46214
46254
  app.db.insert(ccReleaseSyncs).values({
46215
46255
  id,
46216
46256
  release,
@@ -46267,7 +46307,7 @@ async function backlinksRoutes(app, opts) {
46267
46307
  throw validationError("Invalid release id");
46268
46308
  }
46269
46309
  const now = (/* @__PURE__ */ new Date()).toISOString();
46270
- const runId = crypto36.randomUUID();
46310
+ const runId = crypto37.randomUUID();
46271
46311
  app.db.insert(runs).values({
46272
46312
  id: runId,
46273
46313
  projectId: project.id,
@@ -46356,7 +46396,7 @@ async function backlinksRoutes(app, opts) {
46356
46396
  }
46357
46397
 
46358
46398
  // ../api-routes/src/traffic.ts
46359
- import crypto38 from "crypto";
46399
+ import crypto39 from "crypto";
46360
46400
  import { Agent as UndiciAgent } from "undici";
46361
46401
  import { and as and38, desc as desc22, eq as eq46, gte as gte10, lte as lte9, sql as sql18 } from "drizzle-orm";
46362
46402
 
@@ -46377,7 +46417,7 @@ function resolveVercelSyncDeadlineMs(env = process.env) {
46377
46417
  }
46378
46418
 
46379
46419
  // ../integration-cloud-run/src/auth.ts
46380
- import crypto37 from "crypto";
46420
+ import crypto38 from "crypto";
46381
46421
  var GOOGLE_TOKEN_URL3 = "https://oauth2.googleapis.com/token";
46382
46422
  var CLOUD_LOGGING_READ_SCOPE = "https://www.googleapis.com/auth/logging.read";
46383
46423
  var TOKEN_REQUEST_TIMEOUT_MS = 3e4;
@@ -46408,7 +46448,7 @@ function createServiceAccountJwt2(clientEmail, privateKey, scope) {
46408
46448
  const headerB64 = encode(header);
46409
46449
  const payloadB64 = encode(payload);
46410
46450
  const signingInput = `${headerB64}.${payloadB64}`;
46411
- const sign = crypto37.createSign("RSA-SHA256");
46451
+ const sign = crypto38.createSign("RSA-SHA256");
46412
46452
  sign.update(signingInput);
46413
46453
  const signature = sign.sign(privateKey, "base64url");
46414
46454
  return `${signingInput}.${signature}`;
@@ -50385,7 +50425,7 @@ async function runBackfillTask(options) {
50385
50425
  }
50386
50426
  })();
50387
50427
  tx.insert(rawEventSamples).values({
50388
- id: crypto38.randomUUID(),
50428
+ id: crypto39.randomUUID(),
50389
50429
  projectId: project.id,
50390
50430
  sourceId: sourceRow2.id,
50391
50431
  ts: sample.observedAt,
@@ -50514,7 +50554,7 @@ async function trafficRoutes(app, opts) {
50514
50554
  }).where(eq46(trafficSources.id, activeSource.id)).run();
50515
50555
  sourceRow2 = app.db.select().from(trafficSources).where(eq46(trafficSources.id, activeSource.id)).get();
50516
50556
  } else {
50517
- const newId = crypto38.randomUUID();
50557
+ const newId = crypto39.randomUUID();
50518
50558
  app.db.insert(trafficSources).values({
50519
50559
  id: newId,
50520
50560
  projectId: project.id,
@@ -50595,7 +50635,7 @@ async function trafficRoutes(app, opts) {
50595
50635
  }).where(eq46(trafficSources.id, activeSource.id)).run();
50596
50636
  sourceRow2 = app.db.select().from(trafficSources).where(eq46(trafficSources.id, activeSource.id)).get();
50597
50637
  } else {
50598
- const newId = crypto38.randomUUID();
50638
+ const newId = crypto39.randomUUID();
50599
50639
  app.db.insert(trafficSources).values({
50600
50640
  id: newId,
50601
50641
  projectId: project.id,
@@ -50679,7 +50719,7 @@ async function trafficRoutes(app, opts) {
50679
50719
  }).where(eq46(trafficSources.id, activeSource.id)).run();
50680
50720
  row = tx.select().from(trafficSources).where(eq46(trafficSources.id, activeSource.id)).get();
50681
50721
  } else {
50682
- const newId = crypto38.randomUUID();
50722
+ const newId = crypto39.randomUUID();
50683
50723
  tx.insert(trafficSources).values({
50684
50724
  id: newId,
50685
50725
  projectId: project.id,
@@ -50713,7 +50753,7 @@ async function trafficRoutes(app, opts) {
50713
50753
  let created = false;
50714
50754
  if (!existingSchedule) {
50715
50755
  tx.insert(schedules).values({
50716
- id: crypto38.randomUUID(),
50756
+ id: crypto39.randomUUID(),
50717
50757
  projectId: project.id,
50718
50758
  kind: SchedulableRunKinds["traffic-sync"],
50719
50759
  cronExpr: DEFAULT_TRAFFIC_SYNC_CRON,
@@ -50768,7 +50808,7 @@ async function trafficRoutes(app, opts) {
50768
50808
  const windowEnd = /* @__PURE__ */ new Date();
50769
50809
  const startedAt = windowEnd.toISOString();
50770
50810
  const syncStartedAtMs = windowEnd.getTime();
50771
- const runId = crypto38.randomUUID();
50811
+ const runId = crypto39.randomUUID();
50772
50812
  app.db.insert(runs).values({
50773
50813
  id: runId,
50774
50814
  projectId: project.id,
@@ -51161,7 +51201,7 @@ async function trafficRoutes(app, opts) {
51161
51201
  }
51162
51202
  })();
51163
51203
  tx.insert(rawEventSamples).values({
51164
- id: crypto38.randomUUID(),
51204
+ id: crypto39.randomUUID(),
51165
51205
  projectId: project.id,
51166
51206
  sourceId: sourceRow2.id,
51167
51207
  ts: sample.observedAt,
@@ -51412,7 +51452,7 @@ async function trafficRoutes(app, opts) {
51412
51452
  };
51413
51453
  }
51414
51454
  const startedAt = windowEnd.toISOString();
51415
- const runId = crypto38.randomUUID();
51455
+ const runId = crypto39.randomUUID();
51416
51456
  app.db.insert(runs).values({
51417
51457
  id: runId,
51418
51458
  projectId: project.id,
@@ -51782,7 +51822,7 @@ async function trafficRoutes(app, opts) {
51782
51822
  }
51783
51823
 
51784
51824
  // ../api-routes/src/doctor/checks/agent.ts
51785
- import crypto39 from "crypto";
51825
+ import crypto40 from "crypto";
51786
51826
  import fs6 from "fs";
51787
51827
  import path7 from "path";
51788
51828
  var REQUIRED_SKILLS = ["canonry", "aero"];
@@ -52013,7 +52053,7 @@ function isInstalled(dir) {
52013
52053
  }
52014
52054
  function hashInstalledFile(filePath) {
52015
52055
  try {
52016
- return crypto39.createHash("sha256").update(fs6.readFileSync(filePath)).digest("hex");
52056
+ return crypto40.createHash("sha256").update(fs6.readFileSync(filePath)).digest("hex");
52017
52057
  } catch {
52018
52058
  return void 0;
52019
52059
  }
@@ -54086,7 +54126,7 @@ async function doctorRoutes(app, opts) {
54086
54126
  }
54087
54127
 
54088
54128
  // ../api-routes/src/discovery/routes.ts
54089
- import crypto40 from "crypto";
54129
+ import crypto41 from "crypto";
54090
54130
  import { and as and42, desc as desc23, eq as eq53, gte as gte12, inArray as inArray17, isNull as isNull4, or as or10 } from "drizzle-orm";
54091
54131
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
54092
54132
  async function discoveryRoutes(app, opts) {
@@ -54152,8 +54192,8 @@ async function discoveryRoutes(app, opts) {
54152
54192
  if (existing && existing.runId) {
54153
54193
  return { reused: true, sessionId: existing.id, runId: existing.runId };
54154
54194
  }
54155
- const sessionId = crypto40.randomUUID();
54156
- const runId = crypto40.randomUUID();
54195
+ const sessionId = crypto41.randomUUID();
54196
+ const runId = crypto41.randomUUID();
54157
54197
  tx.insert(discoverySessions).values({
54158
54198
  id: sessionId,
54159
54199
  projectId: project.id,
@@ -54407,7 +54447,7 @@ async function discoveryRoutes(app, opts) {
54407
54447
  app.db.transaction((tx) => {
54408
54448
  for (const query of promotedQueries) {
54409
54449
  tx.insert(queries).values({
54410
- id: crypto40.randomUUID(),
54450
+ id: crypto41.randomUUID(),
54411
54451
  projectId: project.id,
54412
54452
  query,
54413
54453
  provenance,
@@ -54416,7 +54456,7 @@ async function discoveryRoutes(app, opts) {
54416
54456
  }
54417
54457
  for (const domain of promotedCompetitors) {
54418
54458
  tx.insert(competitors).values({
54419
- id: crypto40.randomUUID(),
54459
+ id: crypto41.randomUUID(),
54420
54460
  projectId: project.id,
54421
54461
  domain,
54422
54462
  provenance,
@@ -54506,7 +54546,7 @@ function selectEligibleCompetitors(competitorMap, competitorTypes) {
54506
54546
  }
54507
54547
 
54508
54548
  // ../api-routes/src/discovery/orchestrate.ts
54509
- import crypto41 from "crypto";
54549
+ import crypto42 from "crypto";
54510
54550
  import { eq as eq54 } from "drizzle-orm";
54511
54551
  var DEFAULT_MAX_PROBES = 100;
54512
54552
  var ABSOLUTE_MAX_PROBES = 500;
@@ -54674,7 +54714,7 @@ async function executeDiscovery(opts) {
54674
54714
  probeRows.push({ citedDomains: probe.citedDomains, bucket });
54675
54715
  buckets[bucket]++;
54676
54716
  return {
54677
- id: crypto41.randomUUID(),
54717
+ id: crypto42.randomUUID(),
54678
54718
  sessionId: opts.sessionId,
54679
54719
  projectId: opts.project.id,
54680
54720
  query: probedCanonicals[index2],
@@ -54726,7 +54766,7 @@ function upsertDomainClassifications(db, projectId, sessionId, competitorMap) {
54726
54766
  const domain = hostOf(entry.domain) ?? "";
54727
54767
  if (!domain) continue;
54728
54768
  db.insert(domainClassifications).values({
54729
- id: crypto41.randomUUID(),
54769
+ id: crypto42.randomUUID(),
54730
54770
  projectId,
54731
54771
  domain,
54732
54772
  competitorType: entry.competitorType,
@@ -54766,7 +54806,7 @@ function dedupeStrings(input) {
54766
54806
  }
54767
54807
 
54768
54808
  // ../api-routes/src/technical-aeo.ts
54769
- import crypto42 from "crypto";
54809
+ import crypto43 from "crypto";
54770
54810
  import { and as and43, asc as asc10, count, desc as desc24, eq as eq55, inArray as inArray18, lt as lt7 } from "drizzle-orm";
54771
54811
  var SURFACEABLE_STATUSES = [RunStatuses.completed, RunStatuses.partial];
54772
54812
  function emptyScore(projectName) {
@@ -54902,7 +54942,7 @@ async function technicalAeoRoutes(app, opts) {
54902
54942
  return { runId: existing.id, status: existing.status };
54903
54943
  }
54904
54944
  const now = (/* @__PURE__ */ new Date()).toISOString();
54905
- const runId = crypto42.randomUUID();
54945
+ const runId = crypto43.randomUUID();
54906
54946
  app.db.insert(runs).values({
54907
54947
  id: runId,
54908
54948
  projectId: project.id,
@@ -54920,7 +54960,7 @@ async function technicalAeoRoutes(app, opts) {
54920
54960
  }
54921
54961
 
54922
54962
  // ../api-routes/src/research.ts
54923
- import crypto43 from "crypto";
54963
+ import crypto44 from "crypto";
54924
54964
  import { and as and44, desc as desc25, eq as eq56 } from "drizzle-orm";
54925
54965
  var sameLocation = (a, b) => a.label === b.label && a.city === b.city && a.region === b.region && a.country === b.country && a.timezone === b.timezone;
54926
54966
  async function researchRoutes(app, opts) {
@@ -54947,7 +54987,7 @@ async function researchRoutes(app, opts) {
54947
54987
  if (!adapter.modelValidationPattern.test(resolvedModel)) throw validationError(`Invalid resolved model "${resolvedModel}" for provider "${providerName}".`, { provider: providerName, model: resolvedModel, hint: adapter.modelValidationHint });
54948
54988
  if (new Set(input.queries.map((query) => query.toLocaleLowerCase())).size !== input.queries.length) throw validationError("Research queries must be unique within a batch.");
54949
54989
  const normalized = { queries: input.queries, provider: providerName, model: requestedModel, location: location ?? null };
54950
- const requestHash2 = crypto43.createHash("sha256").update(JSON.stringify(normalized)).digest("hex");
54990
+ const requestHash2 = crypto44.createHash("sha256").update(JSON.stringify(normalized)).digest("hex");
54951
54991
  const now = (/* @__PURE__ */ new Date()).toISOString();
54952
54992
  const decision = app.db.transaction((tx) => {
54953
54993
  if (input.idempotencyKey) {
@@ -54957,9 +54997,9 @@ async function researchRoutes(app, opts) {
54957
54997
  return { reused: true, id: existing.id, shouldDispatch: existing.status === ResearchRunStatuses.queued };
54958
54998
  }
54959
54999
  }
54960
- const id = crypto43.randomUUID();
55000
+ const id = crypto44.randomUUID();
54961
55001
  tx.insert(researchRuns).values({ id, projectId: project.id, status: ResearchRunStatuses.queued, provider: providerName, requestedModel, resolvedModel, location: location ?? null, totalQueries: input.queries.length, idempotencyKey: input.idempotencyKey ?? null, requestHash: input.idempotencyKey ? requestHash2 : null, createdAt: now }).run();
54962
- for (const [position, query] of input.queries.entries()) tx.insert(researchRunQueries).values({ id: crypto43.randomUUID(), researchRunId: id, position, queryText: query, status: ResearchQueryStatuses.queued, requestedModel, resolvedModel, groundingSources: [], citedDomains: [], searchQueries: [], createdAt: now }).run();
55002
+ for (const [position, query] of input.queries.entries()) tx.insert(researchRunQueries).values({ id: crypto44.randomUUID(), researchRunId: id, position, queryText: query, status: ResearchQueryStatuses.queued, requestedModel, resolvedModel, groundingSources: [], citedDomains: [], searchQueries: [], createdAt: now }).run();
54963
55003
  writeAuditLog(tx, { projectId: project.id, actor: "api", action: "research.created", entityType: "research_run", entityId: id });
54964
55004
  return { reused: false, id, shouldDispatch: true };
54965
55005
  });
@@ -55376,7 +55416,7 @@ function buildTrafficSourceValidators(opts) {
55376
55416
  }
55377
55417
 
55378
55418
  // src/intelligence-service.ts
55379
- import crypto44 from "crypto";
55419
+ import crypto45 from "crypto";
55380
55420
 
55381
55421
  // src/logger.ts
55382
55422
  var IS_TTY = process.stdout.isTTY === true;
@@ -56017,7 +56057,7 @@ var IntelligenceService = class {
56017
56057
  }).run();
56018
56058
  }
56019
56059
  tx.insert(healthSnapshots).values({
56020
- id: crypto44.randomUUID(),
56060
+ id: crypto45.randomUUID(),
56021
56061
  projectId,
56022
56062
  runId,
56023
56063
  overallCitedRate: String(result.health.overallCitedRate),
@@ -4084,7 +4084,12 @@ var measurementOverviewResponseSchema = z5.object({
4084
4084
  )
4085
4085
  }).strict(),
4086
4086
  properties: measurementCursorPageSchema(measurementPropertyRowSchema),
4087
- /** Scope-wide outcome split. Counted over EVERY Property in scope, not the page. */
4087
+ /**
4088
+ * Outcome split over the whole RESULT SET, not the page — so paging through
4089
+ * does not move it. It narrows with `search` exactly as `properties.totalEstimate`
4090
+ * does, because both are computed from the same filtered rows; with no search
4091
+ * that result set is the entire scope.
4092
+ */
4088
4093
  outcomes: measurementOutcomeCountsSchema,
4089
4094
  flags: z5.object({ total: z5.number().int().nonnegative() }).strict(),
4090
4095
  namedShareOfVoice: measurementNamedShareOfVoiceSchema.optional()