@canonry/canonry 4.117.0 → 4.117.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.
@@ -283,7 +283,7 @@ import {
283
283
  } from "./chunk-IAJ5TQ4S.js";
284
284
 
285
285
  // src/intelligence-service.ts
286
- import { eq as eq38, desc as desc18, asc as asc6, and as and30, ne as ne5, or as or6, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
286
+ import { eq as eq39, desc as desc18, asc as asc6, and as and30, ne as ne5, or as or6, inArray as inArray15, gte as gte7, lte as lte4 } from "drizzle-orm";
287
287
 
288
288
  // ../db/src/client.ts
289
289
  import { mkdirSync } from "fs";
@@ -1668,6 +1668,32 @@ CREATE TABLE IF NOT EXISTS _migrations (
1668
1668
  applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
1669
1669
  );
1670
1670
  `;
1671
+ function relinkOrphanedSnapshotQueryIds(tx) {
1672
+ const orphans = tx.all(sql`
1673
+ SELECT qs.id AS snapId, qs.query_text AS text, r.project_id AS projectId
1674
+ FROM query_snapshots qs
1675
+ JOIN runs r ON r.id = qs.run_id
1676
+ WHERE qs.query_id IS NULL AND qs.query_text IS NOT NULL
1677
+ `);
1678
+ if (orphans.length === 0) return;
1679
+ const queryRows = tx.all(sql`SELECT id, project_id AS projectId, query FROM queries`);
1680
+ const byProject = /* @__PURE__ */ new Map();
1681
+ for (const row of queryRows) {
1682
+ let perProject = byProject.get(row.projectId);
1683
+ if (!perProject) {
1684
+ perProject = /* @__PURE__ */ new Map();
1685
+ byProject.set(row.projectId, perProject);
1686
+ }
1687
+ const key = normalizeQueryText(row.query);
1688
+ if (!perProject.has(key)) perProject.set(key, row.id);
1689
+ }
1690
+ for (const orphan of orphans) {
1691
+ const queryId = byProject.get(orphan.projectId)?.get(normalizeQueryText(orphan.text));
1692
+ if (queryId) {
1693
+ tx.run(sql`UPDATE query_snapshots SET query_id = ${queryId} WHERE id = ${orphan.snapId}`);
1694
+ }
1695
+ }
1696
+ }
1671
1697
  var MIGRATION_VERSIONS = [
1672
1698
  {
1673
1699
  version: 2,
@@ -3558,6 +3584,19 @@ var MIGRATION_VERSIONS = [
3558
3584
  `ALTER TABLE ai_referral_events_hourly ADD COLUMN paid_sessions_or_hits INTEGER NOT NULL DEFAULT 0`,
3559
3585
  `ALTER TABLE ai_referral_events_hourly ADD COLUMN organic_sessions_or_hits INTEGER NOT NULL DEFAULT 0`
3560
3586
  ]
3587
+ },
3588
+ {
3589
+ // One-time self-heal for installs whose snapshot->query FKs were orphaned
3590
+ // by the pre-fix delete-all + reinsert replace paths. Data-only UPDATEs
3591
+ // (no schema change) via run() — TS matching because SQLite lower() is
3592
+ // ASCII-only; see the doc comment on relinkOrphanedSnapshotQueryIds and
3593
+ // the downgrade-safety RUN_HOOK_ALLOWLIST justification.
3594
+ version: 98,
3595
+ name: "relink-orphaned-snapshot-query-ids",
3596
+ statements: [],
3597
+ run: (tx) => {
3598
+ relinkOrphanedSnapshotQueryIds(tx);
3599
+ }
3561
3600
  }
3562
3601
  ];
3563
3602
  function rebuildBacklinkTableWithSource(tx, table) {
@@ -6465,18 +6504,88 @@ function aliasArraysEqual(a, b) {
6465
6504
  }
6466
6505
 
6467
6506
  // ../api-routes/src/queries.ts
6507
+ import crypto6 from "crypto";
6508
+ import { and, eq as eq5, inArray as inArray2, sql as sql4 } from "drizzle-orm";
6509
+
6510
+ // ../api-routes/src/query-replace.ts
6468
6511
  import crypto5 from "crypto";
6469
- import { and, eq as eq4, inArray, sql as sql4 } from "drizzle-orm";
6512
+ import { eq as eq4, inArray } from "drizzle-orm";
6470
6513
  function preserveSnapshotQueryText(tx, projectId, queryIds) {
6471
6514
  const candidates = queryIds && queryIds.length > 0 ? tx.select({ id: queries.id, text: queries.query }).from(queries).where(inArray(queries.id, queryIds)).all() : tx.select({ id: queries.id, text: queries.query }).from(queries).where(eq4(queries.projectId, projectId)).all();
6472
6515
  for (const q of candidates) {
6473
6516
  tx.update(querySnapshots).set({ queryText: q.text }).where(eq4(querySnapshots.queryId, q.id)).run();
6474
6517
  }
6475
6518
  }
6519
+ function diffProjectQueries(existing, incomingTexts) {
6520
+ const groups = /* @__PURE__ */ new Map();
6521
+ for (const row of existing) {
6522
+ const key = normalizeQueryText(row.text);
6523
+ const group = groups.get(key);
6524
+ if (group) group.push(row);
6525
+ else groups.set(key, [row]);
6526
+ }
6527
+ const kept = [];
6528
+ const duplicates = [];
6529
+ const removed = [];
6530
+ const insertedTexts = [];
6531
+ const seenIncoming = /* @__PURE__ */ new Set();
6532
+ for (const text2 of incomingTexts) {
6533
+ const key = normalizeQueryText(text2);
6534
+ if (seenIncoming.has(key)) continue;
6535
+ seenIncoming.add(key);
6536
+ const group = groups.get(key);
6537
+ if (group) {
6538
+ groups.delete(key);
6539
+ const [canonical, ...extras] = group;
6540
+ kept.push({ id: canonical.id, currentText: canonical.text, incomingText: text2 });
6541
+ for (const extra of extras) {
6542
+ duplicates.push({ id: extra.id, text: extra.text, keptId: canonical.id });
6543
+ }
6544
+ } else {
6545
+ insertedTexts.push(text2);
6546
+ }
6547
+ }
6548
+ for (const group of groups.values()) {
6549
+ for (const row of group) removed.push({ id: row.id, text: row.text });
6550
+ }
6551
+ return { kept, duplicates, removed, insertedTexts };
6552
+ }
6553
+ function replaceProjectQueries(tx, projectId, incomingTexts, now) {
6554
+ const existing = tx.select({ id: queries.id, text: queries.query }).from(queries).where(eq4(queries.projectId, projectId)).all();
6555
+ const diff = diffProjectQueries(existing, incomingTexts);
6556
+ if (diff.duplicates.length > 0) {
6557
+ for (const dup of diff.duplicates) {
6558
+ tx.update(querySnapshots).set({ queryId: dup.keptId }).where(eq4(querySnapshots.queryId, dup.id)).run();
6559
+ }
6560
+ tx.delete(queries).where(inArray(queries.id, diff.duplicates.map((d) => d.id))).run();
6561
+ }
6562
+ if (diff.removed.length > 0) {
6563
+ const removedIds = diff.removed.map((r) => r.id);
6564
+ preserveSnapshotQueryText(tx, projectId, removedIds);
6565
+ tx.delete(queries).where(inArray(queries.id, removedIds)).run();
6566
+ }
6567
+ for (const keptRow of diff.kept) {
6568
+ if (keptRow.currentText !== keptRow.incomingText) {
6569
+ tx.update(queries).set({ query: keptRow.incomingText }).where(eq4(queries.id, keptRow.id)).run();
6570
+ }
6571
+ }
6572
+ for (const text2 of diff.insertedTexts) {
6573
+ tx.insert(queries).values({
6574
+ id: crypto5.randomUUID(),
6575
+ projectId,
6576
+ query: text2,
6577
+ provenance: "cli",
6578
+ createdAt: now
6579
+ }).run();
6580
+ }
6581
+ return diff;
6582
+ }
6583
+
6584
+ // ../api-routes/src/queries.ts
6476
6585
  async function queryRoutes(app, opts) {
6477
6586
  app.get("/projects/:name/queries", async (request, reply) => {
6478
6587
  const project = resolveProject(app.db, request.params.name);
6479
- const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6588
+ const rows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6480
6589
  return reply.send(rows.map((r) => ({ id: r.id, query: r.query, createdAt: r.createdAt })));
6481
6590
  });
6482
6591
  app.put("/projects/:name/queries", async (request, reply) => {
@@ -6487,17 +6596,7 @@ async function queryRoutes(app, opts) {
6487
6596
  }
6488
6597
  const now = (/* @__PURE__ */ new Date()).toISOString();
6489
6598
  app.db.transaction((tx) => {
6490
- preserveSnapshotQueryText(tx, project.id);
6491
- tx.delete(queries).where(eq4(queries.projectId, project.id)).run();
6492
- for (const q of body.queries) {
6493
- tx.insert(queries).values({
6494
- id: crypto5.randomUUID(),
6495
- projectId: project.id,
6496
- query: q,
6497
- provenance: "cli",
6498
- createdAt: now
6499
- }).run();
6500
- }
6599
+ replaceProjectQueries(tx, project.id, body.queries, now);
6501
6600
  writeAuditLog(tx, auditFromRequest(request, {
6502
6601
  projectId: project.id,
6503
6602
  actor: "api",
@@ -6506,7 +6605,7 @@ async function queryRoutes(app, opts) {
6506
6605
  diff: { queries: body.queries }
6507
6606
  }));
6508
6607
  });
6509
- const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6608
+ const rows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6510
6609
  return reply.send(rows.map((r) => ({ id: r.id, query: r.query, createdAt: r.createdAt })));
6511
6610
  });
6512
6611
  app.post("/projects/:name/queries/replace-preview", async (request, reply) => {
@@ -6515,20 +6614,22 @@ async function queryRoutes(app, opts) {
6515
6614
  if (!body || !Array.isArray(body.queries)) {
6516
6615
  throw validationError('Body must contain a "queries" array');
6517
6616
  }
6518
- const currentRows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6617
+ const currentRows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6519
6618
  const currentTexts = currentRows.map((r) => r.query);
6520
- const currentSet = new Set(currentTexts);
6521
- const proposedSet = new Set(body.queries);
6522
- const removed = currentTexts.filter((q) => !proposedSet.has(q));
6523
- const added = body.queries.filter((q) => !currentSet.has(q));
6524
- const unchanged = currentTexts.filter((q) => proposedSet.has(q));
6525
- const currentIds = currentRows.map((r) => r.id);
6619
+ const diff = diffProjectQueries(
6620
+ currentRows.map((r) => ({ id: r.id, text: r.query })),
6621
+ body.queries
6622
+ );
6623
+ const removed = diff.removed.map((r) => r.text);
6624
+ const added = diff.insertedTexts;
6625
+ const unchanged = diff.kept.map((k) => k.currentText);
6626
+ const removedIds = diff.removed.map((r) => r.id);
6526
6627
  let snapshotsDetached = 0;
6527
6628
  let affectedQueries = 0;
6528
- if (currentIds.length > 0) {
6529
- const snapshotCount = app.db.select({ n: sql4`count(*)` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
6629
+ if (removedIds.length > 0) {
6630
+ const snapshotCount = app.db.select({ n: sql4`count(*)` }).from(querySnapshots).where(inArray2(querySnapshots.queryId, removedIds)).get();
6530
6631
  snapshotsDetached = snapshotCount?.n ?? 0;
6531
- const distinctAffected = app.db.select({ n: sql4`count(distinct ${querySnapshots.queryId})` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
6632
+ const distinctAffected = app.db.select({ n: sql4`count(distinct ${querySnapshots.queryId})` }).from(querySnapshots).where(inArray2(querySnapshots.queryId, removedIds)).get();
6532
6633
  affectedQueries = distinctAffected?.n ?? 0;
6533
6634
  }
6534
6635
  return reply.send({
@@ -6545,14 +6646,14 @@ async function queryRoutes(app, opts) {
6545
6646
  if (!body || !Array.isArray(body.queries) || body.queries.length === 0) {
6546
6647
  throw validationError('Body must contain a non-empty "queries" array');
6547
6648
  }
6548
- const existing = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6649
+ const existing = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6549
6650
  const toDelete = new Set(body.queries);
6550
6651
  const idsToDelete = existing.filter((q) => toDelete.has(q.query)).map((q) => q.id);
6551
6652
  if (idsToDelete.length > 0) {
6552
6653
  app.db.transaction((tx) => {
6553
6654
  preserveSnapshotQueryText(tx, project.id, idsToDelete);
6554
6655
  for (const id of idsToDelete) {
6555
- tx.delete(queries).where(eq4(queries.id, id)).run();
6656
+ tx.delete(queries).where(eq5(queries.id, id)).run();
6556
6657
  }
6557
6658
  writeAuditLog(tx, auditFromRequest(request, {
6558
6659
  projectId: project.id,
@@ -6563,18 +6664,18 @@ async function queryRoutes(app, opts) {
6563
6664
  }));
6564
6665
  });
6565
6666
  }
6566
- const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6667
+ const rows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6567
6668
  return reply.send(rows.map((r) => ({ id: r.id, query: r.query, createdAt: r.createdAt })));
6568
6669
  });
6569
6670
  app.delete("/projects/:name/queries/:id", async (request, reply) => {
6570
6671
  const project = resolveProject(app.db, request.params.name);
6571
- const query = app.db.select().from(queries).where(and(eq4(queries.projectId, project.id), eq4(queries.id, request.params.id))).get();
6672
+ const query = app.db.select().from(queries).where(and(eq5(queries.projectId, project.id), eq5(queries.id, request.params.id))).get();
6572
6673
  if (!query) {
6573
6674
  throw notFound("Query", request.params.id);
6574
6675
  }
6575
6676
  app.db.transaction((tx) => {
6576
6677
  preserveSnapshotQueryText(tx, project.id, [query.id]);
6577
- tx.delete(queries).where(eq4(queries.id, query.id)).run();
6678
+ tx.delete(queries).where(eq5(queries.id, query.id)).run();
6578
6679
  writeAuditLog(tx, auditFromRequest(request, {
6579
6680
  projectId: project.id,
6580
6681
  actor: "api",
@@ -6593,13 +6694,13 @@ async function queryRoutes(app, opts) {
6593
6694
  throw validationError('Body must contain a "queries" array');
6594
6695
  }
6595
6696
  const now = (/* @__PURE__ */ new Date()).toISOString();
6596
- const existing = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6697
+ const existing = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6597
6698
  const existingSet = new Set(existing.map((q) => q.query));
6598
6699
  const added = [];
6599
6700
  for (const q of body.queries) {
6600
6701
  if (!existingSet.has(q)) {
6601
6702
  app.db.insert(queries).values({
6602
- id: crypto5.randomUUID(),
6703
+ id: crypto6.randomUUID(),
6603
6704
  projectId: project.id,
6604
6705
  query: q,
6605
6706
  provenance: "cli",
@@ -6618,7 +6719,7 @@ async function queryRoutes(app, opts) {
6618
6719
  diff: { added }
6619
6720
  }));
6620
6721
  }
6621
- const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6722
+ const rows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6622
6723
  return reply.send(rows.map((r) => ({ id: r.id, query: r.query, createdAt: r.createdAt })));
6623
6724
  });
6624
6725
  app.post("/projects/:name/queries/generate", async (request, reply) => {
@@ -6645,7 +6746,7 @@ async function queryRoutes(app, opts) {
6645
6746
  if (!opts.onGenerateQueries) {
6646
6747
  throw notImplemented("Query generation is not supported in this deployment");
6647
6748
  }
6648
- const existingRows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6749
+ const existingRows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6649
6750
  const existingQueries = existingRows.map((r) => r.query);
6650
6751
  try {
6651
6752
  const generated = await opts.onGenerateQueries(provider, count2, {
@@ -6663,7 +6764,7 @@ async function queryRoutes(app, opts) {
6663
6764
  });
6664
6765
  app.get("/projects/:name/keywords", async (request, reply) => {
6665
6766
  const project = resolveProject(app.db, request.params.name);
6666
- const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6767
+ const rows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6667
6768
  return reply.send(rows.map((r) => ({ id: r.id, keyword: r.query, createdAt: r.createdAt })));
6668
6769
  });
6669
6770
  app.put("/projects/:name/keywords", async (request, reply) => {
@@ -6674,17 +6775,7 @@ async function queryRoutes(app, opts) {
6674
6775
  }
6675
6776
  const now = (/* @__PURE__ */ new Date()).toISOString();
6676
6777
  app.db.transaction((tx) => {
6677
- preserveSnapshotQueryText(tx, project.id);
6678
- tx.delete(queries).where(eq4(queries.projectId, project.id)).run();
6679
- for (const keyword of body.keywords) {
6680
- tx.insert(queries).values({
6681
- id: crypto5.randomUUID(),
6682
- projectId: project.id,
6683
- query: keyword,
6684
- provenance: "cli",
6685
- createdAt: now
6686
- }).run();
6687
- }
6778
+ replaceProjectQueries(tx, project.id, body.keywords, now);
6688
6779
  writeAuditLog(tx, auditFromRequest(request, {
6689
6780
  projectId: project.id,
6690
6781
  actor: "api",
@@ -6693,7 +6784,7 @@ async function queryRoutes(app, opts) {
6693
6784
  diff: { queries: body.keywords }
6694
6785
  }));
6695
6786
  });
6696
- const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6787
+ const rows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6697
6788
  return reply.send(rows.map((r) => ({ id: r.id, keyword: r.query, createdAt: r.createdAt })));
6698
6789
  });
6699
6790
  app.delete("/projects/:name/keywords", async (request, reply) => {
@@ -6702,14 +6793,14 @@ async function queryRoutes(app, opts) {
6702
6793
  if (!body || !Array.isArray(body.keywords) || body.keywords.length === 0) {
6703
6794
  throw validationError('Body must contain a non-empty "keywords" array');
6704
6795
  }
6705
- const existing = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6796
+ const existing = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6706
6797
  const toDelete = new Set(body.keywords);
6707
6798
  const idsToDelete = existing.filter((q) => toDelete.has(q.query)).map((q) => q.id);
6708
6799
  if (idsToDelete.length > 0) {
6709
6800
  app.db.transaction((tx) => {
6710
6801
  preserveSnapshotQueryText(tx, project.id, idsToDelete);
6711
6802
  for (const id of idsToDelete) {
6712
- tx.delete(queries).where(eq4(queries.id, id)).run();
6803
+ tx.delete(queries).where(eq5(queries.id, id)).run();
6713
6804
  }
6714
6805
  writeAuditLog(tx, auditFromRequest(request, {
6715
6806
  projectId: project.id,
@@ -6720,7 +6811,7 @@ async function queryRoutes(app, opts) {
6720
6811
  }));
6721
6812
  });
6722
6813
  }
6723
- const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6814
+ const rows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6724
6815
  return reply.send(rows.map((r) => ({ id: r.id, keyword: r.query, createdAt: r.createdAt })));
6725
6816
  });
6726
6817
  app.post("/projects/:name/keywords", async (request, reply) => {
@@ -6730,13 +6821,13 @@ async function queryRoutes(app, opts) {
6730
6821
  throw validationError('Body must contain a "keywords" array');
6731
6822
  }
6732
6823
  const now = (/* @__PURE__ */ new Date()).toISOString();
6733
- const existing = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6824
+ const existing = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6734
6825
  const existingSet = new Set(existing.map((q) => q.query));
6735
6826
  const added = [];
6736
6827
  for (const keyword of body.keywords) {
6737
6828
  if (!existingSet.has(keyword)) {
6738
6829
  app.db.insert(queries).values({
6739
- id: crypto5.randomUUID(),
6830
+ id: crypto6.randomUUID(),
6740
6831
  projectId: project.id,
6741
6832
  query: keyword,
6742
6833
  provenance: "cli",
@@ -6755,7 +6846,7 @@ async function queryRoutes(app, opts) {
6755
6846
  diff: { added }
6756
6847
  }));
6757
6848
  }
6758
- const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6849
+ const rows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6759
6850
  return reply.send(rows.map((r) => ({ id: r.id, keyword: r.query, createdAt: r.createdAt })));
6760
6851
  });
6761
6852
  app.post("/projects/:name/keywords/generate", async (request, reply) => {
@@ -6782,7 +6873,7 @@ async function queryRoutes(app, opts) {
6782
6873
  if (!opts.onGenerateQueries) {
6783
6874
  throw notImplemented("Keyword generation is not supported in this deployment");
6784
6875
  }
6785
- const existingRows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
6876
+ const existingRows = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
6786
6877
  const existingQueries = existingRows.map((r) => r.query);
6787
6878
  try {
6788
6879
  const generated = await opts.onGenerateQueries(provider, count2, {
@@ -6801,8 +6892,8 @@ async function queryRoutes(app, opts) {
6801
6892
  }
6802
6893
 
6803
6894
  // ../api-routes/src/competitors.ts
6804
- import crypto6 from "crypto";
6805
- import { and as and2, eq as eq5 } from "drizzle-orm";
6895
+ import crypto7 from "crypto";
6896
+ import { and as and2, eq as eq6 } from "drizzle-orm";
6806
6897
  function normalizeCompetitor(domain) {
6807
6898
  const reg = registrableDomain(domain);
6808
6899
  if (reg) return reg;
@@ -6827,7 +6918,7 @@ function serializeCompetitor(row) {
6827
6918
  async function competitorRoutes(app) {
6828
6919
  app.get("/projects/:name/competitors", async (request, reply) => {
6829
6920
  const project = resolveProject(app.db, request.params.name);
6830
- const rows = app.db.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
6921
+ const rows = app.db.select().from(competitors).where(eq6(competitors.projectId, project.id)).all();
6831
6922
  return reply.send(rows.map(serializeCompetitor));
6832
6923
  });
6833
6924
  app.put("/projects/:name/competitors", async (request, reply) => {
@@ -6839,10 +6930,10 @@ async function competitorRoutes(app) {
6839
6930
  const now = (/* @__PURE__ */ new Date()).toISOString();
6840
6931
  const normalizedCompetitors = normalizeCompetitorList(body.competitors);
6841
6932
  app.db.transaction((tx) => {
6842
- tx.delete(competitors).where(eq5(competitors.projectId, project.id)).run();
6933
+ tx.delete(competitors).where(eq6(competitors.projectId, project.id)).run();
6843
6934
  for (const domain of normalizedCompetitors) {
6844
6935
  tx.insert(competitors).values({
6845
- id: crypto6.randomUUID(),
6936
+ id: crypto7.randomUUID(),
6846
6937
  projectId: project.id,
6847
6938
  domain,
6848
6939
  provenance: "cli",
@@ -6857,7 +6948,7 @@ async function competitorRoutes(app) {
6857
6948
  diff: { competitors: normalizedCompetitors }
6858
6949
  });
6859
6950
  });
6860
- const rows = app.db.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
6951
+ const rows = app.db.select().from(competitors).where(eq6(competitors.projectId, project.id)).all();
6861
6952
  return reply.send(rows.map(serializeCompetitor));
6862
6953
  });
6863
6954
  app.post("/projects/:name/competitors", async (request, reply) => {
@@ -6866,13 +6957,13 @@ async function competitorRoutes(app) {
6866
6957
  const now = (/* @__PURE__ */ new Date()).toISOString();
6867
6958
  const requested = normalizeCompetitorList(body.competitors);
6868
6959
  app.db.transaction((tx) => {
6869
- const existing = tx.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
6960
+ const existing = tx.select().from(competitors).where(eq6(competitors.projectId, project.id)).all();
6870
6961
  const existingSet = new Set(existing.map((c) => c.domain));
6871
6962
  const added = requested.filter((domain) => !existingSet.has(domain));
6872
6963
  if (added.length === 0) return;
6873
6964
  for (const domain of added) {
6874
6965
  tx.insert(competitors).values({
6875
- id: crypto6.randomUUID(),
6966
+ id: crypto7.randomUUID(),
6876
6967
  projectId: project.id,
6877
6968
  domain,
6878
6969
  provenance: "cli",
@@ -6889,7 +6980,7 @@ async function competitorRoutes(app) {
6889
6980
  diff: { added }
6890
6981
  });
6891
6982
  });
6892
- const rows = app.db.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
6983
+ const rows = app.db.select().from(competitors).where(eq6(competitors.projectId, project.id)).all();
6893
6984
  return reply.send(rows.map(serializeCompetitor));
6894
6985
  });
6895
6986
  app.delete("/projects/:name/competitors", async (request, reply) => {
@@ -6897,11 +6988,11 @@ async function competitorRoutes(app) {
6897
6988
  const body = parseCompetitorBatch(request.body);
6898
6989
  const requested = new Set(normalizeCompetitorList(body.competitors));
6899
6990
  app.db.transaction((tx) => {
6900
- const existing = tx.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
6991
+ const existing = tx.select().from(competitors).where(eq6(competitors.projectId, project.id)).all();
6901
6992
  const rowsToDelete = existing.filter((c) => requested.has(c.domain));
6902
6993
  if (rowsToDelete.length === 0) return;
6903
6994
  for (const row of rowsToDelete) {
6904
- tx.delete(competitors).where(eq5(competitors.id, row.id)).run();
6995
+ tx.delete(competitors).where(eq6(competitors.id, row.id)).run();
6905
6996
  }
6906
6997
  writeAuditLog(tx, {
6907
6998
  projectId: project.id,
@@ -6911,17 +7002,17 @@ async function competitorRoutes(app) {
6911
7002
  diff: { deleted: rowsToDelete.map((row) => row.domain) }
6912
7003
  });
6913
7004
  });
6914
- const rows = app.db.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
7005
+ const rows = app.db.select().from(competitors).where(eq6(competitors.projectId, project.id)).all();
6915
7006
  return reply.send(rows.map(serializeCompetitor));
6916
7007
  });
6917
7008
  app.delete("/projects/:name/competitors/:id", async (request, reply) => {
6918
7009
  const project = resolveProject(app.db, request.params.name);
6919
- const competitor = app.db.select().from(competitors).where(and2(eq5(competitors.projectId, project.id), eq5(competitors.id, request.params.id))).get();
7010
+ const competitor = app.db.select().from(competitors).where(and2(eq6(competitors.projectId, project.id), eq6(competitors.id, request.params.id))).get();
6920
7011
  if (!competitor) {
6921
7012
  throw notFound("Competitor", request.params.id);
6922
7013
  }
6923
7014
  app.db.transaction((tx) => {
6924
- tx.delete(competitors).where(eq5(competitors.id, competitor.id)).run();
7015
+ tx.delete(competitors).where(eq6(competitors.id, competitor.id)).run();
6925
7016
  writeAuditLog(tx, auditFromRequest(request, {
6926
7017
  projectId: project.id,
6927
7018
  actor: "api",
@@ -6946,23 +7037,23 @@ function parseCompetitorBatch(value) {
6946
7037
  }
6947
7038
 
6948
7039
  // ../api-routes/src/runs.ts
6949
- import crypto8 from "crypto";
6950
- import { and as and4, eq as eq7, asc, desc, or as or2, sql as sql5 } from "drizzle-orm";
7040
+ import crypto9 from "crypto";
7041
+ import { and as and4, eq as eq8, asc, desc, or as or2, sql as sql5 } from "drizzle-orm";
6951
7042
  import { gte } from "drizzle-orm";
6952
7043
 
6953
7044
  // ../api-routes/src/run-queue.ts
6954
- import crypto7 from "crypto";
6955
- import { and as and3, eq as eq6, or } from "drizzle-orm";
7045
+ import crypto8 from "crypto";
7046
+ import { and as and3, eq as eq7, or } from "drizzle-orm";
6956
7047
  function queueRunIfProjectIdle(db, params) {
6957
7048
  const createdAt = params.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
6958
7049
  const kind = params.kind ?? "answer-visibility";
6959
7050
  const trigger = params.trigger ?? "manual";
6960
- const runId = crypto7.randomUUID();
7051
+ const runId = crypto8.randomUUID();
6961
7052
  return db.transaction((tx) => {
6962
7053
  const activeRun = tx.select().from(runs).where(
6963
7054
  and3(
6964
- eq6(runs.projectId, params.projectId),
6965
- or(eq6(runs.status, "queued"), eq6(runs.status, "running"))
7055
+ eq7(runs.projectId, params.projectId),
7056
+ or(eq7(runs.status, "queued"), eq7(runs.status, "running"))
6966
7057
  )
6967
7058
  ).get();
6968
7059
  if (activeRun) {
@@ -7008,7 +7099,7 @@ async function runRoutes(app, opts) {
7008
7099
  const providers = rawProviders?.length ? rawProviders : void 0;
7009
7100
  let scopedQueries = null;
7010
7101
  if (body.queries?.length) {
7011
- const trackedRows = app.db.select({ query: queries.query }).from(queries).where(eq7(queries.projectId, project.id)).all();
7102
+ const trackedRows = app.db.select({ query: queries.query }).from(queries).where(eq8(queries.projectId, project.id)).all();
7012
7103
  const tracked = new Set(trackedRows.map((r) => r.query));
7013
7104
  const missing = body.queries.filter((q) => !tracked.has(q));
7014
7105
  if (missing.length) {
@@ -7044,15 +7135,15 @@ async function runRoutes(app, opts) {
7044
7135
  }
7045
7136
  const result = app.db.transaction((tx) => {
7046
7137
  const activeRun = tx.select({ id: runs.id }).from(runs).where(and4(
7047
- eq7(runs.projectId, project.id),
7048
- or2(eq7(runs.status, "queued"), eq7(runs.status, "running"))
7138
+ eq8(runs.projectId, project.id),
7139
+ or2(eq8(runs.status, "queued"), eq8(runs.status, "running"))
7049
7140
  )).get();
7050
7141
  if (activeRun) {
7051
7142
  return { conflict: true };
7052
7143
  }
7053
7144
  const inserted = [];
7054
7145
  for (const loc of projectLocations) {
7055
- const runId2 = crypto8.randomUUID();
7146
+ const runId2 = crypto9.randomUUID();
7056
7147
  tx.insert(runs).values({
7057
7148
  id: runId2,
7058
7149
  projectId: project.id,
@@ -7079,7 +7170,7 @@ async function runRoutes(app, opts) {
7079
7170
  entityType: "run",
7080
7171
  entityId: runId2
7081
7172
  });
7082
- const r = app.db.select().from(runs).where(eq7(runs.id, runId2)).get();
7173
+ const r = app.db.select().from(runs).where(eq8(runs.id, runId2)).get();
7083
7174
  if (opts.onRunCreated) {
7084
7175
  opts.onRunCreated(runId2, project.id, providers, loc);
7085
7176
  }
@@ -7105,7 +7196,7 @@ async function runRoutes(app, opts) {
7105
7196
  entityType: "run",
7106
7197
  entityId: runId
7107
7198
  });
7108
- const run = app.db.select().from(runs).where(eq7(runs.id, runId)).get();
7199
+ const run = app.db.select().from(runs).where(eq8(runs.id, runId)).get();
7109
7200
  if (opts.onRunCreated) {
7110
7201
  opts.onRunCreated(runId, project.id, providers, resolvedLocation);
7111
7202
  }
@@ -7116,15 +7207,15 @@ async function runRoutes(app, opts) {
7116
7207
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
7117
7208
  const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? void 0 : parsedLimit;
7118
7209
  const kind = parseListKind(request.query.kind);
7119
- const where = kind ? and4(eq7(runs.projectId, project.id), eq7(runs.kind, kind)) : eq7(runs.projectId, project.id);
7210
+ const where = kind ? and4(eq8(runs.projectId, project.id), eq8(runs.kind, kind)) : eq8(runs.projectId, project.id);
7120
7211
  const rows = limit == null ? app.db.select().from(runs).where(where).orderBy(asc(runs.createdAt)).all() : app.db.select().from(runs).where(where).orderBy(desc(runs.createdAt)).limit(limit).all().reverse();
7121
7212
  return reply.send(rows.map(formatRun));
7122
7213
  });
7123
7214
  app.get("/projects/:name/runs/latest", async (request, reply) => {
7124
7215
  const project = resolveProject(app.db, request.params.name);
7125
- const countRow = app.db.select({ count: sql5`count(*)` }).from(runs).where(and4(eq7(runs.projectId, project.id), notProbeRun())).get();
7216
+ const countRow = app.db.select({ count: sql5`count(*)` }).from(runs).where(and4(eq8(runs.projectId, project.id), notProbeRun())).get();
7126
7217
  const totalRuns = countRow?.count ?? 0;
7127
- const latestRun = app.db.select().from(runs).where(and4(eq7(runs.projectId, project.id), notProbeRun())).orderBy(desc(runs.createdAt), desc(runs.id)).limit(1).get();
7218
+ const latestRun = app.db.select().from(runs).where(and4(eq8(runs.projectId, project.id), notProbeRun())).orderBy(desc(runs.createdAt), desc(runs.id)).limit(1).get();
7128
7219
  if (!latestRun) {
7129
7220
  return reply.send({ totalRuns: 0, run: null });
7130
7221
  }
@@ -7140,15 +7231,15 @@ async function runRoutes(app, opts) {
7140
7231
  const kind = parseListKind(request.query.kind);
7141
7232
  const filters = [gte(runs.createdAt, since)];
7142
7233
  if (!includeProbe) filters.push(notProbeRun());
7143
- if (kind) filters.push(eq7(runs.kind, kind));
7234
+ if (kind) filters.push(eq8(runs.kind, kind));
7144
7235
  const scopedProjectId = request.apiKey?.projectId;
7145
- if (scopedProjectId) filters.push(eq7(runs.projectId, scopedProjectId));
7236
+ if (scopedProjectId) filters.push(eq8(runs.projectId, scopedProjectId));
7146
7237
  const rows = app.db.select().from(runs).where(and4(...filters)).orderBy(desc(runs.createdAt), desc(runs.id)).limit(limit).all();
7147
7238
  return reply.send(rows.map(formatRun));
7148
7239
  });
7149
7240
  app.post("/runs", async (request, reply) => {
7150
7241
  const scopedProjectId = request.apiKey?.projectId;
7151
- const allProjects = (scopedProjectId ? app.db.select().from(projects).where(eq7(projects.id, scopedProjectId)) : app.db.select().from(projects)).all();
7242
+ const allProjects = (scopedProjectId ? app.db.select().from(projects).where(eq8(projects.id, scopedProjectId)) : app.db.select().from(projects)).all();
7152
7243
  if (allProjects.length === 0) {
7153
7244
  return reply.status(207).send([]);
7154
7245
  }
@@ -7203,7 +7294,7 @@ async function runRoutes(app, opts) {
7203
7294
  entityType: "run",
7204
7295
  entityId: runId
7205
7296
  });
7206
- const run = app.db.select().from(runs).where(eq7(runs.id, runId)).get();
7297
+ const run = app.db.select().from(runs).where(eq8(runs.id, runId)).get();
7207
7298
  if (opts.onRunCreated) {
7208
7299
  opts.onRunCreated(runId, project.id, providers, resolvedLocation);
7209
7300
  }
@@ -7212,13 +7303,13 @@ async function runRoutes(app, opts) {
7212
7303
  return reply.status(207).send(results);
7213
7304
  });
7214
7305
  app.post("/runs/:id/cancel", async (request, reply) => {
7215
- const run = app.db.select().from(runs).where(eq7(runs.id, request.params.id)).get();
7306
+ const run = app.db.select().from(runs).where(eq8(runs.id, request.params.id)).get();
7216
7307
  if (!run) throw notFound("Run", request.params.id);
7217
7308
  assertProjectScope(request, run.projectId);
7218
7309
  const terminalStatuses = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
7219
7310
  if (terminalStatuses.has(run.status)) throw runNotCancellable(run.id, run.status);
7220
7311
  const now = (/* @__PURE__ */ new Date()).toISOString();
7221
- app.db.update(runs).set({ status: "cancelled", finishedAt: now, error: serializeRunError({ message: "Cancelled by user" }) }).where(eq7(runs.id, run.id)).run();
7312
+ app.db.update(runs).set({ status: "cancelled", finishedAt: now, error: serializeRunError({ message: "Cancelled by user" }) }).where(eq8(runs.id, run.id)).run();
7222
7313
  writeAuditLog(app.db, {
7223
7314
  projectId: run.projectId,
7224
7315
  actor: "api",
@@ -7226,11 +7317,11 @@ async function runRoutes(app, opts) {
7226
7317
  entityType: "run",
7227
7318
  entityId: run.id
7228
7319
  });
7229
- const updated = app.db.select().from(runs).where(eq7(runs.id, run.id)).get();
7320
+ const updated = app.db.select().from(runs).where(eq8(runs.id, run.id)).get();
7230
7321
  return reply.send(formatRun(updated));
7231
7322
  });
7232
7323
  app.get("/runs/:id", async (request, reply) => {
7233
- const run = app.db.select().from(runs).where(eq7(runs.id, request.params.id)).get();
7324
+ const run = app.db.select().from(runs).where(eq8(runs.id, request.params.id)).get();
7234
7325
  if (!run) throw notFound("Run", request.params.id);
7235
7326
  assertProjectScope(request, run.projectId);
7236
7327
  return reply.send(loadRunDetail(app, run));
@@ -7302,7 +7393,7 @@ function loadRunDetail(app, run) {
7302
7393
  canonicalDomain: projects.canonicalDomain,
7303
7394
  ownedDomains: projects.ownedDomains,
7304
7395
  aliases: projects.aliases
7305
- }).from(projects).where(eq7(projects.id, run.projectId)).get();
7396
+ }).from(projects).where(eq8(projects.id, run.projectId)).get();
7306
7397
  const snapshots = app.db.select({
7307
7398
  id: querySnapshots.id,
7308
7399
  runId: querySnapshots.runId,
@@ -7319,7 +7410,7 @@ function loadRunDetail(app, run) {
7319
7410
  location: querySnapshots.location,
7320
7411
  rawResponse: querySnapshots.rawResponse,
7321
7412
  createdAt: querySnapshots.createdAt
7322
- }).from(querySnapshots).leftJoin(queries, eq7(querySnapshots.queryId, queries.id)).where(eq7(querySnapshots.runId, run.id)).all();
7413
+ }).from(querySnapshots).leftJoin(queries, eq8(querySnapshots.queryId, queries.id)).where(eq8(querySnapshots.runId, run.id)).all();
7323
7414
  return {
7324
7415
  ...formatRun(run),
7325
7416
  snapshots: snapshots.map((s) => {
@@ -7353,8 +7444,8 @@ function loadRunDetail(app, run) {
7353
7444
  }
7354
7445
 
7355
7446
  // ../api-routes/src/apply.ts
7356
- import crypto10 from "crypto";
7357
- import { and as and5, eq as eq8 } from "drizzle-orm";
7447
+ import crypto11 from "crypto";
7448
+ import { and as and5, eq as eq9 } from "drizzle-orm";
7358
7449
 
7359
7450
  // ../api-routes/src/schedule-utils.ts
7360
7451
  import { CronExpressionParser } from "cron-parser";
@@ -7457,7 +7548,7 @@ function nextRunFromCron(cronExpr, timezone, from = /* @__PURE__ */ new Date())
7457
7548
  }
7458
7549
 
7459
7550
  // ../api-routes/src/webhooks.ts
7460
- import crypto9 from "crypto";
7551
+ import crypto10 from "crypto";
7461
7552
  import dns from "dns/promises";
7462
7553
  import http from "http";
7463
7554
  import https from "https";
@@ -7509,7 +7600,7 @@ async function deliverWebhook(target, payload, webhookSecret) {
7509
7600
  "User-Agent": "Canonry/0.1.0"
7510
7601
  };
7511
7602
  if (webhookSecret) {
7512
- headers["X-Canonry-Signature"] = "sha256=" + crypto9.createHmac("sha256", webhookSecret).update(body).digest("hex");
7603
+ headers["X-Canonry-Signature"] = "sha256=" + crypto10.createHmac("sha256", webhookSecret).update(body).digest("hex");
7513
7604
  }
7514
7605
  return await new Promise((resolve) => {
7515
7606
  const requestOptions = {
@@ -7690,7 +7781,7 @@ async function applyRoutes(app, opts) {
7690
7781
  const configQueries = resolveConfigSpecQueries(config.spec);
7691
7782
  const scopedProjectId = request.apiKey?.projectId;
7692
7783
  if (scopedProjectId) {
7693
- const target = app.db.select({ id: projects.id }).from(projects).where(eq8(projects.name, name)).get();
7784
+ const target = app.db.select({ id: projects.id }).from(projects).where(eq9(projects.name, name)).get();
7694
7785
  if (!target || target.id !== scopedProjectId) {
7695
7786
  throw forbidden("This API key is scoped to a single project and cannot apply this config.");
7696
7787
  }
@@ -7699,7 +7790,7 @@ async function applyRoutes(app, opts) {
7699
7790
  let scheduleAction = null;
7700
7791
  let aliasesChanged = false;
7701
7792
  app.db.transaction((tx) => {
7702
- const existing = tx.select().from(projects).where(eq8(projects.name, name)).get();
7793
+ const existing = tx.select().from(projects).where(eq9(projects.name, name)).get();
7703
7794
  const nextAliases = normalizeProjectAliases(config.spec.displayName, config.spec.aliases ?? []);
7704
7795
  if (existing) {
7705
7796
  const prevAliases = existing.aliases;
@@ -7722,7 +7813,7 @@ async function applyRoutes(app, opts) {
7722
7813
  configSource: "config-file",
7723
7814
  configRevision: existing.configRevision + 1,
7724
7815
  updatedAt: now
7725
- }).where(eq8(projects.id, existing.id)).run();
7816
+ }).where(eq9(projects.id, existing.id)).run();
7726
7817
  writeAuditLog(tx, {
7727
7818
  projectId,
7728
7819
  actor: "api",
@@ -7731,7 +7822,7 @@ async function applyRoutes(app, opts) {
7731
7822
  entityId: projectId
7732
7823
  });
7733
7824
  } else {
7734
- projectId = crypto10.randomUUID();
7825
+ projectId = crypto11.randomUUID();
7735
7826
  tx.insert(projects).values({
7736
7827
  id: projectId,
7737
7828
  name,
@@ -7760,16 +7851,7 @@ async function applyRoutes(app, opts) {
7760
7851
  entityId: projectId
7761
7852
  });
7762
7853
  }
7763
- tx.delete(queries).where(eq8(queries.projectId, projectId)).run();
7764
- for (const q of configQueries) {
7765
- tx.insert(queries).values({
7766
- id: crypto10.randomUUID(),
7767
- projectId,
7768
- query: q,
7769
- provenance: "cli",
7770
- createdAt: now
7771
- }).run();
7772
- }
7854
+ replaceProjectQueries(tx, projectId, configQueries, now);
7773
7855
  writeAuditLog(tx, {
7774
7856
  projectId,
7775
7857
  actor: "api",
@@ -7777,11 +7859,11 @@ async function applyRoutes(app, opts) {
7777
7859
  entityType: "query",
7778
7860
  diff: { queries: configQueries }
7779
7861
  });
7780
- tx.delete(competitors).where(eq8(competitors.projectId, projectId)).run();
7862
+ tx.delete(competitors).where(eq9(competitors.projectId, projectId)).run();
7781
7863
  const normalizedCompetitors = normalizeCompetitorList2(config.spec.competitors);
7782
7864
  for (const domain of normalizedCompetitors) {
7783
7865
  tx.insert(competitors).values({
7784
- id: crypto10.randomUUID(),
7866
+ id: crypto11.randomUUID(),
7785
7867
  projectId,
7786
7868
  domain,
7787
7869
  provenance: "cli",
@@ -7797,7 +7879,7 @@ async function applyRoutes(app, opts) {
7797
7879
  });
7798
7880
  const AV_KIND = SchedulableRunKinds["answer-visibility"];
7799
7881
  if (resolvedSchedule) {
7800
- const existingSched = tx.select().from(schedules).where(and5(eq8(schedules.projectId, projectId), eq8(schedules.kind, AV_KIND))).get();
7882
+ const existingSched = tx.select().from(schedules).where(and5(eq9(schedules.projectId, projectId), eq9(schedules.kind, AV_KIND))).get();
7801
7883
  if (existingSched) {
7802
7884
  tx.update(schedules).set({
7803
7885
  cronExpr: resolvedSchedule.cronExpr,
@@ -7806,10 +7888,10 @@ async function applyRoutes(app, opts) {
7806
7888
  providers: config.spec.schedule?.providers ?? [],
7807
7889
  enabled: true,
7808
7890
  updatedAt: now
7809
- }).where(eq8(schedules.id, existingSched.id)).run();
7891
+ }).where(eq9(schedules.id, existingSched.id)).run();
7810
7892
  } else {
7811
7893
  tx.insert(schedules).values({
7812
- id: crypto10.randomUUID(),
7894
+ id: crypto11.randomUUID(),
7813
7895
  projectId,
7814
7896
  kind: AV_KIND,
7815
7897
  cronExpr: resolvedSchedule.cronExpr,
@@ -7823,21 +7905,21 @@ async function applyRoutes(app, opts) {
7823
7905
  }
7824
7906
  scheduleAction = "upsert";
7825
7907
  } else if (deleteSchedule) {
7826
- const existingSched = tx.select().from(schedules).where(and5(eq8(schedules.projectId, projectId), eq8(schedules.kind, AV_KIND))).get();
7908
+ const existingSched = tx.select().from(schedules).where(and5(eq9(schedules.projectId, projectId), eq9(schedules.kind, AV_KIND))).get();
7827
7909
  if (existingSched) {
7828
- tx.delete(schedules).where(and5(eq8(schedules.projectId, projectId), eq8(schedules.kind, AV_KIND))).run();
7910
+ tx.delete(schedules).where(and5(eq9(schedules.projectId, projectId), eq9(schedules.kind, AV_KIND))).run();
7829
7911
  scheduleAction = "delete";
7830
7912
  }
7831
7913
  }
7832
7914
  if (hasNotifications) {
7833
- tx.delete(notifications).where(eq8(notifications.projectId, projectId)).run();
7915
+ tx.delete(notifications).where(eq9(notifications.projectId, projectId)).run();
7834
7916
  for (const notif of config.spec.notifications) {
7835
7917
  tx.insert(notifications).values({
7836
- id: crypto10.randomUUID(),
7918
+ id: crypto11.randomUUID(),
7837
7919
  projectId,
7838
7920
  channel: notif.channel,
7839
7921
  config: { url: notif.url, events: notif.events },
7840
- webhookSecret: crypto10.randomBytes(32).toString("hex"),
7922
+ webhookSecret: crypto11.randomBytes(32).toString("hex"),
7841
7923
  enabled: true,
7842
7924
  createdAt: now,
7843
7925
  updatedAt: now
@@ -7864,7 +7946,7 @@ async function applyRoutes(app, opts) {
7864
7946
  if ("google" in rawSpec && config.spec.google?.gsc?.propertyUrl) {
7865
7947
  opts?.onGoogleConnectionPropertyUpdated?.(config.spec.canonicalDomain, "gsc", config.spec.google.gsc.propertyUrl);
7866
7948
  }
7867
- const project = app.db.select().from(projects).where(eq8(projects.id, projectId)).get();
7949
+ const project = app.db.select().from(projects).where(eq9(projects.id, projectId)).get();
7868
7950
  return reply.status(200).send({
7869
7951
  id: project.id,
7870
7952
  name: project.name,
@@ -7909,7 +7991,7 @@ function normalizeCompetitorList2(domains) {
7909
7991
  }
7910
7992
 
7911
7993
  // ../api-routes/src/history.ts
7912
- import { and as and6, eq as eq9, desc as desc2, inArray as inArray2 } from "drizzle-orm";
7994
+ import { and as and6, eq as eq10, desc as desc2, inArray as inArray3 } from "drizzle-orm";
7913
7995
 
7914
7996
  // ../api-routes/src/notification-redaction.ts
7915
7997
  var REDACTED_URL = {
@@ -7955,19 +8037,19 @@ function redactNotificationDiff(value) {
7955
8037
  async function historyRoutes(app) {
7956
8038
  app.get("/projects/:name/history", async (request, reply) => {
7957
8039
  const project = resolveProject(app.db, request.params.name);
7958
- const rows = app.db.select().from(auditLog).where(eq9(auditLog.projectId, project.id)).orderBy(desc2(auditLog.createdAt)).all();
8040
+ const rows = app.db.select().from(auditLog).where(eq10(auditLog.projectId, project.id)).orderBy(desc2(auditLog.createdAt)).all();
7959
8041
  return reply.send(rows.map(formatAuditEntry));
7960
8042
  });
7961
8043
  app.get("/history", async (request, reply) => {
7962
8044
  const scopedProjectId = request.apiKey?.projectId;
7963
- const rows = app.db.select().from(auditLog).where(scopedProjectId ? eq9(auditLog.projectId, scopedProjectId) : void 0).orderBy(desc2(auditLog.createdAt)).all();
8045
+ const rows = app.db.select().from(auditLog).where(scopedProjectId ? eq10(auditLog.projectId, scopedProjectId) : void 0).orderBy(desc2(auditLog.createdAt)).all();
7964
8046
  return reply.send(rows.map(formatAuditEntry));
7965
8047
  });
7966
8048
  app.get("/projects/:name/snapshots", async (request, reply) => {
7967
8049
  const project = resolveProject(app.db, request.params.name);
7968
8050
  const limit = parseInt(request.query.limit ?? "50", 10);
7969
8051
  const offset = parseInt(request.query.offset ?? "0", 10);
7970
- const projectRuns = app.db.select({ id: runs.id }).from(runs).where(and6(eq9(runs.projectId, project.id), notProbeRun())).all();
8052
+ const projectRuns = app.db.select({ id: runs.id }).from(runs).where(and6(eq10(runs.projectId, project.id), notProbeRun())).all();
7971
8053
  if (projectRuns.length === 0) {
7972
8054
  return reply.send({ snapshots: [], total: 0 });
7973
8055
  }
@@ -7986,7 +8068,7 @@ async function historyRoutes(app) {
7986
8068
  recommendedCompetitors: querySnapshots.recommendedCompetitors,
7987
8069
  location: querySnapshots.location,
7988
8070
  createdAt: querySnapshots.createdAt
7989
- }).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(inArray2(querySnapshots.runId, projectRuns.map((r) => r.id))).orderBy(desc2(querySnapshots.createdAt)).all();
8071
+ }).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, projectRuns.map((r) => r.id))).orderBy(desc2(querySnapshots.createdAt)).all();
7990
8072
  const locationFilter = request.query.location;
7991
8073
  const filtered = locationFilter !== void 0 ? allSnapshots.filter((s) => s.location === (locationFilter || null)) : allSnapshots;
7992
8074
  const total = filtered.length;
@@ -8015,13 +8097,13 @@ async function historyRoutes(app) {
8015
8097
  });
8016
8098
  app.get("/projects/:name/timeline", async (request, reply) => {
8017
8099
  const project = resolveProject(app.db, request.params.name);
8018
- const projectQueries = app.db.select().from(queries).where(eq9(queries.projectId, project.id)).all();
8019
- const projectRuns = app.db.select().from(runs).where(and6(eq9(runs.projectId, project.id), notProbeRun())).orderBy(runs.createdAt).all();
8100
+ const projectQueries = app.db.select().from(queries).where(eq10(queries.projectId, project.id)).all();
8101
+ const projectRuns = app.db.select().from(runs).where(and6(eq10(runs.projectId, project.id), notProbeRun())).orderBy(runs.createdAt).all();
8020
8102
  if (projectRuns.length === 0 || projectQueries.length === 0) {
8021
8103
  return reply.send([]);
8022
8104
  }
8023
8105
  const runIds = new Set(projectRuns.map((r) => r.id));
8024
- const rawSnapshots = app.db.select().from(querySnapshots).where(inArray2(querySnapshots.runId, [...runIds])).all();
8106
+ const rawSnapshots = app.db.select().from(querySnapshots).where(inArray3(querySnapshots.runId, [...runIds])).all();
8025
8107
  const timelineLocationFilter = request.query.location;
8026
8108
  const filteredSnapshots = timelineLocationFilter !== void 0 ? rawSnapshots.filter((s) => s.location === (timelineLocationFilter || null)) : rawSnapshots;
8027
8109
  const allSnapshots = filteredSnapshots.map((snapshot) => ({
@@ -8142,7 +8224,7 @@ async function historyRoutes(app) {
8142
8224
  throw validationError("Both run1 and run2 query params are required");
8143
8225
  }
8144
8226
  const requestedRunIds = [.../* @__PURE__ */ new Set([run1, run2])];
8145
- const runRows = app.db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(inArray2(runs.id, requestedRunIds)).all();
8227
+ const runRows = app.db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(inArray3(runs.id, requestedRunIds)).all();
8146
8228
  const runsById = new Map(runRows.map((row) => [row.id, row]));
8147
8229
  for (const runId of requestedRunIds) {
8148
8230
  const run = runsById.get(runId);
@@ -8156,14 +8238,14 @@ async function historyRoutes(app) {
8156
8238
  citationState: querySnapshots.citationState,
8157
8239
  answerMentioned: querySnapshots.answerMentioned,
8158
8240
  answerText: querySnapshots.answerText
8159
- }).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(eq9(querySnapshots.runId, run1)).all();
8241
+ }).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(eq10(querySnapshots.runId, run1)).all();
8160
8242
  const snaps2 = app.db.select({
8161
8243
  queryId: querySnapshots.queryId,
8162
8244
  query: queries.query,
8163
8245
  citationState: querySnapshots.citationState,
8164
8246
  answerMentioned: querySnapshots.answerMentioned,
8165
8247
  answerText: querySnapshots.answerText
8166
- }).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(eq9(querySnapshots.runId, run2)).all();
8248
+ }).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(eq10(querySnapshots.runId, run2)).all();
8167
8249
  const map1 = /* @__PURE__ */ new Map();
8168
8250
  for (const s of snaps1) {
8169
8251
  const resolved = {
@@ -8227,13 +8309,13 @@ function formatAuditEntry(row) {
8227
8309
  }
8228
8310
 
8229
8311
  // ../api-routes/src/analytics.ts
8230
- import { and as and7, eq as eq10, desc as desc3, inArray as inArray3 } from "drizzle-orm";
8312
+ import { and as and7, eq as eq11, desc as desc3, inArray as inArray4 } from "drizzle-orm";
8231
8313
  async function analyticsRoutes(app) {
8232
8314
  app.get("/projects/:name/analytics/metrics", async (request, reply) => {
8233
8315
  const project = resolveProject(app.db, request.params.name);
8234
8316
  const window = parseWindow(request.query.window);
8235
8317
  const cutoff = windowCutoff(window);
8236
- const projectRuns = app.db.select().from(runs).where(and7(eq10(runs.projectId, project.id), eq10(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
8318
+ const projectRuns = app.db.select().from(runs).where(and7(eq11(runs.projectId, project.id), eq11(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
8237
8319
  if (projectRuns.length === 0) {
8238
8320
  return reply.send({
8239
8321
  window,
@@ -8254,14 +8336,14 @@ async function analyticsRoutes(app) {
8254
8336
  answerMentioned: querySnapshots.answerMentioned,
8255
8337
  answerText: querySnapshots.answerText,
8256
8338
  createdAt: querySnapshots.createdAt
8257
- }).from(querySnapshots).where(inArray3(querySnapshots.runId, runIds)).all());
8339
+ }).from(querySnapshots).where(inArray4(querySnapshots.runId, runIds)).all());
8258
8340
  const allSnapshots = rawSnapshots.map((s) => ({
8259
8341
  ...s,
8260
8342
  resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
8261
8343
  }));
8262
- const projectQueries = app.db.select({ id: queries.id, createdAt: queries.createdAt }).from(queries).where(eq10(queries.projectId, project.id)).all();
8344
+ const projectQueries = app.db.select({ id: queries.id, createdAt: queries.createdAt }).from(queries).where(eq11(queries.projectId, project.id)).all();
8263
8345
  const queryCreatedAt = new Map(projectQueries.map((q) => [q.id, q.createdAt]));
8264
- const mentionShareCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq10(competitors.projectId, project.id)).all().map((c) => ({
8346
+ const mentionShareCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq11(competitors.projectId, project.id)).all().map((c) => ({
8265
8347
  domain: c.domain,
8266
8348
  brandTokens: [brandLabelFromDomain(c.domain)].filter((t) => t.length >= 3)
8267
8349
  }));
@@ -8285,14 +8367,14 @@ async function analyticsRoutes(app) {
8285
8367
  const project = resolveProject(app.db, request.params.name);
8286
8368
  const window = parseWindow(request.query.window);
8287
8369
  const cutoff = windowCutoff(window);
8288
- const completedRuns = app.db.select().from(runs).where(and7(eq10(runs.projectId, project.id), eq10(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc3(runs.createdAt), desc3(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial");
8370
+ const completedRuns = app.db.select().from(runs).where(and7(eq11(runs.projectId, project.id), eq11(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc3(runs.createdAt), desc3(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial");
8289
8371
  const latestGroup = groupRunsByCreatedAt(completedRuns)[0] ?? [];
8290
8372
  const latestGroupRunIds = latestGroup.map((r) => r.id);
8291
8373
  const latestRun = pickGroupRepresentative(latestGroup);
8292
8374
  if (!latestRun) {
8293
8375
  return reply.send({ cited: [], gap: [], uncited: [], mentionedQueries: [], mentionGap: [], notMentioned: [], runId: "", window });
8294
8376
  }
8295
- const windowRuns = app.db.select().from(runs).where(and7(eq10(runs.projectId, project.id), eq10(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
8377
+ const windowRuns = app.db.select().from(runs).where(and7(eq11(runs.projectId, project.id), eq11(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
8296
8378
  const windowRunIds = windowRuns.map((r) => r.id);
8297
8379
  const runIdToCreatedAt = new Map(windowRuns.map((r) => [r.id, r.createdAt]));
8298
8380
  const consistencyMap = /* @__PURE__ */ new Map();
@@ -8303,7 +8385,7 @@ async function analyticsRoutes(app) {
8303
8385
  citationState: querySnapshots.citationState,
8304
8386
  answerMentioned: querySnapshots.answerMentioned,
8305
8387
  answerText: querySnapshots.answerText
8306
- }).from(querySnapshots).where(inArray3(querySnapshots.runId, windowRunIds)).all());
8388
+ }).from(querySnapshots).where(inArray4(querySnapshots.runId, windowRunIds)).all());
8307
8389
  for (const s of allWindowSnaps) {
8308
8390
  const timePoint = runIdToCreatedAt.get(s.runId) ?? s.runId;
8309
8391
  let entry = consistencyMap.get(s.queryId);
@@ -8324,7 +8406,7 @@ async function analyticsRoutes(app) {
8324
8406
  answerMentioned: querySnapshots.answerMentioned,
8325
8407
  answerText: querySnapshots.answerText,
8326
8408
  competitorOverlap: querySnapshots.competitorOverlap
8327
- }).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, latestGroupRunIds)).all());
8409
+ }).from(querySnapshots).leftJoin(queries, eq11(querySnapshots.queryId, queries.id)).where(inArray4(querySnapshots.runId, latestGroupRunIds)).all());
8328
8410
  const snapshots = rawSnapshots.map((s) => ({
8329
8411
  ...s,
8330
8412
  resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
@@ -8416,14 +8498,14 @@ async function analyticsRoutes(app) {
8416
8498
  }
8417
8499
  const classifyCtx = {
8418
8500
  projectDomains: effectiveDomains(project),
8419
- competitorDomains: app.db.select({ domain: competitors.domain }).from(competitors).where(eq10(competitors.projectId, project.id)).all().map((r) => r.domain)
8501
+ competitorDomains: app.db.select({ domain: competitors.domain }).from(competitors).where(eq11(competitors.projectId, project.id)).all().map((r) => r.domain)
8420
8502
  };
8421
8503
  const storedSurfaceClasses = /* @__PURE__ */ new Map();
8422
- for (const row of app.db.select({ domain: domainClassifications.domain, competitorType: domainClassifications.competitorType }).from(domainClassifications).where(eq10(domainClassifications.projectId, project.id)).all()) {
8504
+ for (const row of app.db.select({ domain: domainClassifications.domain, competitorType: domainClassifications.competitorType }).from(domainClassifications).where(eq11(domainClassifications.projectId, project.id)).all()) {
8423
8505
  const mapped = surfaceClassFromCompetitorType(row.competitorType);
8424
8506
  if (mapped) storedSurfaceClasses.set(normalizeProjectDomain(row.domain), mapped);
8425
8507
  }
8426
- const windowRuns = app.db.select().from(runs).where(and7(eq10(runs.projectId, project.id), eq10(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc3(runs.createdAt), desc3(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
8508
+ const windowRuns = app.db.select().from(runs).where(and7(eq11(runs.projectId, project.id), eq11(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc3(runs.createdAt), desc3(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
8427
8509
  if (windowRuns.length === 0) {
8428
8510
  return reply.send({
8429
8511
  overall: [],
@@ -8443,7 +8525,7 @@ async function analyticsRoutes(app) {
8443
8525
  query: queries.query,
8444
8526
  provider: querySnapshots.provider,
8445
8527
  rawResponse: querySnapshots.rawResponse
8446
- }).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, windowRunIds)).all();
8528
+ }).from(querySnapshots).leftJoin(queries, eq11(querySnapshots.queryId, queries.id)).where(inArray4(querySnapshots.runId, windowRunIds)).all();
8447
8529
  const overallCounts = /* @__PURE__ */ new Map();
8448
8530
  const byQuery = {};
8449
8531
  const overallDomains = /* @__PURE__ */ new Map();
@@ -8688,7 +8770,7 @@ function buildCategoryCounts(counts2) {
8688
8770
  }
8689
8771
 
8690
8772
  // ../api-routes/src/intelligence.ts
8691
- import { eq as eq11, desc as desc4, and as and8, inArray as inArray4, like } from "drizzle-orm";
8773
+ import { eq as eq12, desc as desc4, and as and8, inArray as inArray5, like } from "drizzle-orm";
8692
8774
  var SEVERITY_RANK = { low: 0, medium: 1, high: 2, critical: 3 };
8693
8775
  function severitiesAtOrAbove(min) {
8694
8776
  const floor = SEVERITY_RANK[min];
@@ -8806,19 +8888,19 @@ function aggregateHealthSnapshots(projectId, rows) {
8806
8888
  async function intelligenceRoutes(app) {
8807
8889
  app.get("/projects/:name/insights", async (request, reply) => {
8808
8890
  const project = resolveProject(app.db, request.params.name);
8809
- const conditions = [eq11(insights.projectId, project.id)];
8891
+ const conditions = [eq12(insights.projectId, project.id)];
8810
8892
  if (request.query.runId) {
8811
- conditions.push(eq11(insights.runId, request.query.runId));
8893
+ conditions.push(eq12(insights.runId, request.query.runId));
8812
8894
  }
8813
8895
  const typeFilter = request.query.type?.trim();
8814
8896
  if (typeFilter) {
8815
8897
  conditions.push(
8816
- typeFilter.endsWith("*") ? like(insights.type, `${typeFilter.slice(0, -1)}%`) : eq11(insights.type, typeFilter)
8898
+ typeFilter.endsWith("*") ? like(insights.type, `${typeFilter.slice(0, -1)}%`) : eq12(insights.type, typeFilter)
8817
8899
  );
8818
8900
  }
8819
8901
  const severityFilter = request.query.severity?.trim();
8820
8902
  if (severityFilter) {
8821
- conditions.push(inArray4(insights.severity, severitiesAtOrAbove(severityFilter)));
8903
+ conditions.push(inArray5(insights.severity, severitiesAtOrAbove(severityFilter)));
8822
8904
  }
8823
8905
  let limit;
8824
8906
  if (request.query.limit !== void 0) {
@@ -8836,7 +8918,7 @@ async function intelligenceRoutes(app) {
8836
8918
  });
8837
8919
  app.get("/projects/:name/insights/:id", async (request, reply) => {
8838
8920
  const project = resolveProject(app.db, request.params.name);
8839
- const row = app.db.select().from(insights).where(eq11(insights.id, request.params.id)).get();
8921
+ const row = app.db.select().from(insights).where(eq12(insights.id, request.params.id)).get();
8840
8922
  if (!row || row.projectId !== project.id) {
8841
8923
  throw notFound("Insight", request.params.id);
8842
8924
  }
@@ -8844,19 +8926,19 @@ async function intelligenceRoutes(app) {
8844
8926
  });
8845
8927
  app.post("/projects/:name/insights/:id/dismiss", async (request, reply) => {
8846
8928
  const project = resolveProject(app.db, request.params.name);
8847
- const row = app.db.select().from(insights).where(eq11(insights.id, request.params.id)).get();
8929
+ const row = app.db.select().from(insights).where(eq12(insights.id, request.params.id)).get();
8848
8930
  if (!row || row.projectId !== project.id) {
8849
8931
  throw notFound("Insight", request.params.id);
8850
8932
  }
8851
- app.db.update(insights).set({ dismissed: true }).where(eq11(insights.id, request.params.id)).run();
8933
+ app.db.update(insights).set({ dismissed: true }).where(eq12(insights.id, request.params.id)).run();
8852
8934
  return reply.send({ ok: true });
8853
8935
  });
8854
8936
  app.get("/projects/:name/health/latest", async (request, reply) => {
8855
8937
  const project = resolveProject(app.db, request.params.name);
8856
8938
  const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and8(
8857
- eq11(runs.projectId, project.id),
8858
- eq11(runs.kind, RunKinds["answer-visibility"]),
8859
- inArray4(runs.status, [RunStatuses.completed, RunStatuses.partial]),
8939
+ eq12(runs.projectId, project.id),
8940
+ eq12(runs.kind, RunKinds["answer-visibility"]),
8941
+ inArray5(runs.status, [RunStatuses.completed, RunStatuses.partial]),
8860
8942
  // Health-latest is the dashboard headline; probe runs must not
8861
8943
  // displace the most recent real visibility sweep.
8862
8944
  notProbeRun()
@@ -8865,14 +8947,14 @@ async function intelligenceRoutes(app) {
8865
8947
  const latestGroupRunIds = latestGroup.map((r) => r.id);
8866
8948
  if (latestGroupRunIds.length > 0) {
8867
8949
  const groupRows = app.db.select().from(healthSnapshots).where(and8(
8868
- eq11(healthSnapshots.projectId, project.id),
8869
- inArray4(healthSnapshots.runId, latestGroupRunIds)
8950
+ eq12(healthSnapshots.projectId, project.id),
8951
+ inArray5(healthSnapshots.runId, latestGroupRunIds)
8870
8952
  )).all();
8871
8953
  if (groupRows.length > 0) {
8872
8954
  return reply.send(aggregateHealthSnapshots(project.id, groupRows));
8873
8955
  }
8874
8956
  }
8875
- const fallback = app.db.select().from(healthSnapshots).where(eq11(healthSnapshots.projectId, project.id)).orderBy(desc4(healthSnapshots.createdAt)).limit(1).get();
8957
+ const fallback = app.db.select().from(healthSnapshots).where(eq12(healthSnapshots.projectId, project.id)).orderBy(desc4(healthSnapshots.createdAt)).limit(1).get();
8876
8958
  if (!fallback) {
8877
8959
  return reply.send(emptyHealthSnapshot(project.id));
8878
8960
  }
@@ -8882,20 +8964,20 @@ async function intelligenceRoutes(app) {
8882
8964
  const project = resolveProject(app.db, request.params.name);
8883
8965
  const parsed = request.query.limit ? parseInt(request.query.limit, 10) : NaN;
8884
8966
  const limit = Number.isNaN(parsed) ? 30 : Math.min(Math.max(parsed, 1), 100);
8885
- const rows = app.db.select().from(healthSnapshots).where(eq11(healthSnapshots.projectId, project.id)).orderBy(desc4(healthSnapshots.createdAt)).limit(limit).all();
8967
+ const rows = app.db.select().from(healthSnapshots).where(eq12(healthSnapshots.projectId, project.id)).orderBy(desc4(healthSnapshots.createdAt)).limit(limit).all();
8886
8968
  return reply.send(rows.map(mapHealthRow));
8887
8969
  });
8888
8970
  }
8889
8971
 
8890
8972
  // ../api-routes/src/report.ts
8891
- import { and as and12, 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";
8973
+ import { and as and12, desc as desc7, eq as eq16, gte as gte2, inArray as inArray7, lt, lte, ne as ne2, or as or3, sql as sql7 } from "drizzle-orm";
8892
8974
 
8893
8975
  // ../api-routes/src/content.ts
8894
- import crypto11 from "crypto";
8895
- import { and as and10, desc as desc6, eq as eq13 } from "drizzle-orm";
8976
+ import crypto12 from "crypto";
8977
+ import { and as and10, desc as desc6, eq as eq14 } from "drizzle-orm";
8896
8978
 
8897
8979
  // ../api-routes/src/content-data.ts
8898
- import { and as and9, eq as eq12, desc as desc5, inArray as inArray5 } from "drizzle-orm";
8980
+ import { and as and9, eq as eq13, desc as desc5, inArray as inArray6 } from "drizzle-orm";
8899
8981
  var RECENT_RUNS_WINDOW = 5;
8900
8982
  function loadOrchestratorInput(db, project, locationFilter = void 0) {
8901
8983
  const projectId = project.id;
@@ -8944,7 +9026,7 @@ function loadOrchestratorInput(db, project, locationFilter = void 0) {
8944
9026
  };
8945
9027
  }
8946
9028
  function loadDomainClasses(db, projectId) {
8947
- const rows = db.select({ domain: domainClassifications.domain, competitorType: domainClassifications.competitorType }).from(domainClassifications).where(eq12(domainClassifications.projectId, projectId)).all();
9029
+ const rows = db.select({ domain: domainClassifications.domain, competitorType: domainClassifications.competitorType }).from(domainClassifications).where(eq13(domainClassifications.projectId, projectId)).all();
8948
9030
  return new Map(rows.map((r) => [normalizeDomain(r.domain), r.competitorType]));
8949
9031
  }
8950
9032
  function buildQueryIntentModifiers(project, locationFilter) {
@@ -9016,22 +9098,22 @@ var US_REGION_ABBREVIATIONS = {
9016
9098
  wyoming: "wy"
9017
9099
  };
9018
9100
  function listQueries(db, projectId) {
9019
- const rows = db.select({ text: queries.query }).from(queries).where(eq12(queries.projectId, projectId)).all();
9101
+ const rows = db.select({ text: queries.query }).from(queries).where(eq13(queries.projectId, projectId)).all();
9020
9102
  return rows.map((r) => r.text);
9021
9103
  }
9022
9104
  function listCompetitorDomains(db, projectId) {
9023
- const rows = db.select({ domain: competitors.domain }).from(competitors).where(eq12(competitors.projectId, projectId)).all();
9105
+ const rows = db.select({ domain: competitors.domain }).from(competitors).where(eq13(competitors.projectId, projectId)).all();
9024
9106
  return rows.map((r) => r.domain);
9025
9107
  }
9026
9108
  function listRecentAnswerVisibilityRunIds(db, projectId, limit, locationFilter) {
9027
9109
  const rows = db.select({ id: runs.id, location: runs.location }).from(runs).where(
9028
9110
  and9(
9029
- eq12(runs.projectId, projectId),
9030
- eq12(runs.kind, RunKinds["answer-visibility"]),
9111
+ eq13(runs.projectId, projectId),
9112
+ eq13(runs.kind, RunKinds["answer-visibility"]),
9031
9113
  // Queued/running/failed/cancelled runs may have partial or no
9032
9114
  // snapshots; including them risks pointing latestRunId at a run with
9033
9115
  // no usable evidence.
9034
- inArray5(runs.status, [RunStatuses.completed, RunStatuses.partial]),
9116
+ inArray6(runs.status, [RunStatuses.completed, RunStatuses.partial]),
9035
9117
  // Probe runs are operator/agent test runs; they must not poison the
9036
9118
  // recent-runs window the content engine uses to recommend actions.
9037
9119
  notProbeRun()
@@ -9041,22 +9123,22 @@ function listRecentAnswerVisibilityRunIds(db, projectId, limit, locationFilter)
9041
9123
  return filtered.slice(0, limit).map((r) => r.id);
9042
9124
  }
9043
9125
  function lookupRunTimestamp(db, runId) {
9044
- const row = db.select({ createdAt: runs.createdAt }).from(runs).where(eq12(runs.id, runId)).get();
9126
+ const row = db.select({ createdAt: runs.createdAt }).from(runs).where(eq13(runs.id, runId)).get();
9045
9127
  return row?.createdAt ?? "";
9046
9128
  }
9047
9129
  function listGscPagesForProject(db, projectId) {
9048
- const rows = db.selectDistinct({ page: gscSearchData.page }).from(gscSearchData).where(eq12(gscSearchData.projectId, projectId)).all();
9130
+ const rows = db.selectDistinct({ page: gscSearchData.page }).from(gscSearchData).where(eq13(gscSearchData.projectId, projectId)).all();
9049
9131
  return rows.map((r) => r.page);
9050
9132
  }
9051
9133
  function listGa4LandingPagesForProject(db, projectId) {
9052
- const rows = db.selectDistinct({ landingPage: gaTrafficSnapshots.landingPage }).from(gaTrafficSnapshots).where(eq12(gaTrafficSnapshots.projectId, projectId)).all();
9134
+ const rows = db.selectDistinct({ landingPage: gaTrafficSnapshots.landingPage }).from(gaTrafficSnapshots).where(eq13(gaTrafficSnapshots.projectId, projectId)).all();
9053
9135
  return rows.map((r) => r.landingPage);
9054
9136
  }
9055
9137
  function buildGaTrafficByPage(db, projectId) {
9056
9138
  const rows = db.select({
9057
9139
  landingPage: gaTrafficSnapshots.landingPage,
9058
9140
  sessions: gaTrafficSnapshots.sessions
9059
- }).from(gaTrafficSnapshots).where(eq12(gaTrafficSnapshots.projectId, projectId)).all();
9141
+ }).from(gaTrafficSnapshots).where(eq13(gaTrafficSnapshots.projectId, projectId)).all();
9060
9142
  const map = /* @__PURE__ */ new Map();
9061
9143
  for (const row of rows) {
9062
9144
  const path8 = extractPath2(row.landingPage);
@@ -9066,24 +9148,24 @@ function buildGaTrafficByPage(db, projectId) {
9066
9148
  return map;
9067
9149
  }
9068
9150
  function sumAiReferralSessions(db, projectId) {
9069
- const rows = db.select({ sessions: gaAiReferrals.sessions }).from(gaAiReferrals).where(eq12(gaAiReferrals.projectId, projectId)).all();
9151
+ const rows = db.select({ sessions: gaAiReferrals.sessions }).from(gaAiReferrals).where(eq13(gaAiReferrals.projectId, projectId)).all();
9070
9152
  return rows.reduce((acc, r) => acc + (r.sessions ?? 0), 0);
9071
9153
  }
9072
9154
  function buildCandidateQueries(opts) {
9073
9155
  if (opts.candidateQueryStrings.length === 0 || opts.recentRunIds.length === 0) {
9074
9156
  return opts.candidateQueryStrings.map((query) => emptyCandidate(query));
9075
9157
  }
9076
- const queryRows = opts.db.select({ id: queries.id, text: queries.query }).from(queries).where(eq12(queries.projectId, opts.projectId)).all();
9158
+ const queryRows = opts.db.select({ id: queries.id, text: queries.query }).from(queries).where(eq13(queries.projectId, opts.projectId)).all();
9077
9159
  const queryIdByText = new Map(queryRows.map((r) => [r.text, r.id]));
9078
9160
  const candidateQueryIds = opts.candidateQueryStrings.map((q) => queryIdByText.get(q)).filter((id) => Boolean(id));
9079
- const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(inArray5(querySnapshots.runId, opts.recentRunIds)).all()).filter((r) => candidateQueryIds.includes(r.queryId));
9161
+ const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(inArray6(querySnapshots.runId, opts.recentRunIds)).all()).filter((r) => candidateQueryIds.includes(r.queryId));
9080
9162
  const snapshotsByQuery = /* @__PURE__ */ new Map();
9081
9163
  for (const row of snapshotRows) {
9082
9164
  const list = snapshotsByQuery.get(row.queryId) ?? [];
9083
9165
  list.push(row);
9084
9166
  snapshotsByQuery.set(row.queryId, list);
9085
9167
  }
9086
- const gscRows = opts.db.select().from(gscSearchData).where(eq12(gscSearchData.projectId, opts.projectId)).all();
9168
+ const gscRows = opts.db.select().from(gscSearchData).where(eq13(gscSearchData.projectId, opts.projectId)).all();
9087
9169
  const gscByQuery = aggregateGscByQuery(gscRows);
9088
9170
  return opts.candidateQueryStrings.map((query) => {
9089
9171
  const queryId = queryIdByText.get(query);
@@ -9272,7 +9354,7 @@ function extractPath2(url) {
9272
9354
 
9273
9355
  // ../api-routes/src/content.ts
9274
9356
  function loadDismissedTargetRefs(db, projectId) {
9275
- const rows = db.select({ targetRef: contentTargetDismissals.targetRef }).from(contentTargetDismissals).where(eq13(contentTargetDismissals.projectId, projectId)).all();
9357
+ const rows = db.select({ targetRef: contentTargetDismissals.targetRef }).from(contentTargetDismissals).where(eq14(contentTargetDismissals.projectId, projectId)).all();
9276
9358
  return new Set(rows.map((r) => r.targetRef));
9277
9359
  }
9278
9360
  function formatDismissalRow(row) {
@@ -9367,7 +9449,7 @@ async function contentRoutes(app, opts = {}) {
9367
9449
  });
9368
9450
  app.get("/projects/:name/content/dismissals", async (request) => {
9369
9451
  const project = resolveProject(app.db, request.params.name);
9370
- const rows = app.db.select().from(contentTargetDismissals).where(eq13(contentTargetDismissals.projectId, project.id)).orderBy(contentTargetDismissals.dismissedAt).all();
9452
+ const rows = app.db.select().from(contentTargetDismissals).where(eq14(contentTargetDismissals.projectId, project.id)).orderBy(contentTargetDismissals.dismissedAt).all();
9371
9453
  const response = {
9372
9454
  dismissals: rows.map(formatDismissalRow)
9373
9455
  };
@@ -9382,7 +9464,7 @@ async function contentRoutes(app, opts = {}) {
9382
9464
  const { targetRef, addressedUrl, note } = parsed.data;
9383
9465
  const now = (/* @__PURE__ */ new Date()).toISOString();
9384
9466
  app.db.insert(contentTargetDismissals).values({
9385
- id: crypto11.randomUUID(),
9467
+ id: crypto12.randomUUID(),
9386
9468
  projectId: project.id,
9387
9469
  targetRef,
9388
9470
  addressedUrl: addressedUrl ?? null,
@@ -9397,8 +9479,8 @@ async function contentRoutes(app, opts = {}) {
9397
9479
  }
9398
9480
  }).run();
9399
9481
  const row = app.db.select().from(contentTargetDismissals).where(and10(
9400
- eq13(contentTargetDismissals.projectId, project.id),
9401
- eq13(contentTargetDismissals.targetRef, targetRef)
9482
+ eq14(contentTargetDismissals.projectId, project.id),
9483
+ eq14(contentTargetDismissals.targetRef, targetRef)
9402
9484
  )).get();
9403
9485
  if (!row) throw notFound("contentTargetDismissal", targetRef);
9404
9486
  return formatDismissalRow(row);
@@ -9407,8 +9489,8 @@ async function contentRoutes(app, opts = {}) {
9407
9489
  const project = resolveProject(app.db, request.params.name);
9408
9490
  const { targetRef } = request.params;
9409
9491
  const result = app.db.delete(contentTargetDismissals).where(and10(
9410
- eq13(contentTargetDismissals.projectId, project.id),
9411
- eq13(contentTargetDismissals.targetRef, targetRef)
9492
+ eq14(contentTargetDismissals.projectId, project.id),
9493
+ eq14(contentTargetDismissals.targetRef, targetRef)
9412
9494
  )).run();
9413
9495
  if (result.changes === 0) {
9414
9496
  throw notFound("contentTargetDismissal", targetRef);
@@ -9418,8 +9500,8 @@ async function contentRoutes(app, opts = {}) {
9418
9500
  app.get("/projects/:name/content/recommendations/:targetRef/analysis", async (request, reply) => {
9419
9501
  const project = resolveProject(app.db, request.params.name);
9420
9502
  const row = app.db.select().from(recommendationExplanations).where(and10(
9421
- eq13(recommendationExplanations.projectId, project.id),
9422
- eq13(recommendationExplanations.targetRef, request.params.targetRef)
9503
+ eq14(recommendationExplanations.projectId, project.id),
9504
+ eq14(recommendationExplanations.targetRef, request.params.targetRef)
9423
9505
  )).orderBy(desc6(recommendationExplanations.generatedAt)).limit(1).get();
9424
9506
  if (!row) {
9425
9507
  return reply.status(404).send({ error: { code: "NOT_FOUND", message: "No cached explanation for this targetRef." } });
@@ -9446,8 +9528,8 @@ async function contentRoutes(app, opts = {}) {
9446
9528
  }
9447
9529
  if (!body.forceRefresh) {
9448
9530
  const cached = app.db.select().from(recommendationExplanations).where(and10(
9449
- eq13(recommendationExplanations.projectId, project.id),
9450
- eq13(recommendationExplanations.targetRef, targetRef)
9531
+ eq14(recommendationExplanations.projectId, project.id),
9532
+ eq14(recommendationExplanations.targetRef, targetRef)
9451
9533
  )).orderBy(desc6(recommendationExplanations.generatedAt)).limit(1).get();
9452
9534
  if (cached) return reply.send(formatExplanationRow(cached));
9453
9535
  }
@@ -9461,7 +9543,7 @@ async function contentRoutes(app, opts = {}) {
9461
9543
  });
9462
9544
  const now = (/* @__PURE__ */ new Date()).toISOString();
9463
9545
  app.db.insert(recommendationExplanations).values({
9464
- id: crypto11.randomUUID(),
9546
+ id: crypto12.randomUUID(),
9465
9547
  projectId: project.id,
9466
9548
  targetRef,
9467
9549
  promptVersion: result.promptVersion,
@@ -9485,9 +9567,9 @@ async function contentRoutes(app, opts = {}) {
9485
9567
  }
9486
9568
  }).run();
9487
9569
  const row = app.db.select().from(recommendationExplanations).where(and10(
9488
- eq13(recommendationExplanations.projectId, project.id),
9489
- eq13(recommendationExplanations.targetRef, targetRef),
9490
- eq13(recommendationExplanations.promptVersion, result.promptVersion)
9570
+ eq14(recommendationExplanations.projectId, project.id),
9571
+ eq14(recommendationExplanations.targetRef, targetRef),
9572
+ eq14(recommendationExplanations.promptVersion, result.promptVersion)
9491
9573
  )).get();
9492
9574
  if (!row) throw notFound("recommendationExplanation", targetRef);
9493
9575
  return reply.send(formatExplanationRow(row));
@@ -9545,7 +9627,7 @@ async function contentRoutes(app, opts = {}) {
9545
9627
  });
9546
9628
  const now = (/* @__PURE__ */ new Date()).toISOString();
9547
9629
  app.db.insert(recommendationBriefs).values({
9548
- id: crypto11.randomUUID(),
9630
+ id: crypto12.randomUUID(),
9549
9631
  projectId: project.id,
9550
9632
  targetRef,
9551
9633
  promptVersion: result.promptVersion,
@@ -9569,16 +9651,16 @@ async function contentRoutes(app, opts = {}) {
9569
9651
  }
9570
9652
  }).run();
9571
9653
  const row = app.db.select().from(recommendationBriefs).where(and10(
9572
- eq13(recommendationBriefs.projectId, project.id),
9573
- eq13(recommendationBriefs.targetRef, targetRef),
9574
- eq13(recommendationBriefs.promptVersion, result.promptVersion)
9654
+ eq14(recommendationBriefs.projectId, project.id),
9655
+ eq14(recommendationBriefs.targetRef, targetRef),
9656
+ eq14(recommendationBriefs.promptVersion, result.promptVersion)
9575
9657
  )).get();
9576
9658
  if (!row) throw notFound("recommendationBrief", targetRef);
9577
9659
  return reply.send(formatBriefRow(row));
9578
9660
  });
9579
9661
  app.get("/projects/:name/content/domain-classifications", async (request) => {
9580
9662
  const project = resolveProject(app.db, request.params.name);
9581
- const rows = app.db.select().from(domainClassifications).where(eq13(domainClassifications.projectId, project.id)).orderBy(desc6(domainClassifications.hits)).all();
9663
+ const rows = app.db.select().from(domainClassifications).where(eq14(domainClassifications.projectId, project.id)).orderBy(desc6(domainClassifications.hits)).all();
9582
9664
  const response = {
9583
9665
  classifications: rows.map((r) => ({
9584
9666
  domain: r.domain,
@@ -9592,11 +9674,11 @@ async function contentRoutes(app, opts = {}) {
9592
9674
  }
9593
9675
  function lookupCachedBrief(db, projectId, targetRef, promptVersion) {
9594
9676
  const conditions = [
9595
- eq13(recommendationBriefs.projectId, projectId),
9596
- eq13(recommendationBriefs.targetRef, targetRef)
9677
+ eq14(recommendationBriefs.projectId, projectId),
9678
+ eq14(recommendationBriefs.targetRef, targetRef)
9597
9679
  ];
9598
9680
  if (promptVersion !== void 0) {
9599
- conditions.push(eq13(recommendationBriefs.promptVersion, promptVersion));
9681
+ conditions.push(eq14(recommendationBriefs.promptVersion, promptVersion));
9600
9682
  }
9601
9683
  return db.select().from(recommendationBriefs).where(and10(...conditions)).orderBy(desc6(recommendationBriefs.generatedAt)).limit(1).get();
9602
9684
  }
@@ -9624,7 +9706,7 @@ function winnabilityClassRank(winnabilityClass) {
9624
9706
  }
9625
9707
 
9626
9708
  // ../api-routes/src/gsc-totals.ts
9627
- import { and as and11, asc as asc2, eq as eq14, sql as sql6 } from "drizzle-orm";
9709
+ import { and as and11, asc as asc2, eq as eq15, sql as sql6 } from "drizzle-orm";
9628
9710
  function readGscDailyTotals(db, projectId, startDate, endDate) {
9629
9711
  const rows = db.select({
9630
9712
  date: gscDailyTotals.date,
@@ -9633,7 +9715,7 @@ function readGscDailyTotals(db, projectId, startDate, endDate) {
9633
9715
  position: gscDailyTotals.position
9634
9716
  }).from(gscDailyTotals).where(
9635
9717
  and11(
9636
- eq14(gscDailyTotals.projectId, projectId),
9718
+ eq15(gscDailyTotals.projectId, projectId),
9637
9719
  sql6`${gscDailyTotals.date} >= ${startDate}`,
9638
9720
  sql6`${gscDailyTotals.date} <= ${endDate}`
9639
9721
  )
@@ -11953,7 +12035,7 @@ function loadSnapshotsForRun(db, runId) {
11953
12035
  }
11954
12036
  function loadSnapshotsForRunIds(db, runIds) {
11955
12037
  if (runIds.length === 0) return [];
11956
- const rows = db.select().from(querySnapshots).where(inArray6(querySnapshots.runId, [...runIds])).all();
12038
+ const rows = db.select().from(querySnapshots).where(inArray7(querySnapshots.runId, [...runIds])).all();
11957
12039
  return rows.filter((r) => r.queryId !== null).map((r) => ({
11958
12040
  id: r.id,
11959
12041
  runId: r.runId,
@@ -11970,13 +12052,13 @@ function loadSnapshotsForRunIds(db, runIds) {
11970
12052
  }));
11971
12053
  }
11972
12054
  function loadQueryLookup(db, projectId) {
11973
- const rows = db.select().from(queries).where(eq15(queries.projectId, projectId)).all();
12055
+ const rows = db.select().from(queries).where(eq16(queries.projectId, projectId)).all();
11974
12056
  const byId = /* @__PURE__ */ new Map();
11975
12057
  for (const row of rows) byId.set(row.id, row.query);
11976
12058
  return { byId };
11977
12059
  }
11978
12060
  function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trackedQueries, windowDays) {
11979
- const allRows = db.select().from(gscSearchData).where(eq15(gscSearchData.projectId, projectId)).all();
12061
+ const allRows = db.select().from(gscSearchData).where(eq16(gscSearchData.projectId, projectId)).all();
11980
12062
  const allDailyTotals = readGscDailyTotals(db, projectId, "", "9999-12-31");
11981
12063
  let maxDate = "";
11982
12064
  for (const r of allRows) if (r.date > maxDate) maxDate = r.date;
@@ -12068,12 +12150,12 @@ function buildGaSection(db, projectId, windowDays) {
12068
12150
  const gaWindowKey = GA_WINDOW_SUMMARY_KEYS[windowDays];
12069
12151
  const windowSummary = gaWindowKey ? db.select().from(gaTrafficWindowSummaries).where(
12070
12152
  and12(
12071
- eq15(gaTrafficWindowSummaries.projectId, projectId),
12072
- eq15(gaTrafficWindowSummaries.windowKey, gaWindowKey)
12153
+ eq16(gaTrafficWindowSummaries.projectId, projectId),
12154
+ eq16(gaTrafficWindowSummaries.windowKey, gaWindowKey)
12073
12155
  )
12074
12156
  ).limit(1).get() : void 0;
12075
- const fallbackSummary = windowSummary ? null : db.select().from(gaTrafficSummaries).where(eq15(gaTrafficSummaries.projectId, projectId)).orderBy(desc7(gaTrafficSummaries.syncedAt)).limit(1).get();
12076
- const allSnapshotRows = db.select().from(gaTrafficSnapshots).where(eq15(gaTrafficSnapshots.projectId, projectId)).all();
12157
+ const fallbackSummary = windowSummary ? null : db.select().from(gaTrafficSummaries).where(eq16(gaTrafficSummaries.projectId, projectId)).orderBy(desc7(gaTrafficSummaries.syncedAt)).limit(1).get();
12158
+ const allSnapshotRows = db.select().from(gaTrafficSnapshots).where(eq16(gaTrafficSnapshots.projectId, projectId)).all();
12077
12159
  if (!windowSummary && !fallbackSummary && allSnapshotRows.length === 0) return null;
12078
12160
  let snapshotMaxDate = "";
12079
12161
  for (const r of allSnapshotRows) if (r.date > snapshotMaxDate) snapshotMaxDate = r.date;
@@ -12100,8 +12182,8 @@ function buildGaSection(db, projectId, windowDays) {
12100
12182
  organicSessions: data.organic
12101
12183
  })).sort((a, b) => b.sessions - a.sessions).slice(0, TOP_LANDING_PAGES_LIMIT);
12102
12184
  const aiConditions = [
12103
- eq15(gaAiReferrals.projectId, projectId),
12104
- eq15(gaAiReferrals.sourceDimension, "session")
12185
+ eq16(gaAiReferrals.projectId, projectId),
12186
+ eq16(gaAiReferrals.sourceDimension, "session")
12105
12187
  ];
12106
12188
  if (snapshotStartDate && snapshotMaxDate) {
12107
12189
  aiConditions.push(gte2(gaAiReferrals.date, snapshotStartDate));
@@ -12158,7 +12240,7 @@ function buildGaSection(db, projectId, windowDays) {
12158
12240
  };
12159
12241
  }
12160
12242
  function buildSocialReferrals(db, projectId) {
12161
- const rows = db.select().from(gaSocialReferrals).where(eq15(gaSocialReferrals.projectId, projectId)).all();
12243
+ const rows = db.select().from(gaSocialReferrals).where(eq16(gaSocialReferrals.projectId, projectId)).all();
12162
12244
  if (rows.length === 0) return null;
12163
12245
  let total = 0;
12164
12246
  let organic = 0;
@@ -12190,7 +12272,7 @@ function buildSocialReferrals(db, projectId) {
12190
12272
  };
12191
12273
  }
12192
12274
  function buildAiReferrals(db, projectId) {
12193
- const rows = db.select().from(gaAiReferrals).where(eq15(gaAiReferrals.projectId, projectId)).all();
12275
+ const rows = db.select().from(gaAiReferrals).where(eq16(gaAiReferrals.projectId, projectId)).all();
12194
12276
  if (rows.length === 0) return null;
12195
12277
  const dimSessionsByTuple = /* @__PURE__ */ new Map();
12196
12278
  for (const r of rows) {
@@ -12303,7 +12385,7 @@ function nonSubresourceReferralPathCondition() {
12303
12385
  function buildServerActivity(db, projectId, windowDays) {
12304
12386
  const sourceRows = db.select({ id: trafficSources.id }).from(trafficSources).where(
12305
12387
  and12(
12306
- eq15(trafficSources.projectId, projectId),
12388
+ eq16(trafficSources.projectId, projectId),
12307
12389
  ne2(trafficSources.status, TrafficSourceStatuses.archived)
12308
12390
  )
12309
12391
  ).all();
@@ -12319,8 +12401,8 @@ function buildServerActivity(db, projectId, windowDays) {
12319
12401
  const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
12320
12402
  db.select({ total: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
12321
12403
  and12(
12322
- eq15(crawlerEventsHourly.projectId, projectId),
12323
- eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12404
+ eq16(crawlerEventsHourly.projectId, projectId),
12405
+ eq16(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12324
12406
  gte2(crawlerEventsHourly.tsHour, windowStartIso),
12325
12407
  exclusiveEnd ? lt(crawlerEventsHourly.tsHour, windowEndIso) : lte(crawlerEventsHourly.tsHour, windowEndIso)
12326
12408
  )
@@ -12329,7 +12411,7 @@ function buildServerActivity(db, projectId, windowDays) {
12329
12411
  const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
12330
12412
  db.select({ total: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
12331
12413
  and12(
12332
- eq15(crawlerEventsHourly.projectId, projectId),
12414
+ eq16(crawlerEventsHourly.projectId, projectId),
12333
12415
  ne2(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12334
12416
  gte2(crawlerEventsHourly.tsHour, windowStartIso),
12335
12417
  exclusiveEnd ? lt(crawlerEventsHourly.tsHour, windowEndIso) : lte(crawlerEventsHourly.tsHour, windowEndIso)
@@ -12343,7 +12425,7 @@ function buildServerActivity(db, projectId, windowDays) {
12343
12425
  organic: sql7`COALESCE(SUM(${aiReferralEventsHourly.organicSessionsOrHits}), 0)`
12344
12426
  }).from(aiReferralEventsHourly).where(
12345
12427
  and12(
12346
- eq15(aiReferralEventsHourly.projectId, projectId),
12428
+ eq16(aiReferralEventsHourly.projectId, projectId),
12347
12429
  nonSubresourceReferralPathCondition(),
12348
12430
  gte2(aiReferralEventsHourly.tsHour, windowStartIso),
12349
12431
  exclusiveEnd ? lt(aiReferralEventsHourly.tsHour, windowEndIso) : lte(aiReferralEventsHourly.tsHour, windowEndIso)
@@ -12354,7 +12436,7 @@ function buildServerActivity(db, projectId, windowDays) {
12354
12436
  const sumUserFetches = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
12355
12437
  db.select({ total: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
12356
12438
  and12(
12357
- eq15(aiUserFetchEventsHourly.projectId, projectId),
12439
+ eq16(aiUserFetchEventsHourly.projectId, projectId),
12358
12440
  gte2(aiUserFetchEventsHourly.tsHour, windowStartIso),
12359
12441
  exclusiveEnd ? lt(aiUserFetchEventsHourly.tsHour, windowEndIso) : lte(aiUserFetchEventsHourly.tsHour, windowEndIso)
12360
12442
  )
@@ -12374,7 +12456,7 @@ function buildServerActivity(db, projectId, windowDays) {
12374
12456
  hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
12375
12457
  }).from(crawlerEventsHourly).where(
12376
12458
  and12(
12377
- eq15(crawlerEventsHourly.projectId, projectId),
12459
+ eq16(crawlerEventsHourly.projectId, projectId),
12378
12460
  gte2(crawlerEventsHourly.tsHour, headlineStart),
12379
12461
  lte(crawlerEventsHourly.tsHour, headlineEnd)
12380
12462
  )
@@ -12384,8 +12466,8 @@ function buildServerActivity(db, projectId, windowDays) {
12384
12466
  hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
12385
12467
  }).from(crawlerEventsHourly).where(
12386
12468
  and12(
12387
- eq15(crawlerEventsHourly.projectId, projectId),
12388
- eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12469
+ eq16(crawlerEventsHourly.projectId, projectId),
12470
+ eq16(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12389
12471
  gte2(crawlerEventsHourly.tsHour, priorStart),
12390
12472
  lt(crawlerEventsHourly.tsHour, headlineStart)
12391
12473
  )
@@ -12395,7 +12477,7 @@ function buildServerActivity(db, projectId, windowDays) {
12395
12477
  hits: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
12396
12478
  }).from(aiReferralEventsHourly).where(
12397
12479
  and12(
12398
- eq15(aiReferralEventsHourly.projectId, projectId),
12480
+ eq16(aiReferralEventsHourly.projectId, projectId),
12399
12481
  nonSubresourceReferralPathCondition(),
12400
12482
  gte2(aiReferralEventsHourly.tsHour, headlineStart),
12401
12483
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
@@ -12406,7 +12488,7 @@ function buildServerActivity(db, projectId, windowDays) {
12406
12488
  hits: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
12407
12489
  }).from(aiUserFetchEventsHourly).where(
12408
12490
  and12(
12409
- eq15(aiUserFetchEventsHourly.projectId, projectId),
12491
+ eq16(aiUserFetchEventsHourly.projectId, projectId),
12410
12492
  gte2(aiUserFetchEventsHourly.tsHour, headlineStart),
12411
12493
  lte(aiUserFetchEventsHourly.tsHour, headlineEnd)
12412
12494
  )
@@ -12450,8 +12532,8 @@ function buildServerActivity(db, projectId, windowDays) {
12450
12532
  operators: sql7`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
12451
12533
  }).from(crawlerEventsHourly).where(
12452
12534
  and12(
12453
- eq15(crawlerEventsHourly.projectId, projectId),
12454
- eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12535
+ eq16(crawlerEventsHourly.projectId, projectId),
12536
+ eq16(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12455
12537
  gte2(crawlerEventsHourly.tsHour, headlineStart),
12456
12538
  lte(crawlerEventsHourly.tsHour, headlineEnd)
12457
12539
  )
@@ -12467,7 +12549,7 @@ function buildServerActivity(db, projectId, windowDays) {
12467
12549
  landingPaths: sql7`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
12468
12550
  }).from(aiReferralEventsHourly).where(
12469
12551
  and12(
12470
- eq15(aiReferralEventsHourly.projectId, projectId),
12552
+ eq16(aiReferralEventsHourly.projectId, projectId),
12471
12553
  nonSubresourceReferralPathCondition(),
12472
12554
  gte2(aiReferralEventsHourly.tsHour, headlineStart),
12473
12555
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
@@ -12484,7 +12566,7 @@ function buildServerActivity(db, projectId, windowDays) {
12484
12566
  products: sql7`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
12485
12567
  }).from(aiReferralEventsHourly).where(
12486
12568
  and12(
12487
- eq15(aiReferralEventsHourly.projectId, projectId),
12569
+ eq16(aiReferralEventsHourly.projectId, projectId),
12488
12570
  nonSubresourceReferralPathCondition(),
12489
12571
  gte2(aiReferralEventsHourly.tsHour, headlineStart),
12490
12572
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
@@ -12500,8 +12582,8 @@ function buildServerActivity(db, projectId, windowDays) {
12500
12582
  hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
12501
12583
  }).from(crawlerEventsHourly).where(
12502
12584
  and12(
12503
- eq15(crawlerEventsHourly.projectId, projectId),
12504
- eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12585
+ eq16(crawlerEventsHourly.projectId, projectId),
12586
+ eq16(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
12505
12587
  gte2(crawlerEventsHourly.tsHour, trendStart),
12506
12588
  lte(crawlerEventsHourly.tsHour, headlineEnd)
12507
12589
  )
@@ -12511,7 +12593,7 @@ function buildServerActivity(db, projectId, windowDays) {
12511
12593
  hits: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
12512
12594
  }).from(aiReferralEventsHourly).where(
12513
12595
  and12(
12514
- eq15(aiReferralEventsHourly.projectId, projectId),
12596
+ eq16(aiReferralEventsHourly.projectId, projectId),
12515
12597
  nonSubresourceReferralPathCondition(),
12516
12598
  gte2(aiReferralEventsHourly.tsHour, trendStart),
12517
12599
  lte(aiReferralEventsHourly.tsHour, headlineEnd)
@@ -12522,7 +12604,7 @@ function buildServerActivity(db, projectId, windowDays) {
12522
12604
  hits: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
12523
12605
  }).from(aiUserFetchEventsHourly).where(
12524
12606
  and12(
12525
- eq15(aiUserFetchEventsHourly.projectId, projectId),
12607
+ eq16(aiUserFetchEventsHourly.projectId, projectId),
12526
12608
  gte2(aiUserFetchEventsHourly.tsHour, trendStart),
12527
12609
  lte(aiUserFetchEventsHourly.tsHour, headlineEnd)
12528
12610
  )
@@ -12595,7 +12677,7 @@ function buildServerActivity(db, projectId, windowDays) {
12595
12677
  };
12596
12678
  }
12597
12679
  function buildIndexingHealth(db, projectId) {
12598
- const gsc = db.select().from(gscCoverageSnapshots).where(eq15(gscCoverageSnapshots.projectId, projectId)).orderBy(desc7(gscCoverageSnapshots.date)).limit(1).get();
12680
+ const gsc = db.select().from(gscCoverageSnapshots).where(eq16(gscCoverageSnapshots.projectId, projectId)).orderBy(desc7(gscCoverageSnapshots.date)).limit(1).get();
12599
12681
  if (gsc) {
12600
12682
  const total = gsc.indexed + gsc.notIndexed;
12601
12683
  return {
@@ -12608,7 +12690,7 @@ function buildIndexingHealth(db, projectId) {
12608
12690
  indexedPct: total > 0 ? Math.round(gsc.indexed / total * 100) : 0
12609
12691
  };
12610
12692
  }
12611
- const bing = db.select().from(bingCoverageSnapshots).where(eq15(bingCoverageSnapshots.projectId, projectId)).orderBy(desc7(bingCoverageSnapshots.date)).limit(1).get();
12693
+ const bing = db.select().from(bingCoverageSnapshots).where(eq16(bingCoverageSnapshots.projectId, projectId)).orderBy(desc7(bingCoverageSnapshots.date)).limit(1).get();
12612
12694
  if (bing) {
12613
12695
  const total = bing.indexed + bing.notIndexed + bing.unknown;
12614
12696
  return {
@@ -12624,7 +12706,7 @@ function buildIndexingHealth(db, projectId) {
12624
12706
  return null;
12625
12707
  }
12626
12708
  function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
12627
- const visibilityRuns = db.select().from(runs).where(and12(eq15(runs.projectId, projectId), eq15(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter);
12709
+ const visibilityRuns = db.select().from(runs).where(and12(eq16(runs.projectId, projectId), eq16(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter);
12628
12710
  const totalQueries = queryLookup.byId.size;
12629
12711
  const points = [];
12630
12712
  for (const run of visibilityRuns) {
@@ -12673,14 +12755,14 @@ function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
12673
12755
  function buildInsightList(db, projectId, locationFilter) {
12674
12756
  const recentRunIds = db.select({ id: runs.id, location: runs.location }).from(runs).where(
12675
12757
  and12(
12676
- eq15(runs.projectId, projectId),
12677
- eq15(runs.kind, RunKinds["answer-visibility"]),
12678
- or3(eq15(runs.status, RunStatuses.completed), eq15(runs.status, RunStatuses.partial)),
12758
+ eq16(runs.projectId, projectId),
12759
+ eq16(runs.kind, RunKinds["answer-visibility"]),
12760
+ or3(eq16(runs.status, RunStatuses.completed), eq16(runs.status, RunStatuses.partial)),
12679
12761
  notProbeRun()
12680
12762
  )
12681
12763
  ).orderBy(desc7(runs.createdAt)).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter).slice(0, INSIGHT_LOOKBACK_RUNS).map((r) => r.id);
12682
12764
  if (recentRunIds.length === 0) return [];
12683
- const rows = db.select().from(insights).where(and12(eq15(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc7(insights.createdAt)).all();
12765
+ const rows = db.select().from(insights).where(and12(eq16(insights.projectId, projectId), inArray7(insights.runId, recentRunIds))).orderBy(desc7(insights.createdAt)).all();
12684
12766
  const severityRank = { critical: 0, high: 1, medium: 2, low: 3 };
12685
12767
  const flat = rows.filter((r) => !r.dismissed).map((r) => {
12686
12768
  const recommendation = r.recommendation;
@@ -13232,7 +13314,7 @@ function buildProjectReport(db, projectName, periodDays) {
13232
13314
  const project = resolveProject(db, projectName);
13233
13315
  const queryLookup = loadQueryLookup(db, project.id);
13234
13316
  const comparisonWindowDays = reportComparisonWindowDays(periodDays);
13235
- const allRuns = db.select().from(runs).where(and12(eq15(runs.projectId, project.id), notProbeRun())).orderBy(desc7(runs.createdAt), desc7(runs.id)).all();
13317
+ const allRuns = db.select().from(runs).where(and12(eq16(runs.projectId, project.id), notProbeRun())).orderBy(desc7(runs.createdAt), desc7(runs.id)).all();
13236
13318
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
13237
13319
  const completedVisRunGroups = groupRunsByCreatedAt(
13238
13320
  visibilityRuns.filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial)
@@ -13241,7 +13323,7 @@ function buildProjectReport(db, projectName, periodDays) {
13241
13323
  const representativeLatestRun = pickGroupRepresentative(latestVisRunGroup) ?? visibilityRuns[0] ?? null;
13242
13324
  const latestSnapshots = loadSnapshotsForRunIds(db, latestVisRunGroup.map((r) => r.id));
13243
13325
  const latestRunLocation = representativeLatestRun?.location ?? null;
13244
- const competitorRows = db.select().from(competitors).where(eq15(competitors.projectId, project.id)).all();
13326
+ const competitorRows = db.select().from(competitors).where(eq16(competitors.projectId, project.id)).all();
13245
13327
  const competitorDomains = competitorRows.map((c) => c.domain);
13246
13328
  const ownedDomains = project.ownedDomains;
13247
13329
  const projectDomains = [project.canonicalDomain, ...ownedDomains];
@@ -13468,16 +13550,16 @@ async function reportRoutes(app) {
13468
13550
  }
13469
13551
 
13470
13552
  // ../api-routes/src/citations.ts
13471
- import { and as and13, eq as eq16, inArray as inArray7 } from "drizzle-orm";
13553
+ import { and as and13, eq as eq17, inArray as inArray8 } from "drizzle-orm";
13472
13554
  async function citationRoutes(app) {
13473
13555
  app.get("/projects/:name/citations/visibility", async (request, reply) => {
13474
13556
  const project = resolveProject(app.db, request.params.name);
13475
13557
  const configuredProviders = project.providers;
13476
- const projectQueries = app.db.select().from(queries).where(eq16(queries.projectId, project.id)).all();
13558
+ const projectQueries = app.db.select().from(queries).where(eq17(queries.projectId, project.id)).all();
13477
13559
  if (projectQueries.length === 0) {
13478
13560
  return reply.send(emptyCitationVisibility("no-queries"));
13479
13561
  }
13480
- const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and13(eq16(runs.projectId, project.id), notProbeRun())).all();
13562
+ const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and13(eq17(runs.projectId, project.id), notProbeRun())).all();
13481
13563
  if (projectRuns.length === 0) {
13482
13564
  return reply.send(emptyCitationVisibility("no-runs-yet"));
13483
13565
  }
@@ -13492,7 +13574,7 @@ async function citationRoutes(app) {
13492
13574
  competitorOverlap: querySnapshots.competitorOverlap,
13493
13575
  answerMentioned: querySnapshots.answerMentioned,
13494
13576
  createdAt: querySnapshots.createdAt
13495
- }).from(querySnapshots).where(inArray7(querySnapshots.runId, projectRuns.map((r) => r.id))).all();
13577
+ }).from(querySnapshots).where(inArray8(querySnapshots.runId, projectRuns.map((r) => r.id))).all();
13496
13578
  if (rawSnapshots.length === 0) {
13497
13579
  return reply.send(emptyCitationVisibility("no-runs-yet"));
13498
13580
  }
@@ -13501,7 +13583,7 @@ async function citationRoutes(app) {
13501
13583
  queryId: s.queryId,
13502
13584
  runCreatedAt: runCreatedAt.get(s.runId) ?? s.createdAt
13503
13585
  }));
13504
- 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);
13586
+ const projectCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq17(competitors.projectId, project.id)).all().map((c) => normalizeDomain2(c.domain)).filter((d) => d.length > 0);
13505
13587
  const response = computeCitationVisibility({
13506
13588
  queries: projectQueries.map((q) => ({ id: q.id, query: q.query })),
13507
13589
  snapshots,
@@ -13633,7 +13715,7 @@ function normalizeDomain2(domain) {
13633
13715
  }
13634
13716
 
13635
13717
  // ../api-routes/src/visibility-stats.ts
13636
- import { and as and14, desc as desc8, eq as eq17, inArray as inArray8 } from "drizzle-orm";
13718
+ import { and as and14, desc as desc8, eq as eq18, inArray as inArray9 } from "drizzle-orm";
13637
13719
 
13638
13720
  // ../api-routes/src/visibility-compare.ts
13639
13721
  var VISIBILITY_COMPARE_MIN_RUNS = 5;
@@ -14031,8 +14113,8 @@ async function visibilityStatsRoutes(app) {
14031
14113
  if (!Number.isInteger(n) || n <= 0) throw validationError('"lastRuns" must be a positive integer');
14032
14114
  lastRuns = n;
14033
14115
  }
14034
- const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq17(queries.projectId, project.id)).all();
14035
- let projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and14(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);
14116
+ const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq18(queries.projectId, project.id)).all();
14117
+ let projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and14(eq18(runs.projectId, project.id), eq18(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc8(runs.createdAt)).all().filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial);
14036
14118
  if (sinceMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) >= sinceMs);
14037
14119
  if (untilMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) <= untilMs);
14038
14120
  if (lastRuns !== null) projectRuns = projectRuns.slice(0, lastRuns);
@@ -14045,12 +14127,12 @@ async function visibilityStatsRoutes(app) {
14045
14127
  citationState: querySnapshots.citationState,
14046
14128
  answerMentioned: querySnapshots.answerMentioned,
14047
14129
  createdAt: querySnapshots.createdAt
14048
- }).from(querySnapshots).where(inArray8(querySnapshots.runId, runIds)).all() : [];
14130
+ }).from(querySnapshots).where(inArray9(querySnapshots.runId, runIds)).all() : [];
14049
14131
  const stats = computeVisibilityStats({ queries: projectQueries, snapshots, groupBy });
14050
14132
  const queryAttribution = buildQueryAttribution(projectQueries);
14051
14133
  let shareOfVoice;
14052
14134
  if (wantShareOfVoice) {
14053
- const competitorRows = app.db.select({ domain: competitors.domain }).from(competitors).where(eq17(competitors.projectId, project.id)).all();
14135
+ const competitorRows = app.db.select({ domain: competitors.domain }).from(competitors).where(eq18(competitors.projectId, project.id)).all();
14054
14136
  const mentionShareCompetitors = competitorRows.map((c) => ({
14055
14137
  domain: c.domain,
14056
14138
  brandTokens: [brandLabelFromDomain(c.domain)].filter((t) => t.length >= 3)
@@ -14060,7 +14142,7 @@ async function visibilityStatsRoutes(app) {
14060
14142
  queryText: querySnapshots.queryText,
14061
14143
  answerMentioned: querySnapshots.answerMentioned,
14062
14144
  answerText: querySnapshots.answerText
14063
- }).from(querySnapshots).where(inArray8(querySnapshots.runId, runIds)).all() : [];
14145
+ }).from(querySnapshots).where(inArray9(querySnapshots.runId, runIds)).all() : [];
14064
14146
  const attributedSovSnapshots = sovSnapshots.filter((s) => resolveCurrentQuery(queryAttribution, s) !== void 0);
14065
14147
  const result = buildMentionShare(
14066
14148
  attributedSovSnapshots.map((s) => ({ projectMentioned: s.answerMentioned === true, answerText: s.answerText })),
@@ -14119,8 +14201,8 @@ async function visibilityStatsRoutes(app) {
14119
14201
  if (Date.parse(fromBounds.since) >= Date.parse(toBounds.since)) {
14120
14202
  throw validationError('"from" must be a month strictly before "to"');
14121
14203
  }
14122
- const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq17(queries.projectId, project.id)).all();
14123
- const competitorRows = app.db.select({ domain: competitors.domain }).from(competitors).where(eq17(competitors.projectId, project.id)).all();
14204
+ const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq18(queries.projectId, project.id)).all();
14205
+ const competitorRows = app.db.select({ domain: competitors.domain }).from(competitors).where(eq18(competitors.projectId, project.id)).all();
14124
14206
  const competitorInputs = competitorRows.map((c) => ({
14125
14207
  domain: c.domain,
14126
14208
  brandTokens: [brandLabelFromDomain(c.domain)].filter((t) => t.length >= 3)
@@ -14128,7 +14210,7 @@ async function visibilityStatsRoutes(app) {
14128
14210
  const loadMonth = (bounds) => {
14129
14211
  const sinceMs = Date.parse(bounds.since);
14130
14212
  const untilMs = Date.parse(bounds.until);
14131
- const monthRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and14(eq17(runs.projectId, project.id), eq17(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter(
14213
+ const monthRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and14(eq18(runs.projectId, project.id), eq18(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter(
14132
14214
  (r) => (r.status === RunStatuses.completed || r.status === RunStatuses.partial) && Date.parse(r.createdAt) >= sinceMs && Date.parse(r.createdAt) <= untilMs
14133
14215
  );
14134
14216
  const runIds = monthRuns.map((r) => r.id);
@@ -14141,7 +14223,7 @@ async function visibilityStatsRoutes(app) {
14141
14223
  answerMentioned: querySnapshots.answerMentioned,
14142
14224
  answerText: querySnapshots.answerText,
14143
14225
  citedDomains: querySnapshots.citedDomains
14144
- }).from(querySnapshots).where(inArray8(querySnapshots.runId, runIds)).all() : [];
14226
+ }).from(querySnapshots).where(inArray9(querySnapshots.runId, runIds)).all() : [];
14145
14227
  return { runCount: monthRuns.length, snapshots };
14146
14228
  };
14147
14229
  const fromMonth = loadMonth(fromBounds);
@@ -14158,7 +14240,7 @@ async function visibilityStatsRoutes(app) {
14158
14240
  }
14159
14241
 
14160
14242
  // ../api-routes/src/composites.ts
14161
- import { eq as eq18, and as and15, desc as desc9, sql as sql8, like as like2, or as or4, inArray as inArray9 } from "drizzle-orm";
14243
+ import { eq as eq19, and as and15, desc as desc9, sql as sql8, like as like2, or as or4, inArray as inArray10 } from "drizzle-orm";
14162
14244
  var TOP_INSIGHT_LIMIT = 5;
14163
14245
  var SEARCH_HIT_HARD_LIMIT = 50;
14164
14246
  var SEARCH_SNIPPET_RADIUS = 80;
@@ -14176,7 +14258,7 @@ async function compositeRoutes(app) {
14176
14258
  const project = resolveProject(app.db, request.params.name);
14177
14259
  const filterLocation = (request.query.location ?? "").trim() || null;
14178
14260
  const sinceIso = parseSinceFilter(request.query.since);
14179
- const allRunsRaw = app.db.select().from(runs).where(and15(eq18(runs.projectId, project.id), notProbeRun())).orderBy(desc9(runs.createdAt), desc9(runs.id)).all();
14261
+ const allRunsRaw = app.db.select().from(runs).where(and15(eq19(runs.projectId, project.id), notProbeRun())).orderBy(desc9(runs.createdAt), desc9(runs.id)).all();
14180
14262
  const allRuns = allRunsRaw.filter((r) => runMatchesFilters(r, filterLocation, sinceIso));
14181
14263
  const totalRuns = allRuns.length;
14182
14264
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
@@ -14189,15 +14271,15 @@ async function compositeRoutes(app) {
14189
14271
  const previousVisibilityRun = pickGroupRepresentative(previousVisRunGroup);
14190
14272
  const latestRunRow = allRuns[0] ?? null;
14191
14273
  const latestRun = latestRunRow ? { totalRuns, run: summarizeRun(latestRunRow) } : { totalRuns: 0, run: null };
14192
- const healthRow = app.db.select().from(healthSnapshots).where(eq18(healthSnapshots.projectId, project.id)).orderBy(desc9(healthSnapshots.createdAt)).limit(1).get();
14274
+ const healthRow = app.db.select().from(healthSnapshots).where(eq19(healthSnapshots.projectId, project.id)).orderBy(desc9(healthSnapshots.createdAt)).limit(1).get();
14193
14275
  const health = healthRow ? mapHealthRow2(healthRow) : null;
14194
- const insightRows = app.db.select().from(insights).where(eq18(insights.projectId, project.id)).orderBy(desc9(insights.createdAt)).all();
14276
+ const insightRows = app.db.select().from(insights).where(eq19(insights.projectId, project.id)).orderBy(desc9(insights.createdAt)).all();
14195
14277
  const topInsights = insightRows.filter((row) => !row.dismissed).slice(0, TOP_INSIGHT_LIMIT).map(mapInsightRow2);
14196
14278
  const sparklineRunIds = visibilityRuns.slice(0, DEFAULT_RUN_HISTORY_LIMIT).map((r) => r.id);
14197
14279
  const snapshotRunIds = new Set(sparklineRunIds);
14198
14280
  for (const run of latestVisRunGroup) snapshotRunIds.add(run.id);
14199
14281
  for (const run of previousVisRunGroup) snapshotRunIds.add(run.id);
14200
- const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq18(queries.projectId, project.id)).all();
14282
+ const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq19(queries.projectId, project.id)).all();
14201
14283
  const queryIdByText = new Map(projectQueries.map((q) => [normalizeQueryText(q.query), q.id]));
14202
14284
  const snapshotsByRun = loadSnapshotsByRunIds(app, [...snapshotRunIds], queryIdByText);
14203
14285
  const latestSnapshots = latestVisRunGroup.flatMap((r) => snapshotsByRun.get(r.id) ?? []);
@@ -14213,7 +14295,7 @@ async function compositeRoutes(app) {
14213
14295
  trackedPrevious,
14214
14296
  previousVisibilityRun?.createdAt ?? null
14215
14297
  );
14216
- const competitorRows = app.db.select().from(competitors).where(eq18(competitors.projectId, project.id)).all();
14298
+ const competitorRows = app.db.select().from(competitors).where(eq19(competitors.projectId, project.id)).all();
14217
14299
  const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
14218
14300
  for (const snapshots of snapshotsByRun.values()) {
14219
14301
  for (const snapshot of snapshots) {
@@ -14331,9 +14413,9 @@ async function compositeRoutes(app) {
14331
14413
  citedDomains: querySnapshots.citedDomains,
14332
14414
  rawResponse: querySnapshots.rawResponse,
14333
14415
  createdAt: querySnapshots.createdAt
14334
- }).from(querySnapshots).innerJoin(queries, eq18(querySnapshots.queryId, queries.id)).where(
14416
+ }).from(querySnapshots).innerJoin(queries, eq19(querySnapshots.queryId, queries.id)).where(
14335
14417
  and15(
14336
- eq18(queries.projectId, project.id),
14418
+ eq19(queries.projectId, project.id),
14337
14419
  or4(
14338
14420
  sql8`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
14339
14421
  sql8`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
@@ -14344,7 +14426,7 @@ async function compositeRoutes(app) {
14344
14426
  ).orderBy(desc9(querySnapshots.createdAt)).limit(limit + 1).all());
14345
14427
  const insightMatches = app.db.select().from(insights).where(
14346
14428
  and15(
14347
- eq18(insights.projectId, project.id),
14429
+ eq19(insights.projectId, project.id),
14348
14430
  or4(
14349
14431
  like2(insights.title, pattern),
14350
14432
  like2(insights.query, pattern),
@@ -14423,7 +14505,7 @@ function loadSnapshotsByRunIds(app, runIds, queryIdByText) {
14423
14505
  answerText: querySnapshots.answerText,
14424
14506
  competitorOverlap: querySnapshots.competitorOverlap,
14425
14507
  citedDomains: querySnapshots.citedDomains
14426
- }).from(querySnapshots).where(inArray9(querySnapshots.runId, [...runIds])).all();
14508
+ }).from(querySnapshots).where(inArray10(querySnapshots.runId, [...runIds])).all();
14427
14509
  for (const row of rows) {
14428
14510
  const queryText = row.queryText?.trim() || null;
14429
14511
  let queryId;
@@ -14561,7 +14643,7 @@ function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
14561
14643
  // which the JS coerces to 0 — caught by the impression floor anyway).
14562
14644
  avgPosition: sql8`COALESCE(SUM(${gscSearchData.position} * ${gscSearchData.impressions}) * 1.0 / NULLIF(SUM(${gscSearchData.impressions}), 0), 0)`
14563
14645
  }).from(gscSearchData).where(and15(
14564
- eq18(gscSearchData.projectId, projectId),
14646
+ eq19(gscSearchData.projectId, projectId),
14565
14647
  sql8`${gscSearchData.date} >= ${cutoff}`,
14566
14648
  sql8`${gscSearchData.impressions} > 0`
14567
14649
  )).groupBy(gscSearchData.query).orderBy(sql8`SUM(${gscSearchData.impressions}) DESC`).limit(100).all();
@@ -14584,8 +14666,8 @@ function buildIndexCoverageScore(app, projectId) {
14584
14666
  tooltip,
14585
14667
  trend: []
14586
14668
  };
14587
- const gscRow = app.db.select().from(gscCoverageSnapshots).where(eq18(gscCoverageSnapshots.projectId, projectId)).orderBy(desc9(gscCoverageSnapshots.date)).limit(1).get();
14588
- const bingRow = app.db.select().from(bingCoverageSnapshots).where(eq18(bingCoverageSnapshots.projectId, projectId)).orderBy(desc9(bingCoverageSnapshots.date)).limit(1).get();
14669
+ const gscRow = app.db.select().from(gscCoverageSnapshots).where(eq19(gscCoverageSnapshots.projectId, projectId)).orderBy(desc9(gscCoverageSnapshots.date)).limit(1).get();
14670
+ const bingRow = app.db.select().from(bingCoverageSnapshots).where(eq19(bingCoverageSnapshots.projectId, projectId)).orderBy(desc9(bingCoverageSnapshots.date)).limit(1).get();
14589
14671
  const chosen = pickIndexCoverageRow(gscRow, bingRow);
14590
14672
  if (!chosen) return empty;
14591
14673
  const total = chosen.indexed + chosen.notIndexed;
@@ -14611,7 +14693,7 @@ function countGoogleDeindexedUrls(app, projectId) {
14611
14693
  url: gscUrlInspections.url,
14612
14694
  indexingState: gscUrlInspections.indexingState,
14613
14695
  inspectedAt: gscUrlInspections.inspectedAt
14614
- }).from(gscUrlInspections).where(eq18(gscUrlInspections.projectId, projectId)).orderBy(desc9(gscUrlInspections.inspectedAt)).all();
14696
+ }).from(gscUrlInspections).where(eq19(gscUrlInspections.projectId, projectId)).orderBy(desc9(gscUrlInspections.inspectedAt)).all();
14615
14697
  if (rows.length === 0) return 0;
14616
14698
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
14617
14699
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -19349,8 +19431,8 @@ async function settingsRoutes(app, opts) {
19349
19431
  }
19350
19432
 
19351
19433
  // ../api-routes/src/keys.ts
19352
- import crypto12 from "crypto";
19353
- import { desc as desc10, eq as eq19 } from "drizzle-orm";
19434
+ import crypto13 from "crypto";
19435
+ import { desc as desc10, eq as eq20 } from "drizzle-orm";
19354
19436
  var KEYS_WRITE_SCOPE = "keys.write";
19355
19437
  function toApiKeyDto(row) {
19356
19438
  const scopes = Array.isArray(row.scopes) ? row.scopes : [];
@@ -19369,7 +19451,7 @@ function toApiKeyDto(row) {
19369
19451
  async function keysRoutes(app) {
19370
19452
  app.get("/keys", async (request) => {
19371
19453
  const scopedProjectId = request.apiKey?.projectId;
19372
- 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();
19454
+ const rows = scopedProjectId ? app.db.select().from(apiKeys).where(eq20(apiKeys.projectId, scopedProjectId)).orderBy(desc10(apiKeys.createdAt)).all() : app.db.select().from(apiKeys).orderBy(desc10(apiKeys.createdAt)).all();
19373
19455
  return { keys: rows.map(toApiKeyDto) };
19374
19456
  });
19375
19457
  app.get("/keys/self", async (request) => {
@@ -19377,7 +19459,7 @@ async function keysRoutes(app) {
19377
19459
  if (!id) {
19378
19460
  throw notFound("API key", "self");
19379
19461
  }
19380
- const row = app.db.select().from(apiKeys).where(eq19(apiKeys.id, id)).get();
19462
+ const row = app.db.select().from(apiKeys).where(eq20(apiKeys.id, id)).get();
19381
19463
  if (!row) {
19382
19464
  throw notFound("API key", id);
19383
19465
  }
@@ -19395,15 +19477,15 @@ async function keysRoutes(app) {
19395
19477
  throw forbidden("A project-scoped key can only mint keys scoped to its own project.");
19396
19478
  }
19397
19479
  if (projectId) {
19398
- const proj = app.db.select({ id: projects.id }).from(projects).where(eq19(projects.id, projectId)).get();
19480
+ const proj = app.db.select({ id: projects.id }).from(projects).where(eq20(projects.id, projectId)).get();
19399
19481
  if (!proj) {
19400
19482
  throw notFound("Project", projectId);
19401
19483
  }
19402
19484
  }
19403
- const raw = `cnry_${crypto12.randomBytes(16).toString("hex")}`;
19485
+ const raw = `cnry_${crypto13.randomBytes(16).toString("hex")}`;
19404
19486
  const keyHash = hashApiKey(raw);
19405
19487
  const keyPrefix = raw.slice(0, 9);
19406
- const id = crypto12.randomUUID();
19488
+ const id = crypto13.randomUUID();
19407
19489
  const now = (/* @__PURE__ */ new Date()).toISOString();
19408
19490
  const effectiveScopes = scopes ?? ["*"];
19409
19491
  app.db.transaction((tx) => {
@@ -19441,7 +19523,7 @@ async function keysRoutes(app) {
19441
19523
  app.post("/keys/:id/revoke", async (request) => {
19442
19524
  requireScope(request, KEYS_WRITE_SCOPE);
19443
19525
  const { id } = request.params;
19444
- const row = app.db.select().from(apiKeys).where(eq19(apiKeys.id, id)).get();
19526
+ const row = app.db.select().from(apiKeys).where(eq20(apiKeys.id, id)).get();
19445
19527
  if (!row) {
19446
19528
  throw notFound("API key", id);
19447
19529
  }
@@ -19457,7 +19539,7 @@ async function keysRoutes(app) {
19457
19539
  }
19458
19540
  const now = (/* @__PURE__ */ new Date()).toISOString();
19459
19541
  app.db.transaction((tx) => {
19460
- tx.update(apiKeys).set({ revokedAt: now }).where(eq19(apiKeys.id, id)).run();
19542
+ tx.update(apiKeys).set({ revokedAt: now }).where(eq20(apiKeys.id, id)).run();
19461
19543
  writeAuditLog(tx, auditFromRequest(request, {
19462
19544
  actor: "api",
19463
19545
  action: "api-key.revoked",
@@ -19529,8 +19611,8 @@ async function telemetryRoutes(app, opts) {
19529
19611
  }
19530
19612
 
19531
19613
  // ../api-routes/src/schedules.ts
19532
- import crypto13 from "crypto";
19533
- import { and as and16, eq as eq20 } from "drizzle-orm";
19614
+ import crypto14 from "crypto";
19615
+ import { and as and16, eq as eq21 } from "drizzle-orm";
19534
19616
  function parseKindParam(raw) {
19535
19617
  if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
19536
19618
  const parsed = schedulableRunKindSchema.safeParse(raw);
@@ -19557,7 +19639,7 @@ async function scheduleRoutes(app, opts) {
19557
19639
  if (!sourceId) {
19558
19640
  throw validationError('"sourceId" is required when kind is "traffic-sync"');
19559
19641
  }
19560
- const sourceRow = app.db.select().from(trafficSources).where(eq20(trafficSources.id, sourceId)).get();
19642
+ const sourceRow = app.db.select().from(trafficSources).where(eq21(trafficSources.id, sourceId)).get();
19561
19643
  if (!sourceRow || sourceRow.projectId !== project.id) {
19562
19644
  throw notFound("Traffic source", sourceId);
19563
19645
  }
@@ -19602,7 +19684,7 @@ async function scheduleRoutes(app, opts) {
19602
19684
  }
19603
19685
  const now = (/* @__PURE__ */ new Date()).toISOString();
19604
19686
  const enabledBool = enabled !== false;
19605
- const existing = app.db.select().from(schedules).where(and16(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
19687
+ const existing = app.db.select().from(schedules).where(and16(eq21(schedules.projectId, project.id), eq21(schedules.kind, kind))).get();
19606
19688
  if (existing) {
19607
19689
  app.db.update(schedules).set({
19608
19690
  cronExpr,
@@ -19612,10 +19694,10 @@ async function scheduleRoutes(app, opts) {
19612
19694
  sourceId: sourceId ?? null,
19613
19695
  enabled: enabledBool,
19614
19696
  updatedAt: now
19615
- }).where(eq20(schedules.id, existing.id)).run();
19697
+ }).where(eq21(schedules.id, existing.id)).run();
19616
19698
  } else {
19617
19699
  app.db.insert(schedules).values({
19618
- id: crypto13.randomUUID(),
19700
+ id: crypto14.randomUUID(),
19619
19701
  projectId: project.id,
19620
19702
  kind,
19621
19703
  cronExpr,
@@ -19636,13 +19718,13 @@ async function scheduleRoutes(app, opts) {
19636
19718
  diff: { kind, cronExpr, preset, timezone, providers, sourceId }
19637
19719
  });
19638
19720
  opts.onScheduleUpdated?.("upsert", project.id, kind);
19639
- const schedule = app.db.select().from(schedules).where(and16(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
19721
+ const schedule = app.db.select().from(schedules).where(and16(eq21(schedules.projectId, project.id), eq21(schedules.kind, kind))).get();
19640
19722
  return reply.status(existing ? 200 : 201).send(formatSchedule(schedule));
19641
19723
  });
19642
19724
  app.get("/projects/:name/schedule", async (request, reply) => {
19643
19725
  const project = resolveProject(app.db, request.params.name);
19644
19726
  const kind = parseKindParam(request.query?.kind);
19645
- const schedule = app.db.select().from(schedules).where(and16(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
19727
+ const schedule = app.db.select().from(schedules).where(and16(eq21(schedules.projectId, project.id), eq21(schedules.kind, kind))).get();
19646
19728
  if (!schedule) {
19647
19729
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
19648
19730
  }
@@ -19651,11 +19733,11 @@ async function scheduleRoutes(app, opts) {
19651
19733
  app.delete("/projects/:name/schedule", async (request, reply) => {
19652
19734
  const project = resolveProject(app.db, request.params.name);
19653
19735
  const kind = parseKindParam(request.query?.kind);
19654
- const schedule = app.db.select().from(schedules).where(and16(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
19736
+ const schedule = app.db.select().from(schedules).where(and16(eq21(schedules.projectId, project.id), eq21(schedules.kind, kind))).get();
19655
19737
  if (!schedule) {
19656
19738
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
19657
19739
  }
19658
- app.db.delete(schedules).where(eq20(schedules.id, schedule.id)).run();
19740
+ app.db.delete(schedules).where(eq21(schedules.id, schedule.id)).run();
19659
19741
  writeAuditLog(app.db, {
19660
19742
  projectId: project.id,
19661
19743
  actor: "api",
@@ -19687,8 +19769,8 @@ function formatSchedule(row) {
19687
19769
  }
19688
19770
 
19689
19771
  // ../api-routes/src/notifications.ts
19690
- import crypto14 from "crypto";
19691
- import { eq as eq21 } from "drizzle-orm";
19772
+ import crypto15 from "crypto";
19773
+ import { eq as eq22 } from "drizzle-orm";
19692
19774
  var VALID_EVENTS = ["citation.lost", "citation.gained", "run.completed", "run.failed", "insight.critical", "insight.high"];
19693
19775
  async function notificationRoutes(app, opts = {}) {
19694
19776
  const allowLoopback = opts.allowLoopbackWebhooks === true;
@@ -19707,8 +19789,8 @@ async function notificationRoutes(app, opts = {}) {
19707
19789
  throw validationError(`Invalid event(s): ${invalid.join(", ")}. Must be one of: ${VALID_EVENTS.join(", ")}`);
19708
19790
  }
19709
19791
  const now = (/* @__PURE__ */ new Date()).toISOString();
19710
- const id = crypto14.randomUUID();
19711
- const webhookSecret = crypto14.randomBytes(32).toString("hex");
19792
+ const id = crypto15.randomUUID();
19793
+ const webhookSecret = crypto15.randomBytes(32).toString("hex");
19712
19794
  app.db.insert(notifications).values({
19713
19795
  id,
19714
19796
  projectId: project.id,
@@ -19728,22 +19810,22 @@ async function notificationRoutes(app, opts = {}) {
19728
19810
  diff: { channel, ...redactNotificationUrl(url), events }
19729
19811
  });
19730
19812
  return reply.status(201).send({
19731
- ...formatNotification(app.db.select().from(notifications).where(eq21(notifications.id, id)).get()),
19813
+ ...formatNotification(app.db.select().from(notifications).where(eq22(notifications.id, id)).get()),
19732
19814
  webhookSecret
19733
19815
  });
19734
19816
  });
19735
19817
  app.get("/projects/:name/notifications", async (request, reply) => {
19736
19818
  const project = resolveProject(app.db, request.params.name);
19737
- const rows = app.db.select().from(notifications).where(eq21(notifications.projectId, project.id)).all();
19819
+ const rows = app.db.select().from(notifications).where(eq22(notifications.projectId, project.id)).all();
19738
19820
  return reply.send(rows.map(formatNotification));
19739
19821
  });
19740
19822
  app.delete("/projects/:name/notifications/:id", async (request, reply) => {
19741
19823
  const project = resolveProject(app.db, request.params.name);
19742
- const notification = app.db.select().from(notifications).where(eq21(notifications.id, request.params.id)).get();
19824
+ const notification = app.db.select().from(notifications).where(eq22(notifications.id, request.params.id)).get();
19743
19825
  if (!notification || notification.projectId !== project.id) {
19744
19826
  throw notFound("Notification", request.params.id);
19745
19827
  }
19746
- app.db.delete(notifications).where(eq21(notifications.id, notification.id)).run();
19828
+ app.db.delete(notifications).where(eq22(notifications.id, notification.id)).run();
19747
19829
  writeAuditLog(app.db, {
19748
19830
  projectId: project.id,
19749
19831
  actor: "api",
@@ -19755,7 +19837,7 @@ async function notificationRoutes(app, opts = {}) {
19755
19837
  });
19756
19838
  app.post("/projects/:name/notifications/:id/test", async (request, reply) => {
19757
19839
  const project = resolveProject(app.db, request.params.name);
19758
- const notification = app.db.select().from(notifications).where(eq21(notifications.id, request.params.id)).get();
19840
+ const notification = app.db.select().from(notifications).where(eq22(notifications.id, request.params.id)).get();
19759
19841
  if (!notification || notification.projectId !== project.id) {
19760
19842
  throw notFound("Notification", request.params.id);
19761
19843
  }
@@ -19807,8 +19889,8 @@ function formatNotification(row) {
19807
19889
  }
19808
19890
 
19809
19891
  // ../api-routes/src/google.ts
19810
- import crypto18 from "crypto";
19811
- import { eq as eq22, and as and17, desc as desc11, sql as sql9, inArray as inArray10 } from "drizzle-orm";
19892
+ import crypto19 from "crypto";
19893
+ import { eq as eq23, and as and17, desc as desc11, sql as sql9, inArray as inArray11 } from "drizzle-orm";
19812
19894
 
19813
19895
  // ../api-routes/src/gbp-summary.ts
19814
19896
  function computeMetricTotals(rows) {
@@ -20343,7 +20425,7 @@ async function inspectUrl(accessToken, inspectionUrl, siteUrl) {
20343
20425
  }
20344
20426
 
20345
20427
  // ../integration-google-analytics/src/ga4-client.ts
20346
- import crypto15 from "crypto";
20428
+ import crypto16 from "crypto";
20347
20429
 
20348
20430
  // ../integration-google-analytics/src/constants.ts
20349
20431
  var GA4_DATA_API_BASE = "https://analyticsdata.googleapis.com/v1beta";
@@ -20450,7 +20532,7 @@ function createServiceAccountJwt(clientEmail, privateKey, scope) {
20450
20532
  const headerB64 = encode(header);
20451
20533
  const payloadB64 = encode(payload);
20452
20534
  const signingInput = `${headerB64}.${payloadB64}`;
20453
- const sign = crypto15.createSign("RSA-SHA256");
20535
+ const sign = crypto16.createSign("RSA-SHA256");
20454
20536
  sign.update(signingInput);
20455
20537
  const signature = sign.sign(privateKey, "base64url");
20456
20538
  return `${signingInput}.${signature}`;
@@ -21330,7 +21412,7 @@ async function listPlaceActionLinks(accessToken, locationName, opts = {}) {
21330
21412
  }
21331
21413
 
21332
21414
  // ../integration-google-business-profile/src/lodging-client.ts
21333
- import crypto16 from "crypto";
21415
+ import crypto17 from "crypto";
21334
21416
  async function getLodging(accessToken, locationName, opts = {}) {
21335
21417
  const url = `${GBP_LODGING_BASE}/${locationName}/lodging?readMask=*`;
21336
21418
  try {
@@ -21357,7 +21439,7 @@ function isPopulated(value) {
21357
21439
  return true;
21358
21440
  }
21359
21441
  function hashLodging(lodging) {
21360
- return crypto16.createHash("sha256").update(stableStringify2(lodging)).digest("hex");
21442
+ return crypto17.createHash("sha256").update(stableStringify2(lodging)).digest("hex");
21361
21443
  }
21362
21444
  function stableStringify2(value) {
21363
21445
  if (value === null || typeof value !== "object") return JSON.stringify(value);
@@ -21368,7 +21450,7 @@ function stableStringify2(value) {
21368
21450
  }
21369
21451
 
21370
21452
  // ../integration-google-business-profile/src/attributes-client.ts
21371
- import crypto17 from "crypto";
21453
+ import crypto18 from "crypto";
21372
21454
  async function getAttributes(accessToken, locationName, opts = {}) {
21373
21455
  const url = `${GBP_BUSINESS_INFO_BASE}/${locationName}/attributes`;
21374
21456
  let res;
@@ -21398,7 +21480,7 @@ function countAttributes(attrs) {
21398
21480
  }
21399
21481
  function hashAttributes(attrs) {
21400
21482
  const sorted = [...attrs].sort((a, b) => a.name.localeCompare(b.name));
21401
- return crypto17.createHash("sha256").update(stableStringify3(sorted)).digest("hex");
21483
+ return crypto18.createHash("sha256").update(stableStringify3(sorted)).digest("hex");
21402
21484
  }
21403
21485
  function stableStringify3(value) {
21404
21486
  if (value === null || typeof value !== "object") return JSON.stringify(value);
@@ -21420,7 +21502,7 @@ function scopesForConnectionType(type) {
21420
21502
  }
21421
21503
  }
21422
21504
  function signState(payload, secret) {
21423
- return crypto18.createHmac("sha256", secret).update(payload).digest("hex");
21505
+ return crypto19.createHmac("sha256", secret).update(payload).digest("hex");
21424
21506
  }
21425
21507
  function buildSignedState(data, secret) {
21426
21508
  const payload = JSON.stringify(data);
@@ -21431,7 +21513,7 @@ function verifySignedState(encoded, secret) {
21431
21513
  try {
21432
21514
  const { payload, sig } = JSON.parse(Buffer.from(encoded, "base64url").toString());
21433
21515
  const expected = signState(payload, secret);
21434
- if (!crypto18.timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"))) return null;
21516
+ if (!crypto19.timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"))) return null;
21435
21517
  return JSON.parse(payload);
21436
21518
  } catch {
21437
21519
  return null;
@@ -21648,7 +21730,7 @@ async function googleRoutes(app, opts) {
21648
21730
  if (!projectId) {
21649
21731
  return reply.status(400).send("Stale OAuth state \u2014 restart the connect flow.");
21650
21732
  }
21651
- const project = app.db.select().from(projects).where(eq22(projects.id, projectId)).get();
21733
+ const project = app.db.select().from(projects).where(eq23(projects.id, projectId)).get();
21652
21734
  if (!project) {
21653
21735
  return reply.status(400).send("Project no longer exists. Restart the connect flow.");
21654
21736
  }
@@ -21767,7 +21849,7 @@ async function googleRoutes(app, opts) {
21767
21849
  throw validationError('No GSC connection found for this domain. Run "canonry google connect" first.');
21768
21850
  }
21769
21851
  const now = (/* @__PURE__ */ new Date()).toISOString();
21770
- const runId = crypto18.randomUUID();
21852
+ const runId = crypto19.randomUUID();
21771
21853
  app.db.insert(runs).values({
21772
21854
  id: runId,
21773
21855
  projectId: project.id,
@@ -21780,14 +21862,14 @@ async function googleRoutes(app, opts) {
21780
21862
  if (opts.onGscSyncRequested) {
21781
21863
  opts.onGscSyncRequested(runId, project.id, { days, full });
21782
21864
  }
21783
- const run = app.db.select().from(runs).where(eq22(runs.id, runId)).get();
21865
+ const run = app.db.select().from(runs).where(eq23(runs.id, runId)).get();
21784
21866
  return run;
21785
21867
  });
21786
21868
  app.get("/projects/:name/google/gsc/performance", async (request) => {
21787
21869
  const project = resolveProject(app.db, request.params.name);
21788
21870
  const { startDate, endDate, query, page, limit, offset } = request.query;
21789
21871
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
21790
- const conditions = [eq22(gscSearchData.projectId, project.id)];
21872
+ const conditions = [eq23(gscSearchData.projectId, project.id)];
21791
21873
  if (startDate) conditions.push(sql9`${gscSearchData.date} >= ${startDate}`);
21792
21874
  else if (cutoffDate) conditions.push(sql9`${gscSearchData.date} >= ${cutoffDate}`);
21793
21875
  if (endDate) conditions.push(sql9`${gscSearchData.date} <= ${endDate}`);
@@ -21815,7 +21897,7 @@ async function googleRoutes(app, opts) {
21815
21897
  const windowStart = startDate ?? cutoffDate ?? "";
21816
21898
  const windowEnd = endDate ?? "9999-12-31";
21817
21899
  const dailyTotals = readGscDailyTotals(app.db, project.id, windowStart, windowEnd);
21818
- const conditions = [eq22(gscSearchData.projectId, project.id)];
21900
+ const conditions = [eq23(gscSearchData.projectId, project.id)];
21819
21901
  if (startDate) conditions.push(sql9`${gscSearchData.date} >= ${startDate}`);
21820
21902
  else if (cutoffDate) conditions.push(sql9`${gscSearchData.date} >= ${cutoffDate}`);
21821
21903
  if (endDate) conditions.push(sql9`${gscSearchData.date} <= ${endDate}`);
@@ -21876,7 +21958,7 @@ async function googleRoutes(app, opts) {
21876
21958
  const mob = ir.mobileUsabilityResult;
21877
21959
  const rich = ir.richResultsResult;
21878
21960
  const now = (/* @__PURE__ */ new Date()).toISOString();
21879
- const id = crypto18.randomUUID();
21961
+ const id = crypto19.randomUUID();
21880
21962
  app.db.insert(gscUrlInspections).values({
21881
21963
  id,
21882
21964
  projectId: project.id,
@@ -21914,8 +21996,8 @@ async function googleRoutes(app, opts) {
21914
21996
  app.get("/projects/:name/google/gsc/inspections", async (request) => {
21915
21997
  const project = resolveProject(app.db, request.params.name);
21916
21998
  const { url, limit } = request.query;
21917
- const conditions = [eq22(gscUrlInspections.projectId, project.id)];
21918
- if (url) conditions.push(eq22(gscUrlInspections.url, url));
21999
+ const conditions = [eq23(gscUrlInspections.projectId, project.id)];
22000
+ if (url) conditions.push(eq23(gscUrlInspections.url, url));
21919
22001
  const rows = app.db.select().from(gscUrlInspections).where(and17(...conditions)).orderBy(desc11(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
21920
22002
  return rows.map((r) => ({
21921
22003
  id: r.id,
@@ -21935,7 +22017,7 @@ async function googleRoutes(app, opts) {
21935
22017
  });
21936
22018
  app.get("/projects/:name/google/gsc/deindexed", async (request) => {
21937
22019
  const project = resolveProject(app.db, request.params.name);
21938
- const allInspections = app.db.select().from(gscUrlInspections).where(eq22(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
22020
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq23(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
21939
22021
  const byUrl = /* @__PURE__ */ new Map();
21940
22022
  for (const row of allInspections) {
21941
22023
  const existing = byUrl.get(row.url);
@@ -21963,7 +22045,7 @@ async function googleRoutes(app, opts) {
21963
22045
  });
21964
22046
  app.get("/projects/:name/google/gsc/coverage", async (request) => {
21965
22047
  const project = resolveProject(app.db, request.params.name);
21966
- const allInspections = app.db.select().from(gscUrlInspections).where(eq22(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
22048
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq23(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
21967
22049
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
21968
22050
  const latestByUrl = /* @__PURE__ */ new Map();
21969
22051
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -22012,7 +22094,7 @@ async function googleRoutes(app, opts) {
22012
22094
  const total = latestByUrl.size;
22013
22095
  const indexed = indexedUrls.length;
22014
22096
  const notIndexed = notIndexedUrls.length;
22015
- const latestSnapshot = app.db.select({ createdAt: gscCoverageSnapshots.createdAt }).from(gscCoverageSnapshots).where(eq22(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.createdAt)).limit(1).get();
22097
+ const latestSnapshot = app.db.select({ createdAt: gscCoverageSnapshots.createdAt }).from(gscCoverageSnapshots).where(eq23(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.createdAt)).limit(1).get();
22016
22098
  const lastSyncedAt = latestSnapshot?.createdAt ?? null;
22017
22099
  const formatRow = (r) => ({
22018
22100
  id: r.id,
@@ -22063,7 +22145,7 @@ async function googleRoutes(app, opts) {
22063
22145
  const project = resolveProject(app.db, request.params.name);
22064
22146
  const parsed = parseInt(request.query.limit ?? "90", 10);
22065
22147
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
22066
- const rows = app.db.select().from(gscCoverageSnapshots).where(eq22(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.date)).limit(limit).all();
22148
+ const rows = app.db.select().from(gscCoverageSnapshots).where(eq23(gscCoverageSnapshots.projectId, project.id)).orderBy(desc11(gscCoverageSnapshots.date)).limit(limit).all();
22067
22149
  return rows.map((r) => ({
22068
22150
  date: r.date,
22069
22151
  indexed: r.indexed,
@@ -22120,7 +22202,7 @@ async function googleRoutes(app, opts) {
22120
22202
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
22121
22203
  });
22122
22204
  const now = (/* @__PURE__ */ new Date()).toISOString();
22123
- const runId = crypto18.randomUUID();
22205
+ const runId = crypto19.randomUUID();
22124
22206
  app.db.insert(runs).values({
22125
22207
  id: runId,
22126
22208
  projectId: project.id,
@@ -22132,7 +22214,7 @@ async function googleRoutes(app, opts) {
22132
22214
  if (opts.onInspectSitemapRequested) {
22133
22215
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl });
22134
22216
  }
22135
- const run = app.db.select().from(runs).where(eq22(runs.id, runId)).get();
22217
+ const run = app.db.select().from(runs).where(eq23(runs.id, runId)).get();
22136
22218
  return { sitemaps, primarySitemapUrl: sitemapUrl, run };
22137
22219
  });
22138
22220
  app.post("/projects/:name/google/gsc/inspect-sitemap", async (request) => {
@@ -22146,7 +22228,7 @@ async function googleRoutes(app, opts) {
22146
22228
  throw validationError("No GSC property configured for this connection");
22147
22229
  }
22148
22230
  const now = (/* @__PURE__ */ new Date()).toISOString();
22149
- const runId = crypto18.randomUUID();
22231
+ const runId = crypto19.randomUUID();
22150
22232
  app.db.insert(runs).values({
22151
22233
  id: runId,
22152
22234
  projectId: project.id,
@@ -22159,7 +22241,7 @@ async function googleRoutes(app, opts) {
22159
22241
  if (opts.onInspectSitemapRequested) {
22160
22242
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl: sitemapUrl ?? void 0 });
22161
22243
  }
22162
- const run = app.db.select().from(runs).where(eq22(runs.id, runId)).get();
22244
+ const run = app.db.select().from(runs).where(eq23(runs.id, runId)).get();
22163
22245
  return run;
22164
22246
  });
22165
22247
  app.put("/projects/:name/google/connections/:type/sitemap", async (request) => {
@@ -22206,7 +22288,7 @@ async function googleRoutes(app, opts) {
22206
22288
  const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
22207
22289
  let urlsToNotify = request.body?.urls ?? [];
22208
22290
  if (request.body?.allUnindexed) {
22209
- const allInspections = app.db.select().from(gscUrlInspections).where(eq22(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
22291
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq23(gscUrlInspections.projectId, project.id)).orderBy(desc11(gscUrlInspections.inspectedAt)).all();
22210
22292
  const latestByUrl = /* @__PURE__ */ new Map();
22211
22293
  for (const row of allInspections) {
22212
22294
  if (!latestByUrl.has(row.url)) {
@@ -22324,7 +22406,7 @@ async function googleRoutes(app, opts) {
22324
22406
  };
22325
22407
  }
22326
22408
  function listSelectionResponse(projectId) {
22327
- const rows = app.db.select().from(gbpLocations).where(eq22(gbpLocations.projectId, projectId)).all();
22409
+ const rows = app.db.select().from(gbpLocations).where(eq23(gbpLocations.projectId, projectId)).all();
22328
22410
  const dtos = rows.map(rowToDto2);
22329
22411
  return {
22330
22412
  locations: dtos,
@@ -22333,15 +22415,15 @@ async function googleRoutes(app, opts) {
22333
22415
  };
22334
22416
  }
22335
22417
  function clearGbpProjectData(tx, projectId) {
22336
- tx.delete(gbpDailyMetrics).where(eq22(gbpDailyMetrics.projectId, projectId)).run();
22337
- tx.delete(gbpKeywordImpressions).where(eq22(gbpKeywordImpressions.projectId, projectId)).run();
22338
- tx.delete(gbpKeywordMonthly).where(eq22(gbpKeywordMonthly.projectId, projectId)).run();
22339
- tx.delete(gbpPlaceActions).where(eq22(gbpPlaceActions.projectId, projectId)).run();
22340
- tx.delete(gbpLodgingSnapshots).where(eq22(gbpLodgingSnapshots.projectId, projectId)).run();
22341
- tx.delete(gbpLocations).where(eq22(gbpLocations.projectId, projectId)).run();
22418
+ tx.delete(gbpDailyMetrics).where(eq23(gbpDailyMetrics.projectId, projectId)).run();
22419
+ tx.delete(gbpKeywordImpressions).where(eq23(gbpKeywordImpressions.projectId, projectId)).run();
22420
+ tx.delete(gbpKeywordMonthly).where(eq23(gbpKeywordMonthly.projectId, projectId)).run();
22421
+ tx.delete(gbpPlaceActions).where(eq23(gbpPlaceActions.projectId, projectId)).run();
22422
+ tx.delete(gbpLodgingSnapshots).where(eq23(gbpLodgingSnapshots.projectId, projectId)).run();
22423
+ tx.delete(gbpLocations).where(eq23(gbpLocations.projectId, projectId)).run();
22342
22424
  }
22343
22425
  function currentProjectAccount(projectId) {
22344
- const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq22(gbpLocations.projectId, projectId)).limit(1).get();
22426
+ const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq23(gbpLocations.projectId, projectId)).limit(1).get();
22345
22427
  return row?.accountName ?? null;
22346
22428
  }
22347
22429
  app.post("/projects/:name/gbp/locations/discover", async (request) => {
@@ -22411,7 +22493,7 @@ async function googleRoutes(app, opts) {
22411
22493
  app.db.transaction((tx) => {
22412
22494
  if (switching) clearGbpProjectData(tx, project.id);
22413
22495
  for (const remote of remoteLocations) {
22414
- const existing = tx.select().from(gbpLocations).where(and17(eq22(gbpLocations.projectId, project.id), eq22(gbpLocations.locationName, remote.name))).get();
22496
+ const existing = tx.select().from(gbpLocations).where(and17(eq23(gbpLocations.projectId, project.id), eq23(gbpLocations.locationName, remote.name))).get();
22415
22497
  const profile = buildLocationProfileFields(remote);
22416
22498
  if (existing) {
22417
22499
  tx.update(gbpLocations).set({
@@ -22424,10 +22506,10 @@ async function googleRoutes(app, opts) {
22424
22506
  mapsUri: remote.metadata?.mapsUri ?? null,
22425
22507
  ...profile,
22426
22508
  updatedAt: now
22427
- }).where(eq22(gbpLocations.id, existing.id)).run();
22509
+ }).where(eq23(gbpLocations.id, existing.id)).run();
22428
22510
  } else {
22429
22511
  tx.insert(gbpLocations).values({
22430
- id: crypto18.randomUUID(),
22512
+ id: crypto19.randomUUID(),
22431
22513
  projectId: project.id,
22432
22514
  accountName,
22433
22515
  locationName: remote.name,
@@ -22508,11 +22590,11 @@ async function googleRoutes(app, opts) {
22508
22590
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid selection request");
22509
22591
  }
22510
22592
  const { selected } = parsed.data;
22511
- const existing = app.db.select().from(gbpLocations).where(and17(eq22(gbpLocations.projectId, project.id), eq22(gbpLocations.locationName, locationName))).get();
22593
+ const existing = app.db.select().from(gbpLocations).where(and17(eq23(gbpLocations.projectId, project.id), eq23(gbpLocations.locationName, locationName))).get();
22512
22594
  if (!existing) throw notFound("GBP location", locationName);
22513
22595
  const now = (/* @__PURE__ */ new Date()).toISOString();
22514
22596
  app.db.transaction((tx) => {
22515
- tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq22(gbpLocations.id, existing.id)).run();
22597
+ tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq23(gbpLocations.id, existing.id)).run();
22516
22598
  writeAuditLog(tx, {
22517
22599
  projectId: project.id,
22518
22600
  actor: "api",
@@ -22521,7 +22603,7 @@ async function googleRoutes(app, opts) {
22521
22603
  entityId: locationName
22522
22604
  });
22523
22605
  });
22524
- const refreshed = app.db.select().from(gbpLocations).where(eq22(gbpLocations.id, existing.id)).get();
22606
+ const refreshed = app.db.select().from(gbpLocations).where(eq23(gbpLocations.id, existing.id)).get();
22525
22607
  return rowToDto2(refreshed);
22526
22608
  });
22527
22609
  app.delete("/projects/:name/gbp/connection", async (request, reply) => {
@@ -22551,7 +22633,7 @@ async function googleRoutes(app, opts) {
22551
22633
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid sync request");
22552
22634
  }
22553
22635
  const now = (/* @__PURE__ */ new Date()).toISOString();
22554
- const runId = crypto18.randomUUID();
22636
+ const runId = crypto19.randomUUID();
22555
22637
  app.db.insert(runs).values({
22556
22638
  id: runId,
22557
22639
  projectId: project.id,
@@ -22565,9 +22647,9 @@ async function googleRoutes(app, opts) {
22565
22647
  });
22566
22648
  app.get("/projects/:name/gbp/metrics", async (request) => {
22567
22649
  const project = resolveProject(app.db, request.params.name);
22568
- const conditions = [eq22(gbpDailyMetrics.projectId, project.id)];
22569
- if (request.query.locationName) conditions.push(eq22(gbpDailyMetrics.locationName, request.query.locationName));
22570
- if (request.query.metric) conditions.push(eq22(gbpDailyMetrics.metric, request.query.metric));
22650
+ const conditions = [eq23(gbpDailyMetrics.projectId, project.id)];
22651
+ if (request.query.locationName) conditions.push(eq23(gbpDailyMetrics.locationName, request.query.locationName));
22652
+ if (request.query.metric) conditions.push(eq23(gbpDailyMetrics.metric, request.query.metric));
22571
22653
  const rows = app.db.select().from(gbpDailyMetrics).where(and17(...conditions)).orderBy(desc11(gbpDailyMetrics.date)).all();
22572
22654
  return {
22573
22655
  metrics: rows.map((r) => ({ locationName: r.locationName, date: r.date, metric: r.metric, value: r.value })),
@@ -22576,8 +22658,8 @@ async function googleRoutes(app, opts) {
22576
22658
  });
22577
22659
  app.get("/projects/:name/gbp/keywords", async (request) => {
22578
22660
  const project = resolveProject(app.db, request.params.name);
22579
- const conditions = [eq22(gbpKeywordImpressions.projectId, project.id)];
22580
- if (request.query.locationName) conditions.push(eq22(gbpKeywordImpressions.locationName, request.query.locationName));
22661
+ const conditions = [eq23(gbpKeywordImpressions.projectId, project.id)];
22662
+ if (request.query.locationName) conditions.push(eq23(gbpKeywordImpressions.locationName, request.query.locationName));
22581
22663
  const rows = app.db.select().from(gbpKeywordImpressions).where(and17(...conditions)).all();
22582
22664
  rows.sort((a, b) => (b.valueCount ?? -1) - (a.valueCount ?? -1));
22583
22665
  const thresholded = rows.filter((r) => r.valueThreshold !== null).length;
@@ -22596,8 +22678,8 @@ async function googleRoutes(app, opts) {
22596
22678
  });
22597
22679
  app.get("/projects/:name/gbp/place-actions", async (request) => {
22598
22680
  const project = resolveProject(app.db, request.params.name);
22599
- const conditions = [eq22(gbpPlaceActions.projectId, project.id)];
22600
- if (request.query.locationName) conditions.push(eq22(gbpPlaceActions.locationName, request.query.locationName));
22681
+ const conditions = [eq23(gbpPlaceActions.projectId, project.id)];
22682
+ if (request.query.locationName) conditions.push(eq23(gbpPlaceActions.locationName, request.query.locationName));
22601
22683
  const rows = app.db.select().from(gbpPlaceActions).where(and17(...conditions)).all();
22602
22684
  return {
22603
22685
  placeActions: rows.map((r) => ({
@@ -22613,8 +22695,8 @@ async function googleRoutes(app, opts) {
22613
22695
  });
22614
22696
  app.get("/projects/:name/gbp/lodging", async (request) => {
22615
22697
  const project = resolveProject(app.db, request.params.name);
22616
- const conditions = [eq22(gbpLodgingSnapshots.projectId, project.id)];
22617
- if (request.query.locationName) conditions.push(eq22(gbpLodgingSnapshots.locationName, request.query.locationName));
22698
+ const conditions = [eq23(gbpLodgingSnapshots.projectId, project.id)];
22699
+ if (request.query.locationName) conditions.push(eq23(gbpLodgingSnapshots.locationName, request.query.locationName));
22618
22700
  const rows = app.db.select().from(gbpLodgingSnapshots).where(and17(...conditions)).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
22619
22701
  const latestByLocation = /* @__PURE__ */ new Map();
22620
22702
  for (const row of rows) {
@@ -22630,8 +22712,8 @@ async function googleRoutes(app, opts) {
22630
22712
  });
22631
22713
  app.get("/projects/:name/gbp/attributes", async (request) => {
22632
22714
  const project = resolveProject(app.db, request.params.name);
22633
- const conditions = [eq22(gbpAttributesSnapshots.projectId, project.id)];
22634
- if (request.query.locationName) conditions.push(eq22(gbpAttributesSnapshots.locationName, request.query.locationName));
22715
+ const conditions = [eq23(gbpAttributesSnapshots.projectId, project.id)];
22716
+ if (request.query.locationName) conditions.push(eq23(gbpAttributesSnapshots.locationName, request.query.locationName));
22635
22717
  const rows = app.db.select().from(gbpAttributesSnapshots).where(and17(...conditions)).orderBy(desc11(gbpAttributesSnapshots.syncedAt)).all();
22636
22718
  const latestByLocation = /* @__PURE__ */ new Map();
22637
22719
  for (const row of rows) {
@@ -22650,8 +22732,8 @@ async function googleRoutes(app, opts) {
22650
22732
  });
22651
22733
  app.get("/projects/:name/gbp/places", async (request) => {
22652
22734
  const project = resolveProject(app.db, request.params.name);
22653
- const conditions = [eq22(gbpPlaceDetails.projectId, project.id)];
22654
- if (request.query.locationName) conditions.push(eq22(gbpPlaceDetails.locationName, request.query.locationName));
22735
+ const conditions = [eq23(gbpPlaceDetails.projectId, project.id)];
22736
+ if (request.query.locationName) conditions.push(eq23(gbpPlaceDetails.locationName, request.query.locationName));
22655
22737
  const rows = app.db.select().from(gbpPlaceDetails).where(and17(...conditions)).orderBy(desc11(gbpPlaceDetails.syncedAt)).all();
22656
22738
  const latestByLocation = /* @__PURE__ */ new Map();
22657
22739
  for (const row of rows) {
@@ -22671,7 +22753,7 @@ async function googleRoutes(app, opts) {
22671
22753
  app.get("/projects/:name/gbp/summary", async (request) => {
22672
22754
  const project = resolveProject(app.db, request.params.name);
22673
22755
  const locationName = request.query.locationName ?? null;
22674
- const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and17(eq22(gbpLocations.projectId, project.id), eq22(gbpLocations.selected, true))).all().map((r) => r.n);
22756
+ const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and17(eq23(gbpLocations.projectId, project.id), eq23(gbpLocations.selected, true))).all().map((r) => r.n);
22675
22757
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
22676
22758
  if (locationNames.length === 0) {
22677
22759
  return buildGbpSummary({
@@ -22685,10 +22767,10 @@ async function googleRoutes(app, opts) {
22685
22767
  locationProfiles: []
22686
22768
  });
22687
22769
  }
22688
- const metricRows = app.db.select().from(gbpDailyMetrics).where(and17(eq22(gbpDailyMetrics.projectId, project.id), inArray10(gbpDailyMetrics.locationName, locationNames))).all();
22689
- const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and17(eq22(gbpKeywordImpressions.projectId, project.id), inArray10(gbpKeywordImpressions.locationName, locationNames))).all();
22690
- const placeActionRows = app.db.select().from(gbpPlaceActions).where(and17(eq22(gbpPlaceActions.projectId, project.id), inArray10(gbpPlaceActions.locationName, locationNames))).all();
22691
- const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and17(eq22(gbpLodgingSnapshots.projectId, project.id), inArray10(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
22770
+ const metricRows = app.db.select().from(gbpDailyMetrics).where(and17(eq23(gbpDailyMetrics.projectId, project.id), inArray11(gbpDailyMetrics.locationName, locationNames))).all();
22771
+ const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and17(eq23(gbpKeywordImpressions.projectId, project.id), inArray11(gbpKeywordImpressions.locationName, locationNames))).all();
22772
+ const placeActionRows = app.db.select().from(gbpPlaceActions).where(and17(eq23(gbpPlaceActions.projectId, project.id), inArray11(gbpPlaceActions.locationName, locationNames))).all();
22773
+ const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and17(eq23(gbpLodgingSnapshots.projectId, project.id), inArray11(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
22692
22774
  const latestLodgingByLocation = /* @__PURE__ */ new Map();
22693
22775
  for (const row of lodgingRows) {
22694
22776
  if (!latestLodgingByLocation.has(row.locationName)) {
@@ -22702,7 +22784,7 @@ async function googleRoutes(app, opts) {
22702
22784
  regularHours: gbpLocations.regularHours,
22703
22785
  primaryPhone: gbpLocations.primaryPhone,
22704
22786
  openStatus: gbpLocations.openStatus
22705
- }).from(gbpLocations).where(and17(eq22(gbpLocations.projectId, project.id), inArray10(gbpLocations.locationName, locationNames))).all();
22787
+ }).from(gbpLocations).where(and17(eq23(gbpLocations.projectId, project.id), inArray11(gbpLocations.locationName, locationNames))).all();
22706
22788
  return buildGbpSummary({
22707
22789
  locationName,
22708
22790
  locationCount: locationNames.length,
@@ -22717,8 +22799,8 @@ async function googleRoutes(app, opts) {
22717
22799
  }
22718
22800
 
22719
22801
  // ../api-routes/src/ads.ts
22720
- import crypto19 from "crypto";
22721
- import { eq as eq23, and as and18, asc as asc3, gte as gte3, lte as lte2, inArray as inArray11 } from "drizzle-orm";
22802
+ import crypto20 from "crypto";
22803
+ import { eq as eq24, and as and18, asc as asc3, gte as gte3, lte as lte2, inArray as inArray12 } from "drizzle-orm";
22722
22804
  function statusDto(row) {
22723
22805
  if (!row) return { connected: false };
22724
22806
  return {
@@ -22768,7 +22850,7 @@ async function adsRoutes(app, opts) {
22768
22850
  createdAt: existingCfg?.createdAt ?? now,
22769
22851
  updatedAt: now
22770
22852
  });
22771
- const existingRow = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
22853
+ const existingRow = app.db.select().from(adsConnections).where(eq24(adsConnections.projectId, project.id)).get();
22772
22854
  app.db.transaction((tx) => {
22773
22855
  if (existingRow) {
22774
22856
  tx.update(adsConnections).set({
@@ -22778,10 +22860,10 @@ async function adsRoutes(app, opts) {
22778
22860
  timezone: account.timezone,
22779
22861
  status: account.status,
22780
22862
  updatedAt: now
22781
- }).where(eq23(adsConnections.id, existingRow.id)).run();
22863
+ }).where(eq24(adsConnections.id, existingRow.id)).run();
22782
22864
  } else {
22783
22865
  tx.insert(adsConnections).values({
22784
- id: crypto19.randomUUID(),
22866
+ id: crypto20.randomUUID(),
22785
22867
  projectId: project.id,
22786
22868
  adAccountId: account.id,
22787
22869
  displayName: account.name,
@@ -22800,16 +22882,16 @@ async function adsRoutes(app, opts) {
22800
22882
  entityId: account.id
22801
22883
  }));
22802
22884
  });
22803
- const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
22885
+ const row = app.db.select().from(adsConnections).where(eq24(adsConnections.projectId, project.id)).get();
22804
22886
  return statusDto(row);
22805
22887
  }
22806
22888
  );
22807
22889
  app.delete("/projects/:name/ads/connection", async (request) => {
22808
22890
  const project = resolveProject(app.db, request.params.name);
22809
- const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
22891
+ const row = app.db.select().from(adsConnections).where(eq24(adsConnections.projectId, project.id)).get();
22810
22892
  if (row) {
22811
22893
  app.db.transaction((tx) => {
22812
- tx.delete(adsConnections).where(eq23(adsConnections.id, row.id)).run();
22894
+ tx.delete(adsConnections).where(eq24(adsConnections.id, row.id)).run();
22813
22895
  writeAuditLog(tx, auditFromRequest(request, {
22814
22896
  projectId: project.id,
22815
22897
  actor: "api",
@@ -22825,25 +22907,25 @@ async function adsRoutes(app, opts) {
22825
22907
  });
22826
22908
  app.get("/projects/:name/ads/status", async (request) => {
22827
22909
  const project = resolveProject(app.db, request.params.name);
22828
- const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
22910
+ const row = app.db.select().from(adsConnections).where(eq24(adsConnections.projectId, project.id)).get();
22829
22911
  return statusDto(row);
22830
22912
  });
22831
22913
  app.post("/projects/:name/ads/sync", async (request) => {
22832
22914
  const project = resolveProject(app.db, request.params.name);
22833
- const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
22915
+ const row = app.db.select().from(adsConnections).where(eq24(adsConnections.projectId, project.id)).get();
22834
22916
  if (!row) {
22835
22917
  throw validationError('No ads connection for this project. Run "canonry ads connect" first.');
22836
22918
  }
22837
22919
  const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and18(
22838
- eq23(runs.projectId, project.id),
22839
- eq23(runs.kind, RunKinds["ads-sync"]),
22840
- inArray11(runs.status, [RunStatuses.queued, RunStatuses.running])
22920
+ eq24(runs.projectId, project.id),
22921
+ eq24(runs.kind, RunKinds["ads-sync"]),
22922
+ inArray12(runs.status, [RunStatuses.queued, RunStatuses.running])
22841
22923
  )).get();
22842
22924
  if (inFlight) {
22843
22925
  const existing = { runId: inFlight.id, status: inFlight.status };
22844
22926
  return existing;
22845
22927
  }
22846
- const runId = crypto19.randomUUID();
22928
+ const runId = crypto20.randomUUID();
22847
22929
  app.db.insert(runs).values({
22848
22930
  id: runId,
22849
22931
  projectId: project.id,
@@ -22858,9 +22940,9 @@ async function adsRoutes(app, opts) {
22858
22940
  });
22859
22941
  app.get("/projects/:name/ads/campaigns", async (request) => {
22860
22942
  const project = resolveProject(app.db, request.params.name);
22861
- const campaignRows = app.db.select().from(adsCampaigns).where(eq23(adsCampaigns.projectId, project.id)).all();
22862
- const groupRows = app.db.select().from(adsAdGroups).where(eq23(adsAdGroups.projectId, project.id)).all();
22863
- const adRows = app.db.select().from(adsAds).where(eq23(adsAds.projectId, project.id)).all();
22943
+ const campaignRows = app.db.select().from(adsCampaigns).where(eq24(adsCampaigns.projectId, project.id)).all();
22944
+ const groupRows = app.db.select().from(adsAdGroups).where(eq24(adsAdGroups.projectId, project.id)).all();
22945
+ const adRows = app.db.select().from(adsAds).where(eq24(adsAds.projectId, project.id)).all();
22864
22946
  const adsByGroup = /* @__PURE__ */ new Map();
22865
22947
  for (const ad of adRows) {
22866
22948
  const dto = {
@@ -22914,9 +22996,9 @@ async function adsRoutes(app, opts) {
22914
22996
  }
22915
22997
  parsedLevel = result.data;
22916
22998
  }
22917
- const conditions = [eq23(adsInsightsDaily.projectId, project.id)];
22918
- if (parsedLevel) conditions.push(eq23(adsInsightsDaily.level, parsedLevel));
22919
- if (entityId) conditions.push(eq23(adsInsightsDaily.entityId, entityId));
22999
+ const conditions = [eq24(adsInsightsDaily.projectId, project.id)];
23000
+ if (parsedLevel) conditions.push(eq24(adsInsightsDaily.level, parsedLevel));
23001
+ if (entityId) conditions.push(eq24(adsInsightsDaily.entityId, entityId));
22920
23002
  if (from) conditions.push(gte3(adsInsightsDaily.date, from));
22921
23003
  if (to) conditions.push(lte2(adsInsightsDaily.date, to));
22922
23004
  const rows = app.db.select().from(adsInsightsDaily).where(and18(...conditions)).orderBy(asc3(adsInsightsDaily.date)).all();
@@ -22931,19 +23013,19 @@ async function adsRoutes(app, opts) {
22931
23013
  ctr: adsCtr(row.clicks, row.impressions),
22932
23014
  cpcMicros: adsCpcMicros(row.spendMicros, row.clicks)
22933
23015
  }));
22934
- const conn = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
23016
+ const conn = app.db.select().from(adsConnections).where(eq24(adsConnections.projectId, project.id)).get();
22935
23017
  const response = { rows: dtoRows, currencyCode: conn?.currencyCode ?? null };
22936
23018
  return response;
22937
23019
  });
22938
23020
  app.get("/projects/:name/ads/summary", async (request) => {
22939
23021
  const project = resolveProject(app.db, request.params.name);
22940
- const row = app.db.select().from(adsConnections).where(eq23(adsConnections.projectId, project.id)).get();
22941
- const campaignCount = app.db.select().from(adsCampaigns).where(eq23(adsCampaigns.projectId, project.id)).all().length;
22942
- const adGroupCount = app.db.select().from(adsAdGroups).where(eq23(adsAdGroups.projectId, project.id)).all().length;
22943
- const adCount = app.db.select().from(adsAds).where(eq23(adsAds.projectId, project.id)).all().length;
23022
+ const row = app.db.select().from(adsConnections).where(eq24(adsConnections.projectId, project.id)).get();
23023
+ const campaignCount = app.db.select().from(adsCampaigns).where(eq24(adsCampaigns.projectId, project.id)).all().length;
23024
+ const adGroupCount = app.db.select().from(adsAdGroups).where(eq24(adsAdGroups.projectId, project.id)).all().length;
23025
+ const adCount = app.db.select().from(adsAds).where(eq24(adsAds.projectId, project.id)).all().length;
22944
23026
  const campaignInsights = app.db.select().from(adsInsightsDaily).where(and18(
22945
- eq23(adsInsightsDaily.projectId, project.id),
22946
- eq23(adsInsightsDaily.level, "campaign")
23027
+ eq24(adsInsightsDaily.projectId, project.id),
23028
+ eq24(adsInsightsDaily.level, "campaign")
22947
23029
  )).all();
22948
23030
  let impressions = 0;
22949
23031
  let clicks = 0;
@@ -22982,8 +23064,8 @@ async function adsRoutes(app, opts) {
22982
23064
  }
22983
23065
 
22984
23066
  // ../api-routes/src/bing.ts
22985
- import crypto20 from "crypto";
22986
- import { eq as eq24, and as and19, desc as desc12 } from "drizzle-orm";
23067
+ import crypto21 from "crypto";
23068
+ import { eq as eq25, and as and19, desc as desc12 } from "drizzle-orm";
22987
23069
 
22988
23070
  // ../integration-bing/src/constants.ts
22989
23071
  var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
@@ -23357,7 +23439,7 @@ async function bingRoutes(app, opts) {
23357
23439
  const store = requireConnectionStore();
23358
23440
  const project = resolveProject(app.db, request.params.name);
23359
23441
  requireConnection(store, project.canonicalDomain);
23360
- const allInspections = app.db.select().from(bingUrlInspections).where(eq24(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
23442
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq25(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
23361
23443
  const latestByUrl = /* @__PURE__ */ new Map();
23362
23444
  const definitiveByUrl = /* @__PURE__ */ new Map();
23363
23445
  for (const row of allInspections) {
@@ -23414,7 +23496,7 @@ async function bingRoutes(app, opts) {
23414
23496
  const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
23415
23497
  const now = (/* @__PURE__ */ new Date()).toISOString();
23416
23498
  app.db.insert(bingCoverageSnapshots).values({
23417
- id: crypto20.randomUUID(),
23499
+ id: crypto21.randomUUID(),
23418
23500
  projectId: project.id,
23419
23501
  syncRunId: snapshotRunId,
23420
23502
  date: snapshotDate,
@@ -23446,7 +23528,7 @@ async function bingRoutes(app, opts) {
23446
23528
  const project = resolveProject(app.db, request.params.name);
23447
23529
  const parsed = parseInt(request.query.limit ?? "90", 10);
23448
23530
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
23449
- const rows = app.db.select().from(bingCoverageSnapshots).where(eq24(bingCoverageSnapshots.projectId, project.id)).orderBy(desc12(bingCoverageSnapshots.date)).limit(limit).all();
23531
+ const rows = app.db.select().from(bingCoverageSnapshots).where(eq25(bingCoverageSnapshots.projectId, project.id)).orderBy(desc12(bingCoverageSnapshots.date)).limit(limit).all();
23450
23532
  return rows.map((r) => ({
23451
23533
  date: r.date,
23452
23534
  indexed: r.indexed,
@@ -23458,7 +23540,7 @@ async function bingRoutes(app, opts) {
23458
23540
  requireConnectionStore();
23459
23541
  const project = resolveProject(app.db, request.params.name);
23460
23542
  const { url, limit } = request.query;
23461
- const whereClause = url ? and19(eq24(bingUrlInspections.projectId, project.id), eq24(bingUrlInspections.url, url)) : eq24(bingUrlInspections.projectId, project.id);
23543
+ const whereClause = url ? and19(eq25(bingUrlInspections.projectId, project.id), eq25(bingUrlInspections.url, url)) : eq25(bingUrlInspections.projectId, project.id);
23462
23544
  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();
23463
23545
  return filtered.map((r) => ({
23464
23546
  id: r.id,
@@ -23485,7 +23567,7 @@ async function bingRoutes(app, opts) {
23485
23567
  throw validationError("url is required");
23486
23568
  }
23487
23569
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
23488
- const runId = crypto20.randomUUID();
23570
+ const runId = crypto21.randomUUID();
23489
23571
  app.db.insert(runs).values({
23490
23572
  id: runId,
23491
23573
  projectId: project.id,
@@ -23506,7 +23588,7 @@ async function bingRoutes(app, opts) {
23506
23588
  discoveryDate: result.DiscoveryDate ?? null
23507
23589
  });
23508
23590
  const now = (/* @__PURE__ */ new Date()).toISOString();
23509
- const id = crypto20.randomUUID();
23591
+ const id = crypto21.randomUUID();
23510
23592
  const httpCode = result.HttpStatus ?? result.HttpCode ?? null;
23511
23593
  const lastCrawledDate = parseBingDate(result.LastCrawledDate);
23512
23594
  const inIndexDate = parseBingDate(result.InIndexDate);
@@ -23548,7 +23630,7 @@ async function bingRoutes(app, opts) {
23548
23630
  anchorCount: result.AnchorCount ?? null,
23549
23631
  discoveryDate
23550
23632
  }).run();
23551
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq24(runs.id, runId)).run();
23633
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq25(runs.id, runId)).run();
23552
23634
  return {
23553
23635
  id,
23554
23636
  url,
@@ -23564,7 +23646,7 @@ async function bingRoutes(app, opts) {
23564
23646
  } catch (e) {
23565
23647
  const msg = e instanceof Error ? e.message : String(e);
23566
23648
  bingLog("error", "inspect-url.failed", { domain: project.canonicalDomain, url, error: msg });
23567
- app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq24(runs.id, runId)).run();
23649
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq25(runs.id, runId)).run();
23568
23650
  throw e;
23569
23651
  }
23570
23652
  });
@@ -23576,7 +23658,7 @@ async function bingRoutes(app, opts) {
23576
23658
  throw validationError('No Bing site configured. Run "canonry bing set-site <project> <url>" first.');
23577
23659
  }
23578
23660
  const now = (/* @__PURE__ */ new Date()).toISOString();
23579
- const runId = crypto20.randomUUID();
23661
+ const runId = crypto21.randomUUID();
23580
23662
  app.db.insert(runs).values({
23581
23663
  id: runId,
23582
23664
  projectId: project.id,
@@ -23591,7 +23673,7 @@ async function bingRoutes(app, opts) {
23591
23673
  } else {
23592
23674
  bingLog("warn", "inspect-sitemap.no-callback", { domain: project.canonicalDomain, runId });
23593
23675
  }
23594
- const run = app.db.select().from(runs).where(eq24(runs.id, runId)).get();
23676
+ const run = app.db.select().from(runs).where(eq25(runs.id, runId)).get();
23595
23677
  return run;
23596
23678
  });
23597
23679
  app.post("/projects/:name/bing/request-indexing", async (request) => {
@@ -23603,7 +23685,7 @@ async function bingRoutes(app, opts) {
23603
23685
  }
23604
23686
  let urlsToSubmit = request.body?.urls ?? [];
23605
23687
  if (request.body?.allUnindexed) {
23606
- const allInspections = app.db.select().from(bingUrlInspections).where(eq24(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
23688
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq25(bingUrlInspections.projectId, project.id)).orderBy(desc12(bingUrlInspections.inspectedAt)).all();
23607
23689
  const latestByUrl = /* @__PURE__ */ new Map();
23608
23690
  for (const row of allInspections) {
23609
23691
  if (!latestByUrl.has(row.url)) {
@@ -23690,14 +23772,14 @@ async function bingRoutes(app, opts) {
23690
23772
  import fs from "fs";
23691
23773
  import path from "path";
23692
23774
  import os from "os";
23693
- import { eq as eq25, and as and20 } from "drizzle-orm";
23775
+ import { eq as eq26, and as and20 } from "drizzle-orm";
23694
23776
  function getScreenshotDir() {
23695
23777
  return path.join(os.homedir(), ".canonry", "screenshots");
23696
23778
  }
23697
23779
  async function cdpRoutes(app, opts) {
23698
23780
  app.get("/screenshots/:snapshotId", async (request, reply) => {
23699
23781
  const { snapshotId } = request.params;
23700
- 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();
23782
+ const snapshot = app.db.select({ screenshotPath: querySnapshots.screenshotPath, projectId: runs.projectId }).from(querySnapshots).innerJoin(runs, eq26(querySnapshots.runId, runs.id)).where(eq26(querySnapshots.id, snapshotId)).get();
23701
23783
  if (!snapshot?.screenshotPath) {
23702
23784
  const err = notFound("Screenshot", snapshotId);
23703
23785
  return reply.code(err.statusCode).send(err.toJSON());
@@ -23765,7 +23847,7 @@ async function cdpRoutes(app, opts) {
23765
23847
  async (request, reply) => {
23766
23848
  const project = resolveProject(app.db, request.params.name);
23767
23849
  const { runId } = request.params;
23768
- const run = app.db.select().from(runs).where(and20(eq25(runs.id, runId), eq25(runs.projectId, project.id))).get();
23850
+ const run = app.db.select().from(runs).where(and20(eq26(runs.id, runId), eq26(runs.projectId, project.id))).get();
23769
23851
  if (!run) {
23770
23852
  const err = notFound("Run", runId);
23771
23853
  return reply.code(err.statusCode).send(err.toJSON());
@@ -23778,8 +23860,8 @@ async function cdpRoutes(app, opts) {
23778
23860
  citedDomains: querySnapshots.citedDomains,
23779
23861
  screenshotPath: querySnapshots.screenshotPath,
23780
23862
  rawResponse: querySnapshots.rawResponse
23781
- }).from(querySnapshots).where(eq25(querySnapshots.runId, runId)).all());
23782
- const queryRows = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq25(queries.projectId, project.id)).all();
23863
+ }).from(querySnapshots).where(eq26(querySnapshots.runId, runId)).all());
23864
+ const queryRows = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq26(queries.projectId, project.id)).all();
23783
23865
  const queryMap = new Map(queryRows.map((q) => [q.id, q.query]));
23784
23866
  const byQuery = /* @__PURE__ */ new Map();
23785
23867
  for (const snap of snapshots) {
@@ -23861,8 +23943,8 @@ async function cdpRoutes(app, opts) {
23861
23943
  }
23862
23944
 
23863
23945
  // ../api-routes/src/ga.ts
23864
- import crypto21 from "crypto";
23865
- import { eq as eq26, desc as desc13, and as and21, sql as sql10 } from "drizzle-orm";
23946
+ import crypto22 from "crypto";
23947
+ import { eq as eq27, desc as desc13, and as and21, sql as sql10 } from "drizzle-orm";
23866
23948
  function gaLog(level, action, ctx) {
23867
23949
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
23868
23950
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -24139,10 +24221,10 @@ async function ga4Routes(app, opts) {
24139
24221
  if (!saConn && !oauthConn) {
24140
24222
  throw notFound("GA4 connection", project.name);
24141
24223
  }
24142
- app.db.delete(gaTrafficSnapshots).where(eq26(gaTrafficSnapshots.projectId, project.id)).run();
24143
- app.db.delete(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).run();
24144
- app.db.delete(gaAiReferrals).where(eq26(gaAiReferrals.projectId, project.id)).run();
24145
- app.db.delete(gaSocialReferrals).where(eq26(gaSocialReferrals.projectId, project.id)).run();
24224
+ app.db.delete(gaTrafficSnapshots).where(eq27(gaTrafficSnapshots.projectId, project.id)).run();
24225
+ app.db.delete(gaTrafficSummaries).where(eq27(gaTrafficSummaries.projectId, project.id)).run();
24226
+ app.db.delete(gaAiReferrals).where(eq27(gaAiReferrals.projectId, project.id)).run();
24227
+ app.db.delete(gaSocialReferrals).where(eq27(gaSocialReferrals.projectId, project.id)).run();
24146
24228
  const propertyId = saConn?.propertyId ?? oauthConn?.propertyId ?? null;
24147
24229
  opts.ga4CredentialStore?.deleteConnection(project.name);
24148
24230
  opts.googleConnectionStore?.deleteConnection(project.canonicalDomain, "ga4");
@@ -24163,7 +24245,7 @@ async function ga4Routes(app, opts) {
24163
24245
  if (!connected) {
24164
24246
  return { connected: false, propertyId: null, clientEmail: null, authMethod: null, lastSyncedAt: null };
24165
24247
  }
24166
- const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
24248
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq27(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
24167
24249
  return {
24168
24250
  connected: true,
24169
24251
  propertyId: saConn?.propertyId ?? oauthConn?.propertyId ?? null,
@@ -24187,7 +24269,7 @@ async function ga4Routes(app, opts) {
24187
24269
  const syncAi = !only || only === "ai";
24188
24270
  const syncSocial = !only || only === "social";
24189
24271
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
24190
- const runId = crypto21.randomUUID();
24272
+ const runId = crypto22.randomUUID();
24191
24273
  app.db.insert(runs).values({
24192
24274
  id: runId,
24193
24275
  projectId: project.id,
@@ -24228,14 +24310,14 @@ async function ga4Routes(app, opts) {
24228
24310
  if (syncTraffic) {
24229
24311
  tx.delete(gaTrafficSnapshots).where(
24230
24312
  and21(
24231
- eq26(gaTrafficSnapshots.projectId, project.id),
24313
+ eq27(gaTrafficSnapshots.projectId, project.id),
24232
24314
  sql10`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
24233
24315
  sql10`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
24234
24316
  )
24235
24317
  ).run();
24236
24318
  for (const row of rows) {
24237
24319
  tx.insert(gaTrafficSnapshots).values({
24238
- id: crypto21.randomUUID(),
24320
+ id: crypto22.randomUUID(),
24239
24321
  projectId: project.id,
24240
24322
  date: row.date,
24241
24323
  landingPage: row.landingPage,
@@ -24252,14 +24334,14 @@ async function ga4Routes(app, opts) {
24252
24334
  if (syncAi) {
24253
24335
  tx.delete(gaAiReferrals).where(
24254
24336
  and21(
24255
- eq26(gaAiReferrals.projectId, project.id),
24337
+ eq27(gaAiReferrals.projectId, project.id),
24256
24338
  sql10`${gaAiReferrals.date} >= ${summary.periodStart}`,
24257
24339
  sql10`${gaAiReferrals.date} <= ${summary.periodEnd}`
24258
24340
  )
24259
24341
  ).run();
24260
24342
  for (const row of aiReferrals) {
24261
24343
  tx.insert(gaAiReferrals).values({
24262
- id: crypto21.randomUUID(),
24344
+ id: crypto22.randomUUID(),
24263
24345
  projectId: project.id,
24264
24346
  date: row.date,
24265
24347
  source: row.source,
@@ -24284,14 +24366,14 @@ async function ga4Routes(app, opts) {
24284
24366
  if (syncSocial) {
24285
24367
  tx.delete(gaSocialReferrals).where(
24286
24368
  and21(
24287
- eq26(gaSocialReferrals.projectId, project.id),
24369
+ eq27(gaSocialReferrals.projectId, project.id),
24288
24370
  sql10`${gaSocialReferrals.date} >= ${summary.periodStart}`,
24289
24371
  sql10`${gaSocialReferrals.date} <= ${summary.periodEnd}`
24290
24372
  )
24291
24373
  ).run();
24292
24374
  for (const row of socialReferrals) {
24293
24375
  tx.insert(gaSocialReferrals).values({
24294
- id: crypto21.randomUUID(),
24376
+ id: crypto22.randomUUID(),
24295
24377
  projectId: project.id,
24296
24378
  date: row.date,
24297
24379
  source: row.source,
@@ -24305,9 +24387,9 @@ async function ga4Routes(app, opts) {
24305
24387
  }
24306
24388
  }
24307
24389
  if (syncSummary) {
24308
- tx.delete(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).run();
24390
+ tx.delete(gaTrafficSummaries).where(eq27(gaTrafficSummaries.projectId, project.id)).run();
24309
24391
  tx.insert(gaTrafficSummaries).values({
24310
- id: crypto21.randomUUID(),
24392
+ id: crypto22.randomUUID(),
24311
24393
  projectId: project.id,
24312
24394
  periodStart: summary.periodStart,
24313
24395
  periodEnd: summary.periodEnd,
@@ -24317,10 +24399,10 @@ async function ga4Routes(app, opts) {
24317
24399
  syncedAt: now,
24318
24400
  syncRunId: runId
24319
24401
  }).run();
24320
- tx.delete(gaTrafficWindowSummaries).where(eq26(gaTrafficWindowSummaries.projectId, project.id)).run();
24402
+ tx.delete(gaTrafficWindowSummaries).where(eq27(gaTrafficWindowSummaries.projectId, project.id)).run();
24321
24403
  for (const ws of windowSummaries) {
24322
24404
  tx.insert(gaTrafficWindowSummaries).values({
24323
- id: crypto21.randomUUID(),
24405
+ id: crypto22.randomUUID(),
24324
24406
  projectId: project.id,
24325
24407
  windowKey: ws.windowKey,
24326
24408
  periodStart: ws.periodStart,
@@ -24335,7 +24417,7 @@ async function ga4Routes(app, opts) {
24335
24417
  }
24336
24418
  }
24337
24419
  });
24338
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq26(runs.id, runId)).run();
24420
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq27(runs.id, runId)).run();
24339
24421
  const syncedComponents = only ? [
24340
24422
  ...syncTraffic ? ["traffic"] : [],
24341
24423
  ...syncSummary ? ["summary"] : [],
@@ -24364,7 +24446,7 @@ async function ga4Routes(app, opts) {
24364
24446
  } catch (e) {
24365
24447
  const msg = e instanceof Error ? e.message : String(e);
24366
24448
  gaLog("error", "sync.fetch-failed", { projectId: project.id, runId, error: msg });
24367
- app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq26(runs.id, runId)).run();
24449
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq27(runs.id, runId)).run();
24368
24450
  throw e;
24369
24451
  }
24370
24452
  });
@@ -24375,11 +24457,11 @@ async function ga4Routes(app, opts) {
24375
24457
  const window = parseWindow(request.query.window);
24376
24458
  const cutoff = windowCutoff(window);
24377
24459
  const cutoffDate = cutoff?.slice(0, 10) ?? null;
24378
- const snapshotConditions = [eq26(gaTrafficSnapshots.projectId, project.id)];
24460
+ const snapshotConditions = [eq27(gaTrafficSnapshots.projectId, project.id)];
24379
24461
  if (cutoffDate) snapshotConditions.push(sql10`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
24380
- const aiConditions = [eq26(gaAiReferrals.projectId, project.id)];
24462
+ const aiConditions = [eq27(gaAiReferrals.projectId, project.id)];
24381
24463
  if (cutoffDate) aiConditions.push(sql10`${gaAiReferrals.date} >= ${cutoffDate}`);
24382
- const socialConditions = [eq26(gaSocialReferrals.projectId, project.id)];
24464
+ const socialConditions = [eq27(gaSocialReferrals.projectId, project.id)];
24383
24465
  if (cutoffDate) socialConditions.push(sql10`${gaSocialReferrals.date} >= ${cutoffDate}`);
24384
24466
  const windowSummaryRow = cutoffDate ? app.db.select({
24385
24467
  totalSessions: gaTrafficWindowSummaries.totalSessions,
@@ -24388,8 +24470,8 @@ async function ga4Routes(app, opts) {
24388
24470
  totalUsers: gaTrafficWindowSummaries.totalUsers
24389
24471
  }).from(gaTrafficWindowSummaries).where(
24390
24472
  and21(
24391
- eq26(gaTrafficWindowSummaries.projectId, project.id),
24392
- eq26(gaTrafficWindowSummaries.windowKey, window)
24473
+ eq27(gaTrafficWindowSummaries.projectId, project.id),
24474
+ eq27(gaTrafficWindowSummaries.windowKey, window)
24393
24475
  )
24394
24476
  ).get() : null;
24395
24477
  const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
@@ -24401,14 +24483,14 @@ async function ga4Routes(app, opts) {
24401
24483
  totalSessions: gaTrafficSummaries.totalSessions,
24402
24484
  totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
24403
24485
  totalUsers: gaTrafficSummaries.totalUsers
24404
- }).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).get();
24486
+ }).from(gaTrafficSummaries).where(eq27(gaTrafficSummaries.projectId, project.id)).get();
24405
24487
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
24406
24488
  totalDirectSessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
24407
24489
  }).from(gaTrafficSnapshots).where(and21(...snapshotConditions)).get();
24408
24490
  const summaryMeta = app.db.select({
24409
24491
  periodStart: gaTrafficSummaries.periodStart,
24410
24492
  periodEnd: gaTrafficSummaries.periodEnd
24411
- }).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).get();
24493
+ }).from(gaTrafficSummaries).where(eq27(gaTrafficSummaries.projectId, project.id)).get();
24412
24494
  const rows = app.db.select({
24413
24495
  landingPage: sql10`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
24414
24496
  sessions: sql10`SUM(${gaTrafficSnapshots.sessions})`,
@@ -24469,7 +24551,7 @@ async function ga4Routes(app, opts) {
24469
24551
  sessions: sql10`SUM(${gaSocialReferrals.sessions})`,
24470
24552
  users: sql10`SUM(${gaSocialReferrals.users})`
24471
24553
  }).from(gaSocialReferrals).where(and21(...socialConditions)).get();
24472
- const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
24554
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq27(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
24473
24555
  const total = summaryRow?.totalSessions ?? 0;
24474
24556
  const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
24475
24557
  const totalOrganicSessions = summaryRow?.totalOrganicSessions ?? 0;
@@ -24567,7 +24649,7 @@ async function ga4Routes(app, opts) {
24567
24649
  const project = resolveProject(app.db, request.params.name);
24568
24650
  requireGa4Connection(opts, project.name, project.canonicalDomain);
24569
24651
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
24570
- const conditions = [eq26(gaAiReferrals.projectId, project.id)];
24652
+ const conditions = [eq27(gaAiReferrals.projectId, project.id)];
24571
24653
  if (cutoffDate) conditions.push(sql10`${gaAiReferrals.date} >= ${cutoffDate}`);
24572
24654
  const rows = app.db.select({
24573
24655
  date: gaAiReferrals.date,
@@ -24592,7 +24674,7 @@ async function ga4Routes(app, opts) {
24592
24674
  const project = resolveProject(app.db, request.params.name);
24593
24675
  requireGa4Connection(opts, project.name, project.canonicalDomain);
24594
24676
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
24595
- const conditions = [eq26(gaSocialReferrals.projectId, project.id)];
24677
+ const conditions = [eq27(gaSocialReferrals.projectId, project.id)];
24596
24678
  if (cutoffDate) conditions.push(sql10`${gaSocialReferrals.date} >= ${cutoffDate}`);
24597
24679
  const rows = app.db.select({
24598
24680
  date: gaSocialReferrals.date,
@@ -24615,7 +24697,7 @@ async function ga4Routes(app, opts) {
24615
24697
  return fmt(d);
24616
24698
  };
24617
24699
  const sumSocial = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and21(
24618
- eq26(gaSocialReferrals.projectId, project.id),
24700
+ eq27(gaSocialReferrals.projectId, project.id),
24619
24701
  sql10`${gaSocialReferrals.date} >= ${from}`,
24620
24702
  sql10`${gaSocialReferrals.date} < ${to}`
24621
24703
  )).get();
@@ -24628,7 +24710,7 @@ async function ga4Routes(app, opts) {
24628
24710
  source: gaSocialReferrals.source,
24629
24711
  sessions: sql10`SUM(${gaSocialReferrals.sessions})`
24630
24712
  }).from(gaSocialReferrals).where(and21(
24631
- eq26(gaSocialReferrals.projectId, project.id),
24713
+ eq27(gaSocialReferrals.projectId, project.id),
24632
24714
  sql10`${gaSocialReferrals.date} >= ${daysAgo(7)}`,
24633
24715
  sql10`${gaSocialReferrals.date} < ${fmt(today)}`
24634
24716
  )).groupBy(gaSocialReferrals.source).all();
@@ -24636,7 +24718,7 @@ async function ga4Routes(app, opts) {
24636
24718
  source: gaSocialReferrals.source,
24637
24719
  sessions: sql10`SUM(${gaSocialReferrals.sessions})`
24638
24720
  }).from(gaSocialReferrals).where(and21(
24639
- eq26(gaSocialReferrals.projectId, project.id),
24721
+ eq27(gaSocialReferrals.projectId, project.id),
24640
24722
  sql10`${gaSocialReferrals.date} >= ${daysAgo(14)}`,
24641
24723
  sql10`${gaSocialReferrals.date} < ${daysAgo(7)}`
24642
24724
  )).groupBy(gaSocialReferrals.source).all();
@@ -24677,16 +24759,16 @@ async function ga4Routes(app, opts) {
24677
24759
  return fmt(d);
24678
24760
  };
24679
24761
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
24680
- const sumTotal = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and21(eq26(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
24681
- const sumOrganic = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and21(eq26(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
24682
- const sumDirect = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and21(eq26(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
24762
+ const sumTotal = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and21(eq27(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
24763
+ const sumOrganic = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and21(eq27(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
24764
+ const sumDirect = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and21(eq27(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
24683
24765
  const sumAi = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and21(
24684
- eq26(gaAiReferrals.projectId, project.id),
24766
+ eq27(gaAiReferrals.projectId, project.id),
24685
24767
  sql10`${gaAiReferrals.date} >= ${from}`,
24686
24768
  sql10`${gaAiReferrals.date} < ${to}`,
24687
- eq26(gaAiReferrals.sourceDimension, "session")
24769
+ eq27(gaAiReferrals.sourceDimension, "session")
24688
24770
  )).get();
24689
- const sumSocial = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and21(eq26(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${from}`, sql10`${gaSocialReferrals.date} < ${to}`)).get();
24771
+ const sumSocial = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and21(eq27(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${from}`, sql10`${gaSocialReferrals.date} < ${to}`)).get();
24690
24772
  const todayStr = fmt(today);
24691
24773
  const buildTrend = (sum) => {
24692
24774
  const c7 = sum(daysAgo(7), todayStr)?.sessions ?? 0;
@@ -24696,16 +24778,16 @@ async function ga4Routes(app, opts) {
24696
24778
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
24697
24779
  };
24698
24780
  const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and21(
24699
- eq26(gaAiReferrals.projectId, project.id),
24781
+ eq27(gaAiReferrals.projectId, project.id),
24700
24782
  sql10`${gaAiReferrals.date} >= ${daysAgo(7)}`,
24701
24783
  sql10`${gaAiReferrals.date} < ${todayStr}`,
24702
- eq26(gaAiReferrals.sourceDimension, "session")
24784
+ eq27(gaAiReferrals.sourceDimension, "session")
24703
24785
  )).groupBy(gaAiReferrals.source).all();
24704
24786
  const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and21(
24705
- eq26(gaAiReferrals.projectId, project.id),
24787
+ eq27(gaAiReferrals.projectId, project.id),
24706
24788
  sql10`${gaAiReferrals.date} >= ${daysAgo(14)}`,
24707
24789
  sql10`${gaAiReferrals.date} < ${daysAgo(7)}`,
24708
- eq26(gaAiReferrals.sourceDimension, "session")
24790
+ eq27(gaAiReferrals.sourceDimension, "session")
24709
24791
  )).groupBy(gaAiReferrals.source).all();
24710
24792
  const findBiggestMover = (current, prev) => {
24711
24793
  const prevMap = new Map(prev.map((r) => [r.source, r.sessions]));
@@ -24721,8 +24803,8 @@ async function ga4Routes(app, opts) {
24721
24803
  }
24722
24804
  return mover;
24723
24805
  };
24724
- const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and21(eq26(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql10`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
24725
- const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and21(eq26(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql10`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
24806
+ const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and21(eq27(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql10`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
24807
+ const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and21(eq27(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql10`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
24726
24808
  return {
24727
24809
  total: buildTrend(sumTotal),
24728
24810
  organic: buildTrend(sumOrganic),
@@ -24737,7 +24819,7 @@ async function ga4Routes(app, opts) {
24737
24819
  const project = resolveProject(app.db, request.params.name);
24738
24820
  requireGa4Connection(opts, project.name, project.canonicalDomain);
24739
24821
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
24740
- const conditions = [eq26(gaTrafficSnapshots.projectId, project.id)];
24822
+ const conditions = [eq27(gaTrafficSnapshots.projectId, project.id)];
24741
24823
  if (cutoffDate) conditions.push(sql10`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
24742
24824
  const rows = app.db.select({
24743
24825
  date: gaTrafficSnapshots.date,
@@ -24760,7 +24842,7 @@ async function ga4Routes(app, opts) {
24760
24842
  sessions: sql10`SUM(${gaTrafficSnapshots.sessions})`,
24761
24843
  organicSessions: sql10`SUM(${gaTrafficSnapshots.organicSessions})`,
24762
24844
  users: sql10`SUM(${gaTrafficSnapshots.users})`
24763
- }).from(gaTrafficSnapshots).where(eq26(gaTrafficSnapshots.projectId, project.id)).groupBy(sql10`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql10`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
24845
+ }).from(gaTrafficSnapshots).where(eq27(gaTrafficSnapshots.projectId, project.id)).groupBy(sql10`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql10`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
24764
24846
  return {
24765
24847
  pages: trafficPages.map((r) => ({
24766
24848
  landingPage: r.landingPage,
@@ -24894,7 +24976,7 @@ function parseSchemaPageEntry(entry) {
24894
24976
  }
24895
24977
 
24896
24978
  // ../integration-wordpress/src/wordpress-client.ts
24897
- import crypto22 from "crypto";
24979
+ import crypto23 from "crypto";
24898
24980
  function validateUsername(username) {
24899
24981
  if (!username || typeof username !== "string" || username.trim().length === 0) {
24900
24982
  throw new WordpressApiError("AUTH_INVALID", "Username is required and must be a non-empty string", 400);
@@ -25107,7 +25189,7 @@ function buildSnippet(content) {
25107
25189
  return `${text2.slice(0, 157)}...`;
25108
25190
  }
25109
25191
  function contentHash(content) {
25110
- return crypto22.createHash("sha256").update(content).digest("hex");
25192
+ return crypto23.createHash("sha256").update(content).digest("hex");
25111
25193
  }
25112
25194
  function buildAmbiguousSlugMessage(slug, pages) {
25113
25195
  const candidates = pages.map((page) => {
@@ -26407,8 +26489,8 @@ async function wordpressRoutes(app, opts) {
26407
26489
  }
26408
26490
 
26409
26491
  // ../api-routes/src/backlinks.ts
26410
- import crypto23 from "crypto";
26411
- import { and as and23, asc as asc4, desc as desc14, eq as eq27, sql as sql11 } from "drizzle-orm";
26492
+ import crypto24 from "crypto";
26493
+ import { and as and23, asc as asc4, desc as desc14, eq as eq28, sql as sql11 } from "drizzle-orm";
26412
26494
 
26413
26495
  // ../integration-commoncrawl/src/constants.ts
26414
26496
  import os2 from "os";
@@ -26905,10 +26987,10 @@ function mapRunRow(row) {
26905
26987
  }
26906
26988
  function latestSummaryForProject(db, projectId, source, release) {
26907
26989
  const conditions = [
26908
- eq27(backlinkSummaries.projectId, projectId),
26909
- eq27(backlinkSummaries.source, source)
26990
+ eq28(backlinkSummaries.projectId, projectId),
26991
+ eq28(backlinkSummaries.source, source)
26910
26992
  ];
26911
- if (release) conditions.push(eq27(backlinkSummaries.release, release));
26993
+ if (release) conditions.push(eq28(backlinkSummaries.release, release));
26912
26994
  return db.select().from(backlinkSummaries).where(and23(...conditions)).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
26913
26995
  }
26914
26996
  function parseExcludeCrawlers(value) {
@@ -26918,9 +27000,9 @@ function parseExcludeCrawlers(value) {
26918
27000
  }
26919
27001
  function computeFilteredSummary(db, base) {
26920
27002
  const baseDomainCondition = and23(
26921
- eq27(backlinkDomains.projectId, base.projectId),
26922
- eq27(backlinkDomains.source, base.source),
26923
- eq27(backlinkDomains.release, base.release)
27003
+ eq28(backlinkDomains.projectId, base.projectId),
27004
+ eq28(backlinkDomains.source, base.source),
27005
+ eq28(backlinkDomains.release, base.release)
26924
27006
  );
26925
27007
  const filteredCondition = and23(baseDomainCondition, backlinkCrawlerExclusionClause());
26926
27008
  const unfilteredAgg = db.select({
@@ -26952,13 +27034,13 @@ function computeFilteredSummary(db, base) {
26952
27034
  };
26953
27035
  }
26954
27036
  function buildSourceAvailability(db, projectId, source, connected, excludeCrawlers) {
26955
- const summary = db.select().from(backlinkSummaries).where(and23(eq27(backlinkSummaries.projectId, projectId), eq27(backlinkSummaries.source, source))).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
27037
+ const summary = db.select().from(backlinkSummaries).where(and23(eq28(backlinkSummaries.projectId, projectId), eq28(backlinkSummaries.source, source))).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
26956
27038
  let totalLinkingDomains = summary?.totalLinkingDomains ?? 0;
26957
27039
  if (summary && excludeCrawlers) {
26958
27040
  const filtered = db.select({ count: sql11`count(*)` }).from(backlinkDomains).where(and23(
26959
- eq27(backlinkDomains.projectId, projectId),
26960
- eq27(backlinkDomains.source, source),
26961
- eq27(backlinkDomains.release, summary.release),
27041
+ eq28(backlinkDomains.projectId, projectId),
27042
+ eq28(backlinkDomains.source, source),
27043
+ eq28(backlinkDomains.release, summary.release),
26962
27044
  backlinkCrawlerExclusionClause()
26963
27045
  )).get();
26964
27046
  totalLinkingDomains = Number(filtered?.count ?? 0);
@@ -26973,7 +27055,7 @@ function buildSourceAvailability(db, projectId, source, connected, excludeCrawle
26973
27055
  };
26974
27056
  }
26975
27057
  function computeSourceAvailability(db, project, bingStore, excludeCrawlers) {
26976
- const ccReadySync = db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq27(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
27058
+ const ccReadySync = db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq28(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
26977
27059
  const ccConnected = project.autoExtractBacklinks === true && !!ccReadySync;
26978
27060
  const bingConnected = !!bingStore?.getConnection(project.canonicalDomain);
26979
27061
  const sources = [
@@ -27029,7 +27111,7 @@ async function backlinksRoutes(app, opts) {
27029
27111
  "@duckdb/node-api is not installed. Run `canonry backlinks install` to enable the backlinks feature."
27030
27112
  );
27031
27113
  }
27032
- const existing = app.db.select().from(ccReleaseSyncs).where(eq27(ccReleaseSyncs.release, release)).get();
27114
+ const existing = app.db.select().from(ccReleaseSyncs).where(eq28(ccReleaseSyncs.release, release)).get();
27033
27115
  const now = (/* @__PURE__ */ new Date()).toISOString();
27034
27116
  if (existing) {
27035
27117
  if (NON_TERMINAL_SYNC_STATUSES.has(existing.status)) {
@@ -27040,12 +27122,12 @@ async function backlinksRoutes(app, opts) {
27040
27122
  phaseDetail: null,
27041
27123
  error: null,
27042
27124
  updatedAt: now
27043
- }).where(eq27(ccReleaseSyncs.id, existing.id)).run();
27125
+ }).where(eq28(ccReleaseSyncs.id, existing.id)).run();
27044
27126
  opts.onReleaseSyncRequested(existing.id, release);
27045
- const refreshed = app.db.select().from(ccReleaseSyncs).where(eq27(ccReleaseSyncs.id, existing.id)).get();
27127
+ const refreshed = app.db.select().from(ccReleaseSyncs).where(eq28(ccReleaseSyncs.id, existing.id)).get();
27046
27128
  return reply.status(200).send(mapSyncRow(refreshed));
27047
27129
  }
27048
- const id = crypto23.randomUUID();
27130
+ const id = crypto24.randomUUID();
27049
27131
  app.db.insert(ccReleaseSyncs).values({
27050
27132
  id,
27051
27133
  release,
@@ -27054,7 +27136,7 @@ async function backlinksRoutes(app, opts) {
27054
27136
  updatedAt: now
27055
27137
  }).run();
27056
27138
  opts.onReleaseSyncRequested(id, release);
27057
- const inserted = app.db.select().from(ccReleaseSyncs).where(eq27(ccReleaseSyncs.id, id)).get();
27139
+ const inserted = app.db.select().from(ccReleaseSyncs).where(eq28(ccReleaseSyncs.id, id)).get();
27058
27140
  return reply.status(201).send(mapSyncRow(inserted));
27059
27141
  });
27060
27142
  app.get("/backlinks/syncs/latest", async (_request, reply) => {
@@ -27102,7 +27184,7 @@ async function backlinksRoutes(app, opts) {
27102
27184
  throw validationError("Invalid release id");
27103
27185
  }
27104
27186
  const now = (/* @__PURE__ */ new Date()).toISOString();
27105
- const runId = crypto23.randomUUID();
27187
+ const runId = crypto24.randomUUID();
27106
27188
  app.db.insert(runs).values({
27107
27189
  id: runId,
27108
27190
  projectId: project.id,
@@ -27112,7 +27194,7 @@ async function backlinksRoutes(app, opts) {
27112
27194
  createdAt: now
27113
27195
  }).run();
27114
27196
  opts.onBacklinkExtractRequested(runId, project.id, release);
27115
- const run = app.db.select().from(runs).where(eq27(runs.id, runId)).get();
27197
+ const run = app.db.select().from(runs).where(eq28(runs.id, runId)).get();
27116
27198
  return reply.status(201).send(mapRunRow(run));
27117
27199
  });
27118
27200
  app.get(
@@ -27139,9 +27221,9 @@ async function backlinksRoutes(app, opts) {
27139
27221
  const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
27140
27222
  const excludeCrawlers = parseExcludeCrawlers(request.query.excludeCrawlers);
27141
27223
  const baseDomainCondition = and23(
27142
- eq27(backlinkDomains.projectId, project.id),
27143
- eq27(backlinkDomains.source, source),
27144
- eq27(backlinkDomains.release, targetRelease)
27224
+ eq28(backlinkDomains.projectId, project.id),
27225
+ eq28(backlinkDomains.source, source),
27226
+ eq28(backlinkDomains.release, targetRelease)
27145
27227
  );
27146
27228
  const domainCondition = excludeCrawlers ? and23(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
27147
27229
  const totalRow = app.db.select({ count: sql11`count(*)` }).from(backlinkDomains).where(domainCondition).get();
@@ -27167,7 +27249,7 @@ async function backlinksRoutes(app, opts) {
27167
27249
  async (request, reply) => {
27168
27250
  const project = resolveProject(app.db, request.params.name);
27169
27251
  const source = parseSourceParam(request.query.source);
27170
- const rows = app.db.select().from(backlinkSummaries).where(and23(eq27(backlinkSummaries.projectId, project.id), eq27(backlinkSummaries.source, source))).orderBy(asc4(backlinkSummaries.queriedAt)).all();
27252
+ const rows = app.db.select().from(backlinkSummaries).where(and23(eq28(backlinkSummaries.projectId, project.id), eq28(backlinkSummaries.source, source))).orderBy(asc4(backlinkSummaries.queriedAt)).all();
27171
27253
  const response = rows.map((r) => ({
27172
27254
  release: r.release,
27173
27255
  totalLinkingDomains: r.totalLinkingDomains,
@@ -27204,7 +27286,7 @@ async function backlinksRoutes(app, opts) {
27204
27286
  );
27205
27287
  }
27206
27288
  const now = (/* @__PURE__ */ new Date()).toISOString();
27207
- const runId = crypto23.randomUUID();
27289
+ const runId = crypto24.randomUUID();
27208
27290
  app.db.insert(runs).values({
27209
27291
  id: runId,
27210
27292
  projectId: project.id,
@@ -27214,19 +27296,19 @@ async function backlinksRoutes(app, opts) {
27214
27296
  createdAt: now
27215
27297
  }).run();
27216
27298
  opts.onBingBacklinkSyncRequested(runId, project.id);
27217
- const run = app.db.select().from(runs).where(eq27(runs.id, runId)).get();
27299
+ const run = app.db.select().from(runs).where(eq28(runs.id, runId)).get();
27218
27300
  return reply.status(201).send(mapRunRow(run));
27219
27301
  }
27220
27302
  );
27221
27303
  }
27222
27304
 
27223
27305
  // ../api-routes/src/traffic.ts
27224
- import crypto25 from "crypto";
27306
+ import crypto26 from "crypto";
27225
27307
  import { Agent as UndiciAgent } from "undici";
27226
- import { and as and24, desc as desc15, eq as eq28, gte as gte4, lte as lte3, sql as sql12 } from "drizzle-orm";
27308
+ import { and as and24, desc as desc15, eq as eq29, gte as gte4, lte as lte3, sql as sql12 } from "drizzle-orm";
27227
27309
 
27228
27310
  // ../integration-cloud-run/src/auth.ts
27229
- import crypto24 from "crypto";
27311
+ import crypto25 from "crypto";
27230
27312
  var GOOGLE_TOKEN_URL3 = "https://oauth2.googleapis.com/token";
27231
27313
  var CLOUD_LOGGING_READ_SCOPE = "https://www.googleapis.com/auth/logging.read";
27232
27314
  var TOKEN_REQUEST_TIMEOUT_MS = 3e4;
@@ -27257,7 +27339,7 @@ function createServiceAccountJwt2(clientEmail, privateKey, scope) {
27257
27339
  const headerB64 = encode(header);
27258
27340
  const payloadB64 = encode(payload);
27259
27341
  const signingInput = `${headerB64}.${payloadB64}`;
27260
- const sign = crypto24.createSign("RSA-SHA256");
27342
+ const sign = crypto25.createSign("RSA-SHA256");
27261
27343
  sign.update(signingInput);
27262
27344
  const signature = sign.sign(privateKey, "base64url");
27263
27345
  return `${signingInput}.${signature}`;
@@ -31090,8 +31172,8 @@ async function runBackfillTask(options) {
31090
31172
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
31091
31173
  try {
31092
31174
  app.db.transaction((tx) => {
31093
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq28(runs.id, runId)).run();
31094
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq28(trafficSources.id, sourceRow.id)).run();
31175
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq29(runs.id, runId)).run();
31176
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq29(trafficSources.id, sourceRow.id)).run();
31095
31177
  });
31096
31178
  } catch {
31097
31179
  }
@@ -31106,7 +31188,7 @@ async function runBackfillTask(options) {
31106
31188
  if (allEvents.length === 0) {
31107
31189
  const finishedAt2 = (/* @__PURE__ */ new Date()).toISOString();
31108
31190
  try {
31109
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq28(runs.id, runId)).run();
31191
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq29(runs.id, runId)).run();
31110
31192
  } catch {
31111
31193
  }
31112
31194
  return;
@@ -31129,28 +31211,28 @@ async function runBackfillTask(options) {
31129
31211
  app.db.transaction((tx) => {
31130
31212
  tx.delete(crawlerEventsHourly).where(
31131
31213
  and24(
31132
- eq28(crawlerEventsHourly.sourceId, sourceRow.id),
31214
+ eq29(crawlerEventsHourly.sourceId, sourceRow.id),
31133
31215
  gte4(crawlerEventsHourly.tsHour, windowStartIso),
31134
31216
  lte3(crawlerEventsHourly.tsHour, windowEndIso)
31135
31217
  )
31136
31218
  ).run();
31137
31219
  tx.delete(aiUserFetchEventsHourly).where(
31138
31220
  and24(
31139
- eq28(aiUserFetchEventsHourly.sourceId, sourceRow.id),
31221
+ eq29(aiUserFetchEventsHourly.sourceId, sourceRow.id),
31140
31222
  gte4(aiUserFetchEventsHourly.tsHour, windowStartIso),
31141
31223
  lte3(aiUserFetchEventsHourly.tsHour, windowEndIso)
31142
31224
  )
31143
31225
  ).run();
31144
31226
  tx.delete(aiReferralEventsHourly).where(
31145
31227
  and24(
31146
- eq28(aiReferralEventsHourly.sourceId, sourceRow.id),
31228
+ eq29(aiReferralEventsHourly.sourceId, sourceRow.id),
31147
31229
  gte4(aiReferralEventsHourly.tsHour, windowStartIso),
31148
31230
  lte3(aiReferralEventsHourly.tsHour, windowEndIso)
31149
31231
  )
31150
31232
  ).run();
31151
31233
  tx.delete(rawEventSamples).where(
31152
31234
  and24(
31153
- eq28(rawEventSamples.sourceId, sourceRow.id),
31235
+ eq29(rawEventSamples.sourceId, sourceRow.id),
31154
31236
  gte4(rawEventSamples.ts, windowStartIso),
31155
31237
  lte3(rawEventSamples.ts, windowEndIso)
31156
31238
  )
@@ -31217,7 +31299,7 @@ async function runBackfillTask(options) {
31217
31299
  }
31218
31300
  })();
31219
31301
  tx.insert(rawEventSamples).values({
31220
- id: crypto25.randomUUID(),
31302
+ id: crypto26.randomUUID(),
31221
31303
  projectId: project.id,
31222
31304
  sourceId: sourceRow.id,
31223
31305
  ts: sample.observedAt,
@@ -31241,8 +31323,8 @@ async function runBackfillTask(options) {
31241
31323
  lastError: null,
31242
31324
  lastEventIds: newRingBuffer,
31243
31325
  updatedAt: finishedAt
31244
- }).where(eq28(trafficSources.id, sourceRow.id)).run();
31245
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq28(runs.id, runId)).run();
31326
+ }).where(eq29(trafficSources.id, sourceRow.id)).run();
31327
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq29(runs.id, runId)).run();
31246
31328
  });
31247
31329
  } catch (e) {
31248
31330
  markFailed(`Backfill rollup write failed: ${e instanceof Error ? e.message : String(e)}`);
@@ -31324,7 +31406,7 @@ async function trafficRoutes(app, opts) {
31324
31406
  createdAt: existing?.createdAt ?? now,
31325
31407
  updatedAt: now
31326
31408
  });
31327
- 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);
31409
+ const activeSource = app.db.select().from(trafficSources).where(eq29(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes["cloud-run"] && row.status !== TrafficSourceStatuses.archived);
31328
31410
  const config = {
31329
31411
  gcpProjectId,
31330
31412
  serviceName: serviceName ?? null,
@@ -31340,10 +31422,10 @@ async function trafficRoutes(app, opts) {
31340
31422
  lastError: null,
31341
31423
  configJson: config,
31342
31424
  updatedAt: now
31343
- }).where(eq28(trafficSources.id, activeSource.id)).run();
31344
- sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, activeSource.id)).get();
31425
+ }).where(eq29(trafficSources.id, activeSource.id)).run();
31426
+ sourceRow = app.db.select().from(trafficSources).where(eq29(trafficSources.id, activeSource.id)).get();
31345
31427
  } else {
31346
- const newId = crypto25.randomUUID();
31428
+ const newId = crypto26.randomUUID();
31347
31429
  app.db.insert(trafficSources).values({
31348
31430
  id: newId,
31349
31431
  projectId: project.id,
@@ -31358,7 +31440,7 @@ async function trafficRoutes(app, opts) {
31358
31440
  createdAt: now,
31359
31441
  updatedAt: now
31360
31442
  }).run();
31361
- sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, newId)).get();
31443
+ sourceRow = app.db.select().from(trafficSources).where(eq29(trafficSources.id, newId)).get();
31362
31444
  }
31363
31445
  writeAuditLog(app.db, {
31364
31446
  projectId: project.id,
@@ -31410,7 +31492,7 @@ async function trafficRoutes(app, opts) {
31410
31492
  createdAt: existing?.createdAt ?? now,
31411
31493
  updatedAt: now
31412
31494
  });
31413
- const activeSource = app.db.select().from(trafficSources).where(eq28(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
31495
+ const activeSource = app.db.select().from(trafficSources).where(eq29(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
31414
31496
  const config = { baseUrl, username };
31415
31497
  const fallbackName = displayName ?? `WordPress \xB7 ${new URL(baseUrl).host}`;
31416
31498
  let sourceRow;
@@ -31421,10 +31503,10 @@ async function trafficRoutes(app, opts) {
31421
31503
  lastError: null,
31422
31504
  configJson: config,
31423
31505
  updatedAt: now
31424
- }).where(eq28(trafficSources.id, activeSource.id)).run();
31425
- sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, activeSource.id)).get();
31506
+ }).where(eq29(trafficSources.id, activeSource.id)).run();
31507
+ sourceRow = app.db.select().from(trafficSources).where(eq29(trafficSources.id, activeSource.id)).get();
31426
31508
  } else {
31427
- const newId = crypto25.randomUUID();
31509
+ const newId = crypto26.randomUUID();
31428
31510
  app.db.insert(trafficSources).values({
31429
31511
  id: newId,
31430
31512
  projectId: project.id,
@@ -31439,7 +31521,7 @@ async function trafficRoutes(app, opts) {
31439
31521
  createdAt: now,
31440
31522
  updatedAt: now
31441
31523
  }).run();
31442
- sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, newId)).get();
31524
+ sourceRow = app.db.select().from(trafficSources).where(eq29(trafficSources.id, newId)).get();
31443
31525
  }
31444
31526
  writeAuditLog(app.db, {
31445
31527
  projectId: project.id,
@@ -31493,7 +31575,7 @@ async function trafficRoutes(app, opts) {
31493
31575
  createdAt: existing?.createdAt ?? now,
31494
31576
  updatedAt: now
31495
31577
  });
31496
- const activeSource = app.db.select().from(trafficSources).where(eq28(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.vercel && row.status !== TrafficSourceStatuses.archived);
31578
+ const activeSource = app.db.select().from(trafficSources).where(eq29(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.vercel && row.status !== TrafficSourceStatuses.archived);
31497
31579
  const config = { projectId, teamId, environment };
31498
31580
  const fallbackName = displayName ?? `Vercel \xB7 ${projectId}`;
31499
31581
  const { sourceRow, scheduleCreated } = app.db.transaction((tx) => {
@@ -31505,10 +31587,10 @@ async function trafficRoutes(app, opts) {
31505
31587
  lastError: null,
31506
31588
  configJson: config,
31507
31589
  updatedAt: now
31508
- }).where(eq28(trafficSources.id, activeSource.id)).run();
31509
- row = tx.select().from(trafficSources).where(eq28(trafficSources.id, activeSource.id)).get();
31590
+ }).where(eq29(trafficSources.id, activeSource.id)).run();
31591
+ row = tx.select().from(trafficSources).where(eq29(trafficSources.id, activeSource.id)).get();
31510
31592
  } else {
31511
- const newId = crypto25.randomUUID();
31593
+ const newId = crypto26.randomUUID();
31512
31594
  tx.insert(trafficSources).values({
31513
31595
  id: newId,
31514
31596
  projectId: project.id,
@@ -31531,18 +31613,18 @@ async function trafficRoutes(app, opts) {
31531
31613
  createdAt: now,
31532
31614
  updatedAt: now
31533
31615
  }).run();
31534
- row = tx.select().from(trafficSources).where(eq28(trafficSources.id, newId)).get();
31616
+ row = tx.select().from(trafficSources).where(eq29(trafficSources.id, newId)).get();
31535
31617
  }
31536
31618
  const existingSchedule = tx.select().from(schedules).where(
31537
31619
  and24(
31538
- eq28(schedules.projectId, project.id),
31539
- eq28(schedules.kind, SchedulableRunKinds["traffic-sync"])
31620
+ eq29(schedules.projectId, project.id),
31621
+ eq29(schedules.kind, SchedulableRunKinds["traffic-sync"])
31540
31622
  )
31541
31623
  ).get();
31542
31624
  let created = false;
31543
31625
  if (!existingSchedule) {
31544
31626
  tx.insert(schedules).values({
31545
- id: crypto25.randomUUID(),
31627
+ id: crypto26.randomUUID(),
31546
31628
  projectId: project.id,
31547
31629
  kind: SchedulableRunKinds["traffic-sync"],
31548
31630
  cronExpr: DEFAULT_TRAFFIC_SYNC_CRON,
@@ -31585,7 +31667,7 @@ async function trafficRoutes(app, opts) {
31585
31667
  });
31586
31668
  app.post("/projects/:name/traffic/sources/:id/sync", async (request) => {
31587
31669
  const project = resolveProject(app.db, request.params.name);
31588
- const sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, request.params.id)).get();
31670
+ const sourceRow = app.db.select().from(trafficSources).where(eq29(trafficSources.id, request.params.id)).get();
31589
31671
  if (!sourceRow || sourceRow.projectId !== project.id) {
31590
31672
  throw notFound("Traffic source", request.params.id);
31591
31673
  }
@@ -31597,7 +31679,7 @@ async function trafficRoutes(app, opts) {
31597
31679
  const windowEnd = /* @__PURE__ */ new Date();
31598
31680
  const startedAt = windowEnd.toISOString();
31599
31681
  const syncStartedAtMs = windowEnd.getTime();
31600
- const runId = crypto25.randomUUID();
31682
+ const runId = crypto26.randomUUID();
31601
31683
  app.db.insert(runs).values({
31602
31684
  id: runId,
31603
31685
  projectId: project.id,
@@ -31611,8 +31693,8 @@ async function trafficRoutes(app, opts) {
31611
31693
  const markFailed = (msg, errorCode) => {
31612
31694
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
31613
31695
  app.db.transaction((tx) => {
31614
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq28(runs.id, runId)).run();
31615
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq28(trafficSources.id, sourceRow.id)).run();
31696
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq29(runs.id, runId)).run();
31697
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq29(trafficSources.id, sourceRow.id)).run();
31616
31698
  });
31617
31699
  try {
31618
31700
  opts.onTrafficSynced?.({
@@ -31693,7 +31775,7 @@ async function trafficRoutes(app, opts) {
31693
31775
  }
31694
31776
  const credential = credentialStore.getConnection(project.name);
31695
31777
  if (!credential) {
31696
- app.db.delete(runs).where(eq28(runs.id, runId)).run();
31778
+ app.db.delete(runs).where(eq29(runs.id, runId)).run();
31697
31779
  throw validationError(
31698
31780
  `No WordPress credential found for project "${project.name}". Run "canonry traffic connect wordpress" first.`
31699
31781
  );
@@ -31742,12 +31824,12 @@ async function trafficRoutes(app, opts) {
31742
31824
  auditAction = "traffic.vercel.synced";
31743
31825
  const credentialStore = opts.vercelTrafficCredentialStore;
31744
31826
  if (!credentialStore) {
31745
- app.db.delete(runs).where(eq28(runs.id, runId)).run();
31827
+ app.db.delete(runs).where(eq29(runs.id, runId)).run();
31746
31828
  throw validationError("Vercel traffic credential storage is not configured for this deployment");
31747
31829
  }
31748
31830
  const credential = credentialStore.getConnection(project.name);
31749
31831
  if (!credential) {
31750
- app.db.delete(runs).where(eq28(runs.id, runId)).run();
31832
+ app.db.delete(runs).where(eq29(runs.id, runId)).run();
31751
31833
  throw validationError(
31752
31834
  `No Vercel credential found for project "${project.name}". Run "canonry traffic connect vercel" first.`
31753
31835
  );
@@ -31847,7 +31929,7 @@ async function trafficRoutes(app, opts) {
31847
31929
  let aiReferralHitsCount = 0;
31848
31930
  let unknownHitsCount = 0;
31849
31931
  app.db.transaction((tx) => {
31850
- const latestRow = tx.select().from(trafficSources).where(eq28(trafficSources.id, sourceRow.id)).get();
31932
+ const latestRow = tx.select().from(trafficSources).where(eq29(trafficSources.id, sourceRow.id)).get();
31851
31933
  const previousIds = latestRow.lastEventIds ?? [];
31852
31934
  const seenEventIds = new Set(previousIds);
31853
31935
  const dedupedEvents = seenEventIds.size === 0 ? allEvents : allEvents.filter((e) => !seenEventIds.has(e.eventId));
@@ -31987,7 +32069,7 @@ async function trafficRoutes(app, opts) {
31987
32069
  }
31988
32070
  })();
31989
32071
  tx.insert(rawEventSamples).values({
31990
- id: crypto25.randomUUID(),
32072
+ id: crypto26.randomUUID(),
31991
32073
  projectId: project.id,
31992
32074
  sourceId: sourceRow.id,
31993
32075
  ts: sample.observedAt,
@@ -32022,8 +32104,8 @@ async function trafficRoutes(app, opts) {
32022
32104
  if (sourceRow.sourceType === TrafficSourceTypes.wordpress) {
32023
32105
  sourceUpdate.lastCursor = nextCursor ?? null;
32024
32106
  }
32025
- tx.update(trafficSources).set(sourceUpdate).where(eq28(trafficSources.id, sourceRow.id)).run();
32026
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq28(runs.id, runId)).run();
32107
+ tx.update(trafficSources).set(sourceUpdate).where(eq29(trafficSources.id, sourceRow.id)).run();
32108
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq29(runs.id, runId)).run();
32027
32109
  });
32028
32110
  writeAuditLog(app.db, {
32029
32111
  projectId: project.id,
@@ -32075,7 +32157,7 @@ async function trafficRoutes(app, opts) {
32075
32157
  });
32076
32158
  app.post("/projects/:name/traffic/sources/:id/backfill", async (request) => {
32077
32159
  const project = resolveProject(app.db, request.params.name);
32078
- const sourceRow = app.db.select().from(trafficSources).where(eq28(trafficSources.id, request.params.id)).get();
32160
+ const sourceRow = app.db.select().from(trafficSources).where(eq29(trafficSources.id, request.params.id)).get();
32079
32161
  if (!sourceRow || sourceRow.projectId !== project.id) {
32080
32162
  throw notFound("Traffic source", request.params.id);
32081
32163
  }
@@ -32225,7 +32307,7 @@ async function trafficRoutes(app, opts) {
32225
32307
  };
32226
32308
  }
32227
32309
  const startedAt = windowEnd.toISOString();
32228
- const runId = crypto25.randomUUID();
32310
+ const runId = crypto26.randomUUID();
32229
32311
  app.db.insert(runs).values({
32230
32312
  id: runId,
32231
32313
  projectId: project.id,
@@ -32264,7 +32346,7 @@ async function trafficRoutes(app, opts) {
32264
32346
  hits: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
32265
32347
  }).from(crawlerEventsHourly).where(
32266
32348
  and24(
32267
- eq28(crawlerEventsHourly.sourceId, row.id),
32349
+ eq29(crawlerEventsHourly.sourceId, row.id),
32268
32350
  gte4(crawlerEventsHourly.tsHour, since)
32269
32351
  )
32270
32352
  ).groupBy(crawlerEventsHourly.pathNormalized).all();
@@ -32274,27 +32356,27 @@ async function trafficRoutes(app, opts) {
32274
32356
  const crawlerTotal = crawlerSegments.content + crawlerSegments.sitemap + crawlerSegments.robots + crawlerSegments.asset + crawlerSegments.other;
32275
32357
  const aiUserFetchTotals = app.db.select({ total: sql12`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
32276
32358
  and24(
32277
- eq28(aiUserFetchEventsHourly.sourceId, row.id),
32359
+ eq29(aiUserFetchEventsHourly.sourceId, row.id),
32278
32360
  gte4(aiUserFetchEventsHourly.tsHour, since)
32279
32361
  )
32280
32362
  ).get();
32281
32363
  const aiTotals = app.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
32282
32364
  and24(
32283
- eq28(aiReferralEventsHourly.sourceId, row.id),
32365
+ eq29(aiReferralEventsHourly.sourceId, row.id),
32284
32366
  gte4(aiReferralEventsHourly.tsHour, since)
32285
32367
  )
32286
32368
  ).get();
32287
32369
  const sampleTotals = app.db.select({ total: sql12`COUNT(*)` }).from(rawEventSamples).where(
32288
32370
  and24(
32289
- eq28(rawEventSamples.sourceId, row.id),
32371
+ eq29(rawEventSamples.sourceId, row.id),
32290
32372
  gte4(rawEventSamples.ts, since)
32291
32373
  )
32292
32374
  ).get();
32293
32375
  const latestRun = app.db.select().from(runs).where(
32294
32376
  and24(
32295
- eq28(runs.projectId, projectId),
32296
- eq28(runs.kind, RunKinds["traffic-sync"]),
32297
- eq28(runs.sourceId, row.id)
32377
+ eq29(runs.projectId, projectId),
32378
+ eq29(runs.kind, RunKinds["traffic-sync"]),
32379
+ eq29(runs.sourceId, row.id)
32298
32380
  )
32299
32381
  ).orderBy(desc15(runs.startedAt)).limit(1).get();
32300
32382
  return {
@@ -32325,7 +32407,7 @@ async function trafficRoutes(app, opts) {
32325
32407
  "`advanceToNow` must be `true`. There is no implicit reset."
32326
32408
  );
32327
32409
  }
32328
- const sourceRow = app.db.select().from(trafficSources).where(and24(eq28(trafficSources.projectId, project.id), eq28(trafficSources.id, request.params.id))).get();
32410
+ const sourceRow = app.db.select().from(trafficSources).where(and24(eq29(trafficSources.projectId, project.id), eq29(trafficSources.id, request.params.id))).get();
32329
32411
  if (!sourceRow) {
32330
32412
  throw notFound("traffic source", request.params.id);
32331
32413
  }
@@ -32342,7 +32424,7 @@ async function trafficRoutes(app, opts) {
32342
32424
  status: TrafficSourceStatuses.connected,
32343
32425
  lastError: null,
32344
32426
  updatedAt: now
32345
- }).where(eq28(trafficSources.id, sourceRow.id)).run();
32427
+ }).where(eq29(trafficSources.id, sourceRow.id)).run();
32346
32428
  writeAuditLog(tx, auditFromRequest(request, {
32347
32429
  projectId: project.id,
32348
32430
  actor: "api",
@@ -32350,20 +32432,20 @@ async function trafficRoutes(app, opts) {
32350
32432
  entityType: "traffic_source",
32351
32433
  entityId: sourceRow.id
32352
32434
  }));
32353
- updatedRow = tx.select().from(trafficSources).where(eq28(trafficSources.id, sourceRow.id)).get();
32435
+ updatedRow = tx.select().from(trafficSources).where(eq29(trafficSources.id, sourceRow.id)).get();
32354
32436
  });
32355
32437
  return buildSourceDetail(project.id, updatedRow, new Date(Date.now() - 24 * 60 * 6e4).toISOString());
32356
32438
  });
32357
32439
  app.get("/projects/:name/traffic/sources", async (request) => {
32358
32440
  const project = resolveProject(app.db, request.params.name);
32359
- const rows = app.db.select().from(trafficSources).where(eq28(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
32441
+ const rows = app.db.select().from(trafficSources).where(eq29(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
32360
32442
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map(rowToDto);
32361
32443
  const response = { sources };
32362
32444
  return response;
32363
32445
  });
32364
32446
  app.get("/projects/:name/traffic/status", async (request) => {
32365
32447
  const project = resolveProject(app.db, request.params.name);
32366
- const rows = app.db.select().from(trafficSources).where(eq28(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
32448
+ const rows = app.db.select().from(trafficSources).where(eq29(trafficSources.projectId, project.id)).orderBy(desc15(trafficSources.createdAt)).all();
32367
32449
  const since = new Date(Date.now() - 24 * 60 * 6e4).toISOString();
32368
32450
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map((row) => buildSourceDetail(project.id, row, since));
32369
32451
  const response = { sources };
@@ -32373,7 +32455,7 @@ async function trafficRoutes(app, opts) {
32373
32455
  "/projects/:name/traffic/sources/:id",
32374
32456
  async (request) => {
32375
32457
  const project = resolveProject(app.db, request.params.name);
32376
- const row = app.db.select().from(trafficSources).where(eq28(trafficSources.id, request.params.id)).get();
32458
+ const row = app.db.select().from(trafficSources).where(eq29(trafficSources.id, request.params.id)).get();
32377
32459
  if (!row || row.projectId !== project.id) {
32378
32460
  throw notFound("Traffic source", request.params.id);
32379
32461
  }
@@ -32425,11 +32507,11 @@ async function trafficRoutes(app, opts) {
32425
32507
  let aiReferralCounts = aiReferralClassCounts(0, 0, 0);
32426
32508
  if (kind === "all" || kind === TrafficEventKinds.crawler) {
32427
32509
  const crawlerFilters = [
32428
- eq28(crawlerEventsHourly.projectId, project.id),
32510
+ eq29(crawlerEventsHourly.projectId, project.id),
32429
32511
  gte4(crawlerEventsHourly.tsHour, sinceIso),
32430
32512
  lte3(crawlerEventsHourly.tsHour, untilIso)
32431
32513
  ];
32432
- if (sourceIdParam) crawlerFilters.push(eq28(crawlerEventsHourly.sourceId, sourceIdParam));
32514
+ if (sourceIdParam) crawlerFilters.push(eq29(crawlerEventsHourly.sourceId, sourceIdParam));
32433
32515
  const crawlerWhere = and24(...crawlerFilters);
32434
32516
  const pathTotals = app.db.select({
32435
32517
  pathNormalized: crawlerEventsHourly.pathNormalized,
@@ -32457,11 +32539,11 @@ async function trafficRoutes(app, opts) {
32457
32539
  }
32458
32540
  if (kind === "all" || kind === TrafficEventKinds["ai-user-fetch"]) {
32459
32541
  const userFetchFilters = [
32460
- eq28(aiUserFetchEventsHourly.projectId, project.id),
32542
+ eq29(aiUserFetchEventsHourly.projectId, project.id),
32461
32543
  gte4(aiUserFetchEventsHourly.tsHour, sinceIso),
32462
32544
  lte3(aiUserFetchEventsHourly.tsHour, untilIso)
32463
32545
  ];
32464
- if (sourceIdParam) userFetchFilters.push(eq28(aiUserFetchEventsHourly.sourceId, sourceIdParam));
32546
+ if (sourceIdParam) userFetchFilters.push(eq29(aiUserFetchEventsHourly.sourceId, sourceIdParam));
32465
32547
  const userFetchWhere = and24(...userFetchFilters);
32466
32548
  const total = app.db.select({ total: sql12`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(userFetchWhere).get();
32467
32549
  aiUserFetchTotal = Number(total?.total ?? 0);
@@ -32482,11 +32564,11 @@ async function trafficRoutes(app, opts) {
32482
32564
  }
32483
32565
  if (kind === "all" || kind === TrafficEventKinds["ai-referral"]) {
32484
32566
  const aiFilters = [
32485
- eq28(aiReferralEventsHourly.projectId, project.id),
32567
+ eq29(aiReferralEventsHourly.projectId, project.id),
32486
32568
  gte4(aiReferralEventsHourly.tsHour, sinceIso),
32487
32569
  lte3(aiReferralEventsHourly.tsHour, untilIso)
32488
32570
  ];
32489
- if (sourceIdParam) aiFilters.push(eq28(aiReferralEventsHourly.sourceId, sourceIdParam));
32571
+ if (sourceIdParam) aiFilters.push(eq29(aiReferralEventsHourly.sourceId, sourceIdParam));
32490
32572
  const aiWhere = and24(...aiFilters);
32491
32573
  const total = app.db.select({
32492
32574
  total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
@@ -32541,7 +32623,7 @@ async function trafficRoutes(app, opts) {
32541
32623
  }
32542
32624
 
32543
32625
  // ../api-routes/src/doctor/checks/agent.ts
32544
- import crypto26 from "crypto";
32626
+ import crypto27 from "crypto";
32545
32627
  import fs6 from "fs";
32546
32628
  import path7 from "path";
32547
32629
  var REQUIRED_SKILLS = ["canonry", "aero"];
@@ -32694,7 +32776,7 @@ function isInstalled(dir) {
32694
32776
  }
32695
32777
  function hashInstalledFile(filePath) {
32696
32778
  try {
32697
- return crypto26.createHash("sha256").update(fs6.readFileSync(filePath)).digest("hex");
32779
+ return crypto27.createHash("sha256").update(fs6.readFileSync(filePath)).digest("hex");
32698
32780
  } catch {
32699
32781
  return void 0;
32700
32782
  }
@@ -32709,7 +32791,7 @@ function readInstalledManifest(skillDir) {
32709
32791
  var AGENT_CHECKS = [skillsInstalledCheck, skillsCurrentCheck];
32710
32792
 
32711
32793
  // ../api-routes/src/doctor/checks/backlinks.ts
32712
- import { and as and25, eq as eq29 } from "drizzle-orm";
32794
+ import { and as and25, eq as eq30 } from "drizzle-orm";
32713
32795
  function skippedNoProject() {
32714
32796
  return {
32715
32797
  status: CheckStatuses.skipped,
@@ -32725,8 +32807,8 @@ var BACKLINKS_CHECKS = [
32725
32807
  title: "Backlinks source connected",
32726
32808
  run: (ctx) => {
32727
32809
  if (!ctx.project) return skippedNoProject();
32728
- const projectRow = ctx.db.select({ autoExtract: projects.autoExtractBacklinks }).from(projects).where(eq29(projects.id, ctx.project.id)).get();
32729
- const readySync = ctx.db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq29(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
32810
+ const projectRow = ctx.db.select({ autoExtract: projects.autoExtractBacklinks }).from(projects).where(eq30(projects.id, ctx.project.id)).get();
32811
+ const readySync = ctx.db.select({ id: ccReleaseSyncs.id }).from(ccReleaseSyncs).where(eq30(ccReleaseSyncs.status, CcReleaseSyncStatuses.ready)).limit(1).get();
32730
32812
  const ccConnected = projectRow?.autoExtract === true && !!readySync;
32731
32813
  const bingConnected = !!ctx.bingConnectionStore?.getConnection(ctx.project.canonicalDomain);
32732
32814
  const connected = [];
@@ -32742,8 +32824,8 @@ var BACKLINKS_CHECKS = [
32742
32824
  };
32743
32825
  }
32744
32826
  const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and25(
32745
- eq29(backlinkSummaries.projectId, ctx.project.id),
32746
- eq29(backlinkSummaries.source, BacklinkSources.commoncrawl)
32827
+ eq30(backlinkSummaries.projectId, ctx.project.id),
32828
+ eq30(backlinkSummaries.source, BacklinkSources.commoncrawl)
32747
32829
  )).limit(1).get() : false;
32748
32830
  return {
32749
32831
  status: CheckStatuses.ok,
@@ -32903,7 +32985,7 @@ var BING_AUTH_CHECKS = [
32903
32985
  ];
32904
32986
 
32905
32987
  // ../api-routes/src/doctor/checks/content.ts
32906
- import { eq as eq30 } from "drizzle-orm";
32988
+ import { eq as eq31 } from "drizzle-orm";
32907
32989
  var WINNABILITY_COVERAGE_WARN_THRESHOLD = 0.8;
32908
32990
  var UNCLASSIFIED_DOMAIN_SAMPLE_LIMIT = 10;
32909
32991
  function skippedNoProject2() {
@@ -32916,7 +32998,7 @@ function skippedNoProject2() {
32916
32998
  }
32917
32999
  function loadProject(ctx) {
32918
33000
  if (!ctx.project) return null;
32919
- return ctx.db.select().from(projects).where(eq30(projects.id, ctx.project.id)).get() ?? null;
33001
+ return ctx.db.select().from(projects).where(eq31(projects.id, ctx.project.id)).get() ?? null;
32920
33002
  }
32921
33003
  function percent(value) {
32922
33004
  return Math.round(value * 100);
@@ -33008,7 +33090,7 @@ var CONTENT_CHECK_BY_ID = Object.fromEntries(
33008
33090
  );
33009
33091
 
33010
33092
  // ../api-routes/src/doctor/checks/ads.ts
33011
- import { eq as eq31 } from "drizzle-orm";
33093
+ import { eq as eq32 } from "drizzle-orm";
33012
33094
  var RECENT_SYNC_WARN_DAYS = 7;
33013
33095
  var RECENT_SYNC_FAIL_DAYS = 30;
33014
33096
  var adsConnectionCheck = {
@@ -33025,7 +33107,7 @@ var adsConnectionCheck = {
33025
33107
  remediation: null
33026
33108
  };
33027
33109
  }
33028
- const row = ctx.db.select().from(adsConnections).where(eq31(adsConnections.projectId, ctx.project.id)).get();
33110
+ const row = ctx.db.select().from(adsConnections).where(eq32(adsConnections.projectId, ctx.project.id)).get();
33029
33111
  if (!row) {
33030
33112
  return {
33031
33113
  status: CheckStatuses.skipped,
@@ -33075,7 +33157,7 @@ var adsRecentSyncCheck = {
33075
33157
  remediation: null
33076
33158
  };
33077
33159
  }
33078
- const row = ctx.db.select().from(adsConnections).where(eq31(adsConnections.projectId, ctx.project.id)).get();
33160
+ const row = ctx.db.select().from(adsConnections).where(eq32(adsConnections.projectId, ctx.project.id)).get();
33079
33161
  if (!row) {
33080
33162
  return {
33081
33163
  status: CheckStatuses.skipped,
@@ -33266,7 +33348,7 @@ var ga4ConnectionCheck = {
33266
33348
  var GA_AUTH_CHECKS = [ga4ConnectionCheck];
33267
33349
 
33268
33350
  // ../api-routes/src/doctor/checks/gbp-auth.ts
33269
- import { and as and26, eq as eq32 } from "drizzle-orm";
33351
+ import { and as and26, eq as eq33 } from "drizzle-orm";
33270
33352
  var RECENT_SYNC_WARN_DAYS2 = 7;
33271
33353
  var RECENT_SYNC_FAIL_DAYS2 = 30;
33272
33354
  function skippedNoProject3() {
@@ -33499,7 +33581,7 @@ var recentSyncCheck = {
33499
33581
  title: "GBP recent sync",
33500
33582
  run: (ctx) => {
33501
33583
  if (!ctx.project) return skippedNoProject3();
33502
- const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and26(eq32(gbpLocations.projectId, ctx.project.id), eq32(gbpLocations.selected, true))).all();
33584
+ const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and26(eq33(gbpLocations.projectId, ctx.project.id), eq33(gbpLocations.selected, true))).all();
33503
33585
  if (selected.length === 0) {
33504
33586
  return {
33505
33587
  status: CheckStatuses.skipped,
@@ -33559,7 +33641,7 @@ var GBP_AUTH_CHECK_BY_ID = Object.fromEntries(
33559
33641
  );
33560
33642
 
33561
33643
  // ../api-routes/src/doctor/checks/places.ts
33562
- import { eq as eq33 } from "drizzle-orm";
33644
+ import { eq as eq34 } from "drizzle-orm";
33563
33645
  var apiKeyCheck = {
33564
33646
  id: "gbp.places.api-key",
33565
33647
  category: CheckCategories.auth,
@@ -33604,7 +33686,7 @@ var apiKeyCheck = {
33604
33686
  details: { tier: cfg.tier }
33605
33687
  };
33606
33688
  }
33607
- const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq33(gbpLocations.projectId, ctx.project.id)).all();
33689
+ const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq34(gbpLocations.projectId, ctx.project.id)).all();
33608
33690
  const selected = rows.filter((r) => r.selected);
33609
33691
  const locationsWithPlaceId = selected.filter((r) => Boolean(r.placeId)).length;
33610
33692
  const details = {
@@ -34101,7 +34183,7 @@ var RUNTIME_STATE_CHECKS = [
34101
34183
  ];
34102
34184
 
34103
34185
  // ../api-routes/src/doctor/checks/traffic-source.ts
34104
- import { and as and27, eq as eq34, gte as gte5, ne as ne4, sql as sql13 } from "drizzle-orm";
34186
+ import { and as and27, eq as eq35, gte as gte5, ne as ne4, sql as sql13 } from "drizzle-orm";
34105
34187
  var RECENT_DATA_WARN_DAYS = 7;
34106
34188
  var RECENT_DATA_FAIL_DAYS = 30;
34107
34189
  function skippedNoProject5() {
@@ -34116,7 +34198,7 @@ function loadProbes(ctx) {
34116
34198
  if (!ctx.project) return [];
34117
34199
  const rows = ctx.db.select().from(trafficSources).where(
34118
34200
  and27(
34119
- eq34(trafficSources.projectId, ctx.project.id),
34201
+ eq35(trafficSources.projectId, ctx.project.id),
34120
34202
  ne4(trafficSources.status, TrafficSourceStatuses.archived)
34121
34203
  )
34122
34204
  ).all();
@@ -34197,7 +34279,7 @@ var recentDataCheck = {
34197
34279
  const recentCrawlers = Number(
34198
34280
  ctx.db.select({ total: sql13`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
34199
34281
  and27(
34200
- eq34(crawlerEventsHourly.projectId, ctx.project.id),
34282
+ eq35(crawlerEventsHourly.projectId, ctx.project.id),
34201
34283
  gte5(crawlerEventsHourly.tsHour, warnCutoff)
34202
34284
  )
34203
34285
  ).get()?.total ?? 0
@@ -34205,7 +34287,7 @@ var recentDataCheck = {
34205
34287
  const recentReferrals = Number(
34206
34288
  ctx.db.select({ total: sql13`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
34207
34289
  and27(
34208
- eq34(aiReferralEventsHourly.projectId, ctx.project.id),
34290
+ eq35(aiReferralEventsHourly.projectId, ctx.project.id),
34209
34291
  gte5(aiReferralEventsHourly.tsHour, warnCutoff)
34210
34292
  )
34211
34293
  ).get()?.total ?? 0
@@ -34221,7 +34303,7 @@ var recentDataCheck = {
34221
34303
  const olderCrawlers = Number(
34222
34304
  ctx.db.select({ total: sql13`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
34223
34305
  and27(
34224
- eq34(crawlerEventsHourly.projectId, ctx.project.id),
34306
+ eq35(crawlerEventsHourly.projectId, ctx.project.id),
34225
34307
  gte5(crawlerEventsHourly.tsHour, failCutoff)
34226
34308
  )
34227
34309
  ).get()?.total ?? 0
@@ -34229,7 +34311,7 @@ var recentDataCheck = {
34229
34311
  const olderReferrals = Number(
34230
34312
  ctx.db.select({ total: sql13`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
34231
34313
  and27(
34232
- eq34(aiReferralEventsHourly.projectId, ctx.project.id),
34314
+ eq35(aiReferralEventsHourly.projectId, ctx.project.id),
34233
34315
  gte5(aiReferralEventsHourly.tsHour, failCutoff)
34234
34316
  )
34235
34317
  ).get()?.total ?? 0
@@ -34644,8 +34726,8 @@ async function doctorRoutes(app, opts) {
34644
34726
  }
34645
34727
 
34646
34728
  // ../api-routes/src/discovery/routes.ts
34647
- import crypto27 from "crypto";
34648
- import { and as and28, desc as desc16, eq as eq35, gte as gte6, inArray as inArray12, isNull, or as or5 } from "drizzle-orm";
34729
+ import crypto28 from "crypto";
34730
+ import { and as and28, desc as desc16, eq as eq36, gte as gte6, inArray as inArray13, isNull, or as or5 } from "drizzle-orm";
34649
34731
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
34650
34732
  async function discoveryRoutes(app, opts) {
34651
34733
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -34680,12 +34762,12 @@ async function discoveryRoutes(app, opts) {
34680
34762
  const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
34681
34763
  const decision = app.db.transaction((tx) => {
34682
34764
  const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and28(
34683
- eq35(discoverySessions.projectId, project.id),
34684
- eq35(discoverySessions.icpDescription, icpDescription),
34765
+ eq36(discoverySessions.projectId, project.id),
34766
+ eq36(discoverySessions.icpDescription, icpDescription),
34685
34767
  // Buyer is part of session identity: it changes the seed prompt's
34686
34768
  // semantics, so a request with a different (or no) buyer must start
34687
34769
  // its own session, never adopt another buyer's probes.
34688
- parsed.data.buyerDescription == null ? isNull(discoverySessions.buyerDescription) : eq35(discoverySessions.buyerDescription, parsed.data.buyerDescription),
34770
+ parsed.data.buyerDescription == null ? isNull(discoverySessions.buyerDescription) : eq36(discoverySessions.buyerDescription, parsed.data.buyerDescription),
34689
34771
  // Locations are identity too: a different service-area subset seeds
34690
34772
  // and probes a different geo, so it must never reuse another geo's
34691
34773
  // session. resolveLocations is deterministic (project-config order),
@@ -34695,12 +34777,12 @@ async function discoveryRoutes(app, opts) {
34695
34777
  // locations a NULL row's subset is unknowable, so it conservatively
34696
34778
  // never reuses — a one-time, bounded (2h window) non-reuse after
34697
34779
  // upgrade, never a wrong reuse.
34698
- locations.length === 0 ? or5(isNull(discoverySessions.locations), eq35(discoverySessions.locations, locations)) : eq35(discoverySessions.locations, locations),
34780
+ locations.length === 0 ? or5(isNull(discoverySessions.locations), eq36(discoverySessions.locations, locations)) : eq36(discoverySessions.locations, locations),
34699
34781
  // Seed provider set is identity: a different phrasing distribution
34700
34782
  // must never reuse another set's session. Null (= Gemini-only
34701
34783
  // default, including explicit ['gemini']) matches legacy rows.
34702
- seedProviders == null ? isNull(discoverySessions.seedProviders) : eq35(discoverySessions.seedProviders, seedProviders),
34703
- inArray12(discoverySessions.status, [
34784
+ seedProviders == null ? isNull(discoverySessions.seedProviders) : eq36(discoverySessions.seedProviders, seedProviders),
34785
+ inArray13(discoverySessions.status, [
34704
34786
  DiscoverySessionStatuses.queued,
34705
34787
  DiscoverySessionStatuses.seeding,
34706
34788
  DiscoverySessionStatuses.probing
@@ -34710,8 +34792,8 @@ async function discoveryRoutes(app, opts) {
34710
34792
  if (existing && existing.runId) {
34711
34793
  return { reused: true, sessionId: existing.id, runId: existing.runId };
34712
34794
  }
34713
- const sessionId = crypto27.randomUUID();
34714
- const runId = crypto27.randomUUID();
34795
+ const sessionId = crypto28.randomUUID();
34796
+ const runId = crypto28.randomUUID();
34715
34797
  tx.insert(discoverySessions).values({
34716
34798
  id: sessionId,
34717
34799
  projectId: project.id,
@@ -34775,7 +34857,7 @@ async function discoveryRoutes(app, opts) {
34775
34857
  const project = resolveProject(app.db, request.params.name);
34776
34858
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
34777
34859
  const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? 50 : parsedLimit;
34778
- const rows = app.db.select().from(discoverySessions).where(eq35(discoverySessions.projectId, project.id)).orderBy(desc16(discoverySessions.createdAt)).limit(limit).all();
34860
+ const rows = app.db.select().from(discoverySessions).where(eq36(discoverySessions.projectId, project.id)).orderBy(desc16(discoverySessions.createdAt)).limit(limit).all();
34779
34861
  return reply.send(rows.map(serializeSession));
34780
34862
  }
34781
34863
  );
@@ -34783,11 +34865,11 @@ async function discoveryRoutes(app, opts) {
34783
34865
  "/projects/:name/discover/sessions/:id",
34784
34866
  async (request, reply) => {
34785
34867
  const project = resolveProject(app.db, request.params.name);
34786
- const session = app.db.select().from(discoverySessions).where(eq35(discoverySessions.id, request.params.id)).get();
34868
+ const session = app.db.select().from(discoverySessions).where(eq36(discoverySessions.id, request.params.id)).get();
34787
34869
  if (!session || session.projectId !== project.id) {
34788
34870
  throw notFound("Discovery session", request.params.id);
34789
34871
  }
34790
- const probeRows = app.db.select().from(discoveryProbes).where(eq35(discoveryProbes.sessionId, session.id)).all();
34872
+ const probeRows = app.db.select().from(discoveryProbes).where(eq36(discoveryProbes.sessionId, session.id)).all();
34791
34873
  const detail = {
34792
34874
  ...serializeSession(session),
34793
34875
  probes: probeRows.map(serializeProbe)
@@ -34799,7 +34881,7 @@ async function discoveryRoutes(app, opts) {
34799
34881
  "/projects/:name/discover/sessions/:id/harvest",
34800
34882
  async (request, reply) => {
34801
34883
  const project = resolveProject(app.db, request.params.name);
34802
- const session = app.db.select().from(discoverySessions).where(eq35(discoverySessions.id, request.params.id)).get();
34884
+ const session = app.db.select().from(discoverySessions).where(eq36(discoverySessions.id, request.params.id)).get();
34803
34885
  if (!session || session.projectId !== project.id) {
34804
34886
  throw notFound("Discovery session", request.params.id);
34805
34887
  }
@@ -34807,7 +34889,7 @@ async function discoveryRoutes(app, opts) {
34807
34889
  const minProbeHits = Number.isNaN(parsedFloor) || parsedFloor < 1 ? 1 : parsedFloor;
34808
34890
  const applyAnchor = request.query.anchor !== "false";
34809
34891
  const provider = session.seedProvider ?? "gemini";
34810
- const probeRows = app.db.select().from(discoveryProbes).where(eq35(discoveryProbes.sessionId, session.id)).all();
34892
+ const probeRows = app.db.select().from(discoveryProbes).where(eq36(discoveryProbes.sessionId, session.id)).all();
34811
34893
  const extract = opts.harvestSearchQueries;
34812
34894
  const probesWithQueries = probeRows.map((row) => {
34813
34895
  if (!extract || !row.rawResponse) return { searchQueries: [] };
@@ -34818,7 +34900,7 @@ async function discoveryRoutes(app, opts) {
34818
34900
  return { searchQueries: [] };
34819
34901
  }
34820
34902
  });
34821
- const trackedQueries = app.db.select({ query: queries.query }).from(queries).where(eq35(queries.projectId, project.id)).all().map((r) => r.query);
34903
+ const trackedQueries = app.db.select({ query: queries.query }).from(queries).where(eq36(queries.projectId, project.id)).all().map((r) => r.query);
34822
34904
  const anchorTerms = buildHarvestAnchorTerms(
34823
34905
  [session.icpDescription ?? "", ...trackedQueries],
34824
34906
  effectiveDomains(project)
@@ -34865,12 +34947,12 @@ async function discoveryRoutes(app, opts) {
34865
34947
  "/projects/:name/discover/sessions/:id/promote",
34866
34948
  async (request, reply) => {
34867
34949
  const project = resolveProject(app.db, request.params.name);
34868
- const session = app.db.select().from(discoverySessions).where(eq35(discoverySessions.id, request.params.id)).get();
34950
+ const session = app.db.select().from(discoverySessions).where(eq36(discoverySessions.id, request.params.id)).get();
34869
34951
  if (!session || session.projectId !== project.id) {
34870
34952
  throw notFound("Discovery session", request.params.id);
34871
34953
  }
34872
- const probeRows = app.db.select().from(discoveryProbes).where(eq35(discoveryProbes.sessionId, session.id)).all();
34873
- const existingCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq35(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase());
34954
+ const probeRows = app.db.select().from(discoveryProbes).where(eq36(discoveryProbes.sessionId, session.id)).all();
34955
+ const existingCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq36(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase());
34874
34956
  const seenCompetitors = new Set(existingCompetitors);
34875
34957
  const cited = /* @__PURE__ */ new Set();
34876
34958
  const aspirational = /* @__PURE__ */ new Set();
@@ -34899,7 +34981,7 @@ async function discoveryRoutes(app, opts) {
34899
34981
  );
34900
34982
  app.post("/projects/:name/discover/sessions/:id/promote", async (request, reply) => {
34901
34983
  const project = resolveProject(app.db, request.params.name);
34902
- const session = app.db.select().from(discoverySessions).where(eq35(discoverySessions.id, request.params.id)).get();
34984
+ const session = app.db.select().from(discoverySessions).where(eq36(discoverySessions.id, request.params.id)).get();
34903
34985
  if (!session || session.projectId !== project.id) {
34904
34986
  throw notFound("Discovery session", request.params.id);
34905
34987
  }
@@ -34922,7 +35004,7 @@ async function discoveryRoutes(app, opts) {
34922
35004
  const bucketSet = new Set(buckets);
34923
35005
  const includeCompetitors = parsed.data.includeCompetitors ?? true;
34924
35006
  const competitorTypes = parsed.data.competitorTypes ?? DEFAULT_DISCOVERY_PROMOTE_COMPETITOR_TYPES;
34925
- const probeRows = app.db.select().from(discoveryProbes).where(eq35(discoveryProbes.sessionId, session.id)).all();
35007
+ const probeRows = app.db.select().from(discoveryProbes).where(eq36(discoveryProbes.sessionId, session.id)).all();
34926
35008
  const candidateQueries = /* @__PURE__ */ new Set();
34927
35009
  for (const probe of probeRows) {
34928
35010
  if (!probe.bucket) continue;
@@ -34930,7 +35012,7 @@ async function discoveryRoutes(app, opts) {
34930
35012
  if (bucket.success && bucketSet.has(bucket.data)) candidateQueries.add(probe.query);
34931
35013
  }
34932
35014
  const existingQueries = new Set(
34933
- app.db.select({ query: queries.query }).from(queries).where(eq35(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
35015
+ app.db.select({ query: queries.query }).from(queries).where(eq36(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
34934
35016
  );
34935
35017
  const promotedQueries = [];
34936
35018
  const skippedQueries = [];
@@ -34946,7 +35028,7 @@ async function discoveryRoutes(app, opts) {
34946
35028
  const skippedCompetitors = [];
34947
35029
  if (includeCompetitors) {
34948
35030
  const existingCompetitors = new Set(
34949
- app.db.select({ domain: competitors.domain }).from(competitors).where(eq35(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase())
35031
+ app.db.select({ domain: competitors.domain }).from(competitors).where(eq36(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase())
34950
35032
  );
34951
35033
  const competitorMap = parseCompetitorMap(session.competitorMap);
34952
35034
  for (const entry of selectEligibleCompetitors(competitorMap, competitorTypes)) {
@@ -34965,7 +35047,7 @@ async function discoveryRoutes(app, opts) {
34965
35047
  app.db.transaction((tx) => {
34966
35048
  for (const query of promotedQueries) {
34967
35049
  tx.insert(queries).values({
34968
- id: crypto27.randomUUID(),
35050
+ id: crypto28.randomUUID(),
34969
35051
  projectId: project.id,
34970
35052
  query,
34971
35053
  provenance,
@@ -34974,7 +35056,7 @@ async function discoveryRoutes(app, opts) {
34974
35056
  }
34975
35057
  for (const domain of promotedCompetitors) {
34976
35058
  tx.insert(competitors).values({
34977
- id: crypto27.randomUUID(),
35059
+ id: crypto28.randomUUID(),
34978
35060
  projectId: project.id,
34979
35061
  domain,
34980
35062
  provenance,
@@ -35064,8 +35146,8 @@ function selectEligibleCompetitors(competitorMap, competitorTypes) {
35064
35146
  }
35065
35147
 
35066
35148
  // ../api-routes/src/discovery/orchestrate.ts
35067
- import crypto28 from "crypto";
35068
- import { eq as eq36 } from "drizzle-orm";
35149
+ import crypto29 from "crypto";
35150
+ import { eq as eq37 } from "drizzle-orm";
35069
35151
  var DEFAULT_MAX_PROBES = 100;
35070
35152
  var ABSOLUTE_MAX_PROBES = 500;
35071
35153
  function classifyProbeBucket(input) {
@@ -35152,7 +35234,7 @@ async function executeDiscovery(opts) {
35152
35234
  status: DiscoverySessionStatuses.seeding,
35153
35235
  dedupThreshold,
35154
35236
  startedAt
35155
- }).where(eq36(discoverySessions.id, opts.sessionId)).run();
35237
+ }).where(eq37(discoverySessions.id, opts.sessionId)).run();
35156
35238
  const seedResult = await opts.deps.seed({
35157
35239
  project: opts.project,
35158
35240
  icpDescription: opts.icpDescription,
@@ -35213,7 +35295,7 @@ async function executeDiscovery(opts) {
35213
35295
  dedupBandPairFraction: dedupStats.bandPairFraction,
35214
35296
  dedupPairsTotal: dedupStats.pairsTotal,
35215
35297
  warning
35216
- }).where(eq36(discoverySessions.id, opts.sessionId)).run();
35298
+ }).where(eq37(discoverySessions.id, opts.sessionId)).run();
35217
35299
  const probeLocation = opts.locations?.[0];
35218
35300
  const probeResults = await mapWithConcurrency(
35219
35301
  probedCanonicals,
@@ -35232,7 +35314,7 @@ async function executeDiscovery(opts) {
35232
35314
  probeRows.push({ citedDomains: probe.citedDomains, bucket });
35233
35315
  buckets[bucket]++;
35234
35316
  return {
35235
- id: crypto28.randomUUID(),
35317
+ id: crypto29.randomUUID(),
35236
35318
  sessionId: opts.sessionId,
35237
35319
  projectId: opts.project.id,
35238
35320
  query: probedCanonicals[index2],
@@ -35267,7 +35349,7 @@ async function executeDiscovery(opts) {
35267
35349
  wastedCount: buckets["wasted-surface"],
35268
35350
  competitorMap,
35269
35351
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
35270
- }).where(eq36(discoverySessions.id, opts.sessionId)).run();
35352
+ }).where(eq37(discoverySessions.id, opts.sessionId)).run();
35271
35353
  upsertDomainClassifications(opts.db, opts.project.id, opts.sessionId, competitorMap);
35272
35354
  return {
35273
35355
  buckets,
@@ -35284,7 +35366,7 @@ function upsertDomainClassifications(db, projectId, sessionId, competitorMap) {
35284
35366
  const domain = normalizeDomain(entry.domain);
35285
35367
  if (!domain) continue;
35286
35368
  db.insert(domainClassifications).values({
35287
- id: crypto28.randomUUID(),
35369
+ id: crypto29.randomUUID(),
35288
35370
  projectId,
35289
35371
  domain,
35290
35372
  competitorType: entry.competitorType,
@@ -35307,7 +35389,7 @@ function markSessionFailed(db, sessionId, error) {
35307
35389
  status: DiscoverySessionStatuses.failed,
35308
35390
  error,
35309
35391
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
35310
- }).where(eq36(discoverySessions.id, sessionId)).run();
35392
+ }).where(eq37(discoverySessions.id, sessionId)).run();
35311
35393
  }
35312
35394
  function dedupeStrings(input) {
35313
35395
  const seen = /* @__PURE__ */ new Set();
@@ -35324,8 +35406,8 @@ function dedupeStrings(input) {
35324
35406
  }
35325
35407
 
35326
35408
  // ../api-routes/src/technical-aeo.ts
35327
- import crypto29 from "crypto";
35328
- import { and as and29, asc as asc5, count, desc as desc17, eq as eq37, inArray as inArray13 } from "drizzle-orm";
35409
+ import crypto30 from "crypto";
35410
+ import { and as and29, asc as asc5, count, desc as desc17, eq as eq38, inArray as inArray14 } from "drizzle-orm";
35329
35411
  var SURFACEABLE_STATUSES = [RunStatuses.completed, RunStatuses.partial];
35330
35412
  function emptyScore(projectName) {
35331
35413
  return {
@@ -35357,10 +35439,10 @@ function parsePositiveInt(value, fallback, max) {
35357
35439
  async function technicalAeoRoutes(app, opts) {
35358
35440
  app.get("/projects/:name/technical-aeo", async (request) => {
35359
35441
  const project = resolveProject(app.db, request.params.name);
35360
- const rows = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(and29(
35361
- eq37(siteAuditSnapshots.projectId, project.id),
35362
- eq37(runs.kind, RunKinds["site-audit"]),
35363
- inArray13(runs.status, SURFACEABLE_STATUSES),
35442
+ const rows = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq38(siteAuditSnapshots.runId, runs.id)).where(and29(
35443
+ eq38(siteAuditSnapshots.projectId, project.id),
35444
+ eq38(runs.kind, RunKinds["site-audit"]),
35445
+ inArray14(runs.status, SURFACEABLE_STATUSES),
35364
35446
  notProbeRun()
35365
35447
  )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(2).all();
35366
35448
  const latest = rows[0];
@@ -35392,18 +35474,18 @@ async function technicalAeoRoutes(app, opts) {
35392
35474
  });
35393
35475
  app.get("/projects/:name/technical-aeo/pages", async (request) => {
35394
35476
  const project = resolveProject(app.db, request.params.name);
35395
- const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(and29(
35396
- eq37(siteAuditSnapshots.projectId, project.id),
35397
- eq37(runs.kind, RunKinds["site-audit"]),
35398
- inArray13(runs.status, SURFACEABLE_STATUSES),
35477
+ const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq38(siteAuditSnapshots.runId, runs.id)).where(and29(
35478
+ eq38(siteAuditSnapshots.projectId, project.id),
35479
+ eq38(runs.kind, RunKinds["site-audit"]),
35480
+ inArray14(runs.status, SURFACEABLE_STATUSES),
35399
35481
  notProbeRun()
35400
35482
  )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(1).get();
35401
35483
  if (!latest) {
35402
35484
  return { project: project.name, runId: null, auditedAt: null, total: 0, pages: [] };
35403
35485
  }
35404
35486
  const statusFilter = request.query.status === "success" || request.query.status === "error" ? request.query.status : null;
35405
- const conds = [eq37(siteAuditPages.runId, latest.runId)];
35406
- if (statusFilter) conds.push(eq37(siteAuditPages.status, statusFilter));
35487
+ const conds = [eq38(siteAuditPages.runId, latest.runId)];
35488
+ if (statusFilter) conds.push(eq38(siteAuditPages.status, statusFilter));
35407
35489
  const where = and29(...conds);
35408
35490
  const totalRow = app.db.select({ value: count() }).from(siteAuditPages).where(where).get();
35409
35491
  const total = totalRow?.value ?? 0;
@@ -35428,10 +35510,10 @@ async function technicalAeoRoutes(app, opts) {
35428
35510
  auditedAt: siteAuditSnapshots.auditedAt,
35429
35511
  aggregateScore: siteAuditSnapshots.aggregateScore,
35430
35512
  pagesAudited: siteAuditSnapshots.pagesAudited
35431
- }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(and29(
35432
- eq37(siteAuditSnapshots.projectId, project.id),
35433
- eq37(runs.kind, RunKinds["site-audit"]),
35434
- inArray13(runs.status, SURFACEABLE_STATUSES),
35513
+ }).from(siteAuditSnapshots).innerJoin(runs, eq38(siteAuditSnapshots.runId, runs.id)).where(and29(
35514
+ eq38(siteAuditSnapshots.projectId, project.id),
35515
+ eq38(runs.kind, RunKinds["site-audit"]),
35516
+ inArray14(runs.status, SURFACEABLE_STATUSES),
35435
35517
  notProbeRun()
35436
35518
  )).orderBy(desc17(siteAuditSnapshots.createdAt)).limit(limit).all();
35437
35519
  return { project: project.name, points: rows.reverse() };
@@ -35443,15 +35525,15 @@ async function technicalAeoRoutes(app, opts) {
35443
35525
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid site-audit request");
35444
35526
  }
35445
35527
  const existing = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and29(
35446
- eq37(runs.projectId, project.id),
35447
- eq37(runs.kind, RunKinds["site-audit"]),
35448
- inArray13(runs.status, [RunStatuses.queued, RunStatuses.running])
35528
+ eq38(runs.projectId, project.id),
35529
+ eq38(runs.kind, RunKinds["site-audit"]),
35530
+ inArray14(runs.status, [RunStatuses.queued, RunStatuses.running])
35449
35531
  )).get();
35450
35532
  if (existing) {
35451
35533
  return { runId: existing.id, status: existing.status };
35452
35534
  }
35453
35535
  const now = (/* @__PURE__ */ new Date()).toISOString();
35454
- const runId = crypto29.randomUUID();
35536
+ const runId = crypto30.randomUUID();
35455
35537
  app.db.insert(runs).values({
35456
35538
  id: runId,
35457
35539
  projectId: project.id,
@@ -35804,7 +35886,7 @@ function buildTrafficSourceValidators(opts) {
35804
35886
  }
35805
35887
 
35806
35888
  // src/intelligence-service.ts
35807
- import crypto30 from "crypto";
35889
+ import crypto31 from "crypto";
35808
35890
 
35809
35891
  // src/logger.ts
35810
35892
  var IS_TTY = process.stdout.isTTY === true;
@@ -36037,8 +36119,8 @@ var IntelligenceService = class {
36037
36119
  analyzeAndPersist(runId, projectId) {
36038
36120
  const recentRuns = this.db.select().from(runs).where(
36039
36121
  and30(
36040
- eq38(runs.projectId, projectId),
36041
- or6(eq38(runs.status, "completed"), eq38(runs.status, "partial")),
36122
+ eq39(runs.projectId, projectId),
36123
+ or6(eq39(runs.status, "completed"), eq39(runs.status, "partial")),
36042
36124
  // Defensive: RunCoordinator already skips probes before this is
36043
36125
  // called, but if a future call site invokes analyzeAndPersist
36044
36126
  // directly for a probe, probes still must not pollute the
@@ -36120,7 +36202,7 @@ var IntelligenceService = class {
36120
36202
  * Returns the persisted insights so the coordinator can count critical/high.
36121
36203
  */
36122
36204
  analyzeAndPersistGbp(runId, projectId) {
36123
- const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq38(runs.id, runId)).get();
36205
+ const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq39(runs.id, runId)).get();
36124
36206
  if (!runRow) {
36125
36207
  log.info("gbp-intelligence.skip", { runId, reason: "run not found" });
36126
36208
  this.persistGbpInsights(runId, projectId, [], []);
@@ -36129,8 +36211,8 @@ var IntelligenceService = class {
36129
36211
  const windowStart = runRow.startedAt ?? runRow.createdAt;
36130
36212
  const windowEnd = runRow.finishedAt ?? (/* @__PURE__ */ new Date()).toISOString();
36131
36213
  const selected = this.db.select().from(gbpLocations).where(and30(
36132
- eq38(gbpLocations.projectId, projectId),
36133
- eq38(gbpLocations.selected, true),
36214
+ eq39(gbpLocations.projectId, projectId),
36215
+ eq39(gbpLocations.selected, true),
36134
36216
  gte7(gbpLocations.syncedAt, windowStart),
36135
36217
  lte4(gbpLocations.syncedAt, windowEnd)
36136
36218
  )).all();
@@ -36165,12 +36247,12 @@ var IntelligenceService = class {
36165
36247
  }
36166
36248
  /** Build the per-location signal bundle the GBP analyzer consumes. */
36167
36249
  buildGbpLocationSignals(projectId, locationName, displayName, fallbackDate) {
36168
- const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and30(eq38(gbpDailyMetrics.projectId, projectId), eq38(gbpDailyMetrics.locationName, locationName))).all();
36169
- const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and30(eq38(gbpPlaceActions.projectId, projectId), eq38(gbpPlaceActions.locationName, locationName))).all();
36170
- const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and30(eq38(gbpLodgingSnapshots.projectId, projectId), eq38(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc18(gbpLodgingSnapshots.syncedAt)).limit(1).get();
36171
- const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(and30(eq38(gbpLocations.projectId, projectId), eq38(gbpLocations.locationName, locationName))).get();
36250
+ const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and30(eq39(gbpDailyMetrics.projectId, projectId), eq39(gbpDailyMetrics.locationName, locationName))).all();
36251
+ const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and30(eq39(gbpPlaceActions.projectId, projectId), eq39(gbpPlaceActions.locationName, locationName))).all();
36252
+ const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and30(eq39(gbpLodgingSnapshots.projectId, projectId), eq39(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc18(gbpLodgingSnapshots.syncedAt)).limit(1).get();
36253
+ const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(and30(eq39(gbpLocations.projectId, projectId), eq39(gbpLocations.locationName, locationName))).get();
36172
36254
  const descriptionMissing = !(ownerRow?.description ?? "").trim();
36173
- const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and30(eq38(gbpPlaceDetails.projectId, projectId), eq38(gbpPlaceDetails.locationName, locationName))).orderBy(desc18(gbpPlaceDetails.syncedAt)).limit(1).get();
36255
+ const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and30(eq39(gbpPlaceDetails.projectId, projectId), eq39(gbpPlaceDetails.locationName, locationName))).orderBy(desc18(gbpPlaceDetails.syncedAt)).limit(1).get();
36174
36256
  const placesAmenities = placeRow ? extractPlaceAmenities(placeRow.attributes) : [];
36175
36257
  const summary = buildGbpSummary({
36176
36258
  locationName,
@@ -36206,7 +36288,7 @@ var IntelligenceService = class {
36206
36288
  /** Build the month-over-month keyword series for a location from the
36207
36289
  * accumulating gbp_keyword_monthly table (latest complete month vs prior). */
36208
36290
  buildGbpKeywordTrend(projectId, locationName) {
36209
- const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and30(eq38(gbpKeywordMonthly.projectId, projectId), eq38(gbpKeywordMonthly.locationName, locationName))).all();
36291
+ const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and30(eq39(gbpKeywordMonthly.projectId, projectId), eq39(gbpKeywordMonthly.locationName, locationName))).all();
36210
36292
  if (rows.length === 0) return { recentMonth: null, priorMonth: null, points: [] };
36211
36293
  const months = [...new Set(rows.map((r) => r.month))].sort().reverse();
36212
36294
  const recentMonth = months[0] ?? null;
@@ -36237,7 +36319,7 @@ var IntelligenceService = class {
36237
36319
  */
36238
36320
  persistGbpInsights(runId, projectId, gbpInsights, coveredLocationNames) {
36239
36321
  const covered = new Set(coveredLocationNames);
36240
- const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and30(eq38(insights.projectId, projectId), eq38(insights.provider, GBP_INSIGHT_PROVIDER))).all();
36322
+ const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and30(eq39(insights.projectId, projectId), eq39(insights.provider, GBP_INSIGHT_PROVIDER))).all();
36241
36323
  const staleIds = [];
36242
36324
  const dismissedSlots = /* @__PURE__ */ new Set();
36243
36325
  for (const row of existing) {
@@ -36248,7 +36330,7 @@ var IntelligenceService = class {
36248
36330
  }
36249
36331
  this.db.transaction((tx) => {
36250
36332
  for (const id of staleIds) {
36251
- tx.delete(insights).where(eq38(insights.id, id)).run();
36333
+ tx.delete(insights).where(eq39(insights.id, id)).run();
36252
36334
  }
36253
36335
  for (const insight of gbpInsights) {
36254
36336
  const parsed = parseGbpInsightId(insight.id);
@@ -36326,7 +36408,7 @@ var IntelligenceService = class {
36326
36408
  * create per run + aggregate). DB is left untouched.
36327
36409
  */
36328
36410
  backfill(projectName, opts, onProgress) {
36329
- const project = this.db.select().from(projects).where(eq38(projects.name, projectName)).get();
36411
+ const project = this.db.select().from(projects).where(eq39(projects.name, projectName)).get();
36330
36412
  if (!project) {
36331
36413
  throw new Error(`Project "${projectName}" not found`);
36332
36414
  }
@@ -36340,8 +36422,8 @@ var IntelligenceService = class {
36340
36422
  }
36341
36423
  const allRuns = this.db.select().from(runs).where(
36342
36424
  and30(
36343
- eq38(runs.projectId, project.id),
36344
- or6(eq38(runs.status, "completed"), eq38(runs.status, "partial")),
36425
+ eq39(runs.projectId, project.id),
36426
+ or6(eq39(runs.status, "completed"), eq39(runs.status, "partial")),
36345
36427
  // Backfill must not replay probe runs as if they were real sweeps.
36346
36428
  ne5(runs.trigger, RunTriggers.probe)
36347
36429
  )
@@ -36374,7 +36456,7 @@ var IntelligenceService = class {
36374
36456
  let wouldDeleteTotal = 0;
36375
36457
  const existingByRunId = /* @__PURE__ */ new Map();
36376
36458
  if (isDryRun && targetRuns.length > 0) {
36377
- const rows = this.db.select({ runId: insights.runId }).from(insights).where(inArray14(insights.runId, targetRuns.map((r) => r.id))).all();
36459
+ const rows = this.db.select({ runId: insights.runId }).from(insights).where(inArray15(insights.runId, targetRuns.map((r) => r.id))).all();
36378
36460
  for (const r of rows) {
36379
36461
  if (r.runId == null) continue;
36380
36462
  existingByRunId.set(r.runId, (existingByRunId.get(r.runId) ?? 0) + 1);
@@ -36420,7 +36502,7 @@ var IntelligenceService = class {
36420
36502
  return { processed, skipped, totalInsights };
36421
36503
  }
36422
36504
  loadTrackedCompetitors(projectId) {
36423
- return this.db.select({ domain: competitors.domain }).from(competitors).where(eq38(competitors.projectId, projectId)).all().map((r) => r.domain);
36505
+ return this.db.select({ domain: competitors.domain }).from(competitors).where(eq39(competitors.projectId, projectId)).all().map((r) => r.domain);
36424
36506
  }
36425
36507
  /**
36426
36508
  * Wipe transition signals from an analysis result while keeping health.
@@ -36441,15 +36523,15 @@ var IntelligenceService = class {
36441
36523
  }
36442
36524
  persistResult(result, runId, projectId) {
36443
36525
  const previouslyDismissed = /* @__PURE__ */ new Set();
36444
- 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();
36526
+ const existingInsights = this.db.select({ query: insights.query, provider: insights.provider, type: insights.type, dismissed: insights.dismissed }).from(insights).where(eq39(insights.runId, runId)).all();
36445
36527
  for (const row of existingInsights) {
36446
36528
  if (row.dismissed) {
36447
36529
  previouslyDismissed.add(`${row.query}:${row.provider}:${row.type}`);
36448
36530
  }
36449
36531
  }
36450
36532
  this.db.transaction((tx) => {
36451
- tx.delete(insights).where(eq38(insights.runId, runId)).run();
36452
- tx.delete(healthSnapshots).where(eq38(healthSnapshots.runId, runId)).run();
36533
+ tx.delete(insights).where(eq39(insights.runId, runId)).run();
36534
+ tx.delete(healthSnapshots).where(eq39(healthSnapshots.runId, runId)).run();
36453
36535
  const now = (/* @__PURE__ */ new Date()).toISOString();
36454
36536
  for (const insight of result.insights) {
36455
36537
  const wasDismissed = previouslyDismissed.has(`${insight.query}:${insight.provider}:${insight.type}`);
@@ -36469,7 +36551,7 @@ var IntelligenceService = class {
36469
36551
  }).run();
36470
36552
  }
36471
36553
  tx.insert(healthSnapshots).values({
36472
- id: crypto30.randomUUID(),
36554
+ id: crypto31.randomUUID(),
36473
36555
  projectId,
36474
36556
  runId,
36475
36557
  overallCitedRate: String(result.health.overallCitedRate),
@@ -36502,14 +36584,14 @@ var IntelligenceService = class {
36502
36584
  applySeverityTiering(rawInsights, excludeRunId, projectId) {
36503
36585
  const regressions = rawInsights.filter((i) => i.type === "regression");
36504
36586
  if (regressions.length === 0) return rawInsights;
36505
- const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq38(gscSearchData.projectId, projectId)).all();
36587
+ const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq39(gscSearchData.projectId, projectId)).all();
36506
36588
  const gscConnected = gscRows.length > 0;
36507
36589
  const gscImpressionsByQuery = /* @__PURE__ */ new Map();
36508
36590
  for (const row of gscRows) {
36509
36591
  const key = row.query.toLowerCase();
36510
36592
  gscImpressionsByQuery.set(key, (gscImpressionsByQuery.get(key) ?? 0) + row.impressions);
36511
36593
  }
36512
- const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq38(projects.id, projectId)).get();
36594
+ const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq39(projects.id, projectId)).get();
36513
36595
  const locationCount = Math.max(
36514
36596
  1,
36515
36597
  (projectRow?.locations ?? []).length
@@ -36517,9 +36599,9 @@ var IntelligenceService = class {
36517
36599
  const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
36518
36600
  const recentRunRows = this.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
36519
36601
  and30(
36520
- eq38(runs.projectId, projectId),
36521
- eq38(runs.kind, RunKinds["answer-visibility"]),
36522
- or6(eq38(runs.status, "completed"), eq38(runs.status, "partial")),
36602
+ eq39(runs.projectId, projectId),
36603
+ eq39(runs.kind, RunKinds["answer-visibility"]),
36604
+ or6(eq39(runs.status, "completed"), eq39(runs.status, "partial")),
36523
36605
  // Defensive — see top of file.
36524
36606
  ne5(runs.trigger, RunTriggers.probe)
36525
36607
  )
@@ -36539,7 +36621,7 @@ var IntelligenceService = class {
36539
36621
  const haveHistory = recentRunIds.length > 0;
36540
36622
  const priorRegressionsByPair = /* @__PURE__ */ new Map();
36541
36623
  if (haveHistory) {
36542
- const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and30(eq38(insights.type, "regression"), inArray14(insights.runId, recentRunIds))).all();
36624
+ const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and30(eq39(insights.type, "regression"), inArray15(insights.runId, recentRunIds))).all();
36543
36625
  const regressionGroups = /* @__PURE__ */ new Map();
36544
36626
  for (const row of priorRows) {
36545
36627
  if (!row.runId) continue;
@@ -36568,7 +36650,7 @@ var IntelligenceService = class {
36568
36650
  });
36569
36651
  }
36570
36652
  buildRunData(runId, projectId, completedAt, location = null) {
36571
- const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq38(projects.id, projectId)).get();
36653
+ const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq39(projects.id, projectId)).get();
36572
36654
  const projectDomains = projectDomainRow ? effectiveDomains({
36573
36655
  canonicalDomain: projectDomainRow.canonicalDomain,
36574
36656
  ownedDomains: projectDomainRow.ownedDomains
@@ -36585,7 +36667,7 @@ var IntelligenceService = class {
36585
36667
  citedDomains: querySnapshots.citedDomains,
36586
36668
  competitorOverlap: querySnapshots.competitorOverlap,
36587
36669
  snapshotLocation: querySnapshots.location
36588
- }).from(querySnapshots).leftJoin(queries, eq38(querySnapshots.queryId, queries.id)).where(eq38(querySnapshots.runId, runId)).all();
36670
+ }).from(querySnapshots).leftJoin(queries, eq39(querySnapshots.queryId, queries.id)).where(eq39(querySnapshots.runId, runId)).all();
36589
36671
  const snapshots = [];
36590
36672
  let orphanCount = 0;
36591
36673
  for (const r of rows) {