@canonry/canonry 4.148.5 → 4.148.7

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 (25) hide show
  1. package/assets/assets/{AuditHistoryPanel-D5fRv5sF.js → AuditHistoryPanel-C86x9Iku.js} +1 -1
  2. package/assets/assets/{BacklinksPage-CFdO1s1V.js → BacklinksPage-B3oUNNXC.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-BoB1RfNf.js → ChartPrimitives-C6_tMGV3.js} +1 -1
  4. package/assets/assets/{HistoryPage-YT2JCo2G.js → HistoryPage-DYiMm7tS.js} +1 -1
  5. package/assets/assets/{MeasurementPropertyPage-JDEzHX1H.js → MeasurementPropertyPage-Cej0CSpU.js} +1 -1
  6. package/assets/assets/{ProjectPage-B6t_j9tN.js → ProjectPage-CYFnPBUA.js} +9 -9
  7. package/assets/assets/{RunRow-yKrIU8zY.js → RunRow-BoPi92Qj.js} +1 -1
  8. package/assets/assets/{RunsPage-DAeC6Ebm.js → RunsPage-DUM6WBL1.js} +1 -1
  9. package/assets/assets/{SettingsPage-8Z251PH0.js → SettingsPage-DFPenWph.js} +1 -1
  10. package/assets/assets/{TrafficPage-DNJGp16_.js → TrafficPage-S8S9tB6N.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-BuzdNFB8.js → TrafficSourceDetailPage-CfzhhNVa.js} +1 -1
  12. package/assets/assets/{arrow-left-BrVL2J2e.js → arrow-left-Bhi4ZStt.js} +1 -1
  13. package/assets/assets/{extract-error-message-jd9FdYvE.js → extract-error-message-DJGuacu4.js} +1 -1
  14. package/assets/assets/{index-Bo0hzrG2.js → index-DOO7zB-y.js} +3 -3
  15. package/assets/assets/{trash-2-lAIhBm6w.js → trash-2-B-gSnDV3.js} +1 -1
  16. package/assets/index.html +1 -1
  17. package/dist/{chunk-CQBXLHT4.js → chunk-4VNOYFCG.js} +83 -142
  18. package/dist/{chunk-IGPUEHUD.js → chunk-B4JACCYB.js} +1 -1
  19. package/dist/{chunk-AJOOL3D3.js → chunk-HWK2JSV3.js} +45 -1
  20. package/dist/{chunk-UOYZDJOE.js → chunk-R6UQYXLG.js} +59 -0
  21. package/dist/cli.js +4 -4
  22. package/dist/index.js +4 -4
  23. package/dist/{intelligence-service-S5PQT5GP.js → intelligence-service-M6QKJWT7.js} +2 -2
  24. package/dist/mcp.js +2 -2
  25. package/package.json +9 -9
