@ainyc/canonry 4.101.0 → 4.102.1

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/README.md +4 -1
  2. package/assets/assets/{BacklinksPage-aysk_iGy.js → BacklinksPage-CoPo93LS.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-CmKqxndA.js → ChartPrimitives-DqBMtYpk.js} +1 -1
  4. package/assets/assets/{ProjectPage-dduZrrBr.js → ProjectPage-DSJElQg_.js} +1 -1
  5. package/assets/assets/{RunRow-Ba77hJ4d.js → RunRow-2HNKAfK9.js} +1 -1
  6. package/assets/assets/{RunsPage-D3IAdQMB.js → RunsPage-Dw7ZIp-K.js} +1 -1
  7. package/assets/assets/{SettingsPage-Y54JtLw-.js → SettingsPage-CYmxSK3g.js} +1 -1
  8. package/assets/assets/{TrafficPage-DawzPWdt.js → TrafficPage-BI8BNANb.js} +1 -1
  9. package/assets/assets/{TrafficSourceDetailPage-DAhQwFVb.js → TrafficSourceDetailPage-rdvZpta7.js} +1 -1
  10. package/assets/assets/{arrow-left-NsJbgqmV.js → arrow-left-C37crrqh.js} +1 -1
  11. package/assets/assets/{extract-error-message-u00ggxeK.js → extract-error-message-DZnbGnSd.js} +1 -1
  12. package/assets/assets/{index-BCuWbxUj.css → index-LsyRFLMG.css} +1 -1
  13. package/assets/assets/{index-4YArcfwj.js → index-sh-T-1TA.js} +57 -57
  14. package/assets/assets/{trash-2-BBdbbe60.js → trash-2-UvZu9cfJ.js} +1 -1
  15. package/assets/index.html +2 -2
  16. package/dist/{chunk-2YPVK75M.js → chunk-2FUODJQR.js} +1 -1
  17. package/dist/{chunk-O3SFVGDF.js → chunk-3PVCILSN.js} +38 -6
  18. package/dist/{chunk-AV3DHH6B.js → chunk-B2H2RRIT.js} +786 -611
  19. package/dist/{chunk-HUL5FUBD.js → chunk-EOQXB63S.js} +24 -2
  20. package/dist/cli.js +22 -7
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-R6QJ3KC2.js → intelligence-service-RC4BY5HF.js} +2 -2
  23. package/dist/mcp.js +2 -2
  24. package/package.json +8 -8
@@ -263,10 +263,10 @@ import {
263
263
  wordpressSchemaDeployResultDtoSchema,
264
264
  wordpressSchemaStatusResultDtoSchema,
265
265
  wordpressStatusDtoSchema
266
- } from "./chunk-HUL5FUBD.js";
266
+ } from "./chunk-EOQXB63S.js";
267
267
 
268
268
  // src/intelligence-service.ts
