@ainyc/canonry 4.36.0 → 4.39.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.
@@ -5,7 +5,7 @@ import {
5
5
  loadConfig,
6
6
  loadConfigRaw,
7
7
  saveConfigPatch
8
- } from "./chunk-O7S623DL.js";
8
+ } from "./chunk-JS6KRBBZ.js";
9
9
  import {
10
10
  DEFAULT_RUN_HISTORY_LIMIT,
11
11
  IntelligenceService,
@@ -30,11 +30,13 @@ import {
30
30
  buildGapQueryScore,
31
31
  buildInventory,
32
32
  buildMentionCoverage,
33
+ buildMentionGapScore,
33
34
  buildMentionLandscape,
34
35
  buildMovementSummary,
35
36
  buildOverviewCompetitors,
36
37
  buildProviderScores,
37
38
  buildRunHistory,
39
+ buildShareOfVoice,
38
40
  buildVisibilityScore,
39
41
  categorizeQueryByIntent,
40
42
  ccReleaseSyncs,
@@ -74,7 +76,7 @@ import {
74
76
  schedules,
75
77
  trafficSources,
76
78
  usageCounters
77
- } from "./chunk-JQQXMCQ7.js";
79
+ } from "./chunk-DOUV45KI.js";
78
80
  import {
79
81
  AGENT_MEMORY_VALUE_MAX_BYTES,
80
82
  AGENT_PROVIDER_IDS,
@@ -926,7 +928,7 @@ function aliasArraysEqual(a, b) {
926
928
 
927
929
  // ../api-routes/src/queries.ts
928
930
  import crypto5 from "crypto";
929
- import { eq as eq4 } from "drizzle-orm";
931
+ import { eq as eq4, inArray, sql as sql3 } from "drizzle-orm";
930
932
  async function queryRoutes(app, opts) {
931
933
  app.get("/projects/:name/queries", async (request, reply) => {
932
934
  const project = resolveProject(app.db, request.params.name);
@@ -962,6 +964,36 @@ async function queryRoutes(app, opts) {
962
964
  const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
963
965
  return reply.send(rows.map((r) => ({ id: r.id, query: r.query, createdAt: r.createdAt })));
964
966
  });
967
+ app.post("/projects/:name/queries/replace-preview", async (request, reply) => {
968
+ const project = resolveProject(app.db, request.params.name);
969
+ const body = request.body;
970
+ if (!body || !Array.isArray(body.queries)) {
971
+ throw validationError('Body must contain a "queries" array');
972
+ }
973
+ const currentRows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
974
+ const currentTexts = currentRows.map((r) => r.query);
975
+ const currentSet = new Set(currentTexts);
976
+ const proposedSet = new Set(body.queries);
977
+ const removed = currentTexts.filter((q) => !proposedSet.has(q));
978
+ const added = body.queries.filter((q) => !currentSet.has(q));
979
+ const unchanged = currentTexts.filter((q) => proposedSet.has(q));
980
+ const currentIds = currentRows.map((r) => r.id);
981
+ let snapshotsDetached = 0;
982
+ let affectedQueries = 0;
983
+ if (currentIds.length > 0) {
984
+ const snapshotCount = app.db.select({ n: sql3`count(*)` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
985
+ snapshotsDetached = snapshotCount?.n ?? 0;
986
+ const distinctAffected = app.db.select({ n: sql3`count(distinct ${querySnapshots.queryId})` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
987
+ affectedQueries = distinctAffected?.n ?? 0;
988
+ }
989
+ return reply.send({
990
+ project: { id: project.id, name: project.name },
991
+ current: currentTexts,
992
+ proposed: body.queries,
993
+ diff: { added, removed, unchanged },
994
+ snapshotImpact: { affectedQueries, snapshotsDetached }
995
+ });
996
+ });
965
997
  app.delete("/projects/:name/queries", async (request, reply) => {
966
998
  const project = resolveProject(app.db, request.params.name);
967
999
  const body = request.body;
@@ -1325,7 +1357,7 @@ function parseCompetitorBatch(value) {
1325
1357
 
1326
1358
  // ../api-routes/src/runs.ts
1327
1359
  import crypto8 from "crypto";
1328
- import { and as and2, eq as eq7, asc, desc, or as or2, sql as sql3 } from "drizzle-orm";
1360
+ import { and as and2, eq as eq7, asc, desc, or as or2, sql as sql4 } from "drizzle-orm";
1329
1361
 
1330
1362
  // ../api-routes/src/run-queue.ts
1331
1363
  import crypto7 from "crypto";
@@ -1497,7 +1529,7 @@ async function runRoutes(app, opts) {
1497
1529
  });
1498
1530
  app.get("/projects/:name/runs/latest", async (request, reply) => {
1499
1531
  const project = resolveProject(app.db, request.params.name);
1500
- const countRow = app.db.select({ count: sql3`count(*)` }).from(runs).where(eq7(runs.projectId, project.id)).get();
1532
+ const countRow = app.db.select({ count: sql4`count(*)` }).from(runs).where(eq7(runs.projectId, project.id)).get();
1501
1533
  const totalRuns = countRow?.count ?? 0;
1502
1534
  const latestRun = app.db.select().from(runs).where(eq7(runs.projectId, project.id)).orderBy(desc(runs.createdAt), desc(runs.id)).limit(1).get();
1503
1535
  if (!latestRun) {
@@ -2226,7 +2258,7 @@ function normalizeCompetitorList2(domains) {
2226
2258
  }
2227
2259
 
2228
2260
  // ../api-routes/src/history.ts
2229
- import { eq as eq9, desc as desc2, inArray } from "drizzle-orm";
2261
+ import { eq as eq9, desc as desc2, inArray as inArray2 } from "drizzle-orm";
2230
2262
 
2231
2263
  // ../api-routes/src/notification-redaction.ts
2232
2264
  var REDACTED_URL = {
@@ -2302,7 +2334,7 @@ async function historyRoutes(app) {
2302
2334
  recommendedCompetitors: querySnapshots.recommendedCompetitors,
2303
2335
  location: querySnapshots.location,
2304
2336
  createdAt: querySnapshots.createdAt
2305
- }).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(inArray(querySnapshots.runId, projectRuns.map((r) => r.id))).orderBy(desc2(querySnapshots.createdAt)).all();
2337
+ }).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(inArray2(querySnapshots.runId, projectRuns.map((r) => r.id))).orderBy(desc2(querySnapshots.createdAt)).all();
2306
2338
  const locationFilter = request.query.location;
2307
2339
  const filtered = locationFilter !== void 0 ? allSnapshots.filter((s) => s.location === (locationFilter || null)) : allSnapshots;
2308
2340
  const total = filtered.length;
@@ -2337,7 +2369,7 @@ async function historyRoutes(app) {
2337
2369
  return reply.send([]);
2338
2370
  }
2339
2371
  const runIds = new Set(projectRuns.map((r) => r.id));
2340
- const rawSnapshots = app.db.select().from(querySnapshots).where(inArray(querySnapshots.runId, [...runIds])).all();
2372
+ const rawSnapshots = app.db.select().from(querySnapshots).where(inArray2(querySnapshots.runId, [...runIds])).all();
2341
2373
  const timelineLocationFilter = request.query.location;
2342
2374
  const filteredSnapshots = timelineLocationFilter !== void 0 ? rawSnapshots.filter((s) => s.location === (timelineLocationFilter || null)) : rawSnapshots;
2343
2375
  const allSnapshots = filteredSnapshots.map((snapshot) => ({
@@ -2517,7 +2549,7 @@ function formatAuditEntry(row) {
2517
2549
  }
2518
2550
 
2519
2551
  // ../api-routes/src/analytics.ts
2520
- import { eq as eq10, desc as desc3, inArray as inArray2 } from "drizzle-orm";
2552
+ import { eq as eq10, desc as desc3, inArray as inArray3 } from "drizzle-orm";
2521
2553
  async function analyticsRoutes(app) {
2522
2554
  app.get("/projects/:name/analytics/metrics", async (request, reply) => {
2523
2555
  const project = resolveProject(app.db, request.params.name);
@@ -2544,7 +2576,7 @@ async function analyticsRoutes(app) {
2544
2576
  answerMentioned: querySnapshots.answerMentioned,
2545
2577
  answerText: querySnapshots.answerText,
2546
2578
  createdAt: querySnapshots.createdAt
2547
- }).from(querySnapshots).where(inArray2(querySnapshots.runId, runIds)).all());
2579
+ }).from(querySnapshots).where(inArray3(querySnapshots.runId, runIds)).all());
2548
2580
  const allSnapshots = rawSnapshots.map((s) => ({
2549
2581
  ...s,
2550
2582
  resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
@@ -2589,7 +2621,7 @@ async function analyticsRoutes(app) {
2589
2621
  citationState: querySnapshots.citationState,
2590
2622
  answerMentioned: querySnapshots.answerMentioned,
2591
2623
  answerText: querySnapshots.answerText
2592
- }).from(querySnapshots).where(inArray2(querySnapshots.runId, windowRunIds)).all());
2624
+ }).from(querySnapshots).where(inArray3(querySnapshots.runId, windowRunIds)).all());
2593
2625
  for (const s of allWindowSnaps) {
2594
2626
  const timePoint = runIdToCreatedAt.get(s.runId) ?? s.runId;
2595
2627
  let entry = consistencyMap.get(s.queryId);
@@ -2610,7 +2642,7 @@ async function analyticsRoutes(app) {
2610
2642
  answerMentioned: querySnapshots.answerMentioned,
2611
2643
  answerText: querySnapshots.answerText,
2612
2644
  competitorOverlap: querySnapshots.competitorOverlap
2613
- }).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray2(querySnapshots.runId, latestGroupRunIds)).all());
2645
+ }).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, latestGroupRunIds)).all());
2614
2646
  const snapshots = rawSnapshots.map((s) => ({
2615
2647
  ...s,
2616
2648
  resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
@@ -2705,7 +2737,7 @@ async function analyticsRoutes(app) {
2705
2737
  queryId: querySnapshots.queryId,
2706
2738
  query: queries.query,
2707
2739
  rawResponse: querySnapshots.rawResponse
2708
- }).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray2(querySnapshots.runId, windowRunIds)).all();
2740
+ }).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, windowRunIds)).all();
2709
2741
  const overallCounts = /* @__PURE__ */ new Map();
2710
2742
  const byQuery = {};
2711
2743
  for (const snap of snapshots) {
@@ -2864,7 +2896,7 @@ function buildCategoryCounts(counts) {
2864
2896
  }
2865
2897
 
2866
2898
  // ../api-routes/src/intelligence.ts
2867
- import { eq as eq11, desc as desc4, and as and4, inArray as inArray3 } from "drizzle-orm";
2899
+ import { eq as eq11, desc as desc4, and as and4, inArray as inArray4 } from "drizzle-orm";
2868
2900
  function emptyHealthSnapshot(projectId) {
2869
2901
  return {
2870
2902
  id: `no-data:${projectId}`,
@@ -2980,14 +3012,14 @@ async function intelligenceRoutes(app) {
2980
3012
  const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and4(
2981
3013
  eq11(runs.projectId, project.id),
2982
3014
  eq11(runs.kind, RunKinds["answer-visibility"]),
2983
- inArray3(runs.status, [RunStatuses.completed, RunStatuses.partial])
3015
+ inArray4(runs.status, [RunStatuses.completed, RunStatuses.partial])
2984
3016
  )).orderBy(desc4(runs.createdAt), desc4(runs.id)).all();
2985
3017
  const latestGroup = groupRunsByCreatedAt(projectVisRuns)[0] ?? [];
2986
3018
  const latestGroupRunIds = latestGroup.map((r) => r.id);
2987
3019
  if (latestGroupRunIds.length > 0) {
2988
3020
  const groupRows = app.db.select().from(healthSnapshots).where(and4(
2989
3021
  eq11(healthSnapshots.projectId, project.id),
2990
- inArray3(healthSnapshots.runId, latestGroupRunIds)
3022
+ inArray4(healthSnapshots.runId, latestGroupRunIds)
2991
3023
  )).all();
2992
3024
  if (groupRows.length > 0) {
2993
3025
  return reply.send(aggregateHealthSnapshots(project.id, groupRows));
@@ -3009,7 +3041,7 @@ async function intelligenceRoutes(app) {
3009
3041
  }
3010
3042
 
3011
3043
  // ../api-routes/src/report.ts
3012
- import { and as and6, desc as desc6, eq as eq13, gte, inArray as inArray5, lt, lte, ne, or as or3, sql as sql4 } from "drizzle-orm";
3044
+ import { and as and6, desc as desc6, eq as eq13, gte, inArray as inArray6, lt, lte, ne, or as or3, sql as sql5 } from "drizzle-orm";
3013
3045
 
3014
3046
  // ../api-routes/src/report-renderer.ts
3015
3047
  var COLORS = {
@@ -5245,7 +5277,7 @@ function renderReportHtml(report, opts = {}) {
5245
5277
  }
5246
5278
 
5247
5279
  // ../api-routes/src/content-data.ts
5248
- import { and as and5, eq as eq12, desc as desc5, inArray as inArray4 } from "drizzle-orm";
5280
+ import { and as and5, eq as eq12, desc as desc5, inArray as inArray5 } from "drizzle-orm";
5249
5281
  var RECENT_RUNS_WINDOW = 5;
5250
5282
  function loadOrchestratorInput(db, project, locationFilter = void 0) {
5251
5283
  const projectId = project.id;
@@ -5375,7 +5407,7 @@ function listRecentAnswerVisibilityRunIds(db, projectId, limit, locationFilter)
5375
5407
  // Queued/running/failed/cancelled runs may have partial or no
5376
5408
  // snapshots; including them risks pointing latestRunId at a run with
5377
5409
  // no usable evidence.
5378
- inArray4(runs.status, [RunStatuses.completed, RunStatuses.partial])
5410
+ inArray5(runs.status, [RunStatuses.completed, RunStatuses.partial])
5379
5411
  )
5380
5412
  ).orderBy(desc5(runs.createdAt)).all();
5381
5413
  const filtered = locationFilter === void 0 ? rows : rows.filter((r) => (r.location ?? null) === locationFilter);
@@ -5417,7 +5449,7 @@ function buildCandidateQueries(opts) {
5417
5449
  const queryRows = opts.db.select({ id: queries.id, text: queries.query }).from(queries).where(eq12(queries.projectId, opts.projectId)).all();
5418
5450
  const queryIdByText = new Map(queryRows.map((r) => [r.text, r.id]));
5419
5451
  const candidateQueryIds = opts.candidateQueryStrings.map((q) => queryIdByText.get(q)).filter((id) => Boolean(id));
5420
- const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(inArray4(querySnapshots.runId, opts.recentRunIds)).all()).filter((r) => candidateQueryIds.includes(r.queryId));
5452
+ const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(inArray5(querySnapshots.runId, opts.recentRunIds)).all()).filter((r) => candidateQueryIds.includes(r.queryId));
5421
5453
  const snapshotsByQuery = /* @__PURE__ */ new Map();
5422
5454
  for (const row of snapshotRows) {
5423
5455
  const list = snapshotsByQuery.get(row.queryId) ?? [];
@@ -5635,7 +5667,7 @@ function loadSnapshotsForRun(db, runId) {
5635
5667
  }
5636
5668
  function loadSnapshotsForRunIds(db, runIds) {
5637
5669
  if (runIds.length === 0) return [];
5638
- const rows = db.select().from(querySnapshots).where(inArray5(querySnapshots.runId, [...runIds])).all();
5670
+ const rows = db.select().from(querySnapshots).where(inArray6(querySnapshots.runId, [...runIds])).all();
5639
5671
  return rows.filter((r) => r.queryId !== null).map((r) => ({
5640
5672
  id: r.id,
5641
5673
  runId: r.runId,
@@ -5885,7 +5917,7 @@ function buildAiReferrals(db, projectId) {
5885
5917
  return { totalSessions: total, totalUsers, bySource, trend, topLandingPages };
5886
5918
  }
5887
5919
  function nonSubresourceReferralPathCondition() {
5888
- return sql4`
5920
+ return sql5`
5889
5921
  LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/_next/static/%'
5890
5922
  AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/assets/%'
5891
5923
  AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/static/%'
@@ -5924,7 +5956,7 @@ function buildServerActivity(db, projectId) {
5924
5956
  const priorStart = new Date(priorStartMs).toISOString();
5925
5957
  const trendStart = new Date(trendStartMs).toISOString();
5926
5958
  const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
5927
- db.select({ total: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5959
+ db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5928
5960
  and6(
5929
5961
  eq13(crawlerEventsHourly.projectId, projectId),
5930
5962
  eq13(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
@@ -5934,7 +5966,7 @@ function buildServerActivity(db, projectId) {
5934
5966
  ).get()?.total ?? 0
5935
5967
  );
5936
5968
  const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
5937
- db.select({ total: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5969
+ db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5938
5970
  and6(
5939
5971
  eq13(crawlerEventsHourly.projectId, projectId),
5940
5972
  ne(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
@@ -5944,7 +5976,7 @@ function buildServerActivity(db, projectId) {
5944
5976
  ).get()?.total ?? 0
5945
5977
  );
5946
5978
  const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
5947
- db.select({ total: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
5979
+ db.select({ total: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
5948
5980
  and6(
5949
5981
  eq13(aiReferralEventsHourly.projectId, projectId),
5950
5982
  nonSubresourceReferralPathCondition(),
@@ -5962,7 +5994,7 @@ function buildServerActivity(db, projectId) {
5962
5994
  const crawlerByOperatorRows = db.select({
5963
5995
  operator: crawlerEventsHourly.operator,
5964
5996
  verificationStatus: crawlerEventsHourly.verificationStatus,
5965
- hits: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
5997
+ hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
5966
5998
  }).from(crawlerEventsHourly).where(
5967
5999
  and6(
5968
6000
  eq13(crawlerEventsHourly.projectId, projectId),
@@ -5972,7 +6004,7 @@ function buildServerActivity(db, projectId) {
5972
6004
  ).groupBy(crawlerEventsHourly.operator, crawlerEventsHourly.verificationStatus).all();
5973
6005
  const crawlerByOperatorPriorRows = db.select({
5974
6006
  operator: crawlerEventsHourly.operator,
5975
- hits: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
6007
+ hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
5976
6008
  }).from(crawlerEventsHourly).where(
5977
6009
  and6(
5978
6010
  eq13(crawlerEventsHourly.projectId, projectId),
@@ -5983,7 +6015,7 @@ function buildServerActivity(db, projectId) {
5983
6015
  ).groupBy(crawlerEventsHourly.operator).all();
5984
6016
  const referralByOperatorRows = db.select({
5985
6017
  operator: aiReferralEventsHourly.operator,
5986
- hits: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
6018
+ hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
5987
6019
  }).from(aiReferralEventsHourly).where(
5988
6020
  and6(
5989
6021
  eq13(aiReferralEventsHourly.projectId, projectId),
@@ -6023,8 +6055,8 @@ function buildServerActivity(db, projectId) {
6023
6055
  );
6024
6056
  const topPathsRows = db.select({
6025
6057
  path: crawlerEventsHourly.pathNormalized,
6026
- hits: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
6027
- operators: sql4`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
6058
+ hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
6059
+ operators: sql5`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
6028
6060
  }).from(crawlerEventsHourly).where(
6029
6061
  and6(
6030
6062
  eq13(crawlerEventsHourly.projectId, projectId),
@@ -6032,7 +6064,7 @@ function buildServerActivity(db, projectId) {
6032
6064
  gte(crawlerEventsHourly.tsHour, headlineStart),
6033
6065
  lte(crawlerEventsHourly.tsHour, headlineEnd)
6034
6066
  )
6035
- ).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(sql4`SUM(${crawlerEventsHourly.hits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
6067
+ ).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(sql5`SUM(${crawlerEventsHourly.hits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
6036
6068
  const topCrawledPaths = topPathsRows.map((r) => ({
6037
6069
  path: r.path,
6038
6070
  verifiedHits: Number(r.hits),
@@ -6040,8 +6072,8 @@ function buildServerActivity(db, projectId) {
6040
6072
  }));
6041
6073
  const referralProductsRows = db.select({
6042
6074
  product: aiReferralEventsHourly.product,
6043
- arrivals: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6044
- landingPaths: sql4`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
6075
+ arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6076
+ landingPaths: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
6045
6077
  }).from(aiReferralEventsHourly).where(
6046
6078
  and6(
6047
6079
  eq13(aiReferralEventsHourly.projectId, projectId),
@@ -6049,7 +6081,7 @@ function buildServerActivity(db, projectId) {
6049
6081
  gte(aiReferralEventsHourly.tsHour, headlineStart),
6050
6082
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
6051
6083
  )
6052
- ).groupBy(aiReferralEventsHourly.product).orderBy(desc6(sql4`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).all();
6084
+ ).groupBy(aiReferralEventsHourly.product).orderBy(desc6(sql5`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).all();
6053
6085
  const referralProducts = referralProductsRows.map((r) => ({
6054
6086
  product: r.product,
6055
6087
  arrivals: Number(r.arrivals),
@@ -6057,8 +6089,8 @@ function buildServerActivity(db, projectId) {
6057
6089
  }));
6058
6090
  const topReferralRows = db.select({
6059
6091
  path: aiReferralEventsHourly.landingPathNormalized,
6060
- arrivals: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6061
- products: sql4`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
6092
+ arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6093
+ products: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
6062
6094
  }).from(aiReferralEventsHourly).where(
6063
6095
  and6(
6064
6096
  eq13(aiReferralEventsHourly.projectId, projectId),
@@ -6066,15 +6098,15 @@ function buildServerActivity(db, projectId) {
6066
6098
  gte(aiReferralEventsHourly.tsHour, headlineStart),
6067
6099
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
6068
6100
  )
6069
- ).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(sql4`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
6101
+ ).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(sql5`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
6070
6102
  const topReferralLandingPaths = topReferralRows.map((r) => ({
6071
6103
  path: r.path,
6072
6104
  arrivals: Number(r.arrivals),
6073
6105
  distinctProducts: Number(r.products)
6074
6106
  }));
6075
6107
  const crawlerTrendRows = db.select({
6076
- date: sql4`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
6077
- hits: sql4`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
6108
+ date: sql5`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
6109
+ hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
6078
6110
  }).from(crawlerEventsHourly).where(
6079
6111
  and6(
6080
6112
  eq13(crawlerEventsHourly.projectId, projectId),
@@ -6082,10 +6114,10 @@ function buildServerActivity(db, projectId) {
6082
6114
  gte(crawlerEventsHourly.tsHour, trendStart),
6083
6115
  lte(crawlerEventsHourly.tsHour, headlineEnd)
6084
6116
  )
6085
- ).groupBy(sql4`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`).all();
6117
+ ).groupBy(sql5`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`).all();
6086
6118
  const referralTrendRows = db.select({
6087
- date: sql4`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
6088
- hits: sql4`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
6119
+ date: sql5`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
6120
+ hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
6089
6121
  }).from(aiReferralEventsHourly).where(
6090
6122
  and6(
6091
6123
  eq13(aiReferralEventsHourly.projectId, projectId),
@@ -6093,7 +6125,7 @@ function buildServerActivity(db, projectId) {
6093
6125
  gte(aiReferralEventsHourly.tsHour, trendStart),
6094
6126
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
6095
6127
  )
6096
- ).groupBy(sql4`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`).all();
6128
+ ).groupBy(sql5`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`).all();
6097
6129
  const dailyTrendMap = /* @__PURE__ */ new Map();
6098
6130
  for (const r of crawlerTrendRows) {
6099
6131
  const e = dailyTrendMap.get(r.date) ?? { verifiedCrawlerHits: 0, referralArrivals: 0 };
@@ -6215,7 +6247,7 @@ function buildInsightList(db, projectId, locationFilter) {
6215
6247
  )
6216
6248
  ).orderBy(desc6(runs.createdAt)).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter).slice(0, INSIGHT_LOOKBACK_RUNS).map((r) => r.id);
6217
6249
  if (recentRunIds.length === 0) return [];
6218
- const rows = db.select().from(insights).where(and6(eq13(insights.projectId, projectId), inArray5(insights.runId, recentRunIds))).orderBy(desc6(insights.createdAt)).all();
6250
+ const rows = db.select().from(insights).where(and6(eq13(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc6(insights.createdAt)).all();
6219
6251
  const severityRank = { critical: 0, high: 1, medium: 2, low: 3 };
6220
6252
  const flat = rows.filter((r) => !r.dismissed).map((r) => {
6221
6253
  const recommendation = parseJsonColumn(r.recommendation, null);
@@ -6988,7 +7020,7 @@ async function reportRoutes(app) {
6988
7020
  }
6989
7021
 
6990
7022
  // ../api-routes/src/citations.ts
6991
- import { eq as eq14, inArray as inArray6 } from "drizzle-orm";
7023
+ import { eq as eq14, inArray as inArray7 } from "drizzle-orm";
6992
7024
  async function citationRoutes(app) {
6993
7025
  app.get("/projects/:name/citations/visibility", async (request, reply) => {
6994
7026
  const project = resolveProject(app.db, request.params.name);
@@ -7012,7 +7044,7 @@ async function citationRoutes(app) {
7012
7044
  competitorOverlap: querySnapshots.competitorOverlap,
7013
7045
  answerMentioned: querySnapshots.answerMentioned,
7014
7046
  createdAt: querySnapshots.createdAt
7015
- }).from(querySnapshots).where(inArray6(querySnapshots.runId, projectRuns.map((r) => r.id))).all();
7047
+ }).from(querySnapshots).where(inArray7(querySnapshots.runId, projectRuns.map((r) => r.id))).all();
7016
7048
  if (rawSnapshots.length === 0) {
7017
7049
  return reply.send(emptyCitationVisibility("no-runs-yet"));
7018
7050
  }
@@ -7153,7 +7185,7 @@ function normalizeDomain2(domain) {
7153
7185
  }
7154
7186
 
7155
7187
  // ../api-routes/src/composites.ts
7156
- import { eq as eq15, and as and7, desc as desc7, sql as sql5, like, or as or4, inArray as inArray7 } from "drizzle-orm";
7188
+ import { eq as eq15, and as and7, desc as desc7, sql as sql6, like, or as or4, inArray as inArray8 } from "drizzle-orm";
7157
7189
  var TOP_INSIGHT_LIMIT = 5;
7158
7190
  var SEARCH_HIT_HARD_LIMIT = 50;
7159
7191
  var SEARCH_SNIPPET_RADIUS = 80;
@@ -7205,10 +7237,19 @@ async function compositeRoutes(app) {
7205
7237
  const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq15(queries.projectId, project.id)).all();
7206
7238
  const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
7207
7239
  const configuredApiProviders = parseJsonColumn(project.providers, []).filter((p) => !p.startsWith("cdp:"));
7240
+ const projectDomains = effectiveDomains({
7241
+ canonicalDomain: project.canonicalDomain,
7242
+ ownedDomains: parseJsonColumn(project.ownedDomains, [])
7243
+ });
7208
7244
  const scores = {
7209
7245
  mention: buildMentionCoverage(latestSnapshots, { configuredApiProviders }),
7210
7246
  visibility: buildVisibilityScore(latestSnapshots, { configuredApiProviders }),
7247
+ shareOfVoice: buildShareOfVoice(latestSnapshots, {
7248
+ projectDomains,
7249
+ competitorDomains: competitorRows.map((c) => c.domain)
7250
+ }),
7211
7251
  gapQueries: buildGapQueryScore(latestSnapshots),
7252
+ mentionGaps: buildMentionGapScore(latestSnapshots),
7212
7253
  indexCoverage: buildIndexCoverageScore(app, project.id),
7213
7254
  competitorPressure: buildCompetitorPressureScore(
7214
7255
  latestSnapshots,
@@ -7217,7 +7258,9 @@ async function compositeRoutes(app) {
7217
7258
  ),
7218
7259
  runStatus: buildRunStatusScore(allRuns)
7219
7260
  };
7220
- const movementSummary = buildMovementSummary(latestSnapshots, previousSnapshots);
7261
+ const movementSummary = buildMovementSummary(latestSnapshots, previousSnapshots, {
7262
+ queryLookup: queryLookup.byId
7263
+ });
7221
7264
  const providerScores = buildProviderScores(latestSnapshots);
7222
7265
  const overviewCompetitors = buildOverviewCompetitors(
7223
7266
  latestSnapshots,
@@ -7271,9 +7314,9 @@ async function compositeRoutes(app) {
7271
7314
  and7(
7272
7315
  eq15(queries.projectId, project.id),
7273
7316
  or4(
7274
- sql5`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
7275
- sql5`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
7276
- sql5`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
7317
+ sql6`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
7318
+ sql6`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
7319
+ sql6`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
7277
7320
  like(queries.query, pattern)
7278
7321
  )
7279
7322
  )
@@ -7284,8 +7327,8 @@ async function compositeRoutes(app) {
7284
7327
  or4(
7285
7328
  like(insights.title, pattern),
7286
7329
  like(insights.query, pattern),
7287
- sql5`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
7288
- sql5`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
7330
+ sql6`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
7331
+ sql6`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
7289
7332
  )
7290
7333
  )
7291
7334
  ).orderBy(desc7(insights.createdAt)).limit(limit + 1).all();
@@ -7360,7 +7403,7 @@ function loadSnapshotsByRunIds(app, runIds) {
7360
7403
  answerMentioned: querySnapshots.answerMentioned,
7361
7404
  competitorOverlap: querySnapshots.competitorOverlap,
7362
7405
  citedDomains: querySnapshots.citedDomains
7363
- }).from(querySnapshots).where(inArray7(querySnapshots.runId, [...runIds])).all());
7406
+ }).from(querySnapshots).where(inArray8(querySnapshots.runId, [...runIds])).all());
7364
7407
  for (const row of rows) {
7365
7408
  const list = result.get(row.runId) ?? [];
7366
7409
  list.push({
@@ -8139,6 +8182,32 @@ var routeCatalog = [
8139
8182
  200: { description: "Queries appended." }
8140
8183
  }
8141
8184
  },
8185
+ {
8186
+ method: "post",
8187
+ path: "/api/v1/projects/{name}/queries/replace-preview",
8188
+ summary: "Preview the impact of replacing tracked queries",
8189
+ description: "Read-only impact summary backing `canonry query replace --dry-run`. Returns current vs proposed query sets, the added/removed/unchanged diff, and the count of snapshots that would detach (queryId \u2192 NULL; queryText preserved).",
8190
+ tags: ["queries"],
8191
+ parameters: [nameParameter],
8192
+ requestBody: {
8193
+ required: true,
8194
+ content: {
8195
+ "application/json": {
8196
+ schema: {
8197
+ type: "object",
8198
+ required: ["queries"],
8199
+ properties: {
8200
+ queries: stringArraySchema
8201
+ }
8202
+ }
8203
+ }
8204
+ }
8205
+ },
8206
+ responses: {
8207
+ 200: { description: "Replace preview returned." },
8208
+ 404: { description: "Project not found." }
8209
+ }
8210
+ },
8142
8211
  {
8143
8212
  method: "post",
8144
8213
  path: "/api/v1/projects/{name}/queries/generate",
@@ -11418,7 +11487,7 @@ function formatNotification(row) {
11418
11487
 
11419
11488
  // ../api-routes/src/google.ts
11420
11489
  import crypto14 from "crypto";
11421
- import { eq as eq18, and as and9, desc as desc8, sql as sql6 } from "drizzle-orm";
11490
+ import { eq as eq18, and as and9, desc as desc8, sql as sql7 } from "drizzle-orm";
11422
11491
 
11423
11492
  // ../integration-google/src/constants.ts
11424
11493
  var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
@@ -12644,11 +12713,11 @@ async function googleRoutes(app, opts) {
12644
12713
  const { startDate, endDate, query, page, limit, offset } = request.query;
12645
12714
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
12646
12715
  const conditions = [eq18(gscSearchData.projectId, project.id)];
12647
- if (startDate) conditions.push(sql6`${gscSearchData.date} >= ${startDate}`);
12648
- else if (cutoffDate) conditions.push(sql6`${gscSearchData.date} >= ${cutoffDate}`);
12649
- if (endDate) conditions.push(sql6`${gscSearchData.date} <= ${endDate}`);
12650
- if (query) conditions.push(sql6`${gscSearchData.query} LIKE ${"%" + query + "%"}`);
12651
- if (page) conditions.push(sql6`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
12716
+ if (startDate) conditions.push(sql7`${gscSearchData.date} >= ${startDate}`);
12717
+ else if (cutoffDate) conditions.push(sql7`${gscSearchData.date} >= ${cutoffDate}`);
12718
+ if (endDate) conditions.push(sql7`${gscSearchData.date} <= ${endDate}`);
12719
+ if (query) conditions.push(sql7`${gscSearchData.query} LIKE ${"%" + query + "%"}`);
12720
+ if (page) conditions.push(sql7`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
12652
12721
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
12653
12722
  const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
12654
12723
  const rows = app.db.select().from(gscSearchData).where(and9(...conditions)).orderBy(desc8(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
@@ -13893,7 +13962,7 @@ async function cdpRoutes(app, opts) {
13893
13962
 
13894
13963
  // ../api-routes/src/ga.ts
13895
13964
  import crypto16 from "crypto";
13896
- import { eq as eq21, desc as desc10, and as and12, sql as sql7 } from "drizzle-orm";
13965
+ import { eq as eq21, desc as desc10, and as and12, sql as sql8 } from "drizzle-orm";
13897
13966
  function gaLog(level, action, ctx) {
13898
13967
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
13899
13968
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -14190,8 +14259,8 @@ async function ga4Routes(app, opts) {
14190
14259
  tx.delete(gaTrafficSnapshots).where(
14191
14260
  and12(
14192
14261
  eq21(gaTrafficSnapshots.projectId, project.id),
14193
- sql7`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
14194
- sql7`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
14262
+ sql8`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
14263
+ sql8`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
14195
14264
  )
14196
14265
  ).run();
14197
14266
  for (const row of rows) {
@@ -14214,8 +14283,8 @@ async function ga4Routes(app, opts) {
14214
14283
  tx.delete(gaAiReferrals).where(
14215
14284
  and12(
14216
14285
  eq21(gaAiReferrals.projectId, project.id),
14217
- sql7`${gaAiReferrals.date} >= ${summary.periodStart}`,
14218
- sql7`${gaAiReferrals.date} <= ${summary.periodEnd}`
14286
+ sql8`${gaAiReferrals.date} >= ${summary.periodStart}`,
14287
+ sql8`${gaAiReferrals.date} <= ${summary.periodEnd}`
14219
14288
  )
14220
14289
  ).run();
14221
14290
  for (const row of aiReferrals) {
@@ -14240,8 +14309,8 @@ async function ga4Routes(app, opts) {
14240
14309
  tx.delete(gaSocialReferrals).where(
14241
14310
  and12(
14242
14311
  eq21(gaSocialReferrals.projectId, project.id),
14243
- sql7`${gaSocialReferrals.date} >= ${summary.periodStart}`,
14244
- sql7`${gaSocialReferrals.date} <= ${summary.periodEnd}`
14312
+ sql8`${gaSocialReferrals.date} >= ${summary.periodStart}`,
14313
+ sql8`${gaSocialReferrals.date} <= ${summary.periodEnd}`
14245
14314
  )
14246
14315
  ).run();
14247
14316
  for (const row of socialReferrals) {
@@ -14331,11 +14400,11 @@ async function ga4Routes(app, opts) {
14331
14400
  const cutoff = windowCutoff(window);
14332
14401
  const cutoffDate = cutoff?.slice(0, 10) ?? null;
14333
14402
  const snapshotConditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
14334
- if (cutoffDate) snapshotConditions.push(sql7`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
14403
+ if (cutoffDate) snapshotConditions.push(sql8`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
14335
14404
  const aiConditions = [eq21(gaAiReferrals.projectId, project.id)];
14336
- if (cutoffDate) aiConditions.push(sql7`${gaAiReferrals.date} >= ${cutoffDate}`);
14405
+ if (cutoffDate) aiConditions.push(sql8`${gaAiReferrals.date} >= ${cutoffDate}`);
14337
14406
  const socialConditions = [eq21(gaSocialReferrals.projectId, project.id)];
14338
- if (cutoffDate) socialConditions.push(sql7`${gaSocialReferrals.date} >= ${cutoffDate}`);
14407
+ if (cutoffDate) socialConditions.push(sql8`${gaSocialReferrals.date} >= ${cutoffDate}`);
14339
14408
  const windowSummaryRow = cutoffDate ? app.db.select({
14340
14409
  totalSessions: gaTrafficWindowSummaries.totalSessions,
14341
14410
  totalOrganicSessions: gaTrafficWindowSummaries.totalOrganicSessions,
@@ -14348,9 +14417,9 @@ async function ga4Routes(app, opts) {
14348
14417
  )
14349
14418
  ).get() : null;
14350
14419
  const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
14351
- totalSessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
14352
- totalOrganicSessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
14353
- totalUsers: sql7`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
14420
+ totalSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
14421
+ totalOrganicSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
14422
+ totalUsers: sql8`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
14354
14423
  }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get() : null;
14355
14424
  const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
14356
14425
  totalSessions: gaTrafficSummaries.totalSessions,
@@ -14358,38 +14427,38 @@ async function ga4Routes(app, opts) {
14358
14427
  totalUsers: gaTrafficSummaries.totalUsers
14359
14428
  }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
14360
14429
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
14361
- totalDirectSessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
14430
+ totalDirectSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
14362
14431
  }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get();
14363
14432
  const summaryMeta = app.db.select({
14364
14433
  periodStart: gaTrafficSummaries.periodStart,
14365
14434
  periodEnd: gaTrafficSummaries.periodEnd
14366
14435
  }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
14367
14436
  const rows = app.db.select({
14368
- landingPage: sql7`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
14369
- sessions: sql7`SUM(${gaTrafficSnapshots.sessions})`,
14370
- organicSessions: sql7`SUM(${gaTrafficSnapshots.organicSessions})`,
14371
- directSessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
14372
- users: sql7`SUM(${gaTrafficSnapshots.users})`
14373
- }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(sql7`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql7`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
14437
+ landingPage: sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
14438
+ sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
14439
+ organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
14440
+ directSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
14441
+ users: sql8`SUM(${gaTrafficSnapshots.users})`
14442
+ }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
14374
14443
  const aiReferralRows = app.db.select({
14375
14444
  source: gaAiReferrals.source,
14376
14445
  medium: gaAiReferrals.medium,
14377
14446
  sourceDimension: gaAiReferrals.sourceDimension,
14378
- sessions: sql7`SUM(${gaAiReferrals.sessions})`,
14379
- users: sql7`SUM(${gaAiReferrals.users})`
14447
+ sessions: sql8`SUM(${gaAiReferrals.sessions})`,
14448
+ users: sql8`SUM(${gaAiReferrals.users})`
14380
14449
  }).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
14381
14450
  const aiReferralLandingPageRows = app.db.select({
14382
14451
  source: gaAiReferrals.source,
14383
14452
  medium: gaAiReferrals.medium,
14384
14453
  sourceDimension: gaAiReferrals.sourceDimension,
14385
- landingPage: sql7`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
14386
- sessions: sql7`SUM(${gaAiReferrals.sessions})`,
14387
- users: sql7`SUM(${gaAiReferrals.users})`
14454
+ landingPage: sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
14455
+ sessions: sql8`SUM(${gaAiReferrals.sessions})`,
14456
+ users: sql8`SUM(${gaAiReferrals.users})`
14388
14457
  }).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(
14389
14458
  gaAiReferrals.source,
14390
14459
  gaAiReferrals.medium,
14391
14460
  gaAiReferrals.sourceDimension,
14392
- sql7`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
14461
+ sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
14393
14462
  ).all();
14394
14463
  const aiReferrals = pickWinningDimension(
14395
14464
  aiReferralRows,
@@ -14400,10 +14469,10 @@ async function ga4Routes(app, opts) {
14400
14469
  (r) => `${r.source}\0${r.medium}\0${r.landingPage}`
14401
14470
  );
14402
14471
  const aiDeduped = app.db.select({
14403
- sessions: sql7`COALESCE(SUM(max_sessions), 0)`,
14404
- users: sql7`COALESCE(SUM(max_users), 0)`
14472
+ sessions: sql8`COALESCE(SUM(max_sessions), 0)`,
14473
+ users: sql8`COALESCE(SUM(max_users), 0)`
14405
14474
  }).from(
14406
- sql7`(
14475
+ sql8`(
14407
14476
  SELECT date, source, medium,
14408
14477
  MAX(dimension_sessions) AS max_sessions,
14409
14478
  MAX(dimension_users) AS max_users
@@ -14412,7 +14481,7 @@ async function ga4Routes(app, opts) {
14412
14481
  SUM(sessions) AS dimension_sessions,
14413
14482
  SUM(users) AS dimension_users
14414
14483
  FROM ga_ai_referrals
14415
- WHERE project_id = ${project.id}${cutoffDate ? sql7` AND date >= ${cutoffDate}` : sql7``}
14484
+ WHERE project_id = ${project.id}${cutoffDate ? sql8` AND date >= ${cutoffDate}` : sql8``}
14416
14485
  GROUP BY date, source, medium, source_dimension
14417
14486
  )
14418
14487
  GROUP BY date, source, medium
@@ -14420,8 +14489,8 @@ async function ga4Routes(app, opts) {
14420
14489
  ).get();
14421
14490
  const aiBySessionRows = app.db.select({
14422
14491
  channelGroup: gaAiReferrals.channelGroup,
14423
- sessions: sql7`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
14424
- users: sql7`COALESCE(SUM(${gaAiReferrals.users}), 0)`
14492
+ sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
14493
+ users: sql8`COALESCE(SUM(${gaAiReferrals.users}), 0)`
14425
14494
  }).from(gaAiReferrals).where(and12(...aiConditions, eq21(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
14426
14495
  const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
14427
14496
  let aiBySessionUsers = 0;
@@ -14434,12 +14503,12 @@ async function ga4Routes(app, opts) {
14434
14503
  source: gaSocialReferrals.source,
14435
14504
  medium: gaSocialReferrals.medium,
14436
14505
  channelGroup: gaSocialReferrals.channelGroup,
14437
- sessions: sql7`SUM(${gaSocialReferrals.sessions})`,
14438
- users: sql7`SUM(${gaSocialReferrals.users})`
14439
- }).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql7`SUM(${gaSocialReferrals.sessions}) DESC`).all();
14506
+ sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
14507
+ users: sql8`SUM(${gaSocialReferrals.users})`
14508
+ }).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql8`SUM(${gaSocialReferrals.sessions}) DESC`).all();
14440
14509
  const socialTotals = app.db.select({
14441
- sessions: sql7`SUM(${gaSocialReferrals.sessions})`,
14442
- users: sql7`SUM(${gaSocialReferrals.users})`
14510
+ sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
14511
+ users: sql8`SUM(${gaSocialReferrals.users})`
14443
14512
  }).from(gaSocialReferrals).where(and12(...socialConditions)).get();
14444
14513
  const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).orderBy(desc10(gaTrafficSummaries.syncedAt)).limit(1).get();
14445
14514
  const total = summaryRow?.totalSessions ?? 0;
@@ -14522,21 +14591,21 @@ async function ga4Routes(app, opts) {
14522
14591
  requireGa4Connection(opts, project.name, project.canonicalDomain);
14523
14592
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
14524
14593
  const conditions = [eq21(gaAiReferrals.projectId, project.id)];
14525
- if (cutoffDate) conditions.push(sql7`${gaAiReferrals.date} >= ${cutoffDate}`);
14594
+ if (cutoffDate) conditions.push(sql8`${gaAiReferrals.date} >= ${cutoffDate}`);
14526
14595
  const rows = app.db.select({
14527
14596
  date: gaAiReferrals.date,
14528
14597
  source: gaAiReferrals.source,
14529
14598
  medium: gaAiReferrals.medium,
14530
- landingPage: sql7`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
14599
+ landingPage: sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
14531
14600
  sourceDimension: gaAiReferrals.sourceDimension,
14532
- sessions: sql7`SUM(${gaAiReferrals.sessions})`,
14533
- users: sql7`SUM(${gaAiReferrals.users})`
14601
+ sessions: sql8`SUM(${gaAiReferrals.sessions})`,
14602
+ users: sql8`SUM(${gaAiReferrals.users})`
14534
14603
  }).from(gaAiReferrals).where(and12(...conditions)).groupBy(
14535
14604
  gaAiReferrals.date,
14536
14605
  gaAiReferrals.source,
14537
14606
  gaAiReferrals.medium,
14538
14607
  gaAiReferrals.sourceDimension,
14539
- sql7`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
14608
+ sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
14540
14609
  ).orderBy(gaAiReferrals.date).all();
14541
14610
  return rows;
14542
14611
  });
@@ -14545,7 +14614,7 @@ async function ga4Routes(app, opts) {
14545
14614
  requireGa4Connection(opts, project.name, project.canonicalDomain);
14546
14615
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
14547
14616
  const conditions = [eq21(gaSocialReferrals.projectId, project.id)];
14548
- if (cutoffDate) conditions.push(sql7`${gaSocialReferrals.date} >= ${cutoffDate}`);
14617
+ if (cutoffDate) conditions.push(sql8`${gaSocialReferrals.date} >= ${cutoffDate}`);
14549
14618
  const rows = app.db.select({
14550
14619
  date: gaSocialReferrals.date,
14551
14620
  source: gaSocialReferrals.source,
@@ -14566,10 +14635,10 @@ async function ga4Routes(app, opts) {
14566
14635
  d.setDate(d.getDate() - n);
14567
14636
  return fmt(d);
14568
14637
  };
14569
- const sumSocial = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(
14638
+ const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(
14570
14639
  eq21(gaSocialReferrals.projectId, project.id),
14571
- sql7`${gaSocialReferrals.date} >= ${from}`,
14572
- sql7`${gaSocialReferrals.date} < ${to}`
14640
+ sql8`${gaSocialReferrals.date} >= ${from}`,
14641
+ sql8`${gaSocialReferrals.date} < ${to}`
14573
14642
  )).get();
14574
14643
  const current7d = sumSocial(daysAgo2(7), fmt(today));
14575
14644
  const prev7d = sumSocial(daysAgo2(14), daysAgo2(7));
@@ -14578,19 +14647,19 @@ async function ga4Routes(app, opts) {
14578
14647
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
14579
14648
  const sourceCurrent = app.db.select({
14580
14649
  source: gaSocialReferrals.source,
14581
- sessions: sql7`SUM(${gaSocialReferrals.sessions})`
14650
+ sessions: sql8`SUM(${gaSocialReferrals.sessions})`
14582
14651
  }).from(gaSocialReferrals).where(and12(
14583
14652
  eq21(gaSocialReferrals.projectId, project.id),
14584
- sql7`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
14585
- sql7`${gaSocialReferrals.date} < ${fmt(today)}`
14653
+ sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
14654
+ sql8`${gaSocialReferrals.date} < ${fmt(today)}`
14586
14655
  )).groupBy(gaSocialReferrals.source).all();
14587
14656
  const sourcePrev = app.db.select({
14588
14657
  source: gaSocialReferrals.source,
14589
- sessions: sql7`SUM(${gaSocialReferrals.sessions})`
14658
+ sessions: sql8`SUM(${gaSocialReferrals.sessions})`
14590
14659
  }).from(gaSocialReferrals).where(and12(
14591
14660
  eq21(gaSocialReferrals.projectId, project.id),
14592
- sql7`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
14593
- sql7`${gaSocialReferrals.date} < ${daysAgo2(7)}`
14661
+ sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
14662
+ sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`
14594
14663
  )).groupBy(gaSocialReferrals.source).all();
14595
14664
  const prevMap = new Map(sourcePrev.map((r) => [r.source, r.sessions]));
14596
14665
  let biggestMover = null;
@@ -14629,16 +14698,16 @@ async function ga4Routes(app, opts) {
14629
14698
  return fmt(d);
14630
14699
  };
14631
14700
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
14632
- const sumTotal = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql7`${gaTrafficSnapshots.date} >= ${from}`, sql7`${gaTrafficSnapshots.date} < ${to}`)).get();
14633
- const sumOrganic = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql7`${gaTrafficSnapshots.date} >= ${from}`, sql7`${gaTrafficSnapshots.date} < ${to}`)).get();
14634
- const sumDirect = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql7`${gaTrafficSnapshots.date} >= ${from}`, sql7`${gaTrafficSnapshots.date} < ${to}`)).get();
14635
- const sumAi = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14701
+ const sumTotal = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
14702
+ const sumOrganic = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
14703
+ const sumDirect = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
14704
+ const sumAi = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14636
14705
  eq21(gaAiReferrals.projectId, project.id),
14637
- sql7`${gaAiReferrals.date} >= ${from}`,
14638
- sql7`${gaAiReferrals.date} < ${to}`,
14706
+ sql8`${gaAiReferrals.date} >= ${from}`,
14707
+ sql8`${gaAiReferrals.date} < ${to}`,
14639
14708
  eq21(gaAiReferrals.sourceDimension, "session")
14640
14709
  )).get();
14641
- const sumSocial = (from, to) => app.db.select({ sessions: sql7`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql7`${gaSocialReferrals.date} >= ${from}`, sql7`${gaSocialReferrals.date} < ${to}`)).get();
14710
+ const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${from}`, sql8`${gaSocialReferrals.date} < ${to}`)).get();
14642
14711
  const todayStr = fmt(today);
14643
14712
  const buildTrend = (sum) => {
14644
14713
  const c7 = sum(daysAgo2(7), todayStr)?.sessions ?? 0;
@@ -14647,16 +14716,16 @@ async function ga4Routes(app, opts) {
14647
14716
  const p30 = sum(daysAgo2(60), daysAgo2(30))?.sessions ?? 0;
14648
14717
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
14649
14718
  };
14650
- const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql7`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14719
+ const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14651
14720
  eq21(gaAiReferrals.projectId, project.id),
14652
- sql7`${gaAiReferrals.date} >= ${daysAgo2(7)}`,
14653
- sql7`${gaAiReferrals.date} < ${todayStr}`,
14721
+ sql8`${gaAiReferrals.date} >= ${daysAgo2(7)}`,
14722
+ sql8`${gaAiReferrals.date} < ${todayStr}`,
14654
14723
  eq21(gaAiReferrals.sourceDimension, "session")
14655
14724
  )).groupBy(gaAiReferrals.source).all();
14656
- const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql7`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14725
+ const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
14657
14726
  eq21(gaAiReferrals.projectId, project.id),
14658
- sql7`${gaAiReferrals.date} >= ${daysAgo2(14)}`,
14659
- sql7`${gaAiReferrals.date} < ${daysAgo2(7)}`,
14727
+ sql8`${gaAiReferrals.date} >= ${daysAgo2(14)}`,
14728
+ sql8`${gaAiReferrals.date} < ${daysAgo2(7)}`,
14660
14729
  eq21(gaAiReferrals.sourceDimension, "session")
14661
14730
  )).groupBy(gaAiReferrals.source).all();
14662
14731
  const findBiggestMover = (current, prev) => {
@@ -14673,8 +14742,8 @@ async function ga4Routes(app, opts) {
14673
14742
  }
14674
14743
  return mover;
14675
14744
  };
14676
- const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql7`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql7`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql7`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
14677
- const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql7`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql7`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql7`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
14745
+ const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql8`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
14746
+ const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
14678
14747
  return {
14679
14748
  total: buildTrend(sumTotal),
14680
14749
  organic: buildTrend(sumOrganic),
@@ -14690,12 +14759,12 @@ async function ga4Routes(app, opts) {
14690
14759
  requireGa4Connection(opts, project.name, project.canonicalDomain);
14691
14760
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
14692
14761
  const conditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
14693
- if (cutoffDate) conditions.push(sql7`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
14762
+ if (cutoffDate) conditions.push(sql8`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
14694
14763
  const rows = app.db.select({
14695
14764
  date: gaTrafficSnapshots.date,
14696
- sessions: sql7`SUM(${gaTrafficSnapshots.sessions})`,
14697
- organicSessions: sql7`SUM(${gaTrafficSnapshots.organicSessions})`,
14698
- users: sql7`SUM(${gaTrafficSnapshots.users})`
14765
+ sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
14766
+ organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
14767
+ users: sql8`SUM(${gaTrafficSnapshots.users})`
14699
14768
  }).from(gaTrafficSnapshots).where(and12(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
14700
14769
  return rows.map((r) => ({
14701
14770
  date: r.date,
@@ -14708,11 +14777,11 @@ async function ga4Routes(app, opts) {
14708
14777
  const project = resolveProject(app.db, request.params.name);
14709
14778
  requireGa4Connection(opts, project.name, project.canonicalDomain);
14710
14779
  const trafficPages = app.db.select({
14711
- landingPage: sql7`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
14712
- sessions: sql7`SUM(${gaTrafficSnapshots.sessions})`,
14713
- organicSessions: sql7`SUM(${gaTrafficSnapshots.organicSessions})`,
14714
- users: sql7`SUM(${gaTrafficSnapshots.users})`
14715
- }).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(sql7`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql7`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
14780
+ landingPage: sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
14781
+ sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
14782
+ organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
14783
+ users: sql8`SUM(${gaTrafficSnapshots.users})`
14784
+ }).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
14716
14785
  return {
14717
14786
  pages: trafficPages.map((r) => ({
14718
14787
  landingPage: r.landingPage,
@@ -16349,7 +16418,7 @@ async function wordpressRoutes(app, opts) {
16349
16418
 
16350
16419
  // ../api-routes/src/backlinks.ts
16351
16420
  import crypto18 from "crypto";
16352
- import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as sql8 } from "drizzle-orm";
16421
+ import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as sql9 } from "drizzle-orm";
16353
16422
 
16354
16423
  // ../integration-commoncrawl/src/constants.ts
16355
16424
  import os3 from "os";
@@ -16633,7 +16702,7 @@ async function queryBacklinks(opts) {
16633
16702
  const reversed = opts.targets.map(reverseDomain);
16634
16703
  const targetList = reversed.map(quote).join(", ");
16635
16704
  const limitClause = opts.limitPerTarget ? `QUALIFY row_number() OVER (PARTITION BY t.target_rev_domain ORDER BY v.num_hosts DESC) <= ${Math.floor(opts.limitPerTarget)}` : "";
16636
- const sql15 = `
16705
+ const sql16 = `
16637
16706
  WITH vertices AS (
16638
16707
  SELECT * FROM read_csv(
16639
16708
  ${quote(opts.vertexPath)},
@@ -16669,7 +16738,7 @@ async function queryBacklinks(opts) {
16669
16738
  const conn = await instance.connect();
16670
16739
  let rows;
16671
16740
  try {
16672
- const reader = await conn.runAndReadAll(sql15);
16741
+ const reader = await conn.runAndReadAll(sql16);
16673
16742
  rows = reader.getRowObjects();
16674
16743
  } finally {
16675
16744
  conn.disconnectSync?.();
@@ -16845,12 +16914,12 @@ function computeFilteredSummary(db, base) {
16845
16914
  );
16846
16915
  const filteredCondition = and14(baseDomainCondition, backlinkCrawlerExclusionClause());
16847
16916
  const unfilteredAgg = db.select({
16848
- count: sql8`count(*)`,
16849
- total: sql8`coalesce(sum(${backlinkDomains.numHosts}), 0)`
16917
+ count: sql9`count(*)`,
16918
+ total: sql9`coalesce(sum(${backlinkDomains.numHosts}), 0)`
16850
16919
  }).from(backlinkDomains).where(baseDomainCondition).get();
16851
16920
  const filteredAgg = db.select({
16852
- count: sql8`count(*)`,
16853
- total: sql8`coalesce(sum(${backlinkDomains.numHosts}), 0)`
16921
+ count: sql9`count(*)`,
16922
+ total: sql9`coalesce(sum(${backlinkDomains.numHosts}), 0)`
16854
16923
  }).from(backlinkDomains).where(filteredCondition).get();
16855
16924
  const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc11(backlinkDomains.numHosts)).limit(10).all();
16856
16925
  const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
@@ -17024,7 +17093,7 @@ async function backlinksRoutes(app, opts) {
17024
17093
  eq22(backlinkDomains.release, targetRelease)
17025
17094
  );
17026
17095
  const domainCondition = excludeCrawlers ? and14(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
17027
- const totalRow = app.db.select({ count: sql8`count(*)` }).from(backlinkDomains).where(domainCondition).get();
17096
+ const totalRow = app.db.select({ count: sql9`count(*)` }).from(backlinkDomains).where(domainCondition).get();
17028
17097
  const rows = app.db.select({
17029
17098
  linkingDomain: backlinkDomains.linkingDomain,
17030
17099
  numHosts: backlinkDomains.numHosts
@@ -17059,7 +17128,7 @@ async function backlinksRoutes(app, opts) {
17059
17128
 
17060
17129
  // ../api-routes/src/traffic.ts
17061
17130
  import crypto20 from "crypto";
17062
- import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as sql9 } from "drizzle-orm";
17131
+ import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as sql10 } from "drizzle-orm";
17063
17132
 
17064
17133
  // ../integration-cloud-run/src/auth.ts
17065
17134
  import crypto19 from "crypto";
@@ -18797,7 +18866,7 @@ async function trafficRoutes(app, opts) {
18797
18866
  crawlerEventsHourly.status
18798
18867
  ],
18799
18868
  set: {
18800
- hits: sql9`${crawlerEventsHourly.hits} + ${bucket.hits}`,
18869
+ hits: sql10`${crawlerEventsHourly.hits} + ${bucket.hits}`,
18801
18870
  sampledUserAgent: bucket.sampledUserAgent,
18802
18871
  updatedAt: finishedAt
18803
18872
  }
@@ -18832,7 +18901,7 @@ async function trafficRoutes(app, opts) {
18832
18901
  aiReferralEventsHourly.status
18833
18902
  ],
18834
18903
  set: {
18835
- sessionsOrHits: sql9`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
18904
+ sessionsOrHits: sql10`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
18836
18905
  updatedAt: finishedAt
18837
18906
  }
18838
18907
  }).run();
@@ -19084,19 +19153,19 @@ async function trafficRoutes(app, opts) {
19084
19153
  return response;
19085
19154
  });
19086
19155
  function buildSourceDetail(projectId, row, since) {
19087
- const crawlerTotals = app.db.select({ total: sql9`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
19156
+ const crawlerTotals = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
19088
19157
  and15(
19089
19158
  eq23(crawlerEventsHourly.sourceId, row.id),
19090
19159
  gte2(crawlerEventsHourly.tsHour, since)
19091
19160
  )
19092
19161
  ).get();
19093
- const aiTotals = app.db.select({ total: sql9`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
19162
+ const aiTotals = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
19094
19163
  and15(
19095
19164
  eq23(aiReferralEventsHourly.sourceId, row.id),
19096
19165
  gte2(aiReferralEventsHourly.tsHour, since)
19097
19166
  )
19098
19167
  ).get();
19099
- const sampleTotals = app.db.select({ total: sql9`COUNT(*)` }).from(rawEventSamples).where(
19168
+ const sampleTotals = app.db.select({ total: sql10`COUNT(*)` }).from(rawEventSamples).where(
19100
19169
  and15(
19101
19170
  eq23(rawEventSamples.sourceId, row.id),
19102
19171
  gte2(rawEventSamples.ts, since)
@@ -19198,7 +19267,7 @@ async function trafficRoutes(app, opts) {
19198
19267
  ];
19199
19268
  if (sourceIdParam) crawlerFilters.push(eq23(crawlerEventsHourly.sourceId, sourceIdParam));
19200
19269
  const crawlerWhere = and15(...crawlerFilters);
19201
- const total = app.db.select({ total: sql9`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
19270
+ const total = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
19202
19271
  crawlerTotal = Number(total?.total ?? 0);
19203
19272
  const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc12(crawlerEventsHourly.tsHour)).limit(limit).all();
19204
19273
  for (const r of rows) {
@@ -19223,7 +19292,7 @@ async function trafficRoutes(app, opts) {
19223
19292
  ];
19224
19293
  if (sourceIdParam) aiFilters.push(eq23(aiReferralEventsHourly.sourceId, sourceIdParam));
19225
19294
  const aiWhere = and15(...aiFilters);
19226
- const total = app.db.select({ total: sql9`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
19295
+ const total = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
19227
19296
  aiReferralTotal = Number(total?.total ?? 0);
19228
19297
  const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc12(aiReferralEventsHourly.tsHour)).limit(limit).all();
19229
19298
  for (const r of rows) {
@@ -19946,7 +20015,7 @@ var providersConfiguredCheck = {
19946
20015
  var PROVIDERS_CHECKS = [providersConfiguredCheck];
19947
20016
 
19948
20017
  // ../api-routes/src/doctor/checks/traffic-source.ts
19949
- import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as sql10 } from "drizzle-orm";
20018
+ import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as sql11 } from "drizzle-orm";
19950
20019
  var RECENT_DATA_WARN_DAYS = 7;
19951
20020
  var RECENT_DATA_FAIL_DAYS = 30;
19952
20021
  function skippedNoProject2() {
@@ -20040,7 +20109,7 @@ var recentDataCheck = {
20040
20109
  const warnCutoff = new Date(now.getTime() - RECENT_DATA_WARN_DAYS * 24 * 60 * 6e4).toISOString();
20041
20110
  const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
20042
20111
  const recentCrawlers = Number(
20043
- ctx.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
20112
+ ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
20044
20113
  and16(
20045
20114
  eq24(crawlerEventsHourly.projectId, ctx.project.id),
20046
20115
  gte3(crawlerEventsHourly.tsHour, warnCutoff)
@@ -20048,7 +20117,7 @@ var recentDataCheck = {
20048
20117
  ).get()?.total ?? 0
20049
20118
  );
20050
20119
  const recentReferrals = Number(
20051
- ctx.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
20120
+ ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
20052
20121
  and16(
20053
20122
  eq24(aiReferralEventsHourly.projectId, ctx.project.id),
20054
20123
  gte3(aiReferralEventsHourly.tsHour, warnCutoff)
@@ -20064,7 +20133,7 @@ var recentDataCheck = {
20064
20133
  };
20065
20134
  }
20066
20135
  const olderCrawlers = Number(
20067
- ctx.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
20136
+ ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
20068
20137
  and16(
20069
20138
  eq24(crawlerEventsHourly.projectId, ctx.project.id),
20070
20139
  gte3(crawlerEventsHourly.tsHour, failCutoff)
@@ -20072,7 +20141,7 @@ var recentDataCheck = {
20072
20141
  ).get()?.total ?? 0
20073
20142
  );
20074
20143
  const olderReferrals = Number(
20075
- ctx.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
20144
+ ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
20076
20145
  and16(
20077
20146
  eq24(aiReferralEventsHourly.projectId, ctx.project.id),
20078
20147
  gte3(aiReferralEventsHourly.tsHour, failCutoff)
@@ -20372,7 +20441,7 @@ async function doctorRoutes(app, opts) {
20372
20441
 
20373
20442
  // ../api-routes/src/discovery/routes.ts
20374
20443
  import crypto21 from "crypto";
20375
- import { and as and17, desc as desc13, eq as eq25, gte as gte4, inArray as inArray8 } from "drizzle-orm";
20444
+ import { and as and17, desc as desc13, eq as eq25, gte as gte4, inArray as inArray9 } from "drizzle-orm";
20376
20445
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
20377
20446
  async function discoveryRoutes(app, opts) {
20378
20447
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -20407,7 +20476,7 @@ async function discoveryRoutes(app, opts) {
20407
20476
  const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and17(
20408
20477
  eq25(discoverySessions.projectId, project.id),
20409
20478
  eq25(discoverySessions.icpDescription, icpDescription),
20410
- inArray8(discoverySessions.status, [
20479
+ inArray9(discoverySessions.status, [
20411
20480
  DiscoverySessionStatuses.queued,
20412
20481
  DiscoverySessionStatuses.seeding,
20413
20482
  DiscoverySessionStatuses.probing
@@ -23725,7 +23794,7 @@ import crypto24 from "crypto";
23725
23794
  import fs8 from "fs";
23726
23795
  import path10 from "path";
23727
23796
  import os5 from "os";
23728
- import { and as and18, eq as eq27, inArray as inArray9, sql as sql11 } from "drizzle-orm";
23797
+ import { and as and18, eq as eq27, inArray as inArray10, sql as sql12 } from "drizzle-orm";
23729
23798
 
23730
23799
  // src/run-telemetry.ts
23731
23800
  import crypto23 from "crypto";
@@ -24070,7 +24139,7 @@ var JobRunner = class {
24070
24139
  this.registry = registry;
24071
24140
  }
24072
24141
  recoverStaleRuns() {
24073
- const stale = this.db.select({ id: runs.id, status: runs.status }).from(runs).where(inArray9(runs.status, ["running", "queued"])).all();
24142
+ const stale = this.db.select({ id: runs.id, status: runs.status }).from(runs).where(inArray10(runs.status, ["running", "queued"])).all();
24074
24143
  if (stale.length === 0) return;
24075
24144
  const now = (/* @__PURE__ */ new Date()).toISOString();
24076
24145
  for (const run of stale) {
@@ -24133,7 +24202,7 @@ var JobRunner = class {
24133
24202
  }
24134
24203
  log.info("run.dispatch", { runId, providerCount: activeProviders.length, providers: activeProviders.map((p) => p.adapter.name) });
24135
24204
  const scopedQueryNames = parseJsonColumn(existingRun.queries, null);
24136
- projectQueries = scopedQueryNames ? this.db.select().from(queries).where(and18(eq27(queries.projectId, projectId), inArray9(queries.query, scopedQueryNames))).all() : this.db.select().from(queries).where(eq27(queries.projectId, projectId)).all();
24205
+ projectQueries = scopedQueryNames ? this.db.select().from(queries).where(and18(eq27(queries.projectId, projectId), inArray10(queries.query, scopedQueryNames))).all() : this.db.select().from(queries).where(eq27(queries.projectId, projectId)).all();
24137
24206
  const projectCompetitors = this.db.select().from(competitors).where(eq27(competitors.projectId, projectId)).all();
24138
24207
  const competitorDomains = projectCompetitors.map((c) => c.domain);
24139
24208
  const allDomains = effectiveDomains({
@@ -24406,7 +24475,7 @@ var JobRunner = class {
24406
24475
  updatedAt: now
24407
24476
  }).onConflictDoUpdate({
24408
24477
  target: [usageCounters.scope, usageCounters.period, usageCounters.metric],
24409
- set: { count: sql11`${usageCounters.count} + ${count}`, updatedAt: now }
24478
+ set: { count: sql12`${usageCounters.count} + ${count}`, updatedAt: now }
24410
24479
  }).run();
24411
24480
  }
24412
24481
  flushProviderUsage(projectId, providerDispatchCounts) {
@@ -24476,7 +24545,7 @@ function buildPhases(input) {
24476
24545
 
24477
24546
  // src/gsc-sync.ts
24478
24547
  import crypto25 from "crypto";
24479
- import { eq as eq28, and as and19, sql as sql12 } from "drizzle-orm";
24548
+ import { eq as eq28, and as and19, sql as sql13 } from "drizzle-orm";
24480
24549
  var log2 = createLogger("GscSync");
24481
24550
  function formatDate3(d) {
24482
24551
  return d.toISOString().split("T")[0];
@@ -24530,8 +24599,8 @@ async function executeGscSync(db, runId, projectId, opts) {
24530
24599
  db.delete(gscSearchData).where(
24531
24600
  and19(
24532
24601
  eq28(gscSearchData.projectId, projectId),
24533
- sql12`${gscSearchData.date} >= ${startDate}`,
24534
- sql12`${gscSearchData.date} <= ${endDate}`
24602
+ sql13`${gscSearchData.date} >= ${startDate}`,
24603
+ sql13`${gscSearchData.date} <= ${endDate}`
24535
24604
  )
24536
24605
  ).run();
24537
24606
  const batchSize = 500;
@@ -25067,7 +25136,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
25067
25136
  // src/commoncrawl-sync.ts
25068
25137
  import crypto28 from "crypto";
25069
25138
  import path11 from "path";
25070
- import { and as and21, eq as eq31, sql as sql13 } from "drizzle-orm";
25139
+ import { and as and21, eq as eq31, sql as sql14 } from "drizzle-orm";
25071
25140
  var log6 = createLogger("CommonCrawlSync");
25072
25141
  var INSERT_CHUNK_SIZE = 1e4;
25073
25142
  function defaultDeps() {
@@ -25679,23 +25748,25 @@ function buildDiscoveryInsightTitle(input) {
25679
25748
  }
25680
25749
 
25681
25750
  // src/commands/backfill.ts
25682
- import { and as and24, eq as eq34, inArray as inArray10 } from "drizzle-orm";
25751
+ import { and as and24, eq as eq34, inArray as inArray11 } from "drizzle-orm";
25683
25752
  var SNAPSHOT_BATCH_SIZE = 500;
25684
25753
  async function backfillAnswerVisibilityCommand(opts) {
25685
25754
  const config = loadConfig();
25686
25755
  const db = createClient(config.database);
25687
25756
  migrate(db);
25688
25757
  const projectFilter = opts?.project?.trim();
25758
+ const isDryRun = opts?.dryRun === true;
25689
25759
  const scopedProjects = projectFilter ? db.select().from(projects).where(eq34(projects.name, projectFilter)).all() : db.select().from(projects).all();
25690
25760
  let examined = 0;
25691
25761
  let updated = 0;
25762
+ let wouldUpdate = 0;
25692
25763
  let mentioned = 0;
25693
25764
  let reparsed = 0;
25694
25765
  let providerErrors = 0;
25695
25766
  if (scopedProjects.length > 0) {
25696
25767
  const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and24(
25697
25768
  eq34(runs.kind, RunKinds["answer-visibility"]),
25698
- inArray10(runs.projectId, scopedProjects.map((project) => project.id))
25769
+ inArray11(runs.projectId, scopedProjects.map((project) => project.id))
25699
25770
  )).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq34(runs.kind, RunKinds["answer-visibility"])).all();
25700
25771
  const runIdsByProject = /* @__PURE__ */ new Map();
25701
25772
  for (const run of runRows) {
@@ -25727,7 +25798,7 @@ async function backfillAnswerVisibilityCommand(opts) {
25727
25798
  competitorOverlap: querySnapshots.competitorOverlap,
25728
25799
  recommendedCompetitors: querySnapshots.recommendedCompetitors,
25729
25800
  rawResponse: querySnapshots.rawResponse
25730
- }).from(querySnapshots).where(inArray10(querySnapshots.runId, batchRunIds)).all();
25801
+ }).from(querySnapshots).where(inArray11(querySnapshots.runId, batchRunIds)).all();
25731
25802
  const pendingUpdates = [];
25732
25803
  for (const snapshot of snapshotRows) {
25733
25804
  examined++;
@@ -25791,12 +25862,16 @@ async function backfillAnswerVisibilityCommand(opts) {
25791
25862
  }
25792
25863
  }
25793
25864
  if (pendingUpdates.length > 0) {
25794
- db.transaction((tx) => {
25795
- for (const update of pendingUpdates) {
25796
- tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
25797
- }
25798
- });
25799
- updated += pendingUpdates.length;
25865
+ if (isDryRun) {
25866
+ wouldUpdate += pendingUpdates.length;
25867
+ } else {
25868
+ db.transaction((tx) => {
25869
+ for (const update of pendingUpdates) {
25870
+ tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
25871
+ }
25872
+ });
25873
+ updated += pendingUpdates.length;
25874
+ }
25800
25875
  }
25801
25876
  }
25802
25877
  }
@@ -25810,20 +25885,33 @@ async function backfillAnswerVisibilityCommand(opts) {
25810
25885
  reparsed,
25811
25886
  providerErrors
25812
25887
  };
25888
+ if (isDryRun) {
25889
+ result.dryRun = true;
25890
+ result.wouldUpdate = wouldUpdate;
25891
+ }
25813
25892
  if (opts?.format === "json") {
25814
25893
  console.log(JSON.stringify(result, null, 2));
25815
25894
  return;
25816
25895
  }
25817
- console.log("Answer visibility backfill complete.\n");
25896
+ console.log(`Answer visibility backfill ${isDryRun ? "preview" : "complete"}.
25897
+ `);
25818
25898
  if (projectFilter) {
25819
25899
  console.log(` Project: ${projectFilter}`);
25820
25900
  }
25821
25901
  console.log(` Projects: ${scopedProjects.length}`);
25822
- console.log(` Examined: ${examined}`);
25823
- console.log(` Updated: ${updated}`);
25824
- console.log(` Mentioned: ${mentioned}`);
25825
- console.log(` Reparsed: ${reparsed}`);
25826
- console.log(` Errors: ${providerErrors}`);
25902
+ console.log(` Examined: ${examined}`);
25903
+ if (isDryRun) {
25904
+ console.log(` Would update: ${wouldUpdate}`);
25905
+ } else {
25906
+ console.log(` Updated: ${updated}`);
25907
+ }
25908
+ console.log(` Mentioned: ${mentioned}`);
25909
+ console.log(` Reparsed: ${reparsed}`);
25910
+ console.log(` Errors: ${providerErrors}`);
25911
+ if (isDryRun) {
25912
+ console.log(`
25913
+ No DB writes performed. Re-run without --dry-run to apply.`);
25914
+ }
25827
25915
  }
25828
25916
  function backfillNormalizedPaths(db, opts) {
25829
25917
  const baseConditions = [];
@@ -25969,7 +26057,8 @@ async function backfillAiReferralPathsCommand(opts) {
25969
26057
  console.log(` Updated: ${updated}`);
25970
26058
  console.log(` Unchanged: ${unchanged}`);
25971
26059
  }
25972
- function backfillProjectAnswerMentions(db, projectId) {
26060
+ function backfillProjectAnswerMentions(db, projectId, opts) {
26061
+ const isDryRun = opts?.dryRun === true;
25973
26062
  const project = db.select().from(projects).where(eq34(projects.id, projectId)).get();
25974
26063
  if (!project) return { examined: 0, updated: 0, mentioned: 0 };
25975
26064
  const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, projectId)).all().map((row) => row.domain);
@@ -25977,8 +26066,11 @@ function backfillProjectAnswerMentions(db, projectId) {
25977
26066
  const runIds = runRows.map((r) => r.id);
25978
26067
  let examined = 0;
25979
26068
  let updated = 0;
26069
+ let wouldUpdate = 0;
25980
26070
  let mentioned = 0;
25981
- if (runIds.length === 0) return { examined, updated, mentioned };
26071
+ if (runIds.length === 0) {
26072
+ return isDryRun ? { examined, updated, wouldUpdate, mentioned } : { examined, updated, mentioned };
26073
+ }
25982
26074
  const projectDomains = effectiveDomains({
25983
26075
  canonicalDomain: project.canonicalDomain,
25984
26076
  ownedDomains: parseJsonColumn(project.ownedDomains, [])
@@ -25998,7 +26090,7 @@ function backfillProjectAnswerMentions(db, projectId) {
25998
26090
  competitorOverlap: querySnapshots.competitorOverlap,
25999
26091
  recommendedCompetitors: querySnapshots.recommendedCompetitors,
26000
26092
  rawResponse: querySnapshots.rawResponse
26001
- }).from(querySnapshots).where(inArray10(querySnapshots.runId, batchRunIds)).all();
26093
+ }).from(querySnapshots).where(inArray11(querySnapshots.runId, batchRunIds)).all();
26002
26094
  const pendingUpdates = [];
26003
26095
  for (const snapshot of snapshotRows) {
26004
26096
  examined++;
@@ -26041,29 +26133,36 @@ function backfillProjectAnswerMentions(db, projectId) {
26041
26133
  }
26042
26134
  }
26043
26135
  if (pendingUpdates.length > 0) {
26044
- db.transaction((tx) => {
26045
- for (const update of pendingUpdates) {
26046
- tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
26047
- }
26048
- });
26049
- updated += pendingUpdates.length;
26136
+ if (isDryRun) {
26137
+ wouldUpdate += pendingUpdates.length;
26138
+ } else {
26139
+ db.transaction((tx) => {
26140
+ for (const update of pendingUpdates) {
26141
+ tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
26142
+ }
26143
+ });
26144
+ updated += pendingUpdates.length;
26145
+ }
26050
26146
  }
26051
26147
  }
26052
- return { examined, updated, mentioned };
26148
+ return isDryRun ? { examined, updated, wouldUpdate, mentioned } : { examined, updated, mentioned };
26053
26149
  }
26054
26150
  async function backfillAnswerMentionsCommand(opts) {
26055
26151
  const config = loadConfig();
26056
26152
  const db = createClient(config.database);
26057
26153
  migrate(db);
26058
26154
  const projectFilter = opts?.project?.trim();
26155
+ const isDryRun = opts?.dryRun === true;
26059
26156
  const scopedProjects = projectFilter ? db.select().from(projects).where(eq34(projects.name, projectFilter)).all() : db.select().from(projects).all();
26060
26157
  let examined = 0;
26061
26158
  let updated = 0;
26159
+ let wouldUpdate = 0;
26062
26160
  let mentioned = 0;
26063
26161
  for (const project of scopedProjects) {
26064
- const result2 = backfillProjectAnswerMentions(db, project.id);
26162
+ const result2 = backfillProjectAnswerMentions(db, project.id, { dryRun: isDryRun });
26065
26163
  examined += result2.examined;
26066
26164
  updated += result2.updated;
26165
+ wouldUpdate += result2.wouldUpdate ?? 0;
26067
26166
  mentioned += result2.mentioned;
26068
26167
  }
26069
26168
  const result = {
@@ -26073,16 +26172,29 @@ async function backfillAnswerMentionsCommand(opts) {
26073
26172
  updated,
26074
26173
  mentioned
26075
26174
  };
26175
+ if (isDryRun) {
26176
+ result.dryRun = true;
26177
+ result.wouldUpdate = wouldUpdate;
26178
+ }
26076
26179
  if (opts?.format === "json") {
26077
26180
  console.log(JSON.stringify(result, null, 2));
26078
26181
  return;
26079
26182
  }
26080
- console.log("Answer mentions backfill complete.\n");
26081
- if (projectFilter) console.log(` Project: ${projectFilter}`);
26082
- console.log(` Projects: ${scopedProjects.length}`);
26083
- console.log(` Examined: ${examined}`);
26084
- console.log(` Updated: ${updated}`);
26085
- console.log(` Mentioned: ${mentioned}`);
26183
+ console.log(`Answer mentions backfill ${isDryRun ? "preview" : "complete"}.
26184
+ `);
26185
+ if (projectFilter) console.log(` Project: ${projectFilter}`);
26186
+ console.log(` Projects: ${scopedProjects.length}`);
26187
+ console.log(` Examined: ${examined}`);
26188
+ if (isDryRun) {
26189
+ console.log(` Would update: ${wouldUpdate}`);
26190
+ } else {
26191
+ console.log(` Updated: ${updated}`);
26192
+ }
26193
+ console.log(` Mentioned: ${mentioned}`);
26194
+ if (isDryRun) {
26195
+ console.log(`
26196
+ No DB writes performed. Re-run without --dry-run to apply.`);
26197
+ }
26086
26198
  }
26087
26199
  function readStoredGroundingSources(rawResponse) {
26088
26200
  const envelope = parseJsonColumn(rawResponse, {});
@@ -26101,7 +26213,7 @@ function readStoredGroundingSources(rawResponse) {
26101
26213
  return result;
26102
26214
  }
26103
26215
  async function backfillInsightsCommand(project, opts) {
26104
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-7AWRUNI2.js");
26216
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-CIGYPPMR.js");
26105
26217
  const config = loadConfig();
26106
26218
  const db = createClient(config.database);
26107
26219
  migrate(db);
@@ -26419,7 +26531,7 @@ var Scheduler = class {
26419
26531
  };
26420
26532
 
26421
26533
  // src/notifier.ts
26422
- import { eq as eq36, desc as desc16, and as and26, inArray as inArray11, or as or5 } from "drizzle-orm";
26534
+ import { eq as eq36, desc as desc16, and as and26, inArray as inArray12, or as or5 } from "drizzle-orm";
26423
26535
  import crypto31 from "crypto";
26424
26536
  var log10 = createLogger("Notifier");
26425
26537
  var Notifier = class {
@@ -26572,13 +26684,13 @@ var Notifier = class {
26572
26684
  provider: querySnapshots.provider,
26573
26685
  location: querySnapshots.location,
26574
26686
  citationState: querySnapshots.citationState
26575
- }).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(inArray11(querySnapshots.runId, currentRunIds)).all();
26687
+ }).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(inArray12(querySnapshots.runId, currentRunIds)).all();
26576
26688
  const previousSnapshots = this.db.select({
26577
26689
  queryId: querySnapshots.queryId,
26578
26690
  provider: querySnapshots.provider,
26579
26691
  location: querySnapshots.location,
26580
26692
  citationState: querySnapshots.citationState
26581
- }).from(querySnapshots).where(inArray11(querySnapshots.runId, previousRunIds)).all();
26693
+ }).from(querySnapshots).where(inArray12(querySnapshots.runId, previousRunIds)).all();
26582
26694
  const prevMap = /* @__PURE__ */ new Map();
26583
26695
  for (const s of previousSnapshots) {
26584
26696
  if (s.queryId == null) continue;
@@ -27091,7 +27203,7 @@ function resolveSessionProviderAndModel(config, opts) {
27091
27203
 
27092
27204
  // src/agent/memory-store.ts
27093
27205
  import crypto32 from "crypto";
27094
- import { and as and27, desc as desc17, eq as eq38, like as like2, sql as sql14 } from "drizzle-orm";
27206
+ import { and as and27, desc as desc17, eq as eq38, like as like2, sql as sql15 } from "drizzle-orm";
27095
27207
  var COMPACTION_KEY_PREFIX = "compaction:";
27096
27208
  var COMPACTION_NOTES_PER_SESSION = 3;
27097
27209
  function rowToDto2(row) {
@@ -27177,7 +27289,7 @@ function writeCompactionNote(db, args) {
27177
27289
  ).orderBy(desc17(agentMemory.updatedAt)).all();
27178
27290
  const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
27179
27291
  if (stale.length > 0) {
27180
- tx.delete(agentMemory).where(sql14`${agentMemory.id} IN (${sql14.join(stale.map((s) => sql14`${s}`), sql14`, `)})`).run();
27292
+ tx.delete(agentMemory).where(sql15`${agentMemory.id} IN (${sql15.join(stale.map((s) => sql15`${s}`), sql15`, `)})`).run();
27181
27293
  }
27182
27294
  const row = tx.select().from(agentMemory).where(and27(eq38(agentMemory.projectId, args.projectId), eq38(agentMemory.key, key))).get();
27183
27295
  if (row) inserted = rowToDto2(row);