@canonry/canonry 7.24.1 → 7.25.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 (32) hide show
  1. package/assets/agent-workspace/skills/canonry/references/aeo-analysis.md +1 -1
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +6 -0
  3. package/assets/agent-workspace/skills/canonry/references/google-business-profile.md +1 -0
  4. package/assets/agent-workspace/skills/canonry/references/server-side-traffic.md +3 -2
  5. package/assets/assets/{AuditHistoryPanel-DK3zE9dO.js → AuditHistoryPanel-B-9DLSmD.js} +1 -1
  6. package/assets/assets/{BacklinksPage-WYFDcXpX.js → BacklinksPage-DPssWU5-.js} +1 -1
  7. package/assets/assets/{HistoryPage-CSlndAeS.js → HistoryPage-BAPUoHjI.js} +1 -1
  8. package/assets/assets/{MeasurementPropertyPage-CivoZOGQ.js → MeasurementPropertyPage-aTs5l1OV.js} +1 -1
  9. package/assets/assets/{ProjectPage-zidnYv65.js → ProjectPage-sVY1ETmt.js} +1 -1
  10. package/assets/assets/{RunsPage-BGMAZ_mA.js → RunsPage-D0q7ifK3.js} +1 -1
  11. package/assets/assets/{SettingsPage-ndSVVJcE.js → SettingsPage-Oo861eRm.js} +1 -1
  12. package/assets/assets/{SiteHealthSection-C7-RrsKk.js → SiteHealthSection-BMdk3Och.js} +3 -3
  13. package/assets/assets/{TrafficPage-2ANBE9Lc.js → TrafficPage-Ciot_0lN.js} +1 -1
  14. package/assets/assets/{TrafficSourceDetailPage-CLYVB4Uq.js → TrafficSourceDetailPage-D7tWadfJ.js} +1 -1
  15. package/assets/assets/{extract-error-message-BzPoRYiA.js → extract-error-message-Bb4azvlK.js} +1 -1
  16. package/assets/assets/{index-CjowCLs3.js → index-ClA5Nf2V.js} +3 -3
  17. package/assets/assets/{react-sigma_core.esm.min-BmKqjMGN.js → react-sigma_core.esm.min-C-Y9Z74U.js} +1 -1
  18. package/assets/assets/{v2-overview-adapter-C7eI2iBf.js → v2-overview-adapter-D2S25ivE.js} +1 -1
  19. package/assets/index.html +1 -1
  20. package/dist/{chunk-332IBSJJ.js → chunk-6AOIFHC6.js} +1 -1
  21. package/dist/{chunk-5ITYNLS3.js → chunk-BMBHREYM.js} +2 -2
  22. package/dist/{chunk-43FZZFOK.js → chunk-I3546GTK.js} +2 -2
  23. package/dist/{chunk-OAWFZAAJ.js → chunk-NOA7KJDB.js} +3 -3
  24. package/dist/{chunk-35IRQYDW.js → chunk-NX257AKC.js} +145 -25
  25. package/dist/{chunk-656KW4RB.js → chunk-PFEQWGJN.js} +5 -4
  26. package/dist/{chunk-H7UNFLYH.js → chunk-PKQF7BV2.js} +756 -609
  27. package/dist/cli.js +14 -14
  28. package/dist/{demo-server-NLD6B4WH.js → demo-server-RUZWYXSW.js} +4 -4
  29. package/dist/index.js +6 -6
  30. package/dist/{intelligence-service-JFHM2EAY.js → intelligence-service-TYSVXO3S.js} +2 -2
  31. package/dist/mcp.js +4 -4
  32. package/package.json +11 -11
@@ -864,10 +864,10 @@ import {
864
864
  wordpressSchemaDeployResultDtoSchema,
865
865
  wordpressSchemaStatusResultDtoSchema,
866
866
  wordpressStatusDtoSchema
867
- } from "./chunk-656KW4RB.js";
867
+ } from "./chunk-PFEQWGJN.js";
868
868
 
869
869
  // src/intelligence-service.ts
870
- import { eq as eq89, desc as desc41, asc as asc18, and as and75, ne as ne16, or as or22, inArray as inArray36, gte as gte24, lte as lte20, isNull as isNull17, sql as sql32, exists as exists2 } from "drizzle-orm";
870
+ import { eq as eq90, desc as desc42, asc as asc18, and as and76, ne as ne16, or as or23, inArray as inArray37, gte as gte24, lte as lte20, isNull as isNull17, sql as sql32, exists as exists2 } from "drizzle-orm";
871
871
 
872
872
  // ../db/src/client.ts
873
873
  import { mkdirSync } from "fs";
@@ -1771,7 +1771,34 @@ var doctorHealthState = sqliteTable("doctor_health_state", {
1771
1771
  * as changed, so the trigger falls back to the code rule for one pass rather
1772
1772
  * than paging every already-degraded project the moment this ships.
1773
1773
  */
1774
- failingSignature: text("failing_signature")
1774
+ failingSignature: text("failing_signature"),
1775
+ /**
1776
+ * The same set with each entry's check id: `status:code:id`, worst first,
1777
+ * with `warn:health.no-signal:health.no-signal` standing in for a pass where
1778
+ * no check could be graded. A check changes its code when its severity
1779
+ * changes, so only the id can tell a check that eased (fail to warn) from
1780
+ * one that cleared while another started. Kept beside `failingSignature`
1781
+ * rather than in it: a rolled-back release compares that column as a string
1782
+ * and would page every degraded project if its format changed. That release
1783
+ * leaves this column behind, so the notifier trusts it only while its
1784
+ * entries still spell `failingSignature`, and otherwise compares by code for
1785
+ * one pass. NULL on rows written before this column existed.
1786
+ */
1787
+ failingChecks: text("failing_checks"),
1788
+ /**
1789
+ * What the operator has been told is failing, in the `failingChecks` form:
1790
+ * the set reported by the last health alert a webhook accepted. A pass that
1791
+ * only clears checks sends nothing and still advances `failingChecks`, so
1792
+ * the next alert compares against this to list what cleared since the alert
1793
+ * the operator last saw. Delivery retry state lives in `pendingSignature`:
1794
+ * an earlier delivered set cannot acknowledge a later recurrence. NULL
1795
+ * while no alert has been delivered since this column exists, and reset to
1796
+ * NULL when an older release has written the row since; the notifier then
1797
+ * compares against the previous pass alone.
1798
+ */
1799
+ notifiedSignature: text("notified_signature"),
1800
+ /** Triggering breaches owed by an unaccepted degraded alert, as `status:code:id`. */
1801
+ pendingSignature: text("pending_signature")
1775
1802
  });
1776
1803
  var siteLivenessState = sqliteTable("site_liveness_state", {
1777
1804
  projectId: text("project_id").primaryKey(),
@@ -8511,6 +8538,44 @@ var MIGRATION_VERSIONS = [
8511
8538
  `CREATE INDEX IF NOT EXISTS idx_ga_search_landing_pages_order ON ga_search_landing_pages(project_id, window_key, clicks)`,
8512
8539
  `CREATE INDEX IF NOT EXISTS idx_ga_search_landing_pages_run ON ga_search_landing_pages(sync_run_id)`
8513
8540
  ]
8541
+ },
8542
+ {
8543
+ // The failing set the last DELIVERED health alert reported. A pass that
8544
+ // only clears checks now sends nothing, so the stored signature moves on
8545
+ // without the operator hearing about it; the next alert reads this to say
8546
+ // what cleared since the one they last saw, and an alert no webhook
8547
+ // accepted is sent again. Nullable with no default: existing rows, and
8548
+ // rows an older writer keeps updating, read as "no alert on record" and
8549
+ // fall back to the previous pass.
8550
+ version: 177,
8551
+ name: "doctor-health-notified-signature",
8552
+ statements: [
8553
+ `ALTER TABLE doctor_health_state ADD COLUMN notified_signature TEXT`
8554
+ ]
8555
+ },
8556
+ {
8557
+ // The last pass's failing set with check ids (`status:code:id`). A check
8558
+ // changes its code when its severity changes, so without the id a check
8559
+ // easing from fail to warn read as one entry cleared and another gained,
8560
+ // and paged. It sits beside `failing_signature` instead of changing that
8561
+ // column's format: an older binary compares it as a string, and would page
8562
+ // every degraded project after a rollback. Nullable with no default: rows
8563
+ // without it are compared by code for one pass.
8564
+ version: 178,
8565
+ name: "doctor-health-failing-checks",
8566
+ statements: [
8567
+ `ALTER TABLE doctor_health_state ADD COLUMN failing_checks TEXT`
8568
+ ]
8569
+ },
8570
+ {
8571
+ // Delivery debt is separate from the last delivered clearance baseline:
8572
+ // a check can clear quietly and recur with a code that baseline knows.
8573
+ // NULL preserves existing rows without inventing an undelivered alert.
8574
+ version: 179,
8575
+ name: "doctor-health-pending-signature",
8576
+ statements: [
8577
+ `ALTER TABLE doctor_health_state ADD COLUMN pending_signature TEXT`
8578
+ ]
8514
8579
  }
8515
8580
  ];