@@ -1 +1 @@
1
- import{c as a}from"./index-Bo0hzrG2.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-DOO7zB-y.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};
package/assets/index.html CHANGED
@@ -12,7 +12,7 @@
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-Bo0hzrG2.js"></script>
15
+ <script type="module" crossorigin src="./assets/index-DOO7zB-y.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">
@@ -10,7 +10,7 @@ import {
10
10
  loadConfig,
11
11
  loadConfigRaw,
12
12
  saveConfigPatch
13
- } from "./chunk-IGPUEHUD.js";
13
+ } from "./chunk-B4JACCYB.js";
14
14
  import {
15
15
  CC_CACHE_DIR,
16
16
  DUCKDB_SPEC,
@@ -126,7 +126,7 @@ import {
126
126
  siteAuditSnapshots,
127
127
  toAlertView,
128
128
  usageCounters
129
- } from "./chunk-AJOOL3D3.js";
129
+ } from "./chunk-HWK2JSV3.js";
130
130
  import {
131
131
  AGENT_MEMORY_VALUE_MAX_BYTES,
132
132
  AGENT_PROVIDER_IDS,
@@ -177,6 +177,7 @@ import {
177
177
  contentActionLabel,
178
178
  contentBriefDtoSchema,
179
179
  deriveCitedUrlCandidates,
180
+ deriveIndexCoverage,
180
181
  determineAnswerMentioned,
181
182
  dollarsToMicros,
182
183
  effectiveBrandNames,
@@ -217,7 +218,7 @@ import {
217
218
  validationError,
218
219
  winnabilityClassLabel,
219
220
  withRetry
220
- } from "./chunk-UOYZDJOE.js";
221
+ } from "./chunk-R6UQYXLG.js";
221
222
 
222
223
  // src/telemetry.ts
223
224
  import crypto from "crypto";
@@ -4226,71 +4227,6 @@ function maybeShowActivationNotice(io = {}) {
4226
4227
  // src/gsc-sync.ts
4227
4228
  import crypto5 from "crypto";
4228
4229
  import { eq as eq3, and as and3, sql as sql3 } from "drizzle-orm";
4229
-
4230
- // src/gsc-inspect-paced.ts
4231
- var INSPECT_BASE_DELAY_MS = 1e3;
4232
- var INSPECT_PACING_JITTER_MS = 250;
4233
- var INSPECT_MAX_RETRIES = 3;
4234
- var INSPECT_MAX_BACKOFF_MS = 3e4;
4235
- var INSPECT_FAILFAST_THRESHOLD = 5;
4236
- function isRetryableGscInspectError(err) {
4237
- if (err != null && typeof err === "object" && "status" in err) {
4238
- if (err.status === 403) return true;
4239
- }
4240
- return isRetryableHttpError(err);
4241
- }
4242
- function defaultSleep(ms) {
4243
- return new Promise((resolve) => setTimeout(resolve, ms));
4244
- }
4245
- async function inspectUrlsPaced(urls, cb, deps = {}) {
4246
- const sleep3 = deps.sleep ?? defaultSleep;
4247
- const jitter = deps.jitter ?? Math.random;
4248
- let inspected = 0;
4249
- let errors = 0;
4250
- let consecutiveRetryableFailures = 0;
4251
- for (let index = 0; index < urls.length; index++) {
4252
- const url = urls[index];
4253
- try {
4254
- const result = await withRetry(() => cb.inspectOne(url), {
4255
- maxRetries: INSPECT_MAX_RETRIES,
4256
- baseDelayMs: INSPECT_BASE_DELAY_MS,
4257
- maxDelayMs: INSPECT_MAX_BACKOFF_MS,
4258
- isRetryable: isRetryableGscInspectError,
4259
- sleep: sleep3,
4260
- onRetry: ({ attempt, delayMs, err }) => deps.log?.info("inspect.retry", {
4261
- url,
4262
- attempt,
4263
- delayMs: Math.round(delayMs),
4264
- error: err instanceof Error ? err.message : String(err)
4265
- })
4266
- });
4267
- cb.onResult(url, result, index);
4268
- inspected++;
4269
- consecutiveRetryableFailures = 0;
4270
- } catch (err) {
4271
- errors++;
4272
- cb.onError(url, err, index);
4273
- if (isRetryableGscInspectError(err)) {
4274
- consecutiveRetryableFailures++;
4275
- if (consecutiveRetryableFailures >= INSPECT_FAILFAST_THRESHOLD) {
4276
- deps.log?.error("inspect.circuit-break", {
4277
- consecutiveFailures: consecutiveRetryableFailures,
4278
- inspected,
4279
- errors,
4280
- remaining: urls.length - index - 1
4281
- });
4282
- return { inspected, errors, aborted: true, abortError: err };
4283
- }
4284
- }
4285
- }
4286
- if (index < urls.length - 1) {
4287
- await sleep3(INSPECT_BASE_DELAY_MS + jitter() * INSPECT_PACING_JITTER_MS);
4288
- }
4289
- }
4290
- return { inspected, errors, aborted: false };
4291
- }
4292
-
4293
- // src/gsc-sync.ts
4294
4230
  var log2 = createLogger("GscSync");
4295
4231
  function formatDate(d) {
4296
4232
  return d.toISOString().split("T")[0];
@@ -4428,63 +4364,6 @@ async function executeGscSync(db, runId, projectId, opts) {
4428
4364
  }).run();
4429
4365
  }
4430
4366
  log2.info("query-totals.complete", { runId, projectId, rowCount: queryTotalRows.length });
4431
- const pageClicks = /* @__PURE__ */ new Map();
4432
- for (const row of rows) {
4433
- const page = row.keys[1];
4434
- if (page) {
4435
- pageClicks.set(page, (pageClicks.get(page) ?? 0) + row.clicks);
4436
- }
4437
- }
4438
- const topPages = [...pageClicks.entries()].sort((a, b) => b[1] - a[1]).slice(0, 50).map(([page]) => page);
4439
- log2.info("inspect.start", { runId, projectId, urlCount: topPages.length });
4440
- const inspectOutcome = await inspectUrlsPaced(
4441
- topPages,
4442
- {
4443
- inspectOne: (pageUrl) => inspectUrl(accessToken, pageUrl, propertyId),
4444
- onResult: (pageUrl, result) => {
4445
- const ir = result.inspectionResult;
4446
- const idx = ir.indexStatusResult;
4447
- const mob = ir.mobileUsabilityResult;
4448
- const rich = ir.richResultsResult;
4449
- const inspectedAt = (/* @__PURE__ */ new Date()).toISOString();
4450
- db.insert(gscUrlInspections).values({
4451
- id: crypto5.randomUUID(),
4452
- projectId,
4453
- syncRunId: runId,
4454
- url: pageUrl,
4455
- indexingState: idx?.indexingState ?? null,
4456
- verdict: idx?.verdict ?? null,
4457
- coverageState: idx?.coverageState ?? null,
4458
- pageFetchState: idx?.pageFetchState ?? null,
4459
- robotsTxtState: idx?.robotsTxtState ?? null,
4460
- crawlTime: idx?.lastCrawlTime ?? null,
4461
- lastCrawlResult: idx?.crawlResult ?? null,
4462
- isMobileFriendly: mob?.verdict === "PASS" ? true : mob?.verdict === "FAIL" ? false : null,
4463
- richResults: rich?.detectedItems?.map((d) => d.richResultType) ?? [],
4464
- referringUrls: idx?.referringUrls ?? [],
4465
- inspectedAt,
4466
- createdAt: inspectedAt
4467
- }).run();
4468
- },
4469
- onError: (pageUrl, err) => {
4470
- log2.error("inspect.url-failed", { runId, projectId, url: pageUrl, error: err instanceof Error ? err.message : String(err) });
4471
- }
4472
- },
4473
- {
4474
- log: {
4475
- info: (action, ctx) => log2.info(action, { runId, projectId, ...ctx }),
4476
- error: (action, ctx) => log2.error(action, { runId, projectId, ...ctx })
4477
- }
4478
- }
4479
- );
4480
- if (inspectOutcome.aborted) {
4481
- log2.error("inspect.stopped-early", {
4482
- runId,
4483
- projectId,
4484
- inspected: inspectOutcome.inspected,
4485
- note: "URL inspection stopped early after sustained rate/access failures; search-analytics data was still saved"
4486
- });
4487
- }
4488
4367
  const allInspections = db.select().from(gscUrlInspections).where(eq3(gscUrlInspections.projectId, projectId)).all();
4489
4368
  const latestByUrl = /* @__PURE__ */ new Map();
4490
4369
  for (const row of allInspections) {
@@ -4493,18 +4372,14 @@ async function executeGscSync(db, runId, projectId, opts) {
4493
4372
  latestByUrl.set(row.url, row);
4494
4373
  }
4495
4374
  }
4496
- let snapIndexed = 0;
4497
- let snapNotIndexed = 0;
4498
- const reasonCounts = {};
4499
- for (const [, row] of latestByUrl) {
4500
- if (row.indexingState === "INDEXING_ALLOWED") {
4501
- snapIndexed++;
4502
- } else {
4503
- snapNotIndexed++;
4504
- const reason = row.coverageState ?? "Unknown";
4505
- reasonCounts[reason] = (reasonCounts[reason] ?? 0) + 1;
4506
- }
4507
- }
4375
+ const coverage = deriveIndexCoverage({
4376
+ pages: rows.map((row) => ({ page: row.keys[1] ?? "", impressions: row.impressions })),
4377
+ inspections: [...latestByUrl.values()].map((row) => ({
4378
+ url: row.url,
4379
+ indexingState: row.indexingState,
4380
+ coverageState: row.coverageState
4381
+ }))
4382
+ });
4508
4383
  const snapshotDate = formatDate(/* @__PURE__ */ new Date());
4509
4384
  db.delete(gscCoverageSnapshots).where(and3(eq3(gscCoverageSnapshots.projectId, projectId), eq3(gscCoverageSnapshots.date, snapshotDate))).run();
4510
4385
  db.insert(gscCoverageSnapshots).values({
@@ -4512,13 +4387,16 @@ async function executeGscSync(db, runId, projectId, opts) {
4512
4387
  projectId,
4513
4388
  syncRunId: runId,
4514
4389
  date: snapshotDate,
4515
- indexed: snapIndexed,
4516
- notIndexed: snapNotIndexed,
4517
- reasonBreakdown: reasonCounts,
4390
+ indexed: coverage.indexed,
4391
+ notIndexed: coverage.notIndexed,
4392
+ unknownPages: coverage.unknown,
4393
+ verifiedByInspection: coverage.verifiedByInspection,
4394
+ derivedFromImpressions: coverage.derivedFromImpressions,
4395
+ reasonBreakdown: coverage.reasonBreakdown,
4518
4396
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
4519
4397
  }).run();
4520
4398
  db.update(runs).set({ status: "completed", finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq3(runs.id, runId)).run();
4521
- log2.info("sync.completed", { runId, projectId, searchDataRows: rows.length, urlInspections: topPages.length, indexed: snapIndexed, notIndexed: snapNotIndexed });
4399
+ log2.info("sync.completed", { runId, projectId, searchDataRows: rows.length, indexed: coverage.indexed, notIndexed: coverage.notIndexed, unknown: coverage.unknown, verifiedByInspection: coverage.verifiedByInspection });
4522
4400
  } catch (err) {
4523
4401
  const errorMsg = err instanceof Error ? err.message : String(err);
4524
4402
  db.update(runs).set({ status: "failed", error: errorMsg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq3(runs.id, runId)).run();
@@ -5729,6 +5607,69 @@ async function parseSitemapRecursive(url, urls, visited, depth, isChild) {
5729
5607
  LOC_REGEX.lastIndex = 0;
5730
5608
  }
5731
5609
 
5610
+ // src/gsc-inspect-paced.ts
5611
+ var INSPECT_BASE_DELAY_MS = 1e3;
5612
+ var INSPECT_PACING_JITTER_MS = 250;
5613
+ var INSPECT_MAX_RETRIES = 3;
5614
+ var INSPECT_MAX_BACKOFF_MS = 3e4;
5615
+ var INSPECT_FAILFAST_THRESHOLD = 5;
5616
+ function isRetryableGscInspectError(err) {
5617
+ if (err != null && typeof err === "object" && "status" in err) {
5618
+ if (err.status === 403) return true;
5619
+ }
5620
+ return isRetryableHttpError(err);
5621
+ }
5622
+ function defaultSleep(ms) {
5623
+ return new Promise((resolve) => setTimeout(resolve, ms));
5624
+ }
5625
+ async function inspectUrlsPaced(urls, cb, deps = {}) {
5626
+ const sleep3 = deps.sleep ?? defaultSleep;
5627
+ const jitter = deps.jitter ?? Math.random;
5628
+ let inspected = 0;
5629
+ let errors = 0;
5630
+ let consecutiveRetryableFailures = 0;
5631
+ for (let index = 0; index < urls.length; index++) {
5632
+ const url = urls[index];
5633
+ try {
5634
+ const result = await withRetry(() => cb.inspectOne(url), {
5635
+ maxRetries: INSPECT_MAX_RETRIES,
5636
+ baseDelayMs: INSPECT_BASE_DELAY_MS,
5637
+ maxDelayMs: INSPECT_MAX_BACKOFF_MS,
5638
+ isRetryable: isRetryableGscInspectError,
5639
+ sleep: sleep3,
5640
+ onRetry: ({ attempt, delayMs, err }) => deps.log?.info("inspect.retry", {
5641
+ url,
5642
+ attempt,
5643
+ delayMs: Math.round(delayMs),
5644
+ error: err instanceof Error ? err.message : String(err)
5645
+ })
5646
+ });
5647
+ cb.onResult(url, result, index);
5648
+ inspected++;
5649
+ consecutiveRetryableFailures = 0;
5650
+ } catch (err) {
5651
+ errors++;
5652
+ cb.onError(url, err, index);
5653
+ if (isRetryableGscInspectError(err)) {
5654
+ consecutiveRetryableFailures++;
5655
+ if (consecutiveRetryableFailures >= INSPECT_FAILFAST_THRESHOLD) {
5656
+ deps.log?.error("inspect.circuit-break", {
5657
+ consecutiveFailures: consecutiveRetryableFailures,
5658
+ inspected,
5659
+ errors,
5660
+ remaining: urls.length - index - 1
5661
+ });
5662
+ return { inspected, errors, aborted: true, abortError: err };
5663
+ }
5664
+ }
5665
+ }
5666
+ if (index < urls.length - 1) {
5667
+ await sleep3(INSPECT_BASE_DELAY_MS + jitter() * INSPECT_PACING_JITTER_MS);
5668
+ }
5669
+ }
5670
+ return { inspected, errors, aborted: false };
5671
+ }
5672
+
5732
5673
  // src/gsc-inspect-sitemap.ts
5733
5674
  var log6 = createLogger("InspectSitemap");
5734
5675
  async function executeInspectSitemap(db, runId, projectId, opts) {
@@ -7420,7 +7361,7 @@ function readStoredGroundingSources(rawResponse) {
7420
7361
  return result;
7421
7362
  }
7422
7363
  async function backfillInsightsCommand(project, opts) {
7423
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-S5PQT5GP.js");
7364
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-M6QKJWT7.js");
7424
7365
  const config = loadConfig();
7425
7366
  const db = createClient(config.database);
7426
7367
  migrate(db);
@@ -81,7 +81,7 @@ import {
81
81
  trafficConnectWordpressRequestSchema,
82
82
  trafficEventKindSchema,
83
83
  trafficSeriesGranularitySchema
84
- } from "./chunk-UOYZDJOE.js";
84
+ } from "./chunk-R6UQYXLG.js";
85
85
 
86
86
  // src/config.ts
87
87
  import fs from "fs";
@@ -519,7 +519,7 @@ import {
519
519
  wordpressSchemaDeployResultDtoSchema,
520
520
  wordpressSchemaStatusResultDtoSchema,
521
521
  wordpressStatusDtoSchema
522
- } from "./chunk-UOYZDJOE.js";
522
+ } from "./chunk-R6UQYXLG.js";
523
523
 
524
524
  // src/intelligence-service.ts
525
525
  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";
@@ -1133,6 +1133,17 @@ var gscCoverageSnapshots = sqliteTable("gsc_coverage_snapshots", {
1133
1133
  date: text("date").notNull(),
1134
1134
  indexed: integer("indexed").notNull().default(0),
1135
1135
  notIndexed: integer("not_indexed").notNull().default(0),
1136
+ /**
1137
+ * Pages we have no evidence either way for: no impressions in the window and
1138
+ * never inspected. Deliberately NOT folded into `notIndexed` — absence of
1139
+ * impressions is not evidence of exclusion, and collapsing the two would
1140
+ * report every unmeasured page as a problem.
1141
+ */
1142
+ unknownPages: integer("unknown_pages").notNull().default(0),
1143
+ /** Of the above, how many carry a real URL Inspection verdict. */
1144
+ verifiedByInspection: integer("verified_by_inspection").notNull().default(0),
1145
+ /** Pages proven indexed by impressions alone, costing no inspection quota. */
1146
+ derivedFromImpressions: integer("derived_from_impressions").notNull().default(0),
1136
1147
  reasonBreakdown: text("reason_breakdown", { mode: "json" }).$type().notNull().default({}),
1137
1148
  createdAt: text("created_at").notNull()
1138
1149
  }, (table) => [
@@ -5053,6 +5064,36 @@ var MIGRATION_VERSIONS = [
5053
5064
  `CREATE UNIQUE INDEX IF NOT EXISTS idx_measurement_segments_project_id
5054
5065
  ON measurement_segments(project_id, id)`
5055
5066
  ]
5067
+ },
5068
+ {
5069
+ version: 125,
5070
+ name: "gsc-coverage-unknown-and-provenance",
5071
+ statements: [
5072
+ // Create-if-missing first. A database migrating up from a legacy schema
5073
+ // may not have this table yet, and `ALTER TABLE` on a missing table
5074
+ // raises an error the runner does NOT swallow — it only swallows
5075
+ // duplicate-column. Mirrors the original definition so this version is
5076
+ // self-sufficient and safely re-runnable.
5077
+ `CREATE TABLE IF NOT EXISTS gsc_coverage_snapshots (
5078
+ id TEXT PRIMARY KEY,
5079
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
5080
+ sync_run_id TEXT REFERENCES runs(id) ON DELETE CASCADE,
5081
+ date TEXT NOT NULL,
5082
+ indexed INTEGER NOT NULL DEFAULT 0,
5083
+ not_indexed INTEGER NOT NULL DEFAULT 0,
5084
+ reason_breakdown TEXT NOT NULL DEFAULT '{}',
5085
+ created_at TEXT NOT NULL
5086
+ )`,
5087
+ // A THIRD state, not a rename of anything. Named `unknown_pages`
5088
+ // because bare `unknown` is a reserved SQL keyword. A page with no
5089
+ // impressions and no inspection is unmeasured, and folding it into
5090
+ // `not_indexed` would report every such page as a problem.
5091
+ `ALTER TABLE gsc_coverage_snapshots ADD COLUMN unknown_pages INTEGER NOT NULL DEFAULT 0`,
5092
+ // Provenance: how much of the number is a real Google verdict versus
5093
+ // derived from impressions. Lets the UI say how much it actually checked.
5094
+ `ALTER TABLE gsc_coverage_snapshots ADD COLUMN verified_by_inspection INTEGER NOT NULL DEFAULT 0`,
5095
+ `ALTER TABLE gsc_coverage_snapshots ADD COLUMN derived_from_impressions INTEGER NOT NULL DEFAULT 0`
5096
+ ]
5056
5097
  }
5057
5098
  ];
5058
5099
  function addRunsMeasurementPlanVersionForeignKey(tx) {
@@ -36099,6 +36140,9 @@ async function googleRoutes(app, opts) {
36099
36140
  date: r.date,
36100
36141
  indexed: r.indexed,
36101
36142
  notIndexed: r.notIndexed,
36143
+ unknownPages: r.unknownPages,
36144
+ verifiedByInspection: r.verifiedByInspection,
36145
+ derivedFromImpressions: r.derivedFromImpressions,
36102
36146
  reasonBreakdown: r.reasonBreakdown
36103
36147
  })).reverse();
36104
36148
  });
@@ -7529,6 +7529,17 @@ var gscCoverageSnapshotDtoSchema = z17.object({
7529
7529
  date: z17.string(),
7530
7530
  indexed: z17.number(),
7531
7531
  notIndexed: z17.number(),
7532
+ /**
7533
+ * Pages with no evidence either way — no impressions in the window and never
7534
+ * inspected. Deliberately NOT folded into `notIndexed`: absence of
7535
+ * impressions is not evidence of exclusion, and merging the two would report
7536
+ * every unmeasured page as a problem.
7537
+ */
7538
+ unknownPages: z17.number().default(0),
7539
+ /** Of the total, how many carry a real URL Inspection verdict. */
7540
+ verifiedByInspection: z17.number().default(0),
7541
+ /** Pages proven indexed by impressions alone, costing no inspection quota. */
7542
+ derivedFromImpressions: z17.number().default(0),
7532
7543
  reasonBreakdown: z17.record(z17.string(), z17.number()).default({})
7533
7544
  });
7534
7545
  var gscSiteDtoSchema = z17.object({
@@ -11014,6 +11025,53 @@ function summarizeCheckResults(results) {
11014
11025
  return summary;
11015
11026
  }
11016
11027
 
11028
+ // ../contracts/src/index-coverage.ts
11029
+ function deriveIndexCoverage(input) {
11030
+ const impressionsByPage = /* @__PURE__ */ new Map();
11031
+ for (const row of input.pages) {
11032
+ if (!row.page) continue;
11033
+ impressionsByPage.set(row.page, (impressionsByPage.get(row.page) ?? 0) + (row.impressions || 0));
11034
+ }
11035
+ const inspectionByUrl = /* @__PURE__ */ new Map();
11036
+ for (const row of input.inspections ?? []) {
11037
+ if (row.url) inspectionByUrl.set(row.url, row);
11038
+ }
11039
+ const states = /* @__PURE__ */ new Map();
11040
+ const reasonBreakdown = {};
11041
+ let indexed = 0;
11042
+ let notIndexed = 0;
11043
+ let unknown = 0;
11044
+ let verifiedByInspection = 0;
11045
+ let derivedFromImpressions = 0;
11046
+ for (const page of /* @__PURE__ */ new Set([...impressionsByPage.keys(), ...inspectionByUrl.keys()])) {
11047
+ const impressions = impressionsByPage.get(page) ?? 0;
11048
+ const inspection = inspectionByUrl.get(page);
11049
+ if (impressions > 0) {
11050
+ states.set(page, "indexed");
11051
+ indexed++;
11052
+ derivedFromImpressions++;
11053
+ if (inspection) verifiedByInspection++;
11054
+ continue;
11055
+ }
11056
+ if (!inspection) {
11057
+ states.set(page, "unknown");
11058
+ unknown++;
11059
+ continue;
11060
+ }
11061
+ verifiedByInspection++;
11062
+ if (inspection.indexingState === "INDEXING_ALLOWED") {
11063
+ states.set(page, "indexed");
11064
+ indexed++;
11065
+ } else {
11066
+ states.set(page, "not-indexed");
11067
+ notIndexed++;
11068
+ const reason = inspection.coverageState ?? "Unknown";
11069
+ reasonBreakdown[reason] = (reasonBreakdown[reason] ?? 0) + 1;
11070
+ }
11071
+ }
11072
+ return { states, indexed, notIndexed, unknown, verifiedByInspection, derivedFromImpressions, reasonBreakdown };
11073
+ }
11074
+
11017
11075
  // ../contracts/src/query-normalize.ts
11018
11076
  function normalizeQueryText(value) {
11019
11077
  return value.trim().toLowerCase();
@@ -14162,6 +14220,7 @@ export {
14162
14220
  CheckCategories,
14163
14221
  doctorReportSchema,
14164
14222
  summarizeCheckResults,
14223
+ deriveIndexCoverage,
14165
14224
  normalizeQueryText,
14166
14225
  citationVisibilityResponseSchema,
14167
14226
  emptyCitationVisibility,
package/dist/cli.js CHANGED
@@ -30,7 +30,7 @@ import {
30
30
  showFirstRunNotice,
31
31
  trackCliCommandFinished,
32
32
  trackEvent
33
- } from "./chunk-CQBXLHT4.js";
33
+ } from "./chunk-4VNOYFCG.js";
34
34
  import {
35
35
  CliError,
36
36
  EXIT_SYSTEM_ERROR,
@@ -50,7 +50,7 @@ import {
50
50
  saveConfigPatch,
51
51
  systemError,
52
52
  usageError
53
- } from "./chunk-IGPUEHUD.js";
53
+ } from "./chunk-B4JACCYB.js";
54
54
  import {
55
55
  apiKeys,
56
56
  createClient,
@@ -58,7 +58,7 @@ import {
58
58
  projects,
59
59
  queries,
60
60
  renderReportHtml
61
- } from "./chunk-AJOOL3D3.js";
61
+ } from "./chunk-HWK2JSV3.js";
62
62
  import {
63
63
  AdsDeliverySnapshotStatuses,
64
64
  AdsHistoricalCampaignRollupStatuses,
@@ -122,7 +122,7 @@ import {
122
122
  providerQuotaPolicySchema,
123
123
  resolveProviderInput,
124
124
  winnabilityClassSchema
125
- } from "./chunk-UOYZDJOE.js";
125
+ } from "./chunk-R6UQYXLG.js";
126
126
 
127
127
  // src/cli.ts
128
128
  import { pathToFileURL } from "url";
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createServer
3
- } from "./chunk-CQBXLHT4.js";
3
+ } from "./chunk-4VNOYFCG.js";
4
4
  import {
5
5
  loadConfig
6
- } from "./chunk-IGPUEHUD.js";
7
- import "./chunk-AJOOL3D3.js";
8
- import "./chunk-UOYZDJOE.js";
6
+ } from "./chunk-B4JACCYB.js";
7
+ import "./chunk-HWK2JSV3.js";
8
+ import "./chunk-R6UQYXLG.js";
9
9
  export {
10
10
  createServer,
11
11
  loadConfig
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  IntelligenceService
3
- } from "./chunk-AJOOL3D3.js";
4
- import "./chunk-UOYZDJOE.js";
3
+ } from "./chunk-HWK2JSV3.js";
4
+ import "./chunk-R6UQYXLG.js";
5
5
  export {
6
6
  IntelligenceService
7
7
  };
