@ainyc/canonry 4.84.0 → 4.85.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 (24) hide show
  1. package/assets/agent-workspace/skills/aero/references/regression-playbook.md +2 -0
  2. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +19 -0
  3. package/assets/assets/{BacklinksPage-OrSg_iPA.js → BacklinksPage-CDAv0ggn.js} +1 -1
  4. package/assets/assets/{ChartPrimitives-DPBhAT_r.js → ChartPrimitives-CnAmsyt7.js} +1 -1
  5. package/assets/assets/{ProjectPage-CpMcEmtw.js → ProjectPage-C9KEgRxD.js} +1 -1
  6. package/assets/assets/{RunRow-2Rty0BAH.js → RunRow-CVZ5o8fg.js} +1 -1
  7. package/assets/assets/{RunsPage-B3ahqf8s.js → RunsPage-Bzy5c0MZ.js} +1 -1
  8. package/assets/assets/{SettingsPage-BIjeI85q.js → SettingsPage-B1ocxPBe.js} +1 -1
  9. package/assets/assets/{TrafficPage-DjGoj691.js → TrafficPage-D2zepQOC.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-BgKG-2q3.js → TrafficSourceDetailPage-C7JuAkaK.js} +1 -1
  11. package/assets/assets/{arrow-left-Cf7wmru1.js → arrow-left-Bv3CWylm.js} +1 -1
  12. package/assets/assets/{extract-error-message-CANxezte.js → extract-error-message-BtVid5TP.js} +1 -1
  13. package/assets/assets/{index-CGlPx_cu.js → index-DmNti_xn.js} +72 -72
  14. package/assets/assets/{trash-2-6nHJZrvy.js → trash-2-BoimCsYz.js} +1 -1
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-VJBO4VIK.js → chunk-3K3QRSYE.js} +620 -422
  17. package/dist/{chunk-Y3O3HBMN.js → chunk-62YB3ML7.js} +49 -1
  18. package/dist/{chunk-M3IYKTSF.js → chunk-7BMSWI2K.js} +4 -4
  19. package/dist/{chunk-BNF3HXBW.js → chunk-I2BJC3DT.js} +1021 -942
  20. package/dist/cli.js +151 -20
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-PDIAMP5I.js → intelligence-service-AHHBQKRD.js} +2 -2
  23. package/dist/mcp.js +2 -2
  24. package/package.json +9 -9
@@ -169,6 +169,7 @@ import {
169
169
  notFound,
170
170
  notImplemented,
171
171
  notificationDtoSchema,
172
+ parseInclusiveEndMs,
172
173
  parseRunError,
173
174
  parseWindow,
174
175
  pickClusterRepresentative,
@@ -230,6 +231,7 @@ import {
230
231
  unsupportedKind,
231
232
  validationError,
232
233
  visibilityStateFromAnswerMentioned,
234
+ visibilityStatsDtoSchema,
233
235
  windowCutoff,
234
236
  winnabilityClassLabel,
235
237
  winnabilityClassSchema,
@@ -246,10 +248,10 @@ import {
246
248
  wordpressSchemaDeployResultDtoSchema,
247
249
  wordpressSchemaStatusResultDtoSchema,
248
250
  wordpressStatusDtoSchema
249
- } from "./chunk-BNF3HXBW.js";
251
+ } from "./chunk-I2BJC3DT.js";
250
252
 
251
253
  // src/intelligence-service.ts
252
- import { eq as eq36, desc as desc17, asc as asc5, and as and26, ne as ne5, or as or5, inArray as inArray13, gte as gte7, lte as lte4 } from "drizzle-orm";
254
+ import { eq as eq37, desc as desc18, asc as asc5, and as and27, ne as ne5, or as or5, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
253
255
 
254
256
  // ../db/src/client.ts
255
257
  import { mkdirSync } from "fs";
@@ -4339,18 +4341,18 @@ function buildCitationScorecard(snapshots, queryLookup) {
4339
4341
  answerMentioned: snap.answerMentioned ?? null,
4340
4342
  model: snap.model
4341
4343
  };
4342
- const counts = providerCounts.get(snap.provider) ?? { cited: 0, total: 0 };
4343
- counts.total++;
4344
- if (snap.citationState === CitationStates.cited) counts.cited++;
4345
- providerCounts.set(snap.provider, counts);
4344
+ const counts2 = providerCounts.get(snap.provider) ?? { cited: 0, total: 0 };
4345
+ counts2.total++;
4346
+ if (snap.citationState === CitationStates.cited) counts2.cited++;
4347
+ providerCounts.set(snap.provider, counts2);
4346
4348
  }
4347
4349
  const providerRates = providerList.map((provider) => {
4348
- const counts = providerCounts.get(provider) ?? { cited: 0, total: 0 };
4349
- const citationRate = counts.total > 0 ? Math.round(counts.cited / counts.total * 100) : 0;
4350
+ const counts2 = providerCounts.get(provider) ?? { cited: 0, total: 0 };
4351
+ const citationRate = counts2.total > 0 ? Math.round(counts2.cited / counts2.total * 100) : 0;
4350
4352
  return {
4351
4353
  provider,
4352
- citedCount: counts.cited,
4353
- totalCount: counts.total,
4354
+ citedCount: counts2.cited,
4355
+ totalCount: counts2.total,
4354
4356
  citationRate
4355
4357
  };
4356
4358
  });
@@ -7918,13 +7920,13 @@ function buildRankedList(domains, limit) {
7918
7920
  bySurfaceClass
7919
7921
  };
7920
7922
  }