8516
8581
  var AERO_LEDGER_REBUILDS = [
@@ -15903,6 +15968,8 @@ var AlertFieldLabels = {
15903
15968
  check: "Check",
15904
15969
  previously: "Was",
15905
15970
  remediation: "What to do",
15971
+ newlyFailing: "New",
15972
+ cleared: "Cleared",
15906
15973
  alsoFailing: "Also failing",
15907
15974
  event: "Event",
15908
15975
  run: "Run",
@@ -15940,7 +16007,24 @@ function healthView(payload) {
15940
16007
  if (health.remediation) {
15941
16008
  fields.push({ label: AlertFieldLabels.remediation, value: health.remediation });
15942
16009
  }
15943
- const others = health.failing.filter((check2) => check2.code !== health.code);
16010
+ const added = health.added ?? [];
16011
+ if (added.length > 0) {
16012
+ const statusOf = /* @__PURE__ */ new Map();
16013
+ for (const check2 of health.failing) if (!statusOf.has(check2.code)) statusOf.set(check2.code, check2.status);
16014
+ fields.push({
16015
+ label: `${AlertFieldLabels.newlyFailing} (${added.length})`,
16016
+ value: bulletList(added.map((code) => `${statusOf.get(code) ?? health.status}: ${code}`))
16017
+ });
16018
+ }
16019
+ const cleared = health.cleared ?? [];
16020
+ if (cleared.length > 0) {
16021
+ fields.push({
16022
+ label: `${AlertFieldLabels.cleared} (${cleared.length})`,
16023
+ value: bulletList(cleared)
16024
+ });
16025
+ }
16026
+ const newlyFailing = new Set(added);
16027
+ const others = health.failing.filter((check2) => check2.code !== health.code && !newlyFailing.has(check2.code));
15944
16028
  if (others.length > 0) {
15945
16029
  fields.push({
15946
16030
  label: `${AlertFieldLabels.alsoFailing} (${others.length})`,
@@ -62964,7 +63048,7 @@ async function wordpressRoutes(app, opts) {
62964
63048
 
62965
63049
  // ../api-routes/src/backlinks.ts
62966
63050
  import crypto45 from "crypto";
62967
- import { and as and56, asc as asc14, desc as desc34, eq as eq65, sql as sql25 } from "drizzle-orm";
63051
+ import { and as and57, asc as asc14, desc as desc34, eq as eq66, sql as sql25 } from "drizzle-orm";
62968
63052
 
62969
63053
  // ../integration-commoncrawl/src/constants.ts
62970
63054
  import os2 from "os";
@@ -63395,6 +63479,22 @@ function backlinkCrawlerExclusionClause() {
63395
63479
  return combined;
63396
63480
  }
63397
63481
 
63482
+ // ../api-routes/src/backlinks-source-state.ts
63483
+ import { and as and56, eq as eq65, isNotNull as isNotNull8, or as or17 } from "drizzle-orm";
63484
+ function readCommonCrawlSourceState(db, projectId) {
63485
+ const completedReleases = db.select({ status: ccReleaseSyncs.status }).from(ccReleaseSyncs).where(or17(
63486
+ eq65(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready),
63487
+ isNotNull8(ccReleaseSyncs.queryFinishedAt)
63488
+ )).all();
63489
+ const hasReadyRelease = completedReleases.length > 0;
63490
+ const hasCurrentReadyRelease = completedReleases.some((release) => release.status === CcReleaseSyncStatuses.ready);
63491
+ const hasProjectData = db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and56(
63492
+ eq65(backlinkSummaries.projectId, projectId),
63493
+ eq65(backlinkSummaries.source, BacklinkSources.commoncrawl)
63494
+ )).limit(1).get() !== void 0;
63495
+ return { hasReadyRelease, hasCurrentReadyRelease, hasProjectData, connected: hasReadyRelease && hasProjectData };
63496
+ }
63497
+
63398
63498
  // ../api-routes/src/backlinks.ts
63399
63499
  var BACKLINKS_UNSUPPORTED_MESSAGE = "Backlinks sync and install are only available from a local canonry install. Run `canonry backlinks install` locally to use this feature.";
63400
63500
  var NON_TERMINAL_SYNC_STATUSES = /* @__PURE__ */ new Set([
@@ -63461,11 +63561,11 @@ function mapRunRow(row) {
63461
63561
  }
63462
63562
  function latestSummaryForProject(db, projectId, source, release) {
63463
63563
  const conditions = [
63464
- eq65(backlinkSummaries.projectId, projectId),
63465
- eq65(backlinkSummaries.source, source)
63564
+ eq66(backlinkSummaries.projectId, projectId),
63565
+ eq66(backlinkSummaries.source, source)
63466
63566
  ];
63467
- if (release) conditions.push(eq65(backlinkSummaries.release, release));
63468
- return db.select().from(backlinkSummaries).where(and56(...conditions)).orderBy(desc34(backlinkSummaries.queriedAt)).limit(1).get();
63567
+ if (release) conditions.push(eq66(backlinkSummaries.release, release));
63568
+ return db.select().from(backlinkSummaries).where(and57(...conditions)).orderBy(desc34(backlinkSummaries.queriedAt)).limit(1).get();
63469
63569
  }
63470
63570
  function parseExcludeCrawlers(value) {
63471
63571
  if (!value) return false;
@@ -63473,12 +63573,12 @@ function parseExcludeCrawlers(value) {
63473
63573
  return lower === "1" || lower === "true" || lower === "yes";
63474
63574
  }
63475
63575
  function computeFilteredSummary(db, base) {
63476
- const baseDomainCondition = and56(
63477
- eq65(backlinkDomains.projectId, base.projectId),
63478
- eq65(backlinkDomains.source, base.source),
63479
- eq65(backlinkDomains.release, base.release)
63576
+ const baseDomainCondition = and57(
63577
+ eq66(backlinkDomains.projectId, base.projectId),
63578
+ eq66(backlinkDomains.source, base.source),
63579
+ eq66(backlinkDomains.release, base.release)
63480
63580
  );
63481
- const filteredCondition = and56(baseDomainCondition, backlinkCrawlerExclusionClause());
63581
+ const filteredCondition = and57(baseDomainCondition, backlinkCrawlerExclusionClause());
63482
63582
  const unfilteredAgg = db.select({
63483
63583
  count: sql25`count(*)`,
63484
63584
  total: sql25`coalesce(sum(${backlinkDomains.numHosts}), 0)`
@@ -63508,13 +63608,13 @@ function computeFilteredSummary(db, base) {
63508
63608
  };
63509
63609
  }
63510
63610
  function buildSourceAvailability(db, projectId, source, connected, excludeCrawlers) {
63511
- const summary3 = db.select().from(backlinkSummaries).where(and56(eq65(backlinkSummaries.projectId, projectId), eq65(backlinkSummaries.source, source))).orderBy(desc34(backlinkSummaries.queriedAt)).limit(1).get();
63611
+ const summary3 = db.select().from(backlinkSummaries).where(and57(eq66(backlinkSummaries.projectId, projectId), eq66(backlinkSummaries.source, source))).orderBy(desc34(backlinkSummaries.queriedAt)).limit(1).get();
63512
63612
  let totalLinkingDomains = summary3?.totalLinkingDomains ?? 0;
63513
63613
  if (summary3 && excludeCrawlers) {
63514
- const filtered = db.select({ count: sql25`count(*)` }).from(backlinkDomains).where(and56(
63515
- eq65(backlinkDomains.projectId, projectId),
63516
- eq65(backlinkDomains.source, source),
63517
- eq65(backlinkDomains.release, summary3.release),
63614
+ const filtered = db.select({ count: sql25`count(*)` }).from(backlinkDomains).where(and57(
63615
+ eq66(backlinkDomains.projectId, projectId),
63616
+ eq66(backlinkDomains.source, source),
63617
+ eq66(backlinkDomains.release, summary3.release),
63518
63618
  backlinkCrawlerExclusionClause()
63519
63619
  )).get();
63520
63620
  totalLinkingDomains = Number(filtered?.count ?? 0);
@@ -63529,8 +63629,7 @@ function buildSourceAvailability(db, projectId, source, connected, excludeCrawle
63529
63629
  };
63530
63630
  }
63531
63631
  function computeSourceAvailability(db, project, excludeCrawlers) {
63532
- const ccReadySync = db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq65(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
63533
- const ccConnected = project.autoExtractBacklinks === true && !!ccReadySync;
63632
+ const ccConnected = readCommonCrawlSourceState(db, project.id).connected;
63534
63633
  const sources = [
63535
63634
  buildSourceAvailability(db, project.id, BacklinkSources.commoncrawl, ccConnected, excludeCrawlers),
63536
63635
  buildSourceAvailability(db, project.id, BacklinkSources["bing-webmaster"], false, excludeCrawlers)
@@ -63585,7 +63684,7 @@ async function backlinksRoutes(app, opts) {
63585
63684
  "@duckdb/node-api is not installed. Run `canonry backlinks install` to enable the backlinks feature."
63586
63685
  );
63587
63686
  }
63588
- const existing = app.db.select().from(ccReleaseSyncs).where(eq65(ccReleaseSyncs.release, release)).get();
63687
+ const existing = app.db.select().from(ccReleaseSyncs).where(eq66(ccReleaseSyncs.release, release)).get();
63589
63688
  const now = (/* @__PURE__ */ new Date()).toISOString();
63590
63689
  if (existing) {
63591
63690
  if (NON_TERMINAL_SYNC_STATUSES.has(existing.status)) {
@@ -63596,9 +63695,9 @@ async function backlinksRoutes(app, opts) {
63596
63695
  phaseDetail: null,
63597
63696
  error: null,
63598
63697
  updatedAt: now
63599
- }).where(eq65(ccReleaseSyncs.id, existing.id)).run();
63698
+ }).where(eq66(ccReleaseSyncs.id, existing.id)).run();
63600
63699
  opts.onReleaseSyncRequested(existing.id, release);
63601
- const refreshed = app.db.select().from(ccReleaseSyncs).where(eq65(ccReleaseSyncs.id, existing.id)).get();
63700
+ const refreshed = app.db.select().from(ccReleaseSyncs).where(eq66(ccReleaseSyncs.id, existing.id)).get();
63602
63701
  return reply.status(200).send(mapSyncRow(refreshed));
63603
63702
  }
63604
63703
  const id = crypto45.randomUUID();
@@ -63610,7 +63709,7 @@ async function backlinksRoutes(app, opts) {
63610
63709
  updatedAt: now
63611
63710
  }).run();
63612
63711
  opts.onReleaseSyncRequested(id, release);
63613
- const inserted = app.db.select().from(ccReleaseSyncs).where(eq65(ccReleaseSyncs.id, id)).get();
63712
+ const inserted = app.db.select().from(ccReleaseSyncs).where(eq66(ccReleaseSyncs.id, id)).get();
63614
63713
  return reply.status(201).send(mapSyncRow(inserted));
63615
63714
  });
63616
63715
  app.get("/backlinks/syncs/latest", async (_request, reply) => {
@@ -63668,7 +63767,7 @@ async function backlinksRoutes(app, opts) {
63668
63767
  createdAt: now
63669
63768
  }).run();
63670
63769
  opts.onBacklinkExtractRequested(runId, project.id, release);
63671
- const run = app.db.select().from(runs).where(eq65(runs.id, runId)).get();
63770
+ const run = app.db.select().from(runs).where(eq66(runs.id, runId)).get();
63672
63771
  return reply.status(201).send(mapRunRow(run));
63673
63772
  });
63674
63773
  app.get(
@@ -63694,12 +63793,12 @@ async function backlinksRoutes(app, opts) {
63694
63793
  const limit = Math.min(Math.max(parseInt(request.query.limit ?? "50", 10) || 50, 1), 500);
63695
63794
  const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
63696
63795
  const excludeCrawlers = parseExcludeCrawlers(request.query.excludeCrawlers);
63697
- const baseDomainCondition = and56(
63698
- eq65(backlinkDomains.projectId, project.id),
63699
- eq65(backlinkDomains.source, source),
63700
- eq65(backlinkDomains.release, targetRelease)
63796
+ const baseDomainCondition = and57(
63797
+ eq66(backlinkDomains.projectId, project.id),
63798
+ eq66(backlinkDomains.source, source),
63799
+ eq66(backlinkDomains.release, targetRelease)
63701
63800
  );
63702
- const domainCondition = excludeCrawlers ? and56(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
63801
+ const domainCondition = excludeCrawlers ? and57(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
63703
63802
  const totalRow = app.db.select({ count: sql25`count(*)` }).from(backlinkDomains).where(domainCondition).get();
63704
63803
  const rows = app.db.select({
63705
63804
  linkingDomain: backlinkDomains.linkingDomain,
@@ -63723,7 +63822,7 @@ async function backlinksRoutes(app, opts) {
63723
63822
  async (request, reply) => {
63724
63823
  const project = resolveProject(app.db, request.params.name);
63725
63824
  const source = parseSourceParam(request.query.source);
63726
- const rows = app.db.select().from(backlinkSummaries).where(and56(eq65(backlinkSummaries.projectId, project.id), eq65(backlinkSummaries.source, source))).orderBy(asc14(backlinkSummaries.queriedAt)).all();
63825
+ const rows = app.db.select().from(backlinkSummaries).where(and57(eq66(backlinkSummaries.projectId, project.id), eq66(backlinkSummaries.source, source))).orderBy(asc14(backlinkSummaries.queriedAt)).all();
63727
63826
  const response = rows.map((r) => ({
63728
63827
  release: r.release,
63729
63828
  totalLinkingDomains: r.totalLinkingDomains,
@@ -63750,7 +63849,7 @@ async function backlinksRoutes(app, opts) {
63750
63849
  import crypto48 from "crypto";
63751
63850
  import { isIP } from "net";
63752
63851
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
63753
- import { and as and60, desc as desc35, eq as eq69, gte as gte16, inArray as inArray28, lte as lte17, sql as sql28 } from "drizzle-orm";
63852
+ import { and as and61, desc as desc35, eq as eq70, gte as gte16, inArray as inArray28, lte as lte17, sql as sql28 } from "drizzle-orm";
63754
63853
 
63755
63854
  // ../api-routes/src/cloudflare-worker-version.ts
63756
63855
  var CURRENT_CLOUDFLARE_WORKER_VERSION = "1.0.1";
@@ -80416,10 +80515,10 @@ async function ackCloudflareQueueMessages(client, options) {
80416
80515
 
80417
80516
  // ../api-routes/src/traffic-event-ingest.ts
80418
80517
  import crypto47 from "crypto";
80419
- import { and as and58, eq as eq67, gte as gte15, lt as lt12, lte as lte15, sql as sql27 } from "drizzle-orm";
80518
+ import { and as and59, eq as eq68, gte as gte15, lt as lt12, lte as lte15, sql as sql27 } from "drizzle-orm";
80420
80519
 
80421
80520
  // ../api-routes/src/raw-event-sample-retention.ts
80422
- import { and as and57, eq as eq66, lt as lt11, sql as sql26 } from "drizzle-orm";
80521
+ import { and as and58, eq as eq67, lt as lt11, sql as sql26 } from "drizzle-orm";
80423
80522
  var RAW_EVENT_SAMPLE_RETENTION_MS = 30 * 24 * 60 * 6e4;
80424
80523
  var RAW_EVENT_SAMPLE_RETENTION_SWEEP_INTERVAL_MS = 24 * 60 * 6e4;
80425
80524
  var CANONICAL_UTC_TIMESTAMP_GLOB = "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9]Z";
@@ -80429,14 +80528,14 @@ function rawEventSampleRetentionCutoff(referenceAt) {
80429
80528
  return new Date(referenceMs - RAW_EVENT_SAMPLE_RETENTION_MS).toISOString();
80430
80529
  }
80431
80530
  function deleteExpiredRawEventSamples(db, cutoff, sourceId) {
80432
- const sourceScope = sourceId === void 0 ? void 0 : eq66(rawEventSamples.sourceId, sourceId);
80433
- db.delete(rawEventSamples).where(and57(sourceScope, sql26`julianday(${rawEventSamples.ts}) IS NULL`)).run();
80434
- db.delete(rawEventSamples).where(and57(
80531
+ const sourceScope = sourceId === void 0 ? void 0 : eq67(rawEventSamples.sourceId, sourceId);
80532
+ db.delete(rawEventSamples).where(and58(sourceScope, sql26`julianday(${rawEventSamples.ts}) IS NULL`)).run();
80533
+ db.delete(rawEventSamples).where(and58(
80435
80534
  sourceScope,
80436
80535
  sql26`${rawEventSamples.ts} GLOB ${CANONICAL_UTC_TIMESTAMP_GLOB}`,
80437
80536
  lt11(rawEventSamples.ts, cutoff)
80438
80537
  )).run();
80439
- db.delete(rawEventSamples).where(and57(
80538
+ db.delete(rawEventSamples).where(and58(
80440
80539
  sourceScope,
80441
80540
  sql26`${rawEventSamples.ts} NOT GLOB ${CANONICAL_UTC_TIMESTAMP_GLOB}`,
80442
80541
  sql26`julianday(${rawEventSamples.ts}) < julianday(${cutoff})`
@@ -80485,16 +80584,16 @@ function writeTrafficEventBatch(opts) {
80485
80584
  let aiReferralHits = 0;
80486
80585
  let unknownHits = 0;
80487
80586
  opts.db.transaction((tx) => {
80488
- tx.update(trafficSources).set({ updatedAt: sql27`${trafficSources.updatedAt}` }).where(eq67(trafficSources.id, opts.sourceId)).run();
80489
- const source = tx.select().from(trafficSources).where(eq67(trafficSources.id, opts.sourceId)).get();
80587
+ tx.update(trafficSources).set({ updatedAt: sql27`${trafficSources.updatedAt}` }).where(eq68(trafficSources.id, opts.sourceId)).run();
80588
+ const source = tx.select().from(trafficSources).where(eq68(trafficSources.id, opts.sourceId)).get();
80490
80589
  opts.validateSource(source);
80491
80590
  const rawSampleCutoff = enforceRawEventSampleRetention(
80492
80591
  tx,
80493
80592
  opts.sourceId,
80494
80593
  opts.receivedAt
80495
80594
  );
80496
- tx.delete(trafficEventReceipts).where(and58(
80497
- eq67(trafficEventReceipts.sourceId, opts.sourceId),
80595
+ tx.delete(trafficEventReceipts).where(and59(
80596
+ eq68(trafficEventReceipts.sourceId, opts.sourceId),
80498
80597
  lte15(trafficEventReceipts.expiresAt, opts.receivedAt)
80499
80598
  )).run();
80500
80599
  const claimedEvents = [];
@@ -80693,8 +80792,8 @@ function writeTrafficEventBatch(opts) {
80693
80792
  if (!hour || hourlySampleLimit === 0) continue;
80694
80793
  let remaining = remainingSamplesByHour.get(hour.start);
80695
80794
  if (remaining === void 0) {
80696
- const existing = tx.select({ total: sql27`COUNT(*)` }).from(rawEventSamples).where(and58(
80697
- eq67(rawEventSamples.sourceId, opts.sourceId),
80795
+ const existing = tx.select({ total: sql27`COUNT(*)` }).from(rawEventSamples).where(and59(
80796
+ eq68(rawEventSamples.sourceId, opts.sourceId),
80698
80797
  gte15(rawEventSamples.ts, hour.start),
80699
80798
  lt12(rawEventSamples.ts, hour.end)
80700
80799
  )).get();
@@ -80735,7 +80834,7 @@ function writeTrafficEventBatch(opts) {
80735
80834
  remainingSamplesByHour.set(hour.start, remaining - 1);
80736
80835
  }
80737
80836
  }
80738
- tx.update(trafficSources).set(opts.sourceUpdate).where(eq67(trafficSources.id, opts.sourceId)).run();
80837
+ tx.update(trafficSources).set(opts.sourceUpdate).where(eq68(trafficSources.id, opts.sourceId)).run();
80739
80838
  });
80740
80839
  return {
80741
80840
  acceptedEvents,
@@ -80753,7 +80852,7 @@ function writeTrafficEventBatch(opts) {
80753
80852
  }
80754
80853
 
80755
80854
  // ../api-routes/src/traffic-sync-lease.ts
80756
- import { and as and59, eq as eq68, isNull as isNull13, lte as lte16, or as or17 } from "drizzle-orm";
80855
+ import { and as and60, eq as eq69, isNull as isNull13, lte as lte16, or as or18 } from "drizzle-orm";
80757
80856
  function leaseExpiresAt(now, ttlMs) {
80758
80857
  const nowMs = Date.parse(now);
80759
80858
  if (!Number.isFinite(nowMs)) throw new RangeError("Traffic sync lease now must be an ISO instant");
@@ -80766,12 +80865,12 @@ function tryClaimTrafficSyncLease(input) {
80766
80865
  const changed = tx.update(trafficSources).set({
80767
80866
  syncLeaseOwner: input.owner,
80768
80867
  syncLeaseExpiresAt: expiresAt
80769
- }).where(and59(
80770
- eq68(trafficSources.id, input.sourceId),
80771
- or17(
80868
+ }).where(and60(
80869
+ eq69(trafficSources.id, input.sourceId),
80870
+ or18(
80772
80871
  isNull13(trafficSources.syncLeaseOwner),
80773
80872
  lte16(trafficSources.syncLeaseExpiresAt, input.now),
80774
- eq68(trafficSources.syncLeaseOwner, input.owner)
80873
+ eq69(trafficSources.syncLeaseOwner, input.owner)
80775
80874
  )
80776
80875
  )).run();
80777
80876
  return changed.changes === 1;
@@ -80782,9 +80881,9 @@ function releaseTrafficSyncLease(input) {
80782
80881
  const changed = tx.update(trafficSources).set({
80783
80882
  syncLeaseOwner: null,
80784
80883
  syncLeaseExpiresAt: null
80785
- }).where(and59(
80786
- eq68(trafficSources.id, input.sourceId),
80787
- eq68(trafficSources.syncLeaseOwner, input.owner)
80884
+ }).where(and60(
80885
+ eq69(trafficSources.id, input.sourceId),
80886
+ eq69(trafficSources.syncLeaseOwner, input.owner)
80788
80887
  )).run();
80789
80888
  return changed.changes === 1;
80790
80889
  }, { behavior: "immediate" });
@@ -80865,7 +80964,7 @@ function cloudflareIngestRateLimitKey(request, store, db) {
80865
80964
  const authenticated = authenticateCloudflareIngest(request, store);
80866
80965
  const params = request.params;
80867
80966
  if (authenticated && typeof params.name === "string" && authenticated.credential.projectName === params.name) {
80868
- const source = db.select().from(trafficSources).where(eq69(trafficSources.id, authenticated.credential.sourceId)).get();
80967
+ const source = db.select().from(trafficSources).where(eq70(trafficSources.id, authenticated.credential.sourceId)).get();
80869
80968
  if (source && source.sourceType === TrafficSourceTypes.cloudflare && source.status === TrafficSourceStatuses.connected && parseDirectPushCloudflareSourceConfig(source.configJson) && timingSafeEqualHex(authenticated.bearerHash, source.ingestTokenHash)) {
80870
80969
  return `cloudflare-source:${authenticated.credential.sourceId}`;
80871
80970
  }
@@ -81108,7 +81207,7 @@ async function defaultResolveAccessToken(record) {
81108
81207
  );
81109
81208
  }
81110
81209
  function hasConnectedTrafficSourceSibling(db, projectId, sourceId) {
81111
- return db.select().from(trafficSources).where(eq69(trafficSources.projectId, projectId)).all().some((row) => row.id !== sourceId && row.status === TrafficSourceStatuses.connected);
81210
+ return db.select().from(trafficSources).where(eq70(trafficSources.projectId, projectId)).all().some((row) => row.id !== sourceId && row.status === TrafficSourceStatuses.connected);
81112
81211
  }
81113
81212
  function isAuthoritativeTrafficSource(db, source) {
81114
81213
  return (source.status === TrafficSourceStatuses.connected || source.status === TrafficSourceStatuses.error) && !hasConnectedTrafficSourceSibling(db, source.projectId, source.id);
@@ -81132,13 +81231,13 @@ function wordpressBaseUrlIdentity(value) {
81132
81231
  }
81133
81232
  }
81134
81233
  function bindTrafficSyncSchedule(tx, projectId, sourceId, now, createIfMissing = true) {
81135
- const schedule = tx.select().from(schedules).where(and60(
81136
- eq69(schedules.projectId, projectId),
81137
- eq69(schedules.kind, SchedulableRunKinds["traffic-sync"])
81234
+ const schedule = tx.select().from(schedules).where(and61(
81235
+ eq70(schedules.projectId, projectId),
81236
+ eq70(schedules.kind, SchedulableRunKinds["traffic-sync"])
81138
81237
  )).get();
81139
81238
  if (schedule) {
81140
81239
  if (schedule.sourceId === sourceId) return { changed: false, created: false };
81141
- tx.update(schedules).set({ sourceId, updatedAt: nextScheduleUpdatedAt(schedule.updatedAt, Date.parse(now)) }).where(eq69(schedules.id, schedule.id)).run();
81240
+ tx.update(schedules).set({ sourceId, updatedAt: nextScheduleUpdatedAt(schedule.updatedAt, Date.parse(now)) }).where(eq70(schedules.id, schedule.id)).run();
81142
81241
  return { changed: true, created: false };
81143
81242
  }
81144
81243
  if (!createIfMissing) return { changed: false, created: false };
@@ -81189,10 +81288,10 @@ async function runBackfillTask(options) {
81189
81288
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
81190
81289
  try {
81191
81290
  app.db.transaction((tx) => {
81192
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq69(runs.id, runId)).run();
81193
- const latestSource = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
81291
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq70(runs.id, runId)).run();
81292
+ const latestSource = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
81194
81293
  if (!preserveSourceState && latestSource && isAuthoritativeTrafficSource(tx, latestSource) && isSameTrafficSourceGeneration(latestSource, sourceRow2)) {
81195
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq69(trafficSources.id, sourceRow2.id)).run();
81294
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq70(trafficSources.id, sourceRow2.id)).run();
81196
81295
  }
81197
81296
  });
81198
81297
  } catch {
@@ -81219,7 +81318,7 @@ async function runBackfillTask(options) {
81219
81318
  let emptyOutcome;
81220
81319
  try {
81221
81320
  const completed = app.db.transaction((tx) => {
81222
- const latestSource = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
81321
+ const latestSource = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
81223
81322
  const stillAuthoritative = latestSource && isAuthoritativeTrafficSource(tx, latestSource) && isSameTrafficSourceGeneration(latestSource, sourceRow2);
81224
81323
  if (stillAuthoritative) {
81225
81324
  enforceRawEventSampleRetention(tx, sourceRow2.id, finishedAt2);
@@ -81228,7 +81327,7 @@ async function runBackfillTask(options) {
81228
81327
  status: RunStatuses.failed,
81229
81328
  error: "Traffic source was deactivated or reconfigured during backfill",
81230
81329
  finishedAt: finishedAt2
81231
- }).where(eq69(runs.id, runId)).run();
81330
+ }).where(eq70(runs.id, runId)).run();
81232
81331
  return Boolean(stillAuthoritative);
81233
81332
  });
81234
81333
  emptyOutcome = completed ? { status: OutcomeStatuses.skipped, reasonCode: OutcomeReasonCodes.NO_DATA } : { status: OutcomeStatuses.failed, reasonCode: OutcomeReasonCodes.NOT_CONNECTED };
@@ -81254,13 +81353,13 @@ async function runBackfillTask(options) {
81254
81353
  const nextLastSyncedAt = Math.max(currentLastSyncedMs, windowEnd.getTime()) === windowEnd.getTime() ? windowEndIso : sourceRow2.lastSyncedAt;
81255
81354
  try {
81256
81355
  const commitOutcome = app.db.transaction((tx) => {
81257
- const latestSource = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
81356
+ const latestSource = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
81258
81357
  if (latestSource?.sourceType === TrafficSourceTypes.wordpress) {
81259
81358
  tx.update(runs).set({
81260
81359
  status: RunStatuses.failed,
81261
81360
  error: "Generic WordPress replace backfill is unavailable because retained coverage is unproven.",
81262
81361
  finishedAt
81263
- }).where(eq69(runs.id, runId)).run();
81362
+ }).where(eq70(runs.id, runId)).run();
81264
81363
  return "wordpress-backfill-unsupported";
81265
81364
  }
81266
81365
  if (!latestSource || !isAuthoritativeTrafficSource(tx, latestSource) || !isSameTrafficSourceGeneration(latestSource, sourceRow2)) {
@@ -81268,7 +81367,7 @@ async function runBackfillTask(options) {
81268
81367
  status: RunStatuses.failed,
81269
81368
  error: "Traffic source was deactivated or reconfigured during backfill",
81270
81369
  finishedAt
81271
- }).where(eq69(runs.id, runId)).run();
81370
+ }).where(eq70(runs.id, runId)).run();
81272
81371
  return "source-inactive";
81273
81372
  }
81274
81373
  const rawSampleCutoff = enforceRawEventSampleRetention(
@@ -81277,29 +81376,29 @@ async function runBackfillTask(options) {
81277
81376
  finishedAt
81278
81377
  );
81279
81378
  tx.delete(crawlerEventsHourly).where(
81280
- and60(
81281
- eq69(crawlerEventsHourly.sourceId, sourceRow2.id),
81379
+ and61(
81380
+ eq70(crawlerEventsHourly.sourceId, sourceRow2.id),
81282
81381
  gte16(crawlerEventsHourly.tsHour, windowStartIso),
81283
81382
  lte17(crawlerEventsHourly.tsHour, windowEndIso)
81284
81383
  )
81285
81384
  ).run();
81286
81385
  tx.delete(aiUserFetchEventsHourly).where(
81287
- and60(
81288
- eq69(aiUserFetchEventsHourly.sourceId, sourceRow2.id),
81386
+ and61(
81387
+ eq70(aiUserFetchEventsHourly.sourceId, sourceRow2.id),
81289
81388
  gte16(aiUserFetchEventsHourly.tsHour, windowStartIso),
81290
81389
  lte17(aiUserFetchEventsHourly.tsHour, windowEndIso)
81291
81390
  )
81292
81391
  ).run();
81293
81392
  tx.delete(aiReferralEventsHourly).where(
81294
- and60(
81295
- eq69(aiReferralEventsHourly.sourceId, sourceRow2.id),
81393
+ and61(
81394
+ eq70(aiReferralEventsHourly.sourceId, sourceRow2.id),
81296
81395
  gte16(aiReferralEventsHourly.tsHour, windowStartIso),
81297
81396
  lte17(aiReferralEventsHourly.tsHour, windowEndIso)
81298
81397
  )
81299
81398
  ).run();
81300
81399
  tx.delete(rawEventSamples).where(
81301
- and60(
81302
- eq69(rawEventSamples.sourceId, sourceRow2.id),
81400
+ and61(
81401
+ eq70(rawEventSamples.sourceId, sourceRow2.id),
81303
81402
  gte16(rawEventSamples.ts, windowStartIso),
81304
81403
  lte17(rawEventSamples.ts, windowEndIso)
81305
81404
  )
@@ -81491,8 +81590,8 @@ async function runBackfillTask(options) {
81491
81590
  lastEventIds: newRingBuffer,
81492
81591
  ...skipRecovered ? { skippedThroughAt: null } : {},
81493
81592
  updatedAt: finishedAt
81494
- }).where(eq69(trafficSources.id, sourceRow2.id)).run();
81495
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq69(runs.id, runId)).run();
81593
+ }).where(eq70(trafficSources.id, sourceRow2.id)).run();
81594
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq70(runs.id, runId)).run();
81496
81595
  return "committed";
81497
81596
  });
81498
81597
  reportBackfill(commitOutcome === "committed" ? {
@@ -81654,7 +81753,7 @@ async function trafficRoutes(app, opts) {
81654
81753
  };
81655
81754
  const fallbackName = displayName ?? `Cloud Run \xB7 ${gcpProjectId}${serviceName ? ` / ${serviceName}` : ""}`;
81656
81755
  const { sourceRow: sourceRow2, scheduleChanged } = app.db.transaction((tx) => {
81657
- const activeSource = tx.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes["cloud-run"] && row.status !== TrafficSourceStatuses.archived);
81756
+ const activeSource = tx.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes["cloud-run"] && row.status !== TrafficSourceStatuses.archived);
81658
81757
  const sourceId = activeSource?.id ?? crypto48.randomUUID();
81659
81758
  const sourceStatus = trafficConnectStatus(tx, project.id, activeSource);
81660
81759
  if (activeSource) {
@@ -81664,7 +81763,7 @@ async function trafficRoutes(app, opts) {
81664
81763
  lastError: null,
81665
81764
  configJson: config,
81666
81765
  updatedAt: now
81667
- }).where(eq69(trafficSources.id, sourceId)).run();
81766
+ }).where(eq70(trafficSources.id, sourceId)).run();
81668
81767
  } else {
81669
81768
  tx.insert(trafficSources).values({
81670
81769
  id: sourceId,
@@ -81690,7 +81789,7 @@ async function trafficRoutes(app, opts) {
81690
81789
  entityId: sourceId
81691
81790
  });
81692
81791
  return {
81693
- sourceRow: tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceId)).get(),
81792
+ sourceRow: tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceId)).get(),
81694
81793
  scheduleChanged: scheduleBinding.changed
81695
81794
  };
81696
81795
  }, { behavior: "immediate" });
@@ -81745,7 +81844,7 @@ async function trafficRoutes(app, opts) {
81745
81844
  const config = { baseUrl, username };
81746
81845
  const fallbackName = displayName ?? `WordPress \xB7 ${new URL(baseUrl).host}`;
81747
81846
  const { sourceRow: sourceRow2, scheduleChanged } = app.db.transaction((tx) => {
81748
- const activeSource = tx.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
81847
+ const activeSource = tx.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
81749
81848
  const endpointChanged = activeSource !== void 0 && wordpressBaseUrlIdentity(activeSource.configJson.baseUrl) !== wordpressBaseUrlIdentity(baseUrl);
81750
81849
  const sourceId = endpointChanged ? crypto48.randomUUID() : activeSource?.id ?? crypto48.randomUUID();
81751
81850
  const sourceStatus = trafficConnectStatus(tx, project.id, activeSource);
@@ -81756,14 +81855,14 @@ async function trafficRoutes(app, opts) {
81756
81855
  lastError: null,
81757
81856
  configJson: config,
81758
81857
  updatedAt: now
81759
- }).where(eq69(trafficSources.id, sourceId)).run();
81858
+ }).where(eq70(trafficSources.id, sourceId)).run();
81760
81859
  } else {
81761
81860
  if (activeSource) {
81762
81861
  tx.update(trafficSources).set({
81763
81862
  status: TrafficSourceStatuses.archived,
81764
81863
  archivedAt: now,
81765
81864
  updatedAt: now
81766
- }).where(eq69(trafficSources.id, activeSource.id)).run();
81865
+ }).where(eq70(trafficSources.id, activeSource.id)).run();
81767
81866
  }
81768
81867
  tx.insert(trafficSources).values({
81769
81868
  id: sourceId,
@@ -81789,7 +81888,7 @@ async function trafficRoutes(app, opts) {
81789
81888
  entityId: sourceId
81790
81889
  });
81791
81890
  return {
81792
- sourceRow: tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceId)).get(),
81891
+ sourceRow: tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceId)).get(),
81793
81892
  scheduleChanged: scheduleBinding.changed
81794
81893
  };
81795
81894
  }, { behavior: "immediate" });
@@ -81846,7 +81945,7 @@ async function trafficRoutes(app, opts) {
81846
81945
  const config = { projectId, teamId, environment };
81847
81946
  const fallbackName = displayName ?? `Vercel \xB7 ${projectId}`;
81848
81947
  const { sourceRow: sourceRow2, scheduleChanged } = app.db.transaction((tx) => {
81849
- const activeSource = tx.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().find((row2) => row2.sourceType === TrafficSourceTypes.vercel && row2.status !== TrafficSourceStatuses.archived);
81948
+ const activeSource = tx.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().find((row2) => row2.sourceType === TrafficSourceTypes.vercel && row2.status !== TrafficSourceStatuses.archived);
81850
81949
  const sourceStatus = trafficConnectStatus(tx, project.id, activeSource);
81851
81950
  let row;
81852
81951
  if (activeSource) {
@@ -81856,8 +81955,8 @@ async function trafficRoutes(app, opts) {
81856
81955
  lastError: null,
81857
81956
  configJson: config,
81858
81957
  updatedAt: now
81859
- }).where(eq69(trafficSources.id, activeSource.id)).run();
81860
- row = tx.select().from(trafficSources).where(eq69(trafficSources.id, activeSource.id)).get();
81958
+ }).where(eq70(trafficSources.id, activeSource.id)).run();
81959
+ row = tx.select().from(trafficSources).where(eq70(trafficSources.id, activeSource.id)).get();
81861
81960
  } else {
81862
81961
  const newId = crypto48.randomUUID();
81863
81962
  tx.insert(trafficSources).values({
@@ -81882,7 +81981,7 @@ async function trafficRoutes(app, opts) {
81882
81981
  createdAt: now,
81883
81982
  updatedAt: now
81884
81983
  }).run();
81885
- row = tx.select().from(trafficSources).where(eq69(trafficSources.id, newId)).get();
81984
+ row = tx.select().from(trafficSources).where(eq70(trafficSources.id, newId)).get();
81886
81985
  }
81887
81986
  const scheduleBinding = sourceStatus === TrafficSourceStatuses.connected ? bindTrafficSyncSchedule(tx, project.id, row.id, now) : { changed: false, created: false };
81888
81987
  writeAuditLog(tx, {
@@ -81926,7 +82025,7 @@ async function trafficRoutes(app, opts) {
81926
82025
  const { displayName: displayName2, zoneId: zoneId2, accountId: accountId2, queueId, queueName, retentionSeconds, apiToken } = parsed.data;
81927
82026
  const workerRouteHost2 = resolveCloudflareWorkerRouteHost(project.canonicalDomain);
81928
82027
  const now2 = (/* @__PURE__ */ new Date()).toISOString();
81929
- const cloudflareSources = app.db.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().filter((row) => row.sourceType === TrafficSourceTypes.cloudflare && row.status !== TrafficSourceStatuses.archived);
82028
+ const cloudflareSources = app.db.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().filter((row) => row.sourceType === TrafficSourceTypes.cloudflare && row.status !== TrafficSourceStatuses.archived);
81930
82029
  const sameModeSource = cloudflareSources.find((row) => parseQueuePullCloudflareSourceConfig(row.configJson));
81931
82030
  if (sameModeSource) attempt.update({ action: "reauth" });
81932
82031
  const sourceId2 = sameModeSource?.id ?? crypto48.randomUUID();
@@ -81977,7 +82076,7 @@ async function trafficRoutes(app, opts) {
81977
82076
  let scheduleChanged = false;
81978
82077
  try {
81979
82078
  const result = app.db.transaction((tx) => {
81980
- const currentSameModeSource = tx.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.cloudflare && row.status !== TrafficSourceStatuses.archived && parseQueuePullCloudflareSourceConfig(row.configJson) !== null);
82079
+ const currentSameModeSource = tx.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.cloudflare && row.status !== TrafficSourceStatuses.archived && parseQueuePullCloudflareSourceConfig(row.configJson) !== null);
81981
82080
  if ((currentSameModeSource?.id ?? null) !== (sameModeSource?.id ?? null)) {
81982
82081
  throw operationInProgress("Cloudflare Queue source changed during connect; retry");
81983
82082
  }
@@ -81995,7 +82094,7 @@ async function trafficRoutes(app, opts) {
81995
82094
  queueBacklogObservedAt: null
81996
82095
  } : {},
81997
82096
  updatedAt: now2
81998
- }).where(eq69(trafficSources.id, sourceId2)).run();
82097
+ }).where(eq70(trafficSources.id, sourceId2)).run();
81999
82098
  } else {
82000
82099
  tx.insert(trafficSources).values({
82001
82100
  id: sourceId2,
@@ -82015,7 +82114,7 @@ async function trafficRoutes(app, opts) {
82015
82114
  }
82016
82115
  const changed = status === TrafficSourceStatuses.connected ? bindTrafficSyncSchedule(tx, project.id, sourceId2, now2).changed : false;
82017
82116
  return {
82018
- row: tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceId2)).get(),
82117
+ row: tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceId2)).get(),
82019
82118
  scheduleChanged: changed
82020
82119
  };
82021
82120
  }, { behavior: "immediate" });
@@ -82063,7 +82162,7 @@ async function trafficRoutes(app, opts) {
82063
82162
  const { deliveryMode, displayName, zoneId, accountId } = parsed.data;
82064
82163
  const workerRouteHost = resolveCloudflareWorkerRouteHost(project.canonicalDomain);
82065
82164
  const now = (/* @__PURE__ */ new Date()).toISOString();
82066
- const activeSource = app.db.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.cloudflare && row.status !== TrafficSourceStatuses.archived && parseDirectPushCloudflareSourceConfig(row.configJson) !== null);
82165
+ const activeSource = app.db.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.cloudflare && row.status !== TrafficSourceStatuses.archived && parseDirectPushCloudflareSourceConfig(row.configJson) !== null);
82067
82166
  if (activeSource) attempt.update({ action: "reauth" });
82068
82167
  const sourceId = activeSource?.id ?? crypto48.randomUUID();
82069
82168
  const parsedActiveConfig = activeSource ? parseDirectPushCloudflareSourceConfig(activeSource.configJson) : null;
@@ -82128,7 +82227,7 @@ async function trafficRoutes(app, opts) {
82128
82227
  let scheduleRemoved = false;
82129
82228
  try {
82130
82229
  const result = app.db.transaction((tx) => {
82131
- const currentActiveSource = tx.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().find((row2) => row2.sourceType === TrafficSourceTypes.cloudflare && row2.status !== TrafficSourceStatuses.archived && parseDirectPushCloudflareSourceConfig(row2.configJson) !== null);
82230
+ const currentActiveSource = tx.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().find((row2) => row2.sourceType === TrafficSourceTypes.cloudflare && row2.status !== TrafficSourceStatuses.archived && parseDirectPushCloudflareSourceConfig(row2.configJson) !== null);
82132
82231
  if ((currentActiveSource?.id ?? null) !== (activeSource?.id ?? null)) {
82133
82232
  throw operationInProgress("Cloudflare direct-push source changed during connect; retry");
82134
82233
  }
@@ -82142,8 +82241,8 @@ async function trafficRoutes(app, opts) {
82142
82241
  configJson: config,
82143
82242
  ingestTokenHash,
82144
82243
  updatedAt: now
82145
- }).where(eq69(trafficSources.id, currentActiveSource.id)).run();
82146
- row = tx.select().from(trafficSources).where(eq69(trafficSources.id, currentActiveSource.id)).get();
82244
+ }).where(eq70(trafficSources.id, currentActiveSource.id)).run();
82245
+ row = tx.select().from(trafficSources).where(eq70(trafficSources.id, currentActiveSource.id)).get();
82147
82246
  } else {
82148
82247
  tx.insert(trafficSources).values({
82149
82248
  id: sourceId,
@@ -82163,16 +82262,16 @@ async function trafficRoutes(app, opts) {
82163
82262
  createdAt: now,
82164
82263
  updatedAt: now
82165
82264
  }).run();
82166
- row = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceId)).get();
82265
+ row = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceId)).get();
82167
82266
  }
82168
82267
  let removed = false;
82169
82268
  if (row.status === TrafficSourceStatuses.connected) {
82170
- const schedule = tx.select().from(schedules).where(and60(
82171
- eq69(schedules.projectId, project.id),
82172
- eq69(schedules.kind, SchedulableRunKinds["traffic-sync"])
82269
+ const schedule = tx.select().from(schedules).where(and61(
82270
+ eq70(schedules.projectId, project.id),
82271
+ eq70(schedules.kind, SchedulableRunKinds["traffic-sync"])
82173
82272
  )).get();
82174
82273
  if (schedule) {
82175
- tx.delete(schedules).where(eq69(schedules.id, schedule.id)).run();
82274
+ tx.delete(schedules).where(eq70(schedules.id, schedule.id)).run();
82176
82275
  removed = true;
82177
82276
  }
82178
82277
  }
@@ -82226,47 +82325,47 @@ async function trafficRoutes(app, opts) {
82226
82325
  };
82227
82326
  }));
82228
82327
  app.post("/projects/:name/traffic/sources/:id/activate", connectionRoute(app, (request) => {
82229
- const source = app.db.select({ sourceType: trafficSources.sourceType }).from(trafficSources).where(eq69(trafficSources.id, request.params.id)).get();
82328
+ const source = app.db.select({ sourceType: trafficSources.sourceType }).from(trafficSources).where(eq70(trafficSources.id, request.params.id)).get();
82230
82329
  const integration = source ? TRAFFIC_SOURCE_INTEGRATIONS[source.sourceType] : void 0;
82231
82330
  return integration ? { integration, action: "select" } : void 0;
82232
82331
  }, async (request) => {
82233
82332
  const project = resolveProject(app.db, request.params.name);
82234
82333
  const now = (/* @__PURE__ */ new Date()).toISOString();
82235
82334
  const result = app.db.transaction((tx) => {
82236
- const target = tx.select().from(trafficSources).where(eq69(trafficSources.id, request.params.id)).get();
82335
+ const target = tx.select().from(trafficSources).where(eq70(trafficSources.id, request.params.id)).get();
82237
82336
  if (!target || target.projectId !== project.id || target.status === TrafficSourceStatuses.archived) {
82238
82337
  throw notFound("Traffic source", request.params.id);
82239
82338
  }
82240
82339
  const { usesPullSchedule } = validateTrafficSourceCredential(target, project.name);
82241
- const siblings = tx.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all();
82340
+ const siblings = tx.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all();
82242
82341
  for (const sibling of siblings) {
82243
82342
  if (sibling.id === target.id || sibling.status === TrafficSourceStatuses.archived) continue;
82244
82343
  tx.update(trafficSources).set({
82245
82344
  status: TrafficSourceStatuses.paused,
82246
82345
  updatedAt: now
82247
- }).where(eq69(trafficSources.id, sibling.id)).run();
82346
+ }).where(eq70(trafficSources.id, sibling.id)).run();
82248
82347
  }
82249
82348
  tx.update(trafficSources).set({
82250
82349
  status: TrafficSourceStatuses.connected,
82251
82350
  lastError: null,
82252
82351
  archivedAt: null,
82253
82352
  updatedAt: now
82254
- }).where(eq69(trafficSources.id, target.id)).run();
82353
+ }).where(eq70(trafficSources.id, target.id)).run();
82255
82354
  let scheduleAction = null;
82256
82355
  if (usesPullSchedule) {
82257
82356
  bindTrafficSyncSchedule(tx, project.id, target.id, now);
82258
82357
  scheduleAction = "upsert";
82259
82358
  } else {
82260
- const schedule = tx.select().from(schedules).where(and60(
82261
- eq69(schedules.projectId, project.id),
82262
- eq69(schedules.kind, SchedulableRunKinds["traffic-sync"])
82359
+ const schedule = tx.select().from(schedules).where(and61(
82360
+ eq70(schedules.projectId, project.id),
82361
+ eq70(schedules.kind, SchedulableRunKinds["traffic-sync"])
82263
82362
  )).get();
82264
82363
  if (schedule) {
82265
- tx.delete(schedules).where(eq69(schedules.id, schedule.id)).run();
82364
+ tx.delete(schedules).where(eq70(schedules.id, schedule.id)).run();
82266
82365
  scheduleAction = "delete";
82267
82366
  }
82268
82367
  }
82269
- const source = tx.select().from(trafficSources).where(eq69(trafficSources.id, target.id)).get();
82368
+ const source = tx.select().from(trafficSources).where(eq70(trafficSources.id, target.id)).get();
82270
82369
  writeAuditLog(tx, {
82271
82370
  projectId: project.id,
82272
82371
  actor: "api",
@@ -82334,7 +82433,7 @@ async function trafficRoutes(app, opts) {
82334
82433
  }
82335
82434
  const { bearerHash, credential } = authenticated;
82336
82435
  const sourceId = credential.sourceId;
82337
- const sourceRow2 = app.db.select().from(trafficSources).where(eq69(trafficSources.id, sourceId)).get();
82436
+ const sourceRow2 = app.db.select().from(trafficSources).where(eq70(trafficSources.id, sourceId)).get();
82338
82437
  if (!sourceRow2 || sourceRow2.sourceType !== TrafficSourceTypes.cloudflare || sourceRow2.status !== TrafficSourceStatuses.connected || !parseDirectPushCloudflareSourceConfig(sourceRow2.configJson) || !timingSafeEqualHex(bearerHash, sourceRow2.ingestTokenHash)) {
82339
82438
  throw authRequired();
82340
82439
  }
@@ -82408,7 +82507,7 @@ async function trafficRoutes(app, opts) {
82408
82507
  });
82409
82508
  app.post("/projects/:name/traffic/sources/:id/sync", async (request) => {
82410
82509
  const project = resolveProject(app.db, request.params.name);
82411
- const initialSourceRow = app.db.select().from(trafficSources).where(eq69(trafficSources.id, request.params.id)).get();
82510
+ const initialSourceRow = app.db.select().from(trafficSources).where(eq70(trafficSources.id, request.params.id)).get();
82412
82511
  if (!initialSourceRow || initialSourceRow.projectId !== project.id) {
82413
82512
  throw notFound("Traffic source", request.params.id);
82414
82513
  }
@@ -82563,7 +82662,7 @@ async function trafficRoutes(app, opts) {
82563
82662
  now: (/* @__PURE__ */ new Date()).toISOString(),
82564
82663
  ttlMs: CLOUDFLARE_QUEUE_SYNC_LEASE_TTL_MS
82565
82664
  })) throw new Error("Queue sync lease was lost before acknowledgement");
82566
- const latestSource = app.db.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
82665
+ const latestSource = app.db.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
82567
82666
  if (!latestSource || latestSource.projectId !== project.id || latestSource.status !== TrafficSourceStatuses.connected || latestSource.syncLeaseOwner !== leaseOwner || !isDeepStrictEqual2(latestSource.configJson, sourceRow2.configJson)) {
82568
82667
  throw validationError("Cloudflare Queue source was reconfigured before acknowledgement");
82569
82668
  }
@@ -82588,7 +82687,7 @@ async function trafficRoutes(app, opts) {
82588
82687
  const pulledEnvelopeCount = pulled.messages.length + pulled.skippedUnleasedMessageCount;
82589
82688
  if (pulledEnvelopeCount < CLOUDFLARE_QUEUE_BATCH_SIZE || remainingBacklogCount === 0) break;
82590
82689
  }
82591
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: committedAt }).where(eq69(runs.id, runId)).run();
82690
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: committedAt }).where(eq70(runs.id, runId)).run();
82592
82691
  writeAuditLog(app.db, {
82593
82692
  projectId: project.id,
82594
82693
  actor: "api",
@@ -82649,13 +82748,13 @@ async function trafficRoutes(app, opts) {
82649
82748
  } catch {
82650
82749
  }
82651
82750
  app.db.transaction((tx) => {
82652
- tx.update(runs).set({ status: RunStatuses.failed, error: safeError, finishedAt: failedAt }).where(eq69(runs.id, runId)).run();
82653
- const latestSource = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
82751
+ tx.update(runs).set({ status: RunStatuses.failed, error: safeError, finishedAt: failedAt }).where(eq70(runs.id, runId)).run();
82752
+ const latestSource = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
82654
82753
  if (latestSource && latestSource.status === TrafficSourceStatuses.connected && latestSource.syncLeaseOwner === leaseOwner && isDeepStrictEqual2(latestSource.configJson, sourceRow2.configJson)) {
82655
- tx.update(trafficSources).set({ lastError: safeError, updatedAt: failedAt }).where(and60(
82656
- eq69(trafficSources.id, sourceRow2.id),
82657
- eq69(trafficSources.status, TrafficSourceStatuses.connected),
82658
- eq69(trafficSources.syncLeaseOwner, leaseOwner)
82754
+ tx.update(trafficSources).set({ lastError: safeError, updatedAt: failedAt }).where(and61(
82755
+ eq70(trafficSources.id, sourceRow2.id),
82756
+ eq70(trafficSources.status, TrafficSourceStatuses.connected),
82757
+ eq70(trafficSources.syncLeaseOwner, leaseOwner)
82659
82758
  )).run();
82660
82759
  }
82661
82760
  });
@@ -82674,7 +82773,7 @@ async function trafficRoutes(app, opts) {
82674
82773
  `Sync for source type "${sourceRow2.sourceType}" is not implemented yet \u2014 only cloud-run, wordpress, and vercel are supported in v1.`
82675
82774
  );
82676
82775
  }
82677
- const hasConnectedSibling = sourceRow2.status === TrafficSourceStatuses.error && app.db.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().some((row) => row.id !== sourceRow2.id && row.status === TrafficSourceStatuses.connected);
82776
+ const hasConnectedSibling = sourceRow2.status === TrafficSourceStatuses.error && app.db.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().some((row) => row.id !== sourceRow2.id && row.status === TrafficSourceStatuses.connected);
82678
82777
  if (sourceRow2.status === TrafficSourceStatuses.paused || sourceRow2.status === TrafficSourceStatuses.archived || hasConnectedSibling) {
82679
82778
  throw validationError("Traffic source must be active before it can sync");
82680
82779
  }
@@ -82707,10 +82806,10 @@ async function trafficRoutes(app, opts) {
82707
82806
  const markFailed = (msg, errorCode2) => {
82708
82807
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
82709
82808
  app.db.transaction((tx) => {
82710
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq69(runs.id, runId)).run();
82711
- const latestSource = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
82809
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq70(runs.id, runId)).run();
82810
+ const latestSource = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
82712
82811
  if (latestSource?.status === TrafficSourceStatuses.connected && isSameTrafficSourceGeneration(latestSource, sourceRow2) && (wordpressLeaseOwner === void 0 || latestSource.syncLeaseOwner === wordpressLeaseOwner)) {
82713
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq69(trafficSources.id, sourceRow2.id)).run();
82812
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq70(trafficSources.id, sourceRow2.id)).run();
82714
82813
  }
82715
82814
  });
82716
82815
  try {
@@ -82804,7 +82903,7 @@ async function trafficRoutes(app, opts) {
82804
82903
  }
82805
82904
  const credential = credentialStore.getConnection(project.name);
82806
82905
  if (!credential) {
82807
- app.db.delete(runs).where(eq69(runs.id, runId)).run();
82906
+ app.db.delete(runs).where(eq70(runs.id, runId)).run();
82808
82907
  throw validationError(
82809
82908
  `No WordPress credential found for project "${project.name}". Run "canonry traffic connect wordpress" first.`
82810
82909
  );
@@ -82838,14 +82937,14 @@ async function trafficRoutes(app, opts) {
82838
82937
  const windowEndIso2 = windowEnd.toISOString();
82839
82938
  const reservedAt = (/* @__PURE__ */ new Date()).toISOString();
82840
82939
  const reservedSource = app.db.transaction((tx) => {
82841
- const latestSource = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
82940
+ const latestSource = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
82842
82941
  if (!latestSource || !isAuthoritativeTrafficSource(tx, latestSource) || !isSameTrafficSourceGeneration(latestSource, sourceRow2) || latestSource.syncLeaseOwner !== wordpressLeaseOwner || latestSource.lastCursor || latestSource.wordpressPendingUntil) return void 0;
82843
82942
  tx.update(trafficSources).set({
82844
82943
  lastSyncedAt: windowStartIso2,
82845
82944
  wordpressPendingUntil: windowEndIso2,
82846
82945
  updatedAt: reservedAt
82847
- }).where(eq69(trafficSources.id, sourceRow2.id)).run();
82848
- return tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
82946
+ }).where(eq70(trafficSources.id, sourceRow2.id)).run();
82947
+ return tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
82849
82948
  }, { behavior: "immediate" });
82850
82949
  if (!reservedSource) {
82851
82950
  const message = "Traffic source changed while reserving the WordPress sync window; retry the sync.";
@@ -82905,12 +83004,12 @@ async function trafficRoutes(app, opts) {
82905
83004
  auditAction = "traffic.vercel.synced";
82906
83005
  const credentialStore = opts.vercelTrafficCredentialStore;
82907
83006
  if (!credentialStore) {
82908
- app.db.delete(runs).where(eq69(runs.id, runId)).run();
83007
+ app.db.delete(runs).where(eq70(runs.id, runId)).run();
82909
83008
  throw validationError("Vercel traffic credential storage is not configured for this deployment");
82910
83009
  }
82911
83010
  const credential = credentialStore.getConnection(project.name);
82912
83011
  if (!credential) {
82913
- app.db.delete(runs).where(eq69(runs.id, runId)).run();
83012
+ app.db.delete(runs).where(eq70(runs.id, runId)).run();
82914
83013
  throw validationError(
82915
83014
  `No Vercel credential found for project "${project.name}". Run "canonry traffic connect vercel" first.`
82916
83015
  );
@@ -82928,7 +83027,7 @@ async function trafficRoutes(app, opts) {
82928
83027
  if (cappedStartMs > clampedStartMs) {
82929
83028
  const previousSkip = sourceRow2.skippedThroughAt ? Date.parse(sourceRow2.skippedThroughAt) : Number.NaN;
82930
83029
  const skippedThrough = Number.isFinite(previousSkip) ? new Date(Math.max(previousSkip, cappedStartMs)) : new Date(cappedStartMs);
82931
- app.db.update(trafficSources).set({ skippedThroughAt: skippedThrough.toISOString() }).where(eq69(trafficSources.id, sourceRow2.id)).run();
83030
+ app.db.update(trafficSources).set({ skippedThroughAt: skippedThrough.toISOString() }).where(eq70(trafficSources.id, sourceRow2.id)).run();
82932
83031
  request.log.warn(
82933
83032
  {
82934
83033
  sourceId: sourceRow2.id,
@@ -83021,15 +83120,15 @@ async function trafficRoutes(app, opts) {
83021
83120
  throw providerError(`WordPress sync failed: ${msg}`);
83022
83121
  }
83023
83122
  const commitOutcome = app.db.transaction((tx) => {
83024
- const latestRow = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
83025
- const latestHasConnectedSibling = tx.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().some((row) => row.id !== sourceRow2.id && row.status === TrafficSourceStatuses.connected);
83123
+ const latestRow = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
83124
+ const latestHasConnectedSibling = tx.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().some((row) => row.id !== sourceRow2.id && row.status === TrafficSourceStatuses.connected);
83026
83125
  if (!latestRow || latestRow.status !== TrafficSourceStatuses.connected && latestRow.status !== TrafficSourceStatuses.error || latestHasConnectedSibling || wordpressLeaseOwner !== void 0 && latestRow.syncLeaseOwner !== wordpressLeaseOwner || !isSameTrafficSourceGeneration(latestRow, sourceRow2)) {
83027
83126
  const abortedAt = (/* @__PURE__ */ new Date()).toISOString();
83028
83127
  tx.update(runs).set({
83029
83128
  status: RunStatuses.failed,
83030
83129
  error: "Traffic source was deactivated or reconfigured during sync",
83031
83130
  finishedAt: abortedAt
83032
- }).where(eq69(runs.id, runId)).run();
83131
+ }).where(eq70(runs.id, runId)).run();
83033
83132
  return "source-inactive";
83034
83133
  }
83035
83134
  const previousIds = latestRow.lastEventIds ?? [];
@@ -83292,8 +83391,8 @@ async function trafficRoutes(app, opts) {
83292
83391
  sourceUpdate.lastCursor = nextCursor ?? null;
83293
83392
  sourceUpdate.wordpressPendingUntil = nextCursor ? windowEnd.toISOString() : null;
83294
83393
  }
83295
- tx.update(trafficSources).set(sourceUpdate).where(eq69(trafficSources.id, sourceRow2.id)).run();
83296
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq69(runs.id, runId)).run();
83394
+ tx.update(trafficSources).set(sourceUpdate).where(eq70(trafficSources.id, sourceRow2.id)).run();
83395
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq70(runs.id, runId)).run();
83297
83396
  return "committed";
83298
83397
  }, { behavior: "immediate" });
83299
83398
  if (commitOutcome === "source-inactive") {
@@ -83363,7 +83462,7 @@ async function trafficRoutes(app, opts) {
83363
83462
  });
83364
83463
  app.post("/projects/:name/traffic/sources/:id/backfill", async (request) => withFeatureOutcome(app, { feature: "server_traffic", operation: "backfill" }, async (recordOutcome) => {
83365
83464
  const project = resolveProject(app.db, request.params.name);
83366
- const sourceRow2 = app.db.select().from(trafficSources).where(eq69(trafficSources.id, request.params.id)).get();
83465
+ const sourceRow2 = app.db.select().from(trafficSources).where(eq70(trafficSources.id, request.params.id)).get();
83367
83466
  if (!sourceRow2 || sourceRow2.projectId !== project.id) {
83368
83467
  throw notFound("Traffic source", request.params.id);
83369
83468
  }
@@ -83476,7 +83575,7 @@ async function trafficRoutes(app, opts) {
83476
83575
  const startedAt = windowEnd.toISOString();
83477
83576
  const runId = crypto48.randomUUID();
83478
83577
  app.db.transaction((tx) => {
83479
- const latestSource = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
83578
+ const latestSource = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
83480
83579
  if (!latestSource || !isAuthoritativeTrafficSource(tx, latestSource) || !isSameTrafficSourceGeneration(latestSource, sourceRow2)) {
83481
83580
  throw validationError("Traffic source must remain active and unchanged before it can backfill");
83482
83581
  }
@@ -83519,8 +83618,8 @@ async function trafficRoutes(app, opts) {
83519
83618
  pathNormalized: crawlerEventsHourly.pathNormalized,
83520
83619
  hits: sql28`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
83521
83620
  }).from(crawlerEventsHourly).where(
83522
- and60(
83523
- eq69(crawlerEventsHourly.sourceId, row.id),
83621
+ and61(
83622
+ eq70(crawlerEventsHourly.sourceId, row.id),
83524
83623
  gte16(crawlerEventsHourly.tsHour, since)
83525
83624
  )
83526
83625
  ).groupBy(crawlerEventsHourly.pathNormalized).all();
@@ -83529,8 +83628,8 @@ async function trafficRoutes(app, opts) {
83529
83628
  );
83530
83629
  const crawlerTotal = crawlerSegments.content + crawlerSegments.sitemap + crawlerSegments.robots + crawlerSegments.asset + crawlerSegments.other;
83531
83630
  const aiUserFetchTotals = app.db.select({ total: sql28`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
83532
- and60(
83533
- eq69(aiUserFetchEventsHourly.sourceId, row.id),
83631
+ and61(
83632
+ eq70(aiUserFetchEventsHourly.sourceId, row.id),
83534
83633
  gte16(aiUserFetchEventsHourly.tsHour, since)
83535
83634
  )
83536
83635
  ).get();
@@ -83539,22 +83638,22 @@ async function trafficRoutes(app, opts) {
83539
83638
  nonhop: sql28`COALESCE(SUM(CASE WHEN ${referralLandedCondition()} THEN ${aiReferralEventsHourly.sessionsOrHits} ELSE 0 END), 0)`,
83540
83639
  landed: sql28`COALESCE(SUM(CASE WHEN ${countableReferralCondition()} THEN ${aiReferralEventsHourly.sessionsOrHits} ELSE 0 END), 0)`
83541
83640
  }).from(aiReferralEventsHourly).where(
83542
- and60(
83543
- eq69(aiReferralEventsHourly.sourceId, row.id),
83641
+ and61(
83642
+ eq70(aiReferralEventsHourly.sourceId, row.id),
83544
83643
  gte16(aiReferralEventsHourly.tsHour, since)
83545
83644
  )
83546
83645
  ).get();
83547
83646
  const sampleTotals = app.db.select({ total: sql28`COUNT(*)` }).from(rawEventSamples).where(
83548
- and60(
83549
- eq69(rawEventSamples.sourceId, row.id),
83647
+ and61(
83648
+ eq70(rawEventSamples.sourceId, row.id),
83550
83649
  gte16(rawEventSamples.ts, since)
83551
83650
  )
83552
83651
  ).get();
83553
83652
  const latestRun = app.db.select().from(runs).where(
83554
- and60(
83555
- eq69(runs.projectId, projectId),
83556
- eq69(runs.kind, RunKinds["traffic-sync"]),
83557
- eq69(runs.sourceId, row.id)
83653
+ and61(
83654
+ eq70(runs.projectId, projectId),
83655
+ eq70(runs.kind, RunKinds["traffic-sync"]),
83656
+ eq70(runs.sourceId, row.id)
83558
83657
  )
83559
83658
  ).orderBy(desc35(runs.startedAt)).limit(1).get();
83560
83659
  return {
@@ -83590,7 +83689,7 @@ async function trafficRoutes(app, opts) {
83590
83689
  const now = (/* @__PURE__ */ new Date()).toISOString();
83591
83690
  let updatedRow;
83592
83691
  app.db.transaction((tx) => {
83593
- const sourceRow2 = tx.select().from(trafficSources).where(and60(eq69(trafficSources.projectId, project.id), eq69(trafficSources.id, request.params.id))).get();
83692
+ const sourceRow2 = tx.select().from(trafficSources).where(and61(eq70(trafficSources.projectId, project.id), eq70(trafficSources.id, request.params.id))).get();
83594
83693
  if (!sourceRow2) {
83595
83694
  throw notFound("traffic source", request.params.id);
83596
83695
  }
@@ -83604,7 +83703,7 @@ async function trafficRoutes(app, opts) {
83604
83703
  `Traffic source "${sourceRow2.id}" is staged. Activate it explicitly instead of resetting it.`
83605
83704
  );
83606
83705
  }
83607
- const hasConnectedSibling = tx.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).all().some((row) => row.id !== sourceRow2.id && row.status === TrafficSourceStatuses.connected);
83706
+ const hasConnectedSibling = tx.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).all().some((row) => row.id !== sourceRow2.id && row.status === TrafficSourceStatuses.connected);
83608
83707
  if (hasConnectedSibling) {
83609
83708
  throw validationError(
83610
83709
  `Traffic source "${sourceRow2.id}" is not authoritative. Activate it explicitly instead of resetting it.`
@@ -83621,7 +83720,7 @@ async function trafficRoutes(app, opts) {
83621
83720
  status: TrafficSourceStatuses.connected,
83622
83721
  lastError: null,
83623
83722
  updatedAt: now
83624
- }).where(eq69(trafficSources.id, sourceRow2.id)).run();
83723
+ }).where(eq70(trafficSources.id, sourceRow2.id)).run();
83625
83724
  writeAuditLog(tx, auditFromRequest(request, {
83626
83725
  projectId: project.id,
83627
83726
  actor: "api",
@@ -83629,20 +83728,20 @@ async function trafficRoutes(app, opts) {
83629
83728
  entityType: "traffic_source",
83630
83729
  entityId: sourceRow2.id
83631
83730
  }));
83632
- updatedRow = tx.select().from(trafficSources).where(eq69(trafficSources.id, sourceRow2.id)).get();
83731
+ updatedRow = tx.select().from(trafficSources).where(eq70(trafficSources.id, sourceRow2.id)).get();
83633
83732
  });
83634
83733
  return buildSourceDetail(project.id, updatedRow, new Date(Date.now() - 24 * 60 * 6e4).toISOString());
83635
83734
  }));
83636
83735
  app.get("/projects/:name/traffic/sources", async (request) => {
83637
83736
  const project = resolveProject(app.db, request.params.name);
83638
- const rows = app.db.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).orderBy(desc35(trafficSources.createdAt)).all();
83737
+ const rows = app.db.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).orderBy(desc35(trafficSources.createdAt)).all();
83639
83738
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map(rowToDto);
83640
83739
  const response = { sources };
83641
83740
  return response;
83642
83741
  });
83643
83742
  app.get("/projects/:name/traffic/status", async (request) => {
83644
83743
  const project = resolveProject(app.db, request.params.name);
83645
- const rows = app.db.select().from(trafficSources).where(eq69(trafficSources.projectId, project.id)).orderBy(desc35(trafficSources.createdAt)).all();
83744
+ const rows = app.db.select().from(trafficSources).where(eq70(trafficSources.projectId, project.id)).orderBy(desc35(trafficSources.createdAt)).all();
83646
83745
  const since = new Date(Date.now() - 24 * 60 * 6e4).toISOString();
83647
83746
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map((row) => buildSourceDetail(project.id, row, since));
83648
83747
  const response = { sources };
@@ -83652,7 +83751,7 @@ async function trafficRoutes(app, opts) {
83652
83751
  "/projects/:name/traffic/sources/:id",
83653
83752
  async (request) => {
83654
83753
  const project = resolveProject(app.db, request.params.name);
83655
- const row = app.db.select().from(trafficSources).where(eq69(trafficSources.id, request.params.id)).get();
83754
+ const row = app.db.select().from(trafficSources).where(eq70(trafficSources.id, request.params.id)).get();
83656
83755
  if (!row || row.projectId !== project.id) {
83657
83756
  throw notFound("Traffic source", request.params.id);
83658
83757
  }
@@ -83725,12 +83824,12 @@ async function trafficRoutes(app, opts) {
83725
83824
  const seriesByBucket = /* @__PURE__ */ new Map();
83726
83825
  if (kind === "all" || kind === TrafficEventKinds.crawler) {
83727
83826
  const crawlerFilters = [
83728
- eq69(crawlerEventsHourly.projectId, project.id),
83827
+ eq70(crawlerEventsHourly.projectId, project.id),
83729
83828
  gte16(crawlerEventsHourly.tsHour, sinceIso),
83730
83829
  lte17(crawlerEventsHourly.tsHour, untilIso)
83731
83830
  ];
83732
- if (sourceIdParam) crawlerFilters.push(eq69(crawlerEventsHourly.sourceId, sourceIdParam));
83733
- const crawlerWhere = and60(...crawlerFilters);
83831
+ if (sourceIdParam) crawlerFilters.push(eq70(crawlerEventsHourly.sourceId, sourceIdParam));
83832
+ const crawlerWhere = and61(...crawlerFilters);
83734
83833
  const pathTotals = app.db.select({
83735
83834
  pathNormalized: crawlerEventsHourly.pathNormalized,
83736
83835
  hits: sql28`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
@@ -83800,12 +83899,12 @@ async function trafficRoutes(app, opts) {
83800
83899
  }
83801
83900
  if (kind === "all" || kind === TrafficEventKinds["ai-user-fetch"]) {
83802
83901
  const userFetchFilters = [
83803
- eq69(aiUserFetchEventsHourly.projectId, project.id),
83902
+ eq70(aiUserFetchEventsHourly.projectId, project.id),
83804
83903
  gte16(aiUserFetchEventsHourly.tsHour, sinceIso),
83805
83904
  lte17(aiUserFetchEventsHourly.tsHour, untilIso)
83806
83905
  ];
83807
- if (sourceIdParam) userFetchFilters.push(eq69(aiUserFetchEventsHourly.sourceId, sourceIdParam));
83808
- const userFetchWhere = and60(...userFetchFilters);
83906
+ if (sourceIdParam) userFetchFilters.push(eq70(aiUserFetchEventsHourly.sourceId, sourceIdParam));
83907
+ const userFetchWhere = and61(...userFetchFilters);
83809
83908
  const total = app.db.select({
83810
83909
  total: sql28`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`,
83811
83910
  rows: sql28`COUNT(*)`
@@ -83839,12 +83938,12 @@ async function trafficRoutes(app, opts) {
83839
83938
  }
83840
83939
  if (kind === "all" || kind === TrafficEventKinds["ai-referral"]) {
83841
83940
  const aiFilters = [
83842
- eq69(aiReferralEventsHourly.projectId, project.id),
83941
+ eq70(aiReferralEventsHourly.projectId, project.id),
83843
83942
  gte16(aiReferralEventsHourly.tsHour, sinceIso),
83844
83943
  lte17(aiReferralEventsHourly.tsHour, untilIso)
83845
83944
  ];
83846
- if (sourceIdParam) aiFilters.push(eq69(aiReferralEventsHourly.sourceId, sourceIdParam));
83847
- const aiWhere = and60(...aiFilters);
83945
+ if (sourceIdParam) aiFilters.push(eq70(aiReferralEventsHourly.sourceId, sourceIdParam));
83946
+ const aiWhere = and61(...aiFilters);
83848
83947
  const total = app.db.select({
83849
83948
  total: sql28`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
83850
83949
  nonhop: sql28`COALESCE(SUM(CASE WHEN ${referralLandedCondition()} THEN ${aiReferralEventsHourly.sessionsOrHits} ELSE 0 END), 0)`,
@@ -83919,7 +84018,7 @@ async function trafficRoutes(app, opts) {
83919
84018
  windowEnd: untilIso,
83920
84019
  series: (() => {
83921
84020
  const points = completeTrafficSeries(since, until, granularity, seriesByBucket);
83922
- const coverageScope = (col, srcCol) => sourceIdParam ? and60(eq69(col, project.id), eq69(srcCol, sourceIdParam)) : eq69(col, project.id);
84021
+ const coverageScope = (col, srcCol) => sourceIdParam ? and61(eq70(col, project.id), eq70(srcCol, sourceIdParam)) : eq70(col, project.id);
83923
84022
  const firsts = [
83924
84023
  app.db.select({ v: sql28`MIN(${crawlerEventsHourly.tsHour})` }).from(crawlerEventsHourly).where(coverageScope(crawlerEventsHourly.projectId, crawlerEventsHourly.sourceId)).get()?.v,
83925
84024
  app.db.select({ v: sql28`MIN(${aiUserFetchEventsHourly.tsHour})` }).from(aiUserFetchEventsHourly).where(coverageScope(aiUserFetchEventsHourly.projectId, aiUserFetchEventsHourly.sourceId)).get()?.v,
@@ -83997,7 +84096,7 @@ async function trafficRoutes(app, opts) {
83997
84096
  }
83998
84097
 
83999
84098
  // ../api-routes/src/doctor/checks/report-sweeps.ts
84000
- import { and as and61, asc as asc15, eq as eq70, gte as gte17, lte as lte18 } from "drizzle-orm";
84099
+ import { and as and62, asc as asc15, eq as eq71, gte as gte17, lte as lte18 } from "drizzle-orm";
84001
84100
 
84002
84101
  // ../api-routes/src/visibility-attribution.ts
84003
84102
  function buildQueryAttribution2(projectQueries) {
@@ -84031,11 +84130,11 @@ function inspectSweep(ctx, run) {
84031
84130
  answerMentioned: querySnapshots.answerMentioned,
84032
84131
  answerText: querySnapshots.answerText,
84033
84132
  measurementExecutionId: querySnapshots.measurementExecutionId
84034
- }).from(querySnapshots).where(eq70(querySnapshots.runId, run.id)).all();
84133
+ }).from(querySnapshots).where(eq71(querySnapshots.runId, run.id)).all();
84035
84134
  if (snapshots.length === 0) return { detail: { ...base, complete: false, reason: "no-answers" } };
84036
84135
  try {
84037
84136
  if (run.measurementPlanVersionId) {
84038
- const version = ctx.db.select().from(measurementPlanVersions).where(and61(eq70(measurementPlanVersions.projectId, ctx.project.id), eq70(measurementPlanVersions.id, run.measurementPlanVersionId))).get();
84137
+ const version = ctx.db.select().from(measurementPlanVersions).where(and62(eq71(measurementPlanVersions.projectId, ctx.project.id), eq71(measurementPlanVersions.id, run.measurementPlanVersionId))).get();
84039
84138
  if (!version) return { detail: { ...base, complete: false, reason: "missing-plan" } };
84040
84139
  const plan = parseStoredMeasurementPlanAnyVersion(version.canonicalJson);
84041
84140
  const manifest = measurementRunExpectedSlots(run, plan);
@@ -84067,10 +84166,10 @@ function inspectSweep(ctx, run) {
84067
84166
  };
84068
84167
  return unbound ? { detail } : { detail, coverage: slotCoverage(expected2, answered3) };
84069
84168
  }
84070
- const stored = ctx.db.select().from(simpleMeasurementDefinitions).where(eq70(simpleMeasurementDefinitions.runId, run.id)).get();
84169
+ const stored = ctx.db.select().from(simpleMeasurementDefinitions).where(eq71(simpleMeasurementDefinitions.runId, run.id)).get();
84071
84170
  const frozen = stored ? simpleMeasurementDefinitionSchema.parse(stored.definition) : null;
84072
- const project = ctx.db.select().from(projects).where(eq70(projects.id, ctx.project.id)).get();
84073
- const basket = frozen ? frozen.queries.map((query) => ({ id: query.queryId, query: query.queryText })) : ctx.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq70(queries.projectId, project.id)).all();
84171
+ const project = ctx.db.select().from(projects).where(eq71(projects.id, ctx.project.id)).get();
84172
+ const basket = frozen ? frozen.queries.map((query) => ({ id: query.queryId, query: query.queryText })) : ctx.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq71(queries.projectId, project.id)).all();
84074
84173
  const providers = frozen ? frozen.engines.map((engine) => engine.provider) : project.providers.length > 0 ? project.providers : [...new Set(snapshots.map((snapshot) => snapshot.provider))];
84075
84174
  const attribution = buildQueryAttribution2(basket);
84076
84175
  const expected = new Set(basket.flatMap((query) => providers.map((provider) => JSON.stringify([query.id, provider.trim().toLowerCase()]))));
@@ -84119,7 +84218,7 @@ var reportSweepsCheck = {
84119
84218
  const graded = gradedReportMonths(reportMonths, now);
84120
84219
  const months = reportMonths.map((month) => {
84121
84220
  const bounds = calendarMonthBounds(month);
84122
- const monthRuns = ctx.db.select().from(runs).where(and61(eq70(runs.projectId, ctx.project.id), eq70(runs.kind, RunKinds["answer-visibility"]), notProbeRun(), gte17(runs.createdAt, bounds.since), lte18(runs.createdAt, bounds.until))).orderBy(asc15(runs.createdAt), asc15(runs.id)).all();
84221
+ const monthRuns = ctx.db.select().from(runs).where(and62(eq71(runs.projectId, ctx.project.id), eq71(runs.kind, RunKinds["answer-visibility"]), notProbeRun(), gte17(runs.createdAt, bounds.since), lte18(runs.createdAt, bounds.until))).orderBy(asc15(runs.createdAt), asc15(runs.id)).all();
84123
84222
  const evidence = monthRuns.map((run) => inspectSweep(ctx, run));
84124
84223
  return {
84125
84224
  month,
@@ -84142,7 +84241,7 @@ var reportSweepsCheck = {
84142
84241
  };
84143
84242
 
84144
84243
  // ../api-routes/src/doctor/checks/report-models.ts
84145
- import { and as and62, eq as eq71, gte as gte18, inArray as inArray29, lte as lte19 } from "drizzle-orm";
84244
+ import { and as and63, eq as eq72, gte as gte18, inArray as inArray29, lte as lte19 } from "drizzle-orm";
84146
84245
  var reportModelsCheck = {
84147
84246
  id: "report.models",
84148
84247
  category: CheckCategories.providers,
@@ -84157,7 +84256,7 @@ var reportModelsCheck = {
84157
84256
  const months = reportMonths.flatMap((month) => {
84158
84257
  const bounds = calendarMonthBounds(month);
84159
84258
  const previous = new Date(Date.parse(bounds.since) - 1).toISOString().slice(0, 7);
84160
- const observations = ctx.db.select({ provider: querySnapshots.provider, model: querySnapshots.model, at: runs.createdAt }).from(querySnapshots).innerJoin(runs, eq71(runs.id, querySnapshots.runId)).where(and62(eq71(runs.projectId, ctx.project.id), eq71(runs.kind, RunKinds["answer-visibility"]), inArray29(runs.status, [RunStatuses.completed, RunStatuses.partial]), notProbeRun(), gte18(runs.createdAt, bounds.since), lte19(runs.createdAt, bounds.until))).all();
84259
+ const observations = ctx.db.select({ provider: querySnapshots.provider, model: querySnapshots.model, at: runs.createdAt }).from(querySnapshots).innerJoin(runs, eq72(runs.id, querySnapshots.runId)).where(and63(eq72(runs.projectId, ctx.project.id), eq72(runs.kind, RunKinds["answer-visibility"]), inArray29(runs.status, [RunStatuses.completed, RunStatuses.partial]), notProbeRun(), gte18(runs.createdAt, bounds.since), lte19(runs.createdAt, bounds.until))).all();
84161
84260
  return readVisibilityContinuity(ctx.db, ctx.project.name, { from: previous, to: month }).map(({ frame, continuity: continuity2 }) => ({
84162
84261
  month,
84163
84262
  previousMonth: previous,
@@ -84198,7 +84297,7 @@ var reportModelsCheck = {
84198
84297
  };
84199
84298
 
84200
84299
  // ../api-routes/src/doctor/checks/report-daily-data.ts
84201
- import { eq as eq72 } from "drizzle-orm";
84300
+ import { eq as eq73 } from "drizzle-orm";
84202
84301
  var REPORTING_LAG_DAYS = 3;
84203
84302
  function dateSpan(dates) {
84204
84303
  const sorted = [...dates].sort();
@@ -84207,31 +84306,31 @@ function dateSpan(dates) {
84207
84306
  function latestSyncRange(ctx, source) {
84208
84307
  const projectId = ctx.project.id;
84209
84308
  if (source === "ga") {
84210
- const rows2 = ctx.db.select({ date: gaDailyTotals.date, syncedAt: gaDailyTotals.syncedAt }).from(gaDailyTotals).where(eq72(gaDailyTotals.projectId, projectId)).all();
84309
+ const rows2 = ctx.db.select({ date: gaDailyTotals.date, syncedAt: gaDailyTotals.syncedAt }).from(gaDailyTotals).where(eq73(gaDailyTotals.projectId, projectId)).all();
84211
84310
  const latest2 = rows2.reduce((max2, row) => max2 === null || row.syncedAt > max2 ? row.syncedAt : max2, null);
84212
- const summary3 = ctx.db.select({ start: gaTrafficSummaries.periodStart, end: gaTrafficSummaries.periodEnd, syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq72(gaTrafficSummaries.projectId, projectId)).get();
84311
+ const summary3 = ctx.db.select({ start: gaTrafficSummaries.periodStart, end: gaTrafficSummaries.periodEnd, syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq73(gaTrafficSummaries.projectId, projectId)).get();
84213
84312
  if (summary3 && (latest2 === null || summary3.syncedAt >= latest2)) return { start: summary3.start, end: summary3.end, syncedAt: summary3.syncedAt };
84214
84313
  const span2 = dateSpan(rows2.filter((row) => row.syncedAt === latest2).map((row) => row.date));
84215
84314
  return span2 && latest2 !== null ? { ...span2, syncedAt: latest2 } : null;
84216
84315
  }
84217
- const rows = ctx.db.select({ date: gscDailyTotals.date, createdAt: gscDailyTotals.createdAt }).from(gscDailyTotals).where(eq72(gscDailyTotals.projectId, projectId)).all();
84316
+ const rows = ctx.db.select({ date: gscDailyTotals.date, createdAt: gscDailyTotals.createdAt }).from(gscDailyTotals).where(eq73(gscDailyTotals.projectId, projectId)).all();
84218
84317
  const latest = rows.reduce((max2, row) => max2 === null || row.createdAt > max2 ? row.createdAt : max2, null);
84219
84318
  const span = dateSpan(rows.filter((row) => row.createdAt === latest).map((row) => row.date));
84220
84319
  if (!span || latest === null) return null;
84221
- const watermark = ctx.db.select({ through: gscDataWatermarks.syncedThroughDate, updatedAt: gscDataWatermarks.updatedAt }).from(gscDataWatermarks).where(eq72(gscDataWatermarks.projectId, projectId)).get();
84320
+ const watermark = ctx.db.select({ through: gscDataWatermarks.syncedThroughDate, updatedAt: gscDataWatermarks.updatedAt }).from(gscDataWatermarks).where(eq73(gscDataWatermarks.projectId, projectId)).get();
84222
84321
  return watermark?.through && watermark.updatedAt === latest && watermark.through > span.end ? { start: span.start, end: watermark.through, syncedAt: latest } : { ...span, syncedAt: latest };
84223
84322
  }
84224
84323
  function dailyCoverage(ctx, month, source) {
84225
84324
  const project = ctx.project;
84226
84325
  const connection = source === "ga" ? ctx.ga4CredentialStore?.getConnection(project.name) ?? ctx.googleConnectionStore?.getConnection(project.canonicalDomain, "ga4") : ctx.googleConnectionStore?.getConnection(project.canonicalDomain, "gsc");
84227
- const stored = source === "ga" ? ctx.db.select({ date: gaDailyTotals.date, count: gaDailyTotals.sessions }).from(gaDailyTotals).where(eq72(gaDailyTotals.projectId, project.id)).all() : ctx.db.select({ date: gscDailyTotals.date, count: gscDailyTotals.impressions }).from(gscDailyTotals).where(eq72(gscDailyTotals.projectId, project.id)).all();
84326
+ const stored = source === "ga" ? ctx.db.select({ date: gaDailyTotals.date, count: gaDailyTotals.sessions }).from(gaDailyTotals).where(eq73(gaDailyTotals.projectId, project.id)).all() : ctx.db.select({ date: gscDailyTotals.date, count: gscDailyTotals.impressions }).from(gscDailyTotals).where(eq73(gscDailyTotals.projectId, project.id)).all();
84228
84327
  if (!connection && stored.length === 0) return null;
84229
84328
  const bounds = calendarMonthBounds(month);
84230
84329
  const start = bounds.since.slice(0, 10);
84231
84330
  const end = bounds.until.slice(0, 10);
84232
84331
  const timeZone = source === "gsc" ? "America/Los_Angeles" : "UTC";
84233
84332
  const matureThrough = isoDateDaysBeforeInTimeZone((/* @__PURE__ */ new Date()).toISOString(), REPORTING_LAG_DAYS, timeZone);
84234
- const created = ctx.db.select({ date: projects.createdAt }).from(projects).where(eq72(projects.id, project.id)).get().date.slice(0, 10);
84333
+ const created = ctx.db.select({ date: projects.createdAt }).from(projects).where(eq73(projects.id, project.id)).get().date.slice(0, 10);
84235
84334
  const connectedAt = connection?.createdAt?.slice(0, 10) ?? created;
84236
84335
  const onboardingDate = [connectedAt, ...stored.map((row) => row.date)].sort()[0];
84237
84336
  const byDate = new Map(stored.map((row) => [row.date, row.count]));
@@ -84313,7 +84412,7 @@ var reportDailyDataCheck = {
84313
84412
  };
84314
84413
 
84315
84414
  // ../api-routes/src/doctor/checks/referral-assessment.ts
84316
- import { and as and63, eq as eq73, ne as ne14 } from "drizzle-orm";
84415
+ import { and as and64, eq as eq74, ne as ne14 } from "drizzle-orm";
84317
84416
  var REFERRAL_ASSESSMENT_CHECKS = [{
84318
84417
  id: "report.ai-referral-bursts",
84319
84418
  category: CheckCategories.integrations,
@@ -84322,7 +84421,7 @@ var REFERRAL_ASSESSMENT_CHECKS = [{
84322
84421
  notificationPolicy: CheckNotificationPolicies.silent,
84323
84422
  run: (ctx) => {
84324
84423
  if (!ctx.project) return { status: CheckStatuses.skipped, code: "report.ai-referral-bursts.no-project", summary: "Select a project to inspect referral evidence." };
84325
- const configured = ctx.db.select({ id: trafficSources.id }).from(trafficSources).where(and63(eq73(trafficSources.projectId, ctx.project.id), ne14(trafficSources.status, TrafficSourceStatuses.archived))).get();
84424
+ const configured = ctx.db.select({ id: trafficSources.id }).from(trafficSources).where(and64(eq74(trafficSources.projectId, ctx.project.id), ne14(trafficSources.status, TrafficSourceStatuses.archived))).get();
84326
84425
  if (!configured) {
84327
84426
  return { status: CheckStatuses.skipped, code: "report.ai-referral-bursts.not-configured", summary: "No non-archived server traffic source is configured for this project." };
84328
84427
  }
@@ -84682,7 +84781,6 @@ var skillsTriggerSurfaceCheck = {
84682
84781
  var AGENT_CHECKS = [skillsInstalledCheck, skillsCurrentCheck, skillsTriggerSurfaceCheck];
84683
84782
 
84684
84783
  // ../api-routes/src/doctor/checks/backlinks.ts
84685
- import { and as and64, eq as eq74 } from "drizzle-orm";
84686
84784
  function skippedNoProject() {
84687
84785
  return {
84688
84786
  status: CheckStatuses.skipped,
@@ -84698,36 +84796,58 @@ var BACKLINKS_CHECKS = [
84698
84796
  title: "Backlinks source connected",
84699
84797
  run: (ctx) => {
84700
84798
  if (!ctx.project) return skippedNoProject();
84701
- const projectRow = ctx.db.select({ autoExtract: projects.autoExtractBacklinks }).from(projects).where(eq74(projects.id, ctx.project.id)).get();
84702
- const readySync = ctx.db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq74(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
84703
- const ccConnected = projectRow?.autoExtract === true && !!readySync;
84704
- const connected = [];
84705
- if (ccConnected) connected.push(BacklinkSources.commoncrawl);
84706
- if (connected.length === 0) {
84799
+ const state = readCommonCrawlSourceState(ctx.db, ctx.project.id);
84800
+ const details = {
84801
+ commoncrawl: state.connected,
84802
+ commoncrawlReadyRelease: state.hasReadyRelease,
84803
+ commoncrawlHasData: state.hasProjectData
84804
+ };
84805
+ if (!state.hasReadyRelease) {
84806
+ return {
84807
+ status: CheckStatuses.warn,
84808
+ code: "backlinks.source.none",
84809
+ summary: `No backlink source is set up for ${ctx.project.name}: no Common Crawl release has been synced.`,
84810
+ remediation: "Run `canonry backlinks sync` to download the newest Common Crawl release for the workspace.",
84811
+ details
84812
+ };
84813
+ }
84814
+ if (!state.hasProjectData) {
84707
84815
  return {
84708
84816
  status: CheckStatuses.warn,
84709
84817
  code: "backlinks.source.none",
84710
- summary: `No backlink source is set up for ${ctx.project.name}.`,
84711
- remediation: "Enable Common Crawl by setting autoExtractBacklinks on the project, then run `canonry backlinks sync`.",
84712
- details: { commoncrawl: ccConnected }
84818
+ summary: state.hasCurrentReadyRelease ? `A Common Crawl release is synced, but no backlinks have been extracted for ${ctx.project.name} yet.` : `No backlinks have been extracted for ${ctx.project.name} yet, and no Common Crawl release is currently ready.`,
84819
+ remediation: state.hasCurrentReadyRelease ? `Run \`canonry backlinks extract ${ctx.project.name}\` to extract this project's backlinks from the synced release.` : "Run `canonry backlinks sync` to make a Common Crawl release ready and extract the workspace projects.",
84820
+ details
84713
84821
  };
84714
84822
  }
84715
- const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and64(
84716
- eq74(backlinkSummaries.projectId, ctx.project.id),
84717
- eq74(backlinkSummaries.source, BacklinkSources.commoncrawl)
84718
- )).limit(1).get() : false;
84823
+ const connected = [BacklinkSources.commoncrawl];
84719
84824
  return {
84720
84825
  status: CheckStatuses.ok,
84721
84826
  code: "backlinks.source.connected",
84722
84827
  summary: "Common Crawl backlink source is set up.",
84723
- remediation: ccConnected && !ccHasData ? `Common Crawl is ready but no backlinks have been extracted for ${ctx.project.name} yet \u2014 run \`canonry backlinks extract ${ctx.project.name}\`.` : null,
84724
- details: { commoncrawl: ccConnected, connected, commoncrawlHasData: ccHasData }
84828
+ remediation: null,
84829
+ details: { ...details, connected }
84725
84830
  };
84726
84831
  }
84727
84832
  }
84728
84833
  ];
84729
84834
 
84730
84835
  // ../api-routes/src/doctor/checks/bing-auth.ts
84836
+ import { and as and65, desc as desc36, eq as eq75, inArray as inArray30 } from "drizzle-orm";
84837
+ var BING_CONNECTION_ENTITY = "bing_connection";
84838
+ var BING_CONNECTED_ACTION = "bing.connected";
84839
+ var BING_DISCONNECTED_ACTION = "bing.disconnected";
84840
+ function lostBingConnection(ctx, project) {
84841
+ const hasRow = (row) => row !== void 0;
84842
+ const storedData = hasRow(ctx.db.select({ id: bingCoverageSnapshots.id }).from(bingCoverageSnapshots).where(eq75(bingCoverageSnapshots.projectId, project.id)).limit(1).get()) || hasRow(ctx.db.select({ id: bingUrlInspections.id }).from(bingUrlInspections).where(eq75(bingUrlInspections.projectId, project.id)).limit(1).get()) || hasRow(ctx.db.select({ id: bingKeywordStats.id }).from(bingKeywordStats).where(eq75(bingKeywordStats.projectId, project.id)).limit(1).get());
84843
+ if (!storedData) return false;
84844
+ const lastChange = ctx.db.select({ action: auditLog.action }).from(auditLog).where(and65(
84845
+ eq75(auditLog.entityType, BING_CONNECTION_ENTITY),
84846
+ eq75(auditLog.entityId, project.canonicalDomain),
84847
+ inArray30(auditLog.action, [BING_CONNECTED_ACTION, BING_DISCONNECTED_ACTION])
84848
+ )).orderBy(desc36(auditLog.createdAt)).limit(1).get();
84849
+ return lastChange?.action !== BING_DISCONNECTED_ACTION;
84850
+ }
84731
84851
  var BING_AUTH_CHECKS = [
84732
84852
  {
84733
84853
  id: "bing.auth.connection",
@@ -84754,10 +84874,18 @@ var BING_AUTH_CHECKS = [
84754
84874
  }
84755
84875
  const conn = store.getConnection(ctx.project.canonicalDomain);
84756
84876
  if (!conn) {
84877
+ if (!lostBingConnection(ctx, ctx.project)) {
84878
+ return {
84879
+ status: CheckStatuses.skipped,
84880
+ code: "bing.auth.not-connected",
84881
+ summary: `${ctx.project.name} has not connected Bing Webmaster Tools.`,
84882
+ remediation: `Optional: run \`canonry bing connect ${ctx.project.name} --api-key <key>\` to track Bing indexing.`
84883
+ };
84884
+ }
84757
84885
  return {
84758
84886
  status: CheckStatuses.fail,
84759
84887
  code: "bing.auth.no-connection",
84760
- summary: `No Bing connection for ${ctx.project.canonicalDomain}.`,
84888
+ summary: `No Bing connection for ${ctx.project.canonicalDomain}, but ${ctx.project.name} has stored Bing data from an earlier connection.`,
84761
84889
  remediation: `Run \`canonry bing connect ${ctx.project.name} --api-key <key>\` to authorize.`
84762
84890
  };
84763
84891
  }
@@ -84874,7 +85002,7 @@ var BING_AUTH_CHECKS = [
84874
85002
  ];
84875
85003
 
84876
85004
  // ../api-routes/src/doctor/checks/content.ts
84877
- import { eq as eq75 } from "drizzle-orm";
85005
+ import { eq as eq76 } from "drizzle-orm";
84878
85006
  var WINNABILITY_COVERAGE_WARN_THRESHOLD = 0.8;
84879
85007
  var UNCLASSIFIED_DOMAIN_SAMPLE_LIMIT = 10;
84880
85008
  function skippedNoProject2() {
@@ -84887,13 +85015,17 @@ function skippedNoProject2() {
84887
85015
  }
84888
85016
  function loadProject(ctx) {
84889
85017
  if (!ctx.project) return null;
84890
- return ctx.db.select().from(projects).where(eq75(projects.id, ctx.project.id)).get() ?? null;
85018
+ return ctx.db.select().from(projects).where(eq76(projects.id, ctx.project.id)).get() ?? null;
84891
85019
  }
84892
85020
  var winnabilityCoverageCheck = {
84893
85021
  id: "content.winnability.coverage",
84894
85022
  category: CheckCategories.integrations,
84895
85023
  scope: CheckScopes.project,
84896
85024
  title: "Content winnability classification coverage",
85025
+ // Advice about content targeting, not a broken instrument. Most projects sit
85026
+ // below the threshold for good, and a check that never clears keeps the
85027
+ // project permanently degraded, so no recovery could ever be sent.
85028
+ notificationPolicy: CheckNotificationPolicies.silent,
84897
85029
  run: (ctx) => {
84898
85030
  if (!ctx.project) return skippedNoProject2();
84899
85031
  const project = loadProject(ctx);
@@ -84976,7 +85108,7 @@ var CONTENT_CHECK_BY_ID = Object.fromEntries(
84976
85108
  );
84977
85109
 
84978
85110
  // ../api-routes/src/doctor/checks/ads.ts
84979
- import { eq as eq76 } from "drizzle-orm";
85111
+ import { eq as eq77 } from "drizzle-orm";
84980
85112
  var RECENT_SYNC_WARN_DAYS = 7;
84981
85113
  var RECENT_SYNC_FAIL_DAYS = 30;
84982
85114
  var adsConnectionCheck = {
@@ -84993,7 +85125,7 @@ var adsConnectionCheck = {
84993
85125
  remediation: null
84994
85126
  };
84995
85127
  }
84996
- const row = ctx.db.select().from(adsConnections).where(eq76(adsConnections.projectId, ctx.project.id)).get();
85128
+ const row = ctx.db.select().from(adsConnections).where(eq77(adsConnections.projectId, ctx.project.id)).get();
84997
85129
  if (!row) {
84998
85130
  return {
84999
85131
  status: CheckStatuses.skipped,
@@ -85043,7 +85175,7 @@ var adsRecentSyncCheck = {
85043
85175
  remediation: null
85044
85176
  };
85045
85177
  }
85046
- const row = ctx.db.select().from(adsConnections).where(eq76(adsConnections.projectId, ctx.project.id)).get();
85178
+ const row = ctx.db.select().from(adsConnections).where(eq77(adsConnections.projectId, ctx.project.id)).get();
85047
85179
  if (!row) {
85048
85180
  return {
85049
85181
  status: CheckStatuses.skipped,
@@ -85253,7 +85385,7 @@ var ga4ConnectionCheck = {
85253
85385
  var GA_AUTH_CHECKS = [ga4ConnectionCheck];
85254
85386
 
85255
85387
  // ../api-routes/src/doctor/checks/data-freshness.ts
85256
- import { and as and65, desc as desc36, eq as eq77, inArray as inArray30 } from "drizzle-orm";
85388
+ import { and as and66, desc as desc37, eq as eq78, inArray as inArray31 } from "drizzle-orm";
85257
85389
  var GA_DATA_AGING_DAYS = 3;
85258
85390
  var GA_DATA_STALE_DAYS = 5;
85259
85391
  var GSC_DATA_AGING_DAYS = 5;
@@ -85281,7 +85413,7 @@ function skipped(code, summary3) {
85281
85413
  return { status: CheckStatuses.skipped, code, summary: summary3, remediation: null };
85282
85414
  }
85283
85415
  function lastSyncAgeDays(ctx, kinds) {
85284
- const row = ctx.db.select({ createdAt: runs.createdAt }).from(runs).where(and65(eq77(runs.projectId, ctx.project.id), inArray30(runs.kind, kinds), eq77(runs.status, "completed"))).orderBy(desc36(runs.createdAt)).limit(1).get();
85416
+ const row = ctx.db.select({ createdAt: runs.createdAt }).from(runs).where(and66(eq78(runs.projectId, ctx.project.id), inArray31(runs.kind, kinds), eq78(runs.status, "completed"))).orderBy(desc37(runs.createdAt)).limit(1).get();
85285
85417
  if (!row?.createdAt) return null;
85286
85418
  const age = Math.floor((Date.now() - Date.parse(row.createdAt)) / DAY_MS2);
85287
85419
  return Number.isFinite(age) ? age : null;
@@ -85377,7 +85509,7 @@ var gaRecentDataCheck = {
85377
85509
  const connection = gaConnected(ctx);
85378
85510
  if (connection === "store-unavailable") return skipped("ga.data.store-unavailable", "No GA4 credential store configured for this deployment.");
85379
85511
  if (connection === "not-connected") return skipped("ga.data.not-connected", "GA4 is not connected for this project.");
85380
- const newest = ctx.db.select({ date: gaDailyTotals.date }).from(gaDailyTotals).where(eq77(gaDailyTotals.projectId, ctx.project.id)).orderBy(desc36(gaDailyTotals.date)).limit(1).get();
85512
+ const newest = ctx.db.select({ date: gaDailyTotals.date }).from(gaDailyTotals).where(eq78(gaDailyTotals.projectId, ctx.project.id)).orderBy(desc37(gaDailyTotals.date)).limit(1).get();
85381
85513
  return grade(ctx, GA_SPEC, newest?.date);
85382
85514
  }
85383
85515
  };
@@ -85392,9 +85524,9 @@ var gscRecentDataCheck = {
85392
85524
  if (!ctx.googleConnectionStore.getConnection(ctx.project.canonicalDomain, "gsc")) {
85393
85525
  return skipped("gsc.data.not-connected", "Search Console is not connected for this project.");
85394
85526
  }
85395
- const watermark = ctx.db.select({ dataThroughDate: gscDataWatermarks.dataThroughDate }).from(gscDataWatermarks).where(eq77(gscDataWatermarks.projectId, ctx.project.id)).get();
85527
+ const watermark = ctx.db.select({ dataThroughDate: gscDataWatermarks.dataThroughDate }).from(gscDataWatermarks).where(eq78(gscDataWatermarks.projectId, ctx.project.id)).get();
85396
85528
  if (watermark?.dataThroughDate) return grade(ctx, GSC_SPEC, watermark.dataThroughDate);
85397
- const newest = ctx.db.select({ date: gscDailyTotals.date }).from(gscDailyTotals).where(eq77(gscDailyTotals.projectId, ctx.project.id)).orderBy(desc36(gscDailyTotals.date)).limit(1).get();
85529
+ const newest = ctx.db.select({ date: gscDailyTotals.date }).from(gscDailyTotals).where(eq78(gscDailyTotals.projectId, ctx.project.id)).orderBy(desc37(gscDailyTotals.date)).limit(1).get();
85398
85530
  return grade(ctx, GSC_SPEC, newest?.date);
85399
85531
  }
85400
85532
  };
@@ -85662,7 +85794,7 @@ var siteReachabilityCheck = {
85662
85794
  var SITE_REACHABILITY_CHECKS = [siteReachabilityCheck];
85663
85795
 
85664
85796
  // ../api-routes/src/doctor/checks/gbp-auth.ts
85665
- import { and as and66, eq as eq78 } from "drizzle-orm";
85797
+ import { and as and67, eq as eq79 } from "drizzle-orm";
85666
85798
  var RECENT_SYNC_WARN_DAYS2 = 4;
85667
85799
  var RECENT_SYNC_FAIL_DAYS2 = 30;
85668
85800
  function skippedNoProject3() {
@@ -85681,10 +85813,25 @@ function storeUnavailable() {
85681
85813
  remediation: null
85682
85814
  };
85683
85815
  }
85816
+ function hasSelectedLocation(ctx, projectId) {
85817
+ return !!ctx.db.select({ id: gbpLocations.id }).from(gbpLocations).where(and67(eq79(gbpLocations.projectId, projectId), eq79(gbpLocations.selected, true))).limit(1).get();
85818
+ }
85684
85819
  async function resolveGbpToken(ctx) {
85685
85820
  if (!ctx.project) return { ok: false, output: skippedNoProject3() };
85686
85821
  const store = ctx.googleConnectionStore;
85687
85822
  if (!store) return { ok: false, output: storeUnavailable() };
85823
+ const conn = store.getConnection(ctx.project.canonicalDomain, "gbp");
85824
+ if (!conn && !hasSelectedLocation(ctx, ctx.project.id)) {
85825
+ return {
85826
+ ok: false,
85827
+ output: {
85828
+ status: CheckStatuses.skipped,
85829
+ code: "gbp.auth.not-connected",
85830
+ summary: `${ctx.project.name} has not connected Google Business Profile.`,
85831
+ remediation: `Optional: run \`canonry gbp connect ${ctx.project.name}\` to track a Business Profile.`
85832
+ }
85833
+ };
85834
+ }
85688
85835
  const auth = ctx.getGoogleAuthConfig?.() ?? {};
85689
85836
  if (!auth.clientId || !auth.clientSecret) {
85690
85837
  return {
@@ -85697,14 +85844,13 @@ async function resolveGbpToken(ctx) {
85697
85844
  }
85698
85845
  };
85699
85846
  }
85700
- const conn = store.getConnection(ctx.project.canonicalDomain, "gbp");
85701
85847
  if (!conn) {
85702
85848
  return {
85703
85849
  ok: false,
85704
85850
  output: {
85705
85851
  status: CheckStatuses.fail,
85706
85852
  code: "gbp.auth.no-connection",
85707
- summary: `No Google Business Profile connection for ${ctx.project.canonicalDomain}.`,
85853
+ summary: `No Google Business Profile connection for ${ctx.project.canonicalDomain}, but ${ctx.project.name} still has selected locations.`,
85708
85854
  remediation: `Run \`canonry gbp connect ${ctx.project.name}\` to authorize.`
85709
85855
  }
85710
85856
  };
@@ -85918,7 +86064,7 @@ var recentSyncCheck = {
85918
86064
  title: "GBP recent sync",
85919
86065
  run: (ctx) => {
85920
86066
  if (!ctx.project) return skippedNoProject3();
85921
- const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and66(eq78(gbpLocations.projectId, ctx.project.id), eq78(gbpLocations.selected, true))).all();
86067
+ const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and67(eq79(gbpLocations.projectId, ctx.project.id), eq79(gbpLocations.selected, true))).all();
85922
86068
  if (selected.length === 0) {
85923
86069
  return {
85924
86070
  status: CheckStatuses.skipped,
@@ -85978,7 +86124,7 @@ var GBP_AUTH_CHECK_BY_ID = Object.fromEntries(
85978
86124
  );
85979
86125
 
85980
86126
  // ../api-routes/src/doctor/checks/places.ts
85981
- import { eq as eq79 } from "drizzle-orm";
86127
+ import { eq as eq80 } from "drizzle-orm";
85982
86128
  var apiKeyCheck = {
85983
86129
  id: "gbp.places.api-key",
85984
86130
  category: CheckCategories.auth,
@@ -86023,7 +86169,7 @@ var apiKeyCheck = {
86023
86169
  details: { tier: cfg.tier }
86024
86170
  };
86025
86171
  }
86026
- const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq79(gbpLocations.projectId, ctx.project.id)).all();
86172
+ const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq80(gbpLocations.projectId, ctx.project.id)).all();
86027
86173
  const selected = rows.filter((r) => r.selected);
86028
86174
  const locationsWithPlaceId = selected.filter((r) => Boolean(r.placeId)).length;
86029
86175
  const details = {
@@ -86056,7 +86202,7 @@ var PLACES_CHECK_BY_ID = Object.fromEntries(
86056
86202
  );
86057
86203
 
86058
86204
  // ../api-routes/src/doctor/checks/gbp-reviews.ts
86059
- import { and as and67, eq as eq80 } from "drizzle-orm";
86205
+ import { and as and68, eq as eq81 } from "drizzle-orm";
86060
86206
  var REVIEW_EVENTS = /* @__PURE__ */ new Set(["review.negative", "review.rating-dropped"]);
86061
86207
  var V4_ACCESS_REMEDIATION = "Business Profile reviews use the legacy My Business API (mybusiness.googleapis.com), which Google enables per Cloud project only on request; the API Library and `gcloud services enable` cannot turn it on. Open the enable link in the API access approval email as the approved account, or reply to the access-request thread asking Google to enable mybusiness.googleapis.com for your project number, then run `canonry gbp sync`.";
86062
86208
  var reviewsAccessCheck = {
@@ -86084,7 +86230,7 @@ var reviewsAccessCheck = {
86084
86230
  reviewsAccess: gbpLocations.reviewsAccess,
86085
86231
  reviewsAccessReason: gbpLocations.reviewsAccessReason,
86086
86232
  reviewsCheckedAt: gbpLocations.reviewsCheckedAt
86087
- }).from(gbpLocations).where(and67(eq80(gbpLocations.projectId, project.id), eq80(gbpLocations.selected, true))).all();
86233
+ }).from(gbpLocations).where(and68(eq81(gbpLocations.projectId, project.id), eq81(gbpLocations.selected, true))).all();
86088
86234
  if (locations.length === 0) {
86089
86235
  return {
86090
86236
  status: CheckStatuses.skipped,
@@ -86104,7 +86250,7 @@ var reviewsAccessCheck = {
86104
86250
  const places = ctx.getPlacesConfig?.();
86105
86251
  const placesFallback = places !== void 0 && places.tier === "atmosphere" && Boolean(places.apiKey);
86106
86252
  const v4Worked = new Set(
86107
- ctx.db.select({ locationName: gbpReviewRatings.locationName }).from(gbpReviewRatings).where(and67(eq80(gbpReviewRatings.projectId, project.id), eq80(gbpReviewRatings.origin, "gbp"))).all().map((row) => row.locationName)
86253
+ ctx.db.select({ locationName: gbpReviewRatings.locationName }).from(gbpReviewRatings).where(and68(eq81(gbpReviewRatings.projectId, project.id), eq81(gbpReviewRatings.origin, "gbp"))).all().map((row) => row.locationName)
86108
86254
  );
86109
86255
  const coverageOf = (loc) => {
86110
86256
  if (businessProfileReviewsCover(loc.reviewsAccess ?? null, v4Worked.has(loc.locationName))) return "full";
@@ -86138,7 +86284,7 @@ var reviewsAccessCheck = {
86138
86284
  none > 0 ? `${none} have no review source` : null
86139
86285
  ].filter((part) => part !== null);
86140
86286
  const summary3 = `Business Profile reviews are not available${why}. ${parts.join("; ")}.`;
86141
- const subscribed = ctx.db.select({ config: notifications.config, enabled: notifications.enabled }).from(notifications).where(eq80(notifications.projectId, project.id)).all().some((n) => n.enabled && (n.config.events ?? []).some((event) => REVIEW_EVENTS.has(event)));
86287
+ const subscribed = ctx.db.select({ config: notifications.config, enabled: notifications.enabled }).from(notifications).where(eq81(notifications.projectId, project.id)).all().some((n) => n.enabled && (n.config.events ?? []).some((event) => REVIEW_EVENTS.has(event)));
86142
86288
  const placesHint = placesFallback ? "" : " For partial coverage meanwhile, set a Places API key (`GOOGLE_PLACES_API_KEY` or `places.apiKey`) with `places.tier: atmosphere`.";
86143
86289
  if (none > 0 && subscribed) {
86144
86290
  return {
@@ -86696,7 +86842,7 @@ var RUNTIME_STATE_CHECKS = [
86696
86842
  ];
86697
86843
 
86698
86844
  // ../api-routes/src/doctor/checks/traffic-source.ts
86699
- import { and as and68, eq as eq81, gte as gte19, inArray as inArray31, ne as ne15, sql as sql29 } from "drizzle-orm";
86845
+ import { and as and69, eq as eq82, gte as gte19, inArray as inArray32, ne as ne15, sql as sql29 } from "drizzle-orm";
86700
86846
  var RECENT_DATA_WARN_DAYS = 7;
86701
86847
  var RECENT_DATA_FAIL_DAYS = 30;
86702
86848
  function isCloudflareDirectPush(source) {
@@ -86735,8 +86881,8 @@ function skippedNoProject5() {
86735
86881
  function loadProbes(ctx) {
86736
86882
  if (!ctx.project) return [];
86737
86883
  const rows = ctx.db.select().from(trafficSources).where(
86738
- and68(
86739
- eq81(trafficSources.projectId, ctx.project.id),
86884
+ and69(
86885
+ eq82(trafficSources.projectId, ctx.project.id),
86740
86886
  ne15(trafficSources.status, TrafficSourceStatuses.archived)
86741
86887
  )
86742
86888
  ).all();
@@ -86842,27 +86988,27 @@ var recentDataCheck = {
86842
86988
  const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
86843
86989
  const recentCrawlers = Number(
86844
86990
  ctx.db.select({ total: sql29`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
86845
- and68(
86846
- eq81(crawlerEventsHourly.projectId, ctx.project.id),
86847
- inArray31(crawlerEventsHourly.sourceId, activeSourceIds),
86991
+ and69(
86992
+ eq82(crawlerEventsHourly.projectId, ctx.project.id),
86993
+ inArray32(crawlerEventsHourly.sourceId, activeSourceIds),
86848
86994
  gte19(crawlerEventsHourly.tsHour, warnCutoff)
86849
86995
  )
86850
86996
  ).get()?.total ?? 0
86851
86997
  );
86852
86998
  const recentReferrals = Number(
86853
86999
  ctx.db.select({ total: sql29`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
86854
- and68(
86855
- eq81(aiReferralEventsHourly.projectId, ctx.project.id),
86856
- inArray31(aiReferralEventsHourly.sourceId, activeSourceIds),
87000
+ and69(
87001
+ eq82(aiReferralEventsHourly.projectId, ctx.project.id),
87002
+ inArray32(aiReferralEventsHourly.sourceId, activeSourceIds),
86857
87003
  gte19(aiReferralEventsHourly.tsHour, warnCutoff)
86858
87004
  )
86859
87005
  ).get()?.total ?? 0
86860
87006
  );
86861
87007
  const recentUserFetches = Number(
86862
87008
  ctx.db.select({ total: sql29`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
86863
- and68(
86864
- eq81(aiUserFetchEventsHourly.projectId, ctx.project.id),
86865
- inArray31(aiUserFetchEventsHourly.sourceId, activeSourceIds),
87009
+ and69(
87010
+ eq82(aiUserFetchEventsHourly.projectId, ctx.project.id),
87011
+ inArray32(aiUserFetchEventsHourly.sourceId, activeSourceIds),
86866
87012
  gte19(aiUserFetchEventsHourly.tsHour, warnCutoff)
86867
87013
  )
86868
87014
  ).get()?.total ?? 0
@@ -86886,27 +87032,27 @@ var recentDataCheck = {
86886
87032
  }
86887
87033
  const olderCrawlers = Number(
86888
87034
  ctx.db.select({ total: sql29`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
86889
- and68(
86890
- eq81(crawlerEventsHourly.projectId, ctx.project.id),
86891
- inArray31(crawlerEventsHourly.sourceId, activeSourceIds),
87035
+ and69(
87036
+ eq82(crawlerEventsHourly.projectId, ctx.project.id),
87037
+ inArray32(crawlerEventsHourly.sourceId, activeSourceIds),
86892
87038
  gte19(crawlerEventsHourly.tsHour, failCutoff)
86893
87039
  )
86894
87040
  ).get()?.total ?? 0
86895
87041
  );
86896
87042
  const olderReferrals = Number(
86897
87043
  ctx.db.select({ total: sql29`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
86898
- and68(
86899
- eq81(aiReferralEventsHourly.projectId, ctx.project.id),
86900
- inArray31(aiReferralEventsHourly.sourceId, activeSourceIds),
87044
+ and69(
87045
+ eq82(aiReferralEventsHourly.projectId, ctx.project.id),
87046
+ inArray32(aiReferralEventsHourly.sourceId, activeSourceIds),
86901
87047
  gte19(aiReferralEventsHourly.tsHour, failCutoff)
86902
87048
  )
86903
87049
  ).get()?.total ?? 0
86904
87050
  );
86905
87051
  const olderUserFetches = Number(
86906
87052
  ctx.db.select({ total: sql29`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
86907
- and68(
86908
- eq81(aiUserFetchEventsHourly.projectId, ctx.project.id),
86909
- inArray31(aiUserFetchEventsHourly.sourceId, activeSourceIds),
87053
+ and69(
87054
+ eq82(aiUserFetchEventsHourly.projectId, ctx.project.id),
87055
+ inArray32(aiUserFetchEventsHourly.sourceId, activeSourceIds),
86910
87056
  gte19(aiUserFetchEventsHourly.tsHour, failCutoff)
86911
87057
  )
86912
87058
  ).get()?.total ?? 0
@@ -87080,6 +87226,7 @@ var cacheBlindSpotCheck = {
87080
87226
  category: CheckCategories.integrations,
87081
87227
  scope: CheckScopes.project,
87082
87228
  title: "WordPress traffic cache blind spot",
87229
+ notificationPolicy: CheckNotificationPolicies.silent,
87083
87230
  run: (ctx) => {
87084
87231
  if (!ctx.project) return skippedNoProject5();
87085
87232
  const wpSources = loadProbes(ctx).filter(
@@ -87679,7 +87826,7 @@ var GOOGLE_MARKETING_DOCTOR_CHECKS = createGoogleMarketingDoctorChecks(
87679
87826
  );
87680
87827
 
87681
87828
  // ../api-routes/src/doctor/checks/integration-sync.ts
87682
- import { and as and69, count as count3, desc as desc37, eq as eq82, gt as gt7, gte as gte20, inArray as inArray32, isNull as isNull14, notInArray as notInArray2, or as or18 } from "drizzle-orm";
87829
+ import { and as and70, count as count3, desc as desc38, eq as eq83, gt as gt7, gte as gte20, inArray as inArray33, isNull as isNull14, notInArray as notInArray2, or as or19 } from "drizzle-orm";
87683
87830
  var SYNC_FAILURE_WINDOW = 3;
87684
87831
  var SUMMARY_ERROR_LIMIT = 300;
87685
87832
  var FINISHED_STATUSES = [RunStatuses.completed, RunStatuses.partial, RunStatuses.failed];
@@ -87699,7 +87846,7 @@ function gscConnection(ctx, project) {
87699
87846
  function gbpConnection(ctx, project) {
87700
87847
  if (!ctx.googleConnectionStore) return { state: "store-unavailable" };
87701
87848
  if (!ctx.googleConnectionStore.getConnection(project.canonicalDomain, "gbp")) return { state: "not-connected" };
87702
- const selected = ctx.db.select({ locationName: gbpLocations.locationName }).from(gbpLocations).where(and69(eq82(gbpLocations.projectId, project.id), eq82(gbpLocations.selected, true))).limit(1).get();
87849
+ const selected = ctx.db.select({ locationName: gbpLocations.locationName }).from(gbpLocations).where(and70(eq83(gbpLocations.projectId, project.id), eq83(gbpLocations.selected, true))).limit(1).get();
87703
87850
  return selected ? CONNECTED : { state: "no-selected-locations" };
87704
87851
  }
87705
87852
  function gaConnection(ctx, project) {
@@ -87777,20 +87924,20 @@ function gradeSyncFailures(ctx, spec) {
87777
87924
  return skipped3(code("no-selected-locations"), `No ${spec.label} locations are selected for this project.`);
87778
87925
  }
87779
87926
  const nothingToSync = spec.nothingToSyncErrors ?? [];
87780
- const scope = and69(
87781
- eq82(runs.projectId, project.id),
87782
- eq82(runs.kind, spec.kind),
87783
- ...nothingToSync.length > 0 ? [or18(isNull14(runs.error), notInArray2(runs.error, [...nothingToSync]))] : []
87927
+ const scope = and70(
87928
+ eq83(runs.projectId, project.id),
87929
+ eq83(runs.kind, spec.kind),
87930
+ ...nothingToSync.length > 0 ? [or19(isNull14(runs.error), notInArray2(runs.error, [...nothingToSync]))] : []
87784
87931
  );
87785
- const graded = connection.since ? and69(scope, gte20(runs.createdAt, connection.since)) : scope;
87786
- const recent = ctx.db.select({ status: runs.status, error: runs.error, createdAt: runs.createdAt }).from(runs).where(and69(graded, inArray32(runs.status, FINISHED_STATUSES))).orderBy(desc37(runs.createdAt)).limit(SYNC_FAILURE_WINDOW).all();
87932
+ const graded = connection.since ? and70(scope, gte20(runs.createdAt, connection.since)) : scope;
87933
+ const recent = ctx.db.select({ status: runs.status, error: runs.error, createdAt: runs.createdAt }).from(runs).where(and70(graded, inArray33(runs.status, FINISHED_STATUSES))).orderBy(desc38(runs.createdAt)).limit(SYNC_FAILURE_WINDOW).all();
87787
87934
  if (recent.length === 0) {
87788
87935
  return skipped3(code("no-runs"), `${spec.label} is connected but no sync has finished yet.`);
87789
87936
  }
87790
- const lastSuccess = ctx.db.select({ createdAt: runs.createdAt, finishedAt: runs.finishedAt }).from(runs).where(and69(graded, inArray32(runs.status, SUCCESS_STATUSES))).orderBy(desc37(runs.createdAt)).limit(1).get();
87791
- const failuresSinceSuccess = ctx.db.select({ value: count3() }).from(runs).where(and69(
87937
+ const lastSuccess = ctx.db.select({ createdAt: runs.createdAt, finishedAt: runs.finishedAt }).from(runs).where(and70(graded, inArray33(runs.status, SUCCESS_STATUSES))).orderBy(desc38(runs.createdAt)).limit(1).get();
87938
+ const failuresSinceSuccess = ctx.db.select({ value: count3() }).from(runs).where(and70(
87792
87939
  graded,
87793
- eq82(runs.status, RunStatuses.failed),
87940
+ eq83(runs.status, RunStatuses.failed),
87794
87941
  ...lastSuccess ? [gt7(runs.createdAt, lastSuccess.createdAt)] : []
87795
87942
  )).get()?.value ?? 0;
87796
87943
  const lastSuccessAt = lastSuccess ? lastSuccess.finishedAt ?? lastSuccess.createdAt : null;
@@ -88104,7 +88251,7 @@ async function doctorRoutes(app, opts) {
88104
88251
 
88105
88252
  // ../api-routes/src/discovery/routes.ts
88106
88253
  import crypto50 from "crypto";
88107
- import { and as and70, desc as desc38, eq as eq83, gte as gte21, inArray as inArray33, isNull as isNull15, or as or19 } from "drizzle-orm";
88254
+ import { and as and71, desc as desc39, eq as eq84, gte as gte21, inArray as inArray34, isNull as isNull15, or as or20 } from "drizzle-orm";
88108
88255
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
88109
88256
  async function discoveryRoutes(app, opts) {
88110
88257
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -88138,13 +88285,13 @@ async function discoveryRoutes(app, opts) {
88138
88285
  const now = (/* @__PURE__ */ new Date()).toISOString();
88139
88286
  const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
88140
88287
  const decision = app.db.transaction((tx) => {
88141
- const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and70(
88142
- eq83(discoverySessions.projectId, project.id),
88143
- eq83(discoverySessions.icpDescription, icpDescription),
88288
+ const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and71(
88289
+ eq84(discoverySessions.projectId, project.id),
88290
+ eq84(discoverySessions.icpDescription, icpDescription),
88144
88291
  // Buyer is part of session identity: it changes the seed prompt's
88145
88292
  // semantics, so a request with a different (or no) buyer must start
88146
88293
  // its own session, never adopt another buyer's probes.
88147
- parsed.data.buyerDescription == null ? isNull15(discoverySessions.buyerDescription) : eq83(discoverySessions.buyerDescription, parsed.data.buyerDescription),
88294
+ parsed.data.buyerDescription == null ? isNull15(discoverySessions.buyerDescription) : eq84(discoverySessions.buyerDescription, parsed.data.buyerDescription),
88148
88295
  // Locations are identity too: a different service-area subset seeds
88149
88296
  // and probes a different geo, so it must never reuse another geo's
88150
88297
  // session. resolveLocations is deterministic (project-config order),
@@ -88154,18 +88301,18 @@ async function discoveryRoutes(app, opts) {
88154
88301
  // locations a NULL row's subset is unknowable, so it conservatively
88155
88302
  // never reuses — a one-time, bounded (2h window) non-reuse after
88156
88303
  // upgrade, never a wrong reuse.
88157
- locations.length === 0 ? or19(isNull15(discoverySessions.locations), eq83(discoverySessions.locations, locations)) : eq83(discoverySessions.locations, locations),
88304
+ locations.length === 0 ? or20(isNull15(discoverySessions.locations), eq84(discoverySessions.locations, locations)) : eq84(discoverySessions.locations, locations),
88158
88305
  // Seed provider set is identity: a different phrasing distribution
88159
88306
  // must never reuse another set's session. Null (= Gemini-only
88160
88307
  // default, including explicit ['gemini']) matches legacy rows.
88161
- seedProviders == null ? isNull15(discoverySessions.seedProviders) : eq83(discoverySessions.seedProviders, seedProviders),
88162
- inArray33(discoverySessions.status, [
88308
+ seedProviders == null ? isNull15(discoverySessions.seedProviders) : eq84(discoverySessions.seedProviders, seedProviders),
88309
+ inArray34(discoverySessions.status, [
88163
88310
  DiscoverySessionStatuses.queued,
88164
88311
  DiscoverySessionStatuses.seeding,
88165
88312
  DiscoverySessionStatuses.probing
88166
88313
  ]),
88167
88314
  gte21(discoverySessions.createdAt, ageFloorIso)
88168
- )).orderBy(desc38(discoverySessions.createdAt)).get();
88315
+ )).orderBy(desc39(discoverySessions.createdAt)).get();
88169
88316
  if (existing && existing.runId) {
88170
88317
  return { reused: true, sessionId: existing.id, runId: existing.runId };
88171
88318
  }
@@ -88234,7 +88381,7 @@ async function discoveryRoutes(app, opts) {
88234
88381
  const project = resolveProject(app.db, request.params.name);
88235
88382
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
88236
88383
  const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? 50 : parsedLimit;
88237
- const rows = app.db.select().from(discoverySessions).where(eq83(discoverySessions.projectId, project.id)).orderBy(desc38(discoverySessions.createdAt)).limit(limit).all();
88384
+ const rows = app.db.select().from(discoverySessions).where(eq84(discoverySessions.projectId, project.id)).orderBy(desc39(discoverySessions.createdAt)).limit(limit).all();
88238
88385
  return reply.send(rows.map(serializeSession));
88239
88386
  }
88240
88387
  );
@@ -88242,11 +88389,11 @@ async function discoveryRoutes(app, opts) {
88242
88389
  "/projects/:name/discover/sessions/:id",
88243
88390
  async (request, reply) => {
88244
88391
  const project = resolveProject(app.db, request.params.name);
88245
- const session = app.db.select().from(discoverySessions).where(eq83(discoverySessions.id, request.params.id)).get();
88392
+ const session = app.db.select().from(discoverySessions).where(eq84(discoverySessions.id, request.params.id)).get();
88246
88393
  if (!session || session.projectId !== project.id) {
88247
88394
  throw notFound("Discovery session", request.params.id);
88248
88395
  }
88249
- const probeRows = app.db.select().from(discoveryProbes).where(eq83(discoveryProbes.sessionId, session.id)).all();
88396
+ const probeRows = app.db.select().from(discoveryProbes).where(eq84(discoveryProbes.sessionId, session.id)).all();
88250
88397
  const detail = {
88251
88398
  ...serializeSession(session),
88252
88399
  probes: probeRows.map(serializeProbe)
@@ -88258,7 +88405,7 @@ async function discoveryRoutes(app, opts) {
88258
88405
  "/projects/:name/discover/sessions/:id/harvest",
88259
88406
  async (request, reply) => {
88260
88407
  const project = resolveProject(app.db, request.params.name);
88261
- const session = app.db.select().from(discoverySessions).where(eq83(discoverySessions.id, request.params.id)).get();
88408
+ const session = app.db.select().from(discoverySessions).where(eq84(discoverySessions.id, request.params.id)).get();
88262
88409
  if (!session || session.projectId !== project.id) {
88263
88410
  throw notFound("Discovery session", request.params.id);
88264
88411
  }
@@ -88266,7 +88413,7 @@ async function discoveryRoutes(app, opts) {
88266
88413
  const minProbeHits = Number.isNaN(parsedFloor) || parsedFloor < 1 ? 1 : parsedFloor;
88267
88414
  const applyAnchor = request.query.anchor !== "false";
88268
88415
  const provider = session.seedProvider ?? "gemini";
88269
- const probeRows = app.db.select().from(discoveryProbes).where(eq83(discoveryProbes.sessionId, session.id)).all();
88416
+ const probeRows = app.db.select().from(discoveryProbes).where(eq84(discoveryProbes.sessionId, session.id)).all();
88270
88417
  const extract = opts.harvestSearchQueries;
88271
88418
  const probesWithQueries = probeRows.map((row) => {
88272
88419
  if (!extract || !row.rawResponse) return { searchQueries: [] };
@@ -88277,7 +88424,7 @@ async function discoveryRoutes(app, opts) {
88277
88424
  return { searchQueries: [] };
88278
88425
  }
88279
88426
  });
88280
- const trackedQueries = app.db.select({ query: queries.query }).from(queries).where(eq83(queries.projectId, project.id)).all().map((r) => r.query);
88427
+ const trackedQueries = app.db.select({ query: queries.query }).from(queries).where(eq84(queries.projectId, project.id)).all().map((r) => r.query);
88281
88428
  const anchorTerms = buildHarvestAnchorTerms(
88282
88429
  [session.icpDescription ?? "", ...trackedQueries],
88283
88430
  effectiveDomains(project)
@@ -88324,11 +88471,11 @@ async function discoveryRoutes(app, opts) {
88324
88471
  "/projects/:name/discover/sessions/:id/promote",
88325
88472
  async (request, reply) => {
88326
88473
  const project = resolveProject(app.db, request.params.name);
88327
- const session = app.db.select().from(discoverySessions).where(eq83(discoverySessions.id, request.params.id)).get();
88474
+ const session = app.db.select().from(discoverySessions).where(eq84(discoverySessions.id, request.params.id)).get();
88328
88475
  if (!session || session.projectId !== project.id) {
88329
88476
  throw notFound("Discovery session", request.params.id);
88330
88477
  }
88331
- const probeRows = app.db.select().from(discoveryProbes).where(eq83(discoveryProbes.sessionId, session.id)).all();
88478
+ const probeRows = app.db.select().from(discoveryProbes).where(eq84(discoveryProbes.sessionId, session.id)).all();
88332
88479
  const cited = /* @__PURE__ */ new Set();
88333
88480
  const aspirational = /* @__PURE__ */ new Set();
88334
88481
  const wasted = /* @__PURE__ */ new Set();
@@ -88363,7 +88510,7 @@ async function discoveryRoutes(app, opts) {
88363
88510
  );
88364
88511
  app.post("/projects/:name/discover/sessions/:id/promote", async (request, reply) => {
88365
88512
  const project = resolveProject(app.db, request.params.name);
88366
- const session = app.db.select().from(discoverySessions).where(eq83(discoverySessions.id, request.params.id)).get();
88513
+ const session = app.db.select().from(discoverySessions).where(eq84(discoverySessions.id, request.params.id)).get();
88367
88514
  if (!session || session.projectId !== project.id) {
88368
88515
  throw notFound("Discovery session", request.params.id);
88369
88516
  }
@@ -88386,7 +88533,7 @@ async function discoveryRoutes(app, opts) {
88386
88533
  const bucketSet = new Set(buckets);
88387
88534
  const includeCompetitors = parsed.data.includeCompetitors ?? true;
88388
88535
  const competitorTypes = parsed.data.competitorTypes ?? DEFAULT_DISCOVERY_PROMOTE_COMPETITOR_TYPES;
88389
- const probeRows = app.db.select().from(discoveryProbes).where(eq83(discoveryProbes.sessionId, session.id)).all();
88536
+ const probeRows = app.db.select().from(discoveryProbes).where(eq84(discoveryProbes.sessionId, session.id)).all();
88390
88537
  const candidateQueries = /* @__PURE__ */ new Set();
88391
88538
  for (const probe of probeRows) {
88392
88539
  if (!probe.bucket) continue;
@@ -88394,7 +88541,7 @@ async function discoveryRoutes(app, opts) {
88394
88541
  if (bucket.success && bucketSet.has(bucket.data)) candidateQueries.add(probe.query);
88395
88542
  }
88396
88543
  const existingQueries = new Set(
88397
- app.db.select({ query: queries.query }).from(queries).where(eq83(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
88544
+ app.db.select({ query: queries.query }).from(queries).where(eq84(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
88398
88545
  );
88399
88546
  const promotedQueries = [];
88400
88547
  const skippedQueries = [];
@@ -88607,7 +88754,7 @@ function planCompetitorPromotion(input) {
88607
88754
 
88608
88755
  // ../api-routes/src/discovery/orchestrate.ts
88609
88756
  import crypto51 from "crypto";
88610
- import { eq as eq84 } from "drizzle-orm";
88757
+ import { eq as eq85 } from "drizzle-orm";
88611
88758
  var DEFAULT_MAX_PROBES = 100;
88612
88759
  var ABSOLUTE_MAX_PROBES = 500;
88613
88760
  function classifyProbeBucket(input) {
@@ -88692,7 +88839,7 @@ async function executeDiscovery(opts) {
88692
88839
  status: DiscoverySessionStatuses.seeding,
88693
88840
  dedupThreshold,
88694
88841
  startedAt
88695
- }).where(eq84(discoverySessions.id, opts.sessionId)).run();
88842
+ }).where(eq85(discoverySessions.id, opts.sessionId)).run();
88696
88843
  const seedResult = await opts.deps.seed({
88697
88844
  project: opts.project,
88698
88845
  icpDescription: opts.icpDescription,
@@ -88753,7 +88900,7 @@ async function executeDiscovery(opts) {
88753
88900
  dedupBandPairFraction: dedupStats.bandPairFraction,
88754
88901
  dedupPairsTotal: dedupStats.pairsTotal,
88755
88902
  warning
88756
- }).where(eq84(discoverySessions.id, opts.sessionId)).run();
88903
+ }).where(eq85(discoverySessions.id, opts.sessionId)).run();
88757
88904
  const probeLocation = opts.locations?.[0];
88758
88905
  const probeResults = await mapWithConcurrency(
88759
88906
  probedCanonicals,
@@ -88807,7 +88954,7 @@ async function executeDiscovery(opts) {
88807
88954
  wastedCount: buckets["wasted-surface"],
88808
88955
  competitorMap,
88809
88956
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
88810
- }).where(eq84(discoverySessions.id, opts.sessionId)).run();
88957
+ }).where(eq85(discoverySessions.id, opts.sessionId)).run();
88811
88958
  upsertDomainClassifications(opts.db, opts.project.id, opts.sessionId, competitorMap);
88812
88959
  return {
88813
88960
  buckets,
@@ -88847,7 +88994,7 @@ function markSessionFailed(db, sessionId, error) {
88847
88994
  status: DiscoverySessionStatuses.failed,
88848
88995
  error,
88849
88996
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
88850
- }).where(eq84(discoverySessions.id, sessionId)).run();
88997
+ }).where(eq85(discoverySessions.id, sessionId)).run();
88851
88998
  }
88852
88999
  function dedupeStrings(input) {
88853
89000
  const seen2 = /* @__PURE__ */ new Set();
@@ -88865,7 +89012,7 @@ function dedupeStrings(input) {
88865
89012
 
88866
89013
  // ../api-routes/src/technical-aeo.ts
88867
89014
  import crypto52 from "crypto";
88868
- import { and as and71, asc as asc16, count as count4, desc as desc39, eq as eq85, gte as gte22, inArray as inArray34, isNotNull as isNotNull8, isNull as isNull16, lt as lt13, or as or20, sql as sql30 } from "drizzle-orm";
89015
+ import { and as and72, asc as asc16, count as count4, desc as desc40, eq as eq86, gte as gte22, inArray as inArray35, isNotNull as isNotNull9, isNull as isNull16, lt as lt13, or as or21, sql as sql30 } from "drizzle-orm";
88869
89016
  import { alias } from "drizzle-orm/sqlite-core";
88870
89017
  var FETCHED_SITE_CRAWL_STATES = /* @__PURE__ */ new Set([
88871
89018
  ...SiteCrawlFetchedStates,
@@ -89102,8 +89249,8 @@ function parseLinkKind(value) {
89102
89249
  return allowed.data;
89103
89250
  }
89104
89251
  function linkKindFilter(linkKind) {
89105
- if (linkKind === SiteHealthLinkKinds.content) return eq85(siteCrawlEdges.isTemplate, false);
89106
- if (linkKind === SiteHealthLinkKinds.template) return eq85(siteCrawlEdges.isTemplate, true);
89252
+ if (linkKind === SiteHealthLinkKinds.content) return eq86(siteCrawlEdges.isTemplate, false);
89253
+ if (linkKind === SiteHealthLinkKinds.template) return eq86(siteCrawlEdges.isTemplate, true);
89107
89254
  return void 0;
89108
89255
  }
89109
89256
  var graphSourceNode = alias(siteCrawlGraphNodes, "site_crawl_graph_source_node");
@@ -89207,20 +89354,20 @@ function changedFields(before, after, fields) {
89207
89354
  }
89208
89355
  async function technicalAeoRoutes(app, opts) {
89209
89356
  const visibleCrawlFilters = (projectId) => [
89210
- eq85(siteCrawlSnapshots.projectId, projectId),
89211
- eq85(runs.projectId, projectId),
89212
- eq85(runs.kind, RunKinds["site-audit"]),
89213
- inArray34(runs.status, SURFACEABLE_STATUSES),
89357
+ eq86(siteCrawlSnapshots.projectId, projectId),
89358
+ eq86(runs.projectId, projectId),
89359
+ eq86(runs.kind, RunKinds["site-audit"]),
89360
+ inArray35(runs.status, SURFACEABLE_STATUSES),
89214
89361
  notProbeRun()
89215
89362
  ];
89216
89363
  const availableScanDatesFor = (projectId, requestedDate) => {
89217
89364
  if (!requestedDate) return void 0;
89218
89365
  const date = sql30`substr(${siteCrawlSnapshots.createdAt}, 1, 10)`;
89219
89366
  const filters = visibleCrawlFilters(projectId);
89220
- const allDates = and71(...filters);
89221
- const matchingDates = and71(...filters, eq85(sql30`substr(${siteCrawlSnapshots.createdAt}, 6, 5)`, requestedDate.slice(5)));
89222
- const readDates = (where) => app.db.select({ date }).from(siteCrawlSnapshots).innerJoin(runs, eq85(siteCrawlSnapshots.runId, runs.id)).where(where).groupBy(date).orderBy(desc39(date)).limit(10).all().map((row) => row.date);
89223
- const countDates = (where) => app.db.select({ value: sql30`count(distinct ${date})` }).from(siteCrawlSnapshots).innerJoin(runs, eq85(siteCrawlSnapshots.runId, runs.id)).where(where).get()?.value ?? 0;
89367
+ const allDates = and72(...filters);
89368
+ const matchingDates = and72(...filters, eq86(sql30`substr(${siteCrawlSnapshots.createdAt}, 6, 5)`, requestedDate.slice(5)));
89369
+ const readDates = (where) => app.db.select({ date }).from(siteCrawlSnapshots).innerJoin(runs, eq86(siteCrawlSnapshots.runId, runs.id)).where(where).groupBy(date).orderBy(desc40(date)).limit(10).all().map((row) => row.date);
89370
+ const countDates = (where) => app.db.select({ value: sql30`count(distinct ${date})` }).from(siteCrawlSnapshots).innerJoin(runs, eq86(siteCrawlSnapshots.runId, runs.id)).where(where).get()?.value ?? 0;
89224
89371
  return {
89225
89372
  recentDates: readDates(allDates),
89226
89373
  totalDates: countDates(allDates),
@@ -89237,33 +89384,33 @@ async function technicalAeoRoutes(app, opts) {
89237
89384
  filters.push(gte22(siteCrawlSnapshots.createdAt, requestedDate), lt13(siteCrawlSnapshots.createdAt, nextDate2.toISOString()));
89238
89385
  }
89239
89386
  const selectionFilters = scope.scoredOnly ? [...filters, sql30`exists (select 1 from ${siteAuditSnapshots} where ${siteAuditSnapshots.projectId} = ${siteCrawlSnapshots.projectId} and ${siteAuditSnapshots.runId} = ${siteCrawlSnapshots.runId})`] : filters;
89240
- const selected = app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq85(siteCrawlSnapshots.runId, runs.id)).where(and71(...selectionFilters, runId ? eq85(siteCrawlSnapshots.runId, runId) : void 0)).orderBy(
89241
- desc39(sql30`substr(${siteCrawlSnapshots.createdAt}, 1, 10)`),
89242
- desc39(siteCrawlSnapshots.complete),
89243
- desc39(siteCrawlSnapshots.pagesFetched),
89244
- desc39(siteCrawlSnapshots.createdAt),
89245
- desc39(siteCrawlSnapshots.runId)
89387
+ const selected = app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq86(siteCrawlSnapshots.runId, runs.id)).where(and72(...selectionFilters, runId ? eq86(siteCrawlSnapshots.runId, runId) : void 0)).orderBy(
89388
+ desc40(sql30`substr(${siteCrawlSnapshots.createdAt}, 1, 10)`),
89389
+ desc40(siteCrawlSnapshots.complete),
89390
+ desc40(siteCrawlSnapshots.pagesFetched),
89391
+ desc40(siteCrawlSnapshots.createdAt),
89392
+ desc40(siteCrawlSnapshots.runId)
89246
89393
  ).limit(1).get();
89247
89394
  if (!selected) return void 0;
89248
89395
  const date = selected.snapshot.createdAt.slice(0, 10);
89249
89396
  const nextDate = /* @__PURE__ */ new Date(`${date}T00:00:00.000Z`);
89250
89397
  nextDate.setUTCDate(nextDate.getUTCDate() + 1);
89251
- const sameDate = and71(
89398
+ const sameDate = and72(
89252
89399
  ...filters,
89253
89400
  gte22(siteCrawlSnapshots.createdAt, date),
89254
89401
  lt13(siteCrawlSnapshots.createdAt, nextDate.toISOString())
89255
89402
  );
89256
- const sameDateRunCount = app.db.select({ value: count4() }).from(siteCrawlSnapshots).innerJoin(runs, eq85(siteCrawlSnapshots.runId, runs.id)).where(sameDate).get()?.value ?? 0;
89403
+ const sameDateRunCount = app.db.select({ value: count4() }).from(siteCrawlSnapshots).innerJoin(runs, eq86(siteCrawlSnapshots.runId, runs.id)).where(sameDate).get()?.value ?? 0;
89257
89404
  const candidates = app.db.select({
89258
89405
  runId: siteCrawlSnapshots.runId,
89259
89406
  createdAt: siteCrawlSnapshots.createdAt,
89260
89407
  complete: siteCrawlSnapshots.complete,
89261
89408
  pages: siteCrawlSnapshots.pagesFetched
89262
- }).from(siteCrawlSnapshots).innerJoin(runs, eq85(siteCrawlSnapshots.runId, runs.id)).where(sameDate).orderBy(
89263
- desc39(siteCrawlSnapshots.complete),
89264
- desc39(siteCrawlSnapshots.pagesFetched),
89265
- desc39(siteCrawlSnapshots.createdAt),
89266
- desc39(siteCrawlSnapshots.runId)
89409
+ }).from(siteCrawlSnapshots).innerJoin(runs, eq86(siteCrawlSnapshots.runId, runs.id)).where(sameDate).orderBy(
89410
+ desc40(siteCrawlSnapshots.complete),
89411
+ desc40(siteCrawlSnapshots.pagesFetched),
89412
+ desc40(siteCrawlSnapshots.createdAt),
89413
+ desc40(siteCrawlSnapshots.runId)
89267
89414
  ).limit(10).all();
89268
89415
  const runSelection = {
89269
89416
  reason: runId ? "explicit-run" : scope.scoredOnly ? "latest-scored-scan" : requestedDate ? selected.snapshot.complete ? "requested-date-complete" : "requested-date-most-pages" : selected.snapshot.complete ? "latest-date-complete" : "latest-date-most-pages",
@@ -89277,20 +89424,20 @@ async function technicalAeoRoutes(app, opts) {
89277
89424
  };
89278
89425
  const resolveAudit = (projectId, runId, recordedBefore) => {
89279
89426
  const filters = [
89280
- eq85(siteAuditSnapshots.projectId, projectId),
89281
- eq85(runs.projectId, projectId),
89282
- eq85(runs.kind, RunKinds["site-audit"]),
89283
- inArray34(runs.status, SURFACEABLE_STATUSES),
89427
+ eq86(siteAuditSnapshots.projectId, projectId),
89428
+ eq86(runs.projectId, projectId),
89429
+ eq86(runs.kind, RunKinds["site-audit"]),
89430
+ inArray35(runs.status, SURFACEABLE_STATUSES),
89284
89431
  notProbeRun(),
89285
89432
  ...recordedBefore ? [lt13(siteAuditSnapshots.createdAt, recordedBefore)] : []
89286
89433
  ];
89287
89434
  const complete = sql30`case when ${runs.status} = ${RunStatuses.completed} then 1 else 0 end`;
89288
- const selectAudit = (pinnedRunId) => app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq85(siteAuditSnapshots.runId, runs.id)).where(and71(...filters, pinnedRunId ? eq85(siteAuditSnapshots.runId, pinnedRunId) : void 0)).orderBy(
89289
- desc39(sql30`substr(${siteAuditSnapshots.createdAt}, 1, 10)`),
89290
- desc39(complete),
89291
- desc39(siteAuditSnapshots.pagesAudited),
89292
- desc39(siteAuditSnapshots.createdAt),
89293
- desc39(siteAuditSnapshots.runId)
89435
+ const selectAudit = (pinnedRunId) => app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq86(siteAuditSnapshots.runId, runs.id)).where(and72(...filters, pinnedRunId ? eq86(siteAuditSnapshots.runId, pinnedRunId) : void 0)).orderBy(
89436
+ desc40(sql30`substr(${siteAuditSnapshots.createdAt}, 1, 10)`),
89437
+ desc40(complete),
89438
+ desc40(siteAuditSnapshots.pagesAudited),
89439
+ desc40(siteAuditSnapshots.createdAt),
89440
+ desc40(siteAuditSnapshots.runId)
89294
89441
  ).limit(1).get();
89295
89442
  const auditOfCrawl = (target) => {
89296
89443
  const selected2 = target ? selectAudit(target.snapshot.runId) : void 0;
@@ -89306,22 +89453,22 @@ async function technicalAeoRoutes(app, opts) {
89306
89453
  const date = selected.snap.createdAt.slice(0, 10);
89307
89454
  const nextDate = /* @__PURE__ */ new Date(`${date}T00:00:00.000Z`);
89308
89455
  nextDate.setUTCDate(nextDate.getUTCDate() + 1);
89309
- const sameDate = and71(
89456
+ const sameDate = and72(
89310
89457
  ...filters,
89311
89458
  gte22(siteAuditSnapshots.createdAt, date),
89312
89459
  lt13(siteAuditSnapshots.createdAt, nextDate.toISOString())
89313
89460
  );
89314
- const sameDateRunCount = app.db.select({ value: count4() }).from(siteAuditSnapshots).innerJoin(runs, eq85(siteAuditSnapshots.runId, runs.id)).where(sameDate).get()?.value ?? 0;
89461
+ const sameDateRunCount = app.db.select({ value: count4() }).from(siteAuditSnapshots).innerJoin(runs, eq86(siteAuditSnapshots.runId, runs.id)).where(sameDate).get()?.value ?? 0;
89315
89462
  const candidates = app.db.select({
89316
89463
  runId: siteAuditSnapshots.runId,
89317
89464
  createdAt: siteAuditSnapshots.createdAt,
89318
89465
  complete,
89319
89466
  pages: siteAuditSnapshots.pagesAudited
89320
- }).from(siteAuditSnapshots).innerJoin(runs, eq85(siteAuditSnapshots.runId, runs.id)).where(sameDate).orderBy(
89321
- desc39(complete),
89322
- desc39(siteAuditSnapshots.pagesAudited),
89323
- desc39(siteAuditSnapshots.createdAt),
89324
- desc39(siteAuditSnapshots.runId)
89467
+ }).from(siteAuditSnapshots).innerJoin(runs, eq86(siteAuditSnapshots.runId, runs.id)).where(sameDate).orderBy(
89468
+ desc40(complete),
89469
+ desc40(siteAuditSnapshots.pagesAudited),
89470
+ desc40(siteAuditSnapshots.createdAt),
89471
+ desc40(siteAuditSnapshots.runId)
89325
89472
  ).limit(10).all().map((candidate) => ({ ...candidate, complete: Boolean(candidate.complete) }));
89326
89473
  const runSelection = {
89327
89474
  // A crawl exists here only when none stored a scorecard: this score is a fallback.
@@ -89383,10 +89530,10 @@ async function technicalAeoRoutes(app, opts) {
89383
89530
  };
89384
89531
  const inventorySummaryFor = (scope) => {
89385
89532
  if (!scope) return null;
89386
- const groups = crawlReasonGroups(and71(
89387
- eq85(siteCrawlPages.projectId, scope.projectId),
89388
- eq85(siteCrawlPages.runId, scope.runId),
89389
- eq85(siteCrawlPages.attemptId, scope.attemptId)
89533
+ const groups = crawlReasonGroups(and72(
89534
+ eq86(siteCrawlPages.projectId, scope.projectId),
89535
+ eq86(siteCrawlPages.runId, scope.runId),
89536
+ eq86(siteCrawlPages.attemptId, scope.attemptId)
89390
89537
  ));
89391
89538
  const total = groups.reduce((sum, group) => sum + group.pages, 0);
89392
89539
  const eligible = groups.reduce((sum, group) => sum + (group.inventoryEligible ? group.pages : 0), 0);
@@ -89406,34 +89553,34 @@ async function technicalAeoRoutes(app, opts) {
89406
89553
  const NO_CRAWL_COMPLETENESS = { complete: false, termination: null };
89407
89554
  const pageInScope = (scope, selector) => {
89408
89555
  if (!selector.nodeKey && !selector.url) return null;
89409
- return app.db.select().from(siteCrawlPages).where(and71(
89410
- eq85(siteCrawlPages.projectId, scope.projectId),
89411
- eq85(siteCrawlPages.runId, scope.runId),
89412
- eq85(siteCrawlPages.attemptId, scope.attemptId),
89413
- selector.nodeKey ? eq85(siteCrawlPages.nodeKey, selector.nodeKey) : eq85(siteCrawlPages.url, selector.url)
89556
+ return app.db.select().from(siteCrawlPages).where(and72(
89557
+ eq86(siteCrawlPages.projectId, scope.projectId),
89558
+ eq86(siteCrawlPages.runId, scope.runId),
89559
+ eq86(siteCrawlPages.attemptId, scope.attemptId),
89560
+ selector.nodeKey ? eq86(siteCrawlPages.nodeKey, selector.nodeKey) : eq86(siteCrawlPages.url, selector.url)
89414
89561
  )).orderBy(asc16(siteCrawlPages.nodeKey)).limit(1).get() ?? null;
89415
89562
  };
89416
- const rootPageInScope = (scope, rootUrl) => pageInScope(scope, { url: rootUrl }) ?? app.db.select().from(siteCrawlPages).where(and71(
89417
- eq85(siteCrawlPages.projectId, scope.projectId),
89418
- eq85(siteCrawlPages.runId, scope.runId),
89419
- eq85(siteCrawlPages.attemptId, scope.attemptId),
89420
- eq85(siteCrawlPages.depth, 0)
89563
+ const rootPageInScope = (scope, rootUrl) => pageInScope(scope, { url: rootUrl }) ?? app.db.select().from(siteCrawlPages).where(and72(
89564
+ eq86(siteCrawlPages.projectId, scope.projectId),
89565
+ eq86(siteCrawlPages.runId, scope.runId),
89566
+ eq86(siteCrawlPages.attemptId, scope.attemptId),
89567
+ eq86(siteCrawlPages.depth, 0)
89421
89568
  )).orderBy(asc16(siteCrawlPages.nodeKey)).limit(1).get() ?? null;
89422
- const isSurfaceableAuditRun = (projectId, runId) => Boolean(app.db.select({ id: runs.id }).from(runs).where(and71(
89423
- eq85(runs.id, runId),
89424
- eq85(runs.projectId, projectId),
89425
- eq85(runs.kind, RunKinds["site-audit"]),
89426
- inArray34(runs.status, SURFACEABLE_STATUSES),
89569
+ const isSurfaceableAuditRun = (projectId, runId) => Boolean(app.db.select({ id: runs.id }).from(runs).where(and72(
89570
+ eq86(runs.id, runId),
89571
+ eq86(runs.projectId, projectId),
89572
+ eq86(runs.kind, RunKinds["site-audit"]),
89573
+ inArray35(runs.status, SURFACEABLE_STATUSES),
89427
89574
  notProbeRun()
89428
89575
  )).limit(1).get());
89429
89576
  const assertKnownAuditRun = (projectId, runId) => {
89430
89577
  if (runId && !isSurfaceableAuditRun(projectId, runId)) throw notFound("Site crawl run", runId);
89431
89578
  };
89432
- const hasLegacyAudit = (projectId) => Boolean(app.db.select({ runId: siteAuditSnapshots.runId }).from(siteAuditSnapshots).innerJoin(runs, eq85(siteAuditSnapshots.runId, runs.id)).where(and71(
89433
- eq85(siteAuditSnapshots.projectId, projectId),
89434
- eq85(runs.projectId, projectId),
89435
- eq85(runs.kind, RunKinds["site-audit"]),
89436
- inArray34(runs.status, SURFACEABLE_STATUSES),
89579
+ const hasLegacyAudit = (projectId) => Boolean(app.db.select({ runId: siteAuditSnapshots.runId }).from(siteAuditSnapshots).innerJoin(runs, eq86(siteAuditSnapshots.runId, runs.id)).where(and72(
89580
+ eq86(siteAuditSnapshots.projectId, projectId),
89581
+ eq86(runs.projectId, projectId),
89582
+ eq86(runs.kind, RunKinds["site-audit"]),
89583
+ inArray35(runs.status, SURFACEABLE_STATUSES),
89437
89584
  notProbeRun()
89438
89585
  )).limit(1).get());
89439
89586
  const emptyCrawlSummary = (projectName, legacyAuditAvailable) => ({
@@ -89534,14 +89681,14 @@ async function technicalAeoRoutes(app, opts) {
89534
89681
  };
89535
89682
  }
89536
89683
  const statusFilter = request.query.status === "success" || request.query.status === "error" ? request.query.status : null;
89537
- const conds = [eq85(siteAuditPages.projectId, project.id), eq85(siteAuditPages.runId, latest.snap.runId)];
89538
- if (statusFilter) conds.push(eq85(siteAuditPages.status, statusFilter));
89539
- const where = and71(...conds);
89684
+ const conds = [eq86(siteAuditPages.projectId, project.id), eq86(siteAuditPages.runId, latest.snap.runId)];
89685
+ if (statusFilter) conds.push(eq86(siteAuditPages.status, statusFilter));
89686
+ const where = and72(...conds);
89540
89687
  const totalRow = app.db.select({ value: count4() }).from(siteAuditPages).where(where).get();
89541
89688
  const total = totalRow?.value ?? 0;
89542
89689
  const limit = parsePositiveInt(request.query.limit, 100, 500);
89543
89690
  const offset = parsePositiveInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER);
89544
- const orderBy = request.query.sort === "score-desc" ? desc39(siteAuditPages.overallScore) : request.query.sort === "url" ? asc16(siteAuditPages.url) : asc16(siteAuditPages.overallScore);
89691
+ const orderBy = request.query.sort === "score-desc" ? desc40(siteAuditPages.overallScore) : request.query.sort === "url" ? asc16(siteAuditPages.url) : asc16(siteAuditPages.overallScore);
89545
89692
  const rows = app.db.select().from(siteAuditPages).where(where).orderBy(orderBy).limit(limit).offset(offset).all();
89546
89693
  const pages = rows.map((row) => ({
89547
89694
  url: row.url,
@@ -89560,13 +89707,13 @@ async function technicalAeoRoutes(app, opts) {
89560
89707
  auditedAt: siteAuditSnapshots.auditedAt,
89561
89708
  aggregateScore: siteAuditSnapshots.aggregateScore,
89562
89709
  pagesAudited: siteAuditSnapshots.pagesAudited
89563
- }).from(siteAuditSnapshots).innerJoin(runs, eq85(siteAuditSnapshots.runId, runs.id)).where(and71(
89564
- eq85(siteAuditSnapshots.projectId, project.id),
89565
- eq85(runs.projectId, project.id),
89566
- eq85(runs.kind, RunKinds["site-audit"]),
89567
- inArray34(runs.status, SURFACEABLE_STATUSES),
89710
+ }).from(siteAuditSnapshots).innerJoin(runs, eq86(siteAuditSnapshots.runId, runs.id)).where(and72(
89711
+ eq86(siteAuditSnapshots.projectId, project.id),
89712
+ eq86(runs.projectId, project.id),
89713
+ eq86(runs.kind, RunKinds["site-audit"]),
89714
+ inArray35(runs.status, SURFACEABLE_STATUSES),
89568
89715
  notProbeRun()
89569
- )).orderBy(desc39(siteAuditSnapshots.createdAt)).limit(limit).all();
89716
+ )).orderBy(desc40(siteAuditSnapshots.createdAt)).limit(limit).all();
89570
89717
  return { project: project.name, points: rows.reverse() };
89571
89718
  });
89572
89719
  app.get("/projects/:name/technical-aeo/crawl", async (request) => {
@@ -89666,10 +89813,10 @@ async function technicalAeoRoutes(app, opts) {
89666
89813
  { projectId: project.id, runId: snapshot.runId, attemptId: snapshot.attemptId },
89667
89814
  snapshot.rootUrl
89668
89815
  )?.nodeKey ?? null;
89669
- const persistedLayout = app.db.select().from(siteCrawlGraphLayouts).where(and71(
89670
- eq85(siteCrawlGraphLayouts.projectId, project.id),
89671
- eq85(siteCrawlGraphLayouts.runId, snapshot.runId),
89672
- eq85(siteCrawlGraphLayouts.attemptId, snapshot.attemptId)
89816
+ const persistedLayout = app.db.select().from(siteCrawlGraphLayouts).where(and72(
89817
+ eq86(siteCrawlGraphLayouts.projectId, project.id),
89818
+ eq86(siteCrawlGraphLayouts.runId, snapshot.runId),
89819
+ eq86(siteCrawlGraphLayouts.attemptId, snapshot.attemptId)
89673
89820
  )).limit(1).get();
89674
89821
  if (!persistedLayout) {
89675
89822
  return {
@@ -89722,9 +89869,9 @@ async function technicalAeoRoutes(app, opts) {
89722
89869
  const maxNodes = parseBoundedLimit(request.query.maxNodes, SITE_CRAWL_GRAPH_DEFAULT_MAX_NODES, SITE_CRAWL_GRAPH_MAX_NODES);
89723
89870
  const maxEdges = parseBoundedLimit(request.query.maxEdges, SITE_CRAWL_GRAPH_DEFAULT_MAX_EDGES, SITE_CRAWL_GRAPH_MAX_EDGES);
89724
89871
  const graphScope = [
89725
- eq85(siteCrawlGraphNodes.projectId, project.id),
89726
- eq85(siteCrawlGraphNodes.runId, snapshot.runId),
89727
- eq85(siteCrawlGraphNodes.attemptId, snapshot.attemptId)
89872
+ eq86(siteCrawlGraphNodes.projectId, project.id),
89873
+ eq86(siteCrawlGraphNodes.runId, snapshot.runId),
89874
+ eq86(siteCrawlGraphNodes.attemptId, snapshot.attemptId)
89728
89875
  ];
89729
89876
  const nodeRows = app.db.select({
89730
89877
  nodeKey: siteCrawlPages.nodeKey,
@@ -89743,18 +89890,18 @@ async function technicalAeoRoutes(app, opts) {
89743
89890
  linkScoreNormalized: siteCrawlPages.linkScoreNormalized,
89744
89891
  x: siteCrawlGraphNodes.x,
89745
89892
  y: siteCrawlGraphNodes.y
89746
- }).from(siteCrawlGraphNodes).innerJoin(siteCrawlPages, and71(
89747
- eq85(siteCrawlPages.projectId, siteCrawlGraphNodes.projectId),
89748
- eq85(siteCrawlPages.runId, siteCrawlGraphNodes.runId),
89749
- eq85(siteCrawlPages.attemptId, siteCrawlGraphNodes.attemptId),
89750
- eq85(siteCrawlPages.nodeKey, siteCrawlGraphNodes.nodeKey)
89751
- )).where(and71(...graphScope, lt13(siteCrawlGraphNodes.sampleRank, maxNodes))).orderBy(asc16(siteCrawlGraphNodes.sampleRank)).all();
89893
+ }).from(siteCrawlGraphNodes).innerJoin(siteCrawlPages, and72(
89894
+ eq86(siteCrawlPages.projectId, siteCrawlGraphNodes.projectId),
89895
+ eq86(siteCrawlPages.runId, siteCrawlGraphNodes.runId),
89896
+ eq86(siteCrawlPages.attemptId, siteCrawlGraphNodes.attemptId),
89897
+ eq86(siteCrawlPages.nodeKey, siteCrawlGraphNodes.nodeKey)
89898
+ )).where(and72(...graphScope, lt13(siteCrawlGraphNodes.sampleRank, maxNodes))).orderBy(asc16(siteCrawlGraphNodes.sampleRank)).all();
89752
89899
  const nodes = nodeRows.map(({ indexabilityReasons, canonicalNodeKey, ...row }) => ({
89753
89900
  ...row,
89754
89901
  healthState: deriveSiteHealthState({ ...row, indexabilityReasons, canonicalNodeKey }),
89755
89902
  healthReason: deriveSiteHealthReason({ ...row, indexabilityReasons, canonicalNodeKey })
89756
89903
  }));
89757
- const graphLinkKindFilter = linkKind === SiteHealthLinkKinds.all ? void 0 : eq85(siteCrawlGraphEdges.isTemplate, linkKind === SiteHealthLinkKinds.template);
89904
+ const graphLinkKindFilter = linkKind === SiteHealthLinkKinds.all ? void 0 : eq86(siteCrawlGraphEdges.isTemplate, linkKind === SiteHealthLinkKinds.template);
89758
89905
  const edges = app.db.select({
89759
89906
  edgeKey: siteCrawlGraphEdges.edgeKey,
89760
89907
  sourceNodeKey: siteCrawlGraphEdges.sourceNodeKey,
@@ -89762,22 +89909,22 @@ async function technicalAeoRoutes(app, opts) {
89762
89909
  followable: siteCrawlGraphEdges.followable,
89763
89910
  occurrences: siteCrawlGraphEdges.occurrences,
89764
89911
  isTemplate: siteCrawlGraphEdges.isTemplate
89765
- }).from(siteCrawlGraphEdges).innerJoin(graphSourceNode, and71(
89766
- eq85(graphSourceNode.projectId, siteCrawlGraphEdges.projectId),
89767
- eq85(graphSourceNode.runId, siteCrawlGraphEdges.runId),
89768
- eq85(graphSourceNode.attemptId, siteCrawlGraphEdges.attemptId),
89769
- eq85(graphSourceNode.nodeKey, siteCrawlGraphEdges.sourceNodeKey),
89912
+ }).from(siteCrawlGraphEdges).innerJoin(graphSourceNode, and72(
89913
+ eq86(graphSourceNode.projectId, siteCrawlGraphEdges.projectId),
89914
+ eq86(graphSourceNode.runId, siteCrawlGraphEdges.runId),
89915
+ eq86(graphSourceNode.attemptId, siteCrawlGraphEdges.attemptId),
89916
+ eq86(graphSourceNode.nodeKey, siteCrawlGraphEdges.sourceNodeKey),
89770
89917
  lt13(graphSourceNode.sampleRank, maxNodes)
89771
- )).innerJoin(graphTargetNode, and71(
89772
- eq85(graphTargetNode.projectId, siteCrawlGraphEdges.projectId),
89773
- eq85(graphTargetNode.runId, siteCrawlGraphEdges.runId),
89774
- eq85(graphTargetNode.attemptId, siteCrawlGraphEdges.attemptId),
89775
- eq85(graphTargetNode.nodeKey, siteCrawlGraphEdges.targetNodeKey),
89918
+ )).innerJoin(graphTargetNode, and72(
89919
+ eq86(graphTargetNode.projectId, siteCrawlGraphEdges.projectId),
89920
+ eq86(graphTargetNode.runId, siteCrawlGraphEdges.runId),
89921
+ eq86(graphTargetNode.attemptId, siteCrawlGraphEdges.attemptId),
89922
+ eq86(graphTargetNode.nodeKey, siteCrawlGraphEdges.targetNodeKey),
89776
89923
  lt13(graphTargetNode.sampleRank, maxNodes)
89777
- )).where(and71(
89778
- eq85(siteCrawlGraphEdges.projectId, project.id),
89779
- eq85(siteCrawlGraphEdges.runId, snapshot.runId),
89780
- eq85(siteCrawlGraphEdges.attemptId, snapshot.attemptId),
89924
+ )).where(and72(
89925
+ eq86(siteCrawlGraphEdges.projectId, project.id),
89926
+ eq86(siteCrawlGraphEdges.runId, snapshot.runId),
89927
+ eq86(siteCrawlGraphEdges.attemptId, snapshot.attemptId),
89781
89928
  lt13(siteCrawlGraphEdges.sampleRank, maxEdges),
89782
89929
  graphLinkKindFilter
89783
89930
  )).orderBy(asc16(siteCrawlGraphEdges.sampleRank)).all();
@@ -89872,15 +90019,15 @@ async function technicalAeoRoutes(app, opts) {
89872
90019
  let frontier = [focus.nodeKey];
89873
90020
  let queryTruncated = false;
89874
90021
  for (let distance = 0; distance < hops && frontier.length > 0; distance += 1) {
89875
- const candidates = app.db.select().from(siteCrawlEdges).where(and71(
89876
- eq85(siteCrawlEdges.projectId, scope.projectId),
89877
- eq85(siteCrawlEdges.runId, scope.runId),
89878
- eq85(siteCrawlEdges.attemptId, scope.attemptId),
89879
- eq85(siteCrawlEdges.internal, true),
89880
- isNotNull8(siteCrawlEdges.targetNodeKey),
89881
- or20(
89882
- inArray34(siteCrawlEdges.sourceNodeKey, frontier),
89883
- inArray34(siteCrawlEdges.targetNodeKey, frontier)
90022
+ const candidates = app.db.select().from(siteCrawlEdges).where(and72(
90023
+ eq86(siteCrawlEdges.projectId, scope.projectId),
90024
+ eq86(siteCrawlEdges.runId, scope.runId),
90025
+ eq86(siteCrawlEdges.attemptId, scope.attemptId),
90026
+ eq86(siteCrawlEdges.internal, true),
90027
+ isNotNull9(siteCrawlEdges.targetNodeKey),
90028
+ or21(
90029
+ inArray35(siteCrawlEdges.sourceNodeKey, frontier),
90030
+ inArray35(siteCrawlEdges.targetNodeKey, frontier)
89884
90031
  )
89885
90032
  )).orderBy(asc16(siteCrawlEdges.edgeKey)).limit(maxEdges + maxNodes + 1).all();
89886
90033
  if (candidates.length > maxEdges + maxNodes) queryTruncated = true;
@@ -89913,22 +90060,22 @@ async function technicalAeoRoutes(app, opts) {
89913
90060
  frontier = [...next].sort();
89914
90061
  }
89915
90062
  const nodeKeys = [...distances.keys()];
89916
- const pageRows = nodeKeys.length === 0 ? [] : app.db.select().from(siteCrawlPages).where(and71(
89917
- eq85(siteCrawlPages.projectId, scope.projectId),
89918
- eq85(siteCrawlPages.runId, scope.runId),
89919
- eq85(siteCrawlPages.attemptId, scope.attemptId),
89920
- inArray34(siteCrawlPages.nodeKey, nodeKeys)
90063
+ const pageRows = nodeKeys.length === 0 ? [] : app.db.select().from(siteCrawlPages).where(and72(
90064
+ eq86(siteCrawlPages.projectId, scope.projectId),
90065
+ eq86(siteCrawlPages.runId, scope.runId),
90066
+ eq86(siteCrawlPages.attemptId, scope.attemptId),
90067
+ inArray35(siteCrawlPages.nodeKey, nodeKeys)
89921
90068
  )).all();
89922
90069
  const persistedNodeKeys = new Set(pageRows.map((row) => row.nodeKey));
89923
90070
  const outermostNodeKeys = pageRows.filter((row) => distances.get(row.nodeKey) === hops).map((row) => row.nodeKey);
89924
- const omittedOutermostEdge = outermostNodeKeys.length === 0 ? void 0 : app.db.select({ edgeKey: siteCrawlEdges.edgeKey }).from(siteCrawlEdges).where(and71(
89925
- eq85(siteCrawlEdges.projectId, scope.projectId),
89926
- eq85(siteCrawlEdges.runId, scope.runId),
89927
- eq85(siteCrawlEdges.attemptId, scope.attemptId),
89928
- eq85(siteCrawlEdges.internal, true),
89929
- isNotNull8(siteCrawlEdges.targetNodeKey),
89930
- inArray34(siteCrawlEdges.sourceNodeKey, outermostNodeKeys),
89931
- inArray34(siteCrawlEdges.targetNodeKey, outermostNodeKeys)
90071
+ const omittedOutermostEdge = outermostNodeKeys.length === 0 ? void 0 : app.db.select({ edgeKey: siteCrawlEdges.edgeKey }).from(siteCrawlEdges).where(and72(
90072
+ eq86(siteCrawlEdges.projectId, scope.projectId),
90073
+ eq86(siteCrawlEdges.runId, scope.runId),
90074
+ eq86(siteCrawlEdges.attemptId, scope.attemptId),
90075
+ eq86(siteCrawlEdges.internal, true),
90076
+ isNotNull9(siteCrawlEdges.targetNodeKey),
90077
+ inArray35(siteCrawlEdges.sourceNodeKey, outermostNodeKeys),
90078
+ inArray35(siteCrawlEdges.targetNodeKey, outermostNodeKeys)
89932
90079
  )).limit(1).get();
89933
90080
  if (omittedOutermostEdge && !seenEdgeKeys.has(omittedOutermostEdge.edgeKey)) {
89934
90081
  omittedEdgeKeys.add(omittedOutermostEdge.edgeKey);
@@ -90021,23 +90168,23 @@ async function technicalAeoRoutes(app, opts) {
90021
90168
  let found = fromPage.nodeKey === toPage.nodeKey;
90022
90169
  let truncated = false;
90023
90170
  for (let depth = 0; depth < maxDepth && frontier.length > 0 && !found; depth += 1) {
90024
- const candidates = app.db.select().from(siteCrawlEdges).where(and71(
90025
- eq85(siteCrawlEdges.projectId, scope.projectId),
90026
- eq85(siteCrawlEdges.runId, scope.runId),
90027
- eq85(siteCrawlEdges.attemptId, scope.attemptId),
90028
- eq85(siteCrawlEdges.internal, true),
90029
- eq85(siteCrawlEdges.followable, true),
90030
- eq85(siteCrawlEdges.relation, "anchor"),
90031
- isNotNull8(siteCrawlEdges.targetNodeKey),
90032
- inArray34(siteCrawlEdges.sourceNodeKey, frontier)
90171
+ const candidates = app.db.select().from(siteCrawlEdges).where(and72(
90172
+ eq86(siteCrawlEdges.projectId, scope.projectId),
90173
+ eq86(siteCrawlEdges.runId, scope.runId),
90174
+ eq86(siteCrawlEdges.attemptId, scope.attemptId),
90175
+ eq86(siteCrawlEdges.internal, true),
90176
+ eq86(siteCrawlEdges.followable, true),
90177
+ eq86(siteCrawlEdges.relation, "anchor"),
90178
+ isNotNull9(siteCrawlEdges.targetNodeKey),
90179
+ inArray35(siteCrawlEdges.sourceNodeKey, frontier)
90033
90180
  )).orderBy(asc16(siteCrawlEdges.edgeKey)).limit(SITE_HEALTH_PATH_MAX_VISITED_NODES + 1).all();
90034
90181
  if (candidates.length > SITE_HEALTH_PATH_MAX_VISITED_NODES) truncated = true;
90035
90182
  const candidateTargetKeys = [...new Set(candidates.map((edge) => edge.targetNodeKey).filter((nodeKey) => nodeKey != null))];
90036
- const persistedTargetKeys = new Set(candidateTargetKeys.length === 0 ? [] : app.db.select({ nodeKey: siteCrawlPages.nodeKey }).from(siteCrawlPages).where(and71(
90037
- eq85(siteCrawlPages.projectId, scope.projectId),
90038
- eq85(siteCrawlPages.runId, scope.runId),
90039
- eq85(siteCrawlPages.attemptId, scope.attemptId),
90040
- inArray34(siteCrawlPages.nodeKey, candidateTargetKeys)
90183
+ const persistedTargetKeys = new Set(candidateTargetKeys.length === 0 ? [] : app.db.select({ nodeKey: siteCrawlPages.nodeKey }).from(siteCrawlPages).where(and72(
90184
+ eq86(siteCrawlPages.projectId, scope.projectId),
90185
+ eq86(siteCrawlPages.runId, scope.runId),
90186
+ eq86(siteCrawlPages.attemptId, scope.attemptId),
90187
+ inArray35(siteCrawlPages.nodeKey, candidateTargetKeys)
90041
90188
  )).all().map((row) => row.nodeKey));
90042
90189
  const next = /* @__PURE__ */ new Set();
90043
90190
  for (const edge of candidates.slice(0, SITE_HEALTH_PATH_MAX_VISITED_NODES)) {
@@ -90078,11 +90225,11 @@ async function technicalAeoRoutes(app, opts) {
90078
90225
  pathEdges.unshift(step.edge);
90079
90226
  pathKeys.unshift(step.nodeKey);
90080
90227
  }
90081
- const pathRows = app.db.select().from(siteCrawlPages).where(and71(
90082
- eq85(siteCrawlPages.projectId, scope.projectId),
90083
- eq85(siteCrawlPages.runId, scope.runId),
90084
- eq85(siteCrawlPages.attemptId, scope.attemptId),
90085
- inArray34(siteCrawlPages.nodeKey, pathKeys)
90228
+ const pathRows = app.db.select().from(siteCrawlPages).where(and72(
90229
+ eq86(siteCrawlPages.projectId, scope.projectId),
90230
+ eq86(siteCrawlPages.runId, scope.runId),
90231
+ eq86(siteCrawlPages.attemptId, scope.attemptId),
90232
+ inArray35(siteCrawlPages.nodeKey, pathKeys)
90086
90233
  )).all();
90087
90234
  const pageByKey = new Map(pathRows.map((row) => [row.nodeKey, row]));
90088
90235
  const pathDetection = templateDetectionOf(snapshot.templateDetection);
@@ -90110,25 +90257,25 @@ async function technicalAeoRoutes(app, opts) {
90110
90257
  throw validationError("change must be all, added, removed, or changed");
90111
90258
  }
90112
90259
  const completeSnapshotFilters = [
90113
- eq85(siteCrawlSnapshots.projectId, project.id),
90114
- eq85(runs.projectId, project.id),
90115
- eq85(runs.kind, RunKinds["site-audit"]),
90116
- eq85(runs.status, RunStatuses.completed),
90117
- eq85(siteCrawlSnapshots.complete, true),
90260
+ eq86(siteCrawlSnapshots.projectId, project.id),
90261
+ eq86(runs.projectId, project.id),
90262
+ eq86(runs.kind, RunKinds["site-audit"]),
90263
+ eq86(runs.status, RunStatuses.completed),
90264
+ eq86(siteCrawlSnapshots.complete, true),
90118
90265
  notProbeRun()
90119
90266
  ];
90120
- const afterTarget = request.query.toRunId ? resolveCrawl(project.id, request.query.toRunId) : app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq85(siteCrawlSnapshots.runId, runs.id)).where(and71(...completeSnapshotFilters)).orderBy(desc39(siteCrawlSnapshots.createdAt), desc39(siteCrawlSnapshots.runId)).limit(1).get();
90267
+ const afterTarget = request.query.toRunId ? resolveCrawl(project.id, request.query.toRunId) : app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq86(siteCrawlSnapshots.runId, runs.id)).where(and72(...completeSnapshotFilters)).orderBy(desc40(siteCrawlSnapshots.createdAt), desc40(siteCrawlSnapshots.runId)).limit(1).get();
90121
90268
  if (request.query.toRunId && !afterTarget) throw notFound("Site crawl run", request.query.toRunId);
90122
- const beforeTarget = request.query.fromRunId ? resolveCrawl(project.id, request.query.fromRunId) : afterTarget ? app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq85(siteCrawlSnapshots.runId, runs.id)).where(and71(
90269
+ const beforeTarget = request.query.fromRunId ? resolveCrawl(project.id, request.query.fromRunId) : afterTarget ? app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq86(siteCrawlSnapshots.runId, runs.id)).where(and72(
90123
90270
  ...completeSnapshotFilters,
90124
- or20(
90271
+ or21(
90125
90272
  lt13(siteCrawlSnapshots.createdAt, afterTarget.snapshot.createdAt),
90126
- and71(
90127
- eq85(siteCrawlSnapshots.createdAt, afterTarget.snapshot.createdAt),
90273
+ and72(
90274
+ eq86(siteCrawlSnapshots.createdAt, afterTarget.snapshot.createdAt),
90128
90275
  lt13(siteCrawlSnapshots.runId, afterTarget.snapshot.runId)
90129
90276
  )
90130
90277
  )
90131
- )).orderBy(desc39(siteCrawlSnapshots.createdAt), desc39(siteCrawlSnapshots.runId)).limit(1).get() : void 0;
90278
+ )).orderBy(desc40(siteCrawlSnapshots.createdAt), desc40(siteCrawlSnapshots.runId)).limit(1).get() : void 0;
90132
90279
  if (request.query.fromRunId && !beforeTarget) throw notFound("Site crawl run", request.query.fromRunId);
90133
90280
  if (!afterTarget) {
90134
90281
  const latestCrawl = resolveCrawl(project.id);
@@ -90369,18 +90516,18 @@ async function technicalAeoRoutes(app, opts) {
90369
90516
  const visibleKeys = keyRows.slice(0, limit);
90370
90517
  const pageKeys = visibleKeys.filter((row) => row.entity === "page").map((row) => row.key);
90371
90518
  const linkKeys = visibleKeys.filter((row) => row.entity === "link").map((row) => row.key);
90372
- const pageRowsFor = (scope) => pageKeys.length === 0 ? [] : app.db.select().from(siteCrawlPages).where(and71(
90373
- eq85(siteCrawlPages.projectId, scope.projectId),
90374
- eq85(siteCrawlPages.runId, scope.runId),
90375
- eq85(siteCrawlPages.attemptId, scope.attemptId),
90376
- inArray34(siteCrawlPages.nodeKey, pageKeys)
90519
+ const pageRowsFor = (scope) => pageKeys.length === 0 ? [] : app.db.select().from(siteCrawlPages).where(and72(
90520
+ eq86(siteCrawlPages.projectId, scope.projectId),
90521
+ eq86(siteCrawlPages.runId, scope.runId),
90522
+ eq86(siteCrawlPages.attemptId, scope.attemptId),
90523
+ inArray35(siteCrawlPages.nodeKey, pageKeys)
90377
90524
  )).all();
90378
- const edgeRowsFor = (scope) => linkKeys.length === 0 ? [] : app.db.select().from(siteCrawlEdges).where(and71(
90379
- eq85(siteCrawlEdges.projectId, scope.projectId),
90380
- eq85(siteCrawlEdges.runId, scope.runId),
90381
- eq85(siteCrawlEdges.attemptId, scope.attemptId),
90382
- eq85(siteCrawlEdges.internal, true),
90383
- inArray34(siteCrawlEdges.edgeKey, linkKeys)
90525
+ const edgeRowsFor = (scope) => linkKeys.length === 0 ? [] : app.db.select().from(siteCrawlEdges).where(and72(
90526
+ eq86(siteCrawlEdges.projectId, scope.projectId),
90527
+ eq86(siteCrawlEdges.runId, scope.runId),
90528
+ eq86(siteCrawlEdges.attemptId, scope.attemptId),
90529
+ eq86(siteCrawlEdges.internal, true),
90530
+ inArray35(siteCrawlEdges.edgeKey, linkKeys)
90384
90531
  )).all();
90385
90532
  const beforePages = new Map(pageRowsFor(beforeScope).map((row) => [row.nodeKey, mapCrawlPage(row)]));
90386
90533
  const afterPages = new Map(pageRowsFor(afterScope).map((row) => [row.nodeKey, mapCrawlPage(row)]));
@@ -90533,16 +90680,16 @@ async function technicalAeoRoutes(app, opts) {
90533
90680
  }
90534
90681
  const inventorySummary = inventorySummaryFor(detailScopeFor(project.id, snapshot));
90535
90682
  const filters = [
90536
- eq85(siteCrawlPages.projectId, project.id),
90537
- eq85(siteCrawlPages.runId, snapshot.runId),
90538
- eq85(siteCrawlPages.attemptId, snapshot.attemptId)
90683
+ eq86(siteCrawlPages.projectId, project.id),
90684
+ eq86(siteCrawlPages.runId, snapshot.runId),
90685
+ eq86(siteCrawlPages.attemptId, snapshot.attemptId)
90539
90686
  ];
90540
- if (request.query.nodeKey) filters.push(eq85(siteCrawlPages.nodeKey, request.query.nodeKey));
90687
+ if (request.query.nodeKey) filters.push(eq86(siteCrawlPages.nodeKey, request.query.nodeKey));
90541
90688
  const inventoryEligible = parseBoolean2(request.query.inventoryEligible);
90542
- if (inventoryEligible != null) filters.push(eq85(siteCrawlPages.inventoryEligible, inventoryEligible));
90543
- if (request.query.fetchState) filters.push(eq85(siteCrawlPages.fetchState, request.query.fetchState));
90544
- if (request.query.indexabilityState) filters.push(eq85(siteCrawlPages.indexabilityState, request.query.indexabilityState));
90545
- if (request.query.auditState) filters.push(eq85(siteCrawlPages.auditState, request.query.auditState));
90689
+ if (inventoryEligible != null) filters.push(eq86(siteCrawlPages.inventoryEligible, inventoryEligible));
90690
+ if (request.query.fetchState) filters.push(eq86(siteCrawlPages.fetchState, request.query.fetchState));
90691
+ if (request.query.indexabilityState) filters.push(eq86(siteCrawlPages.indexabilityState, request.query.indexabilityState));
90692
+ if (request.query.auditState) filters.push(eq86(siteCrawlPages.auditState, request.query.auditState));
90546
90693
  const healthState = parseSiteHealthState(request.query.healthState);
90547
90694
  const limit = parseBoundedLimit(request.query.limit, 100, 200);
90548
90695
  const sort = ["score-desc", "score-asc", "path"].includes(request.query.sort ?? "") ? request.query.sort : "url";
@@ -90560,17 +90707,17 @@ async function technicalAeoRoutes(app, opts) {
90560
90707
  sort
90561
90708
  })).digest("base64url");
90562
90709
  const offset = decodeCrawlPageCursor(request.query.cursor, selectionKey);
90563
- const orderBy = sort === "score-desc" ? [desc39(siteCrawlPages.auditScore), asc16(siteCrawlPages.nodeKey)] : sort === "score-asc" ? [asc16(siteCrawlPages.auditScore), asc16(siteCrawlPages.nodeKey)] : sort === "path" ? [asc16(siteCrawlPages.path), asc16(siteCrawlPages.nodeKey)] : [asc16(siteCrawlPages.url), asc16(siteCrawlPages.nodeKey)];
90710
+ const orderBy = sort === "score-desc" ? [desc40(siteCrawlPages.auditScore), asc16(siteCrawlPages.nodeKey)] : sort === "score-asc" ? [asc16(siteCrawlPages.auditScore), asc16(siteCrawlPages.nodeKey)] : sort === "path" ? [asc16(siteCrawlPages.path), asc16(siteCrawlPages.nodeKey)] : [asc16(siteCrawlPages.url), asc16(siteCrawlPages.nodeKey)];
90564
90711
  let healthStateFilter = null;
90565
90712
  if (healthState) {
90566
- const legacyRow = app.db.select({ id: siteCrawlPages.id }).from(siteCrawlPages).where(and71(
90567
- eq85(siteCrawlPages.projectId, project.id),
90568
- eq85(siteCrawlPages.runId, snapshot.runId),
90569
- eq85(siteCrawlPages.attemptId, snapshot.attemptId),
90713
+ const legacyRow = app.db.select({ id: siteCrawlPages.id }).from(siteCrawlPages).where(and72(
90714
+ eq86(siteCrawlPages.projectId, project.id),
90715
+ eq86(siteCrawlPages.runId, snapshot.runId),
90716
+ eq86(siteCrawlPages.attemptId, snapshot.attemptId),
90570
90717
  isNull16(siteCrawlPages.healthState)
90571
90718
  )).limit(1).get();
90572
90719
  healthStateFilter = legacyRow ? "unavailable-legacy-scan" : "applied";
90573
- if (healthStateFilter === "applied") filters.push(eq85(siteCrawlPages.healthState, healthState));
90720
+ if (healthStateFilter === "applied") filters.push(eq86(siteCrawlPages.healthState, healthState));
90574
90721
  }
90575
90722
  if (healthStateFilter === "unavailable-legacy-scan") {
90576
90723
  return {
@@ -90587,7 +90734,7 @@ async function technicalAeoRoutes(app, opts) {
90587
90734
  pages: []
90588
90735
  };
90589
90736
  }
90590
- const where = and71(...filters);
90737
+ const where = and72(...filters);
90591
90738
  const total = app.db.select({ value: count4() }).from(siteCrawlPages).where(where).get()?.value ?? 0;
90592
90739
  const rows = app.db.select().from(siteCrawlPages).where(where).orderBy(...orderBy).limit(limit).offset(offset).all();
90593
90740
  const nextOffset = offset + rows.length;
@@ -90622,10 +90769,10 @@ async function technicalAeoRoutes(app, opts) {
90622
90769
  url: siteCrawlPages.url,
90623
90770
  inventoryEligible: siteCrawlPages.inventoryEligible,
90624
90771
  fetchState: siteCrawlPages.fetchState
90625
- }).from(siteCrawlPages).where(and71(
90626
- eq85(siteCrawlPages.projectId, project.id),
90627
- eq85(siteCrawlPages.runId, snapshot.runId),
90628
- eq85(siteCrawlPages.attemptId, snapshot.attemptId)
90772
+ }).from(siteCrawlPages).where(and72(
90773
+ eq86(siteCrawlPages.projectId, project.id),
90774
+ eq86(siteCrawlPages.runId, snapshot.runId),
90775
+ eq86(siteCrawlPages.attemptId, snapshot.attemptId)
90629
90776
  )).limit(MAX_STRUCTURE_SOURCE_ROWS + 1).all();
90630
90777
  if (sourceRows.length > MAX_STRUCTURE_SOURCE_ROWS) {
90631
90778
  throw validationError(`Persisted crawl exceeds the ${MAX_STRUCTURE_SOURCE_ROWS}-page structure limit`);
@@ -90707,17 +90854,17 @@ async function technicalAeoRoutes(app, opts) {
90707
90854
  };
90708
90855
  }
90709
90856
  const filters = [
90710
- eq85(siteCrawlEdges.projectId, project.id),
90711
- eq85(siteCrawlEdges.runId, snapshot.runId),
90712
- eq85(siteCrawlEdges.attemptId, snapshot.attemptId),
90713
- eq85(siteCrawlEdges.internal, true),
90857
+ eq86(siteCrawlEdges.projectId, project.id),
90858
+ eq86(siteCrawlEdges.runId, snapshot.runId),
90859
+ eq86(siteCrawlEdges.attemptId, snapshot.attemptId),
90860
+ eq86(siteCrawlEdges.internal, true),
90714
90861
  linkKindFilter(linkKind)
90715
90862
  ];
90716
- if (request.query.sourceUrl) filters.push(eq85(siteCrawlEdges.sourceUrl, request.query.sourceUrl));
90717
- if (request.query.targetUrl) filters.push(eq85(siteCrawlEdges.targetUrl, request.query.targetUrl));
90863
+ if (request.query.sourceUrl) filters.push(eq86(siteCrawlEdges.sourceUrl, request.query.sourceUrl));
90864
+ if (request.query.targetUrl) filters.push(eq86(siteCrawlEdges.targetUrl, request.query.targetUrl));
90718
90865
  const followable = parseBoolean2(request.query.followable);
90719
- if (followable != null) filters.push(eq85(siteCrawlEdges.followable, followable));
90720
- const where = and71(...filters);
90866
+ if (followable != null) filters.push(eq86(siteCrawlEdges.followable, followable));
90867
+ const where = and72(...filters);
90721
90868
  const total = app.db.select({ value: count4() }).from(siteCrawlEdges).where(where).get()?.value ?? 0;
90722
90869
  const limit = parseBoundedLimit(request.query.limit, 100, 200);
90723
90870
  const offset = decodeCursor(request.query.cursor);
@@ -90778,17 +90925,17 @@ async function technicalAeoRoutes(app, opts) {
90778
90925
  };
90779
90926
  }
90780
90927
  const scope = [
90781
- eq85(siteCrawlEdges.projectId, project.id),
90782
- eq85(siteCrawlEdges.runId, snapshot.runId),
90783
- eq85(siteCrawlEdges.attemptId, snapshot.attemptId),
90784
- eq85(siteCrawlEdges.internal, true),
90928
+ eq86(siteCrawlEdges.projectId, project.id),
90929
+ eq86(siteCrawlEdges.runId, snapshot.runId),
90930
+ eq86(siteCrawlEdges.attemptId, snapshot.attemptId),
90931
+ eq86(siteCrawlEdges.internal, true),
90785
90932
  linkKindFilter(linkKind)
90786
90933
  ];
90787
- const inboundMatch = request.query.nodeKey && request.query.url ? or20(eq85(siteCrawlEdges.targetNodeKey, request.query.nodeKey), eq85(siteCrawlEdges.targetUrl, request.query.url)) : request.query.nodeKey ? eq85(siteCrawlEdges.targetNodeKey, request.query.nodeKey) : eq85(siteCrawlEdges.targetUrl, request.query.url);
90788
- const outboundMatch = request.query.nodeKey && request.query.url ? or20(eq85(siteCrawlEdges.sourceNodeKey, request.query.nodeKey), eq85(siteCrawlEdges.sourceUrl, request.query.url)) : request.query.nodeKey ? eq85(siteCrawlEdges.sourceNodeKey, request.query.nodeKey) : eq85(siteCrawlEdges.sourceUrl, request.query.url);
90934
+ const inboundMatch = request.query.nodeKey && request.query.url ? or21(eq86(siteCrawlEdges.targetNodeKey, request.query.nodeKey), eq86(siteCrawlEdges.targetUrl, request.query.url)) : request.query.nodeKey ? eq86(siteCrawlEdges.targetNodeKey, request.query.nodeKey) : eq86(siteCrawlEdges.targetUrl, request.query.url);
90935
+ const outboundMatch = request.query.nodeKey && request.query.url ? or21(eq86(siteCrawlEdges.sourceNodeKey, request.query.nodeKey), eq86(siteCrawlEdges.sourceUrl, request.query.url)) : request.query.nodeKey ? eq86(siteCrawlEdges.sourceNodeKey, request.query.nodeKey) : eq86(siteCrawlEdges.sourceUrl, request.query.url);
90789
90936
  const limit = parseBoundedLimit(request.query.limit, 50, 100);
90790
- const inboundRows = app.db.select().from(siteCrawlEdges).where(and71(...scope, inboundMatch)).orderBy(asc16(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
90791
- const outboundRows = app.db.select().from(siteCrawlEdges).where(and71(...scope, outboundMatch)).orderBy(asc16(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
90937
+ const inboundRows = app.db.select().from(siteCrawlEdges).where(and72(...scope, inboundMatch)).orderBy(asc16(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
90938
+ const outboundRows = app.db.select().from(siteCrawlEdges).where(and72(...scope, outboundMatch)).orderBy(asc16(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
90792
90939
  return {
90793
90940
  project: project.name,
90794
90941
  hasCrawlData: true,
@@ -90822,11 +90969,11 @@ async function technicalAeoRoutes(app, opts) {
90822
90969
  if (!snapshot.attemptId) {
90823
90970
  return { project: project.name, runId: snapshot.runId, state: "partial", checkDeadLinks: true, checked: snapshot.deadLinksChecked, found: snapshot.deadLinksFound, unverified: snapshot.deadLinksUnverified, total: 0, nextCursor: null, deadLinks: [], runSelection: target.runSelection };
90824
90971
  }
90825
- const where = and71(
90826
- eq85(siteCrawlFindings.projectId, project.id),
90827
- eq85(siteCrawlFindings.runId, snapshot.runId),
90828
- eq85(siteCrawlFindings.attemptId, snapshot.attemptId),
90829
- eq85(siteCrawlFindings.findingType, "dead-link"),
90972
+ const where = and72(
90973
+ eq86(siteCrawlFindings.projectId, project.id),
90974
+ eq86(siteCrawlFindings.runId, snapshot.runId),
90975
+ eq86(siteCrawlFindings.attemptId, snapshot.attemptId),
90976
+ eq86(siteCrawlFindings.findingType, "dead-link"),
90830
90977
  // Belt and braces on the client-facing surface: a row without a status
90831
90978
  // code is not evidence of a broken link, and this response is where such
90832
90979
  // a row would be read as one. Migration 140 removed every stored row that
@@ -90836,7 +90983,7 @@ async function technicalAeoRoutes(app, opts) {
90836
90983
  // a fabricated broken link shown to a client is not worth the saved
90837
90984
  // predicate. Applied to `total` as well as the rows, or the count would
90838
90985
  // promise a page the list cannot fill.
90839
- isNotNull8(sql30`json_extract(${siteCrawlFindings.evidence}, '$.statusCode')`)
90986
+ isNotNull9(sql30`json_extract(${siteCrawlFindings.evidence}, '$.statusCode')`)
90840
90987
  );
90841
90988
  const total = app.db.select({ value: count4() }).from(siteCrawlFindings).where(where).get()?.value ?? 0;
90842
90989
  const limit = parseBoundedLimit(request.query.limit, 100, 200);
@@ -90876,19 +91023,19 @@ async function technicalAeoRoutes(app, opts) {
90876
91023
  createdAt: runs.createdAt,
90877
91024
  startedAt: runs.startedAt,
90878
91025
  finishedAt: runs.finishedAt
90879
- }).from(runs).where(and71(
90880
- eq85(runs.projectId, project.id),
90881
- eq85(runs.kind, RunKinds["site-audit"]),
91026
+ }).from(runs).where(and72(
91027
+ eq86(runs.projectId, project.id),
91028
+ eq86(runs.kind, RunKinds["site-audit"]),
90882
91029
  notProbeRun()
90883
- )).orderBy(desc39(runs.createdAt), desc39(runs.id)).limit(limit).all();
91030
+ )).orderBy(desc40(runs.createdAt), desc40(runs.id)).limit(limit).all();
90884
91031
  const runIds = rows.map((row) => row.runId);
90885
- const crawlRunIds = new Set(runIds.length === 0 ? [] : app.db.select({ runId: siteCrawlSnapshots.runId }).from(siteCrawlSnapshots).innerJoin(runs, eq85(siteCrawlSnapshots.runId, runs.id)).where(and71(
90886
- eq85(siteCrawlSnapshots.projectId, project.id),
90887
- eq85(runs.projectId, project.id),
90888
- eq85(runs.kind, RunKinds["site-audit"]),
90889
- inArray34(runs.status, SURFACEABLE_STATUSES),
91032
+ const crawlRunIds = new Set(runIds.length === 0 ? [] : app.db.select({ runId: siteCrawlSnapshots.runId }).from(siteCrawlSnapshots).innerJoin(runs, eq86(siteCrawlSnapshots.runId, runs.id)).where(and72(
91033
+ eq86(siteCrawlSnapshots.projectId, project.id),
91034
+ eq86(runs.projectId, project.id),
91035
+ eq86(runs.kind, RunKinds["site-audit"]),
91036
+ inArray35(runs.status, SURFACEABLE_STATUSES),
90890
91037
  notProbeRun(),
90891
- inArray34(siteCrawlSnapshots.runId, runIds)
91038
+ inArray35(siteCrawlSnapshots.runId, runIds)
90892
91039
  )).all().map((row) => row.runId));
90893
91040
  return {
90894
91041
  project: project.name,
@@ -90902,21 +91049,21 @@ async function technicalAeoRoutes(app, opts) {
90902
91049
  });
90903
91050
  app.get("/projects/:name/technical-aeo/runs/:runId/progress", async (request) => {
90904
91051
  const project = resolveProject(app.db, request.params.name);
90905
- const run = app.db.select().from(runs).where(and71(
90906
- eq85(runs.id, request.params.runId),
90907
- eq85(runs.projectId, project.id),
90908
- eq85(runs.kind, RunKinds["site-audit"]),
91052
+ const run = app.db.select().from(runs).where(and72(
91053
+ eq86(runs.id, request.params.runId),
91054
+ eq86(runs.projectId, project.id),
91055
+ eq86(runs.kind, RunKinds["site-audit"]),
90909
91056
  notProbeRun()
90910
91057
  )).get();
90911
91058
  if (!run) throw notFound("Site audit run", request.params.runId);
90912
- const attempt = app.db.select().from(siteCrawlAttempts).where(and71(
90913
- eq85(siteCrawlAttempts.projectId, project.id),
90914
- eq85(siteCrawlAttempts.runId, run.id)
90915
- )).orderBy(desc39(siteCrawlAttempts.attemptNumber), desc39(siteCrawlAttempts.updatedAt)).limit(1).get();
90916
- const persistedLayout = attempt ? app.db.select().from(siteCrawlGraphLayouts).where(and71(
90917
- eq85(siteCrawlGraphLayouts.projectId, project.id),
90918
- eq85(siteCrawlGraphLayouts.runId, run.id),
90919
- eq85(siteCrawlGraphLayouts.attemptId, attempt.id)
91059
+ const attempt = app.db.select().from(siteCrawlAttempts).where(and72(
91060
+ eq86(siteCrawlAttempts.projectId, project.id),
91061
+ eq86(siteCrawlAttempts.runId, run.id)
91062
+ )).orderBy(desc40(siteCrawlAttempts.attemptNumber), desc40(siteCrawlAttempts.updatedAt)).limit(1).get();
91063
+ const persistedLayout = attempt ? app.db.select().from(siteCrawlGraphLayouts).where(and72(
91064
+ eq86(siteCrawlGraphLayouts.projectId, project.id),
91065
+ eq86(siteCrawlGraphLayouts.runId, run.id),
91066
+ eq86(siteCrawlGraphLayouts.attemptId, attempt.id)
90920
91067
  )).get() : void 0;
90921
91068
  const layoutState = persistedLayout?.state === "ready" || persistedLayout?.state === "unavailable" ? persistedLayout.state : run.status === RunStatuses.completed || run.status === RunStatuses.partial ? "unavailable" : "pending";
90922
91069
  return {
@@ -90947,19 +91094,19 @@ async function technicalAeoRoutes(app, opts) {
90947
91094
  };
90948
91095
  });
90949
91096
  app.get("/projects/:name/technical-aeo/runs/:runId/page-health-preview", async (request) => app.db.transaction((tx) => {
90950
- const project = tx.select().from(projects).where(eq85(projects.name, request.params.name)).get();
91097
+ const project = tx.select().from(projects).where(eq86(projects.name, request.params.name)).get();
90951
91098
  if (!project) throw notFound("Project", request.params.name);
90952
- const run = tx.select().from(runs).where(and71(
90953
- eq85(runs.id, request.params.runId),
90954
- eq85(runs.projectId, project.id),
90955
- eq85(runs.kind, RunKinds["site-audit"]),
91099
+ const run = tx.select().from(runs).where(and72(
91100
+ eq86(runs.id, request.params.runId),
91101
+ eq86(runs.projectId, project.id),
91102
+ eq86(runs.kind, RunKinds["site-audit"]),
90956
91103
  notProbeRun()
90957
91104
  )).get();
90958
91105
  if (!run) throw notFound("Site audit run", request.params.runId);
90959
- const attempt = tx.select().from(siteCrawlAttempts).where(and71(
90960
- eq85(siteCrawlAttempts.projectId, project.id),
90961
- eq85(siteCrawlAttempts.runId, run.id)
90962
- )).orderBy(desc39(siteCrawlAttempts.attemptNumber), desc39(siteCrawlAttempts.updatedAt)).limit(1).get();
91106
+ const attempt = tx.select().from(siteCrawlAttempts).where(and72(
91107
+ eq86(siteCrawlAttempts.projectId, project.id),
91108
+ eq86(siteCrawlAttempts.runId, run.id)
91109
+ )).orderBy(desc40(siteCrawlAttempts.attemptNumber), desc40(siteCrawlAttempts.updatedAt)).limit(1).get();
90963
91110
  const state = run.status === RunStatuses.queued ? "waiting" : run.status === RunStatuses.running ? "collecting" : "terminal";
90964
91111
  const base = {
90965
91112
  project: project.name,
@@ -90972,11 +91119,11 @@ async function technicalAeoRoutes(app, opts) {
90972
91119
  if (!attempt) {
90973
91120
  return { ...base, pagesAudited: 0, examples: [] };
90974
91121
  }
90975
- const auditedWhere = and71(
90976
- eq85(siteCrawlPages.projectId, project.id),
90977
- eq85(siteCrawlPages.runId, run.id),
90978
- eq85(siteCrawlPages.attemptId, attempt.id),
90979
- eq85(siteCrawlPages.auditState, "success")
91122
+ const auditedWhere = and72(
91123
+ eq86(siteCrawlPages.projectId, project.id),
91124
+ eq86(siteCrawlPages.runId, run.id),
91125
+ eq86(siteCrawlPages.attemptId, attempt.id),
91126
+ eq86(siteCrawlPages.auditState, "success")
90980
91127
  );
90981
91128
  const pagesAudited = tx.select({ value: count4() }).from(siteCrawlPages).where(auditedWhere).get()?.value ?? 0;
90982
91129
  if (state !== "collecting") {
@@ -90987,9 +91134,9 @@ async function technicalAeoRoutes(app, opts) {
90987
91134
  url: siteCrawlPages.url,
90988
91135
  auditScore: siteCrawlPages.auditScore,
90989
91136
  auditFields: siteCrawlPages.auditFields
90990
- }).from(siteCrawlPages).where(and71(
91137
+ }).from(siteCrawlPages).where(and72(
90991
91138
  auditedWhere,
90992
- isNotNull8(siteCrawlPages.auditScore),
91139
+ isNotNull9(siteCrawlPages.auditScore),
90993
91140
  lt13(siteCrawlPages.auditScore, 70)
90994
91141
  )).orderBy(asc16(siteCrawlPages.auditScore), asc16(siteCrawlPages.nodeKey)).limit(LIVE_PAGE_HEALTH_CANDIDATE_LIMIT).all();
90995
91142
  const examples = candidates.flatMap((candidate) => {
@@ -91023,13 +91170,13 @@ async function technicalAeoRoutes(app, opts) {
91023
91170
  status: runs.status,
91024
91171
  identityKey: siteCrawlRunRequests.identityKey,
91025
91172
  effectiveOptions: siteCrawlRunRequests.effectiveOptions
91026
- }).from(runs).leftJoin(siteCrawlRunRequests, and71(
91027
- eq85(siteCrawlRunRequests.projectId, runs.projectId),
91028
- eq85(siteCrawlRunRequests.runId, runs.id)
91029
- )).where(and71(
91030
- eq85(runs.projectId, project.id),
91031
- eq85(runs.kind, RunKinds["site-audit"]),
91032
- inArray34(runs.status, [RunStatuses.queued, RunStatuses.running])
91173
+ }).from(runs).leftJoin(siteCrawlRunRequests, and72(
91174
+ eq86(siteCrawlRunRequests.projectId, runs.projectId),
91175
+ eq86(siteCrawlRunRequests.runId, runs.id)
91176
+ )).where(and72(
91177
+ eq86(runs.projectId, project.id),
91178
+ eq86(runs.kind, RunKinds["site-audit"]),
91179
+ inArray35(runs.status, [RunStatuses.queued, RunStatuses.running])
91033
91180
  )).get();
91034
91181
  if (existing) {
91035
91182
  if (existing.identityKey === identityKey) {
@@ -91083,7 +91230,7 @@ async function technicalAeoRoutes(app, opts) {
91083
91230
  // ../api-routes/src/research.ts
91084
91231
  import crypto53 from "crypto";
91085
91232
  import { z as z5 } from "zod";
91086
- import { and as and72, count as count5, desc as desc40, eq as eq86, gte as gte23, lt as lt14, or as or21, sql as sql31 } from "drizzle-orm";
91233
+ import { and as and73, count as count5, desc as desc41, eq as eq87, gte as gte23, lt as lt14, or as or22, sql as sql31 } from "drizzle-orm";
91087
91234
  var sameLocation = (a, b) => a.label === b.label && a.city === b.city && a.region === b.region && a.country === b.country && a.timezone === b.timezone;
91088
91235
  var BATCH_RECEIPT_PREFIX = "__canonry_research_batch__:";
91089
91236
  async function researchRoutes(app, opts) {
@@ -91101,7 +91248,7 @@ async function researchRoutes(app, opts) {
91101
91248
  const input = parsed.data;
91102
91249
  if (input.idempotencyKey?.startsWith(BATCH_RECEIPT_PREFIX)) throw validationError("This idempotency key prefix is reserved for internal research batch receipts.");
91103
91250
  const requestHash2 = directResearchRequestHash(input);
91104
- const existingReceipt = input.idempotencyKey ? app.db.select().from(researchRuns).where(and72(eq86(researchRuns.projectId, project.id), eq86(researchRuns.idempotencyKey, input.idempotencyKey))).get() : void 0;
91251
+ const existingReceipt = input.idempotencyKey ? app.db.select().from(researchRuns).where(and73(eq87(researchRuns.projectId, project.id), eq87(researchRuns.idempotencyKey, input.idempotencyKey))).get() : void 0;
91105
91252
  if (existingReceipt) {
91106
91253
  assertSameDirectResearchRequest(existingReceipt, input, requestHash2);
91107
91254
  const result2 = getDetail(app, project.id, existingReceipt.id);
@@ -91133,7 +91280,7 @@ async function researchRoutes(app, opts) {
91133
91280
  const initiatedBy = researchPrincipal(request);
91134
91281
  const decision = app.db.transaction((tx) => {
91135
91282
  if (input.idempotencyKey) {
91136
- const existing = tx.select().from(researchRuns).where(and72(eq86(researchRuns.projectId, project.id), eq86(researchRuns.idempotencyKey, input.idempotencyKey))).get();
91283
+ const existing = tx.select().from(researchRuns).where(and73(eq87(researchRuns.projectId, project.id), eq87(researchRuns.idempotencyKey, input.idempotencyKey))).get();
91137
91284
  if (existing) {
91138
91285
  assertSameDirectResearchRequest(existing, input, requestHash2);
91139
91286
  return { reused: true, id: existing.id, shouldDispatch: existing.status === ResearchRunStatuses.queued };
@@ -91141,8 +91288,8 @@ async function researchRoutes(app, opts) {
91141
91288
  }
91142
91289
  if (initiatedBy?.limited) {
91143
91290
  const { start, end, date } = utcDayBounds(now);
91144
- const used = tx.select({ value: count5() }).from(researchRuns).where(and72(
91145
- eq86(researchRuns.projectId, project.id),
91291
+ const used = tx.select({ value: count5() }).from(researchRuns).where(and73(
91292
+ eq87(researchRuns.projectId, project.id),
91146
91293
  gte23(researchRuns.createdAt, start),
91147
91294
  lt14(researchRuns.createdAt, end),
91148
91295
  sql31`(json_extract(${researchRuns.initiatedBy}, '$.role') = ${UserRoles.viewer} OR json_extract(${researchRuns.initiatedBy}, '$.limited') = 1)`
@@ -91173,15 +91320,15 @@ async function researchRoutes(app, opts) {
91173
91320
  if (input.idempotencyKey.startsWith(BATCH_RECEIPT_PREFIX)) throw validationError("This idempotency key prefix is reserved for internal research batch receipts.");
91174
91321
  const requestHash2 = researchBatchRequestHash(input);
91175
91322
  const receiptKeys = input.runs.map((_run, index2) => batchChildReceiptKey(input.idempotencyKey, index2));
91176
- const firstReceipt = app.db.select().from(researchRuns).where(and72(
91177
- eq86(researchRuns.projectId, project.id),
91178
- eq86(researchRuns.idempotencyKey, receiptKeys[0])
91323
+ const firstReceipt = app.db.select().from(researchRuns).where(and73(
91324
+ eq87(researchRuns.projectId, project.id),
91325
+ eq87(researchRuns.idempotencyKey, receiptKeys[0])
91179
91326
  )).get();
91180
91327
  if (firstReceipt) {
91181
91328
  if (firstReceipt.requestHash !== requestHash2) throw alreadyExists("Research batch idempotency key", input.idempotencyKey);
91182
- const saved = receiptKeys.map((key) => app.db.select().from(researchRuns).where(and72(
91183
- eq86(researchRuns.projectId, project.id),
91184
- eq86(researchRuns.idempotencyKey, key)
91329
+ const saved = receiptKeys.map((key) => app.db.select().from(researchRuns).where(and73(
91330
+ eq87(researchRuns.projectId, project.id),
91331
+ eq87(researchRuns.idempotencyKey, key)
91185
91332
  )).get());
91186
91333
  if (saved.some((row) => !row || row.requestHash !== requestHash2)) throw alreadyExists("Research batch idempotency key", input.idempotencyKey);
91187
91334
  const runs3 = saved.map((row) => getDetail(app, project.id, row.id));
@@ -91196,8 +91343,8 @@ async function researchRoutes(app, opts) {
91196
91343
  const decision = app.db.transaction((tx) => {
91197
91344
  if (initiatedBy?.limited) {
91198
91345
  const { start, end, date } = utcDayBounds(now);
91199
- const used = tx.select({ value: count5() }).from(researchRuns).where(and72(
91200
- eq86(researchRuns.projectId, project.id),
91346
+ const used = tx.select({ value: count5() }).from(researchRuns).where(and73(
91347
+ eq87(researchRuns.projectId, project.id),
91201
91348
  gte23(researchRuns.createdAt, start),
91202
91349
  lt14(researchRuns.createdAt, end),
91203
91350
  sql31`(json_extract(${researchRuns.initiatedBy}, '$.role') = ${UserRoles.viewer} OR json_extract(${researchRuns.initiatedBy}, '$.limited') = 1)`
@@ -91251,10 +91398,10 @@ async function researchRoutes(app, opts) {
91251
91398
  const requested = Number.parseInt(request.query.limit ?? "", 10);
91252
91399
  const limit = Number.isInteger(requested) && requested > 0 ? Math.min(requested, 100) : 20;
91253
91400
  const cursor = request.query.cursor ? parseResearchCursor(request.query.cursor, project.id) : null;
91254
- const rows = app.db.select().from(researchRuns).where(and72(
91255
- eq86(researchRuns.projectId, project.id),
91256
- cursor ? or21(lt14(researchRuns.createdAt, cursor.createdAt), and72(eq86(researchRuns.createdAt, cursor.createdAt), lt14(researchRuns.id, cursor.id))) : void 0
91257
- )).orderBy(desc40(researchRuns.createdAt), desc40(researchRuns.id)).limit(limit + 1).all();
91401
+ const rows = app.db.select().from(researchRuns).where(and73(
91402
+ eq87(researchRuns.projectId, project.id),
91403
+ cursor ? or22(lt14(researchRuns.createdAt, cursor.createdAt), and73(eq87(researchRuns.createdAt, cursor.createdAt), lt14(researchRuns.id, cursor.id))) : void 0
91404
+ )).orderBy(desc41(researchRuns.createdAt), desc41(researchRuns.id)).limit(limit + 1).all();
91258
91405
  const runs2 = rows.slice(0, limit).map(serializeRun);
91259
91406
  const last = runs2.at(-1);
91260
91407
  const nextCursor = rows.length > limit && last ? Buffer.from(JSON.stringify({ projectId: project.id, createdAt: last.createdAt, id: last.id })).toString("base64url") : null;
@@ -91380,9 +91527,9 @@ function resolveStoredResearchTemplate(stored, selection, idempotencyKey) {
91380
91527
  return stored;
91381
91528
  }
91382
91529
  function resolveResearchTemplate(app, projectId, selection, scope, location) {
91383
- const savedTemplate = app.db.select().from(measurementQueryTemplates).where(and72(
91384
- eq86(measurementQueryTemplates.projectId, projectId),
91385
- eq86(measurementQueryTemplates.id, selection.templateId)
91530
+ const savedTemplate = app.db.select().from(measurementQueryTemplates).where(and73(
91531
+ eq87(measurementQueryTemplates.projectId, projectId),
91532
+ eq87(measurementQueryTemplates.id, selection.templateId)
91386
91533
  )).get();
91387
91534
  if (!savedTemplate) throw validationError("Unknown or stale research template. Reload the selected template and try again.");
91388
91535
  const templateVersion = savedTemplate.updatedAt;
@@ -91391,7 +91538,7 @@ function resolveResearchTemplate(app, projectId, selection, scope, location) {
91391
91538
  }
91392
91539
  const bindingLocation = selection.bindingLocation === void 0 ? location : selection.bindingLocation;
91393
91540
  if (bindingLocation) {
91394
- const project = app.db.select({ locations: projects.locations }).from(projects).where(eq86(projects.id, projectId)).get();
91541
+ const project = app.db.select({ locations: projects.locations }).from(projects).where(eq87(projects.id, projectId)).get();
91395
91542
  if (!project.locations.some((item2) => sameLocation(item2, bindingLocation))) throw validationError("Research template binding location must match a configured project location.");
91396
91543
  }
91397
91544
  const { bindings, output } = expandResearchTemplate(savedTemplate, scope, bindingLocation);
@@ -91404,9 +91551,9 @@ function classifyResearchQueries(project, plan, queries2) {
91404
91551
  return queries2.map((query) => classifier?.classify(query) ?? null);
91405
91552
  }
91406
91553
  function getDetail(app, projectId, id) {
91407
- const row = app.db.select().from(researchRuns).where(and72(eq86(researchRuns.id, id), eq86(researchRuns.projectId, projectId))).get();
91554
+ const row = app.db.select().from(researchRuns).where(and73(eq87(researchRuns.id, id), eq87(researchRuns.projectId, projectId))).get();
91408
91555
  if (!row) throw notFound("Research run", id);
91409
- const queries2 = app.db.select().from(researchRunQueries).where(eq86(researchRunQueries.researchRunId, id)).orderBy(researchRunQueries.position).all().map(serializeQuery);
91556
+ const queries2 = app.db.select().from(researchRunQueries).where(eq87(researchRunQueries.researchRunId, id)).orderBy(researchRunQueries.position).all().map(serializeQuery);
91410
91557
  return { ...serializeRun(row), queries: queries2 };
91411
91558
  }
91412
91559
  function serializeRun(row) {
@@ -91436,7 +91583,7 @@ function utcDayBounds(now) {
91436
91583
 
91437
91584
  // ../api-routes/src/snapshot-competitor-refresh.ts
91438
91585
  import { setImmediate as yieldToEventLoop2 } from "timers/promises";
91439
- import { and as and73, asc as asc17, eq as eq87, inArray as inArray35 } from "drizzle-orm";
91586
+ import { and as and74, asc as asc17, eq as eq88, inArray as inArray36 } from "drizzle-orm";
91440
91587
  var ANSWER_MENTIONS_CHUNK_SIZE = 50;
91441
91588
  var ANSWER_MENTIONS_MAX_RESTARTS = 3;
91442
91589
  function backfillProjectAnswerMentions(db, projectId, opts) {
@@ -91486,14 +91633,14 @@ function* projectAnswerMentionsSteps(db, projectId, opts, driver) {
91486
91633
  examined = 0;
91487
91634
  wouldUpdate = 0;
91488
91635
  mentioned = 0;
91489
- const runRows = db.select({ id: runs.id, planVersionId: runs.measurementPlanVersionId }).from(runs).where(and73(
91490
- eq87(runs.kind, RunKinds["answer-visibility"]),
91491
- eq87(runs.projectId, projectId),
91492
- opts?.runId === void 0 ? void 0 : eq87(runs.id, opts.runId)
91636
+ const runRows = db.select({ id: runs.id, planVersionId: runs.measurementPlanVersionId }).from(runs).where(and74(
91637
+ eq88(runs.kind, RunKinds["answer-visibility"]),
91638
+ eq88(runs.projectId, projectId),
91639
+ opts?.runId === void 0 ? void 0 : eq88(runs.id, opts.runId)
91493
91640
  )).all();
91494
91641
  const planVersionByRun = new Map(runRows.map((run) => [run.id, run.planVersionId]));
91495
91642
  for (const run of runRows) {
91496
- const snapshotIds = db.select({ id: querySnapshots.id }).from(querySnapshots).where(eq87(querySnapshots.runId, run.id)).orderBy(asc17(querySnapshots.id)).all().map((row) => row.id);
91643
+ const snapshotIds = db.select({ id: querySnapshots.id }).from(querySnapshots).where(eq88(querySnapshots.runId, run.id)).orderBy(asc17(querySnapshots.id)).all().map((row) => row.id);
91497
91644
  for (let offset = 0; offset < snapshotIds.length; offset += ANSWER_MENTIONS_CHUNK_SIZE) {
91498
91645
  if (chunks++ > 0 && driver.recheckIdentity) {
91499
91646
  yield;
@@ -91519,7 +91666,7 @@ function* projectAnswerMentionsSteps(db, projectId, opts, driver) {
91519
91666
  competitorOverlap: querySnapshots.competitorOverlap,
91520
91667
  recommendedCompetitors: querySnapshots.recommendedCompetitors,
91521
91668
  rawResponse: querySnapshots.rawResponse
91522
- }).from(querySnapshots).where(inArray35(querySnapshots.id, snapshotIds.slice(offset, offset + ANSWER_MENTIONS_CHUNK_SIZE))).all();
91669
+ }).from(querySnapshots).where(inArray36(querySnapshots.id, snapshotIds.slice(offset, offset + ANSWER_MENTIONS_CHUNK_SIZE))).all();
91523
91670
  const pendingUpdates = [];
91524
91671
  for (const snapshot of snapshotRows) {
91525
91672
  if (competitorFieldsOnly && snapshot.answerText == null) continue;
@@ -91569,7 +91716,7 @@ function* projectAnswerMentionsSteps(db, projectId, opts, driver) {
91569
91716
  } else {
91570
91717
  db.transaction((tx) => {
91571
91718
  for (const update of pendingUpdates) {
91572
- tx.update(querySnapshots).set(update.patch).where(eq87(querySnapshots.id, update.id)).run();
91719
+ tx.update(querySnapshots).set(update.patch).where(eq88(querySnapshots.id, update.id)).run();
91573
91720
  }
91574
91721
  });
91575
91722
  updated += pendingUpdates.length;
@@ -91599,7 +91746,7 @@ function readStoredGroundingSources(rawResponse) {
91599
91746
 
91600
91747
  // ../api-routes/src/simple-measurement-definitions.ts
91601
91748
  import crypto54 from "crypto";
91602
- import { and as and74, eq as eq88 } from "drizzle-orm";
91749
+ import { and as and75, eq as eq89 } from "drizzle-orm";
91603
91750
  function captureSimpleMeasurementDefinition(db, input) {
91604
91751
  return db.transaction((tx) => {
91605
91752
  const run = tx.select({
@@ -91607,7 +91754,7 @@ function captureSimpleMeasurementDefinition(db, input) {
91607
91754
  trigger: runs.trigger,
91608
91755
  status: runs.status,
91609
91756
  measurementPlanVersionId: runs.measurementPlanVersionId
91610
- }).from(runs).where(and74(eq88(runs.projectId, input.projectId), eq88(runs.id, input.runId))).get();
91757
+ }).from(runs).where(and75(eq89(runs.projectId, input.projectId), eq89(runs.id, input.runId))).get();
91611
91758
  if (!run) throw notFound("Run", input.runId);
91612
91759
  if (run.kind !== RunKinds["answer-visibility"] || run.trigger === RunTriggers.probe || run.measurementPlanVersionId !== null) {
91613
91760
  return null;
@@ -91628,7 +91775,7 @@ function captureSimpleMeasurementDefinition(db, input) {
91628
91775
  throw validationError("Qualified aliases must be a sorted subset of the captured aliases.");
91629
91776
  }
91630
91777
  }
91631
- const existing = tx.select().from(simpleMeasurementDefinitions).where(and74(eq88(simpleMeasurementDefinitions.projectId, input.projectId), eq88(simpleMeasurementDefinitions.runId, input.runId))).get();
91778
+ const existing = tx.select().from(simpleMeasurementDefinitions).where(and75(eq89(simpleMeasurementDefinitions.projectId, input.projectId), eq89(simpleMeasurementDefinitions.runId, input.runId))).get();
91632
91779
  if (existing) {
91633
91780
  const frozen = simpleMeasurementDefinitionSchema.parse(existing.definition);
91634
91781
  const { qualifiedAliases: _liveQualifiedAliases, ...liveIdentity } = definition.identity;
@@ -91654,15 +91801,15 @@ function captureSimpleMeasurementDefinition(db, input) {
91654
91801
  if (run.status !== RunStatuses.running) {
91655
91802
  throw validationError("Only a running run can capture a new measurement definition.");
91656
91803
  }
91657
- if (tx.select({ id: querySnapshots.id }).from(querySnapshots).where(eq88(querySnapshots.runId, input.runId)).limit(1).get()) {
91804
+ if (tx.select({ id: querySnapshots.id }).from(querySnapshots).where(eq89(querySnapshots.runId, input.runId)).limit(1).get()) {
91658
91805
  throw validationError("This run already has stored answers. Its measurement definition cannot be inferred afterward.");
91659
91806
  }
91660
- const projectQueryIds = new Set(tx.select({ id: queries.id }).from(queries).where(eq88(queries.projectId, input.projectId)).all().map((query) => query.id));
91807
+ const projectQueryIds = new Set(tx.select({ id: queries.id }).from(queries).where(eq89(queries.projectId, input.projectId)).all().map((query) => query.id));
91661
91808
  if (definition.queries.some((query) => !projectQueryIds.has(query.queryId))) {
91662
91809
  throw validationError("Every captured query must belong to the run project.");
91663
91810
  }
91664
91811
  if (definition.competitors !== void 0) {
91665
- const liveCompetitorDomains = new Set(tx.select({ domain: competitors.domain }).from(competitors).where(eq88(competitors.projectId, input.projectId)).orderBy(competitors.domain).all().map((competitor) => competitor.domain));
91812
+ const liveCompetitorDomains = new Set(tx.select({ domain: competitors.domain }).from(competitors).where(eq89(competitors.projectId, input.projectId)).orderBy(competitors.domain).all().map((competitor) => competitor.domain));
91666
91813
  const capturedDomains = new Set(definition.competitors.map((competitor) => competitor.domain));
91667
91814
  if (liveCompetitorDomains.size !== capturedDomains.size || [...liveCompetitorDomains].some((domain) => !capturedDomains.has(domain))) {
91668
91815
  throw validationError("Captured competitors must exactly match the project competitors dispatched for this run.");
@@ -92530,10 +92677,10 @@ var log = createLogger("IntelligenceService");
92530
92677
  var runChronologyKey = sql32`coalesce(${runs.finishedAt}, ${runs.createdAt})`;
92531
92678
  function analyzableRunPredicates(projectId) {
92532
92679
  return [
92533
- eq89(runs.projectId, projectId),
92680
+ eq90(runs.projectId, projectId),
92534
92681
  // Only this kind writes query_snapshots.
92535
- eq89(runs.kind, RunKinds["answer-visibility"]),
92536
- or22(eq89(runs.status, RunStatuses.completed), eq89(runs.status, RunStatuses.partial)),
92682
+ eq90(runs.kind, RunKinds["answer-visibility"]),
92683
+ or23(eq90(runs.status, RunStatuses.completed), eq90(runs.status, RunStatuses.partial)),
92537
92684
  // Defensive: RunCoordinator already skips probes before analyzeAndPersist
92538
92685
  // is called, but a future call site invoking it directly for a probe must
92539
92686
  // still not pollute the intelligence window.
@@ -92578,16 +92725,16 @@ var IntelligenceService = class {
92578
92725
  * Returns the analysis result for the coordinator to inspect (e.g. for webhook dispatch).
92579
92726
  */
92580
92727
  analyzeAndPersist(runId, projectId) {
92581
- const currentRunRecord = this.db.select().from(runs).where(and75(eq89(runs.id, runId), ...analyzableRunPredicates(projectId))).get();
92728
+ const currentRunRecord = this.db.select().from(runs).where(and76(eq90(runs.id, runId), ...analyzableRunPredicates(projectId))).get();
92582
92729
  if (!currentRunRecord) {
92583
92730
  log.info("intelligence.skip", { runId, reason: "run not eligible for analysis" });
92584
92731
  return null;
92585
92732
  }
92586
92733
  const currentLocation = currentRunRecord.location ?? null;
92587
92734
  const recentRuns = this.db.select().from(runs).where(
92588
- and75(
92735
+ and76(
92589
92736
  ...analyzableRunPredicates(projectId),
92590
- currentLocation === null ? isNull17(runs.location) : eq89(runs.location, currentLocation),
92737
+ currentLocation === null ? isNull17(runs.location) : eq90(runs.location, currentLocation),
92591
92738
  // Never look forward. The window is this run's own past, so
92592
92739
  // re-analyzing a historical run compares it against its true
92593
92740
  // predecessor rather than against sweeps that came after it.
@@ -92595,10 +92742,10 @@ var IntelligenceService = class {
92595
92742
  // MEASUREMENT: only runs that actually wrote snapshots — see above.
92596
92743
  // Index seek on idx_snapshots_run, short-circuited on the first row.
92597
92744
  exists2(
92598
- this.db.select({ one: sql32`1` }).from(querySnapshots).where(eq89(querySnapshots.runId, runs.id))
92745
+ this.db.select({ one: sql32`1` }).from(querySnapshots).where(eq90(querySnapshots.runId, runs.id))
92599
92746
  )
92600
92747
  )
92601
- ).orderBy(desc41(runs.finishedAt), desc41(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
92748
+ ).orderBy(desc42(runs.finishedAt), desc42(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
92602
92749
  const trackedCompetitors = this.loadTrackedCompetitors(projectId);
92603
92750
  const trackedCompetitorDomains = trackedCompetitors.map((competitor) => competitor.domain);
92604
92751
  const currentRun = this.buildRunData(
@@ -92671,7 +92818,7 @@ var IntelligenceService = class {
92671
92818
  * Returns the persisted insights so the coordinator can count critical/high.
92672
92819
  */
92673
92820
  analyzeAndPersistGbp(runId, projectId) {
92674
- const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq89(runs.id, runId)).get();
92821
+ const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq90(runs.id, runId)).get();
92675
92822
  if (!runRow) {
92676
92823
  log.info("gbp-intelligence.skip", { runId, reason: "run not found" });
92677
92824
  this.persistGbpInsights(runId, projectId, [], []);
@@ -92679,9 +92826,9 @@ var IntelligenceService = class {
92679
92826
  }
92680
92827
  const windowStart = runRow.startedAt ?? runRow.createdAt;
92681
92828
  const windowEnd = runRow.finishedAt ?? (/* @__PURE__ */ new Date()).toISOString();
92682
- const selected = this.db.select().from(gbpLocations).where(and75(
92683
- eq89(gbpLocations.projectId, projectId),
92684
- eq89(gbpLocations.selected, true),
92829
+ const selected = this.db.select().from(gbpLocations).where(and76(
92830
+ eq90(gbpLocations.projectId, projectId),
92831
+ eq90(gbpLocations.selected, true),
92685
92832
  gte24(gbpLocations.syncedAt, windowStart),
92686
92833
  lte20(gbpLocations.syncedAt, windowEnd)
92687
92834
  )).all();
@@ -92716,12 +92863,12 @@ var IntelligenceService = class {
92716
92863
  }
92717
92864
  /** Build the per-location signal bundle the GBP analyzer consumes. */
92718
92865
  buildGbpLocationSignals(projectId, locationName, displayName, fallbackDate) {
92719
- const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and75(eq89(gbpDailyMetrics.projectId, projectId), eq89(gbpDailyMetrics.locationName, locationName))).all();
92720
- const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and75(eq89(gbpPlaceActions.projectId, projectId), eq89(gbpPlaceActions.locationName, locationName))).all();
92721
- const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and75(eq89(gbpLodgingSnapshots.projectId, projectId), eq89(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc41(gbpLodgingSnapshots.syncedAt)).limit(1).get();
92722
- const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(and75(eq89(gbpLocations.projectId, projectId), eq89(gbpLocations.locationName, locationName))).get();
92866
+ const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and76(eq90(gbpDailyMetrics.projectId, projectId), eq90(gbpDailyMetrics.locationName, locationName))).all();
92867
+ const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and76(eq90(gbpPlaceActions.projectId, projectId), eq90(gbpPlaceActions.locationName, locationName))).all();
92868
+ const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and76(eq90(gbpLodgingSnapshots.projectId, projectId), eq90(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc42(gbpLodgingSnapshots.syncedAt)).limit(1).get();
92869
+ const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(and76(eq90(gbpLocations.projectId, projectId), eq90(gbpLocations.locationName, locationName))).get();
92723
92870
  const descriptionMissing = !(ownerRow?.description ?? "").trim();
92724
- const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and75(eq89(gbpPlaceDetails.projectId, projectId), eq89(gbpPlaceDetails.locationName, locationName))).orderBy(desc41(gbpPlaceDetails.syncedAt)).limit(1).get();
92871
+ const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and76(eq90(gbpPlaceDetails.projectId, projectId), eq90(gbpPlaceDetails.locationName, locationName))).orderBy(desc42(gbpPlaceDetails.syncedAt)).limit(1).get();
92725
92872
  const placesAmenities = placeRow ? extractPlaceAmenities(placeRow.attributes) : [];
92726
92873
  const summary3 = buildGbpSummary({
92727
92874
  locationName,
@@ -92757,7 +92904,7 @@ var IntelligenceService = class {
92757
92904
  /** Build the month-over-month keyword series for a location from the
92758
92905
  * accumulating gbp_keyword_monthly table (latest complete month vs prior). */
92759
92906
  buildGbpKeywordTrend(projectId, locationName) {
92760
- const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and75(eq89(gbpKeywordMonthly.projectId, projectId), eq89(gbpKeywordMonthly.locationName, locationName))).all();
92907
+ const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and76(eq90(gbpKeywordMonthly.projectId, projectId), eq90(gbpKeywordMonthly.locationName, locationName))).all();
92761
92908
  if (rows.length === 0) return { recentMonth: null, priorMonth: null, points: [] };
92762
92909
  const months = [...new Set(rows.map((r) => r.month))].sort().reverse();
92763
92910
  const recentMonth = months[0] ?? null;
@@ -92788,7 +92935,7 @@ var IntelligenceService = class {
92788
92935
  */
92789
92936
  persistGbpInsights(runId, projectId, gbpInsights, coveredLocationNames) {
92790
92937
  const covered = new Set(coveredLocationNames);
92791
- const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and75(eq89(insights.projectId, projectId), eq89(insights.provider, GBP_INSIGHT_PROVIDER))).all();
92938
+ const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and76(eq90(insights.projectId, projectId), eq90(insights.provider, GBP_INSIGHT_PROVIDER))).all();
92792
92939
  const staleIds = [];
92793
92940
  const dismissedSlots = /* @__PURE__ */ new Set();
92794
92941
  for (const row of existing) {
@@ -92799,7 +92946,7 @@ var IntelligenceService = class {
92799
92946
  }
92800
92947
  this.db.transaction((tx) => {
92801
92948
  for (const id of staleIds) {
92802
- tx.delete(insights).where(eq89(insights.id, id)).run();
92949
+ tx.delete(insights).where(eq90(insights.id, id)).run();
92803
92950
  }
92804
92951
  for (const insight of gbpInsights) {
92805
92952
  const parsed = parseGbpInsightId(insight.id);
@@ -92886,7 +93033,7 @@ var IntelligenceService = class {
92886
93033
  * create per run + aggregate). DB is left untouched.
92887
93034
  */
92888
93035
  backfill(projectName, opts, onProgress) {
92889
- const project = this.db.select().from(projects).where(eq89(projects.name, projectName)).get();
93036
+ const project = this.db.select().from(projects).where(eq90(projects.name, projectName)).get();
92890
93037
  if (!project) {
92891
93038
  throw new Error(`Project "${projectName}" not found`);
92892
93039
  }
@@ -92898,7 +93045,7 @@ var IntelligenceService = class {
92898
93045
  }
92899
93046
  sinceTimestamp = parsed;
92900
93047
  }
92901
- const allRuns = this.db.select().from(runs).where(and75(...analyzableRunPredicates(project.id))).orderBy(asc18(runs.finishedAt)).all();
93048
+ const allRuns = this.db.select().from(runs).where(and76(...analyzableRunPredicates(project.id))).orderBy(asc18(runs.finishedAt)).all();
92902
93049
  const measuredRunIds = this.runIdsWithSnapshots(allRuns.map((r) => r.id));
92903
93050
  const measuredRuns = allRuns.filter((r) => measuredRunIds.has(r.id));
92904
93051
  let startIdx = 0;
@@ -92929,7 +93076,7 @@ var IntelligenceService = class {
92929
93076
  let wouldDeleteTotal = 0;
92930
93077
  const existingByRunId = /* @__PURE__ */ new Map();
92931
93078
  if (isDryRun && targetRuns.length > 0) {
92932
- const rows = this.db.select({ runId: insights.runId }).from(insights).where(inArray36(insights.runId, targetRuns.map((r) => r.id))).all();
93079
+ const rows = this.db.select({ runId: insights.runId }).from(insights).where(inArray37(insights.runId, targetRuns.map((r) => r.id))).all();
92933
93080
  for (const r of rows) {
92934
93081
  if (r.runId == null) continue;
92935
93082
  existingByRunId.set(r.runId, (existingByRunId.get(r.runId) ?? 0) + 1);
@@ -92980,7 +93127,7 @@ var IntelligenceService = class {
92980
93127
  * signals match the same identities the dashboard and reports do.
92981
93128
  */
92982
93129
  loadTrackedCompetitors(projectId) {
92983
- return this.db.select(competitorIdentityColumns).from(competitors).where(eq89(competitors.projectId, projectId)).orderBy(competitors.domain).all();
93130
+ return this.db.select(competitorIdentityColumns).from(competitors).where(eq90(competitors.projectId, projectId)).orderBy(competitors.domain).all();
92984
93131
  }
92985
93132
  /**
92986
93133
  * Which of `runIds` actually wrote query snapshots. One grouped read, so
@@ -92989,7 +93136,7 @@ var IntelligenceService = class {
92989
93136
  */
92990
93137
  runIdsWithSnapshots(runIds) {
92991
93138
  if (runIds.length === 0) return /* @__PURE__ */ new Set();
92992
- const rows = this.db.select({ runId: querySnapshots.runId }).from(querySnapshots).where(inArray36(querySnapshots.runId, [...runIds])).groupBy(querySnapshots.runId).all();
93139
+ const rows = this.db.select({ runId: querySnapshots.runId }).from(querySnapshots).where(inArray37(querySnapshots.runId, [...runIds])).groupBy(querySnapshots.runId).all();
92993
93140
  return new Set(rows.map((r) => r.runId));
92994
93141
  }
92995
93142
  /**
@@ -93011,15 +93158,15 @@ var IntelligenceService = class {
93011
93158
  }
93012
93159
  persistResult(result, runId, projectId) {
93013
93160
  const previouslyDismissed = /* @__PURE__ */ new Set();
93014
- const existingInsights = this.db.select({ query: insights.query, provider: insights.provider, type: insights.type, dismissed: insights.dismissed }).from(insights).where(eq89(insights.runId, runId)).all();
93161
+ const existingInsights = this.db.select({ query: insights.query, provider: insights.provider, type: insights.type, dismissed: insights.dismissed }).from(insights).where(eq90(insights.runId, runId)).all();
93015
93162
  for (const row of existingInsights) {
93016
93163
  if (row.dismissed) {
93017
93164
  previouslyDismissed.add(`${row.query}:${row.provider}:${row.type}`);
93018
93165
  }
93019
93166
  }
93020
93167
  this.db.transaction((tx) => {
93021
- tx.delete(insights).where(eq89(insights.runId, runId)).run();
93022
- tx.delete(healthSnapshots).where(eq89(healthSnapshots.runId, runId)).run();
93168
+ tx.delete(insights).where(eq90(insights.runId, runId)).run();
93169
+ tx.delete(healthSnapshots).where(eq90(healthSnapshots.runId, runId)).run();
93023
93170
  const now = (/* @__PURE__ */ new Date()).toISOString();
93024
93171
  for (const insight of result.insights) {
93025
93172
  const wasDismissed = previouslyDismissed.has(`${insight.query}:${insight.provider}:${insight.type}`);
@@ -93066,8 +93213,8 @@ var IntelligenceService = class {
93066
93213
  clicks: gscSearchData.clicks,
93067
93214
  impressions: gscSearchData.impressions,
93068
93215
  position: gscSearchData.position
93069
- }).from(gscSearchData).where(and75(
93070
- eq89(gscSearchData.projectId, projectId),
93216
+ }).from(gscSearchData).where(and76(
93217
+ eq90(gscSearchData.projectId, projectId),
93071
93218
  gte24(gscSearchData.date, window.startDate),
93072
93219
  lte20(gscSearchData.date, window.endDate)
93073
93220
  )).all();
@@ -93120,21 +93267,21 @@ var IntelligenceService = class {
93120
93267
  const key = row.query.toLowerCase();
93121
93268
  gscImpressionsByQuery.set(key, (gscImpressionsByQuery.get(key) ?? 0) + row.impressions);
93122
93269
  }
93123
- const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq89(projects.id, projectId)).get();
93270
+ const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq90(projects.id, projectId)).get();
93124
93271
  const locationCount = Math.max(
93125
93272
  1,
93126
93273
  (projectRow?.locations ?? []).length
93127
93274
  );
93128
93275
  const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
93129
93276
  const recentRunRows = this.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
93130
- and75(
93131
- eq89(runs.projectId, projectId),
93132
- eq89(runs.kind, RunKinds["answer-visibility"]),
93133
- or22(eq89(runs.status, "completed"), eq89(runs.status, "partial")),
93277
+ and76(
93278
+ eq90(runs.projectId, projectId),
93279
+ eq90(runs.kind, RunKinds["answer-visibility"]),
93280
+ or23(eq90(runs.status, "completed"), eq90(runs.status, "partial")),
93134
93281
  // Defensive — see top of file.
93135
93282
  ne16(runs.trigger, RunTriggers.probe)
93136
93283
  )
93137
- ).orderBy(desc41(runs.createdAt), desc41(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
93284
+ ).orderBy(desc42(runs.createdAt), desc42(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
93138
93285
  const recentGroups = groupRunsByCreatedAt(recentRunRows);
93139
93286
  const recentRunIds = [];
93140
93287
  const recentRunIdToCreatedAt = /* @__PURE__ */ new Map();
@@ -93150,7 +93297,7 @@ var IntelligenceService = class {
93150
93297
  const haveHistory = recentRunIds.length > 0;
93151
93298
  const priorRegressionsByPair = /* @__PURE__ */ new Map();
93152
93299
  if (haveHistory) {
93153
- const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and75(eq89(insights.type, "regression"), inArray36(insights.runId, recentRunIds))).all();
93300
+ const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and76(eq90(insights.type, "regression"), inArray37(insights.runId, recentRunIds))).all();
93154
93301
  const regressionGroups = /* @__PURE__ */ new Map();
93155
93302
  for (const row of priorRows) {
93156
93303
  if (!row.runId) continue;
@@ -93179,7 +93326,7 @@ var IntelligenceService = class {
93179
93326
  });
93180
93327
  }
93181
93328
  buildRunData(runId, projectId, completedAt, location = null, trackedCompetitors = []) {
93182
- const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq89(projects.id, projectId)).get();
93329
+ const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq90(projects.id, projectId)).get();
93183
93330
  const projectDomains = projectDomainRow ? effectiveDomains({
93184
93331
  canonicalDomain: projectDomainRow.canonicalDomain,
93185
93332
  ownedDomains: projectDomainRow.ownedDomains
@@ -93197,7 +93344,7 @@ var IntelligenceService = class {
93197
93344
  citedDomains: querySnapshots.citedDomains,
93198
93345
  rawResponse: querySnapshots.rawResponse,
93199
93346
  snapshotLocation: querySnapshots.location
93200
- }).from(querySnapshots).leftJoin(queries, eq89(querySnapshots.queryId, queries.id)).where(eq89(querySnapshots.runId, runId)).all();
93347
+ }).from(querySnapshots).leftJoin(queries, eq90(querySnapshots.queryId, queries.id)).where(eq90(querySnapshots.runId, runId)).all();
93201
93348
  const snapshots = [];
93202
93349
  let orphanCount = 0;
93203
93350
  for (const r of rows) {