@ainyc/canonry 4.84.0 → 4.86.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 +22 -1
  3. package/assets/assets/{BacklinksPage-OrSg_iPA.js → BacklinksPage-BNrvc-gV.js} +1 -1
  4. package/assets/assets/{ChartPrimitives-DPBhAT_r.js → ChartPrimitives-BlIkdUdy.js} +1 -1
  5. package/assets/assets/{ProjectPage-CpMcEmtw.js → ProjectPage-CAyx_xNr.js} +2 -2
  6. package/assets/assets/{RunRow-2Rty0BAH.js → RunRow-CAPnKzi7.js} +1 -1
  7. package/assets/assets/{RunsPage-B3ahqf8s.js → RunsPage-idnuzKBn.js} +1 -1
  8. package/assets/assets/{SettingsPage-BIjeI85q.js → SettingsPage-Bka67uJq.js} +1 -1
  9. package/assets/assets/{TrafficPage-DjGoj691.js → TrafficPage-C_o-rA5o.js} +1 -1
  10. package/assets/assets/{TrafficSourceDetailPage-BgKG-2q3.js → TrafficSourceDetailPage-D_jvoSTV.js} +1 -1
  11. package/assets/assets/{arrow-left-Cf7wmru1.js → arrow-left-B-JfzARi.js} +1 -1
  12. package/assets/assets/{extract-error-message-CANxezte.js → extract-error-message-BhPbjIX6.js} +1 -1
  13. package/assets/assets/{index-CGlPx_cu.js → index-uPSrDA8e.js} +77 -77
  14. package/assets/assets/{trash-2-6nHJZrvy.js → trash-2-BbRvn40h.js} +1 -1
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-BNF3HXBW.js → chunk-23HGQV22.js} +1460 -1149
  17. package/dist/{chunk-VJBO4VIK.js → chunk-DLBQU3VG.js} +712 -423
  18. package/dist/{chunk-Y3O3HBMN.js → chunk-LLJPZKHG.js} +94 -1
  19. package/dist/{chunk-M3IYKTSF.js → chunk-SELXBOAP.js} +19 -4
  20. package/dist/cli.js +210 -20
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-PDIAMP5I.js → intelligence-service-ZHUJKZRO.js} +2 -2
  23. package/dist/mcp.js +2 -2
  24. package/package.json +10 -10
@@ -46,8 +46,10 @@ import {
46
46
  adsSummaryDtoSchema,
47
47
  adsSyncResponseSchema,
48
48
  agentProvidersResponseDtoSchema,
49
+ aggregateHarvestedQueries,
49
50
  apiKeyDtoSchema,
50
51
  apiKeyListDtoSchema,
52
+ applyHarvestSemanticNovelty,
51
53
  auditLogEntrySchema,
52
54
  authInvalid,
53
55
  authRequired,
@@ -70,6 +72,7 @@ import {
70
72
  brandKeyFromText,
71
73
  brandLabelFromDomain,
72
74
  brandMetricsDtoSchema,
75
+ buildHarvestAnchorTerms,
73
76
  categorizeSource,
74
77
  categorizeSourceWithCompetitors,
75
78
  categoryLabel,
@@ -104,6 +107,7 @@ import {
104
107
  deriveWinnabilityClass,
105
108
  determineAnswerMentioned,
106
109
  discoveryBucketSchema,
110
+ discoveryHarvestDtoSchema,
107
111
  discoveryPromotePreviewSchema,
108
112
  discoveryPromoteRequestSchema,
109
113
  discoveryPromoteResultSchema,
@@ -130,6 +134,7 @@ import {
130
134
  ga4SocialReferralHistoryEntrySchema,
131
135
  ga4StatusDtoSchema,
132
136
  ga4SyncResponseDtoSchema,
137
+ gateHarvestedSearchQueries,
133
138
  gbpAccountListResponseSchema,
134
139
  gbpDailyMetricListResponseSchema,
135
140
  gbpDiscoverRequestSchema,
@@ -169,6 +174,7 @@ import {
169
174
  notFound,
170
175
  notImplemented,
171
176
  notificationDtoSchema,
177
+ parseInclusiveEndMs,
172
178
  parseRunError,
173
179
  parseWindow,
174
180
  pickClusterRepresentative,
@@ -230,6 +236,7 @@ import {
230
236
  unsupportedKind,
231
237
  validationError,
232
238
  visibilityStateFromAnswerMentioned,
239
+ visibilityStatsDtoSchema,
233
240
  windowCutoff,
234
241
  winnabilityClassLabel,
235
242
  winnabilityClassSchema,
@@ -246,10 +253,10 @@ import {
246
253
  wordpressSchemaDeployResultDtoSchema,
247
254
  wordpressSchemaStatusResultDtoSchema,
248
255
  wordpressStatusDtoSchema
249
- } from "./chunk-BNF3HXBW.js";
256
+ } from "./chunk-23HGQV22.js";
250
257
 
251
258
  // 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";
259
+ 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
260
 
254
261
  // ../db/src/client.ts
255
262
  import { mkdirSync } from "fs";
@@ -4339,18 +4346,18 @@ function buildCitationScorecard(snapshots, queryLookup) {
4339
4346
  answerMentioned: snap.answerMentioned ?? null,
4340
4347
  model: snap.model
4341
4348
  };
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);
4349
+ const counts2 = providerCounts.get(snap.provider) ?? { cited: 0, total: 0 };
4350
+ counts2.total++;
4351
+ if (snap.citationState === CitationStates.cited) counts2.cited++;
4352
+ providerCounts.set(snap.provider, counts2);
4346
4353
  }
4347
4354
  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;
4355
+ const counts2 = providerCounts.get(provider) ?? { cited: 0, total: 0 };
4356
+ const citationRate = counts2.total > 0 ? Math.round(counts2.cited / counts2.total * 100) : 0;
4350
4357
  return {
4351
4358
  provider,
4352
- citedCount: counts.cited,
4353
- totalCount: counts.total,
4359
+ citedCount: counts2.cited,
4360
+ totalCount: counts2.total,
4354
4361
  citationRate
4355
4362
  };
4356
4363
  });
@@ -7918,13 +7925,13 @@ function buildRankedList(domains, limit) {
7918
7925
  bySurfaceClass
7919
7926
  };
7920
7927
  }