269
- 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";
269
+ import { eq as eq38, desc as desc18, asc as asc6, and as and28, ne as ne5, or as or5, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
270
270
 
271
271
  // ../db/src/client.ts
272
272
  import { mkdirSync } from "fs";
@@ -318,6 +318,7 @@ __export(schema_exports, {
318
318
  gbpPlaceDetails: () => gbpPlaceDetails,
319
319
  googleConnections: () => googleConnections,
320
320
  gscCoverageSnapshots: () => gscCoverageSnapshots,
321
+ gscDailyTotals: () => gscDailyTotals,
321
322
  gscSearchData: () => gscSearchData,
322
323
  gscUrlInspections: () => gscUrlInspections,
323
324
  healthSnapshots: () => healthSnapshots,
@@ -468,11 +469,20 @@ var apiKeys = sqliteTable("api_keys", {
468
469
  keyHash: text("key_hash").notNull().unique(),
469
470
  keyPrefix: text("key_prefix").notNull(),
470
471
  scopes: text("scopes", { mode: "json" }).$type().notNull().default(["*"]),
472
+ /**
473
+ * When set, the key is scoped to a SINGLE project: every project-scoped read
474
+ * or write is gated to this project id (enforced centrally in `auth.ts`).
475
+ * NULL keeps full-instance access — the historical default for every key
476
+ * `canonry init` / `canonry key create` writes. Cascade-deletes with the
477
+ * project so a stale scoped key never outlives its project.
478
+ */
479
+ projectId: text("project_id").references(() => projects.id, { onDelete: "cascade" }),
471
480
  createdAt: text("created_at").notNull(),
472
481
  lastUsedAt: text("last_used_at"),
473
482
  revokedAt: text("revoked_at")
474
483
  }, (table) => [
475
- index("idx_api_keys_prefix").on(table.keyPrefix)
484
+ index("idx_api_keys_prefix").on(table.keyPrefix),
485
+ index("idx_api_keys_project").on(table.projectId)
476
486
  ]);
477
487
  var schedules = sqliteTable("schedules", {
478
488
  id: text("id").primaryKey(),
@@ -548,6 +558,20 @@ var gscSearchData = sqliteTable("gsc_search_data", {
548
558
  index("idx_gsc_search_query").on(table.query),
549
559
  index("idx_gsc_search_run").on(table.syncRunId)
550
560
  ]);
561
+ var gscDailyTotals = sqliteTable("gsc_daily_totals", {
562
+ id: text("id").primaryKey(),
563
+ projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
564
+ date: text("date").notNull(),
565
+ clicks: integer("clicks").notNull(),
566
+ impressions: integer("impressions").notNull(),
567
+ // Stored as a string like `gscSearchData.position` (parsed to a number on
568
+ // read). CTR is derived (clicks / impressions) and intentionally not stored.
569
+ position: text("position").notNull(),
570
+ createdAt: text("created_at").notNull()
571
+ }, (table) => [
572
+ uniqueIndex("idx_gsc_daily_totals_project_date").on(table.projectId, table.date),
573
+ index("idx_gsc_daily_totals_project").on(table.projectId)
574
+ ]);
551
575
  var gscUrlInspections = sqliteTable("gsc_url_inspections", {
552
576
  id: text("id").primaryKey(),
553
577
  projectId: text("project_id").notNull().references(() => projects.id, { onDelete: "cascade" }),
@@ -3317,6 +3341,34 @@ var MIGRATION_VERSIONS = [
3317
3341
  `CREATE INDEX IF NOT EXISTS idx_agent_tool_events_tool_created ON agent_tool_events(tool_name, created_at)`,
3318
3342
  `CREATE INDEX IF NOT EXISTS idx_agent_tool_events_status_created ON agent_tool_events(status, created_at)`
3319
3343
  ]
3344
+ },
3345
+ {
3346
+ version: 86,
3347
+ name: "gsc-daily-totals",
3348
+ statements: [
3349
+ `CREATE TABLE IF NOT EXISTS gsc_daily_totals (
3350
+ id TEXT PRIMARY KEY,
3351
+ project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
3352
+ date TEXT NOT NULL,
3353
+ clicks INTEGER NOT NULL,
3354
+ impressions INTEGER NOT NULL,
3355
+ position TEXT NOT NULL,
3356
+ created_at TEXT NOT NULL
3357
+ )`,
3358
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_gsc_daily_totals_project_date ON gsc_daily_totals(project_id, date)`,
3359
+ `CREATE INDEX IF NOT EXISTS idx_gsc_daily_totals_project ON gsc_daily_totals(project_id)`
3360
+ ]
3361
+ },
3362
+ {
3363
+ version: 87,
3364
+ name: "api-key-project-scope",
3365
+ statements: [
3366
+ // Opt-in single-project scoping for API keys. NULL = full-instance access
3367
+ // (the historical default), so this is a no-op for every existing key.
3368
+ // ALTER ADD COLUMN is idempotent (the runner swallows duplicate-column on retry).
3369
+ `ALTER TABLE api_keys ADD COLUMN project_id TEXT REFERENCES projects(id) ON DELETE CASCADE`,
3370
+ `CREATE INDEX IF NOT EXISTS idx_api_keys_project ON api_keys(project_id)`
3371
+ ]
3320
3372
  }
3321
3373
  ];
3322
3374
  function rebuildBacklinkTableWithSource(tx, table) {
@@ -5651,6 +5703,12 @@ function requireScope(request, scope) {
5651
5703
  if (key.scopes.includes("*") || key.scopes.includes(scope)) return;
5652
5704
  throw forbidden(`This action requires the "${scope}" scope on your API key.`);
5653
5705
  }
5706
+ function assertProjectScope(request, projectId) {
5707
+ const scoped = request.apiKey?.projectId;
5708
+ if (scoped && scoped !== projectId) {
5709
+ throw forbidden("This API key is scoped to a different project.");
5710
+ }
5711
+ }
5654
5712
  function hashApiKey(key) {
5655
5713
  return crypto2.createHash("sha256").update(key).digest("hex");
5656
5714
  }
@@ -5711,10 +5769,20 @@ async function authPlugin(app, opts = {}) {
5711
5769
  }
5712
5770
  app.db.update(apiKeys).set({ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(apiKeys.id, key.id)).run();
5713
5771
  const scopes = Array.isArray(key.scopes) ? key.scopes : [];
5714
- request.apiKey = { id: key.id, name: key.name, scopes };
5772
+ request.apiKey = { id: key.id, name: key.name, scopes, projectId: key.projectId ?? null };
5715
5773
  if (isReadOnlyKey(scopes) && WRITE_METHODS.has(request.method)) {
5716
5774
  throw forbidden("This API key is read-only and cannot perform write operations.");
5717
5775
  }
5776
+ if (key.projectId) {
5777
+ const match = url.match(/\/projects\/([^/?#]+)/);
5778
+ if (match) {
5779
+ const projectName = decodeURIComponent(match[1]);
5780
+ const scoped = app.db.select({ id: projects.id }).from(projects).where(eq(projects.name, projectName)).get();
5781
+ if (!scoped || scoped.id !== key.projectId) {
5782
+ throw forbidden("This API key is scoped to a different project.");
5783
+ }
5784
+ }
5785
+ }
5718
5786
  });
5719
5787
  }
5720
5788
 
@@ -5906,8 +5974,9 @@ async function projectRoutes(app, opts) {
5906
5974
  const created = app.db.select().from(projects).where(eq3(projects.id, id)).get();
5907
5975
  return reply.status(201).send(formatProject(created));
5908
5976
  });
5909
- app.get("/projects", async (_request, reply) => {
5910
- const rows = app.db.select().from(projects).all();
5977
+ app.get("/projects", async (request, reply) => {
5978
+ const scoped = request.apiKey?.projectId;
5979
+ const rows = scoped ? app.db.select().from(projects).where(eq3(projects.id, scoped)).all() : app.db.select().from(projects).all();
5911
5980
  return reply.send(rows.map(formatProject));
5912
5981
  });
5913
5982
  app.get("/projects/:name", async (request, reply) => {
@@ -6744,11 +6813,14 @@ async function runRoutes(app, opts) {
6744
6813
  const filters = [gte(runs.createdAt, since)];
6745
6814
  if (!includeProbe) filters.push(notProbeRun());
6746
6815
  if (kind) filters.push(eq7(runs.kind, kind));
6816
+ const scopedProjectId = request.apiKey?.projectId;
6817
+ if (scopedProjectId) filters.push(eq7(runs.projectId, scopedProjectId));
6747
6818
  const rows = app.db.select().from(runs).where(and2(...filters)).orderBy(desc(runs.createdAt), desc(runs.id)).limit(limit).all();
6748
6819
  return reply.send(rows.map(formatRun));
6749
6820
  });
6750
6821
  app.post("/runs", async (request, reply) => {
6751
- const allProjects = app.db.select().from(projects).all();
6822
+ const scopedProjectId = request.apiKey?.projectId;
6823
+ const allProjects = (scopedProjectId ? app.db.select().from(projects).where(eq7(projects.id, scopedProjectId)) : app.db.select().from(projects)).all();
6752
6824
  if (allProjects.length === 0) {
6753
6825
  return reply.status(207).send([]);
6754
6826
  }
@@ -6814,6 +6886,7 @@ async function runRoutes(app, opts) {
6814
6886
  app.post("/runs/:id/cancel", async (request, reply) => {
6815
6887
  const run = app.db.select().from(runs).where(eq7(runs.id, request.params.id)).get();
6816
6888
  if (!run) throw notFound("Run", request.params.id);
6889
+ assertProjectScope(request, run.projectId);
6817
6890
  const terminalStatuses = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
6818
6891
  if (terminalStatuses.has(run.status)) throw runNotCancellable(run.id, run.status);
6819
6892
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -6831,6 +6904,7 @@ async function runRoutes(app, opts) {
6831
6904
  app.get("/runs/:id", async (request, reply) => {
6832
6905
  const run = app.db.select().from(runs).where(eq7(runs.id, request.params.id)).get();
6833
6906
  if (!run) throw notFound("Run", request.params.id);
6907
+ assertProjectScope(request, run.projectId);
6834
6908
  return reply.send(loadRunDetail(app, run));
6835
6909
  });
6836
6910
  }
@@ -7286,6 +7360,13 @@ async function applyRoutes(app, opts) {
7286
7360
  const now = (/* @__PURE__ */ new Date()).toISOString();
7287
7361
  const name = config.metadata.name;
7288
7362
  const configQueries = resolveConfigSpecQueries(config.spec);
7363
+ const scopedProjectId = request.apiKey?.projectId;
7364
+ if (scopedProjectId) {
7365
+ const target = app.db.select({ id: projects.id }).from(projects).where(eq8(projects.name, name)).get();
7366
+ if (!target || target.id !== scopedProjectId) {
7367
+ throw forbidden("This API key is scoped to a single project and cannot apply this config.");
7368
+ }
7369
+ }
7289
7370
  let projectId;
7290
7371
  let scheduleAction = null;
7291
7372
  let aliasesChanged = false;
@@ -7549,8 +7630,9 @@ async function historyRoutes(app) {
7549
7630
  const rows = app.db.select().from(auditLog).where(eq9(auditLog.projectId, project.id)).orderBy(desc2(auditLog.createdAt)).all();
7550
7631
  return reply.send(rows.map(formatAuditEntry));
7551
7632
  });
7552
- app.get("/history", async (_request, reply) => {
7553
- const rows = app.db.select().from(auditLog).orderBy(desc2(auditLog.createdAt)).all();
7633
+ app.get("/history", async (request, reply) => {
7634
+ const scopedProjectId = request.apiKey?.projectId;
7635
+ const rows = app.db.select().from(auditLog).where(scopedProjectId ? eq9(auditLog.projectId, scopedProjectId) : void 0).orderBy(desc2(auditLog.createdAt)).all();
7554
7636
  return reply.send(rows.map(formatAuditEntry));
7555
7637
  });
7556
7638
  app.get("/projects/:name/snapshots", async (request, reply) => {
@@ -7731,6 +7813,15 @@ async function historyRoutes(app) {
7731
7813
  if (!run1 || !run2) {
7732
7814
  throw validationError("Both run1 and run2 query params are required");
7733
7815
  }
7816
+ const requestedRunIds = [.../* @__PURE__ */ new Set([run1, run2])];
7817
+ const runRows = app.db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(inArray2(runs.id, requestedRunIds)).all();
7818
+ const runsById = new Map(runRows.map((row) => [row.id, row]));
7819
+ for (const runId of requestedRunIds) {
7820
+ const run = runsById.get(runId);
7821
+ if (!run || run.projectId !== project.id) {
7822
+ throw notFound("Run", runId);
7823
+ }
7824
+ }
7734
7825
  const snaps1 = app.db.select({
7735
7826
  queryId: querySnapshots.queryId,
7736
7827
  query: queries.query,
@@ -8469,7 +8560,7 @@ async function intelligenceRoutes(app) {
8469
8560
  }
8470
8561
 
8471
8562
  // ../api-routes/src/report.ts
8472
- import { and as and9, desc as desc7, eq as eq14, gte as gte2, inArray as inArray6, lt, lte, ne as ne2, or as or3, sql as sql6 } from "drizzle-orm";
8563
+ import { and as and10, desc as desc7, eq as eq15, gte as gte2, inArray as inArray6, lt, lte, ne as ne2, or as or3, sql as sql7 } from "drizzle-orm";
8473
8564
 
8474
8565
  // ../api-routes/src/content.ts
8475
8566
  import crypto11 from "crypto";
@@ -9204,6 +9295,38 @@ function winnabilityClassRank(winnabilityClass) {
9204
9295
  return winnabilityClass === WinnabilityClasses.ownable ? 0 : 1;
9205
9296
  }
9206
9297
 
9298
+ // ../api-routes/src/gsc-totals.ts
9299
+ import { and as and9, asc as asc2, eq as eq14, sql as sql6 } from "drizzle-orm";
9300
+ function readGscDailyTotals(db, projectId, startDate, endDate) {
9301
+ const rows = db.select({
9302
+ date: gscDailyTotals.date,
9303
+ clicks: gscDailyTotals.clicks,
9304
+ impressions: gscDailyTotals.impressions,
9305
+ position: gscDailyTotals.position
9306
+ }).from(gscDailyTotals).where(
9307
+ and9(
9308
+ eq14(gscDailyTotals.projectId, projectId),
9309
+ sql6`${gscDailyTotals.date} >= ${startDate}`,
9310
+ sql6`${gscDailyTotals.date} <= ${endDate}`
9311
+ )
9312
+ ).orderBy(asc2(gscDailyTotals.date)).all();
9313
+ return rows.map((r) => {
9314
+ const position = Number(r.position);
9315
+ return {
9316
+ date: r.date,
9317
+ clicks: r.clicks,
9318
+ impressions: r.impressions,
9319
+ position: Number.isFinite(position) ? position : 0
9320
+ };
9321
+ });
9322
+ }
9323
+ function mergeGscDailyTotalsWithFallback(propertyTotals, dimensionedFallback) {
9324
+ const byDate = /* @__PURE__ */ new Map();
9325
+ for (const row of dimensionedFallback) byDate.set(row.date, row);
9326
+ for (const row of propertyTotals) byDate.set(row.date, row);
9327
+ return [...byDate.values()].sort((a, b) => a.date.localeCompare(b.date));
9328
+ }
9329
+
9207
9330
  // ../api-routes/src/report-renderer.ts
9208
9331
  var COLORS = {
9209
9332
  bg: "#09090b",
@@ -11515,38 +11638,56 @@ function loadSnapshotsForRunIds(db, runIds) {
11515
11638
  }));
11516
11639
  }
11517
11640
  function loadQueryLookup(db, projectId) {
11518
- const rows = db.select().from(queries).where(eq14(queries.projectId, projectId)).all();
11641
+ const rows = db.select().from(queries).where(eq15(queries.projectId, projectId)).all();
11519
11642
  const byId = /* @__PURE__ */ new Map();
11520
11643
  for (const row of rows) byId.set(row.id, row.query);
11521
11644
  return { byId };
11522
11645
  }
11523
11646
  function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trackedQueries, windowDays) {
11524
- const allRows = db.select().from(gscSearchData).where(eq14(gscSearchData.projectId, projectId)).all();
11525
- if (allRows.length === 0) return null;
11647
+ const allRows = db.select().from(gscSearchData).where(eq15(gscSearchData.projectId, projectId)).all();
11648
+ const allDailyTotals = readGscDailyTotals(db, projectId, "", "9999-12-31");
11526
11649
  let maxDate = "";
11527
11650
  for (const r of allRows) if (r.date > maxDate) maxDate = r.date;
11651
+ for (const r of allDailyTotals) if (r.date > maxDate) maxDate = r.date;
11652
+ if (!maxDate) return null;
11528
11653
  const startDate = windowStartDate(maxDate, windowDays);
11529
11654
  const rows = allRows.filter((r) => r.date >= startDate && r.date <= maxDate);
11530
- if (rows.length === 0) return null;
11531
- let totalClicks = 0;
11532
- let totalImpressions = 0;
11533
- let weightedPositionSum = 0;
11655
+ const dailyTotals = allDailyTotals.filter((r) => r.date >= startDate && r.date <= maxDate);
11656
+ if (rows.length === 0 && dailyTotals.length === 0) return null;
11657
+ let dimensionedClicks = 0;
11534
11658
  const queryAgg = /* @__PURE__ */ new Map();
11535
- const trendAgg = /* @__PURE__ */ new Map();
11659
+ const dimensionedTrendAgg = /* @__PURE__ */ new Map();
11536
11660
  for (const r of rows) {
11537
- totalClicks += r.clicks;
11538
- totalImpressions += r.impressions;
11539
- weightedPositionSum += safeNum(r.position) * r.impressions;
11661
+ dimensionedClicks += r.clicks;
11540
11662
  const q = queryAgg.get(r.query) ?? { clicks: 0, impressions: 0, weightedPositionSum: 0 };
11541
11663
  q.clicks += r.clicks;
11542
11664
  q.impressions += r.impressions;
11543
11665
  q.weightedPositionSum += safeNum(r.position) * r.impressions;
11544
11666
  queryAgg.set(r.query, q);
11545
- const t = trendAgg.get(r.date) ?? { clicks: 0, impressions: 0 };
11667
+ const t = dimensionedTrendAgg.get(r.date) ?? { clicks: 0, impressions: 0, weightedPositionSum: 0 };
11546
11668
  t.clicks += r.clicks;
11547
11669
  t.impressions += r.impressions;
11548
- trendAgg.set(r.date, t);
11670
+ t.weightedPositionSum += safeNum(r.position) * r.impressions;
11671
+ dimensionedTrendAgg.set(r.date, t);
11672
+ }
11673
+ const dailySeries = mergeGscDailyTotalsWithFallback(
11674
+ dailyTotals,
11675
+ [...dimensionedTrendAgg.entries()].map(([date, agg]) => ({
11676
+ date,
11677
+ clicks: agg.clicks,
11678
+ impressions: agg.impressions,
11679
+ position: agg.impressions > 0 ? agg.weightedPositionSum / agg.impressions : 0
11680
+ }))
11681
+ );
11682
+ let totalClicks = 0;
11683
+ let totalImpressions = 0;
11684
+ let weightedPositionSum = 0;
11685
+ for (const d of dailySeries) {
11686
+ totalClicks += d.clicks;
11687
+ totalImpressions += d.impressions;
11688
+ weightedPositionSum += d.position * d.impressions;
11549
11689
  }
11690
+ const trend = dailySeries.map((d) => ({ date: d.date, clicks: d.clicks, impressions: d.impressions }));
11550
11691
  const ctr = totalImpressions > 0 ? totalClicks / totalImpressions : 0;
11551
11692
  const avgPosition = totalImpressions > 0 ? weightedPositionSum / totalImpressions : 0;
11552
11693
  const topQueries = [...queryAgg.entries()].map(([query, agg]) => ({
@@ -11569,9 +11710,8 @@ function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trac
11569
11710
  category,
11570
11711
  clicks: agg.clicks,
11571
11712
  impressions: agg.impressions,
11572
- sharePct: totalClicks > 0 ? Math.round(agg.clicks / totalClicks * 100) : 0
11713
+ sharePct: dimensionedClicks > 0 ? Math.round(agg.clicks / dimensionedClicks * 100) : 0
11573
11714
  })).sort((a, b) => b.clicks - a.clicks);
11574
- const trend = [...trendAgg.entries()].map(([date, agg]) => ({ date, clicks: agg.clicks, impressions: agg.impressions })).sort((a, b) => a.date.localeCompare(b.date));
11575
11715
  const periodStart = trend[0]?.date ?? "";
11576
11716
  const periodEnd = trend.at(-1)?.date ?? "";
11577
11717
  const trackedSet = new Set(trackedQueries.map((q) => q.toLowerCase()));
@@ -11595,13 +11735,13 @@ function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trac
11595
11735
  function buildGaSection(db, projectId, windowDays) {
11596
11736
  const gaWindowKey = GA_WINDOW_SUMMARY_KEYS[windowDays];
11597
11737
  const windowSummary = gaWindowKey ? db.select().from(gaTrafficWindowSummaries).where(
11598
- and9(
11599
- eq14(gaTrafficWindowSummaries.projectId, projectId),
11600
- eq14(gaTrafficWindowSummaries.windowKey, gaWindowKey)
11738
+ and10(
11739
+ eq15(gaTrafficWindowSummaries.projectId, projectId),
11740
+ eq15(gaTrafficWindowSummaries.windowKey, gaWindowKey)
11601
11741
  )
11602
11742
  ).limit(1).get() : void 0;
11603
- const fallbackSummary = windowSummary ? null : db.select().from(gaTrafficSummaries).where(eq14(gaTrafficSummaries.projectId, projectId)).orderBy(desc7(gaTrafficSummaries.syncedAt)).limit(1).get();
11604
- const allSnapshotRows = db.select().from(gaTrafficSnapshots).where(eq14(gaTrafficSnapshots.projectId, projectId)).all();
11743
+ const fallbackSummary = windowSummary ? null : db.select().from(gaTrafficSummaries).where(eq15(gaTrafficSummaries.projectId, projectId)).orderBy(desc7(gaTrafficSummaries.syncedAt)).limit(1).get();
11744
+ const allSnapshotRows = db.select().from(gaTrafficSnapshots).where(eq15(gaTrafficSnapshots.projectId, projectId)).all();
11605
11745
  if (!windowSummary && !fallbackSummary && allSnapshotRows.length === 0) return null;
11606
11746
  let snapshotMaxDate = "";
11607
11747
  for (const r of allSnapshotRows) if (r.date > snapshotMaxDate) snapshotMaxDate = r.date;
@@ -11660,7 +11800,7 @@ function buildGaSection(db, projectId, windowDays) {
11660
11800
  };
11661
11801
  }
11662
11802
  function buildSocialReferrals(db, projectId) {
11663
- const rows = db.select().from(gaSocialReferrals).where(eq14(gaSocialReferrals.projectId, projectId)).all();
11803
+ const rows = db.select().from(gaSocialReferrals).where(eq15(gaSocialReferrals.projectId, projectId)).all();
11664
11804
  if (rows.length === 0) return null;
11665
11805
  let total = 0;
11666
11806
  let organic = 0;
@@ -11692,7 +11832,7 @@ function buildSocialReferrals(db, projectId) {
11692
11832
  };
11693
11833
  }
11694
11834
  function buildAiReferrals(db, projectId) {
11695
- const rows = db.select().from(gaAiReferrals).where(eq14(gaAiReferrals.projectId, projectId)).all();
11835
+ const rows = db.select().from(gaAiReferrals).where(eq15(gaAiReferrals.projectId, projectId)).all();
11696
11836
  if (rows.length === 0) return null;
11697
11837
  const dimSessionsByTuple = /* @__PURE__ */ new Map();
11698
11838
  for (const r of rows) {
@@ -11749,7 +11889,7 @@ function buildAiReferrals(db, projectId) {
11749
11889
  return { totalSessions: total, totalUsers, bySource, trend, topLandingPages };
11750
11890
  }
11751
11891
  function nonSubresourceReferralPathCondition() {
11752
- return sql6`
11892
+ return sql7`
11753
11893
  LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/_next/static/%'
11754
11894
  AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/assets/%'
11755
11895
  AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/static/%'
@@ -11773,8 +11913,8 @@ function nonSubresourceReferralPathCondition() {
11773
11913
  }
11774
11914
  function buildServerActivity(db, projectId, windowDays) {
11775
11915
  const sourceRows = db.select({ id: trafficSources.id }).from(trafficSources).where(
11776
- and9(
11777
- eq14(trafficSources.projectId, projectId),
11916
+ and10(
11917
+ eq15(trafficSources.projectId, projectId),
11778
11918
  ne2(trafficSources.status, TrafficSourceStatuses.archived)
11779
11919
  )
11780
11920
  ).all();
@@ -11788,19 +11928,19 @@ function buildServerActivity(db, projectId, windowDays) {
11788
11928
  const priorStart = new Date(priorStartMs).toISOString();
11789
11929
  const trendStart = new Date(trendStartMs).toISOString();
11790
11930
  const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
11791
- db.select({ total: sql6`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
11792
- and9(
11793
- eq14(crawlerEventsHourly.projectId, projectId),
11794
- eq14(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
11931
+ db.select({ total: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
11932
+ and10(
11933
+ eq15(crawlerEventsHourly.projectId, projectId),
11934
+ eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
11795
11935
  gte2(crawlerEventsHourly.tsHour, windowStartIso),
11796
11936
  exclusiveEnd ? lt(crawlerEventsHourly.tsHour, windowEndIso) : lte(crawlerEventsHourly.tsHour, windowEndIso)
11797
11937
  )
11798
11938
  ).get()?.total ?? 0
11799
11939
  );
11800
11940
  const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
11801
- db.select({ total: sql6`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
11802
- and9(
11803
- eq14(crawlerEventsHourly.projectId, projectId),
11941
+ db.select({ total: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
11942
+ and10(
11943
+ eq15(crawlerEventsHourly.projectId, projectId),
11804
11944
  ne2(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
11805
11945
  gte2(crawlerEventsHourly.tsHour, windowStartIso),
11806
11946
  exclusiveEnd ? lt(crawlerEventsHourly.tsHour, windowEndIso) : lte(crawlerEventsHourly.tsHour, windowEndIso)
@@ -11808,9 +11948,9 @@ function buildServerActivity(db, projectId, windowDays) {
11808
11948
  ).get()?.total ?? 0
11809
11949
  );
11810
11950
  const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
11811
- db.select({ total: sql6`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
11812
- and9(
11813
- eq14(aiReferralEventsHourly.projectId, projectId),
11951
+ db.select({ total: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
11952
+ and10(
11953
+ eq15(aiReferralEventsHourly.projectId, projectId),
11814
11954
  nonSubresourceReferralPathCondition(),
11815
11955
  gte2(aiReferralEventsHourly.tsHour, windowStartIso),
11816
11956
  exclusiveEnd ? lt(aiReferralEventsHourly.tsHour, windowEndIso) : lte(aiReferralEventsHourly.tsHour, windowEndIso)
@@ -11818,9 +11958,9 @@ function buildServerActivity(db, projectId, windowDays) {
11818
11958
  ).get()?.total ?? 0
11819
11959
  );
11820
11960
  const sumUserFetches = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
11821
- db.select({ total: sql6`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
11822
- and9(
11823
- eq14(aiUserFetchEventsHourly.projectId, projectId),
11961
+ db.select({ total: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
11962
+ and10(
11963
+ eq15(aiUserFetchEventsHourly.projectId, projectId),
11824
11964
  gte2(aiUserFetchEventsHourly.tsHour, windowStartIso),
11825
11965
  exclusiveEnd ? lt(aiUserFetchEventsHourly.tsHour, windowEndIso) : lte(aiUserFetchEventsHourly.tsHour, windowEndIso)
11826
11966
  )
@@ -11837,31 +11977,31 @@ function buildServerActivity(db, projectId, windowDays) {
11837
11977
  const crawlerByOperatorRows = db.select({
11838
11978
  operator: crawlerEventsHourly.operator,
11839
11979
  verificationStatus: crawlerEventsHourly.verificationStatus,
11840
- hits: sql6`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
11980
+ hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
11841
11981
  }).from(crawlerEventsHourly).where(
11842
- and9(
11843
- eq14(crawlerEventsHourly.projectId, projectId),
11982
+ and10(
11983
+ eq15(crawlerEventsHourly.projectId, projectId),
11844
11984
  gte2(crawlerEventsHourly.tsHour, headlineStart),
11845
11985
  lte(crawlerEventsHourly.tsHour, headlineEnd)
11846
11986
  )
11847
11987
  ).groupBy(crawlerEventsHourly.operator, crawlerEventsHourly.verificationStatus).all();
11848
11988
  const crawlerByOperatorPriorRows = db.select({
11849
11989
  operator: crawlerEventsHourly.operator,
11850
- hits: sql6`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
11990
+ hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
11851
11991
  }).from(crawlerEventsHourly).where(
11852
- and9(
11853
- eq14(crawlerEventsHourly.projectId, projectId),
11854
- eq14(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
11992
+ and10(
11993
+ eq15(crawlerEventsHourly.projectId, projectId),
11994
+ eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
11855
11995
  gte2(crawlerEventsHourly.tsHour, priorStart),
11856
11996
  lt(crawlerEventsHourly.tsHour, headlineStart)
11857
11997
  )
11858
11998
  ).groupBy(crawlerEventsHourly.operator).all();
11859
11999
  const referralByOperatorRows = db.select({
11860
12000
  operator: aiReferralEventsHourly.operator,
11861
- hits: sql6`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
12001
+ hits: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
11862
12002
  }).from(aiReferralEventsHourly).where(
11863
- and9(
11864
- eq14(aiReferralEventsHourly.projectId, projectId),
12003
+ and10(
12004
+ eq15(aiReferralEventsHourly.projectId, projectId),
11865
12005
  nonSubresourceReferralPathCondition(),
11866
12006
  gte2(aiReferralEventsHourly.tsHour, headlineStart),
11867
12007
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
@@ -11869,10 +12009,10 @@ function buildServerActivity(db, projectId, windowDays) {
11869
12009
  ).groupBy(aiReferralEventsHourly.operator).all();
11870
12010
  const userFetchByOperatorRows = db.select({
11871
12011
  operator: aiUserFetchEventsHourly.operator,
11872
- hits: sql6`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
12012
+ hits: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
11873
12013
  }).from(aiUserFetchEventsHourly).where(
11874
- and9(
11875
- eq14(aiUserFetchEventsHourly.projectId, projectId),
12014
+ and10(
12015
+ eq15(aiUserFetchEventsHourly.projectId, projectId),
11876
12016
  gte2(aiUserFetchEventsHourly.tsHour, headlineStart),
11877
12017
  lte(aiUserFetchEventsHourly.tsHour, headlineEnd)
11878
12018
  )
@@ -11912,16 +12052,16 @@ function buildServerActivity(db, projectId, windowDays) {
11912
12052
  );
11913
12053
  const topPathsRows = db.select({
11914
12054
  path: crawlerEventsHourly.pathNormalized,
11915
- hits: sql6`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
11916
- operators: sql6`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
12055
+ hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
12056
+ operators: sql7`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
11917
12057
  }).from(crawlerEventsHourly).where(
11918
- and9(
11919
- eq14(crawlerEventsHourly.projectId, projectId),
11920
- eq14(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12058
+ and10(
12059
+ eq15(crawlerEventsHourly.projectId, projectId),
12060
+ eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
11921
12061
  gte2(crawlerEventsHourly.tsHour, headlineStart),
11922
12062
  lte(crawlerEventsHourly.tsHour, headlineEnd)
11923
12063
  )
11924
- ).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc7(sql6`SUM(${crawlerEventsHourly.hits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
12064
+ ).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc7(sql7`SUM(${crawlerEventsHourly.hits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
11925
12065
  const topCrawledPaths = topPathsRows.map((r) => ({
11926
12066
  path: r.path,
11927
12067
  verifiedHits: Number(r.hits),
@@ -11929,16 +12069,16 @@ function buildServerActivity(db, projectId, windowDays) {
11929
12069
  }));
11930
12070
  const referralProductsRows = db.select({
11931
12071
  product: aiReferralEventsHourly.product,
11932
- arrivals: sql6`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
11933
- landingPaths: sql6`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
12072
+ arrivals: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
12073
+ landingPaths: sql7`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
11934
12074
  }).from(aiReferralEventsHourly).where(
11935
- and9(
11936
- eq14(aiReferralEventsHourly.projectId, projectId),
12075
+ and10(
12076
+ eq15(aiReferralEventsHourly.projectId, projectId),
11937
12077
  nonSubresourceReferralPathCondition(),
11938
12078
  gte2(aiReferralEventsHourly.tsHour, headlineStart),
11939
12079
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
11940
12080
  )
11941
- ).groupBy(aiReferralEventsHourly.product).orderBy(desc7(sql6`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).all();
12081
+ ).groupBy(aiReferralEventsHourly.product).orderBy(desc7(sql7`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).all();
11942
12082
  const referralProducts = referralProductsRows.map((r) => ({
11943
12083
  product: r.product,
11944
12084
  arrivals: Number(r.arrivals),
@@ -11946,53 +12086,53 @@ function buildServerActivity(db, projectId, windowDays) {
11946
12086
  }));
11947
12087
  const topReferralRows = db.select({
11948
12088
  path: aiReferralEventsHourly.landingPathNormalized,
11949
- arrivals: sql6`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
11950
- products: sql6`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
12089
+ arrivals: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
12090
+ products: sql7`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
11951
12091
  }).from(aiReferralEventsHourly).where(
11952
- and9(
11953
- eq14(aiReferralEventsHourly.projectId, projectId),
12092
+ and10(
12093
+ eq15(aiReferralEventsHourly.projectId, projectId),
11954
12094
  nonSubresourceReferralPathCondition(),
11955
12095
  gte2(aiReferralEventsHourly.tsHour, headlineStart),
11956
12096
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
11957
12097
  )
11958
- ).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc7(sql6`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
12098
+ ).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc7(sql7`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
11959
12099
  const topReferralLandingPaths = topReferralRows.map((r) => ({
11960
12100
  path: r.path,
11961
12101
  arrivals: Number(r.arrivals),
11962
12102
  distinctProducts: Number(r.products)
11963
12103
  }));
11964
12104
  const crawlerTrendRows = db.select({
11965
- date: sql6`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
11966
- hits: sql6`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
12105
+ date: sql7`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
12106
+ hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
11967
12107
  }).from(crawlerEventsHourly).where(
11968
- and9(
11969
- eq14(crawlerEventsHourly.projectId, projectId),
11970
- eq14(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12108
+ and10(
12109
+ eq15(crawlerEventsHourly.projectId, projectId),
12110
+ eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
11971
12111
  gte2(crawlerEventsHourly.tsHour, trendStart),
11972
12112
  lte(crawlerEventsHourly.tsHour, headlineEnd)
11973
12113
  )
11974
- ).groupBy(sql6`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`).all();
12114
+ ).groupBy(sql7`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`).all();
11975
12115
  const referralTrendRows = db.select({
11976
- date: sql6`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
11977
- hits: sql6`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
12116
+ date: sql7`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
12117
+ hits: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
11978
12118
  }).from(aiReferralEventsHourly).where(
11979
- and9(
11980
- eq14(aiReferralEventsHourly.projectId, projectId),
12119
+ and10(
12120
+ eq15(aiReferralEventsHourly.projectId, projectId),
11981
12121
  nonSubresourceReferralPathCondition(),
11982
12122
  gte2(aiReferralEventsHourly.tsHour, trendStart),
11983
12123
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
11984
12124
  )
11985
- ).groupBy(sql6`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`).all();
12125
+ ).groupBy(sql7`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`).all();
11986
12126
  const userFetchTrendRows = db.select({
11987
- date: sql6`SUBSTR(${aiUserFetchEventsHourly.tsHour}, 1, 10)`,
11988
- hits: sql6`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
12127
+ date: sql7`SUBSTR(${aiUserFetchEventsHourly.tsHour}, 1, 10)`,
12128
+ hits: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
11989
12129
  }).from(aiUserFetchEventsHourly).where(
11990
- and9(
11991
- eq14(aiUserFetchEventsHourly.projectId, projectId),
12130
+ and10(
12131
+ eq15(aiUserFetchEventsHourly.projectId, projectId),
11992
12132
  gte2(aiUserFetchEventsHourly.tsHour, trendStart),
11993
12133
  lte(aiUserFetchEventsHourly.tsHour, headlineEnd)
11994
12134
  )
11995
- ).groupBy(sql6`SUBSTR(${aiUserFetchEventsHourly.tsHour}, 1, 10)`).all();
12135
+ ).groupBy(sql7`SUBSTR(${aiUserFetchEventsHourly.tsHour}, 1, 10)`).all();
11996
12136
  const emptyTrendEntry = () => ({ verifiedCrawlerHits: 0, userFetchHits: 0, referralArrivals: 0 });
11997
12137
  const dailyTrendMap = /* @__PURE__ */ new Map();
11998
12138
  for (const r of crawlerTrendRows) {
@@ -12043,7 +12183,7 @@ function buildServerActivity(db, projectId, windowDays) {
12043
12183
  };
12044
12184
  }
12045
12185
  function buildIndexingHealth(db, projectId) {
12046
- const gsc = db.select().from(gscCoverageSnapshots).where(eq14(gscCoverageSnapshots.projectId, projectId)).orderBy(desc7(gscCoverageSnapshots.date)).limit(1).get();
12186
+ const gsc = db.select().from(gscCoverageSnapshots).where(eq15(gscCoverageSnapshots.projectId, projectId)).orderBy(desc7(gscCoverageSnapshots.date)).limit(1).get();
12047
12187
  if (gsc) {
12048
12188
  const total = gsc.indexed + gsc.notIndexed;
12049
12189
  return {
@@ -12056,7 +12196,7 @@ function buildIndexingHealth(db, projectId) {
12056
12196
  indexedPct: total > 0 ? Math.round(gsc.indexed / total * 100) : 0
12057
12197
  };
12058
12198
  }
12059
- const bing = db.select().from(bingCoverageSnapshots).where(eq14(bingCoverageSnapshots.projectId, projectId)).orderBy(desc7(bingCoverageSnapshots.date)).limit(1).get();
12199
+ const bing = db.select().from(bingCoverageSnapshots).where(eq15(bingCoverageSnapshots.projectId, projectId)).orderBy(desc7(bingCoverageSnapshots.date)).limit(1).get();
12060
12200
  if (bing) {
12061
12201
  const total = bing.indexed + bing.notIndexed + bing.unknown;
12062
12202
  return {
@@ -12072,7 +12212,7 @@ function buildIndexingHealth(db, projectId) {
12072
12212
  return null;
12073
12213
  }
12074
12214
  function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
12075
- const visibilityRuns = db.select().from(runs).where(and9(eq14(runs.projectId, projectId), eq14(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter);
12215
+ const visibilityRuns = db.select().from(runs).where(and10(eq15(runs.projectId, projectId), eq15(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter);
12076
12216
  const totalQueries = queryLookup.byId.size;
12077
12217
  const points = [];
12078
12218
  for (const run of visibilityRuns) {
@@ -12120,15 +12260,15 @@ function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
12120
12260
  }
12121
12261
  function buildInsightList(db, projectId, locationFilter) {
12122
12262
  const recentRunIds = db.select({ id: runs.id, location: runs.location }).from(runs).where(
12123
- and9(
12124
- eq14(runs.projectId, projectId),
12125
- eq14(runs.kind, RunKinds["answer-visibility"]),
12126
- or3(eq14(runs.status, RunStatuses.completed), eq14(runs.status, RunStatuses.partial)),
12263
+ and10(
12264
+ eq15(runs.projectId, projectId),
12265
+ eq15(runs.kind, RunKinds["answer-visibility"]),
12266
+ or3(eq15(runs.status, RunStatuses.completed), eq15(runs.status, RunStatuses.partial)),
12127
12267
  notProbeRun()
12128
12268
  )
12129
12269
  ).orderBy(desc7(runs.createdAt)).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter).slice(0, INSIGHT_LOOKBACK_RUNS).map((r) => r.id);
12130
12270
  if (recentRunIds.length === 0) return [];
12131
- const rows = db.select().from(insights).where(and9(eq14(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc7(insights.createdAt)).all();
12271
+ const rows = db.select().from(insights).where(and10(eq15(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc7(insights.createdAt)).all();
12132
12272
  const severityRank = { critical: 0, high: 1, medium: 2, low: 3 };
12133
12273
  const flat = rows.filter((r) => !r.dismissed).map((r) => {
12134
12274
  const recommendation = r.recommendation;
@@ -12680,7 +12820,7 @@ function buildProjectReport(db, projectName, periodDays) {
12680
12820
  const project = resolveProject(db, projectName);
12681
12821
  const queryLookup = loadQueryLookup(db, project.id);
12682
12822
  const comparisonWindowDays = reportComparisonWindowDays(periodDays);
12683
- const allRuns = db.select().from(runs).where(and9(eq14(runs.projectId, project.id), notProbeRun())).orderBy(desc7(runs.createdAt), desc7(runs.id)).all();
12823
+ const allRuns = db.select().from(runs).where(and10(eq15(runs.projectId, project.id), notProbeRun())).orderBy(desc7(runs.createdAt), desc7(runs.id)).all();
12684
12824
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
12685
12825
  const completedVisRunGroups = groupRunsByCreatedAt(
12686
12826
  visibilityRuns.filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial)
@@ -12689,7 +12829,7 @@ function buildProjectReport(db, projectName, periodDays) {
12689
12829
  const representativeLatestRun = pickGroupRepresentative(latestVisRunGroup) ?? visibilityRuns[0] ?? null;
12690
12830
  const latestSnapshots = loadSnapshotsForRunIds(db, latestVisRunGroup.map((r) => r.id));
12691
12831
  const latestRunLocation = representativeLatestRun?.location ?? null;
12692
- const competitorRows = db.select().from(competitors).where(eq14(competitors.projectId, project.id)).all();
12832
+ const competitorRows = db.select().from(competitors).where(eq15(competitors.projectId, project.id)).all();
12693
12833
  const competitorDomains = competitorRows.map((c) => c.domain);
12694
12834
  const ownedDomains = project.ownedDomains;
12695
12835
  const projectDomains = [project.canonicalDomain, ...ownedDomains];
@@ -12916,16 +13056,16 @@ async function reportRoutes(app) {
12916
13056
  }
12917
13057
 
12918
13058
  // ../api-routes/src/citations.ts
12919
- import { and as and10, eq as eq15, inArray as inArray7 } from "drizzle-orm";
13059
+ import { and as and11, eq as eq16, inArray as inArray7 } from "drizzle-orm";
12920
13060
  async function citationRoutes(app) {
12921
13061
  app.get("/projects/:name/citations/visibility", async (request, reply) => {
12922
13062
  const project = resolveProject(app.db, request.params.name);
12923
13063
  const configuredProviders = project.providers;
12924
- const projectQueries = app.db.select().from(queries).where(eq15(queries.projectId, project.id)).all();
13064
+ const projectQueries = app.db.select().from(queries).where(eq16(queries.projectId, project.id)).all();
12925
13065
  if (projectQueries.length === 0) {
12926
13066
  return reply.send(emptyCitationVisibility("no-queries"));
12927
13067
  }
12928
- const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and10(eq15(runs.projectId, project.id), notProbeRun())).all();
13068
+ const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and11(eq16(runs.projectId, project.id), notProbeRun())).all();
12929
13069
  if (projectRuns.length === 0) {
12930
13070
  return reply.send(emptyCitationVisibility("no-runs-yet"));
12931
13071
  }
@@ -12949,7 +13089,7 @@ async function citationRoutes(app) {
12949
13089
  queryId: s.queryId,
12950
13090
  runCreatedAt: runCreatedAt.get(s.runId) ?? s.createdAt
12951
13091
  }));
12952
- const projectCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq15(competitors.projectId, project.id)).all().map((c) => normalizeDomain2(c.domain)).filter((d) => d.length > 0);
13092
+ const projectCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq16(competitors.projectId, project.id)).all().map((c) => normalizeDomain2(c.domain)).filter((d) => d.length > 0);
12953
13093
  const response = computeCitationVisibility({
12954
13094
  queries: projectQueries.map((q) => ({ id: q.id, query: q.query })),
12955
13095
  snapshots,
@@ -13081,7 +13221,7 @@ function normalizeDomain2(domain) {
13081
13221
  }
13082
13222
 
13083
13223
  // ../api-routes/src/visibility-stats.ts
13084
- import { and as and11, desc as desc8, eq as eq16, inArray as inArray8 } from "drizzle-orm";
13224
+ import { and as and12, desc as desc8, eq as eq17, inArray as inArray8 } from "drizzle-orm";
13085
13225
  function round42(value) {
13086
13226
  return Math.round(value * 1e4) / 1e4;
13087
13227
  }
@@ -13201,8 +13341,8 @@ async function visibilityStatsRoutes(app) {
13201
13341
  if (!Number.isInteger(n) || n <= 0) throw validationError('"lastRuns" must be a positive integer');
13202
13342
  lastRuns = n;
13203
13343
  }
13204
- const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq16(queries.projectId, project.id)).all();
13205
- 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);
13344
+ const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq17(queries.projectId, project.id)).all();
13345
+ let projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and12(eq17(runs.projectId, project.id), eq17(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc8(runs.createdAt)).all().filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial);
13206
13346
  if (sinceMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) >= sinceMs);
13207
13347
  if (untilMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) <= untilMs);
13208
13348
  if (lastRuns !== null) projectRuns = projectRuns.slice(0, lastRuns);
@@ -13238,7 +13378,7 @@ async function visibilityStatsRoutes(app) {
13238
13378
  }
13239
13379
 
13240
13380
  // ../api-routes/src/composites.ts
13241
- import { eq as eq17, and as and12, desc as desc9, sql as sql7, like as like2, or as or4, inArray as inArray9 } from "drizzle-orm";
13381
+ import { eq as eq18, and as and13, desc as desc9, sql as sql8, like as like2, or as or4, inArray as inArray9 } from "drizzle-orm";
13242
13382
  var TOP_INSIGHT_LIMIT = 5;
13243
13383
  var SEARCH_HIT_HARD_LIMIT = 50;
13244
13384
  var SEARCH_SNIPPET_RADIUS = 80;
@@ -13256,7 +13396,7 @@ async function compositeRoutes(app) {
13256
13396
  const project = resolveProject(app.db, request.params.name);
13257
13397
  const filterLocation = (request.query.location ?? "").trim() || null;
13258
13398
  const sinceIso = parseSinceFilter(request.query.since);
13259
- const allRunsRaw = app.db.select().from(runs).where(and12(eq17(runs.projectId, project.id), notProbeRun())).orderBy(desc9(runs.createdAt), desc9(runs.id)).all();
13399
+ const allRunsRaw = app.db.select().from(runs).where(and13(eq18(runs.projectId, project.id), notProbeRun())).orderBy(desc9(runs.createdAt), desc9(runs.id)).all();
13260
13400
  const allRuns = allRunsRaw.filter((r) => runMatchesFilters(r, filterLocation, sinceIso));
13261
13401
  const totalRuns = allRuns.length;
13262
13402
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
@@ -13269,15 +13409,15 @@ async function compositeRoutes(app) {
13269
13409
  const previousVisibilityRun = pickGroupRepresentative(previousVisRunGroup);
13270
13410
  const latestRunRow = allRuns[0] ?? null;
13271
13411
  const latestRun = latestRunRow ? { totalRuns, run: summarizeRun(latestRunRow) } : { totalRuns: 0, run: null };
13272
- const healthRow = app.db.select().from(healthSnapshots).where(eq17(healthSnapshots.projectId, project.id)).orderBy(desc9(healthSnapshots.createdAt)).limit(1).get();
13412
+ const healthRow = app.db.select().from(healthSnapshots).where(eq18(healthSnapshots.projectId, project.id)).orderBy(desc9(healthSnapshots.createdAt)).limit(1).get();
13273
13413
  const health = healthRow ? mapHealthRow2(healthRow) : null;
13274
- const insightRows = app.db.select().from(insights).where(eq17(insights.projectId, project.id)).orderBy(desc9(insights.createdAt)).all();
13414
+ const insightRows = app.db.select().from(insights).where(eq18(insights.projectId, project.id)).orderBy(desc9(insights.createdAt)).all();
13275
13415
  const topInsights = insightRows.filter((row) => !row.dismissed).slice(0, TOP_INSIGHT_LIMIT).map(mapInsightRow2);
13276
13416
  const sparklineRunIds = visibilityRuns.slice(0, DEFAULT_RUN_HISTORY_LIMIT).map((r) => r.id);
13277
13417
  const snapshotRunIds = new Set(sparklineRunIds);
13278
13418
  for (const run of latestVisRunGroup) snapshotRunIds.add(run.id);
13279
13419
  for (const run of previousVisRunGroup) snapshotRunIds.add(run.id);
13280
- const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq17(queries.projectId, project.id)).all();
13420
+ const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq18(queries.projectId, project.id)).all();
13281
13421
  const queryIdByText = new Map(projectQueries.map((q) => [normalizeQueryText(q.query), q.id]));
13282
13422
  const snapshotsByRun = loadSnapshotsByRunIds(app, [...snapshotRunIds], queryIdByText);
13283
13423
  const latestSnapshots = latestVisRunGroup.flatMap((r) => snapshotsByRun.get(r.id) ?? []);
@@ -13293,7 +13433,7 @@ async function compositeRoutes(app) {
13293
13433
  trackedPrevious,
13294
13434
  previousVisibilityRun?.createdAt ?? null
13295
13435
  );
13296
- const competitorRows = app.db.select().from(competitors).where(eq17(competitors.projectId, project.id)).all();
13436
+ const competitorRows = app.db.select().from(competitors).where(eq18(competitors.projectId, project.id)).all();
13297
13437
  const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
13298
13438
  for (const snapshots of snapshotsByRun.values()) {
13299
13439
  for (const snapshot of snapshots) {
@@ -13411,25 +13551,25 @@ async function compositeRoutes(app) {
13411
13551
  citedDomains: querySnapshots.citedDomains,
13412
13552
  rawResponse: querySnapshots.rawResponse,
13413
13553
  createdAt: querySnapshots.createdAt
13414
- }).from(querySnapshots).innerJoin(queries, eq17(querySnapshots.queryId, queries.id)).where(
13415
- and12(
13416
- eq17(queries.projectId, project.id),
13554
+ }).from(querySnapshots).innerJoin(queries, eq18(querySnapshots.queryId, queries.id)).where(
13555
+ and13(
13556
+ eq18(queries.projectId, project.id),
13417
13557
  or4(
13418
- sql7`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
13419
- sql7`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
13420
- sql7`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
13558
+ sql8`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
13559
+ sql8`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
13560
+ sql8`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
13421
13561
  like2(queries.query, pattern)
13422
13562
  )
13423
13563
  )
13424
13564
  ).orderBy(desc9(querySnapshots.createdAt)).limit(limit + 1).all());
13425
13565
  const insightMatches = app.db.select().from(insights).where(
13426
- and12(
13427
- eq17(insights.projectId, project.id),
13566
+ and13(
13567
+ eq18(insights.projectId, project.id),
13428
13568
  or4(
13429
13569
  like2(insights.title, pattern),
13430
13570
  like2(insights.query, pattern),
13431
- sql7`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
13432
- sql7`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
13571
+ sql8`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
13572
+ sql8`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
13433
13573
  )
13434
13574
  )
13435
13575
  ).orderBy(desc9(insights.createdAt)).limit(limit + 1).all();
@@ -13634,17 +13774,17 @@ function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
13634
13774
  const cutoff = new Date(Date.now() - 28 * 24 * 60 * 60 * 1e3).toISOString().slice(0, 10);
13635
13775
  const rows = app.db.select({
13636
13776
  query: gscSearchData.query,
13637
- impressions: sql7`COALESCE(SUM(${gscSearchData.impressions}), 0)`,
13638
- clicks: sql7`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
13777
+ impressions: sql8`COALESCE(SUM(${gscSearchData.impressions}), 0)`,
13778
+ clicks: sql8`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
13639
13779
  // Weighted average: SUM(position * impressions) / SUM(impressions).
13640
13780
  // NULLIF guards the degenerate impressions=0 case (SQLite returns NULL,
13641
13781
  // which the JS coerces to 0 — caught by the impression floor anyway).
13642
- avgPosition: sql7`COALESCE(SUM(${gscSearchData.position} * ${gscSearchData.impressions}) * 1.0 / NULLIF(SUM(${gscSearchData.impressions}), 0), 0)`
13643
- }).from(gscSearchData).where(and12(
13644
- eq17(gscSearchData.projectId, projectId),
13645
- sql7`${gscSearchData.date} >= ${cutoff}`,
13646
- sql7`${gscSearchData.impressions} > 0`
13647
- )).groupBy(gscSearchData.query).orderBy(sql7`SUM(${gscSearchData.impressions}) DESC`).limit(100).all();
13782
+ avgPosition: sql8`COALESCE(SUM(${gscSearchData.position} * ${gscSearchData.impressions}) * 1.0 / NULLIF(SUM(${gscSearchData.impressions}), 0), 0)`
13783
+ }).from(gscSearchData).where(and13(
13784
+ eq18(gscSearchData.projectId, projectId),
13785
+ sql8`${gscSearchData.date} >= ${cutoff}`,
13786
+ sql8`${gscSearchData.impressions} > 0`
13787
+ )).groupBy(gscSearchData.query).orderBy(sql8`SUM(${gscSearchData.impressions}) DESC`).limit(100).all();
13648
13788
  const gscRows = rows.map((r) => ({
13649
13789
  query: r.query,
13650
13790
  impressions: Number(r.impressions),
@@ -13664,8 +13804,8 @@ function buildIndexCoverageScore(app, projectId) {
13664
13804
  tooltip,
13665
13805
  trend: []
13666
13806
  };
13667
- const gscRow = app.db.select().from(gscCoverageSnapshots).where(eq17(gscCoverageSnapshots.projectId, projectId)).orderBy(desc9(gscCoverageSnapshots.date)).limit(1).get();
13668
- const bingRow = app.db.select().from(bingCoverageSnapshots).where(eq17(bingCoverageSnapshots.projectId, projectId)).orderBy(desc9(bingCoverageSnapshots.date)).limit(1).get();
13807
+ const gscRow = app.db.select().from(gscCoverageSnapshots).where(eq18(gscCoverageSnapshots.projectId, projectId)).orderBy(desc9(gscCoverageSnapshots.date)).limit(1).get();
13808
+ const bingRow = app.db.select().from(bingCoverageSnapshots).where(eq18(bingCoverageSnapshots.projectId, projectId)).orderBy(desc9(bingCoverageSnapshots.date)).limit(1).get();
13669
13809
  const chosen = pickIndexCoverageRow(gscRow, bingRow);
13670
13810
  if (!chosen) return empty;
13671
13811
  const total = chosen.indexed + chosen.notIndexed;
@@ -13691,7 +13831,7 @@ function countGoogleDeindexedUrls(app, projectId) {
13691
13831
  url: gscUrlInspections.url,
13692
13832
  indexingState: gscUrlInspections.indexingState,
13693
13833
  inspectedAt: gscUrlInspections.inspectedAt
13694
- }).from(gscUrlInspections).where(eq17(gscUrlInspections.projectId, projectId)).orderBy(desc9(gscUrlInspections.inspectedAt)).all();
13834
+ }).from(gscUrlInspections).where(eq18(gscUrlInspections.projectId, projectId)).orderBy(desc9(gscUrlInspections.inspectedAt)).all();
13695
13835
  if (rows.length === 0) return 0;
13696
13836
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
13697
13837
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -18338,7 +18478,7 @@ async function settingsRoutes(app, opts) {
18338
18478
 
18339
18479
  // ../api-routes/src/keys.ts
18340
18480
  import crypto12 from "crypto";
18341
- import { desc as desc10, eq as eq18 } from "drizzle-orm";
18481
+ import { desc as desc10, eq as eq19 } from "drizzle-orm";
18342
18482
  var KEYS_WRITE_SCOPE = "keys.write";
18343
18483
  function toApiKeyDto(row) {
18344
18484
  const scopes = Array.isArray(row.scopes) ? row.scopes : [];
@@ -18347,6 +18487,7 @@ function toApiKeyDto(row) {
18347
18487
  name: row.name,
18348
18488
  keyPrefix: row.keyPrefix,
18349
18489
  scopes,
18490
+ projectId: row.projectId ?? null,
18350
18491
  readOnly: isReadOnlyKey(scopes),
18351
18492
  createdAt: row.createdAt,
18352
18493
  lastUsedAt: row.lastUsedAt ?? null,
@@ -18354,8 +18495,9 @@ function toApiKeyDto(row) {
18354
18495
  };
18355
18496
  }
18356
18497
  async function keysRoutes(app) {
18357
- app.get("/keys", async () => {
18358
- const rows = app.db.select().from(apiKeys).orderBy(desc10(apiKeys.createdAt)).all();
18498
+ app.get("/keys", async (request) => {
18499
+ const scopedProjectId = request.apiKey?.projectId;
18500
+ const rows = scopedProjectId ? app.db.select().from(apiKeys).where(eq19(apiKeys.projectId, scopedProjectId)).orderBy(desc10(apiKeys.createdAt)).all() : app.db.select().from(apiKeys).orderBy(desc10(apiKeys.createdAt)).all();
18359
18501
  return { keys: rows.map(toApiKeyDto) };
18360
18502
  });
18361
18503
  app.get("/keys/self", async (request) => {
@@ -18363,7 +18505,7 @@ async function keysRoutes(app) {
18363
18505
  if (!id) {
18364
18506
  throw notFound("API key", "self");
18365
18507
  }
18366
- const row = app.db.select().from(apiKeys).where(eq18(apiKeys.id, id)).get();
18508
+ const row = app.db.select().from(apiKeys).where(eq19(apiKeys.id, id)).get();
18367
18509
  if (!row) {
18368
18510
  throw notFound("API key", id);
18369
18511
  }
@@ -18375,7 +18517,17 @@ async function keysRoutes(app) {
18375
18517
  if (!parsed.success) {
18376
18518
  throw validationError("Invalid API key request", { issues: parsed.error.issues });
18377
18519
  }
18378
- const { name, scopes } = parsed.data;
18520
+ const { name, scopes, projectId } = parsed.data;
18521
+ const requesterProjectId = request.apiKey?.projectId;
18522
+ if (requesterProjectId && projectId !== requesterProjectId) {
18523
+ throw forbidden("A project-scoped key can only mint keys scoped to its own project.");
18524
+ }
18525
+ if (projectId) {
18526
+ const proj = app.db.select({ id: projects.id }).from(projects).where(eq19(projects.id, projectId)).get();
18527
+ if (!proj) {
18528
+ throw notFound("Project", projectId);
18529
+ }
18530
+ }
18379
18531
  const raw = `cnry_${crypto12.randomBytes(16).toString("hex")}`;
18380
18532
  const keyHash = hashApiKey(raw);
18381
18533
  const keyPrefix = raw.slice(0, 9);
@@ -18389,6 +18541,7 @@ async function keysRoutes(app) {
18389
18541
  keyHash,
18390
18542
  keyPrefix,
18391
18543
  scopes: effectiveScopes,
18544
+ projectId: projectId ?? null,
18392
18545
  createdAt: now
18393
18546
  }).run();
18394
18547
  writeAuditLog(tx, auditFromRequest(request, {
@@ -18396,7 +18549,7 @@ async function keysRoutes(app) {
18396
18549
  action: "api-key.created",
18397
18550
  entityType: "api-key",
18398
18551
  entityId: id,
18399
- diff: { name, keyPrefix, scopes: effectiveScopes }
18552
+ diff: { name, keyPrefix, scopes: effectiveScopes, projectId: projectId ?? null }
18400
18553
  }));
18401
18554
  });
18402
18555
  const dto = {
@@ -18404,6 +18557,7 @@ async function keysRoutes(app) {
18404
18557
  name,
18405
18558
  keyPrefix,
18406
18559
  scopes: effectiveScopes,
18560
+ projectId: projectId ?? null,
18407
18561
  readOnly: isReadOnlyKey(effectiveScopes),
18408
18562
  createdAt: now,
18409
18563
  lastUsedAt: null,
@@ -18415,19 +18569,23 @@ async function keysRoutes(app) {
18415
18569
  app.post("/keys/:id/revoke", async (request) => {
18416
18570
  requireScope(request, KEYS_WRITE_SCOPE);
18417
18571
  const { id } = request.params;
18418
- const row = app.db.select().from(apiKeys).where(eq18(apiKeys.id, id)).get();
18572
+ const row = app.db.select().from(apiKeys).where(eq19(apiKeys.id, id)).get();
18419
18573
  if (!row) {
18420
18574
  throw notFound("API key", id);
18421
18575
  }
18422
18576
  if (request.apiKey?.id === id) {
18423
18577
  throw validationError("Cannot revoke the API key you are currently authenticating with");
18424
18578
  }
18579
+ const requesterProjectId = request.apiKey?.projectId;
18580
+ if (requesterProjectId && row.projectId !== requesterProjectId) {
18581
+ throw forbidden("A project-scoped key can only revoke keys scoped to its own project.");
18582
+ }
18425
18583
  if (row.revokedAt) {
18426
18584
  return toApiKeyDto(row);
18427
18585
  }
18428
18586
  const now = (/* @__PURE__ */ new Date()).toISOString();
18429
18587
  app.db.transaction((tx) => {
18430
- tx.update(apiKeys).set({ revokedAt: now }).where(eq18(apiKeys.id, id)).run();
18588
+ tx.update(apiKeys).set({ revokedAt: now }).where(eq19(apiKeys.id, id)).run();
18431
18589
  writeAuditLog(tx, auditFromRequest(request, {
18432
18590
  actor: "api",
18433
18591
  action: "api-key.revoked",
@@ -18500,7 +18658,7 @@ async function telemetryRoutes(app, opts) {
18500
18658
 
18501
18659
  // ../api-routes/src/schedules.ts
18502
18660
  import crypto13 from "crypto";
18503
- import { and as and13, eq as eq19 } from "drizzle-orm";
18661
+ import { and as and14, eq as eq20 } from "drizzle-orm";
18504
18662
  function parseKindParam(raw) {
18505
18663
  if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
18506
18664
  const parsed = schedulableRunKindSchema.safeParse(raw);
@@ -18527,7 +18685,7 @@ async function scheduleRoutes(app, opts) {
18527
18685
  if (!sourceId) {
18528
18686
  throw validationError('"sourceId" is required when kind is "traffic-sync"');
18529
18687
  }
18530
- const sourceRow = app.db.select().from(trafficSources).where(eq19(trafficSources.id, sourceId)).get();
18688
+ const sourceRow = app.db.select().from(trafficSources).where(eq20(trafficSources.id, sourceId)).get();
18531
18689
  if (!sourceRow || sourceRow.projectId !== project.id) {
18532
18690
  throw notFound("Traffic source", sourceId);
18533
18691
  }
@@ -18572,7 +18730,7 @@ async function scheduleRoutes(app, opts) {
18572
18730
  }
18573
18731
  const now = (/* @__PURE__ */ new Date()).toISOString();
18574
18732
  const enabledBool = enabled !== false;
18575
- const existing = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
18733
+ const existing = app.db.select().from(schedules).where(and14(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
18576
18734
  if (existing) {
18577
18735
  app.db.update(schedules).set({
18578
18736
  cronExpr,
@@ -18582,7 +18740,7 @@ async function scheduleRoutes(app, opts) {
18582
18740
  sourceId: sourceId ?? null,
18583
18741
  enabled: enabledBool,
18584
18742
  updatedAt: now
18585
- }).where(eq19(schedules.id, existing.id)).run();
18743
+ }).where(eq20(schedules.id, existing.id)).run();
18586
18744
  } else {
18587
18745
  app.db.insert(schedules).values({
18588
18746
  id: crypto13.randomUUID(),
@@ -18606,13 +18764,13 @@ async function scheduleRoutes(app, opts) {
18606
18764
  diff: { kind, cronExpr, preset, timezone, providers, sourceId }
18607
18765
  });
18608
18766
  opts.onScheduleUpdated?.("upsert", project.id, kind);
18609
- const schedule = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
18767
+ const schedule = app.db.select().from(schedules).where(and14(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
18610
18768
  return reply.status(existing ? 200 : 201).send(formatSchedule(schedule));
18611
18769
  });
18612
18770
  app.get("/projects/:name/schedule", async (request, reply) => {
18613
18771
  const project = resolveProject(app.db, request.params.name);
18614
18772
  const kind = parseKindParam(request.query?.kind);
18615
- const schedule = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
18773
+ const schedule = app.db.select().from(schedules).where(and14(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
18616
18774
  if (!schedule) {
18617
18775
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
18618
18776
  }
@@ -18621,11 +18779,11 @@ async function scheduleRoutes(app, opts) {
18621
18779
  app.delete("/projects/:name/schedule", async (request, reply) => {
18622
18780
  const project = resolveProject(app.db, request.params.name);
18623
18781
  const kind = parseKindParam(request.query?.kind);
18624
- const schedule = app.db.select().from(schedules).where(and13(eq19(schedules.projectId, project.id), eq19(schedules.kind, kind))).get();
18782
+ const schedule = app.db.select().from(schedules).where(and14(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
18625
18783
  if (!schedule) {
18626
18784
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
18627
18785
  }
18628
- app.db.delete(schedules).where(eq19(schedules.id, schedule.id)).run();
18786
+ app.db.delete(schedules).where(eq20(schedules.id, schedule.id)).run();
18629
18787
  writeAuditLog(app.db, {
18630
18788
  projectId: project.id,
18631
18789
  actor: "api",
@@ -18658,7 +18816,7 @@ function formatSchedule(row) {
18658
18816
 
18659
18817
  // ../api-routes/src/notifications.ts
18660
18818
  import crypto14 from "crypto";
18661
- import { eq as eq20 } from "drizzle-orm";
18819
+ import { eq as eq21 } from "drizzle-orm";
18662
18820
  var VALID_EVENTS = ["citation.lost", "citation.gained", "run.completed", "run.failed", "insight.critical", "insight.high"];
18663
18821
  async function notificationRoutes(app, opts = {}) {
18664
18822
  const allowLoopback = opts.allowLoopbackWebhooks === true;
@@ -18698,22 +18856,22 @@ async function notificationRoutes(app, opts = {}) {
18698
18856
  diff: { channel, ...redactNotificationUrl(url), events }
18699
18857
  });
18700
18858
  return reply.status(201).send({
18701
- ...formatNotification(app.db.select().from(notifications).where(eq20(notifications.id, id)).get()),
18859
+ ...formatNotification(app.db.select().from(notifications).where(eq21(notifications.id, id)).get()),
18702
18860
  webhookSecret
18703
18861
  });
18704
18862
  });
18705
18863
  app.get("/projects/:name/notifications", async (request, reply) => {
18706
18864
  const project = resolveProject(app.db, request.params.name);
18707
- const rows = app.db.select().from(notifications).where(eq20(notifications.projectId, project.id)).all();
18865
+ const rows = app.db.select().from(notifications).where(eq21(notifications.projectId, project.id)).all();
18708
18866
  return reply.send(rows.map(formatNotification));
18709
18867
  });
18710
18868
  app.delete("/projects/:name/notifications/:id", async (request, reply) => {
18711
18869
  const project = resolveProject(app.db, request.params.name);
18712
- const notification = app.db.select().from(notifications).where(eq20(notifications.id, request.params.id)).get();
18870
+ const notification = app.db.select().from(notifications).where(eq21(notifications.id, request.params.id)).get();
18713
18871
  if (!notification || notification.projectId !== project.id) {
18714
18872
  throw notFound("Notification", request.params.id);
18715
18873
  }
18716
- app.db.delete(notifications).where(eq20(notifications.id, notification.id)).run();
18874
+ app.db.delete(notifications).where(eq21(notifications.id, notification.id)).run();
18717
18875
  writeAuditLog(app.db, {
18718
18876
  projectId: project.id,
18719
18877
  actor: "api",
@@ -18725,7 +18883,7 @@ async function notificationRoutes(app, opts = {}) {
18725
18883
  });
18726
18884
  app.post("/projects/:name/notifications/:id/test", async (request, reply) => {
18727
18885
  const project = resolveProject(app.db, request.params.name);
18728
- const notification = app.db.select().from(notifications).where(eq20(notifications.id, request.params.id)).get();
18886
+ const notification = app.db.select().from(notifications).where(eq21(notifications.id, request.params.id)).get();
18729
18887
  if (!notification || notification.projectId !== project.id) {
18730
18888
  throw notFound("Notification", request.params.id);
18731
18889
  }
@@ -18778,7 +18936,7 @@ function formatNotification(row) {
18778
18936
 
18779
18937
  // ../api-routes/src/google.ts
18780
18938
  import crypto18 from "crypto";
18781
- import { eq as eq21, and as and14, desc as desc11, sql as sql8, inArray as inArray10 } from "drizzle-orm";
18939
+ import { eq as eq22, and as and15, desc as desc11, sql as sql9, inArray as inArray10 } from "drizzle-orm";
18782
18940
 
18783
18941
  // ../api-routes/src/gbp-summary.ts
18784
18942
  function computeMetricTotals(rows) {
@@ -20611,7 +20769,7 @@ async function googleRoutes(app, opts) {
20611
20769
  if (!projectId) {
20612
20770
  return reply.status(400).send("Stale OAuth state \u2014 restart the connect flow.");
20613
20771
  }
20614
- const project = app.db.select().from(projects).where(eq21(projects.id, projectId)).get();
20772
+ const project = app.db.select().from(projects).where(eq22(projects.id, projectId)).get();
20615
20773
  if (!project) {
20616
20774
  return reply.status(400).send("Project no longer exists. Restart the connect flow.");
20617
20775
  }
@@ -20743,22 +20901,22 @@ async function googleRoutes(app, opts) {
20743
20901
  if (opts.onGscSyncRequested) {
20744
20902
  opts.onGscSyncRequested(runId, project.id, { days, full });
20745
20903
  }
20746
- const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
20904
+ const run = app.db.select().from(runs).where(eq22(runs.id, runId)).get();
20747
20905
  return run;
20748
20906
  });
20749
20907
  app.get("/projects/:name/google/gsc/performance", async (request) => {
20750
20908
  const project = resolveProject(app.db, request.params.name);
20751
20909
  const { startDate, endDate, query, page, limit, offset } = request.query;
20752
20910
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
20753
- const conditions = [eq21(gscSearchData.projectId, project.id)];
20754
- if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
20755
- else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
20756
- if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
20757
- if (query) conditions.push(sql8`${gscSearchData.query} LIKE ${"%" + escapeLikePattern(query) + "%"} ESCAPE '\\'`);
20758
- if (page) conditions.push(sql8`${gscSearchData.page} LIKE ${"%" + escapeLikePattern(page) + "%"} ESCAPE '\\'`);
20911
+ const conditions = [eq22(gscSearchData.projectId, project.id)];
20912
+ if (startDate) conditions.push(sql9`${gscSearchData.date} >= ${startDate}`);
20913
+ else if (cutoffDate) conditions.push(sql9`${gscSearchData.date} >= ${cutoffDate}`);
20914
+ if (endDate) conditions.push(sql9`${gscSearchData.date} <= ${endDate}`);
20915
+ if (query) conditions.push(sql9`${gscSearchData.query} LIKE ${"%" + escapeLikePattern(query) + "%"} ESCAPE '\\'`);
20916
+ if (page) conditions.push(sql9`${gscSearchData.page} LIKE ${"%" + escapeLikePattern(page) + "%"} ESCAPE '\\'`);
20759
20917
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
20760
20918
  const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
20761
- const rows = app.db.select().from(gscSearchData).where(and14(...conditions)).orderBy(desc11(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
20919
+ const rows = app.db.select().from(gscSearchData).where(and15(...conditions)).orderBy(desc11(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
20762
20920
  return rows.map((r) => ({
20763
20921
  date: r.date,
20764
20922
  query: r.query,
@@ -20775,20 +20933,31 @@ async function googleRoutes(app, opts) {
20775
20933
  const project = resolveProject(app.db, request.params.name);
20776
20934
  const { startDate, endDate } = request.query;
20777
20935
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
20778
- const conditions = [eq21(gscSearchData.projectId, project.id)];
20779
- if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
20780
- else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
20781
- if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
20782
- const rows = app.db.select({
20936
+ const windowStart = startDate ?? cutoffDate ?? "";
20937
+ const windowEnd = endDate ?? "9999-12-31";
20938
+ const dailyTotals = readGscDailyTotals(app.db, project.id, windowStart, windowEnd);
20939
+ const conditions = [eq22(gscSearchData.projectId, project.id)];
20940
+ if (startDate) conditions.push(sql9`${gscSearchData.date} >= ${startDate}`);
20941
+ else if (cutoffDate) conditions.push(sql9`${gscSearchData.date} >= ${cutoffDate}`);
20942
+ if (endDate) conditions.push(sql9`${gscSearchData.date} <= ${endDate}`);
20943
+ const dimensionedRows = app.db.select({
20783
20944
  date: gscSearchData.date,
20784
- clicks: sql8`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
20785
- impressions: sql8`COALESCE(SUM(${gscSearchData.impressions}), 0)`
20786
- }).from(gscSearchData).where(and14(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
20787
- const daily = rows.map((r) => ({
20788
- date: r.date,
20789
- clicks: r.clicks,
20790
- impressions: r.impressions,
20791
- ctr: r.impressions > 0 ? r.clicks / r.impressions : 0
20945
+ clicks: sql9`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
20946
+ impressions: sql9`COALESCE(SUM(${gscSearchData.impressions}), 0)`
20947
+ }).from(gscSearchData).where(and15(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
20948
+ const daily = mergeGscDailyTotalsWithFallback(
20949
+ dailyTotals,
20950
+ dimensionedRows.map((r) => ({
20951
+ date: r.date,
20952
+ clicks: r.clicks,
20953
+ impressions: r.impressions,
20954
+ position: 0
20955
+ }))
20956
+ ).map((d) => ({
20957
+ date: d.date,
20958
+ clicks: d.clicks,
20959
+ impressions: d.impressions,
20960
+ ctr: d.impressions > 0 ? d.clicks / d.impressions : 0
20792
20961
  }));
20793
20962
  const totalClicks = daily.reduce((sum, d) => sum + d.clicks, 0);
20794
20963
  const totalImpressions = daily.reduce((sum, d) => sum + d.impressions, 0);
@@ -20866,9 +21035,9 @@ async function googleRoutes(app, opts) {
20866
21035
  app.get("/projects/:name/google/gsc/inspections", async (request) => {
20867
21036
  const project = resolveProject(app.db, request.params.name);
20868
21037
  const { url, limit } = request.query;
20869
- const conditions = [eq21(gscUrlInspections.projectId, project.id)];
20870
- if (url) conditions.push(eq21(gscUrlInspections.url, url));
20871
- const rows = app.db.select().from(gscUrlInspections).where(and14(...conditions)).orderBy(desc11(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
21038
+ const conditions = [eq22(gscUrlInspections.projectId, project.id)];
21039
+ if (url) conditions.push(eq22(gscUrlInspections.url, url));
21040
+ const rows = app.db.select().from(gscUrlInspections).where(and15(...conditions)).orderBy(desc11(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
20872
21041
  return rows.map((r) => ({
20873
21042
  id: r.id,
20874
21043
  url: r.url,
@@ -20887,7 +21056,7 @@ async function googleRoutes(app, opts) {
20887
21056
  });
20888
21057
  app.get("/projects/:name/google/gsc/deindexed", async (request) => {
20889
21058
  const project = resolveProject(app.db, request.params.name);
20890
- const allInspections = app.db.select().from(gscUrlInspections).where(eq21(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
21059
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq22(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
20891
21060
  const byUrl = /* @__PURE__ */ new Map();
20892
21061
  for (const row of allInspections) {
20893
21062
  const existing = byUrl.get(row.url);
@@ -20915,7 +21084,7 @@ async function googleRoutes(app, opts) {
20915
21084
  });
20916
21085
  app.get("/projects/:name/google/gsc/coverage", async (request) => {
20917
21086
  const project = resolveProject(app.db, request.params.name);
20918
- const allInspections = app.db.select().from(gscUrlInspections).where(eq21(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
21087
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq22(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
20919
21088
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
20920
21089
  const latestByUrl = /* @__PURE__ */ new Map();
20921
21090
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -20964,7 +21133,7 @@ async function googleRoutes(app, opts) {
20964
21133
  const total = latestByUrl.size;
20965
21134
  const indexed = indexedUrls.length;
20966
21135
  const notIndexed = notIndexedUrls.length;
20967
- const latestSnapshot = app.db.select({ createdAt: gscCoverageSnapshots.createdAt }).from(gscCoverageSnapshots).where(eq21(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.createdAt)).limit(1).get();
21136
+ const latestSnapshot = app.db.select({ createdAt: gscCoverageSnapshots.createdAt }).from(gscCoverageSnapshots).where(eq22(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.createdAt)).limit(1).get();
20968
21137
  const lastSyncedAt = latestSnapshot?.createdAt ?? null;
20969
21138
  const formatRow = (r) => ({
20970
21139
  id: r.id,
@@ -21015,7 +21184,7 @@ async function googleRoutes(app, opts) {
21015
21184
  const project = resolveProject(app.db, request.params.name);
21016
21185
  const parsed = parseInt(request.query.limit ?? "90", 10);
21017
21186
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
21018
- const rows = app.db.select().from(gscCoverageSnapshots).where(eq21(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.date)).limit(limit).all();
21187
+ const rows = app.db.select().from(gscCoverageSnapshots).where(eq22(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.date)).limit(limit).all();
21019
21188
  return rows.map((r) => ({
21020
21189
  date: r.date,
21021
21190
  indexed: r.indexed,
@@ -21084,7 +21253,7 @@ async function googleRoutes(app, opts) {
21084
21253
  if (opts.onInspectSitemapRequested) {
21085
21254
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl });
21086
21255
  }
21087
- const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
21256
+ const run = app.db.select().from(runs).where(eq22(runs.id, runId)).get();
21088
21257
  return { sitemaps, primarySitemapUrl: sitemapUrl, run };
21089
21258
  });
21090
21259
  app.post("/projects/:name/google/gsc/inspect-sitemap", async (request) => {
@@ -21111,7 +21280,7 @@ async function googleRoutes(app, opts) {
21111
21280
  if (opts.onInspectSitemapRequested) {
21112
21281
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl: sitemapUrl ?? void 0 });
21113
21282
  }
21114
- const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
21283
+ const run = app.db.select().from(runs).where(eq22(runs.id, runId)).get();
21115
21284
  return run;
21116
21285
  });
21117
21286
  app.put("/projects/:name/google/connections/:type/sitemap", async (request) => {
@@ -21158,7 +21327,7 @@ async function googleRoutes(app, opts) {
21158
21327
  const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
21159
21328
  let urlsToNotify = request.body?.urls ?? [];
21160
21329
  if (request.body?.allUnindexed) {
21161
- const allInspections = app.db.select().from(gscUrlInspections).where(eq21(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
21330
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq22(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
21162
21331
  const latestByUrl = /* @__PURE__ */ new Map();
21163
21332
  for (const row of allInspections) {
21164
21333
  if (!latestByUrl.has(row.url)) {
@@ -21276,7 +21445,7 @@ async function googleRoutes(app, opts) {
21276
21445
  };
21277
21446
  }
21278
21447
  function listSelectionResponse(projectId) {
21279
- const rows = app.db.select().from(gbpLocations).where(eq21(gbpLocations.projectId, projectId)).all();
21448
+ const rows = app.db.select().from(gbpLocations).where(eq22(gbpLocations.projectId, projectId)).all();
21280
21449
  const dtos = rows.map(rowToDto2);
21281
21450
  return {
21282
21451
  locations: dtos,
@@ -21285,15 +21454,15 @@ async function googleRoutes(app, opts) {
21285
21454
  };
21286
21455
  }
21287
21456
  function clearGbpProjectData(tx, projectId) {
21288
- tx.delete(gbpDailyMetrics).where(eq21(gbpDailyMetrics.projectId, projectId)).run();
21289
- tx.delete(gbpKeywordImpressions).where(eq21(gbpKeywordImpressions.projectId, projectId)).run();
21290
- tx.delete(gbpKeywordMonthly).where(eq21(gbpKeywordMonthly.projectId, projectId)).run();
21291
- tx.delete(gbpPlaceActions).where(eq21(gbpPlaceActions.projectId, projectId)).run();
21292
- tx.delete(gbpLodgingSnapshots).where(eq21(gbpLodgingSnapshots.projectId, projectId)).run();
21293
- tx.delete(gbpLocations).where(eq21(gbpLocations.projectId, projectId)).run();
21457
+ tx.delete(gbpDailyMetrics).where(eq22(gbpDailyMetrics.projectId, projectId)).run();
21458
+ tx.delete(gbpKeywordImpressions).where(eq22(gbpKeywordImpressions.projectId, projectId)).run();
21459
+ tx.delete(gbpKeywordMonthly).where(eq22(gbpKeywordMonthly.projectId, projectId)).run();
21460
+ tx.delete(gbpPlaceActions).where(eq22(gbpPlaceActions.projectId, projectId)).run();
21461
+ tx.delete(gbpLodgingSnapshots).where(eq22(gbpLodgingSnapshots.projectId, projectId)).run();
21462
+ tx.delete(gbpLocations).where(eq22(gbpLocations.projectId, projectId)).run();
21294
21463
  }
21295
21464
  function currentProjectAccount(projectId) {
21296
- const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq21(gbpLocations.projectId, projectId)).limit(1).get();
21465
+ const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq22(gbpLocations.projectId, projectId)).limit(1).get();
21297
21466
  return row?.accountName ?? null;
21298
21467
  }
21299
21468
  app.post("/projects/:name/gbp/locations/discover", async (request) => {
@@ -21363,7 +21532,7 @@ async function googleRoutes(app, opts) {
21363
21532
  app.db.transaction((tx) => {
21364
21533
  if (switching) clearGbpProjectData(tx, project.id);
21365
21534
  for (const remote of remoteLocations) {
21366
- const existing = tx.select().from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), eq21(gbpLocations.locationName, remote.name))).get();
21535
+ const existing = tx.select().from(gbpLocations).where(and15(eq22(gbpLocations.projectId, project.id), eq22(gbpLocations.locationName, remote.name))).get();
21367
21536
  const profile = buildLocationProfileFields(remote);
21368
21537
  if (existing) {
21369
21538
  tx.update(gbpLocations).set({
@@ -21376,7 +21545,7 @@ async function googleRoutes(app, opts) {
21376
21545
  mapsUri: remote.metadata?.mapsUri ?? null,
21377
21546
  ...profile,
21378
21547
  updatedAt: now
21379
- }).where(eq21(gbpLocations.id, existing.id)).run();
21548
+ }).where(eq22(gbpLocations.id, existing.id)).run();
21380
21549
  } else {
21381
21550
  tx.insert(gbpLocations).values({
21382
21551
  id: crypto18.randomUUID(),
@@ -21460,11 +21629,11 @@ async function googleRoutes(app, opts) {
21460
21629
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid selection request");
21461
21630
  }
21462
21631
  const { selected } = parsed.data;
21463
- const existing = app.db.select().from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), eq21(gbpLocations.locationName, locationName))).get();
21632
+ const existing = app.db.select().from(gbpLocations).where(and15(eq22(gbpLocations.projectId, project.id), eq22(gbpLocations.locationName, locationName))).get();
21464
21633
  if (!existing) throw notFound("GBP location", locationName);
21465
21634
  const now = (/* @__PURE__ */ new Date()).toISOString();
21466
21635
  app.db.transaction((tx) => {
21467
- tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq21(gbpLocations.id, existing.id)).run();
21636
+ tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq22(gbpLocations.id, existing.id)).run();
21468
21637
  writeAuditLog(tx, {
21469
21638
  projectId: project.id,
21470
21639
  actor: "api",
@@ -21473,7 +21642,7 @@ async function googleRoutes(app, opts) {
21473
21642
  entityId: locationName
21474
21643
  });
21475
21644
  });
21476
- const refreshed = app.db.select().from(gbpLocations).where(eq21(gbpLocations.id, existing.id)).get();
21645
+ const refreshed = app.db.select().from(gbpLocations).where(eq22(gbpLocations.id, existing.id)).get();
21477
21646
  return rowToDto2(refreshed);
21478
21647
  });
21479
21648
  app.delete("/projects/:name/gbp/connection", async (request, reply) => {
@@ -21517,10 +21686,10 @@ async function googleRoutes(app, opts) {
21517
21686
  });
21518
21687
  app.get("/projects/:name/gbp/metrics", async (request) => {
21519
21688
  const project = resolveProject(app.db, request.params.name);
21520
- const conditions = [eq21(gbpDailyMetrics.projectId, project.id)];
21521
- if (request.query.locationName) conditions.push(eq21(gbpDailyMetrics.locationName, request.query.locationName));
21522
- if (request.query.metric) conditions.push(eq21(gbpDailyMetrics.metric, request.query.metric));
21523
- const rows = app.db.select().from(gbpDailyMetrics).where(and14(...conditions)).orderBy(desc11(gbpDailyMetrics.date)).all();
21689
+ const conditions = [eq22(gbpDailyMetrics.projectId, project.id)];
21690
+ if (request.query.locationName) conditions.push(eq22(gbpDailyMetrics.locationName, request.query.locationName));
21691
+ if (request.query.metric) conditions.push(eq22(gbpDailyMetrics.metric, request.query.metric));
21692
+ const rows = app.db.select().from(gbpDailyMetrics).where(and15(...conditions)).orderBy(desc11(gbpDailyMetrics.date)).all();
21524
21693
  return {
21525
21694
  metrics: rows.map((r) => ({ locationName: r.locationName, date: r.date, metric: r.metric, value: r.value })),
21526
21695
  total: rows.length
@@ -21528,9 +21697,9 @@ async function googleRoutes(app, opts) {
21528
21697
  });
21529
21698
  app.get("/projects/:name/gbp/keywords", async (request) => {
21530
21699
  const project = resolveProject(app.db, request.params.name);
21531
- const conditions = [eq21(gbpKeywordImpressions.projectId, project.id)];
21532
- if (request.query.locationName) conditions.push(eq21(gbpKeywordImpressions.locationName, request.query.locationName));
21533
- const rows = app.db.select().from(gbpKeywordImpressions).where(and14(...conditions)).all();
21700
+ const conditions = [eq22(gbpKeywordImpressions.projectId, project.id)];
21701
+ if (request.query.locationName) conditions.push(eq22(gbpKeywordImpressions.locationName, request.query.locationName));
21702
+ const rows = app.db.select().from(gbpKeywordImpressions).where(and15(...conditions)).all();
21534
21703
  rows.sort((a, b) => (b.valueCount ?? -1) - (a.valueCount ?? -1));
21535
21704
  const thresholded = rows.filter((r) => r.valueThreshold !== null).length;
21536
21705
  return {
@@ -21548,9 +21717,9 @@ async function googleRoutes(app, opts) {
21548
21717
  });
21549
21718
  app.get("/projects/:name/gbp/place-actions", async (request) => {
21550
21719
  const project = resolveProject(app.db, request.params.name);
21551
- const conditions = [eq21(gbpPlaceActions.projectId, project.id)];
21552
- if (request.query.locationName) conditions.push(eq21(gbpPlaceActions.locationName, request.query.locationName));
21553
- const rows = app.db.select().from(gbpPlaceActions).where(and14(...conditions)).all();
21720
+ const conditions = [eq22(gbpPlaceActions.projectId, project.id)];
21721
+ if (request.query.locationName) conditions.push(eq22(gbpPlaceActions.locationName, request.query.locationName));
21722
+ const rows = app.db.select().from(gbpPlaceActions).where(and15(...conditions)).all();
21554
21723
  return {
21555
21724
  placeActions: rows.map((r) => ({
21556
21725
  locationName: r.locationName,
@@ -21565,9 +21734,9 @@ async function googleRoutes(app, opts) {
21565
21734
  });
21566
21735
  app.get("/projects/:name/gbp/lodging", async (request) => {
21567
21736
  const project = resolveProject(app.db, request.params.name);
21568
- const conditions = [eq21(gbpLodgingSnapshots.projectId, project.id)];
21569
- if (request.query.locationName) conditions.push(eq21(gbpLodgingSnapshots.locationName, request.query.locationName));
21570
- const rows = app.db.select().from(gbpLodgingSnapshots).where(and14(...conditions)).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
21737
+ const conditions = [eq22(gbpLodgingSnapshots.projectId, project.id)];
21738
+ if (request.query.locationName) conditions.push(eq22(gbpLodgingSnapshots.locationName, request.query.locationName));
21739
+ const rows = app.db.select().from(gbpLodgingSnapshots).where(and15(...conditions)).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
21571
21740
  const latestByLocation = /* @__PURE__ */ new Map();
21572
21741
  for (const row of rows) {
21573
21742
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -21582,9 +21751,9 @@ async function googleRoutes(app, opts) {
21582
21751
  });
21583
21752
  app.get("/projects/:name/gbp/attributes", async (request) => {
21584
21753
  const project = resolveProject(app.db, request.params.name);
21585
- const conditions = [eq21(gbpAttributesSnapshots.projectId, project.id)];
21586
- if (request.query.locationName) conditions.push(eq21(gbpAttributesSnapshots.locationName, request.query.locationName));
21587
- const rows = app.db.select().from(gbpAttributesSnapshots).where(and14(...conditions)).orderBy(desc11(gbpAttributesSnapshots.syncedAt)).all();
21754
+ const conditions = [eq22(gbpAttributesSnapshots.projectId, project.id)];
21755
+ if (request.query.locationName) conditions.push(eq22(gbpAttributesSnapshots.locationName, request.query.locationName));
21756
+ const rows = app.db.select().from(gbpAttributesSnapshots).where(and15(...conditions)).orderBy(desc11(gbpAttributesSnapshots.syncedAt)).all();
21588
21757
  const latestByLocation = /* @__PURE__ */ new Map();
21589
21758
  for (const row of rows) {
21590
21759
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -21602,9 +21771,9 @@ async function googleRoutes(app, opts) {
21602
21771
  });
21603
21772
  app.get("/projects/:name/gbp/places", async (request) => {
21604
21773
  const project = resolveProject(app.db, request.params.name);
21605
- const conditions = [eq21(gbpPlaceDetails.projectId, project.id)];
21606
- if (request.query.locationName) conditions.push(eq21(gbpPlaceDetails.locationName, request.query.locationName));
21607
- const rows = app.db.select().from(gbpPlaceDetails).where(and14(...conditions)).orderBy(desc11(gbpPlaceDetails.syncedAt)).all();
21774
+ const conditions = [eq22(gbpPlaceDetails.projectId, project.id)];
21775
+ if (request.query.locationName) conditions.push(eq22(gbpPlaceDetails.locationName, request.query.locationName));
21776
+ const rows = app.db.select().from(gbpPlaceDetails).where(and15(...conditions)).orderBy(desc11(gbpPlaceDetails.syncedAt)).all();
21608
21777
  const latestByLocation = /* @__PURE__ */ new Map();
21609
21778
  for (const row of rows) {
21610
21779
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -21623,7 +21792,7 @@ async function googleRoutes(app, opts) {
21623
21792
  app.get("/projects/:name/gbp/summary", async (request) => {
21624
21793
  const project = resolveProject(app.db, request.params.name);
21625
21794
  const locationName = request.query.locationName ?? null;
21626
- 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);
21795
+ const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and15(eq22(gbpLocations.projectId, project.id), eq22(gbpLocations.selected, true))).all().map((r) => r.n);
21627
21796
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
21628
21797
  if (locationNames.length === 0) {
21629
21798
  return buildGbpSummary({
@@ -21637,10 +21806,10 @@ async function googleRoutes(app, opts) {
21637
21806
  locationProfiles: []
21638
21807
  });
21639
21808
  }
21640
- const metricRows = app.db.select().from(gbpDailyMetrics).where(and14(eq21(gbpDailyMetrics.projectId, project.id), inArray10(gbpDailyMetrics.locationName, locationNames))).all();
21641
- const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and14(eq21(gbpKeywordImpressions.projectId, project.id), inArray10(gbpKeywordImpressions.locationName, locationNames))).all();
21642
- const placeActionRows = app.db.select().from(gbpPlaceActions).where(and14(eq21(gbpPlaceActions.projectId, project.id), inArray10(gbpPlaceActions.locationName, locationNames))).all();
21643
- const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and14(eq21(gbpLodgingSnapshots.projectId, project.id), inArray10(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
21809
+ const metricRows = app.db.select().from(gbpDailyMetrics).where(and15(eq22(gbpDailyMetrics.projectId, project.id), inArray10(gbpDailyMetrics.locationName, locationNames))).all();
21810
+ const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and15(eq22(gbpKeywordImpressions.projectId, project.id), inArray10(gbpKeywordImpressions.locationName, locationNames))).all();
21811
+ const placeActionRows = app.db.select().from(gbpPlaceActions).where(and15(eq22(gbpPlaceActions.projectId, project.id), inArray10(gbpPlaceActions.locationName, locationNames))).all();
21812
+ const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and15(eq22(gbpLodgingSnapshots.projectId, project.id), inArray10(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
21644
21813
  const latestLodgingByLocation = /* @__PURE__ */ new Map();
21645
21814
  for (const row of lodgingRows) {
21646
21815
  if (!latestLodgingByLocation.has(row.locationName)) {
@@ -21654,7 +21823,7 @@ async function googleRoutes(app, opts) {
21654
21823
  regularHours: gbpLocations.regularHours,
21655
21824
  primaryPhone: gbpLocations.primaryPhone,
21656
21825
  openStatus: gbpLocations.openStatus
21657
- }).from(gbpLocations).where(and14(eq21(gbpLocations.projectId, project.id), inArray10(gbpLocations.locationName, locationNames))).all();
21826
+ }).from(gbpLocations).where(and15(eq22(gbpLocations.projectId, project.id), inArray10(gbpLocations.locationName, locationNames))).all();
21658
21827
  return buildGbpSummary({
21659
21828
  locationName,
21660
21829
  locationCount: locationNames.length,
@@ -21670,7 +21839,7 @@ async function googleRoutes(app, opts) {
21670
21839
 
21671
21840
  // ../api-routes/src/ads.ts
21672
21841
  import crypto19 from "crypto";
21673
- import { eq as eq22, and as and15, asc as asc2, gte as gte3, lte as lte2, inArray as inArray11 } from "drizzle-orm";
21842
+ import { eq as eq23, and as and16, asc as asc3, gte as gte3, lte as lte2, inArray as inArray11 } from "drizzle-orm";
21674
21843
  function statusDto(row) {
21675
21844
  if (!row) return { connected: false };
21676
21845
  return {
@@ -21720,7 +21889,7 @@ async function adsRoutes(app, opts) {
21720
21889
  createdAt: existingCfg?.createdAt ?? now,
21721
21890
  updatedAt: now
21722
21891
  });
21723
- const existingRow = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21892
+ const existingRow = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
21724
21893
  app.db.transaction((tx) => {
21725
21894
  if (existingRow) {
21726
21895
  tx.update(adsConnections).set({
@@ -21730,7 +21899,7 @@ async function adsRoutes(app, opts) {
21730
21899
  timezone: account.timezone,
21731
21900
  status: account.status,
21732
21901
  updatedAt: now
21733
- }).where(eq22(adsConnections.id, existingRow.id)).run();
21902
+ }).where(eq23(adsConnections.id, existingRow.id)).run();
21734
21903
  } else {
21735
21904
  tx.insert(adsConnections).values({
21736
21905
  id: crypto19.randomUUID(),
@@ -21752,16 +21921,16 @@ async function adsRoutes(app, opts) {
21752
21921
  entityId: account.id
21753
21922
  }));
21754
21923
  });
21755
- const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21924
+ const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
21756
21925
  return statusDto(row);
21757
21926
  }
21758
21927
  );
21759
21928
  app.delete("/projects/:name/ads/connection", async (request) => {
21760
21929
  const project = resolveProject(app.db, request.params.name);
21761
- const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21930
+ const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
21762
21931
  if (row) {
21763
21932
  app.db.transaction((tx) => {
21764
- tx.delete(adsConnections).where(eq22(adsConnections.id, row.id)).run();
21933
+ tx.delete(adsConnections).where(eq23(adsConnections.id, row.id)).run();
21765
21934
  writeAuditLog(tx, auditFromRequest(request, {
21766
21935
  projectId: project.id,
21767
21936
  actor: "api",
@@ -21777,18 +21946,18 @@ async function adsRoutes(app, opts) {
21777
21946
  });
21778
21947
  app.get("/projects/:name/ads/status", async (request) => {
21779
21948
  const project = resolveProject(app.db, request.params.name);
21780
- const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21949
+ const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
21781
21950
  return statusDto(row);
21782
21951
  });
21783
21952
  app.post("/projects/:name/ads/sync", async (request) => {
21784
21953
  const project = resolveProject(app.db, request.params.name);
21785
- const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21954
+ const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
21786
21955
  if (!row) {
21787
21956
  throw validationError('No ads connection for this project. Run "canonry ads connect" first.');
21788
21957
  }
21789
- const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and15(
21790
- eq22(runs.projectId, project.id),
21791
- eq22(runs.kind, RunKinds["ads-sync"]),
21958
+ const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and16(
21959
+ eq23(runs.projectId, project.id),
21960
+ eq23(runs.kind, RunKinds["ads-sync"]),
21792
21961
  inArray11(runs.status, [RunStatuses.queued, RunStatuses.running])
21793
21962
  )).get();
21794
21963
  if (inFlight) {
@@ -21810,9 +21979,9 @@ async function adsRoutes(app, opts) {
21810
21979
  });
21811
21980
  app.get("/projects/:name/ads/campaigns", async (request) => {
21812
21981
  const project = resolveProject(app.db, request.params.name);
21813
- const campaignRows = app.db.select().from(adsCampaigns).where(eq22(adsCampaigns.projectId, project.id)).all();
21814
- const groupRows = app.db.select().from(adsAdGroups).where(eq22(adsAdGroups.projectId, project.id)).all();
21815
- const adRows = app.db.select().from(adsAds).where(eq22(adsAds.projectId, project.id)).all();
21982
+ const campaignRows = app.db.select().from(adsCampaigns).where(eq23(adsCampaigns.projectId, project.id)).all();
21983
+ const groupRows = app.db.select().from(adsAdGroups).where(eq23(adsAdGroups.projectId, project.id)).all();
21984
+ const adRows = app.db.select().from(adsAds).where(eq23(adsAds.projectId, project.id)).all();
21816
21985
  const adsByGroup = /* @__PURE__ */ new Map();
21817
21986
  for (const ad of adRows) {
21818
21987
  const dto = {
@@ -21866,12 +22035,12 @@ async function adsRoutes(app, opts) {
21866
22035
  }
21867
22036
  parsedLevel = result.data;
21868
22037
  }
21869
- const conditions = [eq22(adsInsightsDaily.projectId, project.id)];
21870
- if (parsedLevel) conditions.push(eq22(adsInsightsDaily.level, parsedLevel));
21871
- if (entityId) conditions.push(eq22(adsInsightsDaily.entityId, entityId));
22038
+ const conditions = [eq23(adsInsightsDaily.projectId, project.id)];
22039
+ if (parsedLevel) conditions.push(eq23(adsInsightsDaily.level, parsedLevel));
22040
+ if (entityId) conditions.push(eq23(adsInsightsDaily.entityId, entityId));
21872
22041
  if (from) conditions.push(gte3(adsInsightsDaily.date, from));
21873
22042
  if (to) conditions.push(lte2(adsInsightsDaily.date, to));
21874
- const rows = app.db.select().from(adsInsightsDaily).where(and15(...conditions)).orderBy(asc2(adsInsightsDaily.date)).all();
22043
+ const rows = app.db.select().from(adsInsightsDaily).where(and16(...conditions)).orderBy(asc3(adsInsightsDaily.date)).all();
21875
22044
  const dtoRows = rows.map((row) => ({
21876
22045
  level: row.level,
21877
22046
  entityId: row.entityId,
@@ -21883,19 +22052,19 @@ async function adsRoutes(app, opts) {
21883
22052
  ctr: adsCtr(row.clicks, row.impressions),
21884
22053
  cpcMicros: adsCpcMicros(row.spendMicros, row.clicks)
21885
22054
  }));
21886
- const conn = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
22055
+ const conn = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
21887
22056
  const response = { rows: dtoRows, currencyCode: conn?.currencyCode ?? null };
21888
22057
  return response;
21889
22058
  });
21890
22059
  app.get("/projects/:name/ads/summary", async (request) => {
21891
22060
  const project = resolveProject(app.db, request.params.name);
21892
- const row = app.db.select().from(adsConnections).where(eq22(adsConnections.projectId, project.id)).get();
21893
- const campaignCount = app.db.select().from(adsCampaigns).where(eq22(adsCampaigns.projectId, project.id)).all().length;
21894
- const adGroupCount = app.db.select().from(adsAdGroups).where(eq22(adsAdGroups.projectId, project.id)).all().length;
21895
- const adCount = app.db.select().from(adsAds).where(eq22(adsAds.projectId, project.id)).all().length;
21896
- const campaignInsights = app.db.select().from(adsInsightsDaily).where(and15(
21897
- eq22(adsInsightsDaily.projectId, project.id),
21898
- eq22(adsInsightsDaily.level, "campaign")
22061
+ const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
22062
+ const campaignCount = app.db.select().from(adsCampaigns).where(eq23(adsCampaigns.projectId, project.id)).all().length;
22063
+ const adGroupCount = app.db.select().from(adsAdGroups).where(eq23(adsAdGroups.projectId, project.id)).all().length;
22064
+ const adCount = app.db.select().from(adsAds).where(eq23(adsAds.projectId, project.id)).all().length;
22065
+ const campaignInsights = app.db.select().from(adsInsightsDaily).where(and16(
22066
+ eq23(adsInsightsDaily.projectId, project.id),
22067
+ eq23(adsInsightsDaily.level, "campaign")
21899
22068
  )).all();
21900
22069
  let impressions = 0;
21901
22070
  let clicks = 0;
@@ -21935,7 +22104,7 @@ async function adsRoutes(app, opts) {
21935
22104
 
21936
22105
  // ../api-routes/src/bing.ts
21937
22106
  import crypto20 from "crypto";
21938
- import { eq as eq23, and as and16, desc as desc12 } from "drizzle-orm";
22107
+ import { eq as eq24, and as and17, desc as desc12 } from "drizzle-orm";
21939
22108
 
21940
22109
  // ../integration-bing/src/constants.ts
21941
22110
  var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
@@ -22309,7 +22478,7 @@ async function bingRoutes(app, opts) {
22309
22478
  const store = requireConnectionStore();
22310
22479
  const project = resolveProject(app.db, request.params.name);
22311
22480
  requireConnection(store, project.canonicalDomain);
22312
- const allInspections = app.db.select().from(bingUrlInspections).where(eq23(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
22481
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq24(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
22313
22482
  const latestByUrl = /* @__PURE__ */ new Map();
22314
22483
  const definitiveByUrl = /* @__PURE__ */ new Map();
22315
22484
  for (const row of allInspections) {
@@ -22398,7 +22567,7 @@ async function bingRoutes(app, opts) {
22398
22567
  const project = resolveProject(app.db, request.params.name);
22399
22568
  const parsed = parseInt(request.query.limit ?? "90", 10);
22400
22569
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
22401
- const rows = app.db.select().from(bingCoverageSnapshots).where(eq23(bingCoverageSnapshots.projectId, project.id)).orderBy(desc12(bingCoverageSnapshots.date)).limit(limit).all();
22570
+ const rows = app.db.select().from(bingCoverageSnapshots).where(eq24(bingCoverageSnapshots.projectId, project.id)).orderBy(desc12(bingCoverageSnapshots.date)).limit(limit).all();
22402
22571
  return rows.map((r) => ({
22403
22572
  date: r.date,
22404
22573
  indexed: r.indexed,
@@ -22410,7 +22579,7 @@ async function bingRoutes(app, opts) {
22410
22579
  requireConnectionStore();
22411
22580
  const project = resolveProject(app.db, request.params.name);
22412
22581
  const { url, limit } = request.query;
22413
- const whereClause = url ? and16(eq23(bingUrlInspections.projectId, project.id), eq23(bingUrlInspections.url, url)) : eq23(bingUrlInspections.projectId, project.id);
22582
+ const whereClause = url ? and17(eq24(bingUrlInspections.projectId, project.id), eq24(bingUrlInspections.url, url)) : eq24(bingUrlInspections.projectId, project.id);
22414
22583
  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();
22415
22584
  return filtered.map((r) => ({
22416
22585
  id: r.id,
@@ -22500,7 +22669,7 @@ async function bingRoutes(app, opts) {
22500
22669
  anchorCount: result.AnchorCount ?? null,
22501
22670
  discoveryDate
22502
22671
  }).run();
22503
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq23(runs.id, runId)).run();
22672
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq24(runs.id, runId)).run();
22504
22673
  return {
22505
22674
  id,
22506
22675
  url,
@@ -22516,7 +22685,7 @@ async function bingRoutes(app, opts) {
22516
22685
  } catch (e) {
22517
22686
  const msg = e instanceof Error ? e.message : String(e);
22518
22687
  bingLog("error", "inspect-url.failed", { domain: project.canonicalDomain, url, error: msg });
22519
- app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq23(runs.id, runId)).run();
22688
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq24(runs.id, runId)).run();
22520
22689
  throw e;
22521
22690
  }
22522
22691
  });
@@ -22543,7 +22712,7 @@ async function bingRoutes(app, opts) {
22543
22712
  } else {
22544
22713
  bingLog("warn", "inspect-sitemap.no-callback", { domain: project.canonicalDomain, runId });
22545
22714
  }
22546
- const run = app.db.select().from(runs).where(eq23(runs.id, runId)).get();
22715
+ const run = app.db.select().from(runs).where(eq24(runs.id, runId)).get();
22547
22716
  return run;
22548
22717
  });
22549
22718
  app.post("/projects/:name/bing/request-indexing", async (request) => {
@@ -22555,7 +22724,7 @@ async function bingRoutes(app, opts) {
22555
22724
  }
22556
22725
  let urlsToSubmit = request.body?.urls ?? [];
22557
22726
  if (request.body?.allUnindexed) {
22558
- const allInspections = app.db.select().from(bingUrlInspections).where(eq23(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
22727
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq24(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
22559
22728
  const latestByUrl = /* @__PURE__ */ new Map();
22560
22729
  for (const row of allInspections) {
22561
22730
  if (!latestByUrl.has(row.url)) {
@@ -22642,18 +22811,19 @@ async function bingRoutes(app, opts) {
22642
22811
  import fs from "fs";
22643
22812
  import path from "path";
22644
22813
  import os from "os";
22645
- import { eq as eq24, and as and17 } from "drizzle-orm";
22814
+ import { eq as eq25, and as and18 } from "drizzle-orm";
22646
22815
  function getScreenshotDir() {
22647
22816
  return path.join(os.homedir(), ".canonry", "screenshots");
22648
22817
  }
22649
22818
  async function cdpRoutes(app, opts) {
22650
22819
  app.get("/screenshots/:snapshotId", async (request, reply) => {
22651
22820
  const { snapshotId } = request.params;
22652
- const snapshot = app.db.select({ screenshotPath: querySnapshots.screenshotPath }).from(querySnapshots).where(eq24(querySnapshots.id, snapshotId)).get();
22821
+ const snapshot = app.db.select({ screenshotPath: querySnapshots.screenshotPath, projectId: runs.projectId }).from(querySnapshots).innerJoin(runs, eq25(querySnapshots.runId, runs.id)).where(eq25(querySnapshots.id, snapshotId)).get();
22653
22822
  if (!snapshot?.screenshotPath) {
22654
22823
  const err = notFound("Screenshot", snapshotId);
22655
22824
  return reply.code(err.statusCode).send(err.toJSON());
22656
22825
  }
22826
+ assertProjectScope(request, snapshot.projectId);
22657
22827
  const base = path.resolve(getScreenshotDir());
22658
22828
  const fullPath = path.resolve(path.join(base, snapshot.screenshotPath));
22659
22829
  if (!fullPath.startsWith(base + path.sep) && fullPath !== base) {
@@ -22716,7 +22886,7 @@ async function cdpRoutes(app, opts) {
22716
22886
  async (request, reply) => {
22717
22887
  const project = resolveProject(app.db, request.params.name);
22718
22888
  const { runId } = request.params;
22719
- const run = app.db.select().from(runs).where(and17(eq24(runs.id, runId), eq24(runs.projectId, project.id))).get();
22889
+ const run = app.db.select().from(runs).where(and18(eq25(runs.id, runId), eq25(runs.projectId, project.id))).get();
22720
22890
  if (!run) {
22721
22891
  const err = notFound("Run", runId);
22722
22892
  return reply.code(err.statusCode).send(err.toJSON());
@@ -22729,8 +22899,8 @@ async function cdpRoutes(app, opts) {
22729
22899
  citedDomains: querySnapshots.citedDomains,
22730
22900
  screenshotPath: querySnapshots.screenshotPath,
22731
22901
  rawResponse: querySnapshots.rawResponse
22732
- }).from(querySnapshots).where(eq24(querySnapshots.runId, runId)).all());
22733
- const queryRows = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq24(queries.projectId, project.id)).all();
22902
+ }).from(querySnapshots).where(eq25(querySnapshots.runId, runId)).all());
22903
+ const queryRows = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq25(queries.projectId, project.id)).all();
22734
22904
  const queryMap = new Map(queryRows.map((q) => [q.id, q.query]));
22735
22905
  const byQuery = /* @__PURE__ */ new Map();
22736
22906
  for (const snap of snapshots) {
@@ -22813,7 +22983,7 @@ async function cdpRoutes(app, opts) {
22813
22983
 
22814
22984
  // ../api-routes/src/ga.ts
22815
22985
  import crypto21 from "crypto";
22816
- import { eq as eq25, desc as desc13, and as and18, sql as sql9 } from "drizzle-orm";
22986
+ import { eq as eq26, desc as desc13, and as and19, sql as sql10 } from "drizzle-orm";
22817
22987
  function gaLog(level, action, ctx) {
22818
22988
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
22819
22989
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -23020,10 +23190,10 @@ async function ga4Routes(app, opts) {
23020
23190
  if (!saConn && !oauthConn) {
23021
23191
  throw notFound("GA4 connection", project.name);
23022
23192
  }
23023
- app.db.delete(gaTrafficSnapshots).where(eq25(gaTrafficSnapshots.projectId, project.id)).run();
23024
- app.db.delete(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).run();
23025
- app.db.delete(gaAiReferrals).where(eq25(gaAiReferrals.projectId, project.id)).run();
23026
- app.db.delete(gaSocialReferrals).where(eq25(gaSocialReferrals.projectId, project.id)).run();
23193
+ app.db.delete(gaTrafficSnapshots).where(eq26(gaTrafficSnapshots.projectId, project.id)).run();
23194
+ app.db.delete(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).run();
23195
+ app.db.delete(gaAiReferrals).where(eq26(gaAiReferrals.projectId, project.id)).run();
23196
+ app.db.delete(gaSocialReferrals).where(eq26(gaSocialReferrals.projectId, project.id)).run();
23027
23197
  const propertyId = saConn?.propertyId ?? oauthConn?.propertyId ?? null;
23028
23198
  opts.ga4CredentialStore?.deleteConnection(project.name);
23029
23199
  opts.googleConnectionStore?.deleteConnection(project.canonicalDomain, "ga4");
@@ -23044,7 +23214,7 @@ async function ga4Routes(app, opts) {
23044
23214
  if (!connected) {
23045
23215
  return { connected: false, propertyId: null, clientEmail: null, authMethod: null, lastSyncedAt: null };
23046
23216
  }
23047
- const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
23217
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
23048
23218
  return {
23049
23219
  connected: true,
23050
23220
  propertyId: saConn?.propertyId ?? oauthConn?.propertyId ?? null,
@@ -23108,10 +23278,10 @@ async function ga4Routes(app, opts) {
23108
23278
  app.db.transaction((tx) => {
23109
23279
  if (syncTraffic) {
23110
23280
  tx.delete(gaTrafficSnapshots).where(
23111
- and18(
23112
- eq25(gaTrafficSnapshots.projectId, project.id),
23113
- sql9`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
23114
- sql9`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
23281
+ and19(
23282
+ eq26(gaTrafficSnapshots.projectId, project.id),
23283
+ sql10`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
23284
+ sql10`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
23115
23285
  )
23116
23286
  ).run();
23117
23287
  for (const row of rows) {
@@ -23132,10 +23302,10 @@ async function ga4Routes(app, opts) {
23132
23302
  }
23133
23303
  if (syncAi) {
23134
23304
  tx.delete(gaAiReferrals).where(
23135
- and18(
23136
- eq25(gaAiReferrals.projectId, project.id),
23137
- sql9`${gaAiReferrals.date} >= ${summary.periodStart}`,
23138
- sql9`${gaAiReferrals.date} <= ${summary.periodEnd}`
23305
+ and19(
23306
+ eq26(gaAiReferrals.projectId, project.id),
23307
+ sql10`${gaAiReferrals.date} >= ${summary.periodStart}`,
23308
+ sql10`${gaAiReferrals.date} <= ${summary.periodEnd}`
23139
23309
  )
23140
23310
  ).run();
23141
23311
  for (const row of aiReferrals) {
@@ -23158,10 +23328,10 @@ async function ga4Routes(app, opts) {
23158
23328
  }
23159
23329
  if (syncSocial) {
23160
23330
  tx.delete(gaSocialReferrals).where(
23161
- and18(
23162
- eq25(gaSocialReferrals.projectId, project.id),
23163
- sql9`${gaSocialReferrals.date} >= ${summary.periodStart}`,
23164
- sql9`${gaSocialReferrals.date} <= ${summary.periodEnd}`
23331
+ and19(
23332
+ eq26(gaSocialReferrals.projectId, project.id),
23333
+ sql10`${gaSocialReferrals.date} >= ${summary.periodStart}`,
23334
+ sql10`${gaSocialReferrals.date} <= ${summary.periodEnd}`
23165
23335
  )
23166
23336
  ).run();
23167
23337
  for (const row of socialReferrals) {
@@ -23180,7 +23350,7 @@ async function ga4Routes(app, opts) {
23180
23350
  }
23181
23351
  }
23182
23352
  if (syncSummary) {
23183
- tx.delete(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).run();
23353
+ tx.delete(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).run();
23184
23354
  tx.insert(gaTrafficSummaries).values({
23185
23355
  id: crypto21.randomUUID(),
23186
23356
  projectId: project.id,
@@ -23192,7 +23362,7 @@ async function ga4Routes(app, opts) {
23192
23362
  syncedAt: now,
23193
23363
  syncRunId: runId
23194
23364
  }).run();
23195
- tx.delete(gaTrafficWindowSummaries).where(eq25(gaTrafficWindowSummaries.projectId, project.id)).run();
23365
+ tx.delete(gaTrafficWindowSummaries).where(eq26(gaTrafficWindowSummaries.projectId, project.id)).run();
23196
23366
  for (const ws of windowSummaries) {
23197
23367
  tx.insert(gaTrafficWindowSummaries).values({
23198
23368
  id: crypto21.randomUUID(),
@@ -23210,7 +23380,7 @@ async function ga4Routes(app, opts) {
23210
23380
  }
23211
23381
  }
23212
23382
  });
23213
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq25(runs.id, runId)).run();
23383
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq26(runs.id, runId)).run();
23214
23384
  const syncedComponents = only ? [
23215
23385
  ...syncTraffic ? ["traffic"] : [],
23216
23386
  ...syncSummary ? ["summary"] : [],
@@ -23239,7 +23409,7 @@ async function ga4Routes(app, opts) {
23239
23409
  } catch (e) {
23240
23410
  const msg = e instanceof Error ? e.message : String(e);
23241
23411
  gaLog("error", "sync.fetch-failed", { projectId: project.id, runId, error: msg });
23242
- app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq25(runs.id, runId)).run();
23412
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq26(runs.id, runId)).run();
23243
23413
  throw e;
23244
23414
  }
23245
23415
  });
@@ -23250,66 +23420,66 @@ async function ga4Routes(app, opts) {
23250
23420
  const window = parseWindow(request.query.window);
23251
23421
  const cutoff = windowCutoff(window);
23252
23422
  const cutoffDate = cutoff?.slice(0, 10) ?? null;
23253
- const snapshotConditions = [eq25(gaTrafficSnapshots.projectId, project.id)];
23254
- if (cutoffDate) snapshotConditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
23255
- const aiConditions = [eq25(gaAiReferrals.projectId, project.id)];
23256
- if (cutoffDate) aiConditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
23257
- const socialConditions = [eq25(gaSocialReferrals.projectId, project.id)];
23258
- if (cutoffDate) socialConditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
23423
+ const snapshotConditions = [eq26(gaTrafficSnapshots.projectId, project.id)];
23424
+ if (cutoffDate) snapshotConditions.push(sql10`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
23425
+ const aiConditions = [eq26(gaAiReferrals.projectId, project.id)];
23426
+ if (cutoffDate) aiConditions.push(sql10`${gaAiReferrals.date} >= ${cutoffDate}`);
23427
+ const socialConditions = [eq26(gaSocialReferrals.projectId, project.id)];
23428
+ if (cutoffDate) socialConditions.push(sql10`${gaSocialReferrals.date} >= ${cutoffDate}`);
23259
23429
  const windowSummaryRow = cutoffDate ? app.db.select({
23260
23430
  totalSessions: gaTrafficWindowSummaries.totalSessions,
23261
23431
  totalOrganicSessions: gaTrafficWindowSummaries.totalOrganicSessions,
23262
23432
  totalDirectSessions: gaTrafficWindowSummaries.totalDirectSessions,
23263
23433
  totalUsers: gaTrafficWindowSummaries.totalUsers
23264
23434
  }).from(gaTrafficWindowSummaries).where(
23265
- and18(
23266
- eq25(gaTrafficWindowSummaries.projectId, project.id),
23267
- eq25(gaTrafficWindowSummaries.windowKey, window)
23435
+ and19(
23436
+ eq26(gaTrafficWindowSummaries.projectId, project.id),
23437
+ eq26(gaTrafficWindowSummaries.windowKey, window)
23268
23438
  )
23269
23439
  ).get() : null;
23270
23440
  const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
23271
- totalSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
23272
- totalOrganicSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
23273
- totalUsers: sql9`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
23274
- }).from(gaTrafficSnapshots).where(and18(...snapshotConditions)).get() : null;
23441
+ totalSessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
23442
+ totalOrganicSessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
23443
+ totalUsers: sql10`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
23444
+ }).from(gaTrafficSnapshots).where(and19(...snapshotConditions)).get() : null;
23275
23445
  const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
23276
23446
  totalSessions: gaTrafficSummaries.totalSessions,
23277
23447
  totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
23278
23448
  totalUsers: gaTrafficSummaries.totalUsers
23279
- }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).get();
23449
+ }).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).get();
23280
23450
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
23281
- totalDirectSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
23282
- }).from(gaTrafficSnapshots).where(and18(...snapshotConditions)).get();
23451
+ totalDirectSessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
23452
+ }).from(gaTrafficSnapshots).where(and19(...snapshotConditions)).get();
23283
23453
  const summaryMeta = app.db.select({
23284
23454
  periodStart: gaTrafficSummaries.periodStart,
23285
23455
  periodEnd: gaTrafficSummaries.periodEnd
23286
- }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).get();
23456
+ }).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).get();
23287
23457
  const rows = app.db.select({
23288
- landingPage: sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
23289
- sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
23290
- organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
23291
- directSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
23292
- users: sql9`SUM(${gaTrafficSnapshots.users})`
23293
- }).from(gaTrafficSnapshots).where(and18(...snapshotConditions)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
23458
+ landingPage: sql10`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
23459
+ sessions: sql10`SUM(${gaTrafficSnapshots.sessions})`,
23460
+ organicSessions: sql10`SUM(${gaTrafficSnapshots.organicSessions})`,
23461
+ directSessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
23462
+ users: sql10`SUM(${gaTrafficSnapshots.users})`
23463
+ }).from(gaTrafficSnapshots).where(and19(...snapshotConditions)).groupBy(sql10`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql10`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
23294
23464
  const aiReferralRows = app.db.select({
23295
23465
  source: gaAiReferrals.source,
23296
23466
  medium: gaAiReferrals.medium,
23297
23467
  sourceDimension: gaAiReferrals.sourceDimension,
23298
- sessions: sql9`SUM(${gaAiReferrals.sessions})`,
23299
- users: sql9`SUM(${gaAiReferrals.users})`
23300
- }).from(gaAiReferrals).where(and18(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
23468
+ sessions: sql10`SUM(${gaAiReferrals.sessions})`,
23469
+ users: sql10`SUM(${gaAiReferrals.users})`
23470
+ }).from(gaAiReferrals).where(and19(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
23301
23471
  const aiReferralLandingPageRows = app.db.select({
23302
23472
  source: gaAiReferrals.source,
23303
23473
  medium: gaAiReferrals.medium,
23304
23474
  sourceDimension: gaAiReferrals.sourceDimension,
23305
- landingPage: sql9`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
23306
- sessions: sql9`SUM(${gaAiReferrals.sessions})`,
23307
- users: sql9`SUM(${gaAiReferrals.users})`
23308
- }).from(gaAiReferrals).where(and18(...aiConditions)).groupBy(
23475
+ landingPage: sql10`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
23476
+ sessions: sql10`SUM(${gaAiReferrals.sessions})`,
23477
+ users: sql10`SUM(${gaAiReferrals.users})`
23478
+ }).from(gaAiReferrals).where(and19(...aiConditions)).groupBy(
23309
23479
  gaAiReferrals.source,
23310
23480
  gaAiReferrals.medium,
23311
23481
  gaAiReferrals.sourceDimension,
23312
- sql9`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
23482
+ sql10`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
23313
23483
  ).all();
23314
23484
  const aiReferrals = pickWinningDimension(
23315
23485
  aiReferralRows,
@@ -23320,10 +23490,10 @@ async function ga4Routes(app, opts) {
23320
23490
  (r) => `${r.source}\0${r.medium}\0${r.landingPage}`
23321
23491
  );
23322
23492
  const aiDeduped = app.db.select({
23323
- sessions: sql9`COALESCE(SUM(max_sessions), 0)`,
23324
- users: sql9`COALESCE(SUM(max_users), 0)`
23493
+ sessions: sql10`COALESCE(SUM(max_sessions), 0)`,
23494
+ users: sql10`COALESCE(SUM(max_users), 0)`
23325
23495
  }).from(
23326
- sql9`(
23496
+ sql10`(
23327
23497
  SELECT date, source, medium,
23328
23498
  MAX(dimension_sessions) AS max_sessions,
23329
23499
  MAX(dimension_users) AS max_users
@@ -23332,7 +23502,7 @@ async function ga4Routes(app, opts) {
23332
23502
  SUM(sessions) AS dimension_sessions,
23333
23503
  SUM(users) AS dimension_users
23334
23504
  FROM ga_ai_referrals
23335
- WHERE project_id = ${project.id}${cutoffDate ? sql9` AND date >= ${cutoffDate}` : sql9``}
23505
+ WHERE project_id = ${project.id}${cutoffDate ? sql10` AND date >= ${cutoffDate}` : sql10``}
23336
23506
  GROUP BY date, source, medium, source_dimension
23337
23507
  )
23338
23508
  GROUP BY date, source, medium
@@ -23340,9 +23510,9 @@ async function ga4Routes(app, opts) {
23340
23510
  ).get();
23341
23511
  const aiBySessionRows = app.db.select({
23342
23512
  channelGroup: gaAiReferrals.channelGroup,
23343
- sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
23344
- users: sql9`COALESCE(SUM(${gaAiReferrals.users}), 0)`
23345
- }).from(gaAiReferrals).where(and18(...aiConditions, eq25(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
23513
+ sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
23514
+ users: sql10`COALESCE(SUM(${gaAiReferrals.users}), 0)`
23515
+ }).from(gaAiReferrals).where(and19(...aiConditions, eq26(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
23346
23516
  const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
23347
23517
  let aiBySessionUsers = 0;
23348
23518
  for (const row of aiBySessionRows) {
@@ -23354,14 +23524,14 @@ async function ga4Routes(app, opts) {
23354
23524
  source: gaSocialReferrals.source,
23355
23525
  medium: gaSocialReferrals.medium,
23356
23526
  channelGroup: gaSocialReferrals.channelGroup,
23357
- sessions: sql9`SUM(${gaSocialReferrals.sessions})`,
23358
- users: sql9`SUM(${gaSocialReferrals.users})`
23359
- }).from(gaSocialReferrals).where(and18(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql9`SUM(${gaSocialReferrals.sessions}) DESC`).all();
23527
+ sessions: sql10`SUM(${gaSocialReferrals.sessions})`,
23528
+ users: sql10`SUM(${gaSocialReferrals.users})`
23529
+ }).from(gaSocialReferrals).where(and19(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql10`SUM(${gaSocialReferrals.sessions}) DESC`).all();
23360
23530
  const socialTotals = app.db.select({
23361
- sessions: sql9`SUM(${gaSocialReferrals.sessions})`,
23362
- users: sql9`SUM(${gaSocialReferrals.users})`
23363
- }).from(gaSocialReferrals).where(and18(...socialConditions)).get();
23364
- const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq25(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
23531
+ sessions: sql10`SUM(${gaSocialReferrals.sessions})`,
23532
+ users: sql10`SUM(${gaSocialReferrals.users})`
23533
+ }).from(gaSocialReferrals).where(and19(...socialConditions)).get();
23534
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
23365
23535
  const total = summaryRow?.totalSessions ?? 0;
23366
23536
  const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
23367
23537
  const totalOrganicSessions = summaryRow?.totalOrganicSessions ?? 0;
@@ -23441,22 +23611,22 @@ async function ga4Routes(app, opts) {
23441
23611
  const project = resolveProject(app.db, request.params.name);
23442
23612
  requireGa4Connection(opts, project.name, project.canonicalDomain);
23443
23613
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
23444
- const conditions = [eq25(gaAiReferrals.projectId, project.id)];
23445
- if (cutoffDate) conditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
23614
+ const conditions = [eq26(gaAiReferrals.projectId, project.id)];
23615
+ if (cutoffDate) conditions.push(sql10`${gaAiReferrals.date} >= ${cutoffDate}`);
23446
23616
  const rows = app.db.select({
23447
23617
  date: gaAiReferrals.date,
23448
23618
  source: gaAiReferrals.source,
23449
23619
  medium: gaAiReferrals.medium,
23450
- landingPage: sql9`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
23620
+ landingPage: sql10`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
23451
23621
  sourceDimension: gaAiReferrals.sourceDimension,
23452
- sessions: sql9`SUM(${gaAiReferrals.sessions})`,
23453
- users: sql9`SUM(${gaAiReferrals.users})`
23454
- }).from(gaAiReferrals).where(and18(...conditions)).groupBy(
23622
+ sessions: sql10`SUM(${gaAiReferrals.sessions})`,
23623
+ users: sql10`SUM(${gaAiReferrals.users})`
23624
+ }).from(gaAiReferrals).where(and19(...conditions)).groupBy(
23455
23625
  gaAiReferrals.date,
23456
23626
  gaAiReferrals.source,
23457
23627
  gaAiReferrals.medium,
23458
23628
  gaAiReferrals.sourceDimension,
23459
- sql9`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
23629
+ sql10`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
23460
23630
  ).orderBy(gaAiReferrals.date).all();
23461
23631
  return rows;
23462
23632
  });
@@ -23464,8 +23634,8 @@ async function ga4Routes(app, opts) {
23464
23634
  const project = resolveProject(app.db, request.params.name);
23465
23635
  requireGa4Connection(opts, project.name, project.canonicalDomain);
23466
23636
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
23467
- const conditions = [eq25(gaSocialReferrals.projectId, project.id)];
23468
- if (cutoffDate) conditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
23637
+ const conditions = [eq26(gaSocialReferrals.projectId, project.id)];
23638
+ if (cutoffDate) conditions.push(sql10`${gaSocialReferrals.date} >= ${cutoffDate}`);
23469
23639
  const rows = app.db.select({
23470
23640
  date: gaSocialReferrals.date,
23471
23641
  source: gaSocialReferrals.source,
@@ -23473,7 +23643,7 @@ async function ga4Routes(app, opts) {
23473
23643
  channelGroup: gaSocialReferrals.channelGroup,
23474
23644
  sessions: gaSocialReferrals.sessions,
23475
23645
  users: gaSocialReferrals.users
23476
- }).from(gaSocialReferrals).where(and18(...conditions)).orderBy(gaSocialReferrals.date).all();
23646
+ }).from(gaSocialReferrals).where(and19(...conditions)).orderBy(gaSocialReferrals.date).all();
23477
23647
  return rows;
23478
23648
  });
23479
23649
  app.get("/projects/:name/ga/social-referral-trend", async (request, _reply) => {
@@ -23486,10 +23656,10 @@ async function ga4Routes(app, opts) {
23486
23656
  d.setDate(d.getDate() - n);
23487
23657
  return fmt(d);
23488
23658
  };
23489
- const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and18(
23490
- eq25(gaSocialReferrals.projectId, project.id),
23491
- sql9`${gaSocialReferrals.date} >= ${from}`,
23492
- sql9`${gaSocialReferrals.date} < ${to}`
23659
+ const sumSocial = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and19(
23660
+ eq26(gaSocialReferrals.projectId, project.id),
23661
+ sql10`${gaSocialReferrals.date} >= ${from}`,
23662
+ sql10`${gaSocialReferrals.date} < ${to}`
23493
23663
  )).get();
23494
23664
  const current7d = sumSocial(daysAgo(7), fmt(today));
23495
23665
  const prev7d = sumSocial(daysAgo(14), daysAgo(7));
@@ -23498,19 +23668,19 @@ async function ga4Routes(app, opts) {
23498
23668
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
23499
23669
  const sourceCurrent = app.db.select({
23500
23670
  source: gaSocialReferrals.source,
23501
- sessions: sql9`SUM(${gaSocialReferrals.sessions})`
23502
- }).from(gaSocialReferrals).where(and18(
23503
- eq25(gaSocialReferrals.projectId, project.id),
23504
- sql9`${gaSocialReferrals.date} >= ${daysAgo(7)}`,
23505
- sql9`${gaSocialReferrals.date} < ${fmt(today)}`
23671
+ sessions: sql10`SUM(${gaSocialReferrals.sessions})`
23672
+ }).from(gaSocialReferrals).where(and19(
23673
+ eq26(gaSocialReferrals.projectId, project.id),
23674
+ sql10`${gaSocialReferrals.date} >= ${daysAgo(7)}`,
23675
+ sql10`${gaSocialReferrals.date} < ${fmt(today)}`
23506
23676
  )).groupBy(gaSocialReferrals.source).all();
23507
23677
  const sourcePrev = app.db.select({
23508
23678
  source: gaSocialReferrals.source,
23509
- sessions: sql9`SUM(${gaSocialReferrals.sessions})`
23510
- }).from(gaSocialReferrals).where(and18(
23511
- eq25(gaSocialReferrals.projectId, project.id),
23512
- sql9`${gaSocialReferrals.date} >= ${daysAgo(14)}`,
23513
- sql9`${gaSocialReferrals.date} < ${daysAgo(7)}`
23679
+ sessions: sql10`SUM(${gaSocialReferrals.sessions})`
23680
+ }).from(gaSocialReferrals).where(and19(
23681
+ eq26(gaSocialReferrals.projectId, project.id),
23682
+ sql10`${gaSocialReferrals.date} >= ${daysAgo(14)}`,
23683
+ sql10`${gaSocialReferrals.date} < ${daysAgo(7)}`
23514
23684
  )).groupBy(gaSocialReferrals.source).all();
23515
23685
  const prevMap = new Map(sourcePrev.map((r) => [r.source, r.sessions]));
23516
23686
  let biggestMover = null;
@@ -23549,16 +23719,16 @@ async function ga4Routes(app, opts) {
23549
23719
  return fmt(d);
23550
23720
  };
23551
23721
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
23552
- 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();
23553
- 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();
23554
- 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();
23555
- const sumAi = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and18(
23556
- eq25(gaAiReferrals.projectId, project.id),
23557
- sql9`${gaAiReferrals.date} >= ${from}`,
23558
- sql9`${gaAiReferrals.date} < ${to}`,
23559
- eq25(gaAiReferrals.sourceDimension, "session")
23722
+ const sumTotal = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and19(eq26(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
23723
+ const sumOrganic = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and19(eq26(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
23724
+ const sumDirect = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and19(eq26(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
23725
+ const sumAi = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and19(
23726
+ eq26(gaAiReferrals.projectId, project.id),
23727
+ sql10`${gaAiReferrals.date} >= ${from}`,
23728
+ sql10`${gaAiReferrals.date} < ${to}`,
23729
+ eq26(gaAiReferrals.sourceDimension, "session")
23560
23730
  )).get();
23561
- 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();
23731
+ const sumSocial = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and19(eq26(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${from}`, sql10`${gaSocialReferrals.date} < ${to}`)).get();
23562
23732
  const todayStr = fmt(today);
23563
23733
  const buildTrend = (sum) => {
23564
23734
  const c7 = sum(daysAgo(7), todayStr)?.sessions ?? 0;
@@ -23567,17 +23737,17 @@ async function ga4Routes(app, opts) {
23567
23737
  const p30 = sum(daysAgo(60), daysAgo(30))?.sessions ?? 0;
23568
23738
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
23569
23739
  };
23570
- const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and18(
23571
- eq25(gaAiReferrals.projectId, project.id),
23572
- sql9`${gaAiReferrals.date} >= ${daysAgo(7)}`,
23573
- sql9`${gaAiReferrals.date} < ${todayStr}`,
23574
- eq25(gaAiReferrals.sourceDimension, "session")
23740
+ const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and19(
23741
+ eq26(gaAiReferrals.projectId, project.id),
23742
+ sql10`${gaAiReferrals.date} >= ${daysAgo(7)}`,
23743
+ sql10`${gaAiReferrals.date} < ${todayStr}`,
23744
+ eq26(gaAiReferrals.sourceDimension, "session")
23575
23745
  )).groupBy(gaAiReferrals.source).all();
23576
- const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and18(
23577
- eq25(gaAiReferrals.projectId, project.id),
23578
- sql9`${gaAiReferrals.date} >= ${daysAgo(14)}`,
23579
- sql9`${gaAiReferrals.date} < ${daysAgo(7)}`,
23580
- eq25(gaAiReferrals.sourceDimension, "session")
23746
+ const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and19(
23747
+ eq26(gaAiReferrals.projectId, project.id),
23748
+ sql10`${gaAiReferrals.date} >= ${daysAgo(14)}`,
23749
+ sql10`${gaAiReferrals.date} < ${daysAgo(7)}`,
23750
+ eq26(gaAiReferrals.sourceDimension, "session")
23581
23751
  )).groupBy(gaAiReferrals.source).all();
23582
23752
  const findBiggestMover = (current, prev) => {
23583
23753
  const prevMap = new Map(prev.map((r) => [r.source, r.sessions]));
@@ -23593,8 +23763,8 @@ async function ga4Routes(app, opts) {
23593
23763
  }
23594
23764
  return mover;
23595
23765
  };
23596
- 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();
23597
- 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();
23766
+ const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and19(eq26(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql10`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
23767
+ const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and19(eq26(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql10`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
23598
23768
  return {
23599
23769
  total: buildTrend(sumTotal),
23600
23770
  organic: buildTrend(sumOrganic),
@@ -23609,14 +23779,14 @@ async function ga4Routes(app, opts) {
23609
23779
  const project = resolveProject(app.db, request.params.name);
23610
23780
  requireGa4Connection(opts, project.name, project.canonicalDomain);
23611
23781
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
23612
- const conditions = [eq25(gaTrafficSnapshots.projectId, project.id)];
23613
- if (cutoffDate) conditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
23782
+ const conditions = [eq26(gaTrafficSnapshots.projectId, project.id)];
23783
+ if (cutoffDate) conditions.push(sql10`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
23614
23784
  const rows = app.db.select({
23615
23785
  date: gaTrafficSnapshots.date,
23616
- sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
23617
- organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
23618
- users: sql9`SUM(${gaTrafficSnapshots.users})`
23619
- }).from(gaTrafficSnapshots).where(and18(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
23786
+ sessions: sql10`SUM(${gaTrafficSnapshots.sessions})`,
23787
+ organicSessions: sql10`SUM(${gaTrafficSnapshots.organicSessions})`,
23788
+ users: sql10`SUM(${gaTrafficSnapshots.users})`
23789
+ }).from(gaTrafficSnapshots).where(and19(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
23620
23790
  return rows.map((r) => ({
23621
23791
  date: r.date,
23622
23792
  sessions: r.sessions ?? 0,
@@ -23628,11 +23798,11 @@ async function ga4Routes(app, opts) {
23628
23798
  const project = resolveProject(app.db, request.params.name);
23629
23799
  requireGa4Connection(opts, project.name, project.canonicalDomain);
23630
23800
  const trafficPages = app.db.select({
23631
- landingPage: sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
23632
- sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
23633
- organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
23634
- users: sql9`SUM(${gaTrafficSnapshots.users})`
23635
- }).from(gaTrafficSnapshots).where(eq25(gaTrafficSnapshots.projectId, project.id)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
23801
+ landingPage: sql10`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
23802
+ sessions: sql10`SUM(${gaTrafficSnapshots.sessions})`,
23803
+ organicSessions: sql10`SUM(${gaTrafficSnapshots.organicSessions})`,
23804
+ users: sql10`SUM(${gaTrafficSnapshots.users})`
23805
+ }).from(gaTrafficSnapshots).where(eq26(gaTrafficSnapshots.projectId, project.id)).groupBy(sql10`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql10`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
23636
23806
  return {
23637
23807
  pages: trafficPages.map((r) => ({
23638
23808
  landingPage: r.landingPage,
@@ -25280,7 +25450,7 @@ async function wordpressRoutes(app, opts) {
25280
25450
 
25281
25451
  // ../api-routes/src/backlinks.ts
25282
25452
  import crypto23 from "crypto";
25283
- import { and as and20, asc as asc3, desc as desc14, eq as eq26, sql as sql10 } from "drizzle-orm";
25453
+ import { and as and21, asc as asc4, desc as desc14, eq as eq27, sql as sql11 } from "drizzle-orm";
25284
25454
 
25285
25455
  // ../integration-commoncrawl/src/constants.ts
25286
25456
  import os2 from "os";
@@ -25570,7 +25740,7 @@ async function queryBacklinks(opts) {
25570
25740
  const reversed = opts.targets.map(reverseDomain);
25571
25741
  const targetList = reversed.map(quote).join(", ");
25572
25742
  const limitClause = opts.limitPerTarget ? `QUALIFY row_number() OVER (PARTITION BY t.target_rev_domain ORDER BY v.num_hosts DESC) <= ${Math.floor(opts.limitPerTarget)}` : "";
25573
- const sql13 = `
25743
+ const sql14 = `
25574
25744
  WITH vertices AS (
25575
25745
  SELECT * FROM read_csv(
25576
25746
  ${quote(opts.vertexPath)},
@@ -25606,7 +25776,7 @@ async function queryBacklinks(opts) {
25606
25776
  const conn = await instance.connect();
25607
25777
  let rows;
25608
25778
  try {
25609
- const reader = await conn.runAndReadAll(sql13);
25779
+ const reader = await conn.runAndReadAll(sql14);
25610
25780
  rows = reader.getRowObjects();
25611
25781
  } finally {
25612
25782
  conn.disconnectSync?.();
@@ -25683,7 +25853,7 @@ function pruneCachedRelease(release, opts = {}) {
25683
25853
  }
25684
25854
 
25685
25855
  // ../api-routes/src/backlinks-filter.ts
25686
- import { and as and19, ne as ne3, notLike } from "drizzle-orm";
25856
+ import { and as and20, ne as ne3, notLike } from "drizzle-orm";
25687
25857
  var BACKLINK_FILTER_PATTERNS = [
25688
25858
  "*.google.com",
25689
25859
  "*.googleusercontent.com",
@@ -25706,7 +25876,7 @@ function backlinkCrawlerExclusionClause() {
25706
25876
  conditions.push(ne3(backlinkDomains.linkingDomain, pattern));
25707
25877
  }
25708
25878
  }
25709
- const combined = and19(...conditions);
25879
+ const combined = and20(...conditions);
25710
25880
  if (!combined) throw new Error("BACKLINK_FILTER_PATTERNS is unexpectedly empty");
25711
25881
  return combined;
25712
25882
  }
@@ -25777,11 +25947,11 @@ function mapRunRow(row) {
25777
25947
  }
25778
25948
  function latestSummaryForProject(db, projectId, source, release) {
25779
25949
  const conditions = [
25780
- eq26(backlinkSummaries.projectId, projectId),
25781
- eq26(backlinkSummaries.source, source)
25950
+ eq27(backlinkSummaries.projectId, projectId),
25951
+ eq27(backlinkSummaries.source, source)
25782
25952
  ];
25783
- if (release) conditions.push(eq26(backlinkSummaries.release, release));
25784
- return db.select().from(backlinkSummaries).where(and20(...conditions)).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
25953
+ if (release) conditions.push(eq27(backlinkSummaries.release, release));
25954
+ return db.select().from(backlinkSummaries).where(and21(...conditions)).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
25785
25955
  }
25786
25956
  function parseExcludeCrawlers(value) {
25787
25957
  if (!value) return false;
@@ -25789,19 +25959,19 @@ function parseExcludeCrawlers(value) {
25789
25959
  return lower === "1" || lower === "true" || lower === "yes";
25790
25960
  }
25791
25961
  function computeFilteredSummary(db, base) {
25792
- const baseDomainCondition = and20(
25793
- eq26(backlinkDomains.projectId, base.projectId),
25794
- eq26(backlinkDomains.source, base.source),
25795
- eq26(backlinkDomains.release, base.release)
25962
+ const baseDomainCondition = and21(
25963
+ eq27(backlinkDomains.projectId, base.projectId),
25964
+ eq27(backlinkDomains.source, base.source),
25965
+ eq27(backlinkDomains.release, base.release)
25796
25966
  );
25797
- const filteredCondition = and20(baseDomainCondition, backlinkCrawlerExclusionClause());
25967
+ const filteredCondition = and21(baseDomainCondition, backlinkCrawlerExclusionClause());
25798
25968
  const unfilteredAgg = db.select({
25799
- count: sql10`count(*)`,
25800
- total: sql10`coalesce(sum(${backlinkDomains.numHosts}), 0)`
25969
+ count: sql11`count(*)`,
25970
+ total: sql11`coalesce(sum(${backlinkDomains.numHosts}), 0)`
25801
25971
  }).from(backlinkDomains).where(baseDomainCondition).get();
25802
25972
  const filteredAgg = db.select({
25803
- count: sql10`count(*)`,
25804
- total: sql10`coalesce(sum(${backlinkDomains.numHosts}), 0)`
25973
+ count: sql11`count(*)`,
25974
+ total: sql11`coalesce(sum(${backlinkDomains.numHosts}), 0)`
25805
25975
  }).from(backlinkDomains).where(filteredCondition).get();
25806
25976
  const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc14(backlinkDomains.numHosts)).limit(10).all();
25807
25977
  const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
@@ -25824,13 +25994,13 @@ function computeFilteredSummary(db, base) {
25824
25994
  };
25825
25995
  }
25826
25996
  function buildSourceAvailability(db, projectId, source, connected, excludeCrawlers) {
25827
- const summary = db.select().from(backlinkSummaries).where(and20(eq26(backlinkSummaries.projectId, projectId), eq26(backlinkSummaries.source, source))).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
25997
+ const summary = db.select().from(backlinkSummaries).where(and21(eq27(backlinkSummaries.projectId, projectId), eq27(backlinkSummaries.source, source))).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
25828
25998
  let totalLinkingDomains = summary?.totalLinkingDomains ?? 0;
25829
25999
  if (summary && excludeCrawlers) {
25830
- const filtered = db.select({ count: sql10`count(*)` }).from(backlinkDomains).where(and20(
25831
- eq26(backlinkDomains.projectId, projectId),
25832
- eq26(backlinkDomains.source, source),
25833
- eq26(backlinkDomains.release, summary.release),
26000
+ const filtered = db.select({ count: sql11`count(*)` }).from(backlinkDomains).where(and21(
26001
+ eq27(backlinkDomains.projectId, projectId),
26002
+ eq27(backlinkDomains.source, source),
26003
+ eq27(backlinkDomains.release, summary.release),
25834
26004
  backlinkCrawlerExclusionClause()
25835
26005
  )).get();
25836
26006
  totalLinkingDomains = Number(filtered?.count ?? 0);
@@ -25845,7 +26015,7 @@ function buildSourceAvailability(db, projectId, source, connected, excludeCrawle
25845
26015
  };
25846
26016
  }
25847
26017
  function computeSourceAvailability(db, project, bingStore, excludeCrawlers) {
25848
- const ccReadySync = db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
26018
+ const ccReadySync = db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq27(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
25849
26019
  const ccConnected = project.autoExtractBacklinks === true && !!ccReadySync;
25850
26020
  const bingConnected = !!bingStore?.getConnection(project.canonicalDomain);
25851
26021
  const sources = [
@@ -25901,7 +26071,7 @@ async function backlinksRoutes(app, opts) {
25901
26071
  "@duckdb/node-api is not installed. Run `canonry backlinks install` to enable the backlinks feature."
25902
26072
  );
25903
26073
  }
25904
- const existing = app.db.select().from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.release, release)).get();
26074
+ const existing = app.db.select().from(ccReleaseSyncs).where(eq27(ccReleaseSyncs.release, release)).get();
25905
26075
  const now = (/* @__PURE__ */ new Date()).toISOString();
25906
26076
  if (existing) {
25907
26077
  if (NON_TERMINAL_SYNC_STATUSES.has(existing.status)) {
@@ -25912,9 +26082,9 @@ async function backlinksRoutes(app, opts) {
25912
26082
  phaseDetail: null,
25913
26083
  error: null,
25914
26084
  updatedAt: now
25915
- }).where(eq26(ccReleaseSyncs.id, existing.id)).run();
26085
+ }).where(eq27(ccReleaseSyncs.id, existing.id)).run();
25916
26086
  opts.onReleaseSyncRequested(existing.id, release);
25917
- const refreshed = app.db.select().from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.id, existing.id)).get();
26087
+ const refreshed = app.db.select().from(ccReleaseSyncs).where(eq27(ccReleaseSyncs.id, existing.id)).get();
25918
26088
  return reply.status(200).send(mapSyncRow(refreshed));
25919
26089
  }
25920
26090
  const id = crypto23.randomUUID();
@@ -25926,7 +26096,7 @@ async function backlinksRoutes(app, opts) {
25926
26096
  updatedAt: now
25927
26097
  }).run();
25928
26098
  opts.onReleaseSyncRequested(id, release);
25929
- const inserted = app.db.select().from(ccReleaseSyncs).where(eq26(ccReleaseSyncs.id, id)).get();
26099
+ const inserted = app.db.select().from(ccReleaseSyncs).where(eq27(ccReleaseSyncs.id, id)).get();
25930
26100
  return reply.status(201).send(mapSyncRow(inserted));
25931
26101
  });
25932
26102
  app.get("/backlinks/syncs/latest", async (_request, reply) => {
@@ -25984,7 +26154,7 @@ async function backlinksRoutes(app, opts) {
25984
26154
  createdAt: now
25985
26155
  }).run();
25986
26156
  opts.onBacklinkExtractRequested(runId, project.id, release);
25987
- const run = app.db.select().from(runs).where(eq26(runs.id, runId)).get();
26157
+ const run = app.db.select().from(runs).where(eq27(runs.id, runId)).get();
25988
26158
  return reply.status(201).send(mapRunRow(run));
25989
26159
  });
25990
26160
  app.get(
@@ -26010,13 +26180,13 @@ async function backlinksRoutes(app, opts) {
26010
26180
  const limit = Math.min(Math.max(parseInt(request.query.limit ?? "50", 10) || 50, 1), 500);
26011
26181
  const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
26012
26182
  const excludeCrawlers = parseExcludeCrawlers(request.query.excludeCrawlers);
26013
- const baseDomainCondition = and20(
26014
- eq26(backlinkDomains.projectId, project.id),
26015
- eq26(backlinkDomains.source, source),
26016
- eq26(backlinkDomains.release, targetRelease)
26183
+ const baseDomainCondition = and21(
26184
+ eq27(backlinkDomains.projectId, project.id),
26185
+ eq27(backlinkDomains.source, source),
26186
+ eq27(backlinkDomains.release, targetRelease)
26017
26187
  );
26018
- const domainCondition = excludeCrawlers ? and20(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
26019
- const totalRow = app.db.select({ count: sql10`count(*)` }).from(backlinkDomains).where(domainCondition).get();
26188
+ const domainCondition = excludeCrawlers ? and21(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
26189
+ const totalRow = app.db.select({ count: sql11`count(*)` }).from(backlinkDomains).where(domainCondition).get();
26020
26190
  const rows = app.db.select({
26021
26191
  linkingDomain: backlinkDomains.linkingDomain,
26022
26192
  numHosts: backlinkDomains.numHosts,
@@ -26039,7 +26209,7 @@ async function backlinksRoutes(app, opts) {
26039
26209
  async (request, reply) => {
26040
26210
  const project = resolveProject(app.db, request.params.name);
26041
26211
  const source = parseSourceParam(request.query.source);
26042
- const rows = app.db.select().from(backlinkSummaries).where(and20(eq26(backlinkSummaries.projectId, project.id), eq26(backlinkSummaries.source, source))).orderBy(asc3(backlinkSummaries.queriedAt)).all();
26212
+ const rows = app.db.select().from(backlinkSummaries).where(and21(eq27(backlinkSummaries.projectId, project.id), eq27(backlinkSummaries.source, source))).orderBy(asc4(backlinkSummaries.queriedAt)).all();
26043
26213
  const response = rows.map((r) => ({
26044
26214
  release: r.release,
26045
26215
  totalLinkingDomains: r.totalLinkingDomains,
@@ -26086,7 +26256,7 @@ async function backlinksRoutes(app, opts) {
26086
26256
  createdAt: now
26087
26257
  }).run();
26088
26258
  opts.onBingBacklinkSyncRequested(runId, project.id);
26089
- const run = app.db.select().from(runs).where(eq26(runs.id, runId)).get();
26259
+ const run = app.db.select().from(runs).where(eq27(runs.id, runId)).get();
26090
26260
  return reply.status(201).send(mapRunRow(run));
26091
26261
  }
26092
26262
  );
@@ -26095,7 +26265,7 @@ async function backlinksRoutes(app, opts) {
26095
26265
  // ../api-routes/src/traffic.ts
26096
26266
  import crypto25 from "crypto";
26097
26267
  import { Agent as UndiciAgent } from "undici";
26098
- import { and as and21, desc as desc15, eq as eq27, gte as gte4, lte as lte3, sql as sql11 } from "drizzle-orm";
26268
+ import { and as and22, desc as desc15, eq as eq28, gte as gte4, lte as lte3, sql as sql12 } from "drizzle-orm";
26099
26269
 
26100
26270
  // ../integration-cloud-run/src/auth.ts
26101
26271
  import crypto24 from "crypto";
@@ -29297,14 +29467,18 @@ function trimOrNull(value) {
29297
29467
  function buildEventId2(event) {
29298
29468
  return `wordpress:${event.observed_at}:${event.id}`;
29299
29469
  }
29300
- function normalizeWordpressTrafficEvent(event) {
29470
+ function normalizeWordpressTrafficEvent(event, site) {
29301
29471
  if (!event.observed_at) return null;
29302
29472
  if (typeof event.id !== "number" || !Number.isFinite(event.id)) return null;
29303
- const path8 = event.path?.trim();
29473
+ if (!event.path) return null;
29474
+ const path8 = event.path.trim();
29304
29475
  if (!path8) return null;
29305
29476
  const queryString = trimOrNull(event.query_string);
29306
29477
  const host = trimOrNull(event.host);
29307
29478
  const requestUrl = host ? `https://${host}${path8}${queryString ? `?${queryString}` : ""}` : `${path8}${queryString ? `?${queryString}` : ""}`;
29479
+ const labels = {};
29480
+ if (host) labels.host = host;
29481
+ if (site?.anonymous_id) labels.anonymousId = site.anonymous_id;
29308
29482
  return {
29309
29483
  sourceType: TrafficSourceTypes.wordpress,
29310
29484
  evidenceKind: TrafficEvidenceKinds["raw-request"],
@@ -29325,7 +29499,7 @@ function normalizeWordpressTrafficEvent(event) {
29325
29499
  responseSizeBytes: null,
29326
29500
  providerResource: {
29327
29501
  type: "wordpress_site",
29328
- labels: host ? { host } : {}
29502
+ labels
29329
29503
  },
29330
29504
  providerLabels: {}
29331
29505
  };
@@ -29430,7 +29604,7 @@ async function listWordpressTrafficEvents(options) {
29430
29604
  const entries = body.events ?? [];
29431
29605
  rawEntryCount += entries.length;
29432
29606
  for (const entry of entries) {
29433
- const normalized = normalizeWordpressTrafficEvent(entry);
29607
+ const normalized = normalizeWordpressTrafficEvent(entry, body.site);
29434
29608
  if (normalized) {
29435
29609
  events.push(normalized);
29436
29610
  } else {
@@ -29912,8 +30086,8 @@ async function runBackfillTask(options) {
29912
30086
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
29913
30087
  try {
29914
30088
  app.db.transaction((tx) => {
29915
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq27(runs.id, runId)).run();
29916
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq27(trafficSources.id, sourceRow.id)).run();
30089
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq28(runs.id, runId)).run();
30090
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq28(trafficSources.id, sourceRow.id)).run();
29917
30091
  });
29918
30092
  } catch {
29919
30093
  }
@@ -29928,7 +30102,7 @@ async function runBackfillTask(options) {
29928
30102
  if (allEvents.length === 0) {
29929
30103
  const finishedAt2 = (/* @__PURE__ */ new Date()).toISOString();
29930
30104
  try {
29931
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq27(runs.id, runId)).run();
30105
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq28(runs.id, runId)).run();
29932
30106
  } catch {
29933
30107
  }
29934
30108
  return;
@@ -29950,29 +30124,29 @@ async function runBackfillTask(options) {
29950
30124
  try {
29951
30125
  app.db.transaction((tx) => {
29952
30126
  tx.delete(crawlerEventsHourly).where(
29953
- and21(
29954
- eq27(crawlerEventsHourly.sourceId, sourceRow.id),
30127
+ and22(
30128
+ eq28(crawlerEventsHourly.sourceId, sourceRow.id),
29955
30129
  gte4(crawlerEventsHourly.tsHour, windowStartIso),
29956
30130
  lte3(crawlerEventsHourly.tsHour, windowEndIso)
29957
30131
  )
29958
30132
  ).run();
29959
30133
  tx.delete(aiUserFetchEventsHourly).where(
29960
- and21(
29961
- eq27(aiUserFetchEventsHourly.sourceId, sourceRow.id),
30134
+ and22(
30135
+ eq28(aiUserFetchEventsHourly.sourceId, sourceRow.id),
29962
30136
  gte4(aiUserFetchEventsHourly.tsHour, windowStartIso),
29963
30137
  lte3(aiUserFetchEventsHourly.tsHour, windowEndIso)
29964
30138
  )
29965
30139
  ).run();
29966
30140
  tx.delete(aiReferralEventsHourly).where(
29967
- and21(
29968
- eq27(aiReferralEventsHourly.sourceId, sourceRow.id),
30141
+ and22(
30142
+ eq28(aiReferralEventsHourly.sourceId, sourceRow.id),
29969
30143
  gte4(aiReferralEventsHourly.tsHour, windowStartIso),
29970
30144
  lte3(aiReferralEventsHourly.tsHour, windowEndIso)
29971
30145
  )
29972
30146
  ).run();
29973
30147
  tx.delete(rawEventSamples).where(
29974
- and21(
29975
- eq27(rawEventSamples.sourceId, sourceRow.id),
30148
+ and22(
30149
+ eq28(rawEventSamples.sourceId, sourceRow.id),
29976
30150
  gte4(rawEventSamples.ts, windowStartIso),
29977
30151
  lte3(rawEventSamples.ts, windowEndIso)
29978
30152
  )
@@ -30061,8 +30235,8 @@ async function runBackfillTask(options) {
30061
30235
  lastError: null,
30062
30236
  lastEventIds: newRingBuffer,
30063
30237
  updatedAt: finishedAt
30064
- }).where(eq27(trafficSources.id, sourceRow.id)).run();
30065
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq27(runs.id, runId)).run();
30238
+ }).where(eq28(trafficSources.id, sourceRow.id)).run();
30239
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq28(runs.id, runId)).run();
30066
30240
  });
30067
30241
  } catch (e) {
30068
30242
  markFailed(`Backfill rollup write failed: ${e instanceof Error ? e.message : String(e)}`);
@@ -30144,7 +30318,7 @@ async function trafficRoutes(app, opts) {
30144
30318
  createdAt: existing?.createdAt ?? now,
30145
30319
  updatedAt: now
30146
30320
  });
30147
- 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);
30321
+ const activeSource = app.db.select().from(trafficSources).where(eq28(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes["cloud-run"] && row.status !== TrafficSourceStatuses.archived);
30148
30322
  const config = {
30149
30323
  gcpProjectId,
30150
30324
  serviceName: serviceName ?? null,
@@ -30160,8 +30334,8 @@ async function trafficRoutes(app, opts) {
30160
30334
  lastError: null,
30161
30335
  configJson: config,
30162
30336
  updatedAt: now
30163
- }).where(eq27(trafficSources.id, activeSource.id)).run();
30164
- sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, activeSource.id)).get();
30337
+ }).where(eq28(trafficSources.id, activeSource.id)).run();
30338
+ sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, activeSource.id)).get();
30165
30339
  } else {
30166
30340
  const newId = crypto25.randomUUID();
30167
30341
  app.db.insert(trafficSources).values({
@@ -30178,7 +30352,7 @@ async function trafficRoutes(app, opts) {
30178
30352
  createdAt: now,
30179
30353
  updatedAt: now
30180
30354
  }).run();
30181
- sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, newId)).get();
30355
+ sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, newId)).get();
30182
30356
  }
30183
30357
  writeAuditLog(app.db, {
30184
30358
  projectId: project.id,
@@ -30230,7 +30404,7 @@ async function trafficRoutes(app, opts) {
30230
30404
  createdAt: existing?.createdAt ?? now,
30231
30405
  updatedAt: now
30232
30406
  });
30233
- const activeSource = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
30407
+ const activeSource = app.db.select().from(trafficSources).where(eq28(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
30234
30408
  const config = { baseUrl, username };
30235
30409
  const fallbackName = displayName ?? `WordPress \xB7 ${new URL(baseUrl).host}`;
30236
30410
  let sourceRow;
@@ -30241,8 +30415,8 @@ async function trafficRoutes(app, opts) {
30241
30415
  lastError: null,
30242
30416
  configJson: config,
30243
30417
  updatedAt: now
30244
- }).where(eq27(trafficSources.id, activeSource.id)).run();
30245
- sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, activeSource.id)).get();
30418
+ }).where(eq28(trafficSources.id, activeSource.id)).run();
30419
+ sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, activeSource.id)).get();
30246
30420
  } else {
30247
30421
  const newId = crypto25.randomUUID();
30248
30422
  app.db.insert(trafficSources).values({
@@ -30259,7 +30433,7 @@ async function trafficRoutes(app, opts) {
30259
30433
  createdAt: now,
30260
30434
  updatedAt: now
30261
30435
  }).run();
30262
- sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, newId)).get();
30436
+ sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, newId)).get();
30263
30437
  }
30264
30438
  writeAuditLog(app.db, {
30265
30439
  projectId: project.id,
@@ -30313,7 +30487,7 @@ async function trafficRoutes(app, opts) {
30313
30487
  createdAt: existing?.createdAt ?? now,
30314
30488
  updatedAt: now
30315
30489
  });
30316
- const activeSource = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.vercel && row.status !== TrafficSourceStatuses.archived);
30490
+ const activeSource = app.db.select().from(trafficSources).where(eq28(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.vercel && row.status !== TrafficSourceStatuses.archived);
30317
30491
  const config = { projectId, teamId, environment };
30318
30492
  const fallbackName = displayName ?? `Vercel \xB7 ${projectId}`;
30319
30493
  const { sourceRow, scheduleCreated } = app.db.transaction((tx) => {
@@ -30325,8 +30499,8 @@ async function trafficRoutes(app, opts) {
30325
30499
  lastError: null,
30326
30500
  configJson: config,
30327
30501
  updatedAt: now
30328
- }).where(eq27(trafficSources.id, activeSource.id)).run();
30329
- row = tx.select().from(trafficSources).where(eq27(trafficSources.id, activeSource.id)).get();
30502
+ }).where(eq28(trafficSources.id, activeSource.id)).run();
30503
+ row = tx.select().from(trafficSources).where(eq28(trafficSources.id, activeSource.id)).get();
30330
30504
  } else {
30331
30505
  const newId = crypto25.randomUUID();
30332
30506
  tx.insert(trafficSources).values({
@@ -30351,12 +30525,12 @@ async function trafficRoutes(app, opts) {
30351
30525
  createdAt: now,
30352
30526
  updatedAt: now
30353
30527
  }).run();
30354
- row = tx.select().from(trafficSources).where(eq27(trafficSources.id, newId)).get();
30528
+ row = tx.select().from(trafficSources).where(eq28(trafficSources.id, newId)).get();
30355
30529
  }
30356
30530
  const existingSchedule = tx.select().from(schedules).where(
30357
- and21(
30358
- eq27(schedules.projectId, project.id),
30359
- eq27(schedules.kind, SchedulableRunKinds["traffic-sync"])
30531
+ and22(
30532
+ eq28(schedules.projectId, project.id),
30533
+ eq28(schedules.kind, SchedulableRunKinds["traffic-sync"])
30360
30534
  )
30361
30535
  ).get();
30362
30536
  let created = false;
@@ -30405,7 +30579,7 @@ async function trafficRoutes(app, opts) {
30405
30579
  });
30406
30580
  app.post("/projects/:name/traffic/sources/:id/sync", async (request) => {
30407
30581
  const project = resolveProject(app.db, request.params.name);
30408
- const sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, request.params.id)).get();
30582
+ const sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, request.params.id)).get();
30409
30583
  if (!sourceRow || sourceRow.projectId !== project.id) {
30410
30584
  throw notFound("Traffic source", request.params.id);
30411
30585
  }
@@ -30431,8 +30605,8 @@ async function trafficRoutes(app, opts) {
30431
30605
  const markFailed = (msg, errorCode) => {
30432
30606
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
30433
30607
  app.db.transaction((tx) => {
30434
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq27(runs.id, runId)).run();
30435
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq27(trafficSources.id, sourceRow.id)).run();
30608
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq28(runs.id, runId)).run();
30609
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq28(trafficSources.id, sourceRow.id)).run();
30436
30610
  });
30437
30611
  try {
30438
30612
  opts.onTrafficSynced?.({
@@ -30513,7 +30687,7 @@ async function trafficRoutes(app, opts) {
30513
30687
  }
30514
30688
  const credential = credentialStore.getConnection(project.name);
30515
30689
  if (!credential) {
30516
- app.db.delete(runs).where(eq27(runs.id, runId)).run();
30690
+ app.db.delete(runs).where(eq28(runs.id, runId)).run();
30517
30691
  throw validationError(
30518
30692
  `No WordPress credential found for project "${project.name}". Run "canonry traffic connect wordpress" first.`
30519
30693
  );
@@ -30562,12 +30736,12 @@ async function trafficRoutes(app, opts) {
30562
30736
  auditAction = "traffic.vercel.synced";
30563
30737
  const credentialStore = opts.vercelTrafficCredentialStore;
30564
30738
  if (!credentialStore) {
30565
- app.db.delete(runs).where(eq27(runs.id, runId)).run();
30739
+ app.db.delete(runs).where(eq28(runs.id, runId)).run();
30566
30740
  throw validationError("Vercel traffic credential storage is not configured for this deployment");
30567
30741
  }
30568
30742
  const credential = credentialStore.getConnection(project.name);
30569
30743
  if (!credential) {
30570
- app.db.delete(runs).where(eq27(runs.id, runId)).run();
30744
+ app.db.delete(runs).where(eq28(runs.id, runId)).run();
30571
30745
  throw validationError(
30572
30746
  `No Vercel credential found for project "${project.name}". Run "canonry traffic connect vercel" first.`
30573
30747
  );
@@ -30667,7 +30841,7 @@ async function trafficRoutes(app, opts) {
30667
30841
  let aiReferralHitsCount = 0;
30668
30842
  let unknownHitsCount = 0;
30669
30843
  app.db.transaction((tx) => {
30670
- const latestRow = tx.select().from(trafficSources).where(eq27(trafficSources.id, sourceRow.id)).get();
30844
+ const latestRow = tx.select().from(trafficSources).where(eq28(trafficSources.id, sourceRow.id)).get();
30671
30845
  const previousIds = latestRow.lastEventIds ?? [];
30672
30846
  const seenEventIds = new Set(previousIds);
30673
30847
  const dedupedEvents = seenEventIds.size === 0 ? allEvents : allEvents.filter((e) => !seenEventIds.has(e.eventId));
@@ -30715,7 +30889,7 @@ async function trafficRoutes(app, opts) {
30715
30889
  crawlerEventsHourly.status
30716
30890
  ],
30717
30891
  set: {
30718
- hits: sql11`${crawlerEventsHourly.hits} + ${bucket.hits}`,
30892
+ hits: sql12`${crawlerEventsHourly.hits} + ${bucket.hits}`,
30719
30893
  sampledUserAgent: bucket.sampledUserAgent,
30720
30894
  updatedAt: finishedAt
30721
30895
  }
@@ -30748,7 +30922,7 @@ async function trafficRoutes(app, opts) {
30748
30922
  aiUserFetchEventsHourly.status
30749
30923
  ],
30750
30924
  set: {
30751
- hits: sql11`${aiUserFetchEventsHourly.hits} + ${bucket.hits}`,
30925
+ hits: sql12`${aiUserFetchEventsHourly.hits} + ${bucket.hits}`,
30752
30926
  sampledUserAgent: bucket.sampledUserAgent,
30753
30927
  updatedAt: finishedAt
30754
30928
  }
@@ -30783,7 +30957,7 @@ async function trafficRoutes(app, opts) {
30783
30957
  aiReferralEventsHourly.status
30784
30958
  ],
30785
30959
  set: {
30786
- sessionsOrHits: sql11`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
30960
+ sessionsOrHits: sql12`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
30787
30961
  updatedAt: finishedAt
30788
30962
  }
30789
30963
  }).run();
@@ -30835,8 +31009,8 @@ async function trafficRoutes(app, opts) {
30835
31009
  if (sourceRow.sourceType === TrafficSourceTypes.wordpress) {
30836
31010
  sourceUpdate.lastCursor = nextCursor ?? null;
30837
31011
  }
30838
- tx.update(trafficSources).set(sourceUpdate).where(eq27(trafficSources.id, sourceRow.id)).run();
30839
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq27(runs.id, runId)).run();
31012
+ tx.update(trafficSources).set(sourceUpdate).where(eq28(trafficSources.id, sourceRow.id)).run();
31013
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq28(runs.id, runId)).run();
30840
31014
  });
30841
31015
  writeAuditLog(app.db, {
30842
31016
  projectId: project.id,
@@ -30888,7 +31062,7 @@ async function trafficRoutes(app, opts) {
30888
31062
  });
30889
31063
  app.post("/projects/:name/traffic/sources/:id/backfill", async (request) => {
30890
31064
  const project = resolveProject(app.db, request.params.name);
30891
- const sourceRow = app.db.select().from(trafficSources).where(eq27(trafficSources.id, request.params.id)).get();
31065
+ const sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, request.params.id)).get();
30892
31066
  if (!sourceRow || sourceRow.projectId !== project.id) {
30893
31067
  throw notFound("Traffic source", request.params.id);
30894
31068
  }
@@ -31074,10 +31248,10 @@ async function trafficRoutes(app, opts) {
31074
31248
  function buildSourceDetail(projectId, row, since) {
31075
31249
  const crawlerPathRows = app.db.select({
31076
31250
  pathNormalized: crawlerEventsHourly.pathNormalized,
31077
- hits: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
31251
+ hits: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
31078
31252
  }).from(crawlerEventsHourly).where(
31079
- and21(
31080
- eq27(crawlerEventsHourly.sourceId, row.id),
31253
+ and22(
31254
+ eq28(crawlerEventsHourly.sourceId, row.id),
31081
31255
  gte4(crawlerEventsHourly.tsHour, since)
31082
31256
  )
31083
31257
  ).groupBy(crawlerEventsHourly.pathNormalized).all();
@@ -31085,29 +31259,29 @@ async function trafficRoutes(app, opts) {
31085
31259
  crawlerPathRows.map((r) => ({ pathNormalized: r.pathNormalized, hits: Number(r.hits) }))
31086
31260
  );
31087
31261
  const crawlerTotal = crawlerSegments.content + crawlerSegments.sitemap + crawlerSegments.robots + crawlerSegments.asset + crawlerSegments.other;
31088
- const aiUserFetchTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
31089
- and21(
31090
- eq27(aiUserFetchEventsHourly.sourceId, row.id),
31262
+ const aiUserFetchTotals = app.db.select({ total: sql12`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
31263
+ and22(
31264
+ eq28(aiUserFetchEventsHourly.sourceId, row.id),
31091
31265
  gte4(aiUserFetchEventsHourly.tsHour, since)
31092
31266
  )
31093
31267
  ).get();
31094
- const aiTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
31095
- and21(
31096
- eq27(aiReferralEventsHourly.sourceId, row.id),
31268
+ const aiTotals = app.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
31269
+ and22(
31270
+ eq28(aiReferralEventsHourly.sourceId, row.id),
31097
31271
  gte4(aiReferralEventsHourly.tsHour, since)
31098
31272
  )
31099
31273
  ).get();
31100
- const sampleTotals = app.db.select({ total: sql11`COUNT(*)` }).from(rawEventSamples).where(
31101
- and21(
31102
- eq27(rawEventSamples.sourceId, row.id),
31274
+ const sampleTotals = app.db.select({ total: sql12`COUNT(*)` }).from(rawEventSamples).where(
31275
+ and22(
31276
+ eq28(rawEventSamples.sourceId, row.id),
31103
31277
  gte4(rawEventSamples.ts, since)
31104
31278
  )
31105
31279
  ).get();
31106
31280
  const latestRun = app.db.select().from(runs).where(
31107
- and21(
31108
- eq27(runs.projectId, projectId),
31109
- eq27(runs.kind, RunKinds["traffic-sync"]),
31110
- eq27(runs.sourceId, row.id)
31281
+ and22(
31282
+ eq28(runs.projectId, projectId),
31283
+ eq28(runs.kind, RunKinds["traffic-sync"]),
31284
+ eq28(runs.sourceId, row.id)
31111
31285
  )
31112
31286
  ).orderBy(desc15(runs.startedAt)).limit(1).get();
31113
31287
  return {
@@ -31138,7 +31312,7 @@ async function trafficRoutes(app, opts) {
31138
31312
  "`advanceToNow` must be `true`. There is no implicit reset."
31139
31313
  );
31140
31314
  }
31141
- const sourceRow = app.db.select().from(trafficSources).where(and21(eq27(trafficSources.projectId, project.id), eq27(trafficSources.id, request.params.id))).get();
31315
+ const sourceRow = app.db.select().from(trafficSources).where(and22(eq28(trafficSources.projectId, project.id), eq28(trafficSources.id, request.params.id))).get();
31142
31316
  if (!sourceRow) {
31143
31317
  throw notFound("traffic source", request.params.id);
31144
31318
  }
@@ -31155,7 +31329,7 @@ async function trafficRoutes(app, opts) {
31155
31329
  status: TrafficSourceStatuses.connected,
31156
31330
  lastError: null,
31157
31331
  updatedAt: now
31158
- }).where(eq27(trafficSources.id, sourceRow.id)).run();
31332
+ }).where(eq28(trafficSources.id, sourceRow.id)).run();
31159
31333
  writeAuditLog(tx, auditFromRequest(request, {
31160
31334
  projectId: project.id,
31161
31335
  actor: "api",
@@ -31163,20 +31337,20 @@ async function trafficRoutes(app, opts) {
31163
31337
  entityType: "traffic_source",
31164
31338
  entityId: sourceRow.id
31165
31339
  }));
31166
- updatedRow = tx.select().from(trafficSources).where(eq27(trafficSources.id, sourceRow.id)).get();
31340
+ updatedRow = tx.select().from(trafficSources).where(eq28(trafficSources.id, sourceRow.id)).get();
31167
31341
  });
31168
31342
  return buildSourceDetail(project.id, updatedRow, new Date(Date.now() - 24 * 60 * 6e4).toISOString());
31169
31343
  });
31170
31344
  app.get("/projects/:name/traffic/sources", async (request) => {
31171
31345
  const project = resolveProject(app.db, request.params.name);
31172
- const rows = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
31346
+ const rows = app.db.select().from(trafficSources).where(eq28(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
31173
31347
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map(rowToDto);
31174
31348
  const response = { sources };
31175
31349
  return response;
31176
31350
  });
31177
31351
  app.get("/projects/:name/traffic/status", async (request) => {
31178
31352
  const project = resolveProject(app.db, request.params.name);
31179
- const rows = app.db.select().from(trafficSources).where(eq27(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
31353
+ const rows = app.db.select().from(trafficSources).where(eq28(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
31180
31354
  const since = new Date(Date.now() - 24 * 60 * 6e4).toISOString();
31181
31355
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map((row) => buildSourceDetail(project.id, row, since));
31182
31356
  const response = { sources };
@@ -31186,7 +31360,7 @@ async function trafficRoutes(app, opts) {
31186
31360
  "/projects/:name/traffic/sources/:id",
31187
31361
  async (request) => {
31188
31362
  const project = resolveProject(app.db, request.params.name);
31189
- const row = app.db.select().from(trafficSources).where(eq27(trafficSources.id, request.params.id)).get();
31363
+ const row = app.db.select().from(trafficSources).where(eq28(trafficSources.id, request.params.id)).get();
31190
31364
  if (!row || row.projectId !== project.id) {
31191
31365
  throw notFound("Traffic source", request.params.id);
31192
31366
  }
@@ -31238,15 +31412,15 @@ async function trafficRoutes(app, opts) {
31238
31412
  let aiReferralTotal = 0;
31239
31413
  if (kind === "all" || kind === TrafficEventKinds.crawler) {
31240
31414
  const crawlerFilters = [
31241
- eq27(crawlerEventsHourly.projectId, project.id),
31415
+ eq28(crawlerEventsHourly.projectId, project.id),
31242
31416
  gte4(crawlerEventsHourly.tsHour, sinceIso),
31243
31417
  lte3(crawlerEventsHourly.tsHour, untilIso)
31244
31418
  ];
31245
- if (sourceIdParam) crawlerFilters.push(eq27(crawlerEventsHourly.sourceId, sourceIdParam));
31246
- const crawlerWhere = and21(...crawlerFilters);
31419
+ if (sourceIdParam) crawlerFilters.push(eq28(crawlerEventsHourly.sourceId, sourceIdParam));
31420
+ const crawlerWhere = and22(...crawlerFilters);
31247
31421
  const pathTotals = app.db.select({
31248
31422
  pathNormalized: crawlerEventsHourly.pathNormalized,
31249
- hits: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
31423
+ hits: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
31250
31424
  }).from(crawlerEventsHourly).where(crawlerWhere).groupBy(crawlerEventsHourly.pathNormalized).all();
31251
31425
  crawlerSegments = segmentCrawlerHits(
31252
31426
  pathTotals.map((r) => ({ pathNormalized: r.pathNormalized, hits: Number(r.hits) }))
@@ -31270,13 +31444,13 @@ async function trafficRoutes(app, opts) {
31270
31444
  }
31271
31445
  if (kind === "all" || kind === TrafficEventKinds["ai-user-fetch"]) {
31272
31446
  const userFetchFilters = [
31273
- eq27(aiUserFetchEventsHourly.projectId, project.id),
31447
+ eq28(aiUserFetchEventsHourly.projectId, project.id),
31274
31448
  gte4(aiUserFetchEventsHourly.tsHour, sinceIso),
31275
31449
  lte3(aiUserFetchEventsHourly.tsHour, untilIso)
31276
31450
  ];
31277
- if (sourceIdParam) userFetchFilters.push(eq27(aiUserFetchEventsHourly.sourceId, sourceIdParam));
31278
- const userFetchWhere = and21(...userFetchFilters);
31279
- const total = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(userFetchWhere).get();
31451
+ if (sourceIdParam) userFetchFilters.push(eq28(aiUserFetchEventsHourly.sourceId, sourceIdParam));
31452
+ const userFetchWhere = and22(...userFetchFilters);
31453
+ const total = app.db.select({ total: sql12`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(userFetchWhere).get();
31280
31454
  aiUserFetchTotal = Number(total?.total ?? 0);
31281
31455
  const rows = app.db.select().from(aiUserFetchEventsHourly).where(userFetchWhere).orderBy(desc15(aiUserFetchEventsHourly.tsHour)).limit(limit).all();
31282
31456
  for (const r of rows) {
@@ -31295,13 +31469,13 @@ async function trafficRoutes(app, opts) {
31295
31469
  }
31296
31470
  if (kind === "all" || kind === TrafficEventKinds["ai-referral"]) {
31297
31471
  const aiFilters = [
31298
- eq27(aiReferralEventsHourly.projectId, project.id),
31472
+ eq28(aiReferralEventsHourly.projectId, project.id),
31299
31473
  gte4(aiReferralEventsHourly.tsHour, sinceIso),
31300
31474
  lte3(aiReferralEventsHourly.tsHour, untilIso)
31301
31475
  ];
31302
- if (sourceIdParam) aiFilters.push(eq27(aiReferralEventsHourly.sourceId, sourceIdParam));
31303
- const aiWhere = and21(...aiFilters);
31304
- const total = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
31476
+ if (sourceIdParam) aiFilters.push(eq28(aiReferralEventsHourly.sourceId, sourceIdParam));
31477
+ const aiWhere = and22(...aiFilters);
31478
+ const total = app.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
31305
31479
  aiReferralTotal = Number(total?.total ?? 0);
31306
31480
  const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc15(aiReferralEventsHourly.tsHour)).limit(limit).all();
31307
31481
  for (const r of rows) {
@@ -31507,7 +31681,7 @@ function readInstalledManifest(skillDir) {
31507
31681
  var AGENT_CHECKS = [skillsInstalledCheck, skillsCurrentCheck];
31508
31682
 
31509
31683
  // ../api-routes/src/doctor/checks/backlinks.ts
31510
- import { and as and22, eq as eq28 } from "drizzle-orm";
31684
+ import { and as and23, eq as eq29 } from "drizzle-orm";
31511
31685
  function skippedNoProject() {
31512
31686
  return {
31513
31687
  status: CheckStatuses.skipped,
@@ -31523,8 +31697,8 @@ var BACKLINKS_CHECKS = [
31523
31697
  title: "Backlinks source connected",
31524
31698
  run: (ctx) => {
31525
31699
  if (!ctx.project) return skippedNoProject();
31526
- const projectRow = ctx.db.select({ autoExtract: projects.autoExtractBacklinks }).from(projects).where(eq28(projects.id, ctx.project.id)).get();
31527
- const readySync = ctx.db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq28(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
31700
+ const projectRow = ctx.db.select({ autoExtract: projects.autoExtractBacklinks }).from(projects).where(eq29(projects.id, ctx.project.id)).get();
31701
+ const readySync = ctx.db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq29(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
31528
31702
  const ccConnected = projectRow?.autoExtract === true && !!readySync;
31529
31703
  const bingConnected = !!ctx.bingConnectionStore?.getConnection(ctx.project.canonicalDomain);
31530
31704
  const connected = [];
@@ -31539,9 +31713,9 @@ var BACKLINKS_CHECKS = [
31539
31713
  details: { commoncrawl: ccConnected, bingWebmaster: bingConnected }
31540
31714
  };
31541
31715
  }
31542
- const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and22(
31543
- eq28(backlinkSummaries.projectId, ctx.project.id),
31544
- eq28(backlinkSummaries.source, BacklinkSources.commoncrawl)
31716
+ const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and23(
31717
+ eq29(backlinkSummaries.projectId, ctx.project.id),
31718
+ eq29(backlinkSummaries.source, BacklinkSources.commoncrawl)
31545
31719
  )).limit(1).get() : false;
31546
31720
  return {
31547
31721
  status: CheckStatuses.ok,
@@ -31701,7 +31875,7 @@ var BING_AUTH_CHECKS = [
31701
31875
  ];
31702
31876
 
31703
31877
  // ../api-routes/src/doctor/checks/content.ts
31704
- import { eq as eq29 } from "drizzle-orm";
31878
+ import { eq as eq30 } from "drizzle-orm";
31705
31879
  var WINNABILITY_COVERAGE_WARN_THRESHOLD = 0.8;
31706
31880
  var UNCLASSIFIED_DOMAIN_SAMPLE_LIMIT = 10;
31707
31881
  function skippedNoProject2() {
@@ -31714,7 +31888,7 @@ function skippedNoProject2() {
31714
31888
  }
31715
31889
  function loadProject(ctx) {
31716
31890
  if (!ctx.project) return null;
31717
- return ctx.db.select().from(projects).where(eq29(projects.id, ctx.project.id)).get() ?? null;
31891
+ return ctx.db.select().from(projects).where(eq30(projects.id, ctx.project.id)).get() ?? null;
31718
31892
  }
31719
31893
  function percent(value) {
31720
31894
  return Math.round(value * 100);
@@ -31806,7 +31980,7 @@ var CONTENT_CHECK_BY_ID = Object.fromEntries(
31806
31980
  );
31807
31981
 
31808
31982
  // ../api-routes/src/doctor/checks/ads.ts
31809
- import { eq as eq30 } from "drizzle-orm";
31983
+ import { eq as eq31 } from "drizzle-orm";
31810
31984
  var RECENT_SYNC_WARN_DAYS = 7;
31811
31985
  var RECENT_SYNC_FAIL_DAYS = 30;
31812
31986
  var adsConnectionCheck = {
@@ -31823,7 +31997,7 @@ var adsConnectionCheck = {
31823
31997
  remediation: null
31824
31998
  };
31825
31999
  }
31826
- const row = ctx.db.select().from(adsConnections).where(eq30(adsConnections.projectId, ctx.project.id)).get();
32000
+ const row = ctx.db.select().from(adsConnections).where(eq31(adsConnections.projectId, ctx.project.id)).get();
31827
32001
  if (!row) {
31828
32002
  return {
31829
32003
  status: CheckStatuses.skipped,
@@ -31873,7 +32047,7 @@ var adsRecentSyncCheck = {
31873
32047
  remediation: null
31874
32048
  };
31875
32049
  }
31876
- const row = ctx.db.select().from(adsConnections).where(eq30(adsConnections.projectId, ctx.project.id)).get();
32050
+ const row = ctx.db.select().from(adsConnections).where(eq31(adsConnections.projectId, ctx.project.id)).get();
31877
32051
  if (!row) {
31878
32052
  return {
31879
32053
  status: CheckStatuses.skipped,
@@ -32064,7 +32238,7 @@ var ga4ConnectionCheck = {
32064
32238
  var GA_AUTH_CHECKS = [ga4ConnectionCheck];
32065
32239
 
32066
32240
  // ../api-routes/src/doctor/checks/gbp-auth.ts
32067
- import { and as and23, eq as eq31 } from "drizzle-orm";
32241
+ import { and as and24, eq as eq32 } from "drizzle-orm";
32068
32242
  var RECENT_SYNC_WARN_DAYS2 = 7;
32069
32243
  var RECENT_SYNC_FAIL_DAYS2 = 30;
32070
32244
  function skippedNoProject3() {
@@ -32297,7 +32471,7 @@ var recentSyncCheck = {
32297
32471
  title: "GBP recent sync",
32298
32472
  run: (ctx) => {
32299
32473
  if (!ctx.project) return skippedNoProject3();
32300
- 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();
32474
+ const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and24(eq32(gbpLocations.projectId, ctx.project.id), eq32(gbpLocations.selected, true))).all();
32301
32475
  if (selected.length === 0) {
32302
32476
  return {
32303
32477
  status: CheckStatuses.skipped,
@@ -32357,7 +32531,7 @@ var GBP_AUTH_CHECK_BY_ID = Object.fromEntries(
32357
32531
  );
32358
32532
 
32359
32533
  // ../api-routes/src/doctor/checks/places.ts
32360
- import { eq as eq32 } from "drizzle-orm";
32534
+ import { eq as eq33 } from "drizzle-orm";
32361
32535
  var apiKeyCheck = {
32362
32536
  id: "gbp.places.api-key",
32363
32537
  category: CheckCategories.auth,
@@ -32402,7 +32576,7 @@ var apiKeyCheck = {
32402
32576
  details: { tier: cfg.tier }
32403
32577
  };
32404
32578
  }
32405
- const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq32(gbpLocations.projectId, ctx.project.id)).all();
32579
+ const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq33(gbpLocations.projectId, ctx.project.id)).all();
32406
32580
  const selected = rows.filter((r) => r.selected);
32407
32581
  const locationsWithPlaceId = selected.filter((r) => Boolean(r.placeId)).length;
32408
32582
  const details = {
@@ -32899,7 +33073,7 @@ var RUNTIME_STATE_CHECKS = [
32899
33073
  ];
32900
33074
 
32901
33075
  // ../api-routes/src/doctor/checks/traffic-source.ts
32902
- import { and as and24, eq as eq33, gte as gte5, ne as ne4, sql as sql12 } from "drizzle-orm";
33076
+ import { and as and25, eq as eq34, gte as gte5, ne as ne4, sql as sql13 } from "drizzle-orm";
32903
33077
  var RECENT_DATA_WARN_DAYS = 7;
32904
33078
  var RECENT_DATA_FAIL_DAYS = 30;
32905
33079
  function skippedNoProject5() {
@@ -32913,8 +33087,8 @@ function skippedNoProject5() {
32913
33087
  function loadProbes(ctx) {
32914
33088
  if (!ctx.project) return [];
32915
33089
  const rows = ctx.db.select().from(trafficSources).where(
32916
- and24(
32917
- eq33(trafficSources.projectId, ctx.project.id),
33090
+ and25(
33091
+ eq34(trafficSources.projectId, ctx.project.id),
32918
33092
  ne4(trafficSources.status, TrafficSourceStatuses.archived)
32919
33093
  )
32920
33094
  ).all();
@@ -32993,17 +33167,17 @@ var recentDataCheck = {
32993
33167
  const warnCutoff = new Date(now.getTime() - RECENT_DATA_WARN_DAYS * 24 * 60 * 6e4).toISOString();
32994
33168
  const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
32995
33169
  const recentCrawlers = Number(
32996
- ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
32997
- and24(
32998
- eq33(crawlerEventsHourly.projectId, ctx.project.id),
33170
+ ctx.db.select({ total: sql13`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
33171
+ and25(
33172
+ eq34(crawlerEventsHourly.projectId, ctx.project.id),
32999
33173
  gte5(crawlerEventsHourly.tsHour, warnCutoff)
33000
33174
  )
33001
33175
  ).get()?.total ?? 0
33002
33176
  );
33003
33177
  const recentReferrals = Number(
33004
- ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
33005
- and24(
33006
- eq33(aiReferralEventsHourly.projectId, ctx.project.id),
33178
+ ctx.db.select({ total: sql13`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
33179
+ and25(
33180
+ eq34(aiReferralEventsHourly.projectId, ctx.project.id),
33007
33181
  gte5(aiReferralEventsHourly.tsHour, warnCutoff)
33008
33182
  )
33009
33183
  ).get()?.total ?? 0
@@ -33017,17 +33191,17 @@ var recentDataCheck = {
33017
33191
  };
33018
33192
  }
33019
33193
  const olderCrawlers = Number(
33020
- ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
33021
- and24(
33022
- eq33(crawlerEventsHourly.projectId, ctx.project.id),
33194
+ ctx.db.select({ total: sql13`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
33195
+ and25(
33196
+ eq34(crawlerEventsHourly.projectId, ctx.project.id),
33023
33197
  gte5(crawlerEventsHourly.tsHour, failCutoff)
33024
33198
  )
33025
33199
  ).get()?.total ?? 0
33026
33200
  );
33027
33201
  const olderReferrals = Number(
33028
- ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
33029
- and24(
33030
- eq33(aiReferralEventsHourly.projectId, ctx.project.id),
33202
+ ctx.db.select({ total: sql13`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
33203
+ and25(
33204
+ eq34(aiReferralEventsHourly.projectId, ctx.project.id),
33031
33205
  gte5(aiReferralEventsHourly.tsHour, failCutoff)
33032
33206
  )
33033
33207
  ).get()?.total ?? 0
@@ -33443,7 +33617,7 @@ async function doctorRoutes(app, opts) {
33443
33617
 
33444
33618
  // ../api-routes/src/discovery/routes.ts
33445
33619
  import crypto27 from "crypto";
33446
- import { and as and25, desc as desc16, eq as eq34, gte as gte6, inArray as inArray12 } from "drizzle-orm";
33620
+ import { and as and26, desc as desc16, eq as eq35, gte as gte6, inArray as inArray12 } from "drizzle-orm";
33447
33621
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
33448
33622
  async function discoveryRoutes(app, opts) {
33449
33623
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -33475,9 +33649,9 @@ async function discoveryRoutes(app, opts) {
33475
33649
  const now = (/* @__PURE__ */ new Date()).toISOString();
33476
33650
  const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
33477
33651
  const decision = app.db.transaction((tx) => {
33478
- const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and25(
33479
- eq34(discoverySessions.projectId, project.id),
33480
- eq34(discoverySessions.icpDescription, icpDescription),
33652
+ const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and26(
33653
+ eq35(discoverySessions.projectId, project.id),
33654
+ eq35(discoverySessions.icpDescription, icpDescription),
33481
33655
  inArray12(discoverySessions.status, [
33482
33656
  DiscoverySessionStatuses.queued,
33483
33657
  DiscoverySessionStatuses.seeding,
@@ -33547,7 +33721,7 @@ async function discoveryRoutes(app, opts) {
33547
33721
  const project = resolveProject(app.db, request.params.name);
33548
33722
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
33549
33723
  const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? 50 : parsedLimit;
33550
- const rows = app.db.select().from(discoverySessions).where(eq34(discoverySessions.projectId, project.id)).orderBy(desc16(discoverySessions.createdAt)).limit(limit).all();
33724
+ const rows = app.db.select().from(discoverySessions).where(eq35(discoverySessions.projectId, project.id)).orderBy(desc16(discoverySessions.createdAt)).limit(limit).all();
33551
33725
  return reply.send(rows.map(serializeSession));
33552
33726
  }
33553
33727
  );
@@ -33555,11 +33729,11 @@ async function discoveryRoutes(app, opts) {
33555
33729
  "/projects/:name/discover/sessions/:id",
33556
33730
  async (request, reply) => {
33557
33731
  const project = resolveProject(app.db, request.params.name);
33558
- const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
33732
+ const session = app.db.select().from(discoverySessions).where(eq35(discoverySessions.id, request.params.id)).get();
33559
33733
  if (!session || session.projectId !== project.id) {
33560
33734
  throw notFound("Discovery session", request.params.id);
33561
33735
  }
33562
- const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
33736
+ const probeRows = app.db.select().from(discoveryProbes).where(eq35(discoveryProbes.sessionId, session.id)).all();
33563
33737
  const detail = {
33564
33738
  ...serializeSession(session),
33565
33739
  probes: probeRows.map(serializeProbe)
@@ -33571,7 +33745,7 @@ async function discoveryRoutes(app, opts) {
33571
33745
  "/projects/:name/discover/sessions/:id/harvest",
33572
33746
  async (request, reply) => {
33573
33747
  const project = resolveProject(app.db, request.params.name);
33574
- const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
33748
+ const session = app.db.select().from(discoverySessions).where(eq35(discoverySessions.id, request.params.id)).get();
33575
33749
  if (!session || session.projectId !== project.id) {
33576
33750
  throw notFound("Discovery session", request.params.id);
33577
33751
  }
@@ -33579,7 +33753,7 @@ async function discoveryRoutes(app, opts) {
33579
33753
  const minProbeHits = Number.isNaN(parsedFloor) || parsedFloor < 1 ? 1 : parsedFloor;
33580
33754
  const applyAnchor = request.query.anchor !== "false";
33581
33755
  const provider = session.seedProvider ?? "gemini";
33582
- const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
33756
+ const probeRows = app.db.select().from(discoveryProbes).where(eq35(discoveryProbes.sessionId, session.id)).all();
33583
33757
  const extract = opts.harvestSearchQueries;
33584
33758
  const probesWithQueries = probeRows.map((row) => {
33585
33759
  if (!extract || !row.rawResponse) return { searchQueries: [] };
@@ -33590,7 +33764,7 @@ async function discoveryRoutes(app, opts) {
33590
33764
  return { searchQueries: [] };
33591
33765
  }
33592
33766
  });
33593
- const trackedQueries = app.db.select({ query: queries.query }).from(queries).where(eq34(queries.projectId, project.id)).all().map((r) => r.query);
33767
+ const trackedQueries = app.db.select({ query: queries.query }).from(queries).where(eq35(queries.projectId, project.id)).all().map((r) => r.query);
33594
33768
  const anchorTerms = buildHarvestAnchorTerms(
33595
33769
  [session.icpDescription ?? "", ...trackedQueries],
33596
33770
  effectiveDomains(project)
@@ -33637,12 +33811,12 @@ async function discoveryRoutes(app, opts) {
33637
33811
  "/projects/:name/discover/sessions/:id/promote",
33638
33812
  async (request, reply) => {
33639
33813
  const project = resolveProject(app.db, request.params.name);
33640
- const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
33814
+ const session = app.db.select().from(discoverySessions).where(eq35(discoverySessions.id, request.params.id)).get();
33641
33815
  if (!session || session.projectId !== project.id) {
33642
33816
  throw notFound("Discovery session", request.params.id);
33643
33817
  }
33644
- const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
33645
- const existingCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase());
33818
+ const probeRows = app.db.select().from(discoveryProbes).where(eq35(discoveryProbes.sessionId, session.id)).all();
33819
+ const existingCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq35(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase());
33646
33820
  const seenCompetitors = new Set(existingCompetitors);
33647
33821
  const cited = /* @__PURE__ */ new Set();
33648
33822
  const aspirational = /* @__PURE__ */ new Set();
@@ -33671,7 +33845,7 @@ async function discoveryRoutes(app, opts) {
33671
33845
  );
33672
33846
  app.post("/projects/:name/discover/sessions/:id/promote", async (request, reply) => {
33673
33847
  const project = resolveProject(app.db, request.params.name);
33674
- const session = app.db.select().from(discoverySessions).where(eq34(discoverySessions.id, request.params.id)).get();
33848
+ const session = app.db.select().from(discoverySessions).where(eq35(discoverySessions.id, request.params.id)).get();
33675
33849
  if (!session || session.projectId !== project.id) {
33676
33850
  throw notFound("Discovery session", request.params.id);
33677
33851
  }
@@ -33694,7 +33868,7 @@ async function discoveryRoutes(app, opts) {
33694
33868
  const bucketSet = new Set(buckets);
33695
33869
  const includeCompetitors = parsed.data.includeCompetitors ?? true;
33696
33870
  const competitorTypes = parsed.data.competitorTypes ?? DEFAULT_DISCOVERY_PROMOTE_COMPETITOR_TYPES;
33697
- const probeRows = app.db.select().from(discoveryProbes).where(eq34(discoveryProbes.sessionId, session.id)).all();
33871
+ const probeRows = app.db.select().from(discoveryProbes).where(eq35(discoveryProbes.sessionId, session.id)).all();
33698
33872
  const candidateQueries = /* @__PURE__ */ new Set();
33699
33873
  for (const probe of probeRows) {
33700
33874
  if (!probe.bucket) continue;
@@ -33702,7 +33876,7 @@ async function discoveryRoutes(app, opts) {
33702
33876
  if (bucket.success && bucketSet.has(bucket.data)) candidateQueries.add(probe.query);
33703
33877
  }
33704
33878
  const existingQueries = new Set(
33705
- app.db.select({ query: queries.query }).from(queries).where(eq34(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
33879
+ app.db.select({ query: queries.query }).from(queries).where(eq35(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
33706
33880
  );
33707
33881
  const promotedQueries = [];
33708
33882
  const skippedQueries = [];
@@ -33718,7 +33892,7 @@ async function discoveryRoutes(app, opts) {
33718
33892
  const skippedCompetitors = [];
33719
33893
  if (includeCompetitors) {
33720
33894
  const existingCompetitors = new Set(
33721
- app.db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase())
33895
+ app.db.select({ domain: competitors.domain }).from(competitors).where(eq35(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase())
33722
33896
  );
33723
33897
  const competitorMap = parseCompetitorMap(session.competitorMap);
33724
33898
  for (const entry of selectEligibleCompetitors(competitorMap, competitorTypes)) {
@@ -33825,7 +33999,7 @@ function selectEligibleCompetitors(competitorMap, competitorTypes) {
33825
33999
 
33826
34000
  // ../api-routes/src/discovery/orchestrate.ts
33827
34001
  import crypto28 from "crypto";
33828
- import { eq as eq35 } from "drizzle-orm";
34002
+ import { eq as eq36 } from "drizzle-orm";
33829
34003
  var DEFAULT_MAX_PROBES = 100;
33830
34004
  var ABSOLUTE_MAX_PROBES = 500;
33831
34005
  function classifyProbeBucket(input) {
@@ -33879,7 +34053,7 @@ async function executeDiscovery(opts) {
33879
34053
  status: DiscoverySessionStatuses.seeding,
33880
34054
  dedupThreshold,
33881
34055
  startedAt
33882
- }).where(eq35(discoverySessions.id, opts.sessionId)).run();
34056
+ }).where(eq36(discoverySessions.id, opts.sessionId)).run();
33883
34057
  const seedResult = await opts.deps.seed({
33884
34058
  project: opts.project,
33885
34059
  icpDescription: opts.icpDescription,
@@ -33905,7 +34079,7 @@ async function executeDiscovery(opts) {
33905
34079
  seedCountRaw,
33906
34080
  seedCount,
33907
34081
  warning
33908
- }).where(eq35(discoverySessions.id, opts.sessionId)).run();
34082
+ }).where(eq36(discoverySessions.id, opts.sessionId)).run();
33909
34083
  const probeRows = [];
33910
34084
  const buckets = { cited: 0, aspirational: 0, "wasted-surface": 0 };
33911
34085
  for (const query of probedCanonicals) {
@@ -33946,7 +34120,7 @@ async function executeDiscovery(opts) {
33946
34120
  wastedCount: buckets["wasted-surface"],
33947
34121
  competitorMap,
33948
34122
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
33949
- }).where(eq35(discoverySessions.id, opts.sessionId)).run();
34123
+ }).where(eq36(discoverySessions.id, opts.sessionId)).run();
33950
34124
  upsertDomainClassifications(opts.db, opts.project.id, opts.sessionId, competitorMap);
33951
34125
  return {
33952
34126
  buckets,
@@ -33986,7 +34160,7 @@ function markSessionFailed(db, sessionId, error) {
33986
34160
  status: DiscoverySessionStatuses.failed,
33987
34161
  error,
33988
34162
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
33989
- }).where(eq35(discoverySessions.id, sessionId)).run();
34163
+ }).where(eq36(discoverySessions.id, sessionId)).run();
33990
34164
  }
33991
34165
  function dedupeStrings(input) {
33992
34166
  const seen = /* @__PURE__ */ new Set();
@@ -34004,7 +34178,7 @@ function dedupeStrings(input) {
34004
34178
 
34005
34179
  // ../api-routes/src/technical-aeo.ts
34006
34180
  import crypto29 from "crypto";
34007
- import { and as and26, asc as asc4, count, desc as desc17, eq as eq36, inArray as inArray13 } from "drizzle-orm";
34181
+ import { and as and27, asc as asc5, count, desc as desc17, eq as eq37, inArray as inArray13 } from "drizzle-orm";
34008
34182
  var SURFACEABLE_STATUSES = [RunStatuses.completed, RunStatuses.partial];
34009
34183
  function emptyScore(projectName) {
34010
34184
  return {
@@ -34036,9 +34210,9 @@ function parsePositiveInt(value, fallback, max) {
34036
34210
  async function technicalAeoRoutes(app, opts) {
34037
34211
  app.get("/projects/:name/technical-aeo", async (request) => {
34038
34212
  const project = resolveProject(app.db, request.params.name);
34039
- const rows = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq36(siteAuditSnapshots.runId, runs.id)).where(and26(
34040
- eq36(siteAuditSnapshots.projectId, project.id),
34041
- eq36(runs.kind, RunKinds["site-audit"]),
34213
+ const rows = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(and27(
34214
+ eq37(siteAuditSnapshots.projectId, project.id),
34215
+ eq37(runs.kind, RunKinds["site-audit"]),
34042
34216
  inArray13(runs.status, SURFACEABLE_STATUSES),
34043
34217
  notProbeRun()
34044
34218
  )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(2).all();
@@ -34071,9 +34245,9 @@ async function technicalAeoRoutes(app, opts) {
34071
34245
  });
34072
34246
  app.get("/projects/:name/technical-aeo/pages", async (request) => {
34073
34247
  const project = resolveProject(app.db, request.params.name);
34074
- const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq36(siteAuditSnapshots.runId, runs.id)).where(and26(
34075
- eq36(siteAuditSnapshots.projectId, project.id),
34076
- eq36(runs.kind, RunKinds["site-audit"]),
34248
+ const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(and27(
34249
+ eq37(siteAuditSnapshots.projectId, project.id),
34250
+ eq37(runs.kind, RunKinds["site-audit"]),
34077
34251
  inArray13(runs.status, SURFACEABLE_STATUSES),
34078
34252
  notProbeRun()
34079
34253
  )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(1).get();
@@ -34081,14 +34255,14 @@ async function technicalAeoRoutes(app, opts) {
34081
34255
  return { project: project.name, runId: null, auditedAt: null, total: 0, pages: [] };
34082
34256
  }
34083
34257
  const statusFilter = request.query.status === "success" || request.query.status === "error" ? request.query.status : null;
34084
- const conds = [eq36(siteAuditPages.runId, latest.runId)];
34085
- if (statusFilter) conds.push(eq36(siteAuditPages.status, statusFilter));
34086
- const where = and26(...conds);
34258
+ const conds = [eq37(siteAuditPages.runId, latest.runId)];
34259
+ if (statusFilter) conds.push(eq37(siteAuditPages.status, statusFilter));
34260
+ const where = and27(...conds);
34087
34261
  const totalRow = app.db.select({ value: count() }).from(siteAuditPages).where(where).get();
34088
34262
  const total = totalRow?.value ?? 0;
34089
34263
  const limit = parsePositiveInt(request.query.limit, 100, 500);
34090
34264
  const offset = parsePositiveInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER);
34091
- const orderBy = request.query.sort === "score-desc" ? desc17(siteAuditPages.overallScore) : request.query.sort === "url" ? asc4(siteAuditPages.url) : asc4(siteAuditPages.overallScore);
34265
+ const orderBy = request.query.sort === "score-desc" ? desc17(siteAuditPages.overallScore) : request.query.sort === "url" ? asc5(siteAuditPages.url) : asc5(siteAuditPages.overallScore);
34092
34266
  const rows = app.db.select().from(siteAuditPages).where(where).orderBy(orderBy).limit(limit).offset(offset).all();
34093
34267
  const pages = rows.map((row) => ({
34094
34268
  url: row.url,
@@ -34107,9 +34281,9 @@ async function technicalAeoRoutes(app, opts) {
34107
34281
  auditedAt: siteAuditSnapshots.auditedAt,
34108
34282
  aggregateScore: siteAuditSnapshots.aggregateScore,
34109
34283
  pagesAudited: siteAuditSnapshots.pagesAudited
34110
- }).from(siteAuditSnapshots).innerJoin(runs, eq36(siteAuditSnapshots.runId, runs.id)).where(and26(
34111
- eq36(siteAuditSnapshots.projectId, project.id),
34112
- eq36(runs.kind, RunKinds["site-audit"]),
34284
+ }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(and27(
34285
+ eq37(siteAuditSnapshots.projectId, project.id),
34286
+ eq37(runs.kind, RunKinds["site-audit"]),
34113
34287
  inArray13(runs.status, SURFACEABLE_STATUSES),
34114
34288
  notProbeRun()
34115
34289
  )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(limit).all();
@@ -34121,9 +34295,9 @@ async function technicalAeoRoutes(app, opts) {
34121
34295
  if (!parsed.success) {
34122
34296
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid site-audit request");
34123
34297
  }
34124
- const existing = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and26(
34125
- eq36(runs.projectId, project.id),
34126
- eq36(runs.kind, RunKinds["site-audit"]),
34298
+ const existing = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and27(
34299
+ eq37(runs.projectId, project.id),
34300
+ eq37(runs.kind, RunKinds["site-audit"]),
34127
34301
  inArray13(runs.status, [RunStatuses.queued, RunStatuses.running])
34128
34302
  )).get();
34129
34303
  if (existing) {
@@ -34714,9 +34888,9 @@ var IntelligenceService = class {
34714
34888
  */
34715
34889
  analyzeAndPersist(runId, projectId) {
34716
34890
  const recentRuns = this.db.select().from(runs).where(
34717
- and27(
34718
- eq37(runs.projectId, projectId),
34719
- or5(eq37(runs.status, "completed"), eq37(runs.status, "partial")),
34891
+ and28(
34892
+ eq38(runs.projectId, projectId),
34893
+ or5(eq38(runs.status, "completed"), eq38(runs.status, "partial")),
34720
34894
  // Defensive: RunCoordinator already skips probes before this is
34721
34895
  // called, but if a future call site invokes analyzeAndPersist
34722
34896
  // directly for a probe, probes still must not pollute the
@@ -34798,7 +34972,7 @@ var IntelligenceService = class {
34798
34972
  * Returns the persisted insights so the coordinator can count critical/high.
34799
34973
  */
34800
34974
  analyzeAndPersistGbp(runId, projectId) {
34801
- const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq37(runs.id, runId)).get();
34975
+ const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq38(runs.id, runId)).get();
34802
34976
  if (!runRow) {
34803
34977
  log.info("gbp-intelligence.skip", { runId, reason: "run not found" });
34804
34978
  this.persistGbpInsights(runId, projectId, [], []);
@@ -34806,9 +34980,9 @@ var IntelligenceService = class {
34806
34980
  }
34807
34981
  const windowStart = runRow.startedAt ?? runRow.createdAt;
34808
34982
  const windowEnd = runRow.finishedAt ?? (/* @__PURE__ */ new Date()).toISOString();
34809
- const selected = this.db.select().from(gbpLocations).where(and27(
34810
- eq37(gbpLocations.projectId, projectId),
34811
- eq37(gbpLocations.selected, true),
34983
+ const selected = this.db.select().from(gbpLocations).where(and28(
34984
+ eq38(gbpLocations.projectId, projectId),
34985
+ eq38(gbpLocations.selected, true),
34812
34986
  gte7(gbpLocations.syncedAt, windowStart),
34813
34987
  lte4(gbpLocations.syncedAt, windowEnd)
34814
34988
  )).all();
@@ -34843,12 +35017,12 @@ var IntelligenceService = class {
34843
35017
  }
34844
35018
  /** Build the per-location signal bundle the GBP analyzer consumes. */
34845
35019
  buildGbpLocationSignals(projectId, locationName, displayName, fallbackDate) {
34846
- 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();
34847
- const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and27(eq37(gbpPlaceActions.projectId, projectId), eq37(gbpPlaceActions.locationName, locationName))).all();
34848
- 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();
34849
- const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(and27(eq37(gbpLocations.projectId, projectId), eq37(gbpLocations.locationName, locationName))).get();
35020
+ const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and28(eq38(gbpDailyMetrics.projectId, projectId), eq38(gbpDailyMetrics.locationName, locationName))).all();
35021
+ const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and28(eq38(gbpPlaceActions.projectId, projectId), eq38(gbpPlaceActions.locationName, locationName))).all();
35022
+ const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and28(eq38(gbpLodgingSnapshots.projectId, projectId), eq38(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc18(gbpLodgingSnapshots.syncedAt)).limit(1).get();
35023
+ const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(and28(eq38(gbpLocations.projectId, projectId), eq38(gbpLocations.locationName, locationName))).get();
34850
35024
  const descriptionMissing = !(ownerRow?.description ?? "").trim();
34851
- 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();
35025
+ const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and28(eq38(gbpPlaceDetails.projectId, projectId), eq38(gbpPlaceDetails.locationName, locationName))).orderBy(desc18(gbpPlaceDetails.syncedAt)).limit(1).get();
34852
35026
  const placesAmenities = placeRow ? extractPlaceAmenities(placeRow.attributes) : [];
34853
35027
  const summary = buildGbpSummary({
34854
35028
  locationName,
@@ -34884,7 +35058,7 @@ var IntelligenceService = class {
34884
35058
  /** Build the month-over-month keyword series for a location from the
34885
35059
  * accumulating gbp_keyword_monthly table (latest complete month vs prior). */
34886
35060
  buildGbpKeywordTrend(projectId, locationName) {
34887
- 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();
35061
+ const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and28(eq38(gbpKeywordMonthly.projectId, projectId), eq38(gbpKeywordMonthly.locationName, locationName))).all();
34888
35062
  if (rows.length === 0) return { recentMonth: null, priorMonth: null, points: [] };
34889
35063
  const months = [...new Set(rows.map((r) => r.month))].sort().reverse();
34890
35064
  const recentMonth = months[0] ?? null;
@@ -34915,7 +35089,7 @@ var IntelligenceService = class {
34915
35089
  */
34916
35090
  persistGbpInsights(runId, projectId, gbpInsights, coveredLocationNames) {
34917
35091
  const covered = new Set(coveredLocationNames);
34918
- 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();
35092
+ const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and28(eq38(insights.projectId, projectId), eq38(insights.provider, GBP_INSIGHT_PROVIDER))).all();
34919
35093
  const staleIds = [];
34920
35094
  const dismissedSlots = /* @__PURE__ */ new Set();
34921
35095
  for (const row of existing) {
@@ -34926,7 +35100,7 @@ var IntelligenceService = class {
34926
35100
  }
34927
35101
  this.db.transaction((tx) => {
34928
35102
  for (const id of staleIds) {
34929
- tx.delete(insights).where(eq37(insights.id, id)).run();
35103
+ tx.delete(insights).where(eq38(insights.id, id)).run();
34930
35104
  }
34931
35105
  for (const insight of gbpInsights) {
34932
35106
  const parsed = parseGbpInsightId(insight.id);
@@ -35004,7 +35178,7 @@ var IntelligenceService = class {
35004
35178
  * create per run + aggregate). DB is left untouched.
35005
35179
  */
35006
35180
  backfill(projectName, opts, onProgress) {
35007
- const project = this.db.select().from(projects).where(eq37(projects.name, projectName)).get();
35181
+ const project = this.db.select().from(projects).where(eq38(projects.name, projectName)).get();
35008
35182
  if (!project) {
35009
35183
  throw new Error(`Project "${projectName}" not found`);
35010
35184
  }
@@ -35017,13 +35191,13 @@ var IntelligenceService = class {
35017
35191
  sinceTimestamp = parsed;
35018
35192
  }
35019
35193
  const allRuns = this.db.select().from(runs).where(
35020
- and27(
35021
- eq37(runs.projectId, project.id),
35022
- or5(eq37(runs.status, "completed"), eq37(runs.status, "partial")),
35194
+ and28(
35195
+ eq38(runs.projectId, project.id),
35196
+ or5(eq38(runs.status, "completed"), eq38(runs.status, "partial")),
35023
35197
  // Backfill must not replay probe runs as if they were real sweeps.
35024
35198
  ne5(runs.trigger, RunTriggers.probe)
35025
35199
  )
35026
- ).orderBy(asc5(runs.finishedAt)).all();
35200
+ ).orderBy(asc6(runs.finishedAt)).all();
35027
35201
  let startIdx = 0;
35028
35202
  let endIdx = allRuns.length;
35029
35203
  if (opts?.fromRunId) {
@@ -35098,7 +35272,7 @@ var IntelligenceService = class {
35098
35272
  return { processed, skipped, totalInsights };
35099
35273
  }
35100
35274
  loadTrackedCompetitors(projectId) {
35101
- return this.db.select({ domain: competitors.domain }).from(competitors).where(eq37(competitors.projectId, projectId)).all().map((r) => r.domain);
35275
+ return this.db.select({ domain: competitors.domain }).from(competitors).where(eq38(competitors.projectId, projectId)).all().map((r) => r.domain);
35102
35276
  }
35103
35277
  /**
35104
35278
  * Wipe transition signals from an analysis result while keeping health.
@@ -35119,15 +35293,15 @@ var IntelligenceService = class {
35119
35293
  }
35120
35294
  persistResult(result, runId, projectId) {
35121
35295
  const previouslyDismissed = /* @__PURE__ */ new Set();
35122
- 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();
35296
+ const existingInsights = this.db.select({ query: insights.query, provider: insights.provider, type: insights.type, dismissed: insights.dismissed }).from(insights).where(eq38(insights.runId, runId)).all();
35123
35297
  for (const row of existingInsights) {
35124
35298
  if (row.dismissed) {
35125
35299
  previouslyDismissed.add(`${row.query}:${row.provider}:${row.type}`);
35126
35300
  }
35127
35301
  }
35128
35302
  this.db.transaction((tx) => {
35129
- tx.delete(insights).where(eq37(insights.runId, runId)).run();
35130
- tx.delete(healthSnapshots).where(eq37(healthSnapshots.runId, runId)).run();
35303
+ tx.delete(insights).where(eq38(insights.runId, runId)).run();
35304
+ tx.delete(healthSnapshots).where(eq38(healthSnapshots.runId, runId)).run();
35131
35305
  const now = (/* @__PURE__ */ new Date()).toISOString();
35132
35306
  for (const insight of result.insights) {
35133
35307
  const wasDismissed = previouslyDismissed.has(`${insight.query}:${insight.provider}:${insight.type}`);
@@ -35180,24 +35354,24 @@ var IntelligenceService = class {
35180
35354
  applySeverityTiering(rawInsights, excludeRunId, projectId) {
35181
35355
  const regressions = rawInsights.filter((i) => i.type === "regression");
35182
35356
  if (regressions.length === 0) return rawInsights;
35183
- const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq37(gscSearchData.projectId, projectId)).all();
35357
+ const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq38(gscSearchData.projectId, projectId)).all();
35184
35358
  const gscConnected = gscRows.length > 0;
35185
35359
  const gscImpressionsByQuery = /* @__PURE__ */ new Map();
35186
35360
  for (const row of gscRows) {
35187
35361
  const key = row.query.toLowerCase();
35188
35362
  gscImpressionsByQuery.set(key, (gscImpressionsByQuery.get(key) ?? 0) + row.impressions);
35189
35363
  }
35190
- const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq37(projects.id, projectId)).get();
35364
+ const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq38(projects.id, projectId)).get();
35191
35365
  const locationCount = Math.max(
35192
35366
  1,
35193
35367
  (projectRow?.locations ?? []).length
35194
35368
  );
35195
35369
  const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
35196
35370
  const recentRunRows = this.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
35197
- and27(
35198
- eq37(runs.projectId, projectId),
35199
- eq37(runs.kind, RunKinds["answer-visibility"]),
35200
- or5(eq37(runs.status, "completed"), eq37(runs.status, "partial")),
35371
+ and28(
35372
+ eq38(runs.projectId, projectId),
35373
+ eq38(runs.kind, RunKinds["answer-visibility"]),
35374
+ or5(eq38(runs.status, "completed"), eq38(runs.status, "partial")),
35201
35375
  // Defensive — see top of file.
35202
35376
  ne5(runs.trigger, RunTriggers.probe)
35203
35377
  )
@@ -35217,7 +35391,7 @@ var IntelligenceService = class {
35217
35391
  const haveHistory = recentRunIds.length > 0;
35218
35392
  const priorRegressionsByPair = /* @__PURE__ */ new Map();
35219
35393
  if (haveHistory) {
35220
- 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();
35394
+ const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and28(eq38(insights.type, "regression"), inArray14(insights.runId, recentRunIds))).all();
35221
35395
  const regressionGroups = /* @__PURE__ */ new Map();
35222
35396
  for (const row of priorRows) {
35223
35397
  if (!row.runId) continue;
@@ -35246,7 +35420,7 @@ var IntelligenceService = class {
35246
35420
  });
35247
35421
  }
35248
35422
  buildRunData(runId, projectId, completedAt, location = null) {
35249
- const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq37(projects.id, projectId)).get();
35423
+ const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq38(projects.id, projectId)).get();
35250
35424
  const projectDomains = projectDomainRow ? effectiveDomains({
35251
35425
  canonicalDomain: projectDomainRow.canonicalDomain,
35252
35426
  ownedDomains: projectDomainRow.ownedDomains
@@ -35263,7 +35437,7 @@ var IntelligenceService = class {
35263
35437
  citedDomains: querySnapshots.citedDomains,
35264
35438
  competitorOverlap: querySnapshots.competitorOverlap,
35265
35439
  snapshotLocation: querySnapshots.location
35266
- }).from(querySnapshots).leftJoin(queries, eq37(querySnapshots.queryId, queries.id)).where(eq37(querySnapshots.runId, runId)).all();
35440
+ }).from(querySnapshots).leftJoin(queries, eq38(querySnapshots.queryId, queries.id)).where(eq38(querySnapshots.runId, runId)).all();
35267
35441
  const snapshots = [];
35268
35442
  let orphanCount = 0;
35269
35443
  for (const r of rows) {
@@ -35316,6 +35490,7 @@ export {
35316
35490
  schedules,
35317
35491
  notifications,
35318
35492
  gscSearchData,
35493
+ gscDailyTotals,
35319
35494
  gscUrlInspections,
35320
35495
  gscCoverageSnapshots,
35321
35496
  siteAuditSnapshots,