package/dist/mcp.js CHANGED
@@ -3,10 +3,10 @@ import {
3
3
  PACKAGE_VERSION,
4
4
  canonryMcpTools,
5
5
  createApiClient
6
- } from "./chunk-IGPUEHUD.js";
6
+ } from "./chunk-B4JACCYB.js";
7
7
  import {
8
8
  isReadOnlyKey
9
- } from "./chunk-UOYZDJOE.js";
9
+ } from "./chunk-R6UQYXLG.js";
10
10
 
11
11
  // src/mcp/cli.ts
12
12
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonry/canonry",
3
- "version": "4.148.5",
3
+ "version": "4.148.7",
4
4
  "type": "module",
5
5
  "description": "Agent-first open-source AEO operating platform - track how answer engines cite your domain",
6
6
  "license": "FSL-1.1-ALv2",
@@ -67,25 +67,25 @@
67
67
  "tsup": "^8.5.1",
68
68
  "tsx": "^4.19.0",
69
69
  "@ainyc/canonry-api-client": "0.0.0",
70
- "@ainyc/canonry-db": "0.0.0",
70
+ "@ainyc/canonry-config": "0.0.0",
71
71
  "@ainyc/canonry-contracts": "0.0.0",
72
72
  "@ainyc/canonry-api-routes": "0.0.0",
