@canonry/canonry 7.97.7 → 7.97.8

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.
@@ -3537,8 +3537,28 @@ var serpItemSchema = z2.object({
3537
3537
  domain: z2.string().nullish(),
3538
3538
  url: z2.string().nullish(),
3539
3539
  rank_group: z2.number().int().positive().nullish(),
3540
- rank_absolute: z2.number().int().positive().nullish()
3540
+ rank_absolute: z2.number().int().positive().nullish(),
3541
+ /** The address Google shows above the title, such as `https://www.example.com › homes`. */
3542
+ breadcrumb: z2.string().nullish()
3541
3543
  });
3544
+ function isGoogleRedirect(url) {
3545
+ if (!url) return false;
3546
+ try {
3547
+ const parsed = new URL(url);
3548
+ const host = parsed.hostname.toLowerCase().replace(/^www\./, "");
3549
+ return /^google\.[a-z]{2,3}(?:\.[a-z]{2})?$/.test(host) && (parsed.pathname === "/goto" || parsed.pathname === "/url");
3550
+ } catch {
3551
+ return false;
3552
+ }
3553
+ }
3554
+ function resultSite(item) {
3555
+ if (!isGoogleRedirect(item.url)) return { host: item.domain ? normalizeHostname(item.domain) : null, url: item.url ?? null };
3556
+ try {
3557
+ return { host: normalizeHostname(new URL(item.breadcrumb?.trim().split(/[\s\u203a]/)[0] ?? "").hostname), url: null };
3558
+ } catch {
3559
+ return { host: null, url: null };
3560
+ }
3561
+ }
3542
3562
  function provenance(request, collectedAt, sourceUpdatedAt) {
3543
3563
  const serp = request.capability === SeoCapabilities["market-rankings"];
3544
3564
  return {
@@ -3569,15 +3589,16 @@ function marketRankings(rawResult, request, collectedAt, partial) {
3569
3589
  if (searchIdentity(result.keyword) !== searchIdentity(searchText)) throw new Error("SEO provider returned a different search.");
3570
3590
  const organic = (result.items ?? []).filter((item) => serpItemTypeSchema.safeParse(item).data?.type === "organic").map((item) => serpItemSchema.parse(item)).filter((item) => item.rank_group != null);
3571
3591
  if (organic.length === 0) throw new Error("SEO provider returned no organic results.");
3592
+ const sites = organic.map((item) => ({ item, ...resultSite(item) }));
3593
+ if (!sites.some((site) => site.host)) throw new Error("SEO provider returned no organic result with a known site.");
3572
3594
  const depth = partial ? Math.min(search.depth, organic.reduce((deepest, item) => Math.max(deepest, item.rank_group), 0)) : search.depth;
3573
3595
  let best = null;
3574
3596
  const competitorRanks = /* @__PURE__ */ new Map();
3575
3597
  const competitors2 = new Set(search.markets.flatMap((market) => market.competitors));
3576
- for (const item of organic) {
3577
- const host = item.domain ? normalizeHostname(item.domain) : null;
3598
+ for (const { item, host, url } of sites) {
3578
3599
  if (!host) continue;
3579
3600
  if (hostBelongsTo(host, target)) {
3580
- if (!best || item.rank_group < best.rank_group) best = item;
3601
+ if (!best || item.rank_group < best.rank) best = { rank: item.rank_group, rankAbsolute: item.rank_absolute ?? null, url };
3581
3602
  continue;
3582
3603
  }
3583
3604
  const domain = registrableDomain(host);
@@ -3594,8 +3615,8 @@ function marketRankings(rawResult, request, collectedAt, partial) {
3594
3615
  locationLabel: search.locationLabel,
3595
3616
  target,
3596
3617
  searchStatus: partial ? SeoMarketRankingSearchStatuses.partial : SeoMarketRankingSearchStatuses.completed,
3597
- rankOrganic: best?.rank_group ?? null,
3598
- rankAbsolute: best?.rank_absolute ?? null,
3618
+ rankOrganic: best?.rank ?? null,
3619
+ rankAbsolute: best?.rankAbsolute ?? null,
3599
3620
  url: best?.url ?? null,
3600
3621
  depth,
3601
3622
  competitors: ranked.filter((competitor) => tracked.has(competitor.domain)),
@@ -5980,7 +6001,7 @@ function nonBlank(value) {
5980
6001
  return trimmed ? trimmed : void 0;
5981
6002
  }
5982
6003
  function resolveBuildCommit(env = process.env) {
5983
- const embedded = true ? "930bad0500d64bef2401b0455bf57cf735f3cc7a" : void 0;
6004
+ const embedded = true ? "58098d90ba7afb8fa38629099f82a91d52ed434c" : void 0;
5984
6005
  return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
5985
6006
  }
5986
6007
  function resolveInstanceIdentity(env = process.env) {
@@ -7161,7 +7161,7 @@ var SYSTEM_CLI_COMMANDS = [
7161
7161
  options: { access: stringOption() },
7162
7162
  run: async (input) => {
7163
7163
  if (input.positionals.length > 1) throw usageError("Expected at most one command group");
7164
- const [{ printCliCatalog }, { REGISTERED_CLI_COMMANDS: REGISTERED_CLI_COMMANDS2 }] = await Promise.all([import("./cli-catalog-Z4XQIDYU.js"), import("./cli-commands-FC6ICDDA.js")]);
7164
+ const [{ printCliCatalog }, { REGISTERED_CLI_COMMANDS: REGISTERED_CLI_COMMANDS2 }] = await Promise.all([import("./cli-catalog-Z4XQIDYU.js"), import("./cli-commands-3HZFXKVU.js")]);
7165
7165
  printCliCatalog(REGISTERED_CLI_COMMANDS2, input.format, input.positionals[0], getString(input.values, "access"));
7166
7166
  }
7167
7167
  },
@@ -7210,7 +7210,7 @@ var SYSTEM_CLI_COMMANDS = [
7210
7210
  },
7211
7211
  allowPositionals: false,
7212
7212
  run: async (input) => {
7213
- const { initCommand } = await import("./init-4KLJC5N5.js");
7213
+ const { initCommand } = await import("./init-OM64S7AT.js");
7214
7214
  await initCommand({
7215
7215
  force: getBoolean(input.values, "force"),
7216
7216
  ref: getString(input.values, "ref"),
@@ -7265,7 +7265,7 @@ var SYSTEM_CLI_COMMANDS = [
7265
7265
  allowPositionals: false,
7266
7266
  run: async (input) => {
7267
7267
  applyServerEnv(input.values);
7268
- const { serveCommand } = await import("./serve-VTMTK2TK.js");
7268
+ const { serveCommand } = await import("./serve-4QZEQUER.js");
7269
7269
  await serveCommand(input.format);
7270
7270
  }
7271
7271
  },
@@ -47,7 +47,7 @@ function queueServeHandoff() {
47
47
  async function runPendingServeHandoff(format) {
48
48
  if (!pendingServeHandoff) return;
49
49
  pendingServeHandoff = false;
50
- const { serveCommand } = await import("./serve-VTMTK2TK.js");
50
+ const { serveCommand } = await import("./serve-4QZEQUER.js");
51
51
  await serveCommand(format);
52
52
  }
53
53
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  REGISTERED_CLI_COMMANDS
3
- } from "./chunk-JPVLEXY6.js";
3
+ } from "./chunk-JYG3LUXU.js";
4
4
  import "./chunk-6GO5VVAC.js";
5
5
  import "./chunk-VJWDBFSL.js";
6
6
  import "./chunk-7CAOGKRV.js";
package/dist/cli.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node --import tsx
2
2
  import {
3
3
  REGISTERED_CLI_COMMANDS
4
- } from "./chunk-JPVLEXY6.js";
4
+ } from "./chunk-JYG3LUXU.js";
5
5
  import "./chunk-6GO5VVAC.js";
6
6
  import {
7
7
  buildSetupState,
8
8
  runPendingServeHandoff
9
- } from "./chunk-O4X2V6EO.js";
9
+ } from "./chunk-X4DR2LK7.js";
10
10
  import "./chunk-VJWDBFSL.js";
11
11
  import "./chunk-7CAOGKRV.js";
12
12
  import "./chunk-TSENU4GG.js";
package/dist/index.d.ts CHANGED
@@ -12454,7 +12454,7 @@ declare const siteAuditSnapshots: drizzle_orm_sqlite_core.SQLiteTableWithColumns
12454
12454
  weight: number;
12455
12455
  sharePct: number | null;
12456
12456
  avgScore: number;
12457
- status: "partial" | "fail" | "pass";
12457
+ status: "fail" | "partial" | "pass";
12458
12458
  pagesPassing: number;
12459
12459
  pagesPartial: number;
12460
12460
  pagesFailing: number;
@@ -12476,7 +12476,7 @@ declare const siteAuditSnapshots: drizzle_orm_sqlite_core.SQLiteTableWithColumns
12476
12476
  weight: number;
12477
12477
  sharePct: number | null;
12478
12478
  avgScore: number;
12479
- status: "partial" | "fail" | "pass";
12479
+ status: "fail" | "partial" | "pass";
12480
12480
  pagesPassing: number;
12481
12481
  pagesPartial: number;
12482
12482
  pagesFailing: number;
@@ -34780,7 +34780,7 @@ declare const adsOperationSteps: drizzle_orm_sqlite_core.SQLiteTableWithColumns<
34780
34780
  tableName: "ads_operation_steps";
34781
34781
  dataType: "string";
34782
34782
  columnType: "SQLiteText";
34783
- data: "unknown" | "failed" | "active" | "pending" | "executing" | "rollback_executing" | "rolled_back" | "rollback_failed";
34783
+ data: "unknown" | "active" | "failed" | "pending" | "executing" | "rollback_executing" | "rolled_back" | "rollback_failed";
34784
34784
  driverParam: string;
34785
34785
  notNull: true;
34786
34786
  hasDefault: false;
@@ -34793,7 +34793,7 @@ declare const adsOperationSteps: drizzle_orm_sqlite_core.SQLiteTableWithColumns<
34793
34793
  generated: undefined;
34794
34794
  }, {}, {
34795
34795
  length: number | undefined;
34796
- $type: "unknown" | "failed" | "active" | "pending" | "executing" | "rollback_executing" | "rolled_back" | "rollback_failed";
34796
+ $type: "unknown" | "active" | "failed" | "pending" | "executing" | "rollback_executing" | "rolled_back" | "rollback_failed";
34797
34797
  }>;
34798
34798
  providerUpdatedAt: drizzle_orm_sqlite_core.SQLiteColumn<{
34799
34799
  name: "provider_updated_at";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createGoogleMarketingCredentialStore,
4
4
  createGoogleMarketingRuntime,
5
5
  createServer
6
- } from "./chunk-TVVQRHVR.js";
6
+ } from "./chunk-22LX5CWD.js";
7
7
  import "./chunk-GMCVZPKO.js";
8
8
  import "./chunk-I6G5K5PM.js";
9
9
  import "./chunk-AI2G7QMW.js";
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  buildSetupState,
9
9
  queueServeHandoff
10
- } from "./chunk-O4X2V6EO.js";
10
+ } from "./chunk-X4DR2LK7.js";
11
11
  import "./chunk-VJWDBFSL.js";
12
12
  import "./chunk-RABCSQBW.js";
13
13
  import "./chunk-C3LS3ZKI.js";
@@ -9,7 +9,7 @@ import {
9
9
  allowsKeylessFirstRunSetup,
10
10
  createServer,
11
11
  waitForServerRuntimeStartup
12
- } from "./chunk-TVVQRHVR.js";
12
+ } from "./chunk-22LX5CWD.js";
13
13
  import "./chunk-GMCVZPKO.js";
14
14
  import "./chunk-I6G5K5PM.js";
15
15
  import "./chunk-AI2G7QMW.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "7.97.7",
3
+ "version": "7.97.8",
4
4
  "type": "module",
5
5
  "description": "Self-hosted AI visibility (AEO) platform: track how ChatGPT, Claude, Gemini, and Perplexity cite your domain, join it with Search Console, GA4, server-side traffic, and paid media, and fix what you find through agent tools (CLI, REST, MCP). Local SQLite.",
6
6
  "keywords": [