7921
- function buildCategoryCounts(counts) {
7928
+ function buildCategoryCounts(counts2) {
7922
7929
  let grandTotal = 0;
7923
- for (const domains of counts.values()) {
7930
+ for (const domains of counts2.values()) {
7924
7931
  for (const count2 of domains.values()) grandTotal += count2;
7925
7932
  }
7926
7933
  const result = [];
7927
- for (const [category, domains] of counts) {
7934
+ for (const [category, domains] of counts2) {
7928
7935
  let categoryTotal = 0;
7929
7936
  const domainEntries = [];
7930
7937
  for (const [domain, count2] of domains) {
@@ -11738,19 +11745,19 @@ function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
11738
11745
  considered++;
11739
11746
  if (snap.citationState === CitationStates.cited) citedQueryIds.add(snap.queryId);
11740
11747
  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);
11748
+ const counts2 = providerCounts.get(snap.provider) ?? { cited: 0, total: 0 };
11749
+ counts2.total++;
11750
+ if (snap.citationState === CitationStates.cited) counts2.cited++;
11751
+ providerCounts.set(snap.provider, counts2);
11745
11752
  }
11746
11753
  if (considered === 0) continue;
11747
11754
  const citedQueryCount = citedQueryIds.size;
11748
11755
  const mentionedQueryCount = mentionedQueryIds.size;
11749
11756
  const citationRate = totalQueries > 0 ? Math.round(citedQueryCount / totalQueries * 100) : 0;
11750
11757
  const mentionRate = totalQueries > 0 ? Math.round(mentionedQueryCount / totalQueries * 100) : 0;
11751
- const providerRates = [...providerCounts.entries()].map(([provider, counts]) => ({
11758
+ const providerRates = [...providerCounts.entries()].map(([provider, counts2]) => ({
11752
11759
  provider,
11753
- citationRate: counts.total > 0 ? Math.round(counts.cited / counts.total * 100) : 0
11760
+ citationRate: counts2.total > 0 ? Math.round(counts2.cited / counts2.total * 100) : 0
11754
11761
  })).sort((a, b) => a.provider.localeCompare(b.provider));
11755
11762
  points.push({
11756
11763
  runId: run.id,
@@ -12720,8 +12727,165 @@ function normalizeDomain2(domain) {
12720
12727
  return domain.toLowerCase().trim().replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/$/, "");
12721
12728
  }
12722
12729
 
12730
+ // ../api-routes/src/visibility-stats.ts
12731
+ import { and as and11, desc as desc8, eq as eq16, inArray as inArray8 } from "drizzle-orm";
12732
+ function round42(value) {
12733
+ return Math.round(value * 1e4) / 1e4;
12734
+ }
12735
+ function emptyAgg() {
12736
+ return { total: 0, checked: 0, mentioned: 0, cited: 0, first: null, last: null };
12737
+ }
12738
+ function addSnapshot(agg, snap) {
12739
+ agg.total++;
12740
+ if (snap.answerMentioned === true || snap.answerMentioned === false) agg.checked++;
12741
+ if (snap.answerMentioned === true) agg.mentioned++;
12742
+ if (snap.citationState === CitationStates.cited) agg.cited++;
12743
+ if (agg.first === null || snap.createdAt < agg.first) agg.first = snap.createdAt;
12744
+ if (agg.last === null || snap.createdAt > agg.last) agg.last = snap.createdAt;
12745
+ }
12746
+ function counts(agg) {
12747
+ return {
12748
+ total: agg.total,
12749
+ checked: agg.checked,
12750
+ mentioned: agg.mentioned,
12751
+ cited: agg.cited,
12752
+ // mention proportion is over the CHECKED sample; citation proportion is
12753
+ // over the full total (every snapshot is checked for citation).
12754
+ mentionRate: agg.checked > 0 ? round42(agg.mentioned / agg.checked) : null,
12755
+ citedRate: agg.total > 0 ? round42(agg.cited / agg.total) : null
12756
+ };
12757
+ }
12758
+ function providerEntries(byProvider) {
12759
+ return [...byProvider.entries()].map(([provider, agg]) => ({
12760
+ provider,
12761
+ ...counts(agg),
12762
+ // first/last are non-null once at least one snapshot landed in the agg,
12763
+ // which is guaranteed for any provider that made it into the map.
12764
+ firstObserved: agg.first ?? "",
12765
+ lastObserved: agg.last ?? ""
12766
+ })).sort((a, b) => a.provider.localeCompare(b.provider));
12767
+ }
12768
+ function computeVisibilityStats(input) {
12769
+ const { groupBy } = input;
12770
+ const wantProviders = groupBy === "provider";
12771
+ const queryById = /* @__PURE__ */ new Map();
12772
+ const queryByText = /* @__PURE__ */ new Map();
12773
+ for (const q of input.queries) {
12774
+ queryById.set(q.id, q);
12775
+ queryByText.set(q.query, q);
12776
+ }
12777
+ const byQuery = /* @__PURE__ */ new Map();
12778
+ const totals = emptyAgg();
12779
+ const totalsByProvider = /* @__PURE__ */ new Map();
12780
+ for (const snap of input.snapshots) {
12781
+ let resolved;
12782
+ if (snap.queryId && queryById.has(snap.queryId)) resolved = queryById.get(snap.queryId);
12783
+ else if (snap.queryText && queryByText.has(snap.queryText)) resolved = queryByText.get(snap.queryText);
12784
+ if (!resolved) continue;
12785
+ let bucket = byQuery.get(resolved.id);
12786
+ if (!bucket) {
12787
+ bucket = { id: resolved.id, query: resolved.query, agg: emptyAgg(), byProvider: /* @__PURE__ */ new Map() };
12788
+ byQuery.set(resolved.id, bucket);
12789
+ }
12790
+ addSnapshot(bucket.agg, snap);
12791
+ addSnapshot(totals, snap);
12792
+ if (wantProviders) {
12793
+ const qpAgg = bucket.byProvider.get(snap.provider) ?? emptyAgg();
12794
+ addSnapshot(qpAgg, snap);
12795
+ bucket.byProvider.set(snap.provider, qpAgg);
12796
+ const tpAgg = totalsByProvider.get(snap.provider) ?? emptyAgg();
12797
+ addSnapshot(tpAgg, snap);
12798
+ totalsByProvider.set(snap.provider, tpAgg);
12799
+ }
12800
+ }
12801
+ const queryEntries = [...byQuery.values()].map((bucket) => ({
12802
+ queryId: bucket.id,
12803
+ query: bucket.query,
12804
+ ...counts(bucket.agg),
12805
+ firstObserved: bucket.agg.first ?? "",
12806
+ lastObserved: bucket.agg.last ?? "",
12807
+ ...wantProviders ? { providers: providerEntries(bucket.byProvider) } : {}
12808
+ })).sort((a, b) => a.query.localeCompare(b.query));
12809
+ return {
12810
+ totals: counts(totals),
12811
+ ...wantProviders ? { byProvider: providerEntries(totalsByProvider) } : {},
12812
+ queries: queryEntries
12813
+ };
12814
+ }
12815
+ async function visibilityStatsRoutes(app) {
12816
+ app.get("/projects/:name/visibility-stats", async (request, reply) => {
12817
+ const project = resolveProject(app.db, request.params.name);
12818
+ const { since: sinceRaw, until: untilRaw, lastRuns: lastRunsRaw, groupBy: groupByRaw } = request.query;
12819
+ let groupBy = null;
12820
+ if (groupByRaw !== void 0 && groupByRaw !== "") {
12821
+ if (groupByRaw !== "provider") throw validationError('"groupBy" must be "provider"');
12822
+ groupBy = "provider";
12823
+ }
12824
+ const hasSince = sinceRaw !== void 0 && sinceRaw !== "";
12825
+ const hasUntil = untilRaw !== void 0 && untilRaw !== "";
12826
+ const hasLastRuns = lastRunsRaw !== void 0 && lastRunsRaw !== "";
12827
+ if (hasLastRuns && (hasSince || hasUntil)) {
12828
+ throw validationError('"lastRuns" cannot be combined with "since"/"until" \u2014 use one or the other');
12829
+ }
12830
+ let sinceMs = null;
12831
+ let untilMs = null;
12832
+ if (hasSince) {
12833
+ const ms = Date.parse(sinceRaw);
12834
+ if (Number.isNaN(ms)) throw validationError('"since" must be an ISO 8601 date/time');
12835
+ sinceMs = ms;
12836
+ }
12837
+ if (hasUntil) {
12838
+ const ms = parseInclusiveEndMs(untilRaw);
12839
+ if (ms === null) throw validationError('"until" must be an ISO 8601 date/time');
12840
+ untilMs = ms;
12841
+ }
12842
+ if (sinceMs !== null && untilMs !== null && untilMs < sinceMs) {
12843
+ throw validationError('"until" must be on or after "since"');
12844
+ }
12845
+ let lastRuns = null;
12846
+ if (hasLastRuns) {
12847
+ const n = Number(lastRunsRaw);
12848
+ if (!Number.isInteger(n) || n <= 0) throw validationError('"lastRuns" must be a positive integer');
12849
+ lastRuns = n;
12850
+ }
12851
+ const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq16(queries.projectId, project.id)).all();
12852
+ 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);
12853
+ if (sinceMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) >= sinceMs);
12854
+ if (untilMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) <= untilMs);
12855
+ if (lastRuns !== null) projectRuns = projectRuns.slice(0, lastRuns);
12856
+ const runCount = projectRuns.length;
12857
+ const runIds = projectRuns.map((r) => r.id);
12858
+ const snapshots = runIds.length > 0 && projectQueries.length > 0 ? app.db.select({
12859
+ queryId: querySnapshots.queryId,
12860
+ queryText: querySnapshots.queryText,
12861
+ provider: querySnapshots.provider,
12862
+ citationState: querySnapshots.citationState,
12863
+ answerMentioned: querySnapshots.answerMentioned,
12864
+ createdAt: querySnapshots.createdAt
12865
+ }).from(querySnapshots).where(inArray8(querySnapshots.runId, runIds)).all() : [];
12866
+ const stats = computeVisibilityStats({ queries: projectQueries, snapshots, groupBy });
12867
+ const response = {
12868
+ project: project.name,
12869
+ window: {
12870
+ since: hasSince ? sinceRaw : null,
12871
+ until: hasUntil ? untilRaw : null,
12872
+ lastRuns,
12873
+ runCount
12874
+ },
12875
+ totals: stats.totals,
12876
+ queries: stats.queries,
12877
+ // `groupBy` + `byProvider` appear together only when a breakdown was
12878
+ // requested; both are OMITTED otherwise (absent = no breakdown) so the
12879
+ // SDK types `groupBy` as `groupBy?: 'provider'` rather than a misleading
12880
+ // always-present literal.
12881
+ ...groupBy === "provider" ? { groupBy, byProvider: stats.byProvider ?? [] } : {}
12882
+ };
12883
+ return reply.send(response);
12884
+ });
12885
+ }
12886
+
12723
12887
  // ../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";
12888
+ import { eq as eq17, and as and12, desc as desc9, sql as sql7, like, or as or4, inArray as inArray9 } from "drizzle-orm";
12725
12889
  var TOP_INSIGHT_LIMIT = 5;
12726
12890
  var SEARCH_HIT_HARD_LIMIT = 50;
12727
12891
  var SEARCH_SNIPPET_RADIUS = 80;
@@ -12739,7 +12903,7 @@ async function compositeRoutes(app) {
12739
12903
  const project = resolveProject(app.db, request.params.name);
12740
12904
  const filterLocation = (request.query.location ?? "").trim() || null;
12741
12905
  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();
12906
+ const allRunsRaw = app.db.select().from(runs).where(and12(eq17(runs.projectId, project.id), notProbeRun())).orderBy(desc9(runs.createdAt), desc9(runs.id)).all();
12743
12907
  const allRuns = allRunsRaw.filter((r) => runMatchesFilters(r, filterLocation, sinceIso));
12744
12908
  const totalRuns = allRuns.length;
12745
12909
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
@@ -12752,9 +12916,9 @@ async function compositeRoutes(app) {
12752
12916
  const previousVisibilityRun = pickGroupRepresentative(previousVisRunGroup);
12753
12917
  const latestRunRow = allRuns[0] ?? null;
12754
12918
  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();
12919
+ const healthRow = app.db.select().from(healthSnapshots).where(eq17(healthSnapshots.projectId, project.id)).orderBy(desc9(healthSnapshots.createdAt)).limit(1).get();
12756
12920
  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();
12921
+ const insightRows = app.db.select().from(insights).where(eq17(insights.projectId, project.id)).orderBy(desc9(insights.createdAt)).all();
12758
12922
  const topInsights = insightRows.filter((row) => !row.dismissed).slice(0, TOP_INSIGHT_LIMIT).map(mapInsightRow2);
12759
12923
  const sparklineRunIds = visibilityRuns.slice(0, DEFAULT_RUN_HISTORY_LIMIT).map((r) => r.id);
12760
12924
  const snapshotRunIds = new Set(sparklineRunIds);
@@ -12769,8 +12933,8 @@ async function compositeRoutes(app) {
12769
12933
  previousSnapshots,
12770
12934
  previousVisibilityRun?.createdAt ?? null
12771
12935
  );
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();
12936
+ const competitorRows = app.db.select().from(competitors).where(eq17(competitors.projectId, project.id)).all();
12937
+ const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq17(queries.projectId, project.id)).all();
12774
12938
  const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
12775
12939
  const configuredApiProviders = project.providers.filter((p) => !p.startsWith("cdp:"));
12776
12940
  const mentionShareCompetitors = competitorRows.map((c) => ({
@@ -12869,9 +13033,9 @@ async function compositeRoutes(app) {
12869
13033
  citedDomains: querySnapshots.citedDomains,
12870
13034
  rawResponse: querySnapshots.rawResponse,
12871
13035
  createdAt: querySnapshots.createdAt
12872
- }).from(querySnapshots).innerJoin(queries, eq16(querySnapshots.queryId, queries.id)).where(
12873
- and11(
12874
- eq16(queries.projectId, project.id),
13036
+ }).from(querySnapshots).innerJoin(queries, eq17(querySnapshots.queryId, queries.id)).where(
13037
+ and12(
13038
+ eq17(queries.projectId, project.id),
12875
13039
  or4(
12876
13040
  sql7`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
12877
13041
  sql7`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
@@ -12879,10 +13043,10 @@ async function compositeRoutes(app) {
12879
13043
  like(queries.query, pattern)
12880
13044
  )
12881
13045
  )
12882
- ).orderBy(desc8(querySnapshots.createdAt)).limit(limit + 1).all());
13046
+ ).orderBy(desc9(querySnapshots.createdAt)).limit(limit + 1).all());
12883
13047
  const insightMatches = app.db.select().from(insights).where(
12884
- and11(
12885
- eq16(insights.projectId, project.id),
13048
+ and12(
13049
+ eq17(insights.projectId, project.id),
12886
13050
  or4(
12887
13051
  like(insights.title, pattern),
12888
13052
  like(insights.query, pattern),
@@ -12890,7 +13054,7 @@ async function compositeRoutes(app) {
12890
13054
  sql7`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
12891
13055
  )
12892
13056
  )
12893
- ).orderBy(desc8(insights.createdAt)).limit(limit + 1).all();
13057
+ ).orderBy(desc9(insights.createdAt)).limit(limit + 1).all();
12894
13058
  const hits = [];
12895
13059
  for (const row of snapshotMatches) {
12896
13060
  hits.push(buildSnapshotHit(row, rawQuery));
@@ -12960,7 +13124,7 @@ function loadSnapshotsByRunIds(app, runIds) {
12960
13124
  answerText: querySnapshots.answerText,
12961
13125
  competitorOverlap: querySnapshots.competitorOverlap,
12962
13126
  citedDomains: querySnapshots.citedDomains
12963
- }).from(querySnapshots).where(inArray8(querySnapshots.runId, [...runIds])).all());
13127
+ }).from(querySnapshots).where(inArray9(querySnapshots.runId, [...runIds])).all());
12964
13128
  for (const row of rows) {
12965
13129
  const list = result.get(row.runId) ?? [];
12966
13130
  list.push({
@@ -13078,8 +13242,8 @@ function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
13078
13242
  // NULLIF guards the degenerate impressions=0 case (SQLite returns NULL,
13079
13243
  // which the JS coerces to 0 — caught by the impression floor anyway).
13080
13244
  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),
13245
+ }).from(gscSearchData).where(and12(
13246
+ eq17(gscSearchData.projectId, projectId),
13083
13247
  sql7`${gscSearchData.date} >= ${cutoff}`,
13084
13248
  sql7`${gscSearchData.impressions} > 0`
13085
13249
  )).groupBy(gscSearchData.query).orderBy(sql7`SUM(${gscSearchData.impressions}) DESC`).limit(100).all();
@@ -13102,8 +13266,8 @@ function buildIndexCoverageScore(app, projectId) {
13102
13266
  tooltip,
13103
13267
  trend: []
13104
13268
  };
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();
13269
+ const gscRow = app.db.select().from(gscCoverageSnapshots).where(eq17(gscCoverageSnapshots.projectId, projectId)).orderBy(desc9(gscCoverageSnapshots.date)).limit(1).get();
13270
+ const bingRow = app.db.select().from(bingCoverageSnapshots).where(eq17(bingCoverageSnapshots.projectId, projectId)).orderBy(desc9(bingCoverageSnapshots.date)).limit(1).get();
13107
13271
  const chosen = pickIndexCoverageRow(gscRow, bingRow);
13108
13272
  if (!chosen) return empty;
13109
13273
  const total = chosen.indexed + chosen.notIndexed;
@@ -13129,7 +13293,7 @@ function countGoogleDeindexedUrls(app, projectId) {
13129
13293
  url: gscUrlInspections.url,
13130
13294
  indexingState: gscUrlInspections.indexingState,
13131
13295
  inspectedAt: gscUrlInspections.inspectedAt
13132
- }).from(gscUrlInspections).where(eq16(gscUrlInspections.projectId, projectId)).orderBy(desc8(gscUrlInspections.inspectedAt)).all();
13296
+ }).from(gscUrlInspections).where(eq17(gscUrlInspections.projectId, projectId)).orderBy(desc9(gscUrlInspections.inspectedAt)).all();
13133
13297
  if (rows.length === 0) return 0;
13134
13298
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
13135
13299
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -13418,6 +13582,7 @@ var SCHEMA_TABLE = {
13418
13582
  DomainClassificationsResponseDto: domainClassificationsResponseDtoSchema,
13419
13583
  RecommendationBriefDto: recommendationBriefDtoSchema,
13420
13584
  RecommendationExplanationDto: recommendationExplanationDtoSchema,
13585
+ DiscoveryHarvestDto: discoveryHarvestDtoSchema,
13421
13586
  DiscoveryPromotePreview: discoveryPromotePreviewSchema,
13422
13587
  DiscoveryPromoteResult: discoveryPromoteResultSchema,
13423
13588
  DiscoverySessionDetailDto: discoverySessionDetailDtoSchema,
@@ -13475,6 +13640,7 @@ var SCHEMA_TABLE = {
13475
13640
  TrafficSourceListResponse: trafficSourceListResponseSchema,
13476
13641
  TrafficStatusResponse: trafficStatusResponseSchema,
13477
13642
  TrafficSyncResponse: trafficSyncResponseSchema,
13643
+ VisibilityStatsDto: visibilityStatsDtoSchema,
13478
13644
  WordpressAuditPageDto: wordpressAuditPageDtoSchema,
13479
13645
  WordpressBulkMetaResultDto: wordpressBulkMetaResultDtoSchema,
13480
13646
  WordpressDiffDto: wordpressDiffDtoSchema,
@@ -13700,6 +13866,30 @@ var analyticsWindowParameter = {
13700
13866
  description: "Time window for analytics queries.",
13701
13867
  schema: { type: "string", enum: ["7d", "30d", "90d", "all"] }
13702
13868
  };
13869
+ var sinceQueryParameter = {
13870
+ name: "since",
13871
+ in: "query",
13872
+ 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".',
13873
+ schema: stringSchema
13874
+ };
13875
+ var untilQueryParameter = {
13876
+ name: "until",
13877
+ in: "query",
13878
+ 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".',
13879
+ schema: stringSchema
13880
+ };
13881
+ var lastRunsQueryParameter = {
13882
+ name: "lastRuns",
13883
+ in: "query",
13884
+ description: 'Aggregate only the most recent N answer-visibility runs. Mutually exclusive with "since"/"until".',
13885
+ schema: integerSchema
13886
+ };
13887
+ var groupByProviderQueryParameter = {
13888
+ name: "groupBy",
13889
+ in: "query",
13890
+ description: 'Set to "provider" to include a per-provider breakdown whose counts sum to the pooled counts.',
13891
+ schema: { type: "string", enum: ["provider"] }
13892
+ };
13703
13893
  var wordpressEnvQueryParameter = {
13704
13894
  name: "env",
13705
13895
  in: "query",
@@ -14426,6 +14616,19 @@ var routeCatalog = [
14426
14616
  404: errorResponse("Project not found.")
14427
14617
  }
14428
14618
  },
14619
+ {
14620
+ method: "get",
14621
+ path: "/api/v1/projects/{name}/visibility-stats",
14622
+ summary: "Get aggregated mention/citation stats per query",
14623
+ 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.",
14624
+ tags: ["analytics"],
14625
+ parameters: [nameParameter, sinceQueryParameter, untilQueryParameter, lastRunsQueryParameter, groupByProviderQueryParameter],
14626
+ responses: {
14627
+ 200: jsonResponse("Aggregated visibility stats returned.", "VisibilityStatsDto"),
14628
+ 400: errorResponse("Invalid query parameters."),
14629
+ 404: errorResponse("Project not found.")
14630
+ }
14631
+ },
14429
14632
  {
14430
14633
  method: "get",
14431
14634
  path: "/api/v1/projects/{name}/snapshots/diff",
@@ -17235,6 +17438,23 @@ var routeCatalog = [
17235
17438
  404: errorResponse("Project or session not found.")
17236
17439
  }
17237
17440
  },
17441
+ {
17442
+ method: "get",
17443
+ path: "/api/v1/projects/{name}/discover/sessions/{id}/harvest",
17444
+ summary: "Harvest issued search queries (grounding fan-out) from a session",
17445
+ description: "Reads the search queries the answer engine actually issued to answer each probe (Gemini's `groundingMetadata.webSearchQueries` fan-out) back out of the session's stored probe payloads, then runs a mandatory quality gate and returns the survivors as candidate seeds, ranked by how many distinct probes issued each one. The gate drops navigational/phone lookups, over-specific outliers, off-subject acronym collisions, exact already-tracked matches, and \u2014 via an embedding cosine pass over the project's tracked queries \u2014 semantic duplicates (paraphrases/synonyms an exact match can't see). `semanticNoveltyApplied` reports whether that embedding pass ran (it falls back to exact-match when embeddings are unavailable). These are a THIRD signal \u2014 *issued retrieval queries* \u2014 distinct from `mention` (answer text) and `cited` (source list); they carry no demand of their own. Read-only and derived: nothing is probed, tracked, or promoted. `minProbeHits` raises the recurrence floor; `anchor=false` disables the subject anchor for new-subject discovery on a well-scoped project. `stats` carries the raw count and a per-reason rejection tally. Issue #713.",
17446
+ tags: ["discovery"],
17447
+ parameters: [
17448
+ nameParameter,
17449
+ { name: "id", in: "path", required: true, description: "Discovery session ID.", schema: stringSchema },
17450
+ { name: "minProbeHits", in: "query", required: false, description: "Minimum number of distinct probes a candidate must appear in to be admitted (recurrence floor). Default 1.", schema: stringSchema },
17451
+ { name: "anchor", in: "query", required: false, description: 'Set to "false" to disable the subject-anchor filter. Default applies it (when the subject corpus is rich enough).', schema: stringSchema }
17452
+ ],
17453
+ responses: {
17454
+ 200: jsonResponse("Harvested candidate seeds + gate stats returned.", "DiscoveryHarvestDto"),
17455
+ 404: errorResponse("Project or session not found.")
17456
+ }
17457
+ },
17238
17458
  {
17239
17459
  method: "get",
17240
17460
  path: "/api/v1/projects/{name}/discover/sessions/{id}/promote",
@@ -17691,7 +17911,7 @@ async function settingsRoutes(app, opts) {
17691
17911
 
17692
17912
  // ../api-routes/src/keys.ts
17693
17913
  import crypto12 from "crypto";
17694
- import { desc as desc9, eq as eq17 } from "drizzle-orm";
17914
+ import { desc as desc10, eq as eq18 } from "drizzle-orm";
17695
17915
  var KEYS_WRITE_SCOPE = "keys.write";
17696
17916
  function toApiKeyDto(row) {
17697
17917
  const scopes = Array.isArray(row.scopes) ? row.scopes : [];
@@ -17708,7 +17928,7 @@ function toApiKeyDto(row) {
17708
17928
  }
17709
17929
  async function keysRoutes(app) {
17710
17930
  app.get("/keys", async () => {
17711
- const rows = app.db.select().from(apiKeys).orderBy(desc9(apiKeys.createdAt)).all();
17931
+ const rows = app.db.select().from(apiKeys).orderBy(desc10(apiKeys.createdAt)).all();
17712
17932
  return { keys: rows.map(toApiKeyDto) };
17713
17933
  });
17714
17934
  app.get("/keys/self", async (request) => {
@@ -17716,7 +17936,7 @@ async function keysRoutes(app) {
17716
17936
  if (!id) {
17717
17937
  throw notFound("API key", "self");
17718
17938
  }
17719
- const row = app.db.select().from(apiKeys).where(eq17(apiKeys.id, id)).get();
17939
+ const row = app.db.select().from(apiKeys).where(eq18(apiKeys.id, id)).get();
17720
17940
  if (!row) {
17721
17941
  throw notFound("API key", id);
17722
17942
  }
@@ -17768,7 +17988,7 @@ async function keysRoutes(app) {
17768
17988
  app.post("/keys/:id/revoke", async (request) => {
17769
17989
  requireScope(request, KEYS_WRITE_SCOPE);
17770
17990
  const { id } = request.params;
17771
- const row = app.db.select().from(apiKeys).where(eq17(apiKeys.id, id)).get();
17991
+ const row = app.db.select().from(apiKeys).where(eq18(apiKeys.id, id)).get();
17772
17992
  if (!row) {
17773
17993
  throw notFound("API key", id);
17774
17994
  }
@@ -17780,7 +18000,7 @@ async function keysRoutes(app) {
17780
18000
  }
17781
18001
  const now = (/* @__PURE__ */ new Date()).toISOString();
17782
18002
  app.db.transaction((tx) => {
17783
- tx.update(apiKeys).set({ revokedAt: now }).where(eq17(apiKeys.id, id)).run();
18003
+ tx.update(apiKeys).set({ revokedAt: now }).where(eq18(apiKeys.id, id)).run();
17784
18004
  writeAuditLog(tx, auditFromRequest(request, {
17785
18005
  actor: "api",
17786
18006
  action: "api-key.revoked",
@@ -17853,7 +18073,7 @@ async function telemetryRoutes(app, opts) {
17853
18073
 
17854
18074
  // ../api-routes/src/schedules.ts
17855
18075
  import crypto13 from "crypto";
17856
- import { and as and12, eq as eq18 } from "drizzle-orm";
18076
+ import { and as and13, eq as eq19 } from "drizzle-orm";
17857
18077
  function parseKindParam(raw) {
17858
18078
  if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
17859
18079
  const parsed = schedulableRunKindSchema.safeParse(raw);
@@ -17880,7 +18100,7 @@ async function scheduleRoutes(app, opts) {
17880
18100
  if (!sourceId) {
17881
18101
  throw validationError('"sourceId" is required when kind is "traffic-sync"');
17882
18102
  }
17883
- const sourceRow = app.db.select().from(trafficSources).where(eq18(trafficSources.id, sourceId)).get();
18103
+ const sourceRow = app.db.select().from(trafficSources).where(eq19(trafficSources.id, sourceId)).get();
17884
18104
  if (!sourceRow || sourceRow.projectId !== project.id) {
17885
18105
  throw notFound("Traffic source", sourceId);
17886
18106
  }
@@ -17925,7 +18145,7 @@ async function scheduleRoutes(app, opts) {
17925
18145
  }
17926
18146
  const now = (/* @__PURE__ */ new Date()).toISOString();
17927
18147
  const enabledBool = enabled !== false;
17928
- const existing = app.db.select().from(schedules).where(and12(eq18(schedules.projectId, project.id), eq18(schedules.kind, kind))).get();
18148
+ const existing = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
17929
18149
  if (existing) {
17930
18150
  app.db.update(schedules).set({
17931
18151
  cronExpr,
@@ -17935,7 +18155,7 @@ async function scheduleRoutes(app, opts) {
17935
18155
  sourceId: sourceId ?? null,
17936
18156
  enabled: enabledBool,
17937
18157
  updatedAt: now
17938
- }).where(eq18(schedules.id, existing.id)).run();
18158
+ }).where(eq19(schedules.id, existing.id)).run();
17939
18159
  } else {
17940
18160
  app.db.insert(schedules).values({
17941
18161
  id: crypto13.randomUUID(),
@@ -17959,13 +18179,13 @@ async function scheduleRoutes(app, opts) {
17959
18179
  diff: { kind, cronExpr, preset, timezone, providers, sourceId }
17960
18180
  });
17961
18181
  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();
18182
+ const schedule = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
17963
18183
  return reply.status(existing ? 200 : 201).send(formatSchedule(schedule));
17964
18184
  });
17965
18185
  app.get("/projects/:name/schedule", async (request, reply) => {
17966
18186
  const project = resolveProject(app.db, request.params.name);
17967
18187
  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();
18188
+ const schedule = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
17969
18189
  if (!schedule) {
17970
18190
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
17971
18191
  }
@@ -17974,11 +18194,11 @@ async function scheduleRoutes(app, opts) {
17974
18194
  app.delete("/projects/:name/schedule", async (request, reply) => {
17975
18195
  const project = resolveProject(app.db, request.params.name);
17976
18196
  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();
18197
+ const schedule = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
17978
18198
  if (!schedule) {
17979
18199
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
17980
18200
  }
17981
- app.db.delete(schedules).where(eq18(schedules.id, schedule.id)).run();
18201
+ app.db.delete(schedules).where(eq19(schedules.id, schedule.id)).run();
17982
18202
  writeAuditLog(app.db, {
17983
18203
  projectId: project.id,
17984
18204
  actor: "api",
@@ -18011,7 +18231,7 @@ function formatSchedule(row) {
18011
18231
 
18012
18232
  // ../api-routes/src/notifications.ts
18013
18233
  import crypto14 from "crypto";
18014
- import { eq as eq19 } from "drizzle-orm";
18234
+ import { eq as eq20 } from "drizzle-orm";
18015
18235
  var VALID_EVENTS = ["citation.lost", "citation.gained", "run.completed", "run.failed", "insight.critical", "insight.high"];
18016
18236
  async function notificationRoutes(app, opts = {}) {
18017
18237
  const allowLoopback = opts.allowLoopbackWebhooks === true;
@@ -18051,22 +18271,22 @@ async function notificationRoutes(app, opts = {}) {
18051
18271
  diff: { channel, ...redactNotificationUrl(url), events }
18052
18272
  });
18053
18273
  return reply.status(201).send({
18054
- ...formatNotification(app.db.select().from(notifications).where(eq19(notifications.id, id)).get()),
18274
+ ...formatNotification(app.db.select().from(notifications).where(eq20(notifications.id, id)).get()),
18055
18275
  webhookSecret
18056
18276
  });
18057
18277
  });
18058
18278
  app.get("/projects/:name/notifications", async (request, reply) => {
18059
18279
  const project = resolveProject(app.db, request.params.name);
18060
- const rows = app.db.select().from(notifications).where(eq19(notifications.projectId, project.id)).all();
18280
+ const rows = app.db.select().from(notifications).where(eq20(notifications.projectId, project.id)).all();
18061
18281
  return reply.send(rows.map(formatNotification));
18062
18282
  });
18063
18283
  app.delete("/projects/:name/notifications/:id", async (request, reply) => {
18064
18284
  const project = resolveProject(app.db, request.params.name);
18065
- const notification = app.db.select().from(notifications).where(eq19(notifications.id, request.params.id)).get();
18285
+ const notification = app.db.select().from(notifications).where(eq20(notifications.id, request.params.id)).get();
18066
18286
  if (!notification || notification.projectId !== project.id) {
18067
18287
  throw notFound("Notification", request.params.id);
18068
18288
  }
18069
- app.db.delete(notifications).where(eq19(notifications.id, notification.id)).run();
18289
+ app.db.delete(notifications).where(eq20(notifications.id, notification.id)).run();
18070
18290
  writeAuditLog(app.db, {
18071
18291
  projectId: project.id,
18072
18292
  actor: "api",
@@ -18078,7 +18298,7 @@ async function notificationRoutes(app, opts = {}) {
18078
18298
  });
18079
18299
  app.post("/projects/:name/notifications/:id/test", async (request, reply) => {
18080
18300
  const project = resolveProject(app.db, request.params.name);
18081
- const notification = app.db.select().from(notifications).where(eq19(notifications.id, request.params.id)).get();
18301
+ const notification = app.db.select().from(notifications).where(eq20(notifications.id, request.params.id)).get();
18082
18302
  if (!notification || notification.projectId !== project.id) {
18083
18303
  throw notFound("Notification", request.params.id);
18084
18304
  }
@@ -18131,7 +18351,7 @@ function formatNotification(row) {
18131
18351
 
18132
18352
  // ../api-routes/src/google.ts
18133
18353
  import crypto17 from "crypto";
18134
- import { eq as eq20, and as and13, desc as desc10, sql as sql8, inArray as inArray9 } from "drizzle-orm";
18354
+ import { eq as eq21, and as and14, desc as desc11, sql as sql8, inArray as inArray10 } from "drizzle-orm";
18135
18355
 
18136
18356
  // ../api-routes/src/gbp-summary.ts
18137
18357
  function computeMetricTotals(rows) {
@@ -19857,7 +20077,7 @@ async function googleRoutes(app, opts) {
19857
20077
  if (!projectId) {
19858
20078
  return reply.status(400).send("Stale OAuth state \u2014 restart the connect flow.");
19859
20079
  }
19860
- const project = app.db.select().from(projects).where(eq20(projects.id, projectId)).get();
20080
+ const project = app.db.select().from(projects).where(eq21(projects.id, projectId)).get();
19861
20081
  if (!project) {
19862
20082
  return reply.status(400).send("Project no longer exists. Restart the connect flow.");
19863
20083
  }
@@ -19989,14 +20209,14 @@ async function googleRoutes(app, opts) {
19989
20209
  if (opts.onGscSyncRequested) {
19990
20210
  opts.onGscSyncRequested(runId, project.id, { days, full });
19991
20211
  }
19992
- const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
20212
+ const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
19993
20213
  return run;
19994
20214
  });
19995
20215
  app.get("/projects/:name/google/gsc/performance", async (request) => {
19996
20216
  const project = resolveProject(app.db, request.params.name);
19997
20217
  const { startDate, endDate, query, page, limit, offset } = request.query;
19998
20218
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
19999
- const conditions = [eq20(gscSearchData.projectId, project.id)];
20219
+ const conditions = [eq21(gscSearchData.projectId, project.id)];
20000
20220
  if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
20001
20221
  else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
20002
20222
  if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
@@ -20004,7 +20224,7 @@ async function googleRoutes(app, opts) {
20004
20224
  if (page) conditions.push(sql8`${gscSearchData.page} LIKE ${"%" + escapeLikePattern(page) + "%"} ESCAPE '\\'`);
20005
20225
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
20006
20226
  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();
20227
+ const rows = app.db.select().from(gscSearchData).where(and14(...conditions)).orderBy(desc11(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
20008
20228
  return rows.map((r) => ({
20009
20229
  date: r.date,
20010
20230
  query: r.query,
@@ -20021,7 +20241,7 @@ async function googleRoutes(app, opts) {
20021
20241
  const project = resolveProject(app.db, request.params.name);
20022
20242
  const { startDate, endDate } = request.query;
20023
20243
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
20024
- const conditions = [eq20(gscSearchData.projectId, project.id)];
20244
+ const conditions = [eq21(gscSearchData.projectId, project.id)];
20025
20245
  if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
20026
20246
  else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
20027
20247
  if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
@@ -20029,7 +20249,7 @@ async function googleRoutes(app, opts) {
20029
20249
  date: gscSearchData.date,
20030
20250
  clicks: sql8`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
20031
20251
  impressions: sql8`COALESCE(SUM(${gscSearchData.impressions}), 0)`
20032
- }).from(gscSearchData).where(and13(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
20252
+ }).from(gscSearchData).where(and14(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
20033
20253
  const daily = rows.map((r) => ({
20034
20254
  date: r.date,
20035
20255
  clicks: r.clicks,
@@ -20112,9 +20332,9 @@ async function googleRoutes(app, opts) {
20112
20332
  app.get("/projects/:name/google/gsc/inspections", async (request) => {
20113
20333
  const project = resolveProject(app.db, request.params.name);
20114
20334
  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();
20335
+ const conditions = [eq21(gscUrlInspections.projectId, project.id)];
20336
+ if (url) conditions.push(eq21(gscUrlInspections.url, url));
20337
+ const rows = app.db.select().from(gscUrlInspections).where(and14(...conditions)).orderBy(desc11(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
20118
20338
  return rows.map((r) => ({
20119
20339
  id: r.id,
20120
20340
  url: r.url,
@@ -20133,7 +20353,7 @@ async function googleRoutes(app, opts) {
20133
20353
  });
20134
20354
  app.get("/projects/:name/google/gsc/deindexed", async (request) => {
20135
20355
  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();
20356
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq21(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
20137
20357
  const byUrl = /* @__PURE__ */ new Map();
20138
20358
  for (const row of allInspections) {
20139
20359
  const existing = byUrl.get(row.url);
@@ -20161,7 +20381,7 @@ async function googleRoutes(app, opts) {
20161
20381
  });
20162
20382
  app.get("/projects/:name/google/gsc/coverage", async (request) => {
20163
20383
  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();
20384
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq21(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
20165
20385
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
20166
20386
  const latestByUrl = /* @__PURE__ */ new Map();
20167
20387
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -20210,7 +20430,7 @@ async function googleRoutes(app, opts) {
20210
20430
  const total = latestByUrl.size;
20211
20431
  const indexed = indexedUrls.length;
20212
20432
  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();
20433
+ const latestSnapshot = app.db.select({ createdAt: gscCoverageSnapshots.createdAt }).from(gscCoverageSnapshots).where(eq21(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.createdAt)).limit(1).get();
20214
20434
  const lastSyncedAt = latestSnapshot?.createdAt ?? null;
20215
20435
  const formatRow = (r) => ({
20216
20436
  id: r.id,
@@ -20261,7 +20481,7 @@ async function googleRoutes(app, opts) {
20261
20481
  const project = resolveProject(app.db, request.params.name);
20262
20482
  const parsed = parseInt(request.query.limit ?? "90", 10);
20263
20483
  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();
20484
+ const rows = app.db.select().from(gscCoverageSnapshots).where(eq21(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.date)).limit(limit).all();
20265
20485
  return rows.map((r) => ({
20266
20486
  date: r.date,
20267
20487
  indexed: r.indexed,
@@ -20330,7 +20550,7 @@ async function googleRoutes(app, opts) {
20330
20550
  if (opts.onInspectSitemapRequested) {
20331
20551
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl });
20332
20552
  }
20333
- const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
20553
+ const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
20334
20554
  return { sitemaps, primarySitemapUrl: sitemapUrl, run };
20335
20555
  });
20336
20556
  app.post("/projects/:name/google/gsc/inspect-sitemap", async (request) => {
@@ -20357,7 +20577,7 @@ async function googleRoutes(app, opts) {
20357
20577
  if (opts.onInspectSitemapRequested) {
20358
20578
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl: sitemapUrl ?? void 0 });
20359
20579
  }
20360
- const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
20580
+ const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
20361
20581
  return run;
20362
20582
  });
20363
20583
  app.put("/projects/:name/google/connections/:type/sitemap", async (request) => {
@@ -20404,7 +20624,7 @@ async function googleRoutes(app, opts) {
20404
20624
  const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
20405
20625
  let urlsToNotify = request.body?.urls ?? [];
20406
20626
  if (request.body?.allUnindexed) {
20407
- const allInspections = app.db.select().from(gscUrlInspections).where(eq20(gscUrlInspections.projectId, project.id)).orderBy(desc10(gscUrlInspections.inspectedAt)).all();
20627
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq21(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
20408
20628
  const latestByUrl = /* @__PURE__ */ new Map();
20409
20629
  for (const row of allInspections) {
20410
20630
  if (!latestByUrl.has(row.url)) {
@@ -20515,7 +20735,7 @@ async function googleRoutes(app, opts) {
20515
20735
  };
20516
20736
  }
20517
20737
  function listSelectionResponse(projectId) {
20518
- const rows = app.db.select().from(gbpLocations).where(eq20(gbpLocations.projectId, projectId)).all();
20738
+ const rows = app.db.select().from(gbpLocations).where(eq21(gbpLocations.projectId, projectId)).all();
20519
20739
  const dtos = rows.map(rowToDto2);
20520
20740
  return {
20521
20741
  locations: dtos,
@@ -20524,15 +20744,15 @@ async function googleRoutes(app, opts) {
20524
20744
  };
20525
20745
  }
20526
20746
  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();
20747
+ tx.delete(gbpDailyMetrics).where(eq21(gbpDailyMetrics.projectId, projectId)).run();
20748
+ tx.delete(gbpKeywordImpressions).where(eq21(gbpKeywordImpressions.projectId, projectId)).run();
20749
+ tx.delete(gbpKeywordMonthly).where(eq21(gbpKeywordMonthly.projectId, projectId)).run();
20750
+ tx.delete(gbpPlaceActions).where(eq21(gbpPlaceActions.projectId, projectId)).run();
20751
+ tx.delete(gbpLodgingSnapshots).where(eq21(gbpLodgingSnapshots.projectId, projectId)).run();
20752
+ tx.delete(gbpLocations).where(eq21(gbpLocations.projectId, projectId)).run();
20533
20753
  }
20534
20754
  function currentProjectAccount(projectId) {
20535
- const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq20(gbpLocations.projectId, projectId)).limit(1).get();
20755
+ const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq21(gbpLocations.projectId, projectId)).limit(1).get();
20536
20756
  return row?.accountName ?? null;
20537
20757
  }
20538
20758
  app.post("/projects/:name/gbp/locations/discover", async (request) => {
@@ -20602,7 +20822,7 @@ async function googleRoutes(app, opts) {
20602
20822
  app.db.transaction((tx) => {
20603
20823
  if (switching) clearGbpProjectData(tx, project.id);
20604
20824
  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();
20825
+ const existing = tx.select().from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), eq21(gbpLocations.locationName, remote.name))).get();
20606
20826
  if (existing) {
20607
20827
  tx.update(gbpLocations).set({
20608
20828
  accountName,
@@ -20613,7 +20833,7 @@ async function googleRoutes(app, opts) {
20613
20833
  placeId: remote.metadata?.placeId ?? null,
20614
20834
  mapsUri: remote.metadata?.mapsUri ?? null,
20615
20835
  updatedAt: now
20616
- }).where(eq20(gbpLocations.id, existing.id)).run();
20836
+ }).where(eq21(gbpLocations.id, existing.id)).run();
20617
20837
  } else {
20618
20838
  tx.insert(gbpLocations).values({
20619
20839
  id: crypto17.randomUUID(),
@@ -20696,11 +20916,11 @@ async function googleRoutes(app, opts) {
20696
20916
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid selection request");
20697
20917
  }
20698
20918
  const { selected } = parsed.data;
20699
- const existing = app.db.select().from(gbpLocations).where(and13(eq20(gbpLocations.projectId, project.id), eq20(gbpLocations.locationName, locationName))).get();
20919
+ const existing = app.db.select().from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), eq21(gbpLocations.locationName, locationName))).get();
20700
20920
  if (!existing) throw notFound("GBP location", locationName);
20701
20921
  const now = (/* @__PURE__ */ new Date()).toISOString();
20702
20922
  app.db.transaction((tx) => {
20703
- tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq20(gbpLocations.id, existing.id)).run();
20923
+ tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq21(gbpLocations.id, existing.id)).run();
20704
20924
  writeAuditLog(tx, {
20705
20925
  projectId: project.id,
20706
20926
  actor: "api",
@@ -20709,7 +20929,7 @@ async function googleRoutes(app, opts) {
20709
20929
  entityId: locationName
20710
20930
  });
20711
20931
  });
20712
- const refreshed = app.db.select().from(gbpLocations).where(eq20(gbpLocations.id, existing.id)).get();
20932
+ const refreshed = app.db.select().from(gbpLocations).where(eq21(gbpLocations.id, existing.id)).get();
20713
20933
  return rowToDto2(refreshed);
20714
20934
  });
20715
20935
  app.delete("/projects/:name/gbp/connection", async (request, reply) => {
@@ -20753,10 +20973,10 @@ async function googleRoutes(app, opts) {
20753
20973
  });
20754
20974
  app.get("/projects/:name/gbp/metrics", async (request) => {
20755
20975
  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();
20976
+ const conditions = [eq21(gbpDailyMetrics.projectId, project.id)];
20977
+ if (request.query.locationName) conditions.push(eq21(gbpDailyMetrics.locationName, request.query.locationName));
20978
+ if (request.query.metric) conditions.push(eq21(gbpDailyMetrics.metric, request.query.metric));
20979
+ const rows = app.db.select().from(gbpDailyMetrics).where(and14(...conditions)).orderBy(desc11(gbpDailyMetrics.date)).all();
20760
20980
  return {
20761
20981
  metrics: rows.map((r) => ({ locationName: r.locationName, date: r.date, metric: r.metric, value: r.value })),
20762
20982
  total: rows.length
@@ -20764,9 +20984,9 @@ async function googleRoutes(app, opts) {
20764
20984
  });
20765
20985
  app.get("/projects/:name/gbp/keywords", async (request) => {
20766
20986
  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();
20987
+ const conditions = [eq21(gbpKeywordImpressions.projectId, project.id)];
20988
+ if (request.query.locationName) conditions.push(eq21(gbpKeywordImpressions.locationName, request.query.locationName));
20989
+ const rows = app.db.select().from(gbpKeywordImpressions).where(and14(...conditions)).all();
20770
20990
  rows.sort((a, b) => (b.valueCount ?? -1) - (a.valueCount ?? -1));
20771
20991
  const thresholded = rows.filter((r) => r.valueThreshold !== null).length;
20772
20992
  return {
@@ -20784,9 +21004,9 @@ async function googleRoutes(app, opts) {
20784
21004
  });
20785
21005
  app.get("/projects/:name/gbp/place-actions", async (request) => {
20786
21006
  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();
21007
+ const conditions = [eq21(gbpPlaceActions.projectId, project.id)];
21008
+ if (request.query.locationName) conditions.push(eq21(gbpPlaceActions.locationName, request.query.locationName));
21009
+ const rows = app.db.select().from(gbpPlaceActions).where(and14(...conditions)).all();
20790
21010
  return {
20791
21011
  placeActions: rows.map((r) => ({
20792
21012
  locationName: r.locationName,
@@ -20801,9 +21021,9 @@ async function googleRoutes(app, opts) {
20801
21021
  });
20802
21022
  app.get("/projects/:name/gbp/lodging", async (request) => {
20803
21023
  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();
21024
+ const conditions = [eq21(gbpLodgingSnapshots.projectId, project.id)];
21025
+ if (request.query.locationName) conditions.push(eq21(gbpLodgingSnapshots.locationName, request.query.locationName));
21026
+ const rows = app.db.select().from(gbpLodgingSnapshots).where(and14(...conditions)).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
20807
21027
  const latestByLocation = /* @__PURE__ */ new Map();
20808
21028
  for (const row of rows) {
20809
21029
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -20818,9 +21038,9 @@ async function googleRoutes(app, opts) {
20818
21038
  });
20819
21039
  app.get("/projects/:name/gbp/places", async (request) => {
20820
21040
  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();
21041
+ const conditions = [eq21(gbpPlaceDetails.projectId, project.id)];
21042
+ if (request.query.locationName) conditions.push(eq21(gbpPlaceDetails.locationName, request.query.locationName));
21043
+ const rows = app.db.select().from(gbpPlaceDetails).where(and14(...conditions)).orderBy(desc11(gbpPlaceDetails.syncedAt)).all();
20824
21044
  const latestByLocation = /* @__PURE__ */ new Map();
20825
21045
  for (const row of rows) {
20826
21046
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -20839,7 +21059,7 @@ async function googleRoutes(app, opts) {
20839
21059
  app.get("/projects/:name/gbp/summary", async (request) => {
20840
21060
  const project = resolveProject(app.db, request.params.name);
20841
21061
  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);
21062
+ 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
21063
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
20844
21064
  if (locationNames.length === 0) {
20845
21065
  return buildGbpSummary({
@@ -20852,10 +21072,10 @@ async function googleRoutes(app, opts) {
20852
21072
  lodging: []
20853
21073
  });
20854
21074
  }
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();
21075
+ const metricRows = app.db.select().from(gbpDailyMetrics).where(and14(eq21(gbpDailyMetrics.projectId, project.id), inArray10(gbpDailyMetrics.locationName, locationNames))).all();
21076
+ const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and14(eq21(gbpKeywordImpressions.projectId, project.id), inArray10(gbpKeywordImpressions.locationName, locationNames))).all();
21077
+ const placeActionRows = app.db.select().from(gbpPlaceActions).where(and14(eq21(gbpPlaceActions.projectId, project.id), inArray10(gbpPlaceActions.locationName, locationNames))).all();
21078
+ const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and14(eq21(gbpLodgingSnapshots.projectId, project.id), inArray10(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
20859
21079
  const latestLodgingByLocation = /* @__PURE__ */ new Map();
20860
21080
  for (const row of lodgingRows) {
20861
21081
  if (!latestLodgingByLocation.has(row.locationName)) {
@@ -20876,7 +21096,7 @@ async function googleRoutes(app, opts) {
20876
21096
 
20877
21097
  // ../api-routes/src/ads.ts
20878
21098
  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";
21099
+ import { eq as eq22, and as and15, asc as asc2, gte as gte3, lte as lte2, inArray as inArray11 } from "drizzle-orm";
20880
21100
  function statusDto(row) {
20881
21101
  if (!row) return { connected: false };
20882
21102
  return {
@@ -20925,7 +21145,7 @@ async function adsRoutes(app, opts) {
20925
21145
  createdAt: existingCfg?.createdAt ?? now,
20926
21146
  updatedAt: now
20927
21147
  });
20928
- const existingRow = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21148
+ const existingRow = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20929
21149
  app.db.transaction((tx) => {
20930
21150
  if (existingRow) {
20931
21151
  tx.update(adsConnections).set({
@@ -20935,7 +21155,7 @@ async function adsRoutes(app, opts) {
20935
21155
  timezone: account.timezone,
20936
21156
  status: account.status,
20937
21157
  updatedAt: now
20938
- }).where(eq21(adsConnections.id, existingRow.id)).run();
21158
+ }).where(eq22(adsConnections.id, existingRow.id)).run();
20939
21159
  } else {
20940
21160
  tx.insert(adsConnections).values({
20941
21161
  id: crypto18.randomUUID(),
@@ -20957,16 +21177,16 @@ async function adsRoutes(app, opts) {
20957
21177
  entityId: account.id
20958
21178
  }));
20959
21179
  });
20960
- const row = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21180
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20961
21181
  return statusDto(row);
20962
21182
  }
20963
21183
  );
20964
21184
  app.delete("/projects/:name/ads/connection", async (request) => {
20965
21185
  const project = resolveProject(app.db, request.params.name);
20966
- const row = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21186
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20967
21187
  if (row) {
20968
21188
  app.db.transaction((tx) => {
20969
- tx.delete(adsConnections).where(eq21(adsConnections.id, row.id)).run();
21189
+ tx.delete(adsConnections).where(eq22(adsConnections.id, row.id)).run();
20970
21190
  writeAuditLog(tx, auditFromRequest(request, {
20971
21191
  projectId: project.id,
20972
21192
  actor: "api",
@@ -20982,19 +21202,19 @@ async function adsRoutes(app, opts) {
20982
21202
  });
20983
21203
  app.get("/projects/:name/ads/status", async (request) => {
20984
21204
  const project = resolveProject(app.db, request.params.name);
20985
- const row = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21205
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20986
21206
  return statusDto(row);
20987
21207
  });
20988
21208
  app.post("/projects/:name/ads/sync", async (request) => {
20989
21209
  const project = resolveProject(app.db, request.params.name);
20990
- const row = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21210
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
20991
21211
  if (!row) {
20992
21212
  throw validationError('No ads connection for this project. Run "canonry ads connect" first.');
20993
21213
  }
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])
21214
+ const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and15(
21215
+ eq22(runs.projectId, project.id),
21216
+ eq22(runs.kind, RunKinds["ads-sync"]),
21217
+ inArray11(runs.status, [RunStatuses.queued, RunStatuses.running])
20998
21218
  )).get();
20999
21219
  if (inFlight) {
21000
21220
  const existing = { runId: inFlight.id, status: inFlight.status };
@@ -21015,9 +21235,9 @@ async function adsRoutes(app, opts) {
21015
21235
  });
21016
21236
  app.get("/projects/:name/ads/campaigns", async (request) => {
21017
21237
  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();
21238
+ const campaignRows = app.db.select().from(adsCampaigns).where(eq22(adsCampaigns.projectId, project.id)).all();
21239
+ const groupRows = app.db.select().from(adsAdGroups).where(eq22(adsAdGroups.projectId, project.id)).all();
21240
+ const adRows = app.db.select().from(adsAds).where(eq22(adsAds.projectId, project.id)).all();
21021
21241
  const adsByGroup = /* @__PURE__ */ new Map();
21022
21242
  for (const ad of adRows) {
21023
21243
  const dto = {
@@ -21071,12 +21291,12 @@ async function adsRoutes(app, opts) {
21071
21291
  }
21072
21292
  parsedLevel = result.data;
21073
21293
  }
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));
21294
+ const conditions = [eq22(adsInsightsDaily.projectId, project.id)];
21295
+ if (parsedLevel) conditions.push(eq22(adsInsightsDaily.level, parsedLevel));
21296
+ if (entityId) conditions.push(eq22(adsInsightsDaily.entityId, entityId));
21077
21297
  if (from) conditions.push(gte3(adsInsightsDaily.date, from));
21078
21298
  if (to) conditions.push(lte2(adsInsightsDaily.date, to));
21079
- const rows = app.db.select().from(adsInsightsDaily).where(and14(...conditions)).orderBy(asc2(adsInsightsDaily.date)).all();
21299
+ const rows = app.db.select().from(adsInsightsDaily).where(and15(...conditions)).orderBy(asc2(adsInsightsDaily.date)).all();
21080
21300
  const dtoRows = rows.map((row) => ({
21081
21301
  level: row.level,
21082
21302
  entityId: row.entityId,
@@ -21087,19 +21307,19 @@ async function adsRoutes(app, opts) {
21087
21307
  ctr: adsCtr(row.clicks, row.impressions),
21088
21308
  cpcMicros: adsCpcMicros(row.spendMicros, row.clicks)
21089
21309
  }));
21090
- const conn = app.db.select().from(adsConnections).where(eq21(adsConnections.projectId, project.id)).get();
21310
+ const conn = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21091
21311
  const response = { rows: dtoRows, currencyCode: conn?.currencyCode ?? null };
21092
21312
  return response;
21093
21313
  });
21094
21314
  app.get("/projects/:name/ads/summary", async (request) => {
21095
21315
  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")
21316
+ const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21317
+ const campaignCount = app.db.select().from(adsCampaigns).where(eq22(adsCampaigns.projectId, project.id)).all().length;
21318
+ const adGroupCount = app.db.select().from(adsAdGroups).where(eq22(adsAdGroups.projectId, project.id)).all().length;
21319
+ const adCount = app.db.select().from(adsAds).where(eq22(adsAds.projectId, project.id)).all().length;
21320
+ const campaignInsights = app.db.select().from(adsInsightsDaily).where(and15(
21321
+ eq22(adsInsightsDaily.projectId, project.id),
21322
+ eq22(adsInsightsDaily.level, "campaign")
21103
21323
  )).all();
21104
21324
  let impressions = 0;
21105
21325
  let clicks = 0;
@@ -21136,7 +21356,7 @@ async function adsRoutes(app, opts) {
21136
21356
 
21137
21357
  // ../api-routes/src/bing.ts
21138
21358
  import crypto19 from "crypto";
21139
- import { eq as eq22, and as and15, desc as desc11 } from "drizzle-orm";
21359
+ import { eq as eq23, and as and16, desc as desc12 } from "drizzle-orm";
21140
21360
 
21141
21361
  // ../integration-bing/src/constants.ts
21142
21362
  var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
@@ -21510,7 +21730,7 @@ async function bingRoutes(app, opts) {
21510
21730
  const store = requireConnectionStore();
21511
21731
  const project = resolveProject(app.db, request.params.name);
21512
21732
  requireConnection(store, project.canonicalDomain);
21513
- const allInspections = app.db.select().from(bingUrlInspections).where(eq22(bingUrlInspections.projectId, project.id)).orderBy(desc11(bingUrlInspections.inspectedAt)).all();
21733
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq23(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
21514
21734
  const latestByUrl = /* @__PURE__ */ new Map();
21515
21735
  const definitiveByUrl = /* @__PURE__ */ new Map();
21516
21736
  for (const row of allInspections) {
@@ -21599,7 +21819,7 @@ async function bingRoutes(app, opts) {
21599
21819
  const project = resolveProject(app.db, request.params.name);
21600
21820
  const parsed = parseInt(request.query.limit ?? "90", 10);
21601
21821
  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();
21822
+ const rows = app.db.select().from(bingCoverageSnapshots).where(eq23(bingCoverageSnapshots.projectId, project.id)).orderBy(desc12(bingCoverageSnapshots.date)).limit(limit).all();
21603
21823
  return rows.map((r) => ({
21604
21824
  date: r.date,
21605
21825
  indexed: r.indexed,
@@ -21611,8 +21831,8 @@ async function bingRoutes(app, opts) {
21611
21831
  requireConnectionStore();
21612
21832
  const project = resolveProject(app.db, request.params.name);
21613
21833
  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();
21834
+ const whereClause = url ? and16(eq23(bingUrlInspections.projectId, project.id), eq23(bingUrlInspections.url, url)) : eq23(bingUrlInspections.projectId, project.id);
21835
+ 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
21836
  return filtered.map((r) => ({
21617
21837
  id: r.id,
21618
21838
  url: r.url,
@@ -21701,7 +21921,7 @@ async function bingRoutes(app, opts) {
21701
21921
  anchorCount: result.AnchorCount ?? null,
21702
21922
  discoveryDate
21703
21923
  }).run();
21704
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq22(runs.id, runId)).run();
21924
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq23(runs.id, runId)).run();
21705
21925
  return {
21706
21926
  id,
21707
21927
  url,
@@ -21717,7 +21937,7 @@ async function bingRoutes(app, opts) {
21717
21937
  } catch (e) {
21718
21938
  const msg = e instanceof Error ? e.message : String(e);
21719
21939
  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();
21940
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq23(runs.id, runId)).run();
21721
21941
  throw e;
21722
21942
  }
21723
21943
  });
@@ -21744,7 +21964,7 @@ async function bingRoutes(app, opts) {
21744
21964
  } else {
21745
21965
  bingLog("warn", "inspect-sitemap.no-callback", { domain: project.canonicalDomain, runId });
21746
21966
  }
21747
- const run = app.db.select().from(runs).where(eq22(runs.id, runId)).get();
21967
+ const run = app.db.select().from(runs).where(eq23(runs.id, runId)).get();
21748
21968
  return run;
21749
21969
  });
21750
21970
  app.post("/projects/:name/bing/request-indexing", async (request) => {
@@ -21756,7 +21976,7 @@ async function bingRoutes(app, opts) {
21756
21976
  }
21757
21977
  let urlsToSubmit = request.body?.urls ?? [];
21758
21978
  if (request.body?.allUnindexed) {
21759
- const allInspections = app.db.select().from(bingUrlInspections).where(eq22(bingUrlInspections.projectId, project.id)).orderBy(desc11(bingUrlInspections.inspectedAt)).all();
21979
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq23(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
21760
21980
  const latestByUrl = /* @__PURE__ */ new Map();
21761
21981
  for (const row of allInspections) {
21762
21982
  if (!latestByUrl.has(row.url)) {
@@ -21843,14 +22063,14 @@ async function bingRoutes(app, opts) {
21843
22063
  import fs from "fs";
21844
22064
  import path from "path";
21845
22065
  import os from "os";
21846
- import { eq as eq23, and as and16 } from "drizzle-orm";
22066
+ import { eq as eq24, and as and17 } from "drizzle-orm";
21847
22067
  function getScreenshotDir() {
21848
22068
  return path.join(os.homedir(), ".canonry", "screenshots");
21849
22069
  }
21850
22070
  async function cdpRoutes(app, opts) {
21851
22071
  app.get("/screenshots/:snapshotId", async (request, reply) => {
21852
22072
  const { snapshotId } = request.params;
21853
- const snapshot = app.db.select({ screenshotPath: querySnapshots.screenshotPath }).from(querySnapshots).where(eq23(querySnapshots.id, snapshotId)).get();
22073
+ const snapshot = app.db.select({ screenshotPath: querySnapshots.screenshotPath }).from(querySnapshots).where(eq24(querySnapshots.id, snapshotId)).get();
21854
22074
  if (!snapshot?.screenshotPath) {
21855
22075
  const err = notFound("Screenshot", snapshotId);
21856
22076
  return reply.code(err.statusCode).send(err.toJSON());
@@ -21917,7 +22137,7 @@ async function cdpRoutes(app, opts) {
21917
22137
  async (request, reply) => {
21918
22138
  const project = resolveProject(app.db, request.params.name);
21919
22139
  const { runId } = request.params;
21920
- const run = app.db.select().from(runs).where(and16(eq23(runs.id, runId), eq23(runs.projectId, project.id))).get();
22140
+ const run = app.db.select().from(runs).where(and17(eq24(runs.id, runId), eq24(runs.projectId, project.id))).get();
21921
22141
  if (!run) {
21922
22142
  const err = notFound("Run", runId);
21923
22143
  return reply.code(err.statusCode).send(err.toJSON());
@@ -21930,8 +22150,8 @@ async function cdpRoutes(app, opts) {
21930
22150
  citedDomains: querySnapshots.citedDomains,
21931
22151
  screenshotPath: querySnapshots.screenshotPath,
21932
22152
  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();
22153
+ }).from(querySnapshots).where(eq24(querySnapshots.runId, runId)).all());
22154
+ const queryRows = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq24(queries.projectId, project.id)).all();
21935
22155
  const queryMap = new Map(queryRows.map((q) => [q.id, q.query]));
21936
22156
  const byQuery = /* @__PURE__ */ new Map();
21937
22157
  for (const snap of snapshots) {
@@ -22014,7 +22234,7 @@ async function cdpRoutes(app, opts) {
22014
22234
 
22015
22235
  // ../api-routes/src/ga.ts
22016
22236
  import crypto20 from "crypto";
22017
- import { eq as eq24, desc as desc12, and as and17, sql as sql9 } from "drizzle-orm";
22237
+ import { eq as eq25, desc as desc13, and as and18, sql as sql9 } from "drizzle-orm";
22018
22238
  function gaLog(level, action, ctx) {
22019
22239
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
22020
22240
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -22221,10 +22441,10 @@ async function ga4Routes(app, opts) {
22221
22441
  if (!saConn && !oauthConn) {
22222
22442
  throw notFound("GA4 connection", project.name);
22223
22443
  }
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();
22444
+ app.db.delete(gaTrafficSnapshots).where(eq25(gaTrafficSnapshots.projectId, project.id)).run();
22445
+ app.db.delete(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).run();
22446
+ app.db.delete(gaAiReferrals).where(eq25(gaAiReferrals.projectId, project.id)).run();
22447
+ app.db.delete(gaSocialReferrals).where(eq25(gaSocialReferrals.projectId, project.id)).run();
22228
22448
  const propertyId = saConn?.propertyId ?? oauthConn?.propertyId ?? null;
22229
22449
  opts.ga4CredentialStore?.deleteConnection(project.name);
22230
22450
  opts.googleConnectionStore?.deleteConnection(project.canonicalDomain, "ga4");
@@ -22245,7 +22465,7 @@ async function ga4Routes(app, opts) {
22245
22465
  if (!connected) {
22246
22466
  return { connected: false, propertyId: null, clientEmail: null, authMethod: null, lastSyncedAt: null };
22247
22467
  }
22248
- const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).orderBy(desc12(gaTrafficSummaries.syncedAt)).limit(1).get();
22468
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
22249
22469
  return {
22250
22470
  connected: true,
22251
22471
  propertyId: saConn?.propertyId ?? oauthConn?.propertyId ?? null,
@@ -22309,8 +22529,8 @@ async function ga4Routes(app, opts) {
22309
22529
  app.db.transaction((tx) => {
22310
22530
  if (syncTraffic) {
22311
22531
  tx.delete(gaTrafficSnapshots).where(
22312
- and17(
22313
- eq24(gaTrafficSnapshots.projectId, project.id),
22532
+ and18(
22533
+ eq25(gaTrafficSnapshots.projectId, project.id),
22314
22534
  sql9`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
22315
22535
  sql9`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
22316
22536
  )
@@ -22333,8 +22553,8 @@ async function ga4Routes(app, opts) {
22333
22553
  }
22334
22554
  if (syncAi) {
22335
22555
  tx.delete(gaAiReferrals).where(
22336
- and17(
22337
- eq24(gaAiReferrals.projectId, project.id),
22556
+ and18(
22557
+ eq25(gaAiReferrals.projectId, project.id),
22338
22558
  sql9`${gaAiReferrals.date} >= ${summary.periodStart}`,
22339
22559
  sql9`${gaAiReferrals.date} <= ${summary.periodEnd}`
22340
22560
  )
@@ -22359,8 +22579,8 @@ async function ga4Routes(app, opts) {
22359
22579
  }
22360
22580
  if (syncSocial) {
22361
22581
  tx.delete(gaSocialReferrals).where(
22362
- and17(
22363
- eq24(gaSocialReferrals.projectId, project.id),
22582
+ and18(
22583
+ eq25(gaSocialReferrals.projectId, project.id),
22364
22584
  sql9`${gaSocialReferrals.date} >= ${summary.periodStart}`,
22365
22585
  sql9`${gaSocialReferrals.date} <= ${summary.periodEnd}`
22366
22586
  )
@@ -22381,7 +22601,7 @@ async function ga4Routes(app, opts) {
22381
22601
  }
22382
22602
  }
22383
22603
  if (syncSummary) {
22384
- tx.delete(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).run();
22604
+ tx.delete(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).run();
22385
22605
  tx.insert(gaTrafficSummaries).values({
22386
22606
  id: crypto20.randomUUID(),
22387
22607
  projectId: project.id,
@@ -22393,7 +22613,7 @@ async function ga4Routes(app, opts) {
22393
22613
  syncedAt: now,
22394
22614
  syncRunId: runId
22395
22615
  }).run();
22396
- tx.delete(gaTrafficWindowSummaries).where(eq24(gaTrafficWindowSummaries.projectId, project.id)).run();
22616
+ tx.delete(gaTrafficWindowSummaries).where(eq25(gaTrafficWindowSummaries.projectId, project.id)).run();
22397
22617
  for (const ws of windowSummaries) {
22398
22618
  tx.insert(gaTrafficWindowSummaries).values({
22399
22619
  id: crypto20.randomUUID(),
@@ -22411,7 +22631,7 @@ async function ga4Routes(app, opts) {
22411
22631
  }
22412
22632
  }
22413
22633
  });
22414
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq24(runs.id, runId)).run();
22634
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq25(runs.id, runId)).run();
22415
22635
  const syncedComponents = only ? [
22416
22636
  ...syncTraffic ? ["traffic"] : [],
22417
22637
  ...syncSummary ? ["summary"] : [],
@@ -22440,7 +22660,7 @@ async function ga4Routes(app, opts) {
22440
22660
  } catch (e) {
22441
22661
  const msg = e instanceof Error ? e.message : String(e);
22442
22662
  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();
22663
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq25(runs.id, runId)).run();
22444
22664
  throw e;
22445
22665
  }
22446
22666
  });
@@ -22451,11 +22671,11 @@ async function ga4Routes(app, opts) {
22451
22671
  const window = parseWindow(request.query.window);
22452
22672
  const cutoff = windowCutoff(window);
22453
22673
  const cutoffDate = cutoff?.slice(0, 10) ?? null;
22454
- const snapshotConditions = [eq24(gaTrafficSnapshots.projectId, project.id)];
22674
+ const snapshotConditions = [eq25(gaTrafficSnapshots.projectId, project.id)];
22455
22675
  if (cutoffDate) snapshotConditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
22456
- const aiConditions = [eq24(gaAiReferrals.projectId, project.id)];
22676
+ const aiConditions = [eq25(gaAiReferrals.projectId, project.id)];
22457
22677
  if (cutoffDate) aiConditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
22458
- const socialConditions = [eq24(gaSocialReferrals.projectId, project.id)];
22678
+ const socialConditions = [eq25(gaSocialReferrals.projectId, project.id)];
22459
22679
  if (cutoffDate) socialConditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
22460
22680
  const windowSummaryRow = cutoffDate ? app.db.select({
22461
22681
  totalSessions: gaTrafficWindowSummaries.totalSessions,
@@ -22463,42 +22683,42 @@ async function ga4Routes(app, opts) {
22463
22683
  totalDirectSessions: gaTrafficWindowSummaries.totalDirectSessions,
22464
22684
  totalUsers: gaTrafficWindowSummaries.totalUsers
22465
22685
  }).from(gaTrafficWindowSummaries).where(
22466
- and17(
22467
- eq24(gaTrafficWindowSummaries.projectId, project.id),
22468
- eq24(gaTrafficWindowSummaries.windowKey, window)
22686
+ and18(
22687
+ eq25(gaTrafficWindowSummaries.projectId, project.id),
22688
+ eq25(gaTrafficWindowSummaries.windowKey, window)
22469
22689
  )
22470
22690
  ).get() : null;
22471
22691
  const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
22472
22692
  totalSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
22473
22693
  totalOrganicSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
22474
22694
  totalUsers: sql9`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
22475
- }).from(gaTrafficSnapshots).where(and17(...snapshotConditions)).get() : null;
22695
+ }).from(gaTrafficSnapshots).where(and18(...snapshotConditions)).get() : null;
22476
22696
  const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
22477
22697
  totalSessions: gaTrafficSummaries.totalSessions,
22478
22698
  totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
22479
22699
  totalUsers: gaTrafficSummaries.totalUsers
22480
- }).from(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).get();
22700
+ }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).get();
22481
22701
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
22482
22702
  totalDirectSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
22483
- }).from(gaTrafficSnapshots).where(and17(...snapshotConditions)).get();
22703
+ }).from(gaTrafficSnapshots).where(and18(...snapshotConditions)).get();
22484
22704
  const summaryMeta = app.db.select({
22485
22705
  periodStart: gaTrafficSummaries.periodStart,
22486
22706
  periodEnd: gaTrafficSummaries.periodEnd
22487
- }).from(gaTrafficSummaries).where(eq24(gaTrafficSummaries.projectId, project.id)).get();
22707
+ }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).get();
22488
22708
  const rows = app.db.select({
22489
22709
  landingPage: sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
22490
22710
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
22491
22711
  organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
22492
22712
  directSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
22493
22713
  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();
22714
+ }).from(gaTrafficSnapshots).where(and18(...snapshotConditions)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
22495
22715
  const aiReferralRows = app.db.select({
22496
22716
  source: gaAiReferrals.source,
22497
22717
  medium: gaAiReferrals.medium,
22498
22718
  sourceDimension: gaAiReferrals.sourceDimension,
22499
22719
  sessions: sql9`SUM(${gaAiReferrals.sessions})`,
22500
22720
  users: sql9`SUM(${gaAiReferrals.users})`
22501
- }).from(gaAiReferrals).where(and17(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
22721
+ }).from(gaAiReferrals).where(and18(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
22502
22722
  const aiReferralLandingPageRows = app.db.select({
22503
22723
  source: gaAiReferrals.source,
22504
22724
  medium: gaAiReferrals.medium,
@@ -22506,7 +22726,7 @@ async function ga4Routes(app, opts) {
22506
22726
  landingPage: sql9`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
22507
22727
  sessions: sql9`SUM(${gaAiReferrals.sessions})`,
22508
22728
  users: sql9`SUM(${gaAiReferrals.users})`
22509
- }).from(gaAiReferrals).where(and17(...aiConditions)).groupBy(
22729
+ }).from(gaAiReferrals).where(and18(...aiConditions)).groupBy(
22510
22730
  gaAiReferrals.source,
22511
22731
  gaAiReferrals.medium,
22512
22732
  gaAiReferrals.sourceDimension,
@@ -22543,7 +22763,7 @@ async function ga4Routes(app, opts) {
22543
22763
  channelGroup: gaAiReferrals.channelGroup,
22544
22764
  sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
22545
22765
  users: sql9`COALESCE(SUM(${gaAiReferrals.users}), 0)`
22546
- }).from(gaAiReferrals).where(and17(...aiConditions, eq24(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
22766
+ }).from(gaAiReferrals).where(and18(...aiConditions, eq25(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
22547
22767
  const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
22548
22768
  let aiBySessionUsers = 0;
22549
22769
  for (const row of aiBySessionRows) {
@@ -22557,12 +22777,12 @@ async function ga4Routes(app, opts) {
22557
22777
  channelGroup: gaSocialReferrals.channelGroup,
22558
22778
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`,
22559
22779
  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();
22780
+ }).from(gaSocialReferrals).where(and18(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql9`SUM(${gaSocialReferrals.sessions}) DESC`).all();
22561
22781
  const socialTotals = app.db.select({
22562
22782
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`,
22563
22783
  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();
22784
+ }).from(gaSocialReferrals).where(and18(...socialConditions)).get();
22785
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
22566
22786
  const total = summaryRow?.totalSessions ?? 0;
22567
22787
  const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
22568
22788
  const totalOrganicSessions = summaryRow?.totalOrganicSessions ?? 0;
@@ -22642,7 +22862,7 @@ async function ga4Routes(app, opts) {
22642
22862
  const project = resolveProject(app.db, request.params.name);
22643
22863
  requireGa4Connection(opts, project.name, project.canonicalDomain);
22644
22864
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
22645
- const conditions = [eq24(gaAiReferrals.projectId, project.id)];
22865
+ const conditions = [eq25(gaAiReferrals.projectId, project.id)];
22646
22866
  if (cutoffDate) conditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
22647
22867
  const rows = app.db.select({
22648
22868
  date: gaAiReferrals.date,
@@ -22652,7 +22872,7 @@ async function ga4Routes(app, opts) {
22652
22872
  sourceDimension: gaAiReferrals.sourceDimension,
22653
22873
  sessions: sql9`SUM(${gaAiReferrals.sessions})`,
22654
22874
  users: sql9`SUM(${gaAiReferrals.users})`
22655
- }).from(gaAiReferrals).where(and17(...conditions)).groupBy(
22875
+ }).from(gaAiReferrals).where(and18(...conditions)).groupBy(
22656
22876
  gaAiReferrals.date,
22657
22877
  gaAiReferrals.source,
22658
22878
  gaAiReferrals.medium,
@@ -22665,7 +22885,7 @@ async function ga4Routes(app, opts) {
22665
22885
  const project = resolveProject(app.db, request.params.name);
22666
22886
  requireGa4Connection(opts, project.name, project.canonicalDomain);
22667
22887
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
22668
- const conditions = [eq24(gaSocialReferrals.projectId, project.id)];
22888
+ const conditions = [eq25(gaSocialReferrals.projectId, project.id)];
22669
22889
  if (cutoffDate) conditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
22670
22890
  const rows = app.db.select({
22671
22891
  date: gaSocialReferrals.date,
@@ -22674,7 +22894,7 @@ async function ga4Routes(app, opts) {
22674
22894
  channelGroup: gaSocialReferrals.channelGroup,
22675
22895
  sessions: gaSocialReferrals.sessions,
22676
22896
  users: gaSocialReferrals.users
22677
- }).from(gaSocialReferrals).where(and17(...conditions)).orderBy(gaSocialReferrals.date).all();
22897
+ }).from(gaSocialReferrals).where(and18(...conditions)).orderBy(gaSocialReferrals.date).all();
22678
22898
  return rows;
22679
22899
  });
22680
22900
  app.get("/projects/:name/ga/social-referral-trend", async (request, _reply) => {
@@ -22687,8 +22907,8 @@ async function ga4Routes(app, opts) {
22687
22907
  d.setDate(d.getDate() - n);
22688
22908
  return fmt(d);
22689
22909
  };
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),
22910
+ const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and18(
22911
+ eq25(gaSocialReferrals.projectId, project.id),
22692
22912
  sql9`${gaSocialReferrals.date} >= ${from}`,
22693
22913
  sql9`${gaSocialReferrals.date} < ${to}`
22694
22914
  )).get();
@@ -22700,16 +22920,16 @@ async function ga4Routes(app, opts) {
22700
22920
  const sourceCurrent = app.db.select({
22701
22921
  source: gaSocialReferrals.source,
22702
22922
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`
22703
- }).from(gaSocialReferrals).where(and17(
22704
- eq24(gaSocialReferrals.projectId, project.id),
22923
+ }).from(gaSocialReferrals).where(and18(
22924
+ eq25(gaSocialReferrals.projectId, project.id),
22705
22925
  sql9`${gaSocialReferrals.date} >= ${daysAgo(7)}`,
22706
22926
  sql9`${gaSocialReferrals.date} < ${fmt(today)}`
22707
22927
  )).groupBy(gaSocialReferrals.source).all();
22708
22928
  const sourcePrev = app.db.select({
22709
22929
  source: gaSocialReferrals.source,
22710
22930
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`
22711
- }).from(gaSocialReferrals).where(and17(
22712
- eq24(gaSocialReferrals.projectId, project.id),
22931
+ }).from(gaSocialReferrals).where(and18(
22932
+ eq25(gaSocialReferrals.projectId, project.id),
22713
22933
  sql9`${gaSocialReferrals.date} >= ${daysAgo(14)}`,
22714
22934
  sql9`${gaSocialReferrals.date} < ${daysAgo(7)}`
22715
22935
  )).groupBy(gaSocialReferrals.source).all();
@@ -22750,16 +22970,16 @@ async function ga4Routes(app, opts) {
22750
22970
  return fmt(d);
22751
22971
  };
22752
22972
  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),
22973
+ 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();
22974
+ 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();
22975
+ 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();
22976
+ const sumAi = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and18(
22977
+ eq25(gaAiReferrals.projectId, project.id),
22758
22978
  sql9`${gaAiReferrals.date} >= ${from}`,
22759
22979
  sql9`${gaAiReferrals.date} < ${to}`,
22760
- eq24(gaAiReferrals.sourceDimension, "session")
22980
+ eq25(gaAiReferrals.sourceDimension, "session")
22761
22981
  )).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();
22982
+ 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
22983
  const todayStr = fmt(today);
22764
22984
  const buildTrend = (sum) => {
22765
22985
  const c7 = sum(daysAgo(7), todayStr)?.sessions ?? 0;
@@ -22768,17 +22988,17 @@ async function ga4Routes(app, opts) {
22768
22988
  const p30 = sum(daysAgo(60), daysAgo(30))?.sessions ?? 0;
22769
22989
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
22770
22990
  };
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),
22991
+ const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and18(
22992
+ eq25(gaAiReferrals.projectId, project.id),
22773
22993
  sql9`${gaAiReferrals.date} >= ${daysAgo(7)}`,
22774
22994
  sql9`${gaAiReferrals.date} < ${todayStr}`,
22775
- eq24(gaAiReferrals.sourceDimension, "session")
22995
+ eq25(gaAiReferrals.sourceDimension, "session")
22776
22996
  )).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),
22997
+ const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and18(
22998
+ eq25(gaAiReferrals.projectId, project.id),
22779
22999
  sql9`${gaAiReferrals.date} >= ${daysAgo(14)}`,
22780
23000
  sql9`${gaAiReferrals.date} < ${daysAgo(7)}`,
22781
- eq24(gaAiReferrals.sourceDimension, "session")
23001
+ eq25(gaAiReferrals.sourceDimension, "session")
22782
23002
  )).groupBy(gaAiReferrals.source).all();
22783
23003
  const findBiggestMover = (current, prev) => {
22784
23004
  const prevMap = new Map(prev.map((r) => [r.source, r.sessions]));
@@ -22794,8 +23014,8 @@ async function ga4Routes(app, opts) {
22794
23014
  }
22795
23015
  return mover;
22796
23016
  };
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();
23017
+ 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();
23018
+ 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
23019
  return {
22800
23020
  total: buildTrend(sumTotal),
22801
23021
  organic: buildTrend(sumOrganic),
@@ -22810,14 +23030,14 @@ async function ga4Routes(app, opts) {
22810
23030
  const project = resolveProject(app.db, request.params.name);
22811
23031
  requireGa4Connection(opts, project.name, project.canonicalDomain);
22812
23032
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
22813
- const conditions = [eq24(gaTrafficSnapshots.projectId, project.id)];
23033
+ const conditions = [eq25(gaTrafficSnapshots.projectId, project.id)];
22814
23034
  if (cutoffDate) conditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
22815
23035
  const rows = app.db.select({
22816
23036
  date: gaTrafficSnapshots.date,
22817
23037
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
22818
23038
  organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
22819
23039
  users: sql9`SUM(${gaTrafficSnapshots.users})`
22820
- }).from(gaTrafficSnapshots).where(and17(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
23040
+ }).from(gaTrafficSnapshots).where(and18(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
22821
23041
  return rows.map((r) => ({
22822
23042
  date: r.date,
22823
23043
  sessions: r.sessions ?? 0,
@@ -22833,7 +23053,7 @@ async function ga4Routes(app, opts) {
22833
23053
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
22834
23054
  organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
22835
23055
  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();
23056
+ }).from(gaTrafficSnapshots).where(eq25(gaTrafficSnapshots.projectId, project.id)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
22837
23057
  return {
22838
23058
  pages: trafficPages.map((r) => ({
22839
23059
  landingPage: r.landingPage,
@@ -24481,7 +24701,7 @@ async function wordpressRoutes(app, opts) {
24481
24701
 
24482
24702
  // ../api-routes/src/backlinks.ts
24483
24703
  import crypto22 from "crypto";
24484
- import { and as and19, asc as asc3, desc as desc13, eq as eq25, sql as sql10 } from "drizzle-orm";
24704
+ import { and as and20, asc as asc3, desc as desc14, eq as eq26, sql as sql10 } from "drizzle-orm";
24485
24705
 
24486
24706
  // ../integration-commoncrawl/src/constants.ts
24487
24707
  import os2 from "os";
@@ -24884,7 +25104,7 @@ function pruneCachedRelease(release, opts = {}) {
24884
25104
  }
24885
25105
 
24886
25106
  // ../api-routes/src/backlinks-filter.ts
24887
- import { and as and18, ne as ne3, notLike } from "drizzle-orm";
25107
+ import { and as and19, ne as ne3, notLike } from "drizzle-orm";
24888
25108
  var BACKLINK_FILTER_PATTERNS = [
24889
25109
  "*.google.com",
24890
25110
  "*.googleusercontent.com",
@@ -24907,7 +25127,7 @@ function backlinkCrawlerExclusionClause() {
24907
25127
  conditions.push(ne3(backlinkDomains.linkingDomain, pattern));
24908
25128
  }
24909
25129
  }
24910
- const combined = and18(...conditions);
25130
+ const combined = and19(...conditions);
24911
25131
  if (!combined) throw new Error("BACKLINK_FILTER_PATTERNS is unexpectedly empty");
24912
25132
  return combined;
24913
25133
  }
@@ -24978,11 +25198,11 @@ function mapRunRow(row) {
24978
25198
  }
24979
25199
  function latestSummaryForProject(db, projectId, source, release) {
24980
25200
  const conditions = [
24981
- eq25(backlinkSummaries.projectId, projectId),
24982
- eq25(backlinkSummaries.source, source)
25201
+ eq26(backlinkSummaries.projectId, projectId),
25202
+ eq26(backlinkSummaries.source, source)
24983
25203
  ];
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();
25204
+ if (release) conditions.push(eq26(backlinkSummaries.release, release));
25205
+ return db.select().from(backlinkSummaries).where(and20(...conditions)).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
24986
25206
  }
24987
25207
  function parseExcludeCrawlers(value) {
24988
25208
  if (!value) return false;
@@ -24990,12 +25210,12 @@ function parseExcludeCrawlers(value) {
24990
25210
  return lower === "1" || lower === "true" || lower === "yes";
24991
25211
  }
24992
25212
  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)
25213
+ const baseDomainCondition = and20(
25214
+ eq26(backlinkDomains.projectId, base.projectId),
25215
+ eq26(backlinkDomains.source, base.source),
25216
+ eq26(backlinkDomains.release, base.release)
24997
25217
  );
24998
- const filteredCondition = and19(baseDomainCondition, backlinkCrawlerExclusionClause());
25218
+ const filteredCondition = and20(baseDomainCondition, backlinkCrawlerExclusionClause());
24999
25219
  const unfilteredAgg = db.select({
25000
25220
  count: sql10`count(*)`,
25001
25221
  total: sql10`coalesce(sum(${backlinkDomains.numHosts}), 0)`
@@ -25004,7 +25224,7 @@ function computeFilteredSummary(db, base) {
25004
25224
  count: sql10`count(*)`,
25005
25225
  total: sql10`coalesce(sum(${backlinkDomains.numHosts}), 0)`
25006
25226
  }).from(backlinkDomains).where(filteredCondition).get();
25007
- const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc13(backlinkDomains.numHosts)).limit(10).all();
25227
+ const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc14(backlinkDomains.numHosts)).limit(10).all();
25008
25228
  const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
25009
25229
  const totalHosts = Number(filteredAgg?.total ?? 0);
25010
25230
  const unfilteredLinkingDomains = Number(unfilteredAgg?.count ?? 0);
@@ -25025,13 +25245,13 @@ function computeFilteredSummary(db, base) {
25025
25245
  };
25026
25246
  }
25027
25247
  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();
25248
+ const summary = db.select().from(backlinkSummaries).where(and20(eq26(backlinkSummaries.projectId, projectId), eq26(backlinkSummaries.source, source))).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
25029
25249
  let totalLinkingDomains = summary?.totalLinkingDomains ?? 0;
25030
25250
  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),
25251
+ const filtered = db.select({ count: sql10`count(*)` }).from(backlinkDomains).where(and20(
25252
+ eq26(backlinkDomains.projectId, projectId),
25253
+ eq26(backlinkDomains.source, source),
25254
+ eq26(backlinkDomains.release, summary.release),
25035
25255
  backlinkCrawlerExclusionClause()
25036
25256
  )).get();
25037
25257
  totalLinkingDomains = Number(filtered?.count ?? 0);
@@ -25046,7 +25266,7 @@ function buildSourceAvailability(db, projectId, source, connected, excludeCrawle
25046
25266
  };
25047
25267
  }
25048
25268
  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();
25269
+ const ccReadySync = db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
25050
25270
  const ccConnected = project.autoExtractBacklinks === true && !!ccReadySync;
25051
25271
  const bingConnected = !!bingStore?.getConnection(project.canonicalDomain);
25052
25272
  const sources = [
@@ -25102,7 +25322,7 @@ async function backlinksRoutes(app, opts) {
25102
25322
  "@duckdb/node-api is not installed. Run `canonry backlinks install` to enable the backlinks feature."
25103
25323
  );
25104
25324
  }
25105
- const existing = app.db.select().from(ccReleaseSyncs).where(eq25(ccReleaseSyncs.release, release)).get();
25325
+ const existing = app.db.select().from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.release, release)).get();
25106
25326
  const now = (/* @__PURE__ */ new Date()).toISOString();
25107
25327
  if (existing) {
25108
25328
  if (NON_TERMINAL_SYNC_STATUSES.has(existing.status)) {
@@ -25113,9 +25333,9 @@ async function backlinksRoutes(app, opts) {
25113
25333
  phaseDetail: null,
25114
25334
  error: null,
25115
25335
  updatedAt: now
25116
- }).where(eq25(ccReleaseSyncs.id, existing.id)).run();
25336
+ }).where(eq26(ccReleaseSyncs.id, existing.id)).run();
25117
25337
  opts.onReleaseSyncRequested(existing.id, release);
25118
- const refreshed = app.db.select().from(ccReleaseSyncs).where(eq25(ccReleaseSyncs.id, existing.id)).get();
25338
+ const refreshed = app.db.select().from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.id, existing.id)).get();
25119
25339
  return reply.status(200).send(mapSyncRow(refreshed));
25120
25340
  }
25121
25341
  const id = crypto22.randomUUID();
@@ -25127,15 +25347,15 @@ async function backlinksRoutes(app, opts) {
25127
25347
  updatedAt: now
25128
25348
  }).run();
25129
25349
  opts.onReleaseSyncRequested(id, release);
25130
- const inserted = app.db.select().from(ccReleaseSyncs).where(eq25(ccReleaseSyncs.id, id)).get();
25350
+ const inserted = app.db.select().from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.id, id)).get();
25131
25351
  return reply.status(201).send(mapSyncRow(inserted));
25132
25352
  });
25133
25353
  app.get("/backlinks/syncs/latest", async (_request, reply) => {
25134
- const row = app.db.select().from(ccReleaseSyncs).orderBy(desc13(ccReleaseSyncs.updatedAt)).limit(1).get();
25354
+ const row = app.db.select().from(ccReleaseSyncs).orderBy(desc14(ccReleaseSyncs.updatedAt)).limit(1).get();
25135
25355
  return reply.send(row ? mapSyncRow(row) : null);
25136
25356
  });
25137
25357
  app.get("/backlinks/syncs", async (_request, reply) => {
25138
- const rows = app.db.select().from(ccReleaseSyncs).orderBy(desc13(ccReleaseSyncs.updatedAt)).all();
25358
+ const rows = app.db.select().from(ccReleaseSyncs).orderBy(desc14(ccReleaseSyncs.updatedAt)).all();
25139
25359
  return reply.send(rows.map(mapSyncRow));
25140
25360
  });
25141
25361
  app.get("/backlinks/releases", async (_request, reply) => {
@@ -25185,7 +25405,7 @@ async function backlinksRoutes(app, opts) {
25185
25405
  createdAt: now
25186
25406
  }).run();
25187
25407
  opts.onBacklinkExtractRequested(runId, project.id, release);
25188
- const run = app.db.select().from(runs).where(eq25(runs.id, runId)).get();
25408
+ const run = app.db.select().from(runs).where(eq26(runs.id, runId)).get();
25189
25409
  return reply.status(201).send(mapRunRow(run));
25190
25410
  });
25191
25411
  app.get(
@@ -25211,18 +25431,18 @@ async function backlinksRoutes(app, opts) {
25211
25431
  const limit = Math.min(Math.max(parseInt(request.query.limit ?? "50", 10) || 50, 1), 500);
25212
25432
  const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
25213
25433
  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)
25434
+ const baseDomainCondition = and20(
25435
+ eq26(backlinkDomains.projectId, project.id),
25436
+ eq26(backlinkDomains.source, source),
25437
+ eq26(backlinkDomains.release, targetRelease)
25218
25438
  );
25219
- const domainCondition = excludeCrawlers ? and19(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
25439
+ const domainCondition = excludeCrawlers ? and20(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
25220
25440
  const totalRow = app.db.select({ count: sql10`count(*)` }).from(backlinkDomains).where(domainCondition).get();
25221
25441
  const rows = app.db.select({
25222
25442
  linkingDomain: backlinkDomains.linkingDomain,
25223
25443
  numHosts: backlinkDomains.numHosts,
25224
25444
  source: backlinkDomains.source
25225
- }).from(backlinkDomains).where(domainCondition).orderBy(desc13(backlinkDomains.numHosts)).limit(limit).offset(offset).all();
25445
+ }).from(backlinkDomains).where(domainCondition).orderBy(desc14(backlinkDomains.numHosts)).limit(limit).offset(offset).all();
25226
25446
  let summary = null;
25227
25447
  if (summaryRow) {
25228
25448
  summary = excludeCrawlers ? computeFilteredSummary(app.db, summaryRow) : mapSummaryRow(summaryRow);
@@ -25240,7 +25460,7 @@ async function backlinksRoutes(app, opts) {
25240
25460
  async (request, reply) => {
25241
25461
  const project = resolveProject(app.db, request.params.name);
25242
25462
  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();
25463
+ const rows = app.db.select().from(backlinkSummaries).where(and20(eq26(backlinkSummaries.projectId, project.id), eq26(backlinkSummaries.source, source))).orderBy(asc3(backlinkSummaries.queriedAt)).all();
25244
25464
  const response = rows.map((r) => ({
25245
25465
  release: r.release,
25246
25466
  totalLinkingDomains: r.totalLinkingDomains,
@@ -25287,7 +25507,7 @@ async function backlinksRoutes(app, opts) {
25287
25507
  createdAt: now
25288
25508
  }).run();
25289
25509
  opts.onBingBacklinkSyncRequested(runId, project.id);
25290
- const run = app.db.select().from(runs).where(eq25(runs.id, runId)).get();
25510
+ const run = app.db.select().from(runs).where(eq26(runs.id, runId)).get();
25291
25511
  return reply.status(201).send(mapRunRow(run));
25292
25512
  }
25293
25513
  );
@@ -25296,7 +25516,7 @@ async function backlinksRoutes(app, opts) {
25296
25516
  // ../api-routes/src/traffic.ts
25297
25517
  import crypto24 from "crypto";
25298
25518
  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";
25519
+ import { and as and21, desc as desc15, eq as eq27, gte as gte4, lte as lte3, sql as sql11 } from "drizzle-orm";
25300
25520
 
25301
25521
  // ../integration-cloud-run/src/auth.ts
25302
25522
  import crypto23 from "crypto";
@@ -29113,8 +29333,8 @@ async function runBackfillTask(options) {
29113
29333
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
29114
29334
  try {
29115
29335
  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();
29336
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq27(runs.id, runId)).run();
29337
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq27(trafficSources.id, sourceRow.id)).run();
29118
29338
  });
29119
29339
  } catch {
29120
29340
  }
@@ -29129,7 +29349,7 @@ async function runBackfillTask(options) {
29129
29349
  if (allEvents.length === 0) {
29130
29350
  const finishedAt2 = (/* @__PURE__ */ new Date()).toISOString();
29131
29351
  try {
29132
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq26(runs.id, runId)).run();
29352
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq27(runs.id, runId)).run();
29133
29353
  } catch {
29134
29354
  }
29135
29355
  return;
@@ -29151,29 +29371,29 @@ async function runBackfillTask(options) {
29151
29371
  try {
29152
29372
  app.db.transaction((tx) => {
29153
29373
  tx.delete(crawlerEventsHourly).where(
29154
- and20(
29155
- eq26(crawlerEventsHourly.sourceId, sourceRow.id),
29374
+ and21(
29375
+ eq27(crawlerEventsHourly.sourceId, sourceRow.id),
29156
29376
  gte4(crawlerEventsHourly.tsHour, windowStartIso),
29157
29377
  lte3(crawlerEventsHourly.tsHour, windowEndIso)
29158
29378
  )
29159
29379
  ).run();
29160
29380
  tx.delete(aiUserFetchEventsHourly).where(
29161
- and20(
29162
- eq26(aiUserFetchEventsHourly.sourceId, sourceRow.id),
29381
+ and21(
29382
+ eq27(aiUserFetchEventsHourly.sourceId, sourceRow.id),
29163
29383
  gte4(aiUserFetchEventsHourly.tsHour, windowStartIso),
29164
29384
  lte3(aiUserFetchEventsHourly.tsHour, windowEndIso)
29165
29385
  )
29166
29386
  ).run();
29167
29387
  tx.delete(aiReferralEventsHourly).where(
29168
- and20(
29169
- eq26(aiReferralEventsHourly.sourceId, sourceRow.id),
29388
+ and21(
29389
+ eq27(aiReferralEventsHourly.sourceId, sourceRow.id),
29170
29390
  gte4(aiReferralEventsHourly.tsHour, windowStartIso),
29171
29391
  lte3(aiReferralEventsHourly.tsHour, windowEndIso)
29172
29392
  )
29173
29393
  ).run();
29174
29394
  tx.delete(rawEventSamples).where(
29175
- and20(
29176
- eq26(rawEventSamples.sourceId, sourceRow.id),
29395
+ and21(
29396
+ eq27(rawEventSamples.sourceId, sourceRow.id),
29177
29397
  gte4(rawEventSamples.ts, windowStartIso),
29178
29398
  lte3(rawEventSamples.ts, windowEndIso)
29179
29399
  )
@@ -29262,8 +29482,8 @@ async function runBackfillTask(options) {
29262
29482
  lastError: null,
29263
29483
  lastEventIds: newRingBuffer,
29264
29484
  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();
29485
+ }).where(eq27(trafficSources.id, sourceRow.id)).run();
29486
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq27(runs.id, runId)).run();
29267
29487
  });
29268
29488
  } catch (e) {
29269
29489
  markFailed(`Backfill rollup write failed: ${e instanceof Error ? e.message : String(e)}`);
@@ -29345,7 +29565,7 @@ async function trafficRoutes(app, opts) {
29345
29565
  createdAt: existing?.createdAt ?? now,
29346
29566
  updatedAt: now
29347
29567
  });
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);
29568
+ 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
29569
  const config = {
29350
29570
  gcpProjectId,
29351
29571
  serviceName: serviceName ?? null,
@@ -29361,8 +29581,8 @@ async function trafficRoutes(app, opts) {
29361
29581
  lastError: null,
29362
29582
  configJson: config,
29363
29583
  updatedAt: now
29364
- }).where(eq26(trafficSources.id, activeSource.id)).run();
29365
- sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, activeSource.id)).get();
29584
+ }).where(eq27(trafficSources.id, activeSource.id)).run();
29585
+ sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, activeSource.id)).get();
29366
29586
  } else {
29367
29587
  const newId = crypto24.randomUUID();
29368
29588
  app.db.insert(trafficSources).values({
@@ -29379,7 +29599,7 @@ async function trafficRoutes(app, opts) {
29379
29599
  createdAt: now,
29380
29600
  updatedAt: now
29381
29601
  }).run();
29382
- sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, newId)).get();
29602
+ sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, newId)).get();
29383
29603
  }
29384
29604
  writeAuditLog(app.db, {
29385
29605
  projectId: project.id,
@@ -29431,7 +29651,7 @@ async function trafficRoutes(app, opts) {
29431
29651
  createdAt: existing?.createdAt ?? now,
29432
29652
  updatedAt: now
29433
29653
  });
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);
29654
+ 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
29655
  const config = { baseUrl, username };
29436
29656
  const fallbackName = displayName ?? `WordPress \xB7 ${new URL(baseUrl).host}`;
29437
29657
  let sourceRow;
@@ -29442,8 +29662,8 @@ async function trafficRoutes(app, opts) {
29442
29662
  lastError: null,
29443
29663
  configJson: config,
29444
29664
  updatedAt: now
29445
- }).where(eq26(trafficSources.id, activeSource.id)).run();
29446
- sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, activeSource.id)).get();
29665
+ }).where(eq27(trafficSources.id, activeSource.id)).run();
29666
+ sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, activeSource.id)).get();
29447
29667
  } else {
29448
29668
  const newId = crypto24.randomUUID();
29449
29669
  app.db.insert(trafficSources).values({
@@ -29460,7 +29680,7 @@ async function trafficRoutes(app, opts) {
29460
29680
  createdAt: now,
29461
29681
  updatedAt: now
29462
29682
  }).run();
29463
- sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, newId)).get();
29683
+ sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, newId)).get();
29464
29684
  }
29465
29685
  writeAuditLog(app.db, {
29466
29686
  projectId: project.id,
@@ -29514,7 +29734,7 @@ async function trafficRoutes(app, opts) {
29514
29734
  createdAt: existing?.createdAt ?? now,
29515
29735
  updatedAt: now
29516
29736
  });
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);
29737
+ 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
29738
  const config = { projectId, teamId, environment };
29519
29739
  const fallbackName = displayName ?? `Vercel \xB7 ${projectId}`;
29520
29740
  const { sourceRow, scheduleCreated } = app.db.transaction((tx) => {
@@ -29526,8 +29746,8 @@ async function trafficRoutes(app, opts) {
29526
29746
  lastError: null,
29527
29747
  configJson: config,
29528
29748
  updatedAt: now
29529
- }).where(eq26(trafficSources.id, activeSource.id)).run();
29530
- row = tx.select().from(trafficSources).where(eq26(trafficSources.id, activeSource.id)).get();
29749
+ }).where(eq27(trafficSources.id, activeSource.id)).run();
29750
+ row = tx.select().from(trafficSources).where(eq27(trafficSources.id, activeSource.id)).get();
29531
29751
  } else {
29532
29752
  const newId = crypto24.randomUUID();
29533
29753
  tx.insert(trafficSources).values({
@@ -29552,12 +29772,12 @@ async function trafficRoutes(app, opts) {
29552
29772
  createdAt: now,
29553
29773
  updatedAt: now
29554
29774
  }).run();
29555
- row = tx.select().from(trafficSources).where(eq26(trafficSources.id, newId)).get();
29775
+ row = tx.select().from(trafficSources).where(eq27(trafficSources.id, newId)).get();
29556
29776
  }
29557
29777
  const existingSchedule = tx.select().from(schedules).where(
29558
- and20(
29559
- eq26(schedules.projectId, project.id),
29560
- eq26(schedules.kind, SchedulableRunKinds["traffic-sync"])
29778
+ and21(
29779
+ eq27(schedules.projectId, project.id),
29780
+ eq27(schedules.kind, SchedulableRunKinds["traffic-sync"])
29561
29781
  )
29562
29782
  ).get();
29563
29783
  let created = false;
@@ -29606,7 +29826,7 @@ async function trafficRoutes(app, opts) {
29606
29826
  });
29607
29827
  app.post("/projects/:name/traffic/sources/:id/sync", async (request) => {
29608
29828
  const project = resolveProject(app.db, request.params.name);
29609
- const sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, request.params.id)).get();
29829
+ const sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, request.params.id)).get();
29610
29830
  if (!sourceRow || sourceRow.projectId !== project.id) {
29611
29831
  throw notFound("Traffic source", request.params.id);
29612
29832
  }
@@ -29632,8 +29852,8 @@ async function trafficRoutes(app, opts) {
29632
29852
  const markFailed = (msg, errorCode) => {
29633
29853
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
29634
29854
  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();
29855
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq27(runs.id, runId)).run();
29856
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq27(trafficSources.id, sourceRow.id)).run();
29637
29857
  });
29638
29858
  try {
29639
29859
  opts.onTrafficSynced?.({
@@ -29714,7 +29934,7 @@ async function trafficRoutes(app, opts) {
29714
29934
  }
29715
29935
  const credential = credentialStore.getConnection(project.name);
29716
29936
  if (!credential) {
29717
- app.db.delete(runs).where(eq26(runs.id, runId)).run();
29937
+ app.db.delete(runs).where(eq27(runs.id, runId)).run();
29718
29938
  throw validationError(
29719
29939
  `No WordPress credential found for project "${project.name}". Run "canonry traffic connect wordpress" first.`
29720
29940
  );
@@ -29763,12 +29983,12 @@ async function trafficRoutes(app, opts) {
29763
29983
  auditAction = "traffic.vercel.synced";
29764
29984
  const credentialStore = opts.vercelTrafficCredentialStore;
29765
29985
  if (!credentialStore) {
29766
- app.db.delete(runs).where(eq26(runs.id, runId)).run();
29986
+ app.db.delete(runs).where(eq27(runs.id, runId)).run();
29767
29987
  throw validationError("Vercel traffic credential storage is not configured for this deployment");
29768
29988
  }
29769
29989
  const credential = credentialStore.getConnection(project.name);
29770
29990
  if (!credential) {
29771
- app.db.delete(runs).where(eq26(runs.id, runId)).run();
29991
+ app.db.delete(runs).where(eq27(runs.id, runId)).run();
29772
29992
  throw validationError(
29773
29993
  `No Vercel credential found for project "${project.name}". Run "canonry traffic connect vercel" first.`
29774
29994
  );
@@ -29868,7 +30088,7 @@ async function trafficRoutes(app, opts) {
29868
30088
  let aiReferralHitsCount = 0;
29869
30089
  let unknownHitsCount = 0;
29870
30090
  app.db.transaction((tx) => {
29871
- const latestRow = tx.select().from(trafficSources).where(eq26(trafficSources.id, sourceRow.id)).get();
30091
+ const latestRow = tx.select().from(trafficSources).where(eq27(trafficSources.id, sourceRow.id)).get();
29872
30092
  const previousIds = latestRow.lastEventIds ?? [];
29873
30093
  const seenEventIds = new Set(previousIds);
29874
30094
  const dedupedEvents = seenEventIds.size === 0 ? allEvents : allEvents.filter((e) => !seenEventIds.has(e.eventId));
@@ -30036,8 +30256,8 @@ async function trafficRoutes(app, opts) {
30036
30256
  if (sourceRow.sourceType === TrafficSourceTypes.wordpress) {
30037
30257
  sourceUpdate.lastCursor = nextCursor ?? null;
30038
30258
  }
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();
30259
+ tx.update(trafficSources).set(sourceUpdate).where(eq27(trafficSources.id, sourceRow.id)).run();
30260
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq27(runs.id, runId)).run();
30041
30261
  });
30042
30262
  writeAuditLog(app.db, {
30043
30263
  projectId: project.id,
@@ -30089,7 +30309,7 @@ async function trafficRoutes(app, opts) {
30089
30309
  });
30090
30310
  app.post("/projects/:name/traffic/sources/:id/backfill", async (request) => {
30091
30311
  const project = resolveProject(app.db, request.params.name);
30092
- const sourceRow = app.db.select().from(trafficSources).where(eq26(trafficSources.id, request.params.id)).get();
30312
+ const sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, request.params.id)).get();
30093
30313
  if (!sourceRow || sourceRow.projectId !== project.id) {
30094
30314
  throw notFound("Traffic source", request.params.id);
30095
30315
  }
@@ -30274,36 +30494,36 @@ async function trafficRoutes(app, opts) {
30274
30494
  });
30275
30495
  function buildSourceDetail(projectId, row, since) {
30276
30496
  const crawlerTotals = app.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
30277
- and20(
30278
- eq26(crawlerEventsHourly.sourceId, row.id),
30497
+ and21(
30498
+ eq27(crawlerEventsHourly.sourceId, row.id),
30279
30499
  gte4(crawlerEventsHourly.tsHour, since)
30280
30500
  )
30281
30501
  ).get();
30282
30502
  const aiUserFetchTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
30283
- and20(
30284
- eq26(aiUserFetchEventsHourly.sourceId, row.id),
30503
+ and21(
30504
+ eq27(aiUserFetchEventsHourly.sourceId, row.id),
30285
30505
  gte4(aiUserFetchEventsHourly.tsHour, since)
30286
30506
  )
30287
30507
  ).get();
30288
30508
  const aiTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
30289
- and20(
30290
- eq26(aiReferralEventsHourly.sourceId, row.id),
30509
+ and21(
30510
+ eq27(aiReferralEventsHourly.sourceId, row.id),
30291
30511
  gte4(aiReferralEventsHourly.tsHour, since)
30292
30512
  )
30293
30513
  ).get();
30294
30514
  const sampleTotals = app.db.select({ total: sql11`COUNT(*)` }).from(rawEventSamples).where(
30295
- and20(
30296
- eq26(rawEventSamples.sourceId, row.id),
30515
+ and21(
30516
+ eq27(rawEventSamples.sourceId, row.id),
30297
30517
  gte4(rawEventSamples.ts, since)
30298
30518
  )
30299
30519
  ).get();
30300
30520
  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)
30521
+ and21(
30522
+ eq27(runs.projectId, projectId),
30523
+ eq27(runs.kind, RunKinds["traffic-sync"]),
30524
+ eq27(runs.sourceId, row.id)
30305
30525
  )
30306
- ).orderBy(desc14(runs.startedAt)).limit(1).get();
30526
+ ).orderBy(desc15(runs.startedAt)).limit(1).get();
30307
30527
  return {
30308
30528
  ...rowToDto(row),
30309
30529
  totals24h: {
@@ -30329,7 +30549,7 @@ async function trafficRoutes(app, opts) {
30329
30549
  "`advanceToNow` must be `true`. There is no implicit reset."
30330
30550
  );
30331
30551
  }
30332
- const sourceRow = app.db.select().from(trafficSources).where(and20(eq26(trafficSources.projectId, project.id), eq26(trafficSources.id, request.params.id))).get();
30552
+ const sourceRow = app.db.select().from(trafficSources).where(and21(eq27(trafficSources.projectId, project.id), eq27(trafficSources.id, request.params.id))).get();
30333
30553
  if (!sourceRow) {
30334
30554
  throw notFound("traffic source", request.params.id);
30335
30555
  }
@@ -30346,7 +30566,7 @@ async function trafficRoutes(app, opts) {
30346
30566
  status: TrafficSourceStatuses.connected,
30347
30567
  lastError: null,
30348
30568
  updatedAt: now
30349
- }).where(eq26(trafficSources.id, sourceRow.id)).run();
30569
+ }).where(eq27(trafficSources.id, sourceRow.id)).run();
30350
30570
  writeAuditLog(tx, auditFromRequest(request, {
30351
30571
  projectId: project.id,
30352
30572
  actor: "api",
@@ -30354,20 +30574,20 @@ async function trafficRoutes(app, opts) {
30354
30574
  entityType: "traffic_source",
30355
30575
  entityId: sourceRow.id
30356
30576
  }));
30357
- updatedRow = tx.select().from(trafficSources).where(eq26(trafficSources.id, sourceRow.id)).get();
30577
+ updatedRow = tx.select().from(trafficSources).where(eq27(trafficSources.id, sourceRow.id)).get();
30358
30578
  });
30359
30579
  return buildSourceDetail(project.id, updatedRow, new Date(Date.now() - 24 * 60 * 6e4).toISOString());
30360
30580
  });
30361
30581
  app.get("/projects/:name/traffic/sources", async (request) => {
30362
30582
  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();
30583
+ const rows = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
30364
30584
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map(rowToDto);
30365
30585
  const response = { sources };
30366
30586
  return response;
30367
30587
  });
30368
30588
  app.get("/projects/:name/traffic/status", async (request) => {
30369
30589
  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();
30590
+ const rows = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
30371
30591
  const since = new Date(Date.now() - 24 * 60 * 6e4).toISOString();
30372
30592
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map((row) => buildSourceDetail(project.id, row, since));
30373
30593
  const response = { sources };
@@ -30377,7 +30597,7 @@ async function trafficRoutes(app, opts) {
30377
30597
  "/projects/:name/traffic/sources/:id",
30378
30598
  async (request) => {
30379
30599
  const project = resolveProject(app.db, request.params.name);
30380
- const row = app.db.select().from(trafficSources).where(eq26(trafficSources.id, request.params.id)).get();
30600
+ const row = app.db.select().from(trafficSources).where(eq27(trafficSources.id, request.params.id)).get();
30381
30601
  if (!row || row.projectId !== project.id) {
30382
30602
  throw notFound("Traffic source", request.params.id);
30383
30603
  }
@@ -30428,15 +30648,15 @@ async function trafficRoutes(app, opts) {
30428
30648
  let aiReferralTotal = 0;
30429
30649
  if (kind === "all" || kind === TrafficEventKinds.crawler) {
30430
30650
  const crawlerFilters = [
30431
- eq26(crawlerEventsHourly.projectId, project.id),
30651
+ eq27(crawlerEventsHourly.projectId, project.id),
30432
30652
  gte4(crawlerEventsHourly.tsHour, sinceIso),
30433
30653
  lte3(crawlerEventsHourly.tsHour, untilIso)
30434
30654
  ];
30435
- if (sourceIdParam) crawlerFilters.push(eq26(crawlerEventsHourly.sourceId, sourceIdParam));
30436
- const crawlerWhere = and20(...crawlerFilters);
30655
+ if (sourceIdParam) crawlerFilters.push(eq27(crawlerEventsHourly.sourceId, sourceIdParam));
30656
+ const crawlerWhere = and21(...crawlerFilters);
30437
30657
  const total = app.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
30438
30658
  crawlerTotal = Number(total?.total ?? 0);
30439
- const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc14(crawlerEventsHourly.tsHour)).limit(limit).all();
30659
+ const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc15(crawlerEventsHourly.tsHour)).limit(limit).all();
30440
30660
  for (const r of rows) {
30441
30661
  events.push({
30442
30662
  kind: TrafficEventKinds.crawler,
@@ -30453,15 +30673,15 @@ async function trafficRoutes(app, opts) {
30453
30673
  }
30454
30674
  if (kind === "all" || kind === TrafficEventKinds["ai-user-fetch"]) {
30455
30675
  const userFetchFilters = [
30456
- eq26(aiUserFetchEventsHourly.projectId, project.id),
30676
+ eq27(aiUserFetchEventsHourly.projectId, project.id),
30457
30677
  gte4(aiUserFetchEventsHourly.tsHour, sinceIso),
30458
30678
  lte3(aiUserFetchEventsHourly.tsHour, untilIso)
30459
30679
  ];
30460
- if (sourceIdParam) userFetchFilters.push(eq26(aiUserFetchEventsHourly.sourceId, sourceIdParam));
30461
- const userFetchWhere = and20(...userFetchFilters);
30680
+ if (sourceIdParam) userFetchFilters.push(eq27(aiUserFetchEventsHourly.sourceId, sourceIdParam));
30681
+ const userFetchWhere = and21(...userFetchFilters);
30462
30682
  const total = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(userFetchWhere).get();
30463
30683
  aiUserFetchTotal = Number(total?.total ?? 0);
30464
- const rows = app.db.select().from(aiUserFetchEventsHourly).where(userFetchWhere).orderBy(desc14(aiUserFetchEventsHourly.tsHour)).limit(limit).all();
30684
+ const rows = app.db.select().from(aiUserFetchEventsHourly).where(userFetchWhere).orderBy(desc15(aiUserFetchEventsHourly.tsHour)).limit(limit).all();
30465
30685
  for (const r of rows) {
30466
30686
  events.push({
30467
30687
  kind: TrafficEventKinds["ai-user-fetch"],
@@ -30478,15 +30698,15 @@ async function trafficRoutes(app, opts) {
30478
30698
  }
30479
30699
  if (kind === "all" || kind === TrafficEventKinds["ai-referral"]) {
30480
30700
  const aiFilters = [
30481
- eq26(aiReferralEventsHourly.projectId, project.id),
30701
+ eq27(aiReferralEventsHourly.projectId, project.id),
30482
30702
  gte4(aiReferralEventsHourly.tsHour, sinceIso),
30483
30703
  lte3(aiReferralEventsHourly.tsHour, untilIso)
30484
30704
  ];
30485
- if (sourceIdParam) aiFilters.push(eq26(aiReferralEventsHourly.sourceId, sourceIdParam));
30486
- const aiWhere = and20(...aiFilters);
30705
+ if (sourceIdParam) aiFilters.push(eq27(aiReferralEventsHourly.sourceId, sourceIdParam));
30706
+ const aiWhere = and21(...aiFilters);
30487
30707
  const total = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
30488
30708
  aiReferralTotal = Number(total?.total ?? 0);
30489
- const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc14(aiReferralEventsHourly.tsHour)).limit(limit).all();
30709
+ const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc15(aiReferralEventsHourly.tsHour)).limit(limit).all();
30490
30710
  for (const r of rows) {
30491
30711
  events.push({
30492
30712
  kind: TrafficEventKinds["ai-referral"],
@@ -30687,7 +30907,7 @@ function readInstalledManifest(skillDir) {
30687
30907
  var AGENT_CHECKS = [skillsInstalledCheck, skillsCurrentCheck];
30688
30908
 
30689
30909
  // ../api-routes/src/doctor/checks/backlinks.ts
30690
- import { and as and21, eq as eq27 } from "drizzle-orm";
30910
+ import { and as and22, eq as eq28 } from "drizzle-orm";
30691
30911
  function skippedNoProject() {
30692
30912
  return {
30693
30913
  status: CheckStatuses.skipped,
@@ -30703,8 +30923,8 @@ var BACKLINKS_CHECKS = [
30703
30923
  title: "Backlinks source connected",
30704
30924
  run: (ctx) => {
30705
30925
  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();
30926
+ const projectRow = ctx.db.select({ autoExtract: projects.autoExtractBacklinks }).from(projects).where(eq28(projects.id, ctx.project.id)).get();
30927
+ const readySync = ctx.db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq28(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
30708
30928
  const ccConnected = projectRow?.autoExtract === true && !!readySync;
30709
30929
  const bingConnected = !!ctx.bingConnectionStore?.getConnection(ctx.project.canonicalDomain);
30710
30930
  const connected = [];
@@ -30719,9 +30939,9 @@ var BACKLINKS_CHECKS = [
30719
30939
  details: { commoncrawl: ccConnected, bingWebmaster: bingConnected }
30720
30940
  };
30721
30941
  }
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)
30942
+ const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and22(
30943
+ eq28(backlinkSummaries.projectId, ctx.project.id),
30944
+ eq28(backlinkSummaries.source, BacklinkSources.commoncrawl)
30725
30945
  )).limit(1).get() : false;
30726
30946
  return {
30727
30947
  status: CheckStatuses.ok,
@@ -30881,7 +31101,7 @@ var BING_AUTH_CHECKS = [
30881
31101
  ];
30882
31102
 
30883
31103
  // ../api-routes/src/doctor/checks/content.ts
30884
- import { eq as eq28 } from "drizzle-orm";
31104
+ import { eq as eq29 } from "drizzle-orm";
30885
31105
  var WINNABILITY_COVERAGE_WARN_THRESHOLD = 0.8;
30886
31106
  var UNCLASSIFIED_DOMAIN_SAMPLE_LIMIT = 10;
30887
31107
  function skippedNoProject2() {
@@ -30894,7 +31114,7 @@ function skippedNoProject2() {
30894
31114
  }
30895
31115
  function loadProject(ctx) {
30896
31116
  if (!ctx.project) return null;
30897
- return ctx.db.select().from(projects).where(eq28(projects.id, ctx.project.id)).get() ?? null;
31117
+ return ctx.db.select().from(projects).where(eq29(projects.id, ctx.project.id)).get() ?? null;
30898
31118
  }
30899
31119
  function percent(value) {
30900
31120
  return Math.round(value * 100);
@@ -30986,7 +31206,7 @@ var CONTENT_CHECK_BY_ID = Object.fromEntries(
30986
31206
  );
30987
31207
 
30988
31208
  // ../api-routes/src/doctor/checks/ads.ts
30989
- import { eq as eq29 } from "drizzle-orm";
31209
+ import { eq as eq30 } from "drizzle-orm";
30990
31210
  var RECENT_SYNC_WARN_DAYS = 7;
30991
31211
  var RECENT_SYNC_FAIL_DAYS = 30;
30992
31212
  var adsConnectionCheck = {
@@ -31003,7 +31223,7 @@ var adsConnectionCheck = {
31003
31223
  remediation: null
31004
31224
  };
31005
31225
  }
31006
- const row = ctx.db.select().from(adsConnections).where(eq29(adsConnections.projectId, ctx.project.id)).get();
31226
+ const row = ctx.db.select().from(adsConnections).where(eq30(adsConnections.projectId, ctx.project.id)).get();
31007
31227
  if (!row) {
31008
31228
  return {
31009
31229
  status: CheckStatuses.skipped,
@@ -31053,7 +31273,7 @@ var adsRecentSyncCheck = {
31053
31273
  remediation: null
31054
31274
  };
31055
31275
  }
31056
- const row = ctx.db.select().from(adsConnections).where(eq29(adsConnections.projectId, ctx.project.id)).get();
31276
+ const row = ctx.db.select().from(adsConnections).where(eq30(adsConnections.projectId, ctx.project.id)).get();
31057
31277
  if (!row) {
31058
31278
  return {
31059
31279
  status: CheckStatuses.skipped,
@@ -31244,7 +31464,7 @@ var ga4ConnectionCheck = {
31244
31464
  var GA_AUTH_CHECKS = [ga4ConnectionCheck];
31245
31465
 
31246
31466
  // ../api-routes/src/doctor/checks/gbp-auth.ts
31247
- import { and as and22, eq as eq30 } from "drizzle-orm";
31467
+ import { and as and23, eq as eq31 } from "drizzle-orm";
31248
31468
  var RECENT_SYNC_WARN_DAYS2 = 7;
31249
31469
  var RECENT_SYNC_FAIL_DAYS2 = 30;
31250
31470
  function skippedNoProject3() {
@@ -31477,7 +31697,7 @@ var recentSyncCheck = {
31477
31697
  title: "GBP recent sync",
31478
31698
  run: (ctx) => {
31479
31699
  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();
31700
+ 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
31701
  if (selected.length === 0) {
31482
31702
  return {
31483
31703
  status: CheckStatuses.skipped,
@@ -31537,7 +31757,7 @@ var GBP_AUTH_CHECK_BY_ID = Object.fromEntries(
31537
31757
  );
31538
31758
 
31539
31759
  // ../api-routes/src/doctor/checks/places.ts
31540
- import { eq as eq31 } from "drizzle-orm";
31760
+ import { eq as eq32 } from "drizzle-orm";
31541
31761
  var apiKeyCheck = {
31542
31762
  id: "gbp.places.api-key",
31543
31763
  category: CheckCategories.auth,
@@ -31582,7 +31802,7 @@ var apiKeyCheck = {
31582
31802
  details: { tier: cfg.tier }
31583
31803
  };
31584
31804
  }
31585
- const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq31(gbpLocations.projectId, ctx.project.id)).all();
31805
+ const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq32(gbpLocations.projectId, ctx.project.id)).all();
31586
31806
  const selected = rows.filter((r) => r.selected);
31587
31807
  const locationsWithPlaceId = selected.filter((r) => Boolean(r.placeId)).length;
31588
31808
  const details = {
@@ -32033,7 +32253,7 @@ var RUNTIME_STATE_CHECKS = [
32033
32253
  ];
32034
32254
 
32035
32255
  // ../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";
32256
+ import { and as and24, eq as eq33, gte as gte5, ne as ne4, sql as sql12 } from "drizzle-orm";
32037
32257
  var RECENT_DATA_WARN_DAYS = 7;
32038
32258
  var RECENT_DATA_FAIL_DAYS = 30;
32039
32259
  function skippedNoProject5() {
@@ -32047,8 +32267,8 @@ function skippedNoProject5() {
32047
32267
  function loadProbes(ctx) {
32048
32268
  if (!ctx.project) return [];
32049
32269
  const rows = ctx.db.select().from(trafficSources).where(
32050
- and23(
32051
- eq32(trafficSources.projectId, ctx.project.id),
32270
+ and24(
32271
+ eq33(trafficSources.projectId, ctx.project.id),
32052
32272
  ne4(trafficSources.status, TrafficSourceStatuses.archived)
32053
32273
  )
32054
32274
  ).all();
@@ -32128,16 +32348,16 @@ var recentDataCheck = {
32128
32348
  const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
32129
32349
  const recentCrawlers = Number(
32130
32350
  ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
32131
- and23(
32132
- eq32(crawlerEventsHourly.projectId, ctx.project.id),
32351
+ and24(
32352
+ eq33(crawlerEventsHourly.projectId, ctx.project.id),
32133
32353
  gte5(crawlerEventsHourly.tsHour, warnCutoff)
32134
32354
  )
32135
32355
  ).get()?.total ?? 0
32136
32356
  );
32137
32357
  const recentReferrals = Number(
32138
32358
  ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
32139
- and23(
32140
- eq32(aiReferralEventsHourly.projectId, ctx.project.id),
32359
+ and24(
32360
+ eq33(aiReferralEventsHourly.projectId, ctx.project.id),
32141
32361
  gte5(aiReferralEventsHourly.tsHour, warnCutoff)
32142
32362
  )
32143
32363
  ).get()?.total ?? 0
@@ -32152,16 +32372,16 @@ var recentDataCheck = {
32152
32372
  }
32153
32373
  const olderCrawlers = Number(
32154
32374
  ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
32155
- and23(
32156
- eq32(crawlerEventsHourly.projectId, ctx.project.id),
32375
+ and24(
32376
+ eq33(crawlerEventsHourly.projectId, ctx.project.id),
32157
32377
  gte5(crawlerEventsHourly.tsHour, failCutoff)
32158
32378
  )
32159
32379
  ).get()?.total ?? 0
32160
32380
  );
32161
32381
  const olderReferrals = Number(
32162
32382
  ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
32163
- and23(
32164
- eq32(aiReferralEventsHourly.projectId, ctx.project.id),
32383
+ and24(
32384
+ eq33(aiReferralEventsHourly.projectId, ctx.project.id),
32165
32385
  gte5(aiReferralEventsHourly.tsHour, failCutoff)
32166
32386
  )
32167
32387
  ).get()?.total ?? 0
@@ -32575,7 +32795,7 @@ async function doctorRoutes(app, opts) {
32575
32795
 
32576
32796
  // ../api-routes/src/discovery/routes.ts
32577
32797
  import crypto26 from "crypto";
32578
- import { and as and24, desc as desc15, eq as eq33, gte as gte6, inArray as inArray11 } from "drizzle-orm";
32798
+ import { and as and25, desc as desc16, eq as eq34, gte as gte6, inArray as inArray12 } from "drizzle-orm";
32579
32799
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
32580
32800
  async function discoveryRoutes(app, opts) {
32581
32801
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -32607,16 +32827,16 @@ async function discoveryRoutes(app, opts) {
32607
32827
  const now = (/* @__PURE__ */ new Date()).toISOString();
32608
32828
  const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
32609
32829
  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, [
32830
+ const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and25(
32831
+ eq34(discoverySessions.projectId, project.id),
32832
+ eq34(discoverySessions.icpDescription, icpDescription),
32833
+ inArray12(discoverySessions.status, [
32614
32834
  DiscoverySessionStatuses.queued,
32615
32835
  DiscoverySessionStatuses.seeding,
32616
32836
  DiscoverySessionStatuses.probing
32617
32837
  ]),
32618
32838
  gte6(discoverySessions.createdAt, ageFloorIso)
32619
- )).orderBy(desc15(discoverySessions.createdAt)).get();
32839
+ )).orderBy(desc16(discoverySessions.createdAt)).get();
32620
32840
  if (existing && existing.runId) {
32621
32841
  return { reused: true, sessionId: existing.id, runId: existing.runId };
32622
32842
  }
@@ -32679,7 +32899,7 @@ async function discoveryRoutes(app, opts) {
32679
32899
  const project = resolveProject(app.db, request.params.name);
32680
32900
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
32681
32901
  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();
32902
+ const rows = app.db.select().from(discoverySessions).where(eq34(discoverySessions.projectId, project.id)).orderBy(desc16(discoverySessions.createdAt)).limit(limit).all();
32683
32903
  return reply.send(rows.map(serializeSession));
32684
32904
  }
32685
32905
  );
@@ -32687,11 +32907,11 @@ async function discoveryRoutes(app, opts) {
32687
32907
  "/projects/:name/discover/sessions/:id",
32688
32908
  async (request, reply) => {
32689
32909
  const project = resolveProject(app.db, request.params.name);
32690
- const session = app.db.select().from(discoverySessions).where(eq33(discoverySessions.id, request.params.id)).get();
32910
+ const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
32691
32911
  if (!session || session.projectId !== project.id) {
32692
32912
  throw notFound("Discovery session", request.params.id);
32693
32913
  }
32694
- const probeRows = app.db.select().from(discoveryProbes).where(eq33(discoveryProbes.sessionId, session.id)).all();
32914
+ const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
32695
32915
  const detail = {
32696
32916
  ...serializeSession(session),
32697
32917
  probes: probeRows.map(serializeProbe)
@@ -32699,16 +32919,82 @@ async function discoveryRoutes(app, opts) {
32699
32919
  return reply.send(detail);
32700
32920
  }
32701
32921
  );
32922
+ app.get(
32923
+ "/projects/:name/discover/sessions/:id/harvest",
32924
+ async (request, reply) => {
32925
+ const project = resolveProject(app.db, request.params.name);
32926
+ const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
32927
+ if (!session || session.projectId !== project.id) {
32928
+ throw notFound("Discovery session", request.params.id);
32929
+ }
32930
+ const parsedFloor = parseInt(request.query.minProbeHits ?? "", 10);
32931
+ const minProbeHits = Number.isNaN(parsedFloor) || parsedFloor < 1 ? 1 : parsedFloor;
32932
+ const applyAnchor = request.query.anchor !== "false";
32933
+ const provider = session.seedProvider ?? "gemini";
32934
+ const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
32935
+ const extract = opts.harvestSearchQueries;
32936
+ const probesWithQueries = probeRows.map((row) => {
32937
+ if (!extract || !row.rawResponse) return { searchQueries: [] };
32938
+ try {
32939
+ const raw = JSON.parse(row.rawResponse);
32940
+ return { searchQueries: extract({ provider, rawResponse: raw }) };
32941
+ } catch {
32942
+ return { searchQueries: [] };
32943
+ }
32944
+ });
32945
+ const trackedQueries = app.db.select({ query: queries.query }).from(queries).where(eq34(queries.projectId, project.id)).all().map((r) => r.query);
32946
+ const anchorTerms = buildHarvestAnchorTerms(
32947
+ [session.icpDescription ?? "", ...trackedQueries],
32948
+ effectiveDomains(project)
32949
+ );
32950
+ const aggregated = aggregateHarvestedQueries(probesWithQueries);
32951
+ let result = gateHarvestedSearchQueries({
32952
+ candidates: aggregated,
32953
+ trackedQueries,
32954
+ anchorTerms,
32955
+ minProbeHits,
32956
+ applyAnchor
32957
+ });
32958
+ let semanticNoveltyApplied = false;
32959
+ if (opts.embedQueries && result.admitted.length > 0 && trackedQueries.length > 0) {
32960
+ try {
32961
+ const candidateTexts = result.admitted.map((c) => c.query);
32962
+ const vectors = await opts.embedQueries([...candidateTexts, ...trackedQueries]);
32963
+ if (vectors.length === candidateTexts.length + trackedQueries.length) {
32964
+ result = applyHarvestSemanticNovelty({
32965
+ result,
32966
+ candidateVectors: vectors.slice(0, candidateTexts.length),
32967
+ trackedVectors: vectors.slice(candidateTexts.length)
32968
+ });
32969
+ semanticNoveltyApplied = true;
32970
+ }
32971
+ } catch {
32972
+ }
32973
+ }
32974
+ const harvest = {
32975
+ sessionId: session.id,
32976
+ projectId: project.id,
32977
+ provider,
32978
+ status: session.status,
32979
+ minProbeHits,
32980
+ anchorApplied: result.anchorApplied,
32981
+ semanticNoveltyApplied,
32982
+ candidates: result.admitted,
32983
+ stats: result.stats
32984
+ };
32985
+ return reply.send(harvest);
32986
+ }
32987
+ );
32702
32988
  app.get(
32703
32989
  "/projects/:name/discover/sessions/:id/promote",
32704
32990
  async (request, reply) => {
32705
32991
  const project = resolveProject(app.db, request.params.name);
32706
- const session = app.db.select().from(discoverySessions).where(eq33(discoverySessions.id, request.params.id)).get();
32992
+ const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
32707
32993
  if (!session || session.projectId !== project.id) {
32708
32994
  throw notFound("Discovery session", request.params.id);
32709
32995
  }
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());
32996
+ const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
32997
+ const existingCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase());
32712
32998
  const seenCompetitors = new Set(existingCompetitors);
32713
32999
  const cited = /* @__PURE__ */ new Set();
32714
33000
  const aspirational = /* @__PURE__ */ new Set();
@@ -32737,7 +33023,7 @@ async function discoveryRoutes(app, opts) {
32737
33023
  );
32738
33024
  app.post("/projects/:name/discover/sessions/:id/promote", async (request, reply) => {
32739
33025
  const project = resolveProject(app.db, request.params.name);
32740
- const session = app.db.select().from(discoverySessions).where(eq33(discoverySessions.id, request.params.id)).get();
33026
+ const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
32741
33027
  if (!session || session.projectId !== project.id) {
32742
33028
  throw notFound("Discovery session", request.params.id);
32743
33029
  }
@@ -32760,7 +33046,7 @@ async function discoveryRoutes(app, opts) {
32760
33046
  const bucketSet = new Set(buckets);
32761
33047
  const includeCompetitors = parsed.data.includeCompetitors ?? true;
32762
33048
  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();
33049
+ const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
32764
33050
  const candidateQueries = /* @__PURE__ */ new Set();
32765
33051
  for (const probe of probeRows) {
32766
33052
  if (!probe.bucket) continue;
@@ -32768,7 +33054,7 @@ async function discoveryRoutes(app, opts) {
32768
33054
  if (bucket.success && bucketSet.has(bucket.data)) candidateQueries.add(probe.query);
32769
33055
  }
32770
33056
  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())
33057
+ app.db.select({ query: queries.query }).from(queries).where(eq34(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
32772
33058
  );
32773
33059
  const promotedQueries = [];
32774
33060
  const skippedQueries = [];
@@ -32784,7 +33070,7 @@ async function discoveryRoutes(app, opts) {
32784
33070
  const skippedCompetitors = [];
32785
33071
  if (includeCompetitors) {
32786
33072
  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())
33073
+ app.db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase())
32788
33074
  );
32789
33075
  const competitorMap = parseCompetitorMap(session.competitorMap);
32790
33076
  for (const entry of selectEligibleCompetitors(competitorMap, competitorTypes)) {
@@ -32891,7 +33177,7 @@ function selectEligibleCompetitors(competitorMap, competitorTypes) {
32891
33177
 
32892
33178
  // ../api-routes/src/discovery/orchestrate.ts
32893
33179
  import crypto27 from "crypto";
32894
- import { eq as eq34 } from "drizzle-orm";
33180
+ import { eq as eq35 } from "drizzle-orm";
32895
33181
  var DEFAULT_MAX_PROBES = 100;
32896
33182
  var ABSOLUTE_MAX_PROBES = 500;
32897
33183
  function classifyProbeBucket(input) {
@@ -32904,7 +33190,7 @@ function classifyProbeBucket(input) {
32904
33190
  }
32905
33191
  function buildCompetitorMap(probes, project, classification = {}) {
32906
33192
  const canonical = new Set(project.canonicalDomains.map((d) => d.toLowerCase()));
32907
- const counts = /* @__PURE__ */ new Map();
33193
+ const counts2 = /* @__PURE__ */ new Map();
32908
33194
  for (const probe of probes) {
32909
33195
  const seenInProbe = /* @__PURE__ */ new Set();
32910
33196
  for (const raw of probe.citedDomains) {
@@ -32912,10 +33198,10 @@ function buildCompetitorMap(probes, project, classification = {}) {
32912
33198
  if (canonical.has(domain)) continue;
32913
33199
  if (seenInProbe.has(domain)) continue;
32914
33200
  seenInProbe.add(domain);
32915
- counts.set(domain, (counts.get(domain) ?? 0) + 1);
33201
+ counts2.set(domain, (counts2.get(domain) ?? 0) + 1);
32916
33202
  }
32917
33203
  }
32918
- return Array.from(counts.entries()).map(([domain, hits]) => ({
33204
+ return Array.from(counts2.entries()).map(([domain, hits]) => ({
32919
33205
  domain,
32920
33206
  hits,
32921
33207
  competitorType: classification[domain] ?? DiscoveryCompetitorTypes.unknown
@@ -32945,7 +33231,7 @@ async function executeDiscovery(opts) {
32945
33231
  status: DiscoverySessionStatuses.seeding,
32946
33232
  dedupThreshold,
32947
33233
  startedAt
32948
- }).where(eq34(discoverySessions.id, opts.sessionId)).run();
33234
+ }).where(eq35(discoverySessions.id, opts.sessionId)).run();
32949
33235
  const seedResult = await opts.deps.seed({
32950
33236
  project: opts.project,
32951
33237
  icpDescription: opts.icpDescription,
@@ -32971,7 +33257,7 @@ async function executeDiscovery(opts) {
32971
33257
  seedCountRaw,
32972
33258
  seedCount,
32973
33259
  warning
32974
- }).where(eq34(discoverySessions.id, opts.sessionId)).run();
33260
+ }).where(eq35(discoverySessions.id, opts.sessionId)).run();
32975
33261
  const probeRows = [];
32976
33262
  const buckets = { cited: 0, aspirational: 0, "wasted-surface": 0 };
32977
33263
  for (const query of probedCanonicals) {
@@ -33012,7 +33298,7 @@ async function executeDiscovery(opts) {
33012
33298
  wastedCount: buckets["wasted-surface"],
33013
33299
  competitorMap,
33014
33300
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
33015
- }).where(eq34(discoverySessions.id, opts.sessionId)).run();
33301
+ }).where(eq35(discoverySessions.id, opts.sessionId)).run();
33016
33302
  upsertDomainClassifications(opts.db, opts.project.id, opts.sessionId, competitorMap);
33017
33303
  return {
33018
33304
  buckets,
@@ -33052,7 +33338,7 @@ function markSessionFailed(db, sessionId, error) {
33052
33338
  status: DiscoverySessionStatuses.failed,
33053
33339
  error,
33054
33340
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
33055
- }).where(eq34(discoverySessions.id, sessionId)).run();
33341
+ }).where(eq35(discoverySessions.id, sessionId)).run();
33056
33342
  }
33057
33343
  function dedupeStrings(input) {
33058
33344
  const seen = /* @__PURE__ */ new Set();
@@ -33070,7 +33356,7 @@ function dedupeStrings(input) {
33070
33356
 
33071
33357
  // ../api-routes/src/technical-aeo.ts
33072
33358
  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";
33359
+ import { and as and26, asc as asc4, count, desc as desc17, eq as eq36, inArray as inArray13 } from "drizzle-orm";
33074
33360
  var SURFACEABLE_STATUSES = [RunStatuses.completed, RunStatuses.partial];
33075
33361
  function emptyScore(projectName) {
33076
33362
  return {
@@ -33102,12 +33388,12 @@ function parsePositiveInt(value, fallback, max) {
33102
33388
  async function technicalAeoRoutes(app, opts) {
33103
33389
  app.get("/projects/:name/technical-aeo", async (request) => {
33104
33390
  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),
33391
+ const rows = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq36(siteAuditSnapshots.runId, runs.id)).where(and26(
33392
+ eq36(siteAuditSnapshots.projectId, project.id),
33393
+ eq36(runs.kind, RunKinds["site-audit"]),
33394
+ inArray13(runs.status, SURFACEABLE_STATUSES),
33109
33395
  notProbeRun()
33110
- )).orderBy(desc16(siteAuditSnapshots.createdAt)).limit(2).all();
33396
+ )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(2).all();
33111
33397
  const latest = rows[0];
33112
33398
  if (!latest) return emptyScore(project.name);
33113
33399
  const snap = latest.snap;
@@ -33137,24 +33423,24 @@ async function technicalAeoRoutes(app, opts) {
33137
33423
  });
33138
33424
  app.get("/projects/:name/technical-aeo/pages", async (request) => {
33139
33425
  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),
33426
+ const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq36(siteAuditSnapshots.runId, runs.id)).where(and26(
33427
+ eq36(siteAuditSnapshots.projectId, project.id),
33428
+ eq36(runs.kind, RunKinds["site-audit"]),
33429
+ inArray13(runs.status, SURFACEABLE_STATUSES),
33144
33430
  notProbeRun()
33145
- )).orderBy(desc16(siteAuditSnapshots.createdAt)).limit(1).get();
33431
+ )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(1).get();
33146
33432
  if (!latest) {
33147
33433
  return { project: project.name, runId: null, auditedAt: null, total: 0, pages: [] };
33148
33434
  }
33149
33435
  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);
33436
+ const conds = [eq36(siteAuditPages.runId, latest.runId)];
33437
+ if (statusFilter) conds.push(eq36(siteAuditPages.status, statusFilter));
33438
+ const where = and26(...conds);
33153
33439
  const totalRow = app.db.select({ value: count() }).from(siteAuditPages).where(where).get();
33154
33440
  const total = totalRow?.value ?? 0;
33155
33441
  const limit = parsePositiveInt(request.query.limit, 100, 500);
33156
33442
  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);
33443
+ const orderBy = request.query.sort === "score-desc" ? desc17(siteAuditPages.overallScore) : request.query.sort === "url" ? asc4(siteAuditPages.url) : asc4(siteAuditPages.overallScore);
33158
33444
  const rows = app.db.select().from(siteAuditPages).where(where).orderBy(orderBy).limit(limit).offset(offset).all();
33159
33445
  const pages = rows.map((row) => ({
33160
33446
  url: row.url,
@@ -33173,12 +33459,12 @@ async function technicalAeoRoutes(app, opts) {
33173
33459
  auditedAt: siteAuditSnapshots.auditedAt,
33174
33460
  aggregateScore: siteAuditSnapshots.aggregateScore,
33175
33461
  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),
33462
+ }).from(siteAuditSnapshots).innerJoin(runs, eq36(siteAuditSnapshots.runId, runs.id)).where(and26(
33463
+ eq36(siteAuditSnapshots.projectId, project.id),
33464
+ eq36(runs.kind, RunKinds["site-audit"]),
33465
+ inArray13(runs.status, SURFACEABLE_STATUSES),
33180
33466
  notProbeRun()
33181
- )).orderBy(desc16(siteAuditSnapshots.createdAt)).limit(limit).all();
33467
+ )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(limit).all();
33182
33468
  return { project: project.name, points: rows.reverse() };
33183
33469
  });
33184
33470
  app.post("/projects/:name/technical-aeo/runs", async (request) => {
@@ -33187,10 +33473,10 @@ async function technicalAeoRoutes(app, opts) {
33187
33473
  if (!parsed.success) {
33188
33474
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid site-audit request");
33189
33475
  }
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])
33476
+ const existing = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and26(
33477
+ eq36(runs.projectId, project.id),
33478
+ eq36(runs.kind, RunKinds["site-audit"]),
33479
+ inArray13(runs.status, [RunStatuses.queued, RunStatuses.running])
33194
33480
  )).get();
33195
33481
  if (existing) {
33196
33482
  return { runId: existing.id, status: existing.status };
@@ -33293,6 +33579,7 @@ async function apiRoutes(app, opts) {
33293
33579
  await api.register(intelligenceRoutes);
33294
33580
  await api.register(reportRoutes);
33295
33581
  await api.register(citationRoutes);
33582
+ await api.register(visibilityStatsRoutes);
33296
33583
  await api.register(compositeRoutes);
33297
33584
  await api.register(contentRoutes, {
33298
33585
  explainContentRecommendation: opts.explainContentRecommendation,
@@ -33383,7 +33670,9 @@ async function apiRoutes(app, opts) {
33383
33670
  discoverLatestRelease: opts.discoverLatestRelease
33384
33671
  });
33385
33672
  await api.register(discoveryRoutes, {
33386
- onDiscoveryRunRequested: opts.onDiscoveryRunRequested
33673
+ onDiscoveryRunRequested: opts.onDiscoveryRunRequested,
33674
+ harvestSearchQueries: opts.harvestSearchQueries,
33675
+ embedQueries: opts.embedQueries
33387
33676
  });
33388
33677
  await api.register(technicalAeoRoutes, {
33389
33678
  onSiteAuditRequested: opts.onSiteAuditRequested
@@ -33776,16 +34065,16 @@ var IntelligenceService = class {
33776
34065
  */
33777
34066
  analyzeAndPersist(runId, projectId) {
33778
34067
  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")),
34068
+ and27(
34069
+ eq37(runs.projectId, projectId),
34070
+ or5(eq37(runs.status, "completed"), eq37(runs.status, "partial")),
33782
34071
  // Defensive: RunCoordinator already skips probes before this is
33783
34072
  // called, but if a future call site invokes analyzeAndPersist
33784
34073
  // directly for a probe, probes still must not pollute the
33785
34074
  // intelligence window.
33786
34075
  ne5(runs.trigger, RunTriggers.probe)
33787
34076
  )
33788
- ).orderBy(desc17(runs.finishedAt), desc17(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
34077
+ ).orderBy(desc18(runs.finishedAt), desc18(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
33789
34078
  if (recentRuns.length === 0) {
33790
34079
  log.info("intelligence.skip", { runId, reason: "no completed runs" });
33791
34080
  return null;
@@ -33860,7 +34149,7 @@ var IntelligenceService = class {
33860
34149
  * Returns the persisted insights so the coordinator can count critical/high.
33861
34150
  */
33862
34151
  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();
34152
+ const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq37(runs.id, runId)).get();
33864
34153
  if (!runRow) {
33865
34154
  log.info("gbp-intelligence.skip", { runId, reason: "run not found" });
33866
34155
  this.persistGbpInsights(runId, projectId, [], []);
@@ -33868,9 +34157,9 @@ var IntelligenceService = class {
33868
34157
  }
33869
34158
  const windowStart = runRow.startedAt ?? runRow.createdAt;
33870
34159
  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),
34160
+ const selected = this.db.select().from(gbpLocations).where(and27(
34161
+ eq37(gbpLocations.projectId, projectId),
34162
+ eq37(gbpLocations.selected, true),
33874
34163
  gte7(gbpLocations.syncedAt, windowStart),
33875
34164
  lte4(gbpLocations.syncedAt, windowEnd)
33876
34165
  )).all();
@@ -33905,10 +34194,10 @@ var IntelligenceService = class {
33905
34194
  }
33906
34195
  /** Build the per-location signal bundle the GBP analyzer consumes. */
33907
34196
  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();
34197
+ 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();
34198
+ const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and27(eq37(gbpPlaceActions.projectId, projectId), eq37(gbpPlaceActions.locationName, locationName))).all();
34199
+ 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();
34200
+ 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
34201
  const placesAmenities = placeRow ? extractPlaceAmenities(placeRow.attributes) : [];
33913
34202
  const summary = buildGbpSummary({
33914
34203
  locationName,
@@ -33940,7 +34229,7 @@ var IntelligenceService = class {
33940
34229
  /** Build the month-over-month keyword series for a location from the
33941
34230
  * accumulating gbp_keyword_monthly table (latest complete month vs prior). */
33942
34231
  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();
34232
+ 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
34233
  if (rows.length === 0) return { recentMonth: null, priorMonth: null, points: [] };
33945
34234
  const months = [...new Set(rows.map((r) => r.month))].sort().reverse();
33946
34235
  const recentMonth = months[0] ?? null;
@@ -33971,7 +34260,7 @@ var IntelligenceService = class {
33971
34260
  */
33972
34261
  persistGbpInsights(runId, projectId, gbpInsights, coveredLocationNames) {
33973
34262
  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();
34263
+ 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
34264
  const staleIds = [];
33976
34265
  const dismissedSlots = /* @__PURE__ */ new Set();
33977
34266
  for (const row of existing) {
@@ -33982,7 +34271,7 @@ var IntelligenceService = class {
33982
34271
  }
33983
34272
  this.db.transaction((tx) => {
33984
34273
  for (const id of staleIds) {
33985
- tx.delete(insights).where(eq36(insights.id, id)).run();
34274
+ tx.delete(insights).where(eq37(insights.id, id)).run();
33986
34275
  }
33987
34276
  for (const insight of gbpInsights) {
33988
34277
  const parsed = parseGbpInsightId(insight.id);
@@ -34060,7 +34349,7 @@ var IntelligenceService = class {
34060
34349
  * create per run + aggregate). DB is left untouched.
34061
34350
  */
34062
34351
  backfill(projectName, opts, onProgress) {
34063
- const project = this.db.select().from(projects).where(eq36(projects.name, projectName)).get();
34352
+ const project = this.db.select().from(projects).where(eq37(projects.name, projectName)).get();
34064
34353
  if (!project) {
34065
34354
  throw new Error(`Project "${projectName}" not found`);
34066
34355
  }
@@ -34073,9 +34362,9 @@ var IntelligenceService = class {
34073
34362
  sinceTimestamp = parsed;
34074
34363
  }
34075
34364
  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")),
34365
+ and27(
34366
+ eq37(runs.projectId, project.id),
34367
+ or5(eq37(runs.status, "completed"), eq37(runs.status, "partial")),
34079
34368
  // Backfill must not replay probe runs as if they were real sweeps.
34080
34369
  ne5(runs.trigger, RunTriggers.probe)
34081
34370
  )
@@ -34108,7 +34397,7 @@ var IntelligenceService = class {
34108
34397
  let wouldDeleteTotal = 0;
34109
34398
  const existingByRunId = /* @__PURE__ */ new Map();
34110
34399
  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();
34400
+ const rows = this.db.select({ runId: insights.runId }).from(insights).where(inArray14(insights.runId, targetRuns.map((r) => r.id))).all();
34112
34401
  for (const r of rows) {
34113
34402
  if (r.runId == null) continue;
34114
34403
  existingByRunId.set(r.runId, (existingByRunId.get(r.runId) ?? 0) + 1);
@@ -34154,7 +34443,7 @@ var IntelligenceService = class {
34154
34443
  return { processed, skipped, totalInsights };
34155
34444
  }
34156
34445
  loadTrackedCompetitors(projectId) {
34157
- return this.db.select({ domain: competitors.domain }).from(competitors).where(eq36(competitors.projectId, projectId)).all().map((r) => r.domain);
34446
+ return this.db.select({ domain: competitors.domain }).from(competitors).where(eq37(competitors.projectId, projectId)).all().map((r) => r.domain);
34158
34447
  }
34159
34448
  /**
34160
34449
  * Wipe transition signals from an analysis result while keeping health.
@@ -34175,15 +34464,15 @@ var IntelligenceService = class {
34175
34464
  }
34176
34465
  persistResult(result, runId, projectId) {
34177
34466
  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();
34467
+ 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
34468
  for (const row of existingInsights) {
34180
34469
  if (row.dismissed) {
34181
34470
  previouslyDismissed.add(`${row.query}:${row.provider}:${row.type}`);
34182
34471
  }
34183
34472
  }
34184
34473
  this.db.transaction((tx) => {
34185
- tx.delete(insights).where(eq36(insights.runId, runId)).run();
34186
- tx.delete(healthSnapshots).where(eq36(healthSnapshots.runId, runId)).run();
34474
+ tx.delete(insights).where(eq37(insights.runId, runId)).run();
34475
+ tx.delete(healthSnapshots).where(eq37(healthSnapshots.runId, runId)).run();
34187
34476
  const now = (/* @__PURE__ */ new Date()).toISOString();
34188
34477
  for (const insight of result.insights) {
34189
34478
  const wasDismissed = previouslyDismissed.has(`${insight.query}:${insight.provider}:${insight.type}`);
@@ -34236,28 +34525,28 @@ var IntelligenceService = class {
34236
34525
  applySeverityTiering(rawInsights, excludeRunId, projectId) {
34237
34526
  const regressions = rawInsights.filter((i) => i.type === "regression");
34238
34527
  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();
34528
+ const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq37(gscSearchData.projectId, projectId)).all();
34240
34529
  const gscConnected = gscRows.length > 0;
34241
34530
  const gscImpressionsByQuery = /* @__PURE__ */ new Map();
34242
34531
  for (const row of gscRows) {
34243
34532
  const key = row.query.toLowerCase();
34244
34533
  gscImpressionsByQuery.set(key, (gscImpressionsByQuery.get(key) ?? 0) + row.impressions);
34245
34534
  }
34246
- const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq36(projects.id, projectId)).get();
34535
+ const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq37(projects.id, projectId)).get();
34247
34536
  const locationCount = Math.max(
34248
34537
  1,
34249
34538
  (projectRow?.locations ?? []).length
34250
34539
  );
34251
34540
  const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
34252
34541
  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")),
34542
+ and27(
34543
+ eq37(runs.projectId, projectId),
34544
+ eq37(runs.kind, RunKinds["answer-visibility"]),
34545
+ or5(eq37(runs.status, "completed"), eq37(runs.status, "partial")),
34257
34546
  // Defensive — see top of file.
34258
34547
  ne5(runs.trigger, RunTriggers.probe)
34259
34548
  )
34260
- ).orderBy(desc17(runs.createdAt), desc17(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
34549
+ ).orderBy(desc18(runs.createdAt), desc18(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
34261
34550
  const recentGroups = groupRunsByCreatedAt(recentRunRows);
34262
34551
  const recentRunIds = [];
34263
34552
  const recentRunIdToCreatedAt = /* @__PURE__ */ new Map();
@@ -34273,7 +34562,7 @@ var IntelligenceService = class {
34273
34562
  const haveHistory = recentRunIds.length > 0;
34274
34563
  const priorRegressionsByPair = /* @__PURE__ */ new Map();
34275
34564
  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();
34565
+ 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
34566
  const regressionGroups = /* @__PURE__ */ new Map();
34278
34567
  for (const row of priorRows) {
34279
34568
  if (!row.runId) continue;
@@ -34302,7 +34591,7 @@ var IntelligenceService = class {
34302
34591
  });
34303
34592
  }
34304
34593
  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();
34594
+ const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq37(projects.id, projectId)).get();
34306
34595
  const projectDomains = projectDomainRow ? effectiveDomains({
34307
34596
  canonicalDomain: projectDomainRow.canonicalDomain,
34308
34597
  ownedDomains: projectDomainRow.ownedDomains
@@ -34319,7 +34608,7 @@ var IntelligenceService = class {
34319
34608
  citedDomains: querySnapshots.citedDomains,
34320
34609
  competitorOverlap: querySnapshots.competitorOverlap,
34321
34610
  snapshotLocation: querySnapshots.location
34322
- }).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(eq36(querySnapshots.runId, runId)).all();
34611
+ }).from(querySnapshots).leftJoin(queries, eq37(querySnapshots.queryId, queries.id)).where(eq37(querySnapshots.runId, runId)).all();
34323
34612
  const snapshots = [];
34324
34613
  let orphanCount = 0;
34325
34614
  for (const r of rows) {