73
- "@ainyc/canonry-config": "0.0.0",
73
+ "@ainyc/canonry-db": "0.0.0",
74
74
  "@ainyc/canonry-integration-bing": "0.0.0",
75
75
  "@ainyc/canonry-integration-openai-ads": "0.0.0",
76
- "@ainyc/canonry-integration-cloud-run": "0.0.0",
77
- "@ainyc/canonry-integration-google": "0.0.0",
78
- "@ainyc/canonry-integration-google-business-profile": "0.0.0",
79
76
  "@ainyc/canonry-integration-commoncrawl": "0.0.0",
77
+ "@ainyc/canonry-integration-google-places": "0.0.0",
78
+ "@ainyc/canonry-integration-google-business-profile": "0.0.0",
79
+ "@ainyc/canonry-integration-google": "0.0.0",
80
+ "@ainyc/canonry-integration-cloud-run": "0.0.0",
81
+ "@ainyc/canonry-integration-traffic": "0.0.0",
80
82
  "@ainyc/canonry-integration-wordpress": "0.0.0",
81
83
  "@ainyc/canonry-intelligence": "0.0.0",
82
- "@ainyc/canonry-integration-traffic": "0.0.0",
83
- "@ainyc/canonry-integration-google-places": "0.0.0",
84
- "@ainyc/canonry-provider-cdp": "0.0.0",
85
84
  "@ainyc/canonry-provider-claude": "0.0.0",
86
85
  "@ainyc/canonry-provider-gemini": "0.0.0",
87
86
  "@ainyc/canonry-provider-local": "0.0.0",
88
87
  "@ainyc/canonry-provider-openai": "0.0.0",
88
+ "@ainyc/canonry-provider-cdp": "0.0.0",
89
89
  "@ainyc/canonry-provider-perplexity": "0.0.0"
90
90
  },
91
91
  "scripts": {