7921
- function buildCategoryCounts(counts) {
7923
+ function buildCategoryCounts(counts2) {
7922
7924
  let grandTotal = 0;
7923
- for (const domains of counts.values()) {
7925
+ for (const domains of counts2.values()) {
7924
7926
  for (const count2 of domains.values()) grandTotal += count2;
7925
7927
  }
7926
7928
  const result = [];
7927
- for (const [category, domains] of counts) {
7929
+ for (const [category, domains] of counts2) {
7928
7930
  let categoryTotal = 0;
7929
7931
  const domainEntries = [];
7930
7932
  for (const [domain, count2] of domains) {
@@ -11738,19 +11740,19 @@ function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
11738
11740
  considered++;
11739
11741
  if (snap.citationState === CitationStates.cited) citedQueryIds.add(snap.queryId);
11740
11742
  if (snap.answerMentioned) mentionedQueryIds.add(snap.queryId);
11741
- const counts = providerCounts.get(snap.provider) ?? { cited: 0, total: 0 };
11742
- counts.total++;
11743
- if (snap.citationState === CitationStates.cited) counts.cited++;
11744
- providerCounts.set(snap.provider, counts);
11743
+ const counts2 = providerCounts.get(snap.provider) ?? { cited: 0, total: 0 };
11744
+ counts2.total++;
11745
+ if (snap.citationState === CitationStates.cited) counts2.cited++;
11746
+ providerCounts.set(snap.provider, counts2);
11745
11747
  }
11746
11748
  if (considered === 0) continue;
11747
11749
  const citedQueryCount = citedQueryIds.size;
11748
11750
  const mentionedQueryCount = mentionedQueryIds.size;
11749
11751
  const citationRate = totalQueries > 0 ? Math.round(citedQueryCount / totalQueries * 100) : 0;
11750
11752
  const mentionRate = totalQueries > 0 ? Math.round(mentionedQueryCount / totalQueries * 100) : 0;
11751
- const providerRates = [...providerCounts.entries()].map(([provider, counts]) => ({
11753
+ const providerRates = [...providerCounts.entries()].map(([provider, counts2]) => ({
11752
11754
  provider,
11753
- citationRate: counts.total > 0 ? Math.round(counts.cited / counts.total * 100) : 0
11755
+ citationRate: counts2.total > 0 ? Math.round(counts2.cited / counts2.total * 100) : 0
11754
11756
  })).sort((a, b) => a.provider.localeCompare(b.provider));
11755
11757
  points.push({
11756
11758
  runId: run.id,
@@ -12720,8 +12722,165 @@ function normalizeDomain2(domain) {
12720
12722
  return domain.toLowerCase().trim().replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/$/, "");
12721
12723
  }
12722
12724
 
12725
+ // ../api-routes/src/visibility-stats.ts
12726
+ import { and as and11, desc as desc8, eq as eq16, inArray as inArray8 } from "drizzle-orm";
12727
+ function round42(value) {
12728
+ return Math.round(value * 1e4) / 1e4;
12729
+ }
12730
+ function emptyAgg() {
12731
+ return { total: 0, checked: 0, mentioned: 0, cited: 0, first: null, last: null };
12732
+ }
12733
+ function addSnapshot(agg, snap) {
12734
+ agg.total++;
12735
+ if (snap.answerMentioned === true || snap.answerMentioned === false) agg.checked++;
12736
+ if (snap.answerMentioned === true) agg.mentioned++;
12737
+ if (snap.citationState === CitationStates.cited) agg.cited++;
12738
+ if (agg.first === null || snap.createdAt < agg.first) agg.first = snap.createdAt;
12739
+ if (agg.last === null || snap.createdAt > agg.last) agg.last = snap.createdAt;
12740
+ }
12741
+ function counts(agg) {
12742
+ return {
12743
+ total: agg.total,
12744
+ checked: agg.checked,
12745
+ mentioned: agg.mentioned,
12746
+ cited: agg.cited,
12747
+ // mention proportion is over the CHECKED sample; citation proportion is
12748
+ // over the full total (every snapshot is checked for citation).
12749
+ mentionRate: agg.checked > 0 ? round42(agg.mentioned / agg.checked) : null,
12750
+ citedRate: agg.total > 0 ? round42(agg.cited / agg.total) : null
12751
+ };
12752
+ }
12753
+ function providerEntries(byProvider) {
12754
+ return [...byProvider.entries()].map(([provider, agg]) => ({
12755
+ provider,
12756
+ ...counts(agg),
12757
+ // first/last are non-null once at least one snapshot landed in the agg,
12758
+ // which is guaranteed for any provider that made it into the map.
12759
+ firstObserved: agg.first ?? "",
12760
+ lastObserved: agg.last ?? ""
12761
+ })).sort((a, b) => a.provider.localeCompare(b.provider));
12762
+ }
12763
+ function computeVisibilityStats(input) {
12764
+ const { groupBy } = input;
12765
+ const wantProviders = groupBy === "provider";
12766
+ const queryById = /* @__PURE__ */ new Map();
12767
+ const queryByText = /* @__PURE__ */ new Map();
12768
+ for (const q of input.queries) {
12769
+ queryById.set(q.id, q);
12770
+ queryByText.set(q.query, q);
12771
+ }
12772
+ const byQuery = /* @__PURE__ */ new Map();
12773
+ const totals = emptyAgg();
12774
+ const totalsByProvider = /* @__PURE__ */ new Map();
12775
+ for (const snap of input.snapshots) {
12776
+ let resolved;
12777
+ if (snap.queryId && queryById.has(snap.queryId)) resolved = queryById.get(snap.queryId);
12778
+ else if (snap.queryText && queryByText.has(snap.queryText)) resolved = queryByText.get(snap.queryText);
12779
+ if (!resolved) continue;
12780
+ let bucket = byQuery.get(resolved.id);
12781
+ if (!bucket) {
12782
+ bucket = { id: resolved.id, query: resolved.query, agg: emptyAgg(), byProvider: /* @__PURE__ */ new Map() };
12783
+ byQuery.set(resolved.id, bucket);
12784
+ }
12785
+ addSnapshot(bucket.agg, snap);
12786
+ addSnapshot(totals, snap);
12787
+ if (wantProviders) {
12788
+ const qpAgg = bucket.byProvider.get(snap.provider) ?? emptyAgg();
12789
+ addSnapshot(qpAgg, snap);
12790
+ bucket.byProvider.set(snap.provider, qpAgg);
12791
+ const tpAgg = totalsByProvider.get(snap.provider) ?? emptyAgg();
12792
+ addSnapshot(tpAgg, snap);
12793
+ totalsByProvider.set(snap.provider, tpAgg);
12794
+ }
12795
+ }
12796
+ const queryEntries = [...byQuery.values()].map((bucket) => ({
12797
+ queryId: bucket.id,
12798
+ query: bucket.query,
12799
+ ...counts(bucket.agg),
12800
+ firstObserved: bucket.agg.first ?? "",
12801
+ lastObserved: bucket.agg.last ?? "",
12802
+ ...wantProviders ? { providers: providerEntries(bucket.byProvider) } : {}
12803
+ })).sort((a, b) => a.query.localeCompare(b.query));
12804
+ return {
12805
+ totals: counts(totals),
12806
+ ...wantProviders ? { byProvider: providerEntries(totalsByProvider) } : {},
12807
+ queries: queryEntries
12808
+ };
12809
+ }
12810
+ async function visibilityStatsRoutes(app) {
12811
+ app.get("/projects/:name/visibility-stats", async (request, reply) => {
12812
+ const project = resolveProject(app.db, request.params.name);
12813
+ const { since: sinceRaw, until: untilRaw, lastRuns: lastRunsRaw, groupBy: groupByRaw } = request.query;
12814
+ let groupBy = null;
12815
+ if (groupByRaw !== void 0 && groupByRaw !== "") {
12816
+ if (groupByRaw !== "provider") throw validationError('"groupBy" must be "provider"');
12817
+ groupBy = "provider";
12818
+ }
12819
+ const hasSince = sinceRaw !== void 0 && sinceRaw !== "";
12820
+ const hasUntil = untilRaw !== void 0 && untilRaw !== "";
12821
+ const hasLastRuns = lastRunsRaw !== void 0 && lastRunsRaw !== "";
12822
+ if (hasLastRuns && (hasSince || hasUntil)) {
12823
+ throw validationError('"lastRuns" cannot be combined with "since"/"until" \u2014 use one or the other');
12824
+ }
12825
+ let sinceMs = null;
12826
+ let untilMs = null;
12827
+ if (hasSince) {
12828
+ const ms = Date.parse(sinceRaw);
12829
+ if (Number.isNaN(ms)) throw validationError('"since" must be an ISO 8601 date/time');
12830
+ sinceMs = ms;
12831
+ }
12832
+ if (hasUntil) {
12833
+ const ms = parseInclusiveEndMs(untilRaw);
12834
+ if (ms === null) throw validationError('"until" must be an ISO 8601 date/time');
12835
+ untilMs = ms;
12836
+ }
12837
+ if (sinceMs !== null && untilMs !== null && untilMs < sinceMs) {
12838
+ throw validationError('"until" must be on or after "since"');
12839
+ }
12840
+ let lastRuns = null;
12841
+ if (hasLastRuns) {
12842
+ const n = Number(lastRunsRaw);
12843
+ if (!Number.isInteger(n) || n <= 0) throw validationError('"lastRuns" must be a positive integer');
12844
+ lastRuns = n;
12845
+ }
12846
+ const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq16(queries.projectId, project.id)).all();
12847
+ let projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and11(eq16(runs.projectId, project.id), eq16(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc8(runs.createdAt)).all().filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial);
12848
+ if (sinceMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) >= sinceMs);
12849
+ if (untilMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) <= untilMs);
12850
+ if (lastRuns !== null) projectRuns = projectRuns.slice(0, lastRuns);
12851
+ const runCount = projectRuns.length;
12852
+ const runIds = projectRuns.map((r) => r.id);
12853
+ const snapshots = runIds.length > 0 && projectQueries.length > 0 ? app.db.select({
12854
+ queryId: querySnapshots.queryId,
12855
+ queryText: querySnapshots.queryText,
12856
+ provider: querySnapshots.provider,
12857
+ citationState: querySnapshots.citationState,
12858
+ answerMentioned: querySnapshots.answerMentioned,
12859
+ createdAt: querySnapshots.createdAt
12860
+ }).from(querySnapshots).where(inArray8(querySnapshots.runId, runIds)).all() : [];
12861
+ const stats = computeVisibilityStats({ queries: projectQueries, snapshots, groupBy });
12862
+ const response = {
12863
+ project: project.name,
12864
+ window: {
12865
+ since: hasSince ? sinceRaw : null,
12866
+ until: hasUntil ? untilRaw : null,
12867
+ lastRuns,
12868
+ runCount
12869
+ },
12870
+ totals: stats.totals,
12871
+ queries: stats.queries,
12872
+ // `groupBy` + `byProvider` appear together only when a breakdown was
12873
+ // requested; both are OMITTED otherwise (absent = no breakdown) so the
12874
+ // SDK types `groupBy` as `groupBy?: 'provider'` rather than a misleading
12875
+ // always-present literal.
12876
+ ...groupBy === "provider" ? { groupBy, byProvider: stats.byProvider ?? [] } : {}
12877
+ };
12878
+ return reply.send(response);
12879
+ });
12880
+ }
12881
+
12723
12882
  // ../api-routes/src/composites.ts
12724
- import { eq as eq16, and as and11, desc as desc8, sql as sql7, like, or as or4, inArray as inArray8 } from "drizzle-orm";
12883
+ import { eq as eq17, and as and12, desc as desc9, sql as sql7, like, or as or4, inArray as inArray9 } from "drizzle-orm";
12725
12884
  var TOP_INSIGHT_LIMIT = 5;
12726
12885
  var SEARCH_HIT_HARD_LIMIT = 50;
12727
12886
  var SEARCH_SNIPPET_RADIUS = 80;
@@ -12739,7 +12898,7 @@ async function compositeRoutes(app) {
12739
12898
  const project = resolveProject(app.db, request.params.name);
12740
12899
  const filterLocation = (request.query.location ?? "").trim() || null;
12741
12900
  const sinceIso = parseSinceFilter(request.query.since);
12742
- const allRunsRaw = app.db.select().from(runs).where(and11(eq16(runs.projectId, project.id), notProbeRun())).orderBy(desc8(runs.createdAt), desc8(runs.id)).all();
12901
+ const allRunsRaw = app.db.select().from(runs).where(and12(eq17(runs.projectId, project.id), notProbeRun())).orderBy(desc9(runs.createdAt), desc9(runs.id)).all();
12743
12902
  const allRuns = allRunsRaw.filter((r) => runMatchesFilters(r, filterLocation, sinceIso));
12744
12903
  const totalRuns = allRuns.length;
12745
12904
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
@@ -12752,9 +12911,9 @@ async function compositeRoutes(app) {
12752
12911
  const previousVisibilityRun = pickGroupRepresentative(previousVisRunGroup);
12753
12912
  const latestRunRow = allRuns[0] ?? null;
12754
12913
  const latestRun = latestRunRow ? { totalRuns, run: summarizeRun(latestRunRow) } : { totalRuns: 0, run: null };
12755
- const healthRow = app.db.select().from(healthSnapshots).where(eq16(healthSnapshots.projectId, project.id)).orderBy(desc8(healthSnapshots.createdAt)).limit(1).get();
12914
+ const healthRow = app.db.select().from(healthSnapshots).where(eq17(healthSnapshots.projectId, project.id)).orderBy(desc9(healthSnapshots.createdAt)).limit(1).get();
12756
12915
  const health = healthRow ? mapHealthRow2(healthRow) : null;
12757
- const insightRows = app.db.select().from(insights).where(eq16(insights.projectId, project.id)).orderBy(desc8(insights.createdAt)).all();
12916
+ const insightRows = app.db.select().from(insights).where(eq17(insights.projectId, project.id)).orderBy(desc9(insights.createdAt)).all();
12758
12917
  const topInsights = insightRows.filter((row) => !row.dismissed).slice(0, TOP_INSIGHT_LIMIT).map(mapInsightRow2);
12759
12918
  const sparklineRunIds = visibilityRuns.slice(0, DEFAULT_RUN_HISTORY_LIMIT).map((r) => r.id);
12760
12919
  const snapshotRunIds = new Set(sparklineRunIds);
@@ -12769,8 +12928,8 @@ async function compositeRoutes(app) {
12769
12928
  previousSnapshots,
12770
12929
  previousVisibilityRun?.createdAt ?? null
12771
12930
  );
12772
- const competitorRows = app.db.select().from(competitors).where(eq16(competitors.projectId, project.id)).all();
12773
- const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq16(queries.projectId, project.id)).all();
12931
+ const competitorRows = app.db.select().from(competitors).where(eq17(competitors.projectId, project.id)).all();
12932
+ const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq17(queries.projectId, project.id)).all();
12774
12933
  const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
12775
12934
  const configuredApiProviders = project.providers.filter((p) => !p.startsWith("cdp:"));
12776
12935
  const mentionShareCompetitors = competitorRows.map((c) => ({
@@ -12869,9 +13028,9 @@ async function compositeRoutes(app) {
12869
13028
  citedDomains: querySnapshots.citedDomains,
12870
13029
  rawResponse: querySnapshots.rawResponse,
12871
13030
  createdAt: querySnapshots.createdAt
12872
- }).from(querySnapshots).innerJoin(queries, eq16(querySnapshots.queryId, queries.id)).where(
12873
- and11(
12874
- eq16(queries.projectId, project.id),
13031
+ }).from(querySnapshots).innerJoin(queries, eq17(querySnapshots.queryId, queries.id)).where(
13032
+ and12(
13033
+ eq17(queries.projectId, project.id),
12875
13034
  or4(
12876
13035
  sql7`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
12877
13036
  sql7`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
@@ -12879,10 +13038,10 @@ async function compositeRoutes(app) {
12879
13038
  like(queries.query, pattern)
12880
13039
  )
12881
13040
  )
12882
- ).orderBy(desc8(querySnapshots.createdAt)).limit(limit + 1).all());
13041
+ ).orderBy(desc9(querySnapshots.createdAt)).limit(limit + 1).all());
12883
13042
  const insightMatches = app.db.select().from(insights).where(
12884
- and11(
12885
- eq16(insights.projectId, project.id),
13043
+ and12(
13044
+ eq17(insights.projectId, project.id),
12886
13045
  or4(
12887
13046
  like(insights.title, pattern),
12888
13047
  like(insights.query, pattern),
@@ -12890,7 +13049,7 @@ async function compositeRoutes(app) {
12890
13049
  sql7`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
12891
13050
  )
12892
13051
  )
12893
- ).orderBy(desc8(insights.createdAt)).limit(limit + 1).all();
13052
+ ).orderBy(desc9(insights.createdAt)).limit(limit + 1).all();
12894
13053
  const hits = [];
12895
13054
  for (const row of snapshotMatches) {
12896
13055
  hits.push(buildSnapshotHit(row, rawQuery));
@@ -12960,7 +13119,7 @@ function loadSnapshotsByRunIds(app, runIds) {
12960
13119
  answerText: querySnapshots.answerText,
12961
13120
  competitorOverlap: querySnapshots.competitorOverlap,
12962
13121
  citedDomains: querySnapshots.citedDomains
12963
- }).from(querySnapshots).where(inArray8(querySnapshots.runId, [...runIds])).all());
13122
+ }).from(querySnapshots).where(inArray9(querySnapshots.runId, [...runIds])).all());
12964
13123
  for (const row of rows) {
12965
13124
  const list = result.get(row.runId) ?? [];
12966
13125
  list.push({
@@ -13078,8 +13237,8 @@ function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
13078
13237
  // NULLIF guards the degenerate impressions=0 case (SQLite returns NULL,
13079
13238
  // which the JS coerces to 0 — caught by the impression floor anyway).
13080
13239
  avgPosition: sql7`COALESCE(SUM(${gscSearchData.position} * ${gscSearchData.impressions}) * 1.0 / NULLIF(SUM(${gscSearchData.impressions}), 0), 0)`
13081
- }).from(gscSearchData).where(and11(
13082
- eq16(gscSearchData.projectId, projectId),
13240
+ }).from(gscSearchData).where(and12(
13241
+ eq17(gscSearchData.projectId, projectId),
13083
13242
  sql7`${gscSearchData.date} >= ${cutoff}`,
13084
13243
  sql7`${gscSearchData.impressions} > 0`
13085
13244
  )).groupBy(gscSearchData.query).orderBy(sql7`SUM(${gscSearchData.impressions}) DESC`).limit(100).all();
@@ -13102,8 +13261,8 @@ function buildIndexCoverageScore(app, projectId) {
13102
13261
  tooltip,
13103
13262
  trend: []
13104
13263
  };
13105
- const gscRow = app.db.select().from(gscCoverageSnapshots).where(eq16(gscCoverageSnapshots.projectId, projectId)).orderBy(desc8(gscCoverageSnapshots.date)).limit(1).get();
13106
- const bingRow = app.db.select().from(bingCoverageSnapshots).where(eq16(bingCoverageSnapshots.projectId, projectId)).orderBy(desc8(bingCoverageSnapshots.date)).limit(1).get();
13264
+ const gscRow = app.db.select().from(gscCoverageSnapshots).where(eq17(gscCoverageSnapshots.projectId, projectId)).orderBy(desc9(gscCoverageSnapshots.date)).limit(1).get();
13265
+ const bingRow = app.db.select().from(bingCoverageSnapshots).where(eq17(bingCoverageSnapshots.projectId, projectId)).orderBy(desc9(bingCoverageSnapshots.date)).limit(1).get();
13107
13266
  const chosen = pickIndexCoverageRow(gscRow, bingRow);
13108
13267
  if (!chosen) return empty;
13109
13268
  const total = chosen.indexed + chosen.notIndexed;
@@ -13129,7 +13288,7 @@ function countGoogleDeindexedUrls(app, projectId) {
13129
13288
  url: gscUrlInspections.url,
13130
13289
  indexingState: gscUrlInspections.indexingState,
13131
13290
  inspectedAt: gscUrlInspections.inspectedAt
13132
- }).from(gscUrlInspections).where(eq16(gscUrlInspections.projectId, projectId)).orderBy(desc8(gscUrlInspections.inspectedAt)).all();
13291
+ }).from(gscUrlInspections).where(eq17(gscUrlInspections.projectId, projectId)).orderBy(desc9(gscUrlInspections.inspectedAt)).all();
13133
13292
  if (rows.length === 0) return 0;
13134
13293
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
13135
13294
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -13475,6 +13634,7 @@ var SCHEMA_TABLE = {
13475
13634
  TrafficSourceListResponse: trafficSourceListResponseSchema,
13476
13635
  TrafficStatusResponse: trafficStatusResponseSchema,
13477
13636
  TrafficSyncResponse: trafficSyncResponseSchema,
13637
+ VisibilityStatsDto: visibilityStatsDtoSchema,
13478
13638
  WordpressAuditPageDto: wordpressAuditPageDtoSchema,
13479
13639
  WordpressBulkMetaResultDto: wordpressBulkMetaResultDtoSchema,
13480
13640
  WordpressDiffDto: wordpressDiffDtoSchema,
@@ -13700,6 +13860,30 @@ var analyticsWindowParameter = {
13700
13860
  description: "Time window for analytics queries.",
13701
13861
  schema: { type: "string", enum: ["7d", "30d", "90d", "all"] }
13702
13862
  };
13863
+ var sinceQueryParameter = {
13864
+ name: "since",
13865
+ in: "query",
13866
+ description: 'Inclusive lower bound on run createdAt (ISO 8601). A date-only value (YYYY-MM-DD) is the start of that UTC day. Mutually exclusive with "lastRuns".',
13867
+ schema: stringSchema
13868
+ };
13869
+ var untilQueryParameter = {
13870
+ name: "until",
13871
+ in: "query",
13872
+ description: 'Inclusive upper bound on run createdAt (ISO 8601). A date-only value (YYYY-MM-DD) covers the whole UTC day (through 23:59:59.999). Mutually exclusive with "lastRuns".',
13873
+ schema: stringSchema
13874
+ };
13875
+ var lastRunsQueryParameter = {
13876
+ name: "lastRuns",
13877
+ in: "query",
13878
+ description: 'Aggregate only the most recent N answer-visibility runs. Mutually exclusive with "since"/"until".',
13879
+ schema: integerSchema
13880
+ };
13881
+ var groupByProviderQueryParameter = {
13882
+ name: "groupBy",
13883
+ in: "query",
13884
+ description: 'Set to "provider" to include a per-provider breakdown whose counts sum to the pooled counts.',
13885
+ schema: { type: "string", enum: ["provider"] }
13886
+ };
13703
13887
  var wordpressEnvQueryParameter = {
13704
13888
  name: "env",
13705
13889
  in: "query",
@@ -14426,6 +14610,19 @@ var routeCatalog = [
14426
14610
  404: errorResponse("Project not found.")
14427
14611
  }
14428
14612
  },
14613
+ {
14614
+ method: "get",
14615
+ path: "/api/v1/projects/{name}/visibility-stats",
14616
+ summary: "Get aggregated mention/citation stats per query",
14617
+ description: "Per-query mention (answer-text) and citation (source-list) counts with a sample size, pooled across many answer-visibility runs (probe-excluded). Tri-state aware: `checked` counts only snapshots where answerMentioned was recorded (null = not checked is excluded). Lets a consumer compute confidence-aware (e.g. Wilson) proportions without N+1 run fetches. With no since/until/lastRuns, EVERY completed/partial answer-visibility run is pooled \u2014 `window.runCount` reports how many; bound the window with lastRuns or since/until for a recent sample. Set groupBy=provider for a per-provider breakdown whose counts sum to the pooled counts.",
14618
+ tags: ["analytics"],
14619
+ parameters: [nameParameter, sinceQueryParameter, untilQueryParameter, lastRunsQueryParameter, groupByProviderQueryParameter],
14620
+ responses: {
14621
+ 200: jsonResponse("Aggregated visibility stats returned.", "VisibilityStatsDto"),
14622
+ 400: errorResponse("Invalid query parameters."),
14623
+ 404: errorResponse("Project not found.")
14624
+ }
14625
+ },
14429
14626
  {
14430
14627
  method: "get",
14431
14628
  path: "/api/v1/projects/{name}/snapshots/diff",
@@ -17691,7 +17888,7 @@ async function settingsRoutes(app, opts) {
17691
17888
 
17692
17889
  // ../api-routes/src/keys.ts
17693
17890
  import crypto12 from "crypto";
17694
- import { desc as desc9, eq as eq17 } from "drizzle-orm";
17891
+ import { desc as desc10, eq as eq18 } from "drizzle-orm";
17695
17892
  var KEYS_WRITE_SCOPE = "keys.write";
17696
17893
  function toApiKeyDto(row) {
17697
17894
  const scopes = Array.isArray(row.scopes) ? row.scopes : [];
@@ -17708,7 +17905,7 @@ function toApiKeyDto(row) {
17708
17905
  }
17709
17906
  async function keysRoutes(app) {
17710
17907
  app.get("/keys", async () => {
17711
- const rows = app.db.select().from(apiKeys).orderBy(desc9(apiKeys.createdAt)).all();
17908
+ const rows = app.db.select().from(apiKeys).orderBy(desc10(apiKeys.createdAt)).all();
17712
17909
  return { keys: rows.map(toApiKeyDto) };
17713
17910
  });
17714
17911
  app.get("/keys/self", async (request) => {
@@ -17716,7 +17913,7 @@ async function keysRoutes(app) {
17716
17913
  if (!id) {
17717
17914
  throw notFound("API key", "self");
17718
17915
  }
17719
- const row = app.db.select().from(apiKeys).where(eq17(apiKeys.id, id)).get();
17916
+ const row = app.db.select().from(apiKeys).where(eq18(apiKeys.id, id)).get();
17720
17917
  if (!row) {
17721
17918
  throw notFound("API key", id);
17722
17919
  }
@@ -17768,7 +17965,7 @@ async function keysRoutes(app) {
17768
17965
  app.post("/keys/:id/revoke", async (request) => {
17769
17966
  requireScope(request, KEYS_WRITE_SCOPE);
17770
17967
  const { id } = request.params;
17771
- const row = app.db.select().from(apiKeys).where(eq17(apiKeys.id, id)).get();
17968
+ const row = app.db.select().from(apiKeys).where(eq18(apiKeys.id, id)).get();
17772
17969
  if (!row) {
17773
17970
  throw notFound("API key", id);
17774
17971
  }
@@ -17780,7 +17977,7 @@ async function keysRoutes(app) {
17780
17977
  }
17781
17978
  const now = (/* @__PURE__ */ new Date()).toISOString();
17782
17979
  app.db.transaction((tx) => {
17783
- tx.update(apiKeys).set({ revokedAt: now }).where(eq17(apiKeys.id, id)).run();
17980
+ tx.update(apiKeys).set({ revokedAt: now }).where(eq18(apiKeys.id, id)).run();
17784
17981
  writeAuditLog(tx, auditFromRequest(request, {
17785
17982
  actor: "api",
17786
17983
  action: "api-key.revoked",
@@ -17853,7 +18050,7 @@ async function telemetryRoutes(app, opts) {
17853
18050
 
17854
18051
  // ../api-routes/src/schedules.ts
17855
18052
  import crypto13 from "crypto";
17856
- import { and as and12, eq as eq18 } from "drizzle-orm";
18053
+ import { and as and13, eq as eq19 } from "drizzle-orm";
17857
18054
  function parseKindParam(raw) {
17858
18055
  if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
17859
18056
  const parsed = schedulableRunKindSchema.safeParse(raw);
@@ -17880,7 +18077,7 @@ async function scheduleRoutes(app, opts) {
17880
18077
  if (!sourceId) {
17881
18078
  throw validationError('"sourceId" is required when kind is "traffic-sync"');
17882
18079
  }
17883
- const sourceRow = app.db.select().from(trafficSources).where(eq18(trafficSources.id, sourceId)).get();
18080
+ const sourceRow = app.db.select().from(trafficSources).where(eq19(trafficSources.id, sourceId)).get();
17884
18081
  if (!sourceRow || sourceRow.projectId !== project.id) {
17885
18082
  throw notFound("Traffic source", sourceId);
17886
18083
  }
@@ -17925,7 +18122,7 @@ async function scheduleRoutes(app, opts) {
17925
18122
  }
17926
18123
  const now = (/* @__PURE__ */ new Date()).toISOString();
17927
18124
  const enabledBool = enabled !== false;
17928
- const existing = app.db.select().from(schedules).where(and12(eq18(schedules.projectId, project.id), eq18(schedules.kind, kind))).get();
18125
+ const existing = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
17929
18126
  if (existing) {
17930
18127
  app.db.update(schedules).set({
17931
18128
  cronExpr,
@@ -17935,7 +18132,7 @@ async function scheduleRoutes(app, opts) {
17935
18132
  sourceId: sourceId ?? null,
17936
18133
  enabled: enabledBool,
17937
18134
  updatedAt: now
17938
- }).where(eq18(schedules.id, existing.id)).run();
18135
+ }).where(eq19(schedules.id, existing.id)).run();
17939
18136
  } else {
17940
18137
  app.db.insert(schedules).values({
17941
18138
  id: crypto13.randomUUID(),
@@ -17959,13 +18156,13 @@ async function scheduleRoutes(app, opts) {
17959
18156
  diff: { kind, cronExpr, preset, timezone, providers, sourceId }
17960
18157
  });
17961
18158
  opts.onScheduleUpdated?.("upsert", project.id, kind);
17962
- const schedule = app.db.select().from(schedules).where(and12(eq18(schedules.projectId, project.id), eq18(schedules.kind, kind))).get();
18159
+ const schedule = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
17963
18160
  return reply.status(existing ? 200 : 201).send(formatSchedule(schedule));
17964
18161
  });
17965
18162
  app.get("/projects/:name/schedule", async (request, reply) => {
17966
18163
  const project = resolveProject(app.db, request.params.name);
17967
18164
  const kind = parseKindParam(request.query?.kind);
17968
- const schedule = app.db.select().from(schedules).where(and12(eq18(schedules.projectId, project.id), eq18(schedules.kind, kind))).get();
18165
+ const schedule = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
17969
18166
  if (!schedule) {
17970
18167
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
17971
18168
  }
@@ -17974,11 +18171,11 @@ async function scheduleRoutes(app, opts) {
17974
18171
  app.delete("/projects/:name/schedule", async (request, reply) => {
17975
18172
  const project = resolveProject(app.db, request.params.name);
17976
18173
  const kind = parseKindParam(request.query?.kind);
17977
- const schedule = app.db.select().from(schedules).where(and12(eq18(schedules.projectId, project.id), eq18(schedules.kind, kind))).get();
18174
+ const schedule = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
17978
18175
  if (!schedule) {
17979
18176
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
17980
18177
  }
17981
- app.db.delete(schedules).where(eq18(schedules.id, schedule.id)).run();
18178
+ app.db.delete(schedules).where(eq19(schedules.id, schedule.id)).run();
17982
18179
  writeAuditLog(app.db, {
17983
18180
  projectId: project.id,
17984
18181
  actor: "api",
@@ -18011,7 +18208,7 @@ function formatSchedule(row) {
18011
18208
 
18012
18209
  // ../api-routes/src/notifications.ts
18013
18210
  import crypto14 from "crypto";
18014
- import { eq as eq19 } from "drizzle-orm";
18211
+ import { eq as eq20 } from "drizzle-orm";
18015
18212
  var VALID_EVENTS = ["citation.lost", "citation.gained", "run.completed", "run.failed", "insight.critical", "insight.high"];
18016
18213
  async function notificationRoutes(app, opts = {}) {
18017
18214
  const allowLoopback = opts.allowLoopbackWebhooks === true;
@@ -18051,22 +18248,22 @@ async function notificationRoutes(app, opts = {}) {
18051
18248
  diff: { channel, ...redactNotificationUrl(url), events }
18052
18249
  });
18053
18250
  return reply.status(201).send({
18054
- ...formatNotification(app.db.select().from(notifications).where(eq19(notifications.id, id)).get()),
18251
+ ...formatNotification(app.db.select().from(notifications).where(eq20(notifications.id, id)).get()),
18055
18252
  webhookSecret
18056
18253
  });
18057
18254
  });
18058
18255
  app.get("/projects/:name/notifications", async (request, reply) => {
18059
18256
  const project = resolveProject(app.db, request.params.name);
18060
- const rows = app.db.select().from(notifications).where(eq19(notifications.projectId, project.id)).all();
18257
+ const rows = app.db.select().from(notifications).where(eq20(notifications.projectId, project.id)).all();
18061
18258
  return reply.send(rows.map(formatNotification));
18062
18259
  });
18063
18260
  app.delete("/projects/:name/notifications/:id", async (request, reply) => {
18064
18261
  const project = resolveProject(app.db, request.params.name);
18065
- const notification = app.db.select().from(notifications).where(eq19(notifications.id, request.params.id)).get();
18262
+ const notification = app.db.select().from(notifications).where(eq20(notifications.id, request.params.id)).get();
18066
18263
  if (!notification || notification.projectId !== project.id) {
18067
18264
  throw notFound("Notification", request.params.id);
18068
18265
  }
18069
- app.db.delete(notifications).where(eq19(notifications.id, notification.id)).run();
18266
+ app.db.delete(notifications).where(eq20(notifications.id, notification.id)).run();
18070
18267
  writeAuditLog(app.db, {
18071
18268
  projectId: project.id,
18072
18269
  actor: "api",
@@ -18078,7 +18275,7 @@ async function notificationRoutes(app, opts = {}) {
18078
18275
  });
18079
18276
  app.post("/projects/:name/notifications/:id/test", async (request, reply) => {
18080
18277
  const project = resolveProject(app.db, request.params.name);
18081
- const notification = app.db.select().from(notifications).where(eq19(notifications.id, request.params.id)).get();
18278
+ const notification = app.db.select().from(notifications).where(eq20(notifications.id, request.params.id)).get();
18082
18279
  if (!notification || notification.projectId !== project.id) {
18083
18280
  throw notFound("Notification", request.params.id);
18084
18281
  }
@@ -18131,7 +18328,7 @@ function formatNotification(row) {
18131
18328
 
18132
18329
  // ../api-routes/src/google.ts
18133
18330
  import crypto17 from "crypto";
18134
- import { eq as eq20, and as and13, desc as desc10, sql as sql8, inArray as inArray9 } from "drizzle-orm";
18331
+ import { eq as eq21, and as and14, desc as desc11, sql as sql8, inArray as inArray10 } from "drizzle-orm";
18135
18332
 
18136
18333
  // ../api-routes/src/gbp-summary.ts
18137
18334
  function computeMetricTotals(rows) {
@@ -19857,7 +20054,7 @@ async function googleRoutes(app, opts) {
19857
20054
  if (!projectId) {
19858
20055
  return reply.status(400).send("Stale OAuth state \u2014 restart the connect flow.");
19859
20056
  }
19860
- const project = app.db.select().from(projects).where(eq20(projects.id, projectId)).get();
20057
+ const project = app.db.select().from(projects).where(eq21(projects.id, projectId)).get();
19861
20058
  if (!project) {
19862
20059
  return reply.status(400).send("Project no longer exists. Restart the connect flow.");
19863
20060
  }
@@ -19989,14 +20186,14 @@ async function googleRoutes(app, opts) {
19989
20186
  if (opts.onGscSyncRequested) {
19990
20187
  opts.onGscSyncRequested(runId, project.id, { days, full });
19991
20188
  }
19992
- const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
20189
+ const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
19993
20190
  return run;
19994
20191
  });
19995
20192
  app.get("/projects/:name/google/gsc/performance", async (request) => {
19996
20193
  const project = resolveProject(app.db, request.params.name);
19997
20194
  const { startDate, endDate, query, page, limit, offset } = request.query;
19998
20195
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
19999
- const conditions = [eq20(gscSearchData.projectId, project.id)];
20196
+ const conditions = [eq21(gscSearchData.projectId, project.id)];
20000
20197
  if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
20001
20198
  else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
20002
20199
  if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
@@ -20004,7 +20201,7 @@ async function googleRoutes(app, opts) {
20004
20201
  if (page) conditions.push(sql8`${gscSearchData.page} LIKE ${"%" + escapeLikePattern(page) + "%"} ESCAPE '\\'`);
20005
20202
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
20006
20203
  const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
20007
- const rows = app.db.select().from(gscSearchData).where(and13(...conditions)).orderBy(desc10(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
20204
+ const rows = app.db.select().from(gscSearchData).where(and14(...conditions)).orderBy(desc11(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
20008
20205
  return rows.map((r) => ({
20009
20206
  date: r.date,
20010
20207
  query: r.query,
@@ -20021,7 +20218,7 @@ async function googleRoutes(app, opts) {
20021
20218
  const project = resolveProject(app.db, request.params.name);
20022
20219
  const { startDate, endDate } = request.query;
20023
20220
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
20024
- const conditions = [eq20(gscSearchData.projectId, project.id)];
20221
+ const conditions = [eq21(gscSearchData.projectId, project.id)];
20025
20222
  if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
20026
20223
  else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
20027
20224
  if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
@@ -20029,7 +20226,7 @@ async function googleRoutes(app, opts) {
20029
20226
  date: gscSearchData.date,
20030
20227
  clicks: sql8`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
20031
20228
  impressions: sql8`COALESCE(SUM(${gscSearchData.impressions}), 0)`
20032
- }).from(gscSearchData).where(and13(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
20229
+ }).from(gscSearchData).where(and14(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
20033
20230
  const daily = rows.map((r) => ({
20034
20231
  date: r.date,
20035
20232
  clicks: r.clicks,
@@ -20112,9 +20309,9 @@ async function googleRoutes(app, opts) {
20112
20309
  app.get("/projects/:name/google/gsc/inspections", async (request) => {
20113
20310
  const project = resolveProject(app.db, request.params.name);
20114
20311
  const { url, limit } = request.query;
20115
- const conditions = [eq20(gscUrlInspections.projectId, project.id)];
20116
- if (url) conditions.push(eq20(gscUrlInspections.url, url));
20117
- const rows = app.db.select().from(gscUrlInspections).where(and13(...conditions)).orderBy(desc10(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
20312
+ const conditions = [eq21(gscUrlInspections.projectId, project.id)];
20313
+ if (url) conditions.push(eq21(gscUrlInspections.url, url));
20314
+ const rows = app.db.select().from(gscUrlInspections).where(and14(...conditions)).orderBy(desc11(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
20118
20315
  return rows.map((r) => ({
20119
20316
  id: r.id,
20120
20317
  url: r.url,
@@ -20133,7 +20330,7 @@ async function googleRoutes(app, opts) {
20133
20330
  });
20134
20331
  app.get("/projects/:name/google/gsc/deindexed", async (request) => {
20135
20332
  const project = resolveProject(app.db, request.params.name);
20136
- const allInspections = app.db.select().from(gscUrlInspections).where(eq20(gscUrlInspections.projectId, project.id)).orderBy(desc10(gscUrlInspections.inspectedAt)).all();
20333
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq21(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
20137
20334
  const byUrl = /* @__PURE__ */ new Map();
20138
20335
  for (const row of allInspections) {
20139
20336
  const existing = byUrl.get(row.url);
@@ -20161,7 +20358,7 @@ async function googleRoutes(app, opts) {
20161
20358
  });
20162
20359
  app.get("/projects/:name/google/gsc/coverage", async (request) => {
20163
20360
  const project = resolveProject(app.db, request.params.name);
20164
- const allInspections = app.db.select().from(gscUrlInspections).where(eq20(gscUrlInspections.projectId, project.id)).orderBy(desc10(gscUrlInspections.inspectedAt)).all();
20361
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq21(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
20165
20362
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
20166
20363
  const latestByUrl = /* @__PURE__ */ new Map();
20167
20364
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -20210,7 +20407,7 @@ async function googleRoutes(app, opts) {
20210
20407
  const total = latestByUrl.size;
20211
20408
  const indexed = indexedUrls.length;
20212
20409
  const notIndexed = notIndexedUrls.length;
20213
- const latestSnapshot = app.db.select({ createdAt: gscCoverageSnapshots.createdAt }).from(gscCoverageSnapshots).where(eq20(gscCoverageSnapshots.projectId, project.id)).orderBy(desc10(gscCoverageSnapshots.createdAt)).limit(1).get();
20410
+ const latestSnapshot = app.db.select({ createdAt: gscCoverageSnapshots.createdAt }).from(gscCoverageSnapshots).where(eq21(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.createdAt)).limit(1).get();
20214
20411
  const lastSyncedAt = latestSnapshot?.createdAt ?? null;
20215
20412
  const formatRow = (r) => ({
20216
20413
  id: r.id,
@@ -20261,7 +20458,7 @@ async function googleRoutes(app, opts) {
20261
20458
  const project = resolveProject(app.db, request.params.name);
20262
20459
  const parsed = parseInt(request.query.limit ?? "90", 10);
20263
20460
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
20264
- const rows = app.db.select().from(gscCoverageSnapshots).where(eq20(gscCoverageSnapshots.projectId, project.id)).orderBy(desc10(gscCoverageSnapshots.date)).limit(limit).all();
20461
+ const rows = app.db.select().from(gscCoverageSnapshots).where(eq21(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.date)).limit(limit).all();
20265
20462
  return rows.map((r) => ({
20266
20463
  date: r.date,
20267
20464
  indexed: r.indexed,
@@ -20330,7 +20527,7 @@ async function googleRoutes(app, opts) {
20330
20527
  if (opts.onInspectSitemapRequested) {
20331
20528
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl });
20332
20529
  }
20333
- const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
20530
+ const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
20334
20531
  return { sitemaps, primarySitemapUrl: sitemapUrl, run };
20335
20532
  });
20336
20533
  app.post("/projects/:name/google/gsc/inspect-sitemap", async (request) => {
@@ -20357,7 +20554,7 @@ async function googleRoutes(app, opts) {
20357
20554
  if (opts.onInspectSitemapRequested) {
20358
20555
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl: sitemapUrl ?? void 0 });
20359
20556
  }
20360
- const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
20557
+ const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
20361
20558
  return run;
20362
20559
  });
20363
20560
  app.put("/projects/:name/google/connections/:type/sitemap", async (request) => {
@@ -20404,7 +20601,7 @@ async function googleRoutes(app, opts) {
20404
20601
  const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
20405
20602
  let urlsToNotify = request.body?.urls ?? [];
20406
20603
  if (request.body?.allUnindexed) {
20407
- const allInspections = app.db.select().from(gscUrlInspections).where(eq20(gscUrlInspections.projectId, project.id)).orderBy(desc10(gscUrlInspections.inspectedAt)).all();
20604
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq21(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
20408
20605
  const latestByUrl = /* @__PURE__ */ new Map();
20409
20606
  for (const row of allInspections) {
20410
20607
  if (!latestByUrl.has(row.url)) {
@@ -20515,7 +20712,7 @@ async function googleRoutes(app, opts) {
20515
20712
  };
20516
20713
  }
20517
20714
  function listSelectionResponse(projectId) {
20518
- const rows = app.db.select().from(gbpLocations).where(eq20(gbpLocations.projectId, projectId)).all();
20715
+ const rows = app.db.select().from(gbpLocations).where(eq21(gbpLocations.projectId, projectId)).all();
20519
20716
  const dtos = rows.map(rowToDto2);
20520
20717
  return {
20521
20718
  locations: dtos,
@@ -20524,15 +20721,15 @@ async function googleRoutes(app, opts) {
20524
20721
  };
20525
20722
  }
20526
20723
  function clearGbpProjectData(tx, projectId) {
20527
- tx.delete(gbpDailyMetrics).where(eq20(gbpDailyMetrics.projectId, projectId)).run();
20528
- tx.delete(gbpKeywordImpressions).where(eq20(gbpKeywordImpressions.projectId, projectId)).run();
20529
- tx.delete(gbpKeywordMonthly).where(eq20(gbpKeywordMonthly.projectId, projectId)).run();
20530
- tx.delete(gbpPlaceActions).where(eq20(gbpPlaceActions.projectId, projectId)).run();
20531
- tx.delete(gbpLodgingSnapshots).where(eq20(gbpLodgingSnapshots.projectId, projectId)).run();
20532
- tx.delete(gbpLocations).where(eq20(gbpLocations.projectId, projectId)).run();
20724
+ tx.delete(gbpDailyMetrics).where(eq21(gbpDailyMetrics.projectId, projectId)).run();
20725
+ tx.delete(gbpKeywordImpressions).where(eq21(gbpKeywordImpressions.projectId, projectId)).run();
20726
+ tx.delete(gbpKeywordMonthly).where(eq21(gbpKeywordMonthly.projectId, projectId)).run();
20727
+ tx.delete(gbpPlaceActions).where(eq21(gbpPlaceActions.projectId, projectId)).run();
20728
+ tx.delete(gbpLodgingSnapshots).where(eq21(gbpLodgingSnapshots.projectId, projectId)).run();
20729
+ tx.delete(gbpLocations).where(eq21(gbpLocations.projectId, projectId)).run();
20533
20730
  }
20534
20731
  function currentProjectAccount(projectId) {
20535
- const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq20(gbpLocations.projectId, projectId)).limit(1).get();
20732
+ const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq21(gbpLocations.projectId, projectId)).limit(1).get();
20536
20733
  return row?.accountName ?? null;
20537
20734
  }
20538
20735
  app.post("/projects/:name/gbp/locations/discover", async (request) => {
@@ -20602,7 +20799,7 @@ async function googleRoutes(app, opts) {
20602
20799
  app.db.transaction((tx) => {
20603
20800
  if (switching) clearGbpProjectData(tx, project.id);
20604
20801
  for (const remote of remoteLocations) {
20605
- const existing = tx.select().from(gbpLocations).where(and13(eq20(gbpLocations.projectId, project.id), eq20(gbpLocations.locationName, remote.name))).get();
20802
+ const existing = tx.select().from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), eq21(gbpLocations.locationName, remote.name))).get();
20606
20803
  if (existing) {
20607
20804
  tx.update(gbpLocations).set({
20608
20805
  accountName,
@@ -20613,7 +20810,7 @@ async function googleRoutes(app, opts) {
20613
20810
  placeId: remote.metadata?.placeId ?? null,
20614
20811
  mapsUri: remote.metadata?.mapsUri ?? null,
20615
20812
  updatedAt: now
20616
- }).where(eq20(gbpLocations.id, existing.id)).run();
20813
+ }).where(eq21(gbpLocations.id, existing.id)).run();
20617
20814
  } else {
20618
20815
  tx.insert(gbpLocations).values({
20619
20816
  id: crypto17.randomUUID(),
@@ -20696,11 +20893,11 @@ async function googleRoutes(app, opts) {
20696
20893
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid selection request");
20697
20894
  }
20698
20895
  const { selected } = parsed.data;
20699
- const existing = app.db.select().from(gbpLocations).where(and13(eq20(gbpLocations.projectId, project.id), eq20(gbpLocations.locationName, locationName))).get();
20896
+ const existing = app.db.select().from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), eq21(gbpLocations.locationName, locationName))).get();
20700
20897
  if (!existing) throw notFound("GBP location", locationName);
20701
20898
  const now = (/* @__PURE__ */ new Date()).toISOString();
20702
20899
  app.db.transaction((tx) => {
20703
- tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq20(gbpLocations.id, existing.id)).run();
20900
+ tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq21(gbpLocations.id, existing.id)).run();
20704
20901
  writeAuditLog(tx, {
20705
20902
  projectId: project.id,
20706
20903
  actor: "api",
@@ -20709,7 +20906,7 @@ async function googleRoutes(app, opts) {
20709
20906
  entityId: locationName
20710
20907
  });
20711
20908
  });
20712
- const refreshed = app.db.select().from(gbpLocations).where(eq20(gbpLocations.id, existing.id)).get();
20909
+ const refreshed = app.db.select().from(gbpLocations).where(eq21(gbpLocations.id, existing.id)).get();
20713
20910
  return rowToDto2(refreshed);
20714
20911
  });
20715
20912
  app.delete("/projects/:name/gbp/connection", async (request, reply) => {
@@ -20753,10 +20950,10 @@ async function googleRoutes(app, opts) {
20753
20950
  });
20754
20951
  app.get("/projects/:name/gbp/metrics", async (request) => {
20755
20952
  const project = resolveProject(app.db, request.params.name);
20756
- const conditions = [eq20(gbpDailyMetrics.projectId, project.id)];
20757
- if (request.query.locationName) conditions.push(eq20(gbpDailyMetrics.locationName, request.query.locationName));
20758
- if (request.query.metric) conditions.push(eq20(gbpDailyMetrics.metric, request.query.metric));
20759
- const rows = app.db.select().from(gbpDailyMetrics).where(and13(...conditions)).orderBy(desc10(gbpDailyMetrics.date)).all();
20953
+ const conditions = [eq21(gbpDailyMetrics.projectId, project.id)];
20954
+ if (request.query.locationName) conditions.push(eq21(gbpDailyMetrics.locationName, request.query.locationName));
20955
+ if (request.query.metric) conditions.push(eq21(gbpDailyMetrics.metric, request.query.metric));
20956
+ const rows = app.db.select().from(gbpDailyMetrics).where(and14(...conditions)).orderBy(desc11(gbpDailyMetrics.date)).all();
20760
20957
  return {
20761
20958
  metrics: rows.map((r) => ({ locationName: r.locationName, date: r.date, metric: r.metric, value: r.value })),
20762
20959
  total: rows.length
@@ -20764,9 +20961,9 @@ async function googleRoutes(app, opts) {
20764
20961
  });
20765
20962
  app.get("/projects/:name/gbp/keywords", async (request) => {
20766
20963
  const project = resolveProject(app.db, request.params.name);
20767
- const conditions = [eq20(gbpKeywordImpressions.projectId, project.id)];
20768
- if (request.query.locationName) conditions.push(eq20(gbpKeywordImpressions.locationName, request.query.locationName));
20769
- const rows = app.db.select().from(gbpKeywordImpressions).where(and13(...conditions)).all();
20964
+ const conditions = [eq21(gbpKeywordImpressions.projectId, project.id)];
20965
+ if (request.query.locationName) conditions.push(eq21(gbpKeywordImpressions.locationName, request.query.locationName));
20966
+ const rows = app.db.select().from(gbpKeywordImpressions).where(and14(...conditions)).all();
20770
20967
  rows.sort((a, b) => (b.valueCount ?? -1) - (a.valueCount ?? -1));
20771
20968
  const thresholded = rows.filter((r) => r.valueThreshold !== null).length;
20772
20969
  return {
@@ -20784,9 +20981,9 @@ async function googleRoutes(app, opts) {
20784
20981
  });
20785
20982
  app.get("/projects/:name/gbp/place-actions", async (request) => {
20786
20983
  const project = resolveProject(app.db, request.params.name);
20787
- const conditions = [eq20(gbpPlaceActions.projectId, project.id)];
20788
- if (request.query.locationName) conditions.push(eq20(gbpPlaceActions.locationName, request.query.locationName));
20789
- const rows = app.db.select().from(gbpPlaceActions).where(and13(...conditions)).all();
20984
+ const conditions = [eq21(gbpPlaceActions.projectId, project.id)];
20985
+ if (request.query.locationName) conditions.push(eq21(gbpPlaceActions.locationName, request.query.locationName));
20986
+ const rows = app.db.select().from(gbpPlaceActions).where(and14(...conditions)).all();
20790
20987
  return {
20791
20988
  placeActions: rows.map((r) => ({
20792
20989
  locationName: r.locationName,
@@ -20801,9 +20998,9 @@ async function googleRoutes(app, opts) {
20801
20998
  });
20802
20999
  app.get("/projects/:name/gbp/lodging", async (request) => {
20803
21000
  const project = resolveProject(app.db, request.params.name);
20804
- const conditions = [eq20(gbpLodgingSnapshots.projectId, project.id)];
20805
- if (request.query.locationName) conditions.push(eq20(gbpLodgingSnapshots.locationName, request.query.locationName));
20806
- const rows = app.db.select().from(gbpLodgingSnapshots).where(and13(...conditions)).orderBy(desc10(gbpLodgingSnapshots.syncedAt)).all();
21001
+ const conditions = [eq21(gbpLodgingSnapshots.projectId, project.id)];
21002
+ if (request.query.locationName) conditions.push(eq21(gbpLodgingSnapshots.locationName, request.query.locationName));
21003
+ const rows = app.db.select().from(gbpLodgingSnapshots).where(and14(...conditions)).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
20807
21004
  const latestByLocation = /* @__PURE__ */ new Map();
20808
21005
  for (const row of rows) {
20809
21006
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -20818,9 +21015,9 @@ async function googleRoutes(app, opts) {
20818
21015
  });
20819
21016
  app.get("/projects/:name/gbp/places", async (request) => {
20820
21017
  const project = resolveProject(app.db, request.params.name);
20821
- const conditions = [eq20(gbpPlaceDetails.projectId, project.id)];
20822
- if (request.query.locationName) conditions.push(eq20(gbpPlaceDetails.locationName, request.query.locationName));
20823
- const rows = app.db.select().from(gbpPlaceDetails).where(and13(...conditions)).orderBy(desc10(gbpPlaceDetails.syncedAt)).all();
21018
+ const conditions = [eq21(gbpPlaceDetails.projectId, project.id)];
21019
+ if (request.query.locationName) conditions.push(eq21(gbpPlaceDetails.locationName, request.query.locationName));
21020
+ const rows = app.db.select().from(gbpPlaceDetails).where(and14(...conditions)).orderBy(desc11(gbpPlaceDetails.syncedAt)).all();
20824
21021
  const latestByLocation = /* @__PURE__ */ new Map();
20825
21022
  for (const row of rows) {
20826
21023
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -20839,7 +21036,7 @@ async function googleRoutes(app, opts) {
20839
21036
  app.get("/projects/:name/gbp/summary", async (request) => {
20840
21037
  const project = resolveProject(app.db, request.params.name);
20841
21038
  const locationName = request.query.locationName ?? null;
20842
- const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and13(eq20(gbpLocations.projectId, project.id), eq20(gbpLocations.selected, true))).all().map((r) => r.n);
21039
+ const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), eq21(gbpLocations.selected, true))).all().map((r) => r.n);
20843
21040
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
20844
21041
  if (locationNames.length === 0) {
20845
21042
  return buildGbpSummary({
@@ -20852,10 +21049,10 @@ async function googleRoutes(app, opts) {
20852
21049
  lodging: []
20853
21050
  });
20854
21051
  }
20855
- const metricRows = app.db.select().from(gbpDailyMetrics).where(and13(eq20(gbpDailyMetrics.projectId, project.id), inArray9(gbpDailyMetrics.locationName, locationNames))).all();
20856
- const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and13(eq20(gbpKeywordImpressions.projectId, project.id), inArray9(gbpKeywordImpressions.locationName, locationNames))).all();
20857
- const placeActionRows = app.db.select().from(gbpPlaceActions).where(and13(eq20(gbpPlaceActions.projectId, project.id), inArray9(gbpPlaceActions.locationName, locationNames))).all();
20858
- const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and13(eq20(gbpLodgingSnapshots.projectId, project.id), inArray9(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc10(gbpLodgingSnapshots.syncedAt)).all();
21052
+ const metricRows = app.db.select().from(gbpDailyMetrics).where(and14(eq21(gbpDailyMetrics.projectId, project.id), inArray10(gbpDailyMetrics.locationName, locationNames))).all();
21053
+ const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and14(eq21(gbpKeywordImpressions.projectId, project.id), inArray10(gbpKeywordImpressions.locationName, locationNames))).all();
21054
+ const placeActionRows = app.db.select().from(gbpPlaceActions).where(and14(eq21(gbpPlaceActions.projectId, project.id), inArray10(gbpPlaceActions.locationName, locationNames))).all();
21055
+ const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and14(eq21(gbpLodgingSnapshots.projectId, project.id), inArray10(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
20859
21056
  const latestLodgingByLocation = /* @__PURE__ */ new Map();
20860
21057
  for (const row of lodgingRows) {
20861
21058
  if (!latestLodgingByLocation.has(row.locationName)) {
@@ -20876,7 +21073,7 @@ async function googleRoutes(app, opts) {
20876
21073
 
20877
21074
  // ../api-routes/src/ads.ts
20878
21075
  import crypto18 from "crypto";
20879
- import { eq as eq21, and as and14, asc as asc2, gte as gte3, lte as lte2, inArray as inArray10 } from "drizzle-orm";
21076
+ import { eq as eq22, and as and15, asc as asc2, gte as gte3, lte as lte2, inArray as inArray11 } from "drizzle-orm";
20880
21077
  function statusDto(row) {
20881
21078
  if (!row) return { connected: false };
20882
21079
  return {
@@ -20925,7 +21122,7 @@ async function adsRoutes(app, opts) {
20925
21122
  createdAt: existingCfg?.createdAt ?? now,
20926
21123
  updatedAt: now
20927
21124
  });
20928
- const existingRow = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21125
+ const existingRow = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20929
21126
  app.db.transaction((tx) => {
20930
21127
  if (existingRow) {
20931
21128
  tx.update(adsConnections).set({
@@ -20935,7 +21132,7 @@ async function adsRoutes(app, opts) {
20935
21132
  timezone: account.timezone,
20936
21133
  status: account.status,
20937
21134
  updatedAt: now
20938
- }).where(eq21(adsConnections.id, existingRow.id)).run();
21135
+ }).where(eq22(adsConnections.id, existingRow.id)).run();
20939
21136
  } else {
20940
21137
  tx.insert(adsConnections).values({
20941
21138
  id: crypto18.randomUUID(),
@@ -20957,16 +21154,16 @@ async function adsRoutes(app, opts) {
20957
21154
  entityId: account.id
20958
21155
  }));
20959
21156
  });
20960
- const row = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21157
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20961
21158
  return statusDto(row);
20962
21159
  }
20963
21160
  );
20964
21161
  app.delete("/projects/:name/ads/connection", async (request) => {
20965
21162
  const project = resolveProject(app.db, request.params.name);
20966
- const row = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21163
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20967
21164
  if (row) {
20968
21165
  app.db.transaction((tx) => {
20969
- tx.delete(adsConnections).where(eq21(adsConnections.id, row.id)).run();
21166
+ tx.delete(adsConnections).where(eq22(adsConnections.id, row.id)).run();
20970
21167
  writeAuditLog(tx, auditFromRequest(request, {
20971
21168
  projectId: project.id,
20972
21169
  actor: "api",
@@ -20982,19 +21179,19 @@ async function adsRoutes(app, opts) {
20982
21179
  });
20983
21180
  app.get("/projects/:name/ads/status", async (request) => {
20984
21181
  const project = resolveProject(app.db, request.params.name);
20985
- const row = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21182
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20986
21183
  return statusDto(row);
20987
21184
  });
20988
21185
  app.post("/projects/:name/ads/sync", async (request) => {
20989
21186
  const project = resolveProject(app.db, request.params.name);
20990
- const row = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21187
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20991
21188
  if (!row) {
20992
21189
  throw validationError('No ads connection for this project. Run "canonry ads connect" first.');
20993
21190
  }
20994
- const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and14(
20995
- eq21(runs.projectId, project.id),
20996
- eq21(runs.kind, RunKinds["ads-sync"]),
20997
- inArray10(runs.status, [RunStatuses.queued, RunStatuses.running])
21191
+ const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and15(
21192
+ eq22(runs.projectId, project.id),
21193
+ eq22(runs.kind, RunKinds["ads-sync"]),
21194
+ inArray11(runs.status, [RunStatuses.queued, RunStatuses.running])
20998
21195
  )).get();
20999
21196
  if (inFlight) {
21000
21197
  const existing = { runId: inFlight.id, status: inFlight.status };
@@ -21015,9 +21212,9 @@ async function adsRoutes(app, opts) {
21015
21212
  });
21016
21213
  app.get("/projects/:name/ads/campaigns", async (request) => {
21017
21214
  const project = resolveProject(app.db, request.params.name);
21018
- const campaignRows = app.db.select().from(adsCampaigns).where(eq21(adsCampaigns.projectId, project.id)).all();
21019
- const groupRows = app.db.select().from(adsAdGroups).where(eq21(adsAdGroups.projectId, project.id)).all();
21020
- const adRows = app.db.select().from(adsAds).where(eq21(adsAds.projectId, project.id)).all();
21215
+ const campaignRows = app.db.select().from(adsCampaigns).where(eq22(adsCampaigns.projectId, project.id)).all();
21216
+ const groupRows = app.db.select().from(adsAdGroups).where(eq22(adsAdGroups.projectId, project.id)).all();
21217
+ const adRows = app.db.select().from(adsAds).where(eq22(adsAds.projectId, project.id)).all();
21021
21218
  const adsByGroup = /* @__PURE__ */ new Map();
21022
21219
  for (const ad of adRows) {
21023
21220
  const dto = {
@@ -21071,12 +21268,12 @@ async function adsRoutes(app, opts) {
21071
21268
  }
21072
21269
  parsedLevel = result.data;
21073
21270
  }
21074
- const conditions = [eq21(adsInsightsDaily.projectId, project.id)];
21075
- if (parsedLevel) conditions.push(eq21(adsInsightsDaily.level, parsedLevel));
21076
- if (entityId) conditions.push(eq21(adsInsightsDaily.entityId, entityId));
21271
+ const conditions = [eq22(adsInsightsDaily.projectId, project.id)];
21272
+ if (parsedLevel) conditions.push(eq22(adsInsightsDaily.level, parsedLevel));
21273
+ if (entityId) conditions.push(eq22(adsInsightsDaily.entityId, entityId));
21077
21274
  if (from) conditions.push(gte3(adsInsightsDaily.date, from));
21078
21275
  if (to) conditions.push(lte2(adsInsightsDaily.date, to));
21079
- const rows = app.db.select().from(adsInsightsDaily).where(and14(...conditions)).orderBy(asc2(adsInsightsDaily.date)).all();
21276
+ const rows = app.db.select().from(adsInsightsDaily).where(and15(...conditions)).orderBy(asc2(adsInsightsDaily.date)).all();
21080
21277
  const dtoRows = rows.map((row) => ({
21081
21278
  level: row.level,
21082
21279
  entityId: row.entityId,
@@ -21087,19 +21284,19 @@ async function adsRoutes(app, opts) {
21087
21284
  ctr: adsCtr(row.clicks, row.impressions),
21088
21285
  cpcMicros: adsCpcMicros(row.spendMicros, row.clicks)
21089
21286
  }));
21090
- const conn = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21287
+ const conn = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21091
21288
  const response = { rows: dtoRows, currencyCode: conn?.currencyCode ?? null };
21092
21289
  return response;
21093
21290
  });
21094
21291
  app.get("/projects/:name/ads/summary", async (request) => {
21095
21292
  const project = resolveProject(app.db, request.params.name);
21096
- const row = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21097
- const campaignCount = app.db.select().from(adsCampaigns).where(eq21(adsCampaigns.projectId, project.id)).all().length;
21098
- const adGroupCount = app.db.select().from(adsAdGroups).where(eq21(adsAdGroups.projectId, project.id)).all().length;
21099
- const adCount = app.db.select().from(adsAds).where(eq21(adsAds.projectId, project.id)).all().length;
21100
- const campaignInsights = app.db.select().from(adsInsightsDaily).where(and14(
21101
- eq21(adsInsightsDaily.projectId, project.id),
21102
- eq21(adsInsightsDaily.level, "campaign")
21293
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21294
+ const campaignCount = app.db.select().from(adsCampaigns).where(eq22(adsCampaigns.projectId, project.id)).all().length;
21295
+ const adGroupCount = app.db.select().from(adsAdGroups).where(eq22(adsAdGroups.projectId, project.id)).all().length;
21296
+ const adCount = app.db.select().from(adsAds).where(eq22(adsAds.projectId, project.id)).all().length;
21297
+ const campaignInsights = app.db.select().from(adsInsightsDaily).where(and15(
21298
+ eq22(adsInsightsDaily.projectId, project.id),
21299
+ eq22(adsInsightsDaily.level, "campaign")
21103
21300
  )).all();
21104
21301
  let impressions = 0;
21105
21302
  let clicks = 0;
@@ -21136,7 +21333,7 @@ async function adsRoutes(app, opts) {
21136
21333
 
21137
21334
  // ../api-routes/src/bing.ts
21138
21335
  import crypto19 from "crypto";
21139
- import { eq as eq22, and as and15, desc as desc11 } from "drizzle-orm";
21336
+ import { eq as eq23, and as and16, desc as desc12 } from "drizzle-orm";
21140
21337
 
21141
21338
  // ../integration-bing/src/constants.ts
21142
21339
  var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
@@ -21510,7 +21707,7 @@ async function bingRoutes(app, opts) {
21510
21707
  const store = requireConnectionStore();
21511
21708
  const project = resolveProject(app.db, request.params.name);
21512
21709
  requireConnection(store, project.canonicalDomain);
21513
- const allInspections = app.db.select().from(bingUrlInspections).where(eq22(bingUrlInspections.projectId, project.id)).orderBy(desc11(bingUrlInspections.inspectedAt)).all();
21710
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq23(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
21514
21711
  const latestByUrl = /* @__PURE__ */ new Map();
21515
21712
  const definitiveByUrl = /* @__PURE__ */ new Map();
21516
21713
  for (const row of allInspections) {
@@ -21599,7 +21796,7 @@ async function bingRoutes(app, opts) {
21599
21796
  const project = resolveProject(app.db, request.params.name);
21600
21797
  const parsed = parseInt(request.query.limit ?? "90", 10);
21601
21798
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
21602
- const rows = app.db.select().from(bingCoverageSnapshots).where(eq22(bingCoverageSnapshots.projectId, project.id)).orderBy(desc11(bingCoverageSnapshots.date)).limit(limit).all();
21799
+ const rows = app.db.select().from(bingCoverageSnapshots).where(eq23(bingCoverageSnapshots.projectId, project.id)).orderBy(desc12(bingCoverageSnapshots.date)).limit(limit).all();
21603
21800
  return rows.map((r) => ({
21604
21801
  date: r.date,
21605
21802
  indexed: r.indexed,
@@ -21611,8 +21808,8 @@ async function bingRoutes(app, opts) {
21611
21808
  requireConnectionStore();
21612
21809
  const project = resolveProject(app.db, request.params.name);
21613
21810
  const { url, limit } = request.query;
21614
- const whereClause = url ? and15(eq22(bingUrlInspections.projectId, project.id), eq22(bingUrlInspections.url, url)) : eq22(bingUrlInspections.projectId, project.id);
21615
- const filtered = app.db.select().from(bingUrlInspections).where(whereClause).orderBy(desc11(bingUrlInspections.inspectedAt)).limit(Math.max(1, Math.min(parseInt(limit ?? "100", 10) || 100, 1e3))).all();
21811
+ const whereClause = url ? and16(eq23(bingUrlInspections.projectId, project.id), eq23(bingUrlInspections.url, url)) : eq23(bingUrlInspections.projectId, project.id);
21812
+ const filtered = app.db.select().from(bingUrlInspections).where(whereClause).orderBy(desc12(bingUrlInspections.inspectedAt)).limit(Math.max(1, Math.min(parseInt(limit ?? "100", 10) || 100, 1e3))).all();
21616
21813
  return filtered.map((r) => ({
21617
21814
  id: r.id,
21618
21815
  url: r.url,
@@ -21701,7 +21898,7 @@ async function bingRoutes(app, opts) {
21701
21898
  anchorCount: result.AnchorCount ?? null,
21702
21899
  discoveryDate
21703
21900
  }).run();
21704
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq22(runs.id, runId)).run();
21901
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq23(runs.id, runId)).run();
21705
21902
  return {
21706
21903
  id,
21707
21904
  url,
@@ -21717,7 +21914,7 @@ async function bingRoutes(app, opts) {
21717
21914
  } catch (e) {
21718
21915
  const msg = e instanceof Error ? e.message : String(e);
21719
21916
  bingLog("error", "inspect-url.failed", { domain: project.canonicalDomain, url, error: msg });
21720
- app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq22(runs.id, runId)).run();
21917
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq23(runs.id, runId)).run();
21721
21918
  throw e;
21722
21919
  }
21723
21920
  });
@@ -21744,7 +21941,7 @@ async function bingRoutes(app, opts) {
21744
21941
  } else {
21745
21942
  bingLog("warn", "inspect-sitemap.no-callback", { domain: project.canonicalDomain, runId });
21746
21943
  }
21747
- const run = app.db.select().from(runs).where(eq22(runs.id, runId)).get();
21944
+ const run = app.db.select().from(runs).where(eq23(runs.id, runId)).get();
21748
21945
  return run;
21749
21946
  });
21750
21947
  app.post("/projects/:name/bing/request-indexing", async (request) => {
@@ -21756,7 +21953,7 @@ async function bingRoutes(app, opts) {
21756
21953
  }
21757
21954
  let urlsToSubmit = request.body?.urls ?? [];
21758
21955
  if (request.body?.allUnindexed) {
21759
- const allInspections = app.db.select().from(bingUrlInspections).where(eq22(bingUrlInspections.projectId, project.id)).orderBy(desc11(bingUrlInspections.inspectedAt)).all();
21956
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq23(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
21760
21957
  const latestByUrl = /* @__PURE__ */ new Map();
21761
21958
  for (const row of allInspections) {
21762
21959
  if (!latestByUrl.has(row.url)) {
@@ -21843,14 +22040,14 @@ async function bingRoutes(app, opts) {
21843
22040
  import fs from "fs";
21844
22041
  import path from "path";
21845
22042
  import os from "os";
21846
- import { eq as eq23, and as and16 } from "drizzle-orm";
22043
+ import { eq as eq24, and as and17 } from "drizzle-orm";
21847
22044
  function getScreenshotDir() {
21848
22045
  return path.join(os.homedir(), ".canonry", "screenshots");
21849
22046
  }
21850
22047
  async function cdpRoutes(app, opts) {
21851
22048
  app.get("/screenshots/:snapshotId", async (request, reply) => {
21852
22049
  const { snapshotId } = request.params;
21853
- const snapshot = app.db.select({ screenshotPath: querySnapshots.screenshotPath }).from(querySnapshots).where(eq23(querySnapshots.id, snapshotId)).get();
22050
+ const snapshot = app.db.select({ screenshotPath: querySnapshots.screenshotPath }).from(querySnapshots).where(eq24(querySnapshots.id, snapshotId)).get();
21854
22051
  if (!snapshot?.screenshotPath) {
21855
22052
  const err = notFound("Screenshot", snapshotId);
21856
22053
  return reply.code(err.statusCode).send(err.toJSON());
@@ -21917,7 +22114,7 @@ async function cdpRoutes(app, opts) {
21917
22114
  async (request, reply) => {
21918
22115
  const project = resolveProject(app.db, request.params.name);
21919
22116
  const { runId } = request.params;
21920
- const run = app.db.select().from(runs).where(and16(eq23(runs.id, runId), eq23(runs.projectId, project.id))).get();
22117
+ const run = app.db.select().from(runs).where(and17(eq24(runs.id, runId), eq24(runs.projectId, project.id))).get();
21921
22118
  if (!run) {
21922
22119
  const err = notFound("Run", runId);
21923
22120
  return reply.code(err.statusCode).send(err.toJSON());
@@ -21930,8 +22127,8 @@ async function cdpRoutes(app, opts) {
21930
22127
  citedDomains: querySnapshots.citedDomains,
21931
22128
  screenshotPath: querySnapshots.screenshotPath,
21932
22129
  rawResponse: querySnapshots.rawResponse
21933
- }).from(querySnapshots).where(eq23(querySnapshots.runId, runId)).all());
21934
- const queryRows = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq23(queries.projectId, project.id)).all();
22130
+ }).from(querySnapshots).where(eq24(querySnapshots.runId, runId)).all());
22131
+ const queryRows = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq24(queries.projectId, project.id)).all();
21935
22132
  const queryMap = new Map(queryRows.map((q) => [q.id, q.query]));
21936
22133
  const byQuery = /* @__PURE__ */ new Map();
21937
22134
  for (const snap of snapshots) {
@@ -22014,7 +22211,7 @@ async function cdpRoutes(app, opts) {
22014
22211
 
22015
22212
  // ../api-routes/src/ga.ts
22016
22213
  import crypto20 from "crypto";
22017
- import { eq as eq24, desc as desc12, and as and17, sql as sql9 } from "drizzle-orm";
22214
+ import { eq as eq25, desc as desc13, and as and18, sql as sql9 } from "drizzle-orm";
22018
22215
  function gaLog(level, action, ctx) {
22019
22216
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
22020
22217
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -22221,10 +22418,10 @@ async function ga4Routes(app, opts) {
22221
22418
  if (!saConn && !oauthConn) {
22222
22419
  throw notFound("GA4 connection", project.name);
22223
22420
  }
22224
- app.db.delete(gaTrafficSnapshots).where(eq24(gaTrafficSnapshots.projectId, project.id)).run();
22225
- app.db.delete(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).run();
22226
- app.db.delete(gaAiReferrals).where(eq24(gaAiReferrals.projectId, project.id)).run();
22227
- app.db.delete(gaSocialReferrals).where(eq24(gaSocialReferrals.projectId, project.id)).run();
22421
+ app.db.delete(gaTrafficSnapshots).where(eq25(gaTrafficSnapshots.projectId, project.id)).run();
22422
+ app.db.delete(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).run();
22423
+ app.db.delete(gaAiReferrals).where(eq25(gaAiReferrals.projectId, project.id)).run();
22424
+ app.db.delete(gaSocialReferrals).where(eq25(gaSocialReferrals.projectId, project.id)).run();
22228
22425
  const propertyId = saConn?.propertyId ?? oauthConn?.propertyId ?? null;
22229
22426
  opts.ga4CredentialStore?.deleteConnection(project.name);
22230
22427
  opts.googleConnectionStore?.deleteConnection(project.canonicalDomain, "ga4");
@@ -22245,7 +22442,7 @@ async function ga4Routes(app, opts) {
22245
22442
  if (!connected) {
22246
22443
  return { connected: false, propertyId: null, clientEmail: null, authMethod: null, lastSyncedAt: null };
22247
22444
  }
22248
- const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).orderBy(desc12(gaTrafficSummaries.syncedAt)).limit(1).get();
22445
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
22249
22446
  return {
22250
22447
  connected: true,
22251
22448
  propertyId: saConn?.propertyId ?? oauthConn?.propertyId ?? null,
@@ -22309,8 +22506,8 @@ async function ga4Routes(app, opts) {
22309
22506
  app.db.transaction((tx) => {
22310
22507
  if (syncTraffic) {
22311
22508
  tx.delete(gaTrafficSnapshots).where(
22312
- and17(
22313
- eq24(gaTrafficSnapshots.projectId, project.id),
22509
+ and18(
22510
+ eq25(gaTrafficSnapshots.projectId, project.id),
22314
22511
  sql9`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
22315
22512
  sql9`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
22316
22513
  )
@@ -22333,8 +22530,8 @@ async function ga4Routes(app, opts) {
22333
22530
  }
22334
22531
  if (syncAi) {
22335
22532
  tx.delete(gaAiReferrals).where(
22336
- and17(
22337
- eq24(gaAiReferrals.projectId, project.id),
22533
+ and18(
22534
+ eq25(gaAiReferrals.projectId, project.id),
22338
22535
  sql9`${gaAiReferrals.date} >= ${summary.periodStart}`,
22339
22536
  sql9`${gaAiReferrals.date} <= ${summary.periodEnd}`
22340
22537
  )
@@ -22359,8 +22556,8 @@ async function ga4Routes(app, opts) {
22359
22556
  }
22360
22557
  if (syncSocial) {
22361
22558
  tx.delete(gaSocialReferrals).where(
22362
- and17(
22363
- eq24(gaSocialReferrals.projectId, project.id),
22559
+ and18(
22560
+ eq25(gaSocialReferrals.projectId, project.id),
22364
22561
  sql9`${gaSocialReferrals.date} >= ${summary.periodStart}`,
22365
22562
  sql9`${gaSocialReferrals.date} <= ${summary.periodEnd}`
22366
22563
  )
@@ -22381,7 +22578,7 @@ async function ga4Routes(app, opts) {
22381
22578
  }
22382
22579
  }
22383
22580
  if (syncSummary) {
22384
- tx.delete(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).run();
22581
+ tx.delete(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).run();
22385
22582
  tx.insert(gaTrafficSummaries).values({
22386
22583
  id: crypto20.randomUUID(),
22387
22584
  projectId: project.id,
@@ -22393,7 +22590,7 @@ async function ga4Routes(app, opts) {
22393
22590
  syncedAt: now,
22394
22591
  syncRunId: runId
22395
22592
  }).run();
22396
- tx.delete(gaTrafficWindowSummaries).where(eq24(gaTrafficWindowSummaries.projectId, project.id)).run();
22593
+ tx.delete(gaTrafficWindowSummaries).where(eq25(gaTrafficWindowSummaries.projectId, project.id)).run();
22397
22594
  for (const ws of windowSummaries) {
22398
22595
  tx.insert(gaTrafficWindowSummaries).values({
22399
22596
  id: crypto20.randomUUID(),
@@ -22411,7 +22608,7 @@ async function ga4Routes(app, opts) {
22411
22608
  }
22412
22609
  }
22413
22610
  });
22414
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq24(runs.id, runId)).run();
22611
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq25(runs.id, runId)).run();
22415
22612
  const syncedComponents = only ? [
22416
22613
  ...syncTraffic ? ["traffic"] : [],
22417
22614
  ...syncSummary ? ["summary"] : [],
@@ -22440,7 +22637,7 @@ async function ga4Routes(app, opts) {
22440
22637
  } catch (e) {
22441
22638
  const msg = e instanceof Error ? e.message : String(e);
22442
22639
  gaLog("error", "sync.fetch-failed", { projectId: project.id, runId, error: msg });
22443
- app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq24(runs.id, runId)).run();
22640
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq25(runs.id, runId)).run();
22444
22641
  throw e;
22445
22642
  }
22446
22643
  });
@@ -22451,11 +22648,11 @@ async function ga4Routes(app, opts) {
22451
22648
  const window = parseWindow(request.query.window);
22452
22649
  const cutoff = windowCutoff(window);
22453
22650
  const cutoffDate = cutoff?.slice(0, 10) ?? null;
22454
- const snapshotConditions = [eq24(gaTrafficSnapshots.projectId, project.id)];
22651
+ const snapshotConditions = [eq25(gaTrafficSnapshots.projectId, project.id)];
22455
22652
  if (cutoffDate) snapshotConditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
22456
- const aiConditions = [eq24(gaAiReferrals.projectId, project.id)];
22653
+ const aiConditions = [eq25(gaAiReferrals.projectId, project.id)];
22457
22654
  if (cutoffDate) aiConditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
22458
- const socialConditions = [eq24(gaSocialReferrals.projectId, project.id)];
22655
+ const socialConditions = [eq25(gaSocialReferrals.projectId, project.id)];
22459
22656
  if (cutoffDate) socialConditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
22460
22657
  const windowSummaryRow = cutoffDate ? app.db.select({
22461
22658
  totalSessions: gaTrafficWindowSummaries.totalSessions,
@@ -22463,42 +22660,42 @@ async function ga4Routes(app, opts) {
22463
22660
  totalDirectSessions: gaTrafficWindowSummaries.totalDirectSessions,
22464
22661
  totalUsers: gaTrafficWindowSummaries.totalUsers
22465
22662
  }).from(gaTrafficWindowSummaries).where(
22466
- and17(
22467
- eq24(gaTrafficWindowSummaries.projectId, project.id),
22468
- eq24(gaTrafficWindowSummaries.windowKey, window)
22663
+ and18(
22664
+ eq25(gaTrafficWindowSummaries.projectId, project.id),
22665
+ eq25(gaTrafficWindowSummaries.windowKey, window)
22469
22666
  )
22470
22667
  ).get() : null;
22471
22668
  const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
22472
22669
  totalSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
22473
22670
  totalOrganicSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
22474
22671
  totalUsers: sql9`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
22475
- }).from(gaTrafficSnapshots).where(and17(...snapshotConditions)).get() : null;
22672
+ }).from(gaTrafficSnapshots).where(and18(...snapshotConditions)).get() : null;
22476
22673
  const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
22477
22674
  totalSessions: gaTrafficSummaries.totalSessions,
22478
22675
  totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
22479
22676
  totalUsers: gaTrafficSummaries.totalUsers
22480
- }).from(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).get();
22677
+ }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).get();
22481
22678
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
22482
22679
  totalDirectSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
22483
- }).from(gaTrafficSnapshots).where(and17(...snapshotConditions)).get();
22680
+ }).from(gaTrafficSnapshots).where(and18(...snapshotConditions)).get();
22484
22681
  const summaryMeta = app.db.select({
22485
22682
  periodStart: gaTrafficSummaries.periodStart,
22486
22683
  periodEnd: gaTrafficSummaries.periodEnd
22487
- }).from(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).get();
22684
+ }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).get();
22488
22685
  const rows = app.db.select({
22489
22686
  landingPage: sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
22490
22687
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
22491
22688
  organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
22492
22689
  directSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
22493
22690
  users: sql9`SUM(${gaTrafficSnapshots.users})`
22494
- }).from(gaTrafficSnapshots).where(and17(...snapshotConditions)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
22691
+ }).from(gaTrafficSnapshots).where(and18(...snapshotConditions)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
22495
22692
  const aiReferralRows = app.db.select({
22496
22693
  source: gaAiReferrals.source,
22497
22694
  medium: gaAiReferrals.medium,
22498
22695
  sourceDimension: gaAiReferrals.sourceDimension,
22499
22696
  sessions: sql9`SUM(${gaAiReferrals.sessions})`,
22500
22697
  users: sql9`SUM(${gaAiReferrals.users})`
22501
- }).from(gaAiReferrals).where(and17(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
22698
+ }).from(gaAiReferrals).where(and18(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
22502
22699
  const aiReferralLandingPageRows = app.db.select({
22503
22700
  source: gaAiReferrals.source,
22504
22701
  medium: gaAiReferrals.medium,
@@ -22506,7 +22703,7 @@ async function ga4Routes(app, opts) {
22506
22703
  landingPage: sql9`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
22507
22704
  sessions: sql9`SUM(${gaAiReferrals.sessions})`,
22508
22705
  users: sql9`SUM(${gaAiReferrals.users})`
22509
- }).from(gaAiReferrals).where(and17(...aiConditions)).groupBy(
22706
+ }).from(gaAiReferrals).where(and18(...aiConditions)).groupBy(
22510
22707
  gaAiReferrals.source,
22511
22708
  gaAiReferrals.medium,
22512
22709
  gaAiReferrals.sourceDimension,
@@ -22543,7 +22740,7 @@ async function ga4Routes(app, opts) {
22543
22740
  channelGroup: gaAiReferrals.channelGroup,
22544
22741
  sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
22545
22742
  users: sql9`COALESCE(SUM(${gaAiReferrals.users}), 0)`
22546
- }).from(gaAiReferrals).where(and17(...aiConditions, eq24(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
22743
+ }).from(gaAiReferrals).where(and18(...aiConditions, eq25(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
22547
22744
  const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
22548
22745
  let aiBySessionUsers = 0;
22549
22746
  for (const row of aiBySessionRows) {
@@ -22557,12 +22754,12 @@ async function ga4Routes(app, opts) {
22557
22754
  channelGroup: gaSocialReferrals.channelGroup,
22558
22755
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`,
22559
22756
  users: sql9`SUM(${gaSocialReferrals.users})`
22560
- }).from(gaSocialReferrals).where(and17(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql9`SUM(${gaSocialReferrals.sessions}) DESC`).all();
22757
+ }).from(gaSocialReferrals).where(and18(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql9`SUM(${gaSocialReferrals.sessions}) DESC`).all();
22561
22758
  const socialTotals = app.db.select({
22562
22759
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`,
22563
22760
  users: sql9`SUM(${gaSocialReferrals.users})`
22564
- }).from(gaSocialReferrals).where(and17(...socialConditions)).get();
22565
- const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).orderBy(desc12(gaTrafficSummaries.syncedAt)).limit(1).get();
22761
+ }).from(gaSocialReferrals).where(and18(...socialConditions)).get();
22762
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
22566
22763
  const total = summaryRow?.totalSessions ?? 0;
22567
22764
  const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
22568
22765
  const totalOrganicSessions = summaryRow?.totalOrganicSessions ?? 0;
@@ -22642,7 +22839,7 @@ async function ga4Routes(app, opts) {
22642
22839
  const project = resolveProject(app.db, request.params.name);
22643
22840
  requireGa4Connection(opts, project.name, project.canonicalDomain);
22644
22841
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
22645
- const conditions = [eq24(gaAiReferrals.projectId, project.id)];
22842
+ const conditions = [eq25(gaAiReferrals.projectId, project.id)];
22646
22843
  if (cutoffDate) conditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
22647
22844
  const rows = app.db.select({
22648
22845
  date: gaAiReferrals.date,
@@ -22652,7 +22849,7 @@ async function ga4Routes(app, opts) {
22652
22849
  sourceDimension: gaAiReferrals.sourceDimension,
22653
22850
  sessions: sql9`SUM(${gaAiReferrals.sessions})`,
22654
22851
  users: sql9`SUM(${gaAiReferrals.users})`
22655
- }).from(gaAiReferrals).where(and17(...conditions)).groupBy(
22852
+ }).from(gaAiReferrals).where(and18(...conditions)).groupBy(
22656
22853
  gaAiReferrals.date,
22657
22854
  gaAiReferrals.source,
22658
22855
  gaAiReferrals.medium,
@@ -22665,7 +22862,7 @@ async function ga4Routes(app, opts) {
22665
22862
  const project = resolveProject(app.db, request.params.name);
22666
22863
  requireGa4Connection(opts, project.name, project.canonicalDomain);
22667
22864
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
22668
- const conditions = [eq24(gaSocialReferrals.projectId, project.id)];
22865
+ const conditions = [eq25(gaSocialReferrals.projectId, project.id)];
22669
22866
  if (cutoffDate) conditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
22670
22867
  const rows = app.db.select({
22671
22868
  date: gaSocialReferrals.date,
@@ -22674,7 +22871,7 @@ async function ga4Routes(app, opts) {
22674
22871
  channelGroup: gaSocialReferrals.channelGroup,
22675
22872
  sessions: gaSocialReferrals.sessions,
22676
22873
  users: gaSocialReferrals.users
22677
- }).from(gaSocialReferrals).where(and17(...conditions)).orderBy(gaSocialReferrals.date).all();
22874
+ }).from(gaSocialReferrals).where(and18(...conditions)).orderBy(gaSocialReferrals.date).all();
22678
22875
  return rows;
22679
22876
  });
22680
22877
  app.get("/projects/:name/ga/social-referral-trend", async (request, _reply) => {
@@ -22687,8 +22884,8 @@ async function ga4Routes(app, opts) {
22687
22884
  d.setDate(d.getDate() - n);
22688
22885
  return fmt(d);
22689
22886
  };
22690
- const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and17(
22691
- eq24(gaSocialReferrals.projectId, project.id),
22887
+ const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and18(
22888
+ eq25(gaSocialReferrals.projectId, project.id),
22692
22889
  sql9`${gaSocialReferrals.date} >= ${from}`,
22693
22890
  sql9`${gaSocialReferrals.date} < ${to}`
22694
22891
  )).get();
@@ -22700,16 +22897,16 @@ async function ga4Routes(app, opts) {
22700
22897
  const sourceCurrent = app.db.select({
22701
22898
  source: gaSocialReferrals.source,
22702
22899
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`
22703
- }).from(gaSocialReferrals).where(and17(
22704
- eq24(gaSocialReferrals.projectId, project.id),
22900
+ }).from(gaSocialReferrals).where(and18(
22901
+ eq25(gaSocialReferrals.projectId, project.id),
22705
22902
  sql9`${gaSocialReferrals.date} >= ${daysAgo(7)}`,
22706
22903
  sql9`${gaSocialReferrals.date} < ${fmt(today)}`
22707
22904
  )).groupBy(gaSocialReferrals.source).all();
22708
22905
  const sourcePrev = app.db.select({
22709
22906
  source: gaSocialReferrals.source,
22710
22907
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`
22711
- }).from(gaSocialReferrals).where(and17(
22712
- eq24(gaSocialReferrals.projectId, project.id),
22908
+ }).from(gaSocialReferrals).where(and18(
22909
+ eq25(gaSocialReferrals.projectId, project.id),
22713
22910
  sql9`${gaSocialReferrals.date} >= ${daysAgo(14)}`,
22714
22911
  sql9`${gaSocialReferrals.date} < ${daysAgo(7)}`
22715
22912
  )).groupBy(gaSocialReferrals.source).all();
@@ -22750,16 +22947,16 @@ async function ga4Routes(app, opts) {
22750
22947
  return fmt(d);
22751
22948
  };
22752
22949
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
22753
- const sumTotal = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and17(eq24(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
22754
- const sumOrganic = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and17(eq24(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
22755
- const sumDirect = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and17(eq24(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
22756
- const sumAi = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and17(
22757
- eq24(gaAiReferrals.projectId, project.id),
22950
+ const sumTotal = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and18(eq25(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
22951
+ const sumOrganic = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and18(eq25(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
22952
+ const sumDirect = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and18(eq25(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
22953
+ const sumAi = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and18(
22954
+ eq25(gaAiReferrals.projectId, project.id),
22758
22955
  sql9`${gaAiReferrals.date} >= ${from}`,
22759
22956
  sql9`${gaAiReferrals.date} < ${to}`,
22760
- eq24(gaAiReferrals.sourceDimension, "session")
22957
+ eq25(gaAiReferrals.sourceDimension, "session")
22761
22958
  )).get();
22762
- const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and17(eq24(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${from}`, sql9`${gaSocialReferrals.date} < ${to}`)).get();
22959
+ const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and18(eq25(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${from}`, sql9`${gaSocialReferrals.date} < ${to}`)).get();
22763
22960
  const todayStr = fmt(today);
22764
22961
  const buildTrend = (sum) => {
22765
22962
  const c7 = sum(daysAgo(7), todayStr)?.sessions ?? 0;
@@ -22768,17 +22965,17 @@ async function ga4Routes(app, opts) {
22768
22965
  const p30 = sum(daysAgo(60), daysAgo(30))?.sessions ?? 0;
22769
22966
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
22770
22967
  };
22771
- const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and17(
22772
- eq24(gaAiReferrals.projectId, project.id),
22968
+ const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and18(
22969
+ eq25(gaAiReferrals.projectId, project.id),
22773
22970
  sql9`${gaAiReferrals.date} >= ${daysAgo(7)}`,
22774
22971
  sql9`${gaAiReferrals.date} < ${todayStr}`,
22775
- eq24(gaAiReferrals.sourceDimension, "session")
22972
+ eq25(gaAiReferrals.sourceDimension, "session")
22776
22973
  )).groupBy(gaAiReferrals.source).all();
22777
- const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and17(
22778
- eq24(gaAiReferrals.projectId, project.id),
22974
+ const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and18(
22975
+ eq25(gaAiReferrals.projectId, project.id),
22779
22976
  sql9`${gaAiReferrals.date} >= ${daysAgo(14)}`,
22780
22977
  sql9`${gaAiReferrals.date} < ${daysAgo(7)}`,
22781
- eq24(gaAiReferrals.sourceDimension, "session")
22978
+ eq25(gaAiReferrals.sourceDimension, "session")
22782
22979
  )).groupBy(gaAiReferrals.source).all();
22783
22980
  const findBiggestMover = (current, prev) => {
22784
22981
  const prevMap = new Map(prev.map((r) => [r.source, r.sessions]));
@@ -22794,8 +22991,8 @@ async function ga4Routes(app, opts) {
22794
22991
  }
22795
22992
  return mover;
22796
22993
  };
22797
- const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql9`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and17(eq24(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql9`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
22798
- const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql9`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and17(eq24(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql9`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
22994
+ const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql9`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and18(eq25(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql9`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
22995
+ const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql9`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and18(eq25(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql9`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
22799
22996
  return {
22800
22997
  total: buildTrend(sumTotal),
22801
22998
  organic: buildTrend(sumOrganic),
@@ -22810,14 +23007,14 @@ async function ga4Routes(app, opts) {
22810
23007
  const project = resolveProject(app.db, request.params.name);
22811
23008
  requireGa4Connection(opts, project.name, project.canonicalDomain);
22812
23009
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
22813
- const conditions = [eq24(gaTrafficSnapshots.projectId, project.id)];
23010
+ const conditions = [eq25(gaTrafficSnapshots.projectId, project.id)];
22814
23011
  if (cutoffDate) conditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
22815
23012
  const rows = app.db.select({
22816
23013
  date: gaTrafficSnapshots.date,
22817
23014
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
22818
23015
  organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
22819
23016
  users: sql9`SUM(${gaTrafficSnapshots.users})`
22820
- }).from(gaTrafficSnapshots).where(and17(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
23017
+ }).from(gaTrafficSnapshots).where(and18(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
22821
23018
  return rows.map((r) => ({
22822
23019
  date: r.date,
22823
23020
  sessions: r.sessions ?? 0,
@@ -22833,7 +23030,7 @@ async function ga4Routes(app, opts) {
22833
23030
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
22834
23031
  organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
22835
23032
  users: sql9`SUM(${gaTrafficSnapshots.users})`
22836
- }).from(gaTrafficSnapshots).where(eq24(gaTrafficSnapshots.projectId, project.id)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
23033
+ }).from(gaTrafficSnapshots).where(eq25(gaTrafficSnapshots.projectId, project.id)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
22837
23034
  return {
22838
23035
  pages: trafficPages.map((r) => ({
22839
23036
  landingPage: r.landingPage,
@@ -24481,7 +24678,7 @@ async function wordpressRoutes(app, opts) {
24481
24678
 
24482
24679
  // ../api-routes/src/backlinks.ts
24483
24680
  import crypto22 from "crypto";
24484
- import { and as and19, asc as asc3, desc as desc13, eq as eq25, sql as sql10 } from "drizzle-orm";
24681
+ import { and as and20, asc as asc3, desc as desc14, eq as eq26, sql as sql10 } from "drizzle-orm";
24485
24682
 
24486
24683
  // ../integration-commoncrawl/src/constants.ts
24487
24684
  import os2 from "os";
@@ -24884,7 +25081,7 @@ function pruneCachedRelease(release, opts = {}) {
24884
25081
  }
24885
25082
 
24886
25083
  // ../api-routes/src/backlinks-filter.ts
24887
- import { and as and18, ne as ne3, notLike } from "drizzle-orm";
25084
+ import { and as and19, ne as ne3, notLike } from "drizzle-orm";
24888
25085
  var BACKLINK_FILTER_PATTERNS = [
24889
25086
  "*.google.com",
24890
25087
  "*.googleusercontent.com",
@@ -24907,7 +25104,7 @@ function backlinkCrawlerExclusionClause() {
24907
25104
  conditions.push(ne3(backlinkDomains.linkingDomain, pattern));
24908
25105
  }
24909
25106
  }
24910
- const combined = and18(...conditions);
25107
+ const combined = and19(...conditions);
24911
25108
  if (!combined) throw new Error("BACKLINK_FILTER_PATTERNS is unexpectedly empty");
24912
25109
  return combined;
24913
25110
  }
@@ -24978,11 +25175,11 @@ function mapRunRow(row) {
24978
25175
  }
24979
25176
  function latestSummaryForProject(db, projectId, source, release) {
24980
25177
  const conditions = [
24981
- eq25(backlinkSummaries.projectId, projectId),
24982
- eq25(backlinkSummaries.source, source)
25178
+ eq26(backlinkSummaries.projectId, projectId),
25179
+ eq26(backlinkSummaries.source, source)
24983
25180
  ];
24984
- if (release) conditions.push(eq25(backlinkSummaries.release, release));
24985
- return db.select().from(backlinkSummaries).where(and19(...conditions)).orderBy(desc13(backlinkSummaries.queriedAt)).limit(1).get();
25181
+ if (release) conditions.push(eq26(backlinkSummaries.release, release));
25182
+ return db.select().from(backlinkSummaries).where(and20(...conditions)).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
24986
25183
  }
24987
25184
  function parseExcludeCrawlers(value) {
24988
25185
  if (!value) return false;
@@ -24990,12 +25187,12 @@ function parseExcludeCrawlers(value) {
24990
25187
  return lower === "1" || lower === "true" || lower === "yes";
24991
25188
  }
24992
25189
  function computeFilteredSummary(db, base) {
24993
- const baseDomainCondition = and19(
24994
- eq25(backlinkDomains.projectId, base.projectId),
24995
- eq25(backlinkDomains.source, base.source),
24996
- eq25(backlinkDomains.release, base.release)
25190
+ const baseDomainCondition = and20(
25191
+ eq26(backlinkDomains.projectId, base.projectId),
25192
+ eq26(backlinkDomains.source, base.source),
25193
+ eq26(backlinkDomains.release, base.release)
24997
25194
  );
24998
- const filteredCondition = and19(baseDomainCondition, backlinkCrawlerExclusionClause());
25195
+ const filteredCondition = and20(baseDomainCondition, backlinkCrawlerExclusionClause());
24999
25196
  const unfilteredAgg = db.select({
25000
25197
  count: sql10`count(*)`,
25001
25198
  total: sql10`coalesce(sum(${backlinkDomains.numHosts}), 0)`
@@ -25004,7 +25201,7 @@ function computeFilteredSummary(db, base) {
25004
25201
  count: sql10`count(*)`,
25005
25202
  total: sql10`coalesce(sum(${backlinkDomains.numHosts}), 0)`
25006
25203
  }).from(backlinkDomains).where(filteredCondition).get();
25007
- const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc13(backlinkDomains.numHosts)).limit(10).all();
25204
+ const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc14(backlinkDomains.numHosts)).limit(10).all();
25008
25205
  const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
25009
25206
  const totalHosts = Number(filteredAgg?.total ?? 0);
25010
25207
  const unfilteredLinkingDomains = Number(unfilteredAgg?.count ?? 0);
@@ -25025,13 +25222,13 @@ function computeFilteredSummary(db, base) {
25025
25222
  };
25026
25223
  }
25027
25224
  function buildSourceAvailability(db, projectId, source, connected, excludeCrawlers) {
25028
- const summary = db.select().from(backlinkSummaries).where(and19(eq25(backlinkSummaries.projectId, projectId), eq25(backlinkSummaries.source, source))).orderBy(desc13(backlinkSummaries.queriedAt)).limit(1).get();
25225
+ const summary = db.select().from(backlinkSummaries).where(and20(eq26(backlinkSummaries.projectId, projectId), eq26(backlinkSummaries.source, source))).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
25029
25226
  let totalLinkingDomains = summary?.totalLinkingDomains ?? 0;
25030
25227
  if (summary && excludeCrawlers) {
25031
- const filtered = db.select({ count: sql10`count(*)` }).from(backlinkDomains).where(and19(
25032
- eq25(backlinkDomains.projectId, projectId),
25033
- eq25(backlinkDomains.source, source),
25034
- eq25(backlinkDomains.release, summary.release),
25228
+ const filtered = db.select({ count: sql10`count(*)` }).from(backlinkDomains).where(and20(
25229
+ eq26(backlinkDomains.projectId, projectId),
25230
+ eq26(backlinkDomains.source, source),
25231
+ eq26(backlinkDomains.release, summary.release),
25035
25232
  backlinkCrawlerExclusionClause()
25036
25233
  )).get();
25037
25234
  totalLinkingDomains = Number(filtered?.count ?? 0);
@@ -25046,7 +25243,7 @@ function buildSourceAvailability(db, projectId, source, connected, excludeCrawle
25046
25243
  };
25047
25244
  }
25048
25245
  function computeSourceAvailability(db, project, bingStore, excludeCrawlers) {
25049
- const ccReadySync = db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq25(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
25246
+ const ccReadySync = db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
25050
25247
  const ccConnected = project.autoExtractBacklinks === true && !!ccReadySync;
25051
25248
  const bingConnected = !!bingStore?.getConnection(project.canonicalDomain);
25052
25249
  const sources = [
@@ -25102,7 +25299,7 @@ async function backlinksRoutes(app, opts) {
25102
25299
  "@duckdb/node-api is not installed. Run `canonry backlinks install` to enable the backlinks feature."
25103
25300
  );
25104
25301
  }
25105
- const existing = app.db.select().from(ccReleaseSyncs).where(eq25(ccReleaseSyncs.release, release)).get();
25302
+ const existing = app.db.select().from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.release, release)).get();
25106
25303
  const now = (/* @__PURE__ */ new Date()).toISOString();
25107
25304
  if (existing) {
25108
25305
  if (NON_TERMINAL_SYNC_STATUSES.has(existing.status)) {
@@ -25113,9 +25310,9 @@ async function backlinksRoutes(app, opts) {
25113
25310
  phaseDetail: null,
25114
25311
  error: null,
25115
25312
  updatedAt: now
25116
- }).where(eq25(ccReleaseSyncs.id, existing.id)).run();
25313
+ }).where(eq26(ccReleaseSyncs.id, existing.id)).run();
25117
25314
  opts.onReleaseSyncRequested(existing.id, release);
25118
- const refreshed = app.db.select().from(ccReleaseSyncs).where(eq25(ccReleaseSyncs.id, existing.id)).get();
25315
+ const refreshed = app.db.select().from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.id, existing.id)).get();
25119
25316
  return reply.status(200).send(mapSyncRow(refreshed));
25120
25317
  }
25121
25318
  const id = crypto22.randomUUID();
@@ -25127,15 +25324,15 @@ async function backlinksRoutes(app, opts) {
25127
25324
  updatedAt: now
25128
25325
  }).run();
25129
25326
  opts.onReleaseSyncRequested(id, release);
25130
- const inserted = app.db.select().from(ccReleaseSyncs).where(eq25(ccReleaseSyncs.id, id)).get();
25327
+ const inserted = app.db.select().from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.id, id)).get();
25131
25328
  return reply.status(201).send(mapSyncRow(inserted));
25132
25329
  });
25133
25330
  app.get("/backlinks/syncs/latest", async (_request, reply) => {
25134
- const row = app.db.select().from(ccReleaseSyncs).orderBy(desc13(ccReleaseSyncs.updatedAt)).limit(1).get();
25331
+ const row = app.db.select().from(ccReleaseSyncs).orderBy(desc14(ccReleaseSyncs.updatedAt)).limit(1).get();
25135
25332
  return reply.send(row ? mapSyncRow(row) : null);
25136
25333
  });
25137
25334
  app.get("/backlinks/syncs", async (_request, reply) => {
25138
- const rows = app.db.select().from(ccReleaseSyncs).orderBy(desc13(ccReleaseSyncs.updatedAt)).all();
25335
+ const rows = app.db.select().from(ccReleaseSyncs).orderBy(desc14(ccReleaseSyncs.updatedAt)).all();
25139
25336
  return reply.send(rows.map(mapSyncRow));
25140
25337
  });
25141
25338
  app.get("/backlinks/releases", async (_request, reply) => {
@@ -25185,7 +25382,7 @@ async function backlinksRoutes(app, opts) {
25185
25382
  createdAt: now
25186
25383
  }).run();
25187
25384
  opts.onBacklinkExtractRequested(runId, project.id, release);
25188
- const run = app.db.select().from(runs).where(eq25(runs.id, runId)).get();
25385
+ const run = app.db.select().from(runs).where(eq26(runs.id, runId)).get();
25189
25386
  return reply.status(201).send(mapRunRow(run));
25190
25387
  });
25191
25388
  app.get(
@@ -25211,18 +25408,18 @@ async function backlinksRoutes(app, opts) {
25211
25408
  const limit = Math.min(Math.max(parseInt(request.query.limit ?? "50", 10) || 50, 1), 500);
25212
25409
  const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
25213
25410
  const excludeCrawlers = parseExcludeCrawlers(request.query.excludeCrawlers);
25214
- const baseDomainCondition = and19(
25215
- eq25(backlinkDomains.projectId, project.id),
25216
- eq25(backlinkDomains.source, source),
25217
- eq25(backlinkDomains.release, targetRelease)
25411
+ const baseDomainCondition = and20(
25412
+ eq26(backlinkDomains.projectId, project.id),
25413
+ eq26(backlinkDomains.source, source),
25414
+ eq26(backlinkDomains.release, targetRelease)
25218
25415
  );
25219
- const domainCondition = excludeCrawlers ? and19(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
25416
+ const domainCondition = excludeCrawlers ? and20(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
25220
25417
  const totalRow = app.db.select({ count: sql10`count(*)` }).from(backlinkDomains).where(domainCondition).get();
25221
25418
  const rows = app.db.select({
25222
25419
  linkingDomain: backlinkDomains.linkingDomain,
25223
25420
  numHosts: backlinkDomains.numHosts,
25224
25421
  source: backlinkDomains.source
25225
- }).from(backlinkDomains).where(domainCondition).orderBy(desc13(backlinkDomains.numHosts)).limit(limit).offset(offset).all();
25422
+ }).from(backlinkDomains).where(domainCondition).orderBy(desc14(backlinkDomains.numHosts)).limit(limit).offset(offset).all();
25226
25423
  let summary = null;
25227
25424
  if (summaryRow) {
25228
25425
  summary = excludeCrawlers ? computeFilteredSummary(app.db, summaryRow) : mapSummaryRow(summaryRow);
@@ -25240,7 +25437,7 @@ async function backlinksRoutes(app, opts) {
25240
25437
  async (request, reply) => {
25241
25438
  const project = resolveProject(app.db, request.params.name);
25242
25439
  const source = parseSourceParam(request.query.source);
25243
- const rows = app.db.select().from(backlinkSummaries).where(and19(eq25(backlinkSummaries.projectId, project.id), eq25(backlinkSummaries.source, source))).orderBy(asc3(backlinkSummaries.queriedAt)).all();
25440
+ const rows = app.db.select().from(backlinkSummaries).where(and20(eq26(backlinkSummaries.projectId, project.id), eq26(backlinkSummaries.source, source))).orderBy(asc3(backlinkSummaries.queriedAt)).all();
25244
25441
  const response = rows.map((r) => ({
25245
25442
  release: r.release,
25246
25443
  totalLinkingDomains: r.totalLinkingDomains,
@@ -25287,7 +25484,7 @@ async function backlinksRoutes(app, opts) {
25287
25484
  createdAt: now
25288
25485
  }).run();
25289
25486
  opts.onBingBacklinkSyncRequested(runId, project.id);
25290
- const run = app.db.select().from(runs).where(eq25(runs.id, runId)).get();
25487
+ const run = app.db.select().from(runs).where(eq26(runs.id, runId)).get();
25291
25488
  return reply.status(201).send(mapRunRow(run));
25292
25489
  }
25293
25490
  );
@@ -25296,7 +25493,7 @@ async function backlinksRoutes(app, opts) {
25296
25493
  // ../api-routes/src/traffic.ts
25297
25494
  import crypto24 from "crypto";
25298
25495
  import { Agent as UndiciAgent } from "undici";
25299
- import { and as and20, desc as desc14, eq as eq26, gte as gte4, lte as lte3, sql as sql11 } from "drizzle-orm";
25496
+ import { and as and21, desc as desc15, eq as eq27, gte as gte4, lte as lte3, sql as sql11 } from "drizzle-orm";
25300
25497
 
25301
25498
  // ../integration-cloud-run/src/auth.ts
25302
25499
  import crypto23 from "crypto";
@@ -29113,8 +29310,8 @@ async function runBackfillTask(options) {
29113
29310
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
29114
29311
  try {
29115
29312
  app.db.transaction((tx) => {
29116
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq26(runs.id, runId)).run();
29117
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq26(trafficSources.id, sourceRow.id)).run();
29313
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq27(runs.id, runId)).run();
29314
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq27(trafficSources.id, sourceRow.id)).run();
29118
29315
  });
29119
29316
  } catch {
29120
29317
  }
@@ -29129,7 +29326,7 @@ async function runBackfillTask(options) {
29129
29326
  if (allEvents.length === 0) {
29130
29327
  const finishedAt2 = (/* @__PURE__ */ new Date()).toISOString();
29131
29328
  try {
29132
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq26(runs.id, runId)).run();
29329
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq27(runs.id, runId)).run();
29133
29330
  } catch {
29134
29331
  }
29135
29332
  return;
@@ -29151,29 +29348,29 @@ async function runBackfillTask(options) {
29151
29348
  try {
29152
29349
  app.db.transaction((tx) => {
29153
29350
  tx.delete(crawlerEventsHourly).where(
29154
- and20(
29155
- eq26(crawlerEventsHourly.sourceId, sourceRow.id),
29351
+ and21(
29352
+ eq27(crawlerEventsHourly.sourceId, sourceRow.id),
29156
29353
  gte4(crawlerEventsHourly.tsHour, windowStartIso),
29157
29354
  lte3(crawlerEventsHourly.tsHour, windowEndIso)
29158
29355
  )
29159
29356
  ).run();
29160
29357
  tx.delete(aiUserFetchEventsHourly).where(
29161
- and20(
29162
- eq26(aiUserFetchEventsHourly.sourceId, sourceRow.id),
29358
+ and21(
29359
+ eq27(aiUserFetchEventsHourly.sourceId, sourceRow.id),
29163
29360
  gte4(aiUserFetchEventsHourly.tsHour, windowStartIso),
29164
29361
  lte3(aiUserFetchEventsHourly.tsHour, windowEndIso)
29165
29362
  )
29166
29363
  ).run();
29167
29364
  tx.delete(aiReferralEventsHourly).where(
29168
- and20(
29169
- eq26(aiReferralEventsHourly.sourceId, sourceRow.id),
29365
+ and21(
29366
+ eq27(aiReferralEventsHourly.sourceId, sourceRow.id),
29170
29367
  gte4(aiReferralEventsHourly.tsHour, windowStartIso),
29171
29368
  lte3(aiReferralEventsHourly.tsHour, windowEndIso)
29172
29369
  )
29173
29370
  ).run();
29174
29371
  tx.delete(rawEventSamples).where(
29175
- and20(
29176
- eq26(rawEventSamples.sourceId, sourceRow.id),
29372
+ and21(
29373
+ eq27(rawEventSamples.sourceId, sourceRow.id),
29177
29374
  gte4(rawEventSamples.ts, windowStartIso),
29178
29375
  lte3(rawEventSamples.ts, windowEndIso)
29179
29376
  )
@@ -29262,8 +29459,8 @@ async function runBackfillTask(options) {
29262
29459
  lastError: null,
29263
29460
  lastEventIds: newRingBuffer,
29264
29461
  updatedAt: finishedAt
29265
- }).where(eq26(trafficSources.id, sourceRow.id)).run();
29266
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq26(runs.id, runId)).run();
29462
+ }).where(eq27(trafficSources.id, sourceRow.id)).run();
29463
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq27(runs.id, runId)).run();
29267
29464
  });
29268
29465
  } catch (e) {
29269
29466
  markFailed(`Backfill rollup write failed: ${e instanceof Error ? e.message : String(e)}`);
@@ -29345,7 +29542,7 @@ async function trafficRoutes(app, opts) {
29345
29542
  createdAt: existing?.createdAt ?? now,
29346
29543
  updatedAt: now
29347
29544
  });
29348
- const activeSource = app.db.select().from(trafficSources).where(eq26(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes["cloud-run"] && row.status !== TrafficSourceStatuses.archived);
29545
+ const activeSource = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes["cloud-run"] && row.status !== TrafficSourceStatuses.archived);
29349
29546
  const config = {
29350
29547
  gcpProjectId,
29351
29548
  serviceName: serviceName ?? null,
@@ -29361,8 +29558,8 @@ async function trafficRoutes(app, opts) {
29361
29558
  lastError: null,
29362
29559
  configJson: config,
29363
29560
  updatedAt: now
29364
- }).where(eq26(trafficSources.id, activeSource.id)).run();
29365
- sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, activeSource.id)).get();
29561
+ }).where(eq27(trafficSources.id, activeSource.id)).run();
29562
+ sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, activeSource.id)).get();
29366
29563
  } else {
29367
29564
  const newId = crypto24.randomUUID();
29368
29565
  app.db.insert(trafficSources).values({
@@ -29379,7 +29576,7 @@ async function trafficRoutes(app, opts) {
29379
29576
  createdAt: now,
29380
29577
  updatedAt: now
29381
29578
  }).run();
29382
- sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, newId)).get();
29579
+ sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, newId)).get();
29383
29580
  }
29384
29581
  writeAuditLog(app.db, {
29385
29582
  projectId: project.id,
@@ -29431,7 +29628,7 @@ async function trafficRoutes(app, opts) {
29431
29628
  createdAt: existing?.createdAt ?? now,
29432
29629
  updatedAt: now
29433
29630
  });
29434
- const activeSource = app.db.select().from(trafficSources).where(eq26(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
29631
+ const activeSource = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
29435
29632
  const config = { baseUrl, username };
29436
29633
  const fallbackName = displayName ?? `WordPress \xB7 ${new URL(baseUrl).host}`;
29437
29634
  let sourceRow;
@@ -29442,8 +29639,8 @@ async function trafficRoutes(app, opts) {
29442
29639
  lastError: null,
29443
29640
  configJson: config,
29444
29641
  updatedAt: now
29445
- }).where(eq26(trafficSources.id, activeSource.id)).run();
29446
- sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, activeSource.id)).get();
29642
+ }).where(eq27(trafficSources.id, activeSource.id)).run();
29643
+ sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, activeSource.id)).get();
29447
29644
  } else {
29448
29645
  const newId = crypto24.randomUUID();
29449
29646
  app.db.insert(trafficSources).values({
@@ -29460,7 +29657,7 @@ async function trafficRoutes(app, opts) {
29460
29657
  createdAt: now,
29461
29658
  updatedAt: now
29462
29659
  }).run();
29463
- sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, newId)).get();
29660
+ sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, newId)).get();
29464
29661
  }
29465
29662
  writeAuditLog(app.db, {
29466
29663
  projectId: project.id,
@@ -29514,7 +29711,7 @@ async function trafficRoutes(app, opts) {
29514
29711
  createdAt: existing?.createdAt ?? now,
29515
29712
  updatedAt: now
29516
29713
  });
29517
- const activeSource = app.db.select().from(trafficSources).where(eq26(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.vercel && row.status !== TrafficSourceStatuses.archived);
29714
+ const activeSource = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.vercel && row.status !== TrafficSourceStatuses.archived);
29518
29715
  const config = { projectId, teamId, environment };
29519
29716
  const fallbackName = displayName ?? `Vercel \xB7 ${projectId}`;
29520
29717
  const { sourceRow, scheduleCreated } = app.db.transaction((tx) => {
@@ -29526,8 +29723,8 @@ async function trafficRoutes(app, opts) {
29526
29723
  lastError: null,
29527
29724
  configJson: config,
29528
29725
  updatedAt: now
29529
- }).where(eq26(trafficSources.id, activeSource.id)).run();
29530
- row = tx.select().from(trafficSources).where(eq26(trafficSources.id, activeSource.id)).get();
29726
+ }).where(eq27(trafficSources.id, activeSource.id)).run();
29727
+ row = tx.select().from(trafficSources).where(eq27(trafficSources.id, activeSource.id)).get();
29531
29728
  } else {
29532
29729
  const newId = crypto24.randomUUID();
29533
29730
  tx.insert(trafficSources).values({
@@ -29552,12 +29749,12 @@ async function trafficRoutes(app, opts) {
29552
29749
  createdAt: now,
29553
29750
  updatedAt: now
29554
29751
  }).run();
29555
- row = tx.select().from(trafficSources).where(eq26(trafficSources.id, newId)).get();
29752
+ row = tx.select().from(trafficSources).where(eq27(trafficSources.id, newId)).get();
29556
29753
  }
29557
29754
  const existingSchedule = tx.select().from(schedules).where(
29558
- and20(
29559
- eq26(schedules.projectId, project.id),
29560
- eq26(schedules.kind, SchedulableRunKinds["traffic-sync"])
29755
+ and21(
29756
+ eq27(schedules.projectId, project.id),
29757
+ eq27(schedules.kind, SchedulableRunKinds["traffic-sync"])
29561
29758
  )
29562
29759
  ).get();
29563
29760
  let created = false;
@@ -29606,7 +29803,7 @@ async function trafficRoutes(app, opts) {
29606
29803
  });
29607
29804
  app.post("/projects/:name/traffic/sources/:id/sync", async (request) => {
29608
29805
  const project = resolveProject(app.db, request.params.name);
29609
- const sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, request.params.id)).get();
29806
+ const sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, request.params.id)).get();
29610
29807
  if (!sourceRow || sourceRow.projectId !== project.id) {
29611
29808
  throw notFound("Traffic source", request.params.id);
29612
29809
  }
@@ -29632,8 +29829,8 @@ async function trafficRoutes(app, opts) {
29632
29829
  const markFailed = (msg, errorCode) => {
29633
29830
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
29634
29831
  app.db.transaction((tx) => {
29635
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq26(runs.id, runId)).run();
29636
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq26(trafficSources.id, sourceRow.id)).run();
29832
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq27(runs.id, runId)).run();
29833
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq27(trafficSources.id, sourceRow.id)).run();
29637
29834
  });
29638
29835
  try {
29639
29836
  opts.onTrafficSynced?.({
@@ -29714,7 +29911,7 @@ async function trafficRoutes(app, opts) {
29714
29911
  }
29715
29912
  const credential = credentialStore.getConnection(project.name);
29716
29913
  if (!credential) {
29717
- app.db.delete(runs).where(eq26(runs.id, runId)).run();
29914
+ app.db.delete(runs).where(eq27(runs.id, runId)).run();
29718
29915
  throw validationError(
29719
29916
  `No WordPress credential found for project "${project.name}". Run "canonry traffic connect wordpress" first.`
29720
29917
  );
@@ -29763,12 +29960,12 @@ async function trafficRoutes(app, opts) {
29763
29960
  auditAction = "traffic.vercel.synced";
29764
29961
  const credentialStore = opts.vercelTrafficCredentialStore;
29765
29962
  if (!credentialStore) {
29766
- app.db.delete(runs).where(eq26(runs.id, runId)).run();
29963
+ app.db.delete(runs).where(eq27(runs.id, runId)).run();
29767
29964
  throw validationError("Vercel traffic credential storage is not configured for this deployment");
29768
29965
  }
29769
29966
  const credential = credentialStore.getConnection(project.name);
29770
29967
  if (!credential) {
29771
- app.db.delete(runs).where(eq26(runs.id, runId)).run();
29968
+ app.db.delete(runs).where(eq27(runs.id, runId)).run();
29772
29969
  throw validationError(
29773
29970
  `No Vercel credential found for project "${project.name}". Run "canonry traffic connect vercel" first.`
29774
29971
  );
@@ -29868,7 +30065,7 @@ async function trafficRoutes(app, opts) {
29868
30065
  let aiReferralHitsCount = 0;
29869
30066
  let unknownHitsCount = 0;
29870
30067
  app.db.transaction((tx) => {
29871
- const latestRow = tx.select().from(trafficSources).where(eq26(trafficSources.id, sourceRow.id)).get();
30068
+ const latestRow = tx.select().from(trafficSources).where(eq27(trafficSources.id, sourceRow.id)).get();
29872
30069
  const previousIds = latestRow.lastEventIds ?? [];
29873
30070
  const seenEventIds = new Set(previousIds);
29874
30071
  const dedupedEvents = seenEventIds.size === 0 ? allEvents : allEvents.filter((e) => !seenEventIds.has(e.eventId));
@@ -30036,8 +30233,8 @@ async function trafficRoutes(app, opts) {
30036
30233
  if (sourceRow.sourceType === TrafficSourceTypes.wordpress) {
30037
30234
  sourceUpdate.lastCursor = nextCursor ?? null;
30038
30235
  }
30039
- tx.update(trafficSources).set(sourceUpdate).where(eq26(trafficSources.id, sourceRow.id)).run();
30040
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq26(runs.id, runId)).run();
30236
+ tx.update(trafficSources).set(sourceUpdate).where(eq27(trafficSources.id, sourceRow.id)).run();
30237
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq27(runs.id, runId)).run();
30041
30238
  });
30042
30239
  writeAuditLog(app.db, {
30043
30240
  projectId: project.id,
@@ -30089,7 +30286,7 @@ async function trafficRoutes(app, opts) {
30089
30286
  });
30090
30287
  app.post("/projects/:name/traffic/sources/:id/backfill", async (request) => {
30091
30288
  const project = resolveProject(app.db, request.params.name);
30092
- const sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, request.params.id)).get();
30289
+ const sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, request.params.id)).get();
30093
30290
  if (!sourceRow || sourceRow.projectId !== project.id) {
30094
30291
  throw notFound("Traffic source", request.params.id);
30095
30292
  }
@@ -30274,36 +30471,36 @@ async function trafficRoutes(app, opts) {
30274
30471
  });
30275
30472
  function buildSourceDetail(projectId, row, since) {
30276
30473
  const crawlerTotals = app.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
30277
- and20(
30278
- eq26(crawlerEventsHourly.sourceId, row.id),
30474
+ and21(
30475
+ eq27(crawlerEventsHourly.sourceId, row.id),
30279
30476
  gte4(crawlerEventsHourly.tsHour, since)
30280
30477
  )
30281
30478
  ).get();
30282
30479
  const aiUserFetchTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
30283
- and20(
30284
- eq26(aiUserFetchEventsHourly.sourceId, row.id),
30480
+ and21(
30481
+ eq27(aiUserFetchEventsHourly.sourceId, row.id),
30285
30482
  gte4(aiUserFetchEventsHourly.tsHour, since)
30286
30483
  )
30287
30484
  ).get();
30288
30485
  const aiTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
30289
- and20(
30290
- eq26(aiReferralEventsHourly.sourceId, row.id),
30486
+ and21(
30487
+ eq27(aiReferralEventsHourly.sourceId, row.id),
30291
30488
  gte4(aiReferralEventsHourly.tsHour, since)
30292
30489
  )
30293
30490
  ).get();
30294
30491
  const sampleTotals = app.db.select({ total: sql11`COUNT(*)` }).from(rawEventSamples).where(
30295
- and20(
30296
- eq26(rawEventSamples.sourceId, row.id),
30492
+ and21(
30493
+ eq27(rawEventSamples.sourceId, row.id),
30297
30494
  gte4(rawEventSamples.ts, since)
30298
30495
  )
30299
30496
  ).get();
30300
30497
  const latestRun = app.db.select().from(runs).where(
30301
- and20(
30302
- eq26(runs.projectId, projectId),
30303
- eq26(runs.kind, RunKinds["traffic-sync"]),
30304
- eq26(runs.sourceId, row.id)
30498
+ and21(
30499
+ eq27(runs.projectId, projectId),
30500
+ eq27(runs.kind, RunKinds["traffic-sync"]),
30501
+ eq27(runs.sourceId, row.id)
30305
30502
  )
30306
- ).orderBy(desc14(runs.startedAt)).limit(1).get();
30503
+ ).orderBy(desc15(runs.startedAt)).limit(1).get();
30307
30504
  return {
30308
30505
  ...rowToDto(row),
30309
30506
  totals24h: {
@@ -30329,7 +30526,7 @@ async function trafficRoutes(app, opts) {
30329
30526
  "`advanceToNow` must be `true`. There is no implicit reset."
30330
30527
  );
30331
30528
  }
30332
- const sourceRow = app.db.select().from(trafficSources).where(and20(eq26(trafficSources.projectId, project.id), eq26(trafficSources.id, request.params.id))).get();
30529
+ const sourceRow = app.db.select().from(trafficSources).where(and21(eq27(trafficSources.projectId, project.id), eq27(trafficSources.id, request.params.id))).get();
30333
30530
  if (!sourceRow) {
30334
30531
  throw notFound("traffic source", request.params.id);
30335
30532
  }
@@ -30346,7 +30543,7 @@ async function trafficRoutes(app, opts) {
30346
30543
  status: TrafficSourceStatuses.connected,
30347
30544
  lastError: null,
30348
30545
  updatedAt: now
30349
- }).where(eq26(trafficSources.id, sourceRow.id)).run();
30546
+ }).where(eq27(trafficSources.id, sourceRow.id)).run();
30350
30547
  writeAuditLog(tx, auditFromRequest(request, {
30351
30548
  projectId: project.id,
30352
30549
  actor: "api",
@@ -30354,20 +30551,20 @@ async function trafficRoutes(app, opts) {
30354
30551
  entityType: "traffic_source",
30355
30552
  entityId: sourceRow.id
30356
30553
  }));
30357
- updatedRow = tx.select().from(trafficSources).where(eq26(trafficSources.id, sourceRow.id)).get();
30554
+ updatedRow = tx.select().from(trafficSources).where(eq27(trafficSources.id, sourceRow.id)).get();
30358
30555
  });
30359
30556
  return buildSourceDetail(project.id, updatedRow, new Date(Date.now() - 24 * 60 * 6e4).toISOString());
30360
30557
  });
30361
30558
  app.get("/projects/:name/traffic/sources", async (request) => {
30362
30559
  const project = resolveProject(app.db, request.params.name);
30363
- const rows = app.db.select().from(trafficSources).where(eq26(trafficSources.projectId, project.id)).orderBy(desc14(trafficSources.createdAt)).all();
30560
+ const rows = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
30364
30561
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map(rowToDto);
30365
30562
  const response = { sources };
30366
30563
  return response;
30367
30564
  });
30368
30565
  app.get("/projects/:name/traffic/status", async (request) => {
30369
30566
  const project = resolveProject(app.db, request.params.name);
30370
- const rows = app.db.select().from(trafficSources).where(eq26(trafficSources.projectId, project.id)).orderBy(desc14(trafficSources.createdAt)).all();
30567
+ const rows = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
30371
30568
  const since = new Date(Date.now() - 24 * 60 * 6e4).toISOString();
30372
30569
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map((row) => buildSourceDetail(project.id, row, since));
30373
30570
  const response = { sources };
@@ -30377,7 +30574,7 @@ async function trafficRoutes(app, opts) {
30377
30574
  "/projects/:name/traffic/sources/:id",
30378
30575
  async (request) => {
30379
30576
  const project = resolveProject(app.db, request.params.name);
30380
- const row = app.db.select().from(trafficSources).where(eq26(trafficSources.id, request.params.id)).get();
30577
+ const row = app.db.select().from(trafficSources).where(eq27(trafficSources.id, request.params.id)).get();
30381
30578
  if (!row || row.projectId !== project.id) {
30382
30579
  throw notFound("Traffic source", request.params.id);
30383
30580
  }
@@ -30428,15 +30625,15 @@ async function trafficRoutes(app, opts) {
30428
30625
  let aiReferralTotal = 0;
30429
30626
  if (kind === "all" || kind === TrafficEventKinds.crawler) {
30430
30627
  const crawlerFilters = [
30431
- eq26(crawlerEventsHourly.projectId, project.id),
30628
+ eq27(crawlerEventsHourly.projectId, project.id),
30432
30629
  gte4(crawlerEventsHourly.tsHour, sinceIso),
30433
30630
  lte3(crawlerEventsHourly.tsHour, untilIso)
30434
30631
  ];
30435
- if (sourceIdParam) crawlerFilters.push(eq26(crawlerEventsHourly.sourceId, sourceIdParam));
30436
- const crawlerWhere = and20(...crawlerFilters);
30632
+ if (sourceIdParam) crawlerFilters.push(eq27(crawlerEventsHourly.sourceId, sourceIdParam));
30633
+ const crawlerWhere = and21(...crawlerFilters);
30437
30634
  const total = app.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
30438
30635
  crawlerTotal = Number(total?.total ?? 0);
30439
- const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc14(crawlerEventsHourly.tsHour)).limit(limit).all();
30636
+ const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc15(crawlerEventsHourly.tsHour)).limit(limit).all();
30440
30637
  for (const r of rows) {
30441
30638
  events.push({
30442
30639
  kind: TrafficEventKinds.crawler,
@@ -30453,15 +30650,15 @@ async function trafficRoutes(app, opts) {
30453
30650
  }
30454
30651
  if (kind === "all" || kind === TrafficEventKinds["ai-user-fetch"]) {
30455
30652
  const userFetchFilters = [
30456
- eq26(aiUserFetchEventsHourly.projectId, project.id),
30653
+ eq27(aiUserFetchEventsHourly.projectId, project.id),
30457
30654
  gte4(aiUserFetchEventsHourly.tsHour, sinceIso),
30458
30655
  lte3(aiUserFetchEventsHourly.tsHour, untilIso)
30459
30656
  ];
30460
- if (sourceIdParam) userFetchFilters.push(eq26(aiUserFetchEventsHourly.sourceId, sourceIdParam));
30461
- const userFetchWhere = and20(...userFetchFilters);
30657
+ if (sourceIdParam) userFetchFilters.push(eq27(aiUserFetchEventsHourly.sourceId, sourceIdParam));
30658
+ const userFetchWhere = and21(...userFetchFilters);
30462
30659
  const total = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(userFetchWhere).get();
30463
30660
  aiUserFetchTotal = Number(total?.total ?? 0);
30464
- const rows = app.db.select().from(aiUserFetchEventsHourly).where(userFetchWhere).orderBy(desc14(aiUserFetchEventsHourly.tsHour)).limit(limit).all();
30661
+ const rows = app.db.select().from(aiUserFetchEventsHourly).where(userFetchWhere).orderBy(desc15(aiUserFetchEventsHourly.tsHour)).limit(limit).all();
30465
30662
  for (const r of rows) {
30466
30663
  events.push({
30467
30664
  kind: TrafficEventKinds["ai-user-fetch"],
@@ -30478,15 +30675,15 @@ async function trafficRoutes(app, opts) {
30478
30675
  }
30479
30676
  if (kind === "all" || kind === TrafficEventKinds["ai-referral"]) {
30480
30677
  const aiFilters = [
30481
- eq26(aiReferralEventsHourly.projectId, project.id),
30678
+ eq27(aiReferralEventsHourly.projectId, project.id),
30482
30679
  gte4(aiReferralEventsHourly.tsHour, sinceIso),
30483
30680
  lte3(aiReferralEventsHourly.tsHour, untilIso)
30484
30681
  ];
30485
- if (sourceIdParam) aiFilters.push(eq26(aiReferralEventsHourly.sourceId, sourceIdParam));
30486
- const aiWhere = and20(...aiFilters);
30682
+ if (sourceIdParam) aiFilters.push(eq27(aiReferralEventsHourly.sourceId, sourceIdParam));
30683
+ const aiWhere = and21(...aiFilters);
30487
30684
  const total = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
30488
30685
  aiReferralTotal = Number(total?.total ?? 0);
30489
- const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc14(aiReferralEventsHourly.tsHour)).limit(limit).all();
30686
+ const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc15(aiReferralEventsHourly.tsHour)).limit(limit).all();
30490
30687
  for (const r of rows) {
30491
30688
  events.push({
30492
30689
  kind: TrafficEventKinds["ai-referral"],
@@ -30687,7 +30884,7 @@ function readInstalledManifest(skillDir) {
30687
30884
  var AGENT_CHECKS = [skillsInstalledCheck, skillsCurrentCheck];
30688
30885
 
30689
30886
  // ../api-routes/src/doctor/checks/backlinks.ts
30690
- import { and as and21, eq as eq27 } from "drizzle-orm";
30887
+ import { and as and22, eq as eq28 } from "drizzle-orm";
30691
30888
  function skippedNoProject() {
30692
30889
  return {
30693
30890
  status: CheckStatuses.skipped,
@@ -30703,8 +30900,8 @@ var BACKLINKS_CHECKS = [
30703
30900
  title: "Backlinks source connected",
30704
30901
  run: (ctx) => {
30705
30902
  if (!ctx.project) return skippedNoProject();
30706
- const projectRow = ctx.db.select({ autoExtract: projects.autoExtractBacklinks }).from(projects).where(eq27(projects.id, ctx.project.id)).get();
30707
- const readySync = ctx.db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq27(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
30903
+ const projectRow = ctx.db.select({ autoExtract: projects.autoExtractBacklinks }).from(projects).where(eq28(projects.id, ctx.project.id)).get();
30904
+ const readySync = ctx.db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq28(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
30708
30905
  const ccConnected = projectRow?.autoExtract === true && !!readySync;
30709
30906
  const bingConnected = !!ctx.bingConnectionStore?.getConnection(ctx.project.canonicalDomain);
30710
30907
  const connected = [];
@@ -30719,9 +30916,9 @@ var BACKLINKS_CHECKS = [
30719
30916
  details: { commoncrawl: ccConnected, bingWebmaster: bingConnected }
30720
30917
  };
30721
30918
  }
30722
- const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and21(
30723
- eq27(backlinkSummaries.projectId, ctx.project.id),
30724
- eq27(backlinkSummaries.source, BacklinkSources.commoncrawl)
30919
+ const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and22(
30920
+ eq28(backlinkSummaries.projectId, ctx.project.id),
30921
+ eq28(backlinkSummaries.source, BacklinkSources.commoncrawl)
30725
30922
  )).limit(1).get() : false;
30726
30923
  return {
30727
30924
  status: CheckStatuses.ok,
@@ -30881,7 +31078,7 @@ var BING_AUTH_CHECKS = [
30881
31078
  ];
30882
31079
 
30883
31080
  // ../api-routes/src/doctor/checks/content.ts
30884
- import { eq as eq28 } from "drizzle-orm";
31081
+ import { eq as eq29 } from "drizzle-orm";
30885
31082
  var WINNABILITY_COVERAGE_WARN_THRESHOLD = 0.8;
30886
31083
  var UNCLASSIFIED_DOMAIN_SAMPLE_LIMIT = 10;
30887
31084
  function skippedNoProject2() {
@@ -30894,7 +31091,7 @@ function skippedNoProject2() {
30894
31091
  }
30895
31092
  function loadProject(ctx) {
30896
31093
  if (!ctx.project) return null;
30897
- return ctx.db.select().from(projects).where(eq28(projects.id, ctx.project.id)).get() ?? null;
31094
+ return ctx.db.select().from(projects).where(eq29(projects.id, ctx.project.id)).get() ?? null;
30898
31095
  }
30899
31096
  function percent(value) {
30900
31097
  return Math.round(value * 100);
@@ -30986,7 +31183,7 @@ var CONTENT_CHECK_BY_ID = Object.fromEntries(
30986
31183
  );
30987
31184
 
30988
31185
  // ../api-routes/src/doctor/checks/ads.ts
30989
- import { eq as eq29 } from "drizzle-orm";
31186
+ import { eq as eq30 } from "drizzle-orm";
30990
31187
  var RECENT_SYNC_WARN_DAYS = 7;
30991
31188
  var RECENT_SYNC_FAIL_DAYS = 30;
30992
31189
  var adsConnectionCheck = {
@@ -31003,7 +31200,7 @@ var adsConnectionCheck = {
31003
31200
  remediation: null
31004
31201
  };
31005
31202
  }
31006
- const row = ctx.db.select().from(adsConnections).where(eq29(adsConnections.projectId, ctx.project.id)).get();
31203
+ const row = ctx.db.select().from(adsConnections).where(eq30(adsConnections.projectId, ctx.project.id)).get();
31007
31204
  if (!row) {
31008
31205
  return {
31009
31206
  status: CheckStatuses.skipped,
@@ -31053,7 +31250,7 @@ var adsRecentSyncCheck = {
31053
31250
  remediation: null
31054
31251
  };
31055
31252
  }
31056
- const row = ctx.db.select().from(adsConnections).where(eq29(adsConnections.projectId, ctx.project.id)).get();
31253
+ const row = ctx.db.select().from(adsConnections).where(eq30(adsConnections.projectId, ctx.project.id)).get();
31057
31254
  if (!row) {
31058
31255
  return {
31059
31256
  status: CheckStatuses.skipped,
@@ -31244,7 +31441,7 @@ var ga4ConnectionCheck = {
31244
31441
  var GA_AUTH_CHECKS = [ga4ConnectionCheck];
31245
31442
 
31246
31443
  // ../api-routes/src/doctor/checks/gbp-auth.ts
31247
- import { and as and22, eq as eq30 } from "drizzle-orm";
31444
+ import { and as and23, eq as eq31 } from "drizzle-orm";
31248
31445
  var RECENT_SYNC_WARN_DAYS2 = 7;
31249
31446
  var RECENT_SYNC_FAIL_DAYS2 = 30;
31250
31447
  function skippedNoProject3() {
@@ -31477,7 +31674,7 @@ var recentSyncCheck = {
31477
31674
  title: "GBP recent sync",
31478
31675
  run: (ctx) => {
31479
31676
  if (!ctx.project) return skippedNoProject3();
31480
- const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and22(eq30(gbpLocations.projectId, ctx.project.id), eq30(gbpLocations.selected, true))).all();
31677
+ const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and23(eq31(gbpLocations.projectId, ctx.project.id), eq31(gbpLocations.selected, true))).all();
31481
31678
  if (selected.length === 0) {
31482
31679
  return {
31483
31680
  status: CheckStatuses.skipped,
@@ -31537,7 +31734,7 @@ var GBP_AUTH_CHECK_BY_ID = Object.fromEntries(
31537
31734
  );
31538
31735
 
31539
31736
  // ../api-routes/src/doctor/checks/places.ts
31540
- import { eq as eq31 } from "drizzle-orm";
31737
+ import { eq as eq32 } from "drizzle-orm";
31541
31738
  var apiKeyCheck = {
31542
31739
  id: "gbp.places.api-key",
31543
31740
  category: CheckCategories.auth,
@@ -31582,7 +31779,7 @@ var apiKeyCheck = {
31582
31779
  details: { tier: cfg.tier }
31583
31780
  };
31584
31781
  }
31585
- const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq31(gbpLocations.projectId, ctx.project.id)).all();
31782
+ const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq32(gbpLocations.projectId, ctx.project.id)).all();
31586
31783
  const selected = rows.filter((r) => r.selected);
31587
31784
  const locationsWithPlaceId = selected.filter((r) => Boolean(r.placeId)).length;
31588
31785
  const details = {
@@ -32033,7 +32230,7 @@ var RUNTIME_STATE_CHECKS = [
32033
32230
  ];
32034
32231
 
32035
32232
  // ../api-routes/src/doctor/checks/traffic-source.ts
32036
- import { and as and23, eq as eq32, gte as gte5, ne as ne4, sql as sql12 } from "drizzle-orm";
32233
+ import { and as and24, eq as eq33, gte as gte5, ne as ne4, sql as sql12 } from "drizzle-orm";
32037
32234
  var RECENT_DATA_WARN_DAYS = 7;
32038
32235
  var RECENT_DATA_FAIL_DAYS = 30;
32039
32236
  function skippedNoProject5() {
@@ -32047,8 +32244,8 @@ function skippedNoProject5() {
32047
32244
  function loadProbes(ctx) {
32048
32245
  if (!ctx.project) return [];
32049
32246
  const rows = ctx.db.select().from(trafficSources).where(
32050
- and23(
32051
- eq32(trafficSources.projectId, ctx.project.id),
32247
+ and24(
32248
+ eq33(trafficSources.projectId, ctx.project.id),
32052
32249
  ne4(trafficSources.status, TrafficSourceStatuses.archived)
32053
32250
  )
32054
32251
  ).all();
@@ -32128,16 +32325,16 @@ var recentDataCheck = {
32128
32325
  const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
32129
32326
  const recentCrawlers = Number(
32130
32327
  ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
32131
- and23(
32132
- eq32(crawlerEventsHourly.projectId, ctx.project.id),
32328
+ and24(
32329
+ eq33(crawlerEventsHourly.projectId, ctx.project.id),
32133
32330
  gte5(crawlerEventsHourly.tsHour, warnCutoff)
32134
32331
  )
32135
32332
  ).get()?.total ?? 0
32136
32333
  );
32137
32334
  const recentReferrals = Number(
32138
32335
  ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
32139
- and23(
32140
- eq32(aiReferralEventsHourly.projectId, ctx.project.id),
32336
+ and24(
32337
+ eq33(aiReferralEventsHourly.projectId, ctx.project.id),
32141
32338
  gte5(aiReferralEventsHourly.tsHour, warnCutoff)
32142
32339
  )
32143
32340
  ).get()?.total ?? 0
@@ -32152,16 +32349,16 @@ var recentDataCheck = {
32152
32349
  }
32153
32350
  const olderCrawlers = Number(
32154
32351
  ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
32155
- and23(
32156
- eq32(crawlerEventsHourly.projectId, ctx.project.id),
32352
+ and24(
32353
+ eq33(crawlerEventsHourly.projectId, ctx.project.id),
32157
32354
  gte5(crawlerEventsHourly.tsHour, failCutoff)
32158
32355
  )
32159
32356
  ).get()?.total ?? 0
32160
32357
  );
32161
32358
  const olderReferrals = Number(
32162
32359
  ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
32163
- and23(
32164
- eq32(aiReferralEventsHourly.projectId, ctx.project.id),
32360
+ and24(
32361
+ eq33(aiReferralEventsHourly.projectId, ctx.project.id),
32165
32362
  gte5(aiReferralEventsHourly.tsHour, failCutoff)
32166
32363
  )
32167
32364
  ).get()?.total ?? 0
@@ -32575,7 +32772,7 @@ async function doctorRoutes(app, opts) {
32575
32772
 
32576
32773
  // ../api-routes/src/discovery/routes.ts
32577
32774
  import crypto26 from "crypto";
32578
- import { and as and24, desc as desc15, eq as eq33, gte as gte6, inArray as inArray11 } from "drizzle-orm";
32775
+ import { and as and25, desc as desc16, eq as eq34, gte as gte6, inArray as inArray12 } from "drizzle-orm";
32579
32776
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
32580
32777
  async function discoveryRoutes(app, opts) {
32581
32778
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -32607,16 +32804,16 @@ async function discoveryRoutes(app, opts) {
32607
32804
  const now = (/* @__PURE__ */ new Date()).toISOString();
32608
32805
  const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
32609
32806
  const decision = app.db.transaction((tx) => {
32610
- const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and24(
32611
- eq33(discoverySessions.projectId, project.id),
32612
- eq33(discoverySessions.icpDescription, icpDescription),
32613
- inArray11(discoverySessions.status, [
32807
+ const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and25(
32808
+ eq34(discoverySessions.projectId, project.id),
32809
+ eq34(discoverySessions.icpDescription, icpDescription),
32810
+ inArray12(discoverySessions.status, [
32614
32811
  DiscoverySessionStatuses.queued,
32615
32812
  DiscoverySessionStatuses.seeding,
32616
32813
  DiscoverySessionStatuses.probing
32617
32814
  ]),
32618
32815
  gte6(discoverySessions.createdAt, ageFloorIso)
32619
- )).orderBy(desc15(discoverySessions.createdAt)).get();
32816
+ )).orderBy(desc16(discoverySessions.createdAt)).get();
32620
32817
  if (existing && existing.runId) {
32621
32818
  return { reused: true, sessionId: existing.id, runId: existing.runId };
32622
32819
  }
@@ -32679,7 +32876,7 @@ async function discoveryRoutes(app, opts) {
32679
32876
  const project = resolveProject(app.db, request.params.name);
32680
32877
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
32681
32878
  const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? 50 : parsedLimit;
32682
- const rows = app.db.select().from(discoverySessions).where(eq33(discoverySessions.projectId, project.id)).orderBy(desc15(discoverySessions.createdAt)).limit(limit).all();
32879
+ const rows = app.db.select().from(discoverySessions).where(eq34(discoverySessions.projectId, project.id)).orderBy(desc16(discoverySessions.createdAt)).limit(limit).all();
32683
32880
  return reply.send(rows.map(serializeSession));
32684
32881
  }
32685
32882
  );
@@ -32687,11 +32884,11 @@ async function discoveryRoutes(app, opts) {
32687
32884
  "/projects/:name/discover/sessions/:id",
32688
32885
  async (request, reply) => {
32689
32886
  const project = resolveProject(app.db, request.params.name);
32690
- const session = app.db.select().from(discoverySessions).where(eq33(discoverySessions.id, request.params.id)).get();
32887
+ const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
32691
32888
  if (!session || session.projectId !== project.id) {
32692
32889
  throw notFound("Discovery session", request.params.id);
32693
32890
  }
32694
- const probeRows = app.db.select().from(discoveryProbes).where(eq33(discoveryProbes.sessionId, session.id)).all();
32891
+ const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
32695
32892
  const detail = {
32696
32893
  ...serializeSession(session),
32697
32894
  probes: probeRows.map(serializeProbe)
@@ -32703,12 +32900,12 @@ async function discoveryRoutes(app, opts) {
32703
32900
  "/projects/:name/discover/sessions/:id/promote",
32704
32901
  async (request, reply) => {
32705
32902
  const project = resolveProject(app.db, request.params.name);
32706
- const session = app.db.select().from(discoverySessions).where(eq33(discoverySessions.id, request.params.id)).get();
32903
+ const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
32707
32904
  if (!session || session.projectId !== project.id) {
32708
32905
  throw notFound("Discovery session", request.params.id);
32709
32906
  }
32710
- const probeRows = app.db.select().from(discoveryProbes).where(eq33(discoveryProbes.sessionId, session.id)).all();
32711
- const existingCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq33(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase());
32907
+ const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
32908
+ const existingCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase());
32712
32909
  const seenCompetitors = new Set(existingCompetitors);
32713
32910
  const cited = /* @__PURE__ */ new Set();
32714
32911
  const aspirational = /* @__PURE__ */ new Set();
@@ -32737,7 +32934,7 @@ async function discoveryRoutes(app, opts) {
32737
32934
  );
32738
32935
  app.post("/projects/:name/discover/sessions/:id/promote", async (request, reply) => {
32739
32936
  const project = resolveProject(app.db, request.params.name);
32740
- const session = app.db.select().from(discoverySessions).where(eq33(discoverySessions.id, request.params.id)).get();
32937
+ const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
32741
32938
  if (!session || session.projectId !== project.id) {
32742
32939
  throw notFound("Discovery session", request.params.id);
32743
32940
  }
@@ -32760,7 +32957,7 @@ async function discoveryRoutes(app, opts) {
32760
32957
  const bucketSet = new Set(buckets);
32761
32958
  const includeCompetitors = parsed.data.includeCompetitors ?? true;
32762
32959
  const competitorTypes = parsed.data.competitorTypes ?? DEFAULT_DISCOVERY_PROMOTE_COMPETITOR_TYPES;
32763
- const probeRows = app.db.select().from(discoveryProbes).where(eq33(discoveryProbes.sessionId, session.id)).all();
32960
+ const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
32764
32961
  const candidateQueries = /* @__PURE__ */ new Set();
32765
32962
  for (const probe of probeRows) {
32766
32963
  if (!probe.bucket) continue;
@@ -32768,7 +32965,7 @@ async function discoveryRoutes(app, opts) {
32768
32965
  if (bucket.success && bucketSet.has(bucket.data)) candidateQueries.add(probe.query);
32769
32966
  }
32770
32967
  const existingQueries = new Set(
32771
- app.db.select({ query: queries.query }).from(queries).where(eq33(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
32968
+ app.db.select({ query: queries.query }).from(queries).where(eq34(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
32772
32969
  );
32773
32970
  const promotedQueries = [];
32774
32971
  const skippedQueries = [];
@@ -32784,7 +32981,7 @@ async function discoveryRoutes(app, opts) {
32784
32981
  const skippedCompetitors = [];
32785
32982
  if (includeCompetitors) {
32786
32983
  const existingCompetitors = new Set(
32787
- app.db.select({ domain: competitors.domain }).from(competitors).where(eq33(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase())
32984
+ app.db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase())
32788
32985
  );
32789
32986
  const competitorMap = parseCompetitorMap(session.competitorMap);
32790
32987
  for (const entry of selectEligibleCompetitors(competitorMap, competitorTypes)) {
@@ -32891,7 +33088,7 @@ function selectEligibleCompetitors(competitorMap, competitorTypes) {
32891
33088
 
32892
33089
  // ../api-routes/src/discovery/orchestrate.ts
32893
33090
  import crypto27 from "crypto";
32894
- import { eq as eq34 } from "drizzle-orm";
33091
+ import { eq as eq35 } from "drizzle-orm";
32895
33092
  var DEFAULT_MAX_PROBES = 100;
32896
33093
  var ABSOLUTE_MAX_PROBES = 500;
32897
33094
  function classifyProbeBucket(input) {
@@ -32904,7 +33101,7 @@ function classifyProbeBucket(input) {
32904
33101
  }
32905
33102
  function buildCompetitorMap(probes, project, classification = {}) {
32906
33103
  const canonical = new Set(project.canonicalDomains.map((d) => d.toLowerCase()));
32907
- const counts = /* @__PURE__ */ new Map();
33104
+ const counts2 = /* @__PURE__ */ new Map();
32908
33105
  for (const probe of probes) {
32909
33106
  const seenInProbe = /* @__PURE__ */ new Set();
32910
33107
  for (const raw of probe.citedDomains) {
@@ -32912,10 +33109,10 @@ function buildCompetitorMap(probes, project, classification = {}) {
32912
33109
  if (canonical.has(domain)) continue;
32913
33110
  if (seenInProbe.has(domain)) continue;
32914
33111
  seenInProbe.add(domain);
32915
- counts.set(domain, (counts.get(domain) ?? 0) + 1);
33112
+ counts2.set(domain, (counts2.get(domain) ?? 0) + 1);
32916
33113
  }
32917
33114
  }
32918
- return Array.from(counts.entries()).map(([domain, hits]) => ({
33115
+ return Array.from(counts2.entries()).map(([domain, hits]) => ({
32919
33116
  domain,
32920
33117
  hits,
32921
33118
  competitorType: classification[domain] ?? DiscoveryCompetitorTypes.unknown
@@ -32945,7 +33142,7 @@ async function executeDiscovery(opts) {
32945
33142
  status: DiscoverySessionStatuses.seeding,
32946
33143
  dedupThreshold,
32947
33144
  startedAt
32948
- }).where(eq34(discoverySessions.id, opts.sessionId)).run();
33145
+ }).where(eq35(discoverySessions.id, opts.sessionId)).run();
32949
33146
  const seedResult = await opts.deps.seed({
32950
33147
  project: opts.project,
32951
33148
  icpDescription: opts.icpDescription,
@@ -32971,7 +33168,7 @@ async function executeDiscovery(opts) {
32971
33168
  seedCountRaw,
32972
33169
  seedCount,
32973
33170
  warning
32974
- }).where(eq34(discoverySessions.id, opts.sessionId)).run();
33171
+ }).where(eq35(discoverySessions.id, opts.sessionId)).run();
32975
33172
  const probeRows = [];
32976
33173
  const buckets = { cited: 0, aspirational: 0, "wasted-surface": 0 };
32977
33174
  for (const query of probedCanonicals) {
@@ -33012,7 +33209,7 @@ async function executeDiscovery(opts) {
33012
33209
  wastedCount: buckets["wasted-surface"],
33013
33210
  competitorMap,
33014
33211
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
33015
- }).where(eq34(discoverySessions.id, opts.sessionId)).run();
33212
+ }).where(eq35(discoverySessions.id, opts.sessionId)).run();
33016
33213
  upsertDomainClassifications(opts.db, opts.project.id, opts.sessionId, competitorMap);
33017
33214
  return {
33018
33215
  buckets,
@@ -33052,7 +33249,7 @@ function markSessionFailed(db, sessionId, error) {
33052
33249
  status: DiscoverySessionStatuses.failed,
33053
33250
  error,
33054
33251
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
33055
- }).where(eq34(discoverySessions.id, sessionId)).run();
33252
+ }).where(eq35(discoverySessions.id, sessionId)).run();
33056
33253
  }
33057
33254
  function dedupeStrings(input) {
33058
33255
  const seen = /* @__PURE__ */ new Set();
@@ -33070,7 +33267,7 @@ function dedupeStrings(input) {
33070
33267
 
33071
33268
  // ../api-routes/src/technical-aeo.ts
33072
33269
  import crypto28 from "crypto";
33073
- import { and as and25, asc as asc4, count, desc as desc16, eq as eq35, inArray as inArray12 } from "drizzle-orm";
33270
+ import { and as and26, asc as asc4, count, desc as desc17, eq as eq36, inArray as inArray13 } from "drizzle-orm";
33074
33271
  var SURFACEABLE_STATUSES = [RunStatuses.completed, RunStatuses.partial];
33075
33272
  function emptyScore(projectName) {
33076
33273
  return {
@@ -33102,12 +33299,12 @@ function parsePositiveInt(value, fallback, max) {
33102
33299
  async function technicalAeoRoutes(app, opts) {
33103
33300
  app.get("/projects/:name/technical-aeo", async (request) => {
33104
33301
  const project = resolveProject(app.db, request.params.name);
33105
- const rows = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq35(siteAuditSnapshots.runId, runs.id)).where(and25(
33106
- eq35(siteAuditSnapshots.projectId, project.id),
33107
- eq35(runs.kind, RunKinds["site-audit"]),
33108
- inArray12(runs.status, SURFACEABLE_STATUSES),
33302
+ const rows = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq36(siteAuditSnapshots.runId, runs.id)).where(and26(
33303
+ eq36(siteAuditSnapshots.projectId, project.id),
33304
+ eq36(runs.kind, RunKinds["site-audit"]),
33305
+ inArray13(runs.status, SURFACEABLE_STATUSES),
33109
33306
  notProbeRun()
33110
- )).orderBy(desc16(siteAuditSnapshots.createdAt)).limit(2).all();
33307
+ )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(2).all();
33111
33308
  const latest = rows[0];
33112
33309
  if (!latest) return emptyScore(project.name);
33113
33310
  const snap = latest.snap;
@@ -33137,24 +33334,24 @@ async function technicalAeoRoutes(app, opts) {
33137
33334
  });
33138
33335
  app.get("/projects/:name/technical-aeo/pages", async (request) => {
33139
33336
  const project = resolveProject(app.db, request.params.name);
33140
- const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq35(siteAuditSnapshots.runId, runs.id)).where(and25(
33141
- eq35(siteAuditSnapshots.projectId, project.id),
33142
- eq35(runs.kind, RunKinds["site-audit"]),
33143
- inArray12(runs.status, SURFACEABLE_STATUSES),
33337
+ const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq36(siteAuditSnapshots.runId, runs.id)).where(and26(
33338
+ eq36(siteAuditSnapshots.projectId, project.id),
33339
+ eq36(runs.kind, RunKinds["site-audit"]),
33340
+ inArray13(runs.status, SURFACEABLE_STATUSES),
33144
33341
  notProbeRun()
33145
- )).orderBy(desc16(siteAuditSnapshots.createdAt)).limit(1).get();
33342
+ )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(1).get();
33146
33343
  if (!latest) {
33147
33344
  return { project: project.name, runId: null, auditedAt: null, total: 0, pages: [] };
33148
33345
  }
33149
33346
  const statusFilter = request.query.status === "success" || request.query.status === "error" ? request.query.status : null;
33150
- const conds = [eq35(siteAuditPages.runId, latest.runId)];
33151
- if (statusFilter) conds.push(eq35(siteAuditPages.status, statusFilter));
33152
- const where = and25(...conds);
33347
+ const conds = [eq36(siteAuditPages.runId, latest.runId)];
33348
+ if (statusFilter) conds.push(eq36(siteAuditPages.status, statusFilter));
33349
+ const where = and26(...conds);
33153
33350
  const totalRow = app.db.select({ value: count() }).from(siteAuditPages).where(where).get();
33154
33351
  const total = totalRow?.value ?? 0;
33155
33352
  const limit = parsePositiveInt(request.query.limit, 100, 500);
33156
33353
  const offset = parsePositiveInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER);
33157
- const orderBy = request.query.sort === "score-desc" ? desc16(siteAuditPages.overallScore) : request.query.sort === "url" ? asc4(siteAuditPages.url) : asc4(siteAuditPages.overallScore);
33354
+ const orderBy = request.query.sort === "score-desc" ? desc17(siteAuditPages.overallScore) : request.query.sort === "url" ? asc4(siteAuditPages.url) : asc4(siteAuditPages.overallScore);
33158
33355
  const rows = app.db.select().from(siteAuditPages).where(where).orderBy(orderBy).limit(limit).offset(offset).all();
33159
33356
  const pages = rows.map((row) => ({
33160
33357
  url: row.url,
@@ -33173,12 +33370,12 @@ async function technicalAeoRoutes(app, opts) {
33173
33370
  auditedAt: siteAuditSnapshots.auditedAt,
33174
33371
  aggregateScore: siteAuditSnapshots.aggregateScore,
33175
33372
  pagesAudited: siteAuditSnapshots.pagesAudited
33176
- }).from(siteAuditSnapshots).innerJoin(runs, eq35(siteAuditSnapshots.runId, runs.id)).where(and25(
33177
- eq35(siteAuditSnapshots.projectId, project.id),
33178
- eq35(runs.kind, RunKinds["site-audit"]),
33179
- inArray12(runs.status, SURFACEABLE_STATUSES),
33373
+ }).from(siteAuditSnapshots).innerJoin(runs, eq36(siteAuditSnapshots.runId, runs.id)).where(and26(
33374
+ eq36(siteAuditSnapshots.projectId, project.id),
33375
+ eq36(runs.kind, RunKinds["site-audit"]),
33376
+ inArray13(runs.status, SURFACEABLE_STATUSES),
33180
33377
  notProbeRun()
33181
- )).orderBy(desc16(siteAuditSnapshots.createdAt)).limit(limit).all();
33378
+ )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(limit).all();
33182
33379
  return { project: project.name, points: rows.reverse() };
33183
33380
  });
33184
33381
  app.post("/projects/:name/technical-aeo/runs", async (request) => {
@@ -33187,10 +33384,10 @@ async function technicalAeoRoutes(app, opts) {
33187
33384
  if (!parsed.success) {
33188
33385
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid site-audit request");
33189
33386
  }
33190
- const existing = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and25(
33191
- eq35(runs.projectId, project.id),
33192
- eq35(runs.kind, RunKinds["site-audit"]),
33193
- inArray12(runs.status, [RunStatuses.queued, RunStatuses.running])
33387
+ const existing = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and26(
33388
+ eq36(runs.projectId, project.id),
33389
+ eq36(runs.kind, RunKinds["site-audit"]),
33390
+ inArray13(runs.status, [RunStatuses.queued, RunStatuses.running])
33194
33391
  )).get();
33195
33392
  if (existing) {
33196
33393
  return { runId: existing.id, status: existing.status };
@@ -33293,6 +33490,7 @@ async function apiRoutes(app, opts) {
33293
33490
  await api.register(intelligenceRoutes);
33294
33491
  await api.register(reportRoutes);
33295
33492
  await api.register(citationRoutes);
33493
+ await api.register(visibilityStatsRoutes);
33296
33494
  await api.register(compositeRoutes);
33297
33495
  await api.register(contentRoutes, {
33298
33496
  explainContentRecommendation: opts.explainContentRecommendation,
@@ -33776,16 +33974,16 @@ var IntelligenceService = class {
33776
33974
  */
33777
33975
  analyzeAndPersist(runId, projectId) {
33778
33976
  const recentRuns = this.db.select().from(runs).where(
33779
- and26(
33780
- eq36(runs.projectId, projectId),
33781
- or5(eq36(runs.status, "completed"), eq36(runs.status, "partial")),
33977
+ and27(
33978
+ eq37(runs.projectId, projectId),
33979
+ or5(eq37(runs.status, "completed"), eq37(runs.status, "partial")),
33782
33980
  // Defensive: RunCoordinator already skips probes before this is
33783
33981
  // called, but if a future call site invokes analyzeAndPersist
33784
33982
  // directly for a probe, probes still must not pollute the
33785
33983
  // intelligence window.
33786
33984
  ne5(runs.trigger, RunTriggers.probe)
33787
33985
  )
33788
- ).orderBy(desc17(runs.finishedAt), desc17(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
33986
+ ).orderBy(desc18(runs.finishedAt), desc18(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
33789
33987
  if (recentRuns.length === 0) {
33790
33988
  log.info("intelligence.skip", { runId, reason: "no completed runs" });
33791
33989
  return null;
@@ -33860,7 +34058,7 @@ var IntelligenceService = class {
33860
34058
  * Returns the persisted insights so the coordinator can count critical/high.
33861
34059
  */
33862
34060
  analyzeAndPersistGbp(runId, projectId) {
33863
- const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq36(runs.id, runId)).get();
34061
+ const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq37(runs.id, runId)).get();
33864
34062
  if (!runRow) {
33865
34063
  log.info("gbp-intelligence.skip", { runId, reason: "run not found" });
33866
34064
  this.persistGbpInsights(runId, projectId, [], []);
@@ -33868,9 +34066,9 @@ var IntelligenceService = class {
33868
34066
  }
33869
34067
  const windowStart = runRow.startedAt ?? runRow.createdAt;
33870
34068
  const windowEnd = runRow.finishedAt ?? (/* @__PURE__ */ new Date()).toISOString();
33871
- const selected = this.db.select().from(gbpLocations).where(and26(
33872
- eq36(gbpLocations.projectId, projectId),
33873
- eq36(gbpLocations.selected, true),
34069
+ const selected = this.db.select().from(gbpLocations).where(and27(
34070
+ eq37(gbpLocations.projectId, projectId),
34071
+ eq37(gbpLocations.selected, true),
33874
34072
  gte7(gbpLocations.syncedAt, windowStart),
33875
34073
  lte4(gbpLocations.syncedAt, windowEnd)
33876
34074
  )).all();
@@ -33905,10 +34103,10 @@ var IntelligenceService = class {
33905
34103
  }
33906
34104
  /** Build the per-location signal bundle the GBP analyzer consumes. */
33907
34105
  buildGbpLocationSignals(projectId, locationName, displayName, fallbackDate) {
33908
- const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and26(eq36(gbpDailyMetrics.projectId, projectId), eq36(gbpDailyMetrics.locationName, locationName))).all();
33909
- const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and26(eq36(gbpPlaceActions.projectId, projectId), eq36(gbpPlaceActions.locationName, locationName))).all();
33910
- const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and26(eq36(gbpLodgingSnapshots.projectId, projectId), eq36(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc17(gbpLodgingSnapshots.syncedAt)).limit(1).get();
33911
- const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and26(eq36(gbpPlaceDetails.projectId, projectId), eq36(gbpPlaceDetails.locationName, locationName))).orderBy(desc17(gbpPlaceDetails.syncedAt)).limit(1).get();
34106
+ const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and27(eq37(gbpDailyMetrics.projectId, projectId), eq37(gbpDailyMetrics.locationName, locationName))).all();
34107
+ const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and27(eq37(gbpPlaceActions.projectId, projectId), eq37(gbpPlaceActions.locationName, locationName))).all();
34108
+ const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and27(eq37(gbpLodgingSnapshots.projectId, projectId), eq37(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc18(gbpLodgingSnapshots.syncedAt)).limit(1).get();
34109
+ const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and27(eq37(gbpPlaceDetails.projectId, projectId), eq37(gbpPlaceDetails.locationName, locationName))).orderBy(desc18(gbpPlaceDetails.syncedAt)).limit(1).get();
33912
34110
  const placesAmenities = placeRow ? extractPlaceAmenities(placeRow.attributes) : [];
33913
34111
  const summary = buildGbpSummary({
33914
34112
  locationName,
@@ -33940,7 +34138,7 @@ var IntelligenceService = class {
33940
34138
  /** Build the month-over-month keyword series for a location from the
33941
34139
  * accumulating gbp_keyword_monthly table (latest complete month vs prior). */
33942
34140
  buildGbpKeywordTrend(projectId, locationName) {
33943
- const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and26(eq36(gbpKeywordMonthly.projectId, projectId), eq36(gbpKeywordMonthly.locationName, locationName))).all();
34141
+ const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and27(eq37(gbpKeywordMonthly.projectId, projectId), eq37(gbpKeywordMonthly.locationName, locationName))).all();
33944
34142
  if (rows.length === 0) return { recentMonth: null, priorMonth: null, points: [] };
33945
34143
  const months = [...new Set(rows.map((r) => r.month))].sort().reverse();
33946
34144
  const recentMonth = months[0] ?? null;
@@ -33971,7 +34169,7 @@ var IntelligenceService = class {
33971
34169
  */
33972
34170
  persistGbpInsights(runId, projectId, gbpInsights, coveredLocationNames) {
33973
34171
  const covered = new Set(coveredLocationNames);
33974
- const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and26(eq36(insights.projectId, projectId), eq36(insights.provider, GBP_INSIGHT_PROVIDER))).all();
34172
+ const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and27(eq37(insights.projectId, projectId), eq37(insights.provider, GBP_INSIGHT_PROVIDER))).all();
33975
34173
  const staleIds = [];
33976
34174
  const dismissedSlots = /* @__PURE__ */ new Set();
33977
34175
  for (const row of existing) {
@@ -33982,7 +34180,7 @@ var IntelligenceService = class {
33982
34180
  }
33983
34181
  this.db.transaction((tx) => {
33984
34182
  for (const id of staleIds) {
33985
- tx.delete(insights).where(eq36(insights.id, id)).run();
34183
+ tx.delete(insights).where(eq37(insights.id, id)).run();
33986
34184
  }
33987
34185
  for (const insight of gbpInsights) {
33988
34186
  const parsed = parseGbpInsightId(insight.id);
@@ -34060,7 +34258,7 @@ var IntelligenceService = class {
34060
34258
  * create per run + aggregate). DB is left untouched.
34061
34259
  */
34062
34260
  backfill(projectName, opts, onProgress) {
34063
- const project = this.db.select().from(projects).where(eq36(projects.name, projectName)).get();
34261
+ const project = this.db.select().from(projects).where(eq37(projects.name, projectName)).get();
34064
34262
  if (!project) {
34065
34263
  throw new Error(`Project "${projectName}" not found`);
34066
34264
  }
@@ -34073,9 +34271,9 @@ var IntelligenceService = class {
34073
34271
  sinceTimestamp = parsed;
34074
34272
  }
34075
34273
  const allRuns = this.db.select().from(runs).where(
34076
- and26(
34077
- eq36(runs.projectId, project.id),
34078
- or5(eq36(runs.status, "completed"), eq36(runs.status, "partial")),
34274
+ and27(
34275
+ eq37(runs.projectId, project.id),
34276
+ or5(eq37(runs.status, "completed"), eq37(runs.status, "partial")),
34079
34277
  // Backfill must not replay probe runs as if they were real sweeps.
34080
34278
  ne5(runs.trigger, RunTriggers.probe)
34081
34279
  )
@@ -34108,7 +34306,7 @@ var IntelligenceService = class {
34108
34306
  let wouldDeleteTotal = 0;
34109
34307
  const existingByRunId = /* @__PURE__ */ new Map();
34110
34308
  if (isDryRun && targetRuns.length > 0) {
34111
- const rows = this.db.select({ runId: insights.runId }).from(insights).where(inArray13(insights.runId, targetRuns.map((r) => r.id))).all();
34309
+ const rows = this.db.select({ runId: insights.runId }).from(insights).where(inArray14(insights.runId, targetRuns.map((r) => r.id))).all();
34112
34310
  for (const r of rows) {
34113
34311
  if (r.runId == null) continue;
34114
34312
  existingByRunId.set(r.runId, (existingByRunId.get(r.runId) ?? 0) + 1);
@@ -34154,7 +34352,7 @@ var IntelligenceService = class {
34154
34352
  return { processed, skipped, totalInsights };
34155
34353
  }
34156
34354
  loadTrackedCompetitors(projectId) {
34157
- return this.db.select({ domain: competitors.domain }).from(competitors).where(eq36(competitors.projectId, projectId)).all().map((r) => r.domain);
34355
+ return this.db.select({ domain: competitors.domain }).from(competitors).where(eq37(competitors.projectId, projectId)).all().map((r) => r.domain);
34158
34356
  }
34159
34357
  /**
34160
34358
  * Wipe transition signals from an analysis result while keeping health.
@@ -34175,15 +34373,15 @@ var IntelligenceService = class {
34175
34373
  }
34176
34374
  persistResult(result, runId, projectId) {
34177
34375
  const previouslyDismissed = /* @__PURE__ */ new Set();
34178
- const existingInsights = this.db.select({ query: insights.query, provider: insights.provider, type: insights.type, dismissed: insights.dismissed }).from(insights).where(eq36(insights.runId, runId)).all();
34376
+ const existingInsights = this.db.select({ query: insights.query, provider: insights.provider, type: insights.type, dismissed: insights.dismissed }).from(insights).where(eq37(insights.runId, runId)).all();
34179
34377
  for (const row of existingInsights) {
34180
34378
  if (row.dismissed) {
34181
34379
  previouslyDismissed.add(`${row.query}:${row.provider}:${row.type}`);
34182
34380
  }
34183
34381
  }
34184
34382
  this.db.transaction((tx) => {
34185
- tx.delete(insights).where(eq36(insights.runId, runId)).run();
34186
- tx.delete(healthSnapshots).where(eq36(healthSnapshots.runId, runId)).run();
34383
+ tx.delete(insights).where(eq37(insights.runId, runId)).run();
34384
+ tx.delete(healthSnapshots).where(eq37(healthSnapshots.runId, runId)).run();
34187
34385
  const now = (/* @__PURE__ */ new Date()).toISOString();
34188
34386
  for (const insight of result.insights) {
34189
34387
  const wasDismissed = previouslyDismissed.has(`${insight.query}:${insight.provider}:${insight.type}`);
@@ -34236,28 +34434,28 @@ var IntelligenceService = class {
34236
34434
  applySeverityTiering(rawInsights, excludeRunId, projectId) {
34237
34435
  const regressions = rawInsights.filter((i) => i.type === "regression");
34238
34436
  if (regressions.length === 0) return rawInsights;
34239
- const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq36(gscSearchData.projectId, projectId)).all();
34437
+ const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq37(gscSearchData.projectId, projectId)).all();
34240
34438
  const gscConnected = gscRows.length > 0;
34241
34439
  const gscImpressionsByQuery = /* @__PURE__ */ new Map();
34242
34440
  for (const row of gscRows) {
34243
34441
  const key = row.query.toLowerCase();
34244
34442
  gscImpressionsByQuery.set(key, (gscImpressionsByQuery.get(key) ?? 0) + row.impressions);
34245
34443
  }
34246
- const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq36(projects.id, projectId)).get();
34444
+ const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq37(projects.id, projectId)).get();
34247
34445
  const locationCount = Math.max(
34248
34446
  1,
34249
34447
  (projectRow?.locations ?? []).length
34250
34448
  );
34251
34449
  const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
34252
34450
  const recentRunRows = this.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
34253
- and26(
34254
- eq36(runs.projectId, projectId),
34255
- eq36(runs.kind, RunKinds["answer-visibility"]),
34256
- or5(eq36(runs.status, "completed"), eq36(runs.status, "partial")),
34451
+ and27(
34452
+ eq37(runs.projectId, projectId),
34453
+ eq37(runs.kind, RunKinds["answer-visibility"]),
34454
+ or5(eq37(runs.status, "completed"), eq37(runs.status, "partial")),
34257
34455
  // Defensive — see top of file.
34258
34456
  ne5(runs.trigger, RunTriggers.probe)
34259
34457
  )
34260
- ).orderBy(desc17(runs.createdAt), desc17(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
34458
+ ).orderBy(desc18(runs.createdAt), desc18(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
34261
34459
  const recentGroups = groupRunsByCreatedAt(recentRunRows);
34262
34460
  const recentRunIds = [];
34263
34461
  const recentRunIdToCreatedAt = /* @__PURE__ */ new Map();
@@ -34273,7 +34471,7 @@ var IntelligenceService = class {
34273
34471
  const haveHistory = recentRunIds.length > 0;
34274
34472
  const priorRegressionsByPair = /* @__PURE__ */ new Map();
34275
34473
  if (haveHistory) {
34276
- const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and26(eq36(insights.type, "regression"), inArray13(insights.runId, recentRunIds))).all();
34474
+ const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and27(eq37(insights.type, "regression"), inArray14(insights.runId, recentRunIds))).all();
34277
34475
  const regressionGroups = /* @__PURE__ */ new Map();
34278
34476
  for (const row of priorRows) {
34279
34477
  if (!row.runId) continue;
@@ -34302,7 +34500,7 @@ var IntelligenceService = class {
34302
34500
  });
34303
34501
  }
34304
34502
  buildRunData(runId, projectId, completedAt, location = null) {
34305
- const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq36(projects.id, projectId)).get();
34503
+ const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq37(projects.id, projectId)).get();
34306
34504
  const projectDomains = projectDomainRow ? effectiveDomains({
34307
34505
  canonicalDomain: projectDomainRow.canonicalDomain,
34308
34506
  ownedDomains: projectDomainRow.ownedDomains
@@ -34319,7 +34517,7 @@ var IntelligenceService = class {
34319
34517
  citedDomains: querySnapshots.citedDomains,
34320
34518
  competitorOverlap: querySnapshots.competitorOverlap,
34321
34519
  snapshotLocation: querySnapshots.location
34322
- }).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(eq36(querySnapshots.runId, runId)).all();
34520
+ }).from(querySnapshots).leftJoin(queries, eq37(querySnapshots.queryId, queries.id)).where(eq37(querySnapshots.runId, runId)).all();
34323
34521
  const snapshots = [];
34324
34522
  let orphanCount = 0;
34325
34523
  for (const r of rows) {