@ainyc/canonry 4.69.0 → 4.69.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +20 -1
  2. package/assets/assets/{BacklinksPage-BXVoTc3S.js → BacklinksPage-DLsgGSXv.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-pJRJPd17.js → ChartPrimitives-CERLI8Z-.js} +1 -1
  4. package/assets/assets/{ProjectPage-CQ1_itHh.js → ProjectPage-CaRqQ1vH.js} +1 -1
  5. package/assets/assets/{RunRow-DPL4FxPl.js → RunRow-C3x47chX.js} +1 -1
  6. package/assets/assets/{RunsPage-B4dCG_66.js → RunsPage-DRIlEete.js} +1 -1
  7. package/assets/assets/{SettingsPage-D8aWhLsU.js → SettingsPage-CHaqBpnQ.js} +1 -1
  8. package/assets/assets/{TrafficPage-COZa5_Q_.js → TrafficPage-UbL0daLy.js} +1 -1
  9. package/assets/assets/{TrafficSourceDetailPage-CN8Cx6YI.js → TrafficSourceDetailPage-CjLDbjf_.js} +1 -1
  10. package/assets/assets/{extract-error-message-D8g8YXDH.js → extract-error-message-BndXGmUh.js} +1 -1
  11. package/assets/assets/{index-DPO3uDWZ.js → index-BCwC5OlW.js} +53 -53
  12. package/assets/assets/{server-traffic-0JT1Vbj_.js → server-traffic-DOnVZFEw.js} +1 -1
  13. package/assets/assets/{trash-2-_1TgguOP.js → trash-2-HEZdy4sJ.js} +1 -1
  14. package/assets/index.html +1 -1
  15. package/dist/{chunk-D75O5A27.js → chunk-5FM7QRYD.js} +1558 -1526
  16. package/dist/{chunk-WQ44ZXGQ.js → chunk-SBYA3LEJ.js} +4 -4
  17. package/dist/{chunk-B2CH7GBW.js → chunk-WFM2O72W.js} +465 -307
  18. package/dist/{chunk-YYFBMDLC.js → chunk-ZNWMVYYU.js} +53 -1
  19. package/dist/cli.js +195 -79
  20. package/dist/index.js +4 -4
  21. package/dist/{intelligence-service-IUKD3PMZ.js → intelligence-service-DVVSE3G7.js} +2 -2
  22. package/dist/mcp.js +2 -2
  23. package/package.json +10 -10
@@ -32,6 +32,8 @@ import {
32
32
  absolutizeProjectUrl,
33
33
  actionConfidenceLabel,
34
34
  agentProvidersResponseDtoSchema,
35
+ apiKeyDtoSchema,
36
+ apiKeyListDtoSchema,
35
37
  auditLogEntrySchema,
36
38
  authInvalid,
37
39
  authRequired,
@@ -74,6 +76,8 @@ import {
74
76
  contentTargetDismissalDtoSchema,
75
77
  contentTargetDismissalsResponseDtoSchema,
76
78
  contentTargetsResponseDtoSchema,
79
+ createApiKeyRequestSchema,
80
+ createdApiKeyDtoSchema,
77
81
  dedupeReportActions,
78
82
  dedupeReportOpportunities,
79
83
  deliveryFailed,
@@ -208,10 +212,10 @@ import {
208
212
  wordpressSchemaDeployResultDtoSchema,
209
213
  wordpressSchemaStatusResultDtoSchema,
210
214
  wordpressStatusDtoSchema
211
- } from "./chunk-D75O5A27.js";
215
+ } from "./chunk-5FM7QRYD.js";
212
216
 
213
217
  // src/intelligence-service.ts
214
- import { eq as eq30, desc as desc15, asc as asc3, and as and23, ne as ne5, or as or5, inArray as inArray11, gte as gte6, lte as lte3 } from "drizzle-orm";
218
+ import { eq as eq31, desc as desc16, asc as asc3, and as and23, ne as ne5, or as or5, inArray as inArray11, gte as gte6, lte as lte3 } from "drizzle-orm";
215
219
 
216
220
  // ../db/src/client.ts
217
221
  import { mkdirSync } from "fs";
@@ -4326,19 +4330,26 @@ function buildRunHistory(runs2, snapshotsByRunId, limit = DEFAULT_RUN_HISTORY_LI
4326
4330
  return recent.map((run) => {
4327
4331
  const snapshots = snapshotsByRunId.get(run.id) ?? [];
4328
4332
  const queryCited = /* @__PURE__ */ new Map();
4333
+ const queryMentioned = /* @__PURE__ */ new Map();
4329
4334
  for (const snap of snapshots) {
4330
4335
  if (!queryCited.has(snap.queryId)) queryCited.set(snap.queryId, false);
4331
4336
  if (snap.citationState === CitationStates.cited) queryCited.set(snap.queryId, true);
4337
+ if (!queryMentioned.has(snap.queryId)) queryMentioned.set(snap.queryId, false);
4338
+ if (snap.answerMentioned === true) queryMentioned.set(snap.queryId, true);
4332
4339
  }
4333
4340
  const totalCount = queryCited.size;
4334
4341
  const citedCount = [...queryCited.values()].filter(Boolean).length;
4335
4342
  const citationRate = totalCount > 0 ? Math.round(citedCount / totalCount * 100) : 0;
4343
+ const mentionedCount = [...queryMentioned.values()].filter(Boolean).length;
4344
+ const mentionRate = totalCount > 0 ? Math.round(mentionedCount / totalCount * 100) : 0;
4336
4345
  return {
4337
4346
  runId: run.id,
4338
4347
  createdAt: run.createdAt,
4339
4348
  citedCount,
4340
4349
  totalCount,
4341
4350
  citationRate,
4351
+ mentionedCount,
4352
+ mentionRate,
4342
4353
  status: run.status
4343
4354
  };
4344
4355
  });
@@ -4831,7 +4842,7 @@ function requireScope(request, scope) {
4831
4842
  if (key.scopes.includes("*") || key.scopes.includes(scope)) return;
4832
4843
  throw forbidden(`This action requires the "${scope}" scope on your API key.`);
4833
4844
  }
4834
- function hashKey(key) {
4845
+ function hashApiKey(key) {
4835
4846
  return crypto2.createHash("sha256").update(key).digest("hex");
4836
4847
  }
4837
4848
  var SKIP_PATHS = ["/health"];
@@ -4870,7 +4881,7 @@ async function authPlugin(app, opts = {}) {
4870
4881
  throw authRequired();
4871
4882
  }
4872
4883
  const token = parts[1];
4873
- const hash = hashKey(token);
4884
+ const hash = hashApiKey(token);
4874
4885
  key = app.db.select().from(apiKeys).where(eq(apiKeys.keyHash, hash)).get();
4875
4886
  if (!key || key.revokedAt) {
4876
4887
  throw authInvalid();
@@ -12030,6 +12041,7 @@ async function compositeRoutes(app) {
12030
12041
  const attentionItems = buildAttentionItems(insightRows, allRuns);
12031
12042
  const sparklineRuns = visibilityRuns.slice(0, DEFAULT_RUN_HISTORY_LIMIT).map((r) => ({ id: r.id, createdAt: r.createdAt, status: r.status }));
12032
12043
  const runHistory = buildRunHistory(sparklineRuns, snapshotsByRun);
12044
+ scores.mention.trend = runHistory.map((p) => p.mentionRate);
12033
12045
  scores.visibility.trend = runHistory.map((p) => p.citationRate);
12034
12046
  const suggestedQueries = buildSuggestedQueriesFromGsc(
12035
12047
  app,
@@ -12575,6 +12587,8 @@ function makeSnippet(text2, query) {
12575
12587
  import { z } from "zod";
12576
12588
  var SCHEMA_TABLE = {
12577
12589
  AgentProvidersResponseDto: agentProvidersResponseDtoSchema,
12590
+ ApiKeyDto: apiKeyDtoSchema,
12591
+ ApiKeyListDto: apiKeyListDtoSchema,
12578
12592
  AuditLogEntry: auditLogEntrySchema,
12579
12593
  BacklinkHistoryEntry: backlinkHistoryEntrySchema,
12580
12594
  BacklinkListResponse: backlinkListResponseSchema,
@@ -12602,6 +12616,8 @@ var SCHEMA_TABLE = {
12602
12616
  ContentTargetDismissalDto: contentTargetDismissalDtoSchema,
12603
12617
  ContentTargetDismissalsResponseDto: contentTargetDismissalsResponseDtoSchema,
12604
12618
  ContentTargetsResponseDto: contentTargetsResponseDtoSchema,
12619
+ CreateApiKeyRequest: createApiKeyRequestSchema,
12620
+ CreatedApiKeyDto: createdApiKeyDtoSchema,
12605
12621
  RecommendationExplanationDto: recommendationExplanationDtoSchema,
12606
12622
  DiscoveryPromotePreview: discoveryPromotePreviewSchema,
12607
12623
  DiscoveryPromoteResult: discoveryPromoteResultSchema,
@@ -12769,6 +12785,13 @@ var notificationIdParameter = {
12769
12785
  description: "Notification ID.",
12770
12786
  schema: stringSchema
12771
12787
  };
12788
+ var keyIdParameter = {
12789
+ name: "id",
12790
+ in: "path",
12791
+ required: true,
12792
+ description: "API key ID.",
12793
+ schema: stringSchema
12794
+ };
12772
12795
  var providerNameParameter = {
12773
12796
  name: "name",
12774
12797
  in: "path",
@@ -13692,6 +13715,50 @@ var routeCatalog = [
13692
13715
  501: errorResponse("Google settings updates are not supported.")
13693
13716
  }
13694
13717
  },
13718
+ {
13719
+ method: "get",
13720
+ path: "/api/v1/keys",
13721
+ summary: "List API keys",
13722
+ description: "Returns every API key on the instance, newest first, as SAFE metadata only \u2014 id, name, key prefix, scopes, created / last-used / revoked timestamps. The stored hash and the plaintext token are NEVER returned here; the raw token is shown exactly once at creation. Ungated: any valid bearer can list.",
13723
+ tags: ["keys"],
13724
+ responses: {
13725
+ 200: jsonResponse("Keys returned.", "ApiKeyListDto")
13726
+ }
13727
+ },
13728
+ {
13729
+ method: "post",
13730
+ path: "/api/v1/keys",
13731
+ summary: "Create an API key",
13732
+ description: 'Mints a new `cnry_\u2026` API key. Requires the `keys.write` scope (the default `*` key satisfies it). The response includes the plaintext `key` field exactly ONCE \u2014 it is stored only as a sha256 hash and cannot be recovered later, so persist it on receipt. Omit `scopes` to default to `["*"]`.',
13733
+ tags: ["keys"],
13734
+ requestBody: {
13735
+ required: true,
13736
+ content: {
13737
+ "application/json": {
13738
+ schema: { $ref: "#/components/schemas/CreateApiKeyRequest" }
13739
+ }
13740
+ }
13741
+ },
13742
+ responses: {
13743
+ 200: jsonResponse("Key created. Includes the one-time plaintext `key`.", "CreatedApiKeyDto"),
13744
+ 400: errorResponse("Invalid request body."),
13745
+ 403: errorResponse("Missing the keys.write scope.")
13746
+ }
13747
+ },
13748
+ {
13749
+ method: "post",
13750
+ path: "/api/v1/keys/{id}/revoke",
13751
+ summary: "Revoke an API key",
13752
+ description: "Revokes the key by id. Requires the `keys.write` scope. Revocation is immediate \u2014 the auth layer rejects a revoked key on the next request. Idempotent: revoking an already-revoked key returns it unchanged. Refuses to revoke the key the caller is currently authenticating with (use a different key).",
13753
+ tags: ["keys"],
13754
+ parameters: [keyIdParameter],
13755
+ responses: {
13756
+ 200: jsonResponse("Key revoked (or already revoked).", "ApiKeyDto"),
13757
+ 400: errorResponse("Cannot revoke the currently-authenticating key."),
13758
+ 403: errorResponse("Missing the keys.write scope."),
13759
+ 404: errorResponse("Key not found.")
13760
+ }
13761
+ },
13695
13762
  {
13696
13763
  method: "post",
13697
13764
  path: "/api/v1/snapshot",
@@ -16541,6 +16608,96 @@ async function settingsRoutes(app, opts) {
16541
16608
  });
16542
16609
  }
16543
16610
 
16611
+ // ../api-routes/src/keys.ts
16612
+ import crypto12 from "crypto";
16613
+ import { desc as desc9, eq as eq17 } from "drizzle-orm";
16614
+ var KEYS_WRITE_SCOPE = "keys.write";
16615
+ function toApiKeyDto(row) {
16616
+ return {
16617
+ id: row.id,
16618
+ name: row.name,
16619
+ keyPrefix: row.keyPrefix,
16620
+ scopes: Array.isArray(row.scopes) ? row.scopes : [],
16621
+ createdAt: row.createdAt,
16622
+ lastUsedAt: row.lastUsedAt ?? null,
16623
+ revokedAt: row.revokedAt ?? null
16624
+ };
16625
+ }
16626
+ async function keysRoutes(app) {
16627
+ app.get("/keys", async () => {
16628
+ const rows = app.db.select().from(apiKeys).orderBy(desc9(apiKeys.createdAt)).all();
16629
+ return { keys: rows.map(toApiKeyDto) };
16630
+ });
16631
+ app.post("/keys", async (request) => {
16632
+ requireScope(request, KEYS_WRITE_SCOPE);
16633
+ const parsed = createApiKeyRequestSchema.safeParse(request.body);
16634
+ if (!parsed.success) {
16635
+ throw validationError("Invalid API key request", { issues: parsed.error.issues });
16636
+ }
16637
+ const { name, scopes } = parsed.data;
16638
+ const raw = `cnry_${crypto12.randomBytes(16).toString("hex")}`;
16639
+ const keyHash = hashApiKey(raw);
16640
+ const keyPrefix = raw.slice(0, 9);
16641
+ const id = crypto12.randomUUID();
16642
+ const now = (/* @__PURE__ */ new Date()).toISOString();
16643
+ const effectiveScopes = scopes ?? ["*"];
16644
+ app.db.transaction((tx) => {
16645
+ tx.insert(apiKeys).values({
16646
+ id,
16647
+ name,
16648
+ keyHash,
16649
+ keyPrefix,
16650
+ scopes: effectiveScopes,
16651
+ createdAt: now
16652
+ }).run();
16653
+ writeAuditLog(tx, auditFromRequest(request, {
16654
+ actor: "api",
16655
+ action: "api-key.created",
16656
+ entityType: "api-key",
16657
+ entityId: id,
16658
+ diff: { name, keyPrefix, scopes: effectiveScopes }
16659
+ }));
16660
+ });
16661
+ const dto = {
16662
+ id,
16663
+ name,
16664
+ keyPrefix,
16665
+ scopes: effectiveScopes,
16666
+ createdAt: now,
16667
+ lastUsedAt: null,
16668
+ revokedAt: null,
16669
+ key: raw
16670
+ };
16671
+ return dto;
16672
+ });
16673
+ app.post("/keys/:id/revoke", async (request) => {
16674
+ requireScope(request, KEYS_WRITE_SCOPE);
16675
+ const { id } = request.params;
16676
+ const row = app.db.select().from(apiKeys).where(eq17(apiKeys.id, id)).get();
16677
+ if (!row) {
16678
+ throw notFound("API key", id);
16679
+ }
16680
+ if (request.apiKey?.id === id) {
16681
+ throw validationError("Cannot revoke the API key you are currently authenticating with");
16682
+ }
16683
+ if (row.revokedAt) {
16684
+ return toApiKeyDto(row);
16685
+ }
16686
+ const now = (/* @__PURE__ */ new Date()).toISOString();
16687
+ app.db.transaction((tx) => {
16688
+ tx.update(apiKeys).set({ revokedAt: now }).where(eq17(apiKeys.id, id)).run();
16689
+ writeAuditLog(tx, auditFromRequest(request, {
16690
+ actor: "api",
16691
+ action: "api-key.revoked",
16692
+ entityType: "api-key",
16693
+ entityId: id,
16694
+ diff: { name: row.name, keyPrefix: row.keyPrefix }
16695
+ }));
16696
+ });
16697
+ return toApiKeyDto({ ...row, revokedAt: now });
16698
+ });
16699
+ }
16700
+
16544
16701
  // ../api-routes/src/snapshot.ts
16545
16702
  async function snapshotRoutes(app, opts) {
16546
16703
  app.post("/snapshot", async (request) => {
@@ -16600,8 +16757,8 @@ async function telemetryRoutes(app, opts) {
16600
16757
  }
16601
16758
 
16602
16759
  // ../api-routes/src/schedules.ts
16603
- import crypto12 from "crypto";
16604
- import { and as and12, eq as eq17 } from "drizzle-orm";
16760
+ import crypto13 from "crypto";
16761
+ import { and as and12, eq as eq18 } from "drizzle-orm";
16605
16762
  function parseKindParam(raw) {
16606
16763
  if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
16607
16764
  const parsed = schedulableRunKindSchema.safeParse(raw);
@@ -16628,7 +16785,7 @@ async function scheduleRoutes(app, opts) {
16628
16785
  if (!sourceId) {
16629
16786
  throw validationError('"sourceId" is required when kind is "traffic-sync"');
16630
16787
  }
16631
- const sourceRow = app.db.select().from(trafficSources).where(eq17(trafficSources.id, sourceId)).get();
16788
+ const sourceRow = app.db.select().from(trafficSources).where(eq18(trafficSources.id, sourceId)).get();
16632
16789
  if (!sourceRow || sourceRow.projectId !== project.id) {
16633
16790
  throw notFound("Traffic source", sourceId);
16634
16791
  }
@@ -16670,7 +16827,7 @@ async function scheduleRoutes(app, opts) {
16670
16827
  }
16671
16828
  const now = (/* @__PURE__ */ new Date()).toISOString();
16672
16829
  const enabledBool = enabled !== false;
16673
- const existing = app.db.select().from(schedules).where(and12(eq17(schedules.projectId, project.id), eq17(schedules.kind, kind))).get();
16830
+ const existing = app.db.select().from(schedules).where(and12(eq18(schedules.projectId, project.id), eq18(schedules.kind, kind))).get();
16674
16831
  if (existing) {
16675
16832
  app.db.update(schedules).set({
16676
16833
  cronExpr,
@@ -16680,10 +16837,10 @@ async function scheduleRoutes(app, opts) {
16680
16837
  sourceId: sourceId ?? null,
16681
16838
  enabled: enabledBool,
16682
16839
  updatedAt: now
16683
- }).where(eq17(schedules.id, existing.id)).run();
16840
+ }).where(eq18(schedules.id, existing.id)).run();
16684
16841
  } else {
16685
16842
  app.db.insert(schedules).values({
16686
- id: crypto12.randomUUID(),
16843
+ id: crypto13.randomUUID(),
16687
16844
  projectId: project.id,
16688
16845
  kind,
16689
16846
  cronExpr,
@@ -16704,13 +16861,13 @@ async function scheduleRoutes(app, opts) {
16704
16861
  diff: { kind, cronExpr, preset, timezone, providers, sourceId }
16705
16862
  });
16706
16863
  opts.onScheduleUpdated?.("upsert", project.id, kind);
16707
- const schedule = app.db.select().from(schedules).where(and12(eq17(schedules.projectId, project.id), eq17(schedules.kind, kind))).get();
16864
+ const schedule = app.db.select().from(schedules).where(and12(eq18(schedules.projectId, project.id), eq18(schedules.kind, kind))).get();
16708
16865
  return reply.status(existing ? 200 : 201).send(formatSchedule(schedule));
16709
16866
  });
16710
16867
  app.get("/projects/:name/schedule", async (request, reply) => {
16711
16868
  const project = resolveProject(app.db, request.params.name);
16712
16869
  const kind = parseKindParam(request.query?.kind);
16713
- const schedule = app.db.select().from(schedules).where(and12(eq17(schedules.projectId, project.id), eq17(schedules.kind, kind))).get();
16870
+ const schedule = app.db.select().from(schedules).where(and12(eq18(schedules.projectId, project.id), eq18(schedules.kind, kind))).get();
16714
16871
  if (!schedule) {
16715
16872
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
16716
16873
  }
@@ -16719,11 +16876,11 @@ async function scheduleRoutes(app, opts) {
16719
16876
  app.delete("/projects/:name/schedule", async (request, reply) => {
16720
16877
  const project = resolveProject(app.db, request.params.name);
16721
16878
  const kind = parseKindParam(request.query?.kind);
16722
- const schedule = app.db.select().from(schedules).where(and12(eq17(schedules.projectId, project.id), eq17(schedules.kind, kind))).get();
16879
+ const schedule = app.db.select().from(schedules).where(and12(eq18(schedules.projectId, project.id), eq18(schedules.kind, kind))).get();
16723
16880
  if (!schedule) {
16724
16881
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
16725
16882
  }
16726
- app.db.delete(schedules).where(eq17(schedules.id, schedule.id)).run();
16883
+ app.db.delete(schedules).where(eq18(schedules.id, schedule.id)).run();
16727
16884
  writeAuditLog(app.db, {
16728
16885
  projectId: project.id,
16729
16886
  actor: "api",
@@ -16755,8 +16912,8 @@ function formatSchedule(row) {
16755
16912
  }
16756
16913
 
16757
16914
  // ../api-routes/src/notifications.ts
16758
- import crypto13 from "crypto";
16759
- import { eq as eq18 } from "drizzle-orm";
16915
+ import crypto14 from "crypto";
16916
+ import { eq as eq19 } from "drizzle-orm";
16760
16917
  var VALID_EVENTS = ["citation.lost", "citation.gained", "run.completed", "run.failed", "insight.critical", "insight.high"];
16761
16918
  async function notificationRoutes(app, opts = {}) {
16762
16919
  const allowLoopback = opts.allowLoopbackWebhooks === true;
@@ -16775,8 +16932,8 @@ async function notificationRoutes(app, opts = {}) {
16775
16932
  throw validationError(`Invalid event(s): ${invalid.join(", ")}. Must be one of: ${VALID_EVENTS.join(", ")}`);
16776
16933
  }
16777
16934
  const now = (/* @__PURE__ */ new Date()).toISOString();
16778
- const id = crypto13.randomUUID();
16779
- const webhookSecret = crypto13.randomBytes(32).toString("hex");
16935
+ const id = crypto14.randomUUID();
16936
+ const webhookSecret = crypto14.randomBytes(32).toString("hex");
16780
16937
  app.db.insert(notifications).values({
16781
16938
  id,
16782
16939
  projectId: project.id,
@@ -16796,22 +16953,22 @@ async function notificationRoutes(app, opts = {}) {
16796
16953
  diff: { channel, ...redactNotificationUrl(url), events }
16797
16954
  });
16798
16955
  return reply.status(201).send({
16799
- ...formatNotification(app.db.select().from(notifications).where(eq18(notifications.id, id)).get()),
16956
+ ...formatNotification(app.db.select().from(notifications).where(eq19(notifications.id, id)).get()),
16800
16957
  webhookSecret
16801
16958
  });
16802
16959
  });
16803
16960
  app.get("/projects/:name/notifications", async (request, reply) => {
16804
16961
  const project = resolveProject(app.db, request.params.name);
16805
- const rows = app.db.select().from(notifications).where(eq18(notifications.projectId, project.id)).all();
16962
+ const rows = app.db.select().from(notifications).where(eq19(notifications.projectId, project.id)).all();
16806
16963
  return reply.send(rows.map(formatNotification));
16807
16964
  });
16808
16965
  app.delete("/projects/:name/notifications/:id", async (request, reply) => {
16809
16966
  const project = resolveProject(app.db, request.params.name);
16810
- const notification = app.db.select().from(notifications).where(eq18(notifications.id, request.params.id)).get();
16967
+ const notification = app.db.select().from(notifications).where(eq19(notifications.id, request.params.id)).get();
16811
16968
  if (!notification || notification.projectId !== project.id) {
16812
16969
  throw notFound("Notification", request.params.id);
16813
16970
  }
16814
- app.db.delete(notifications).where(eq18(notifications.id, notification.id)).run();
16971
+ app.db.delete(notifications).where(eq19(notifications.id, notification.id)).run();
16815
16972
  writeAuditLog(app.db, {
16816
16973
  projectId: project.id,
16817
16974
  actor: "api",
@@ -16823,7 +16980,7 @@ async function notificationRoutes(app, opts = {}) {
16823
16980
  });
16824
16981
  app.post("/projects/:name/notifications/:id/test", async (request, reply) => {
16825
16982
  const project = resolveProject(app.db, request.params.name);
16826
- const notification = app.db.select().from(notifications).where(eq18(notifications.id, request.params.id)).get();
16983
+ const notification = app.db.select().from(notifications).where(eq19(notifications.id, request.params.id)).get();
16827
16984
  if (!notification || notification.projectId !== project.id) {
16828
16985
  throw notFound("Notification", request.params.id);
16829
16986
  }
@@ -16875,8 +17032,8 @@ function formatNotification(row) {
16875
17032
  }
16876
17033
 
16877
17034
  // ../api-routes/src/google.ts
16878
- import crypto16 from "crypto";
16879
- import { eq as eq19, and as and13, desc as desc9, sql as sql8, inArray as inArray9 } from "drizzle-orm";
17035
+ import crypto17 from "crypto";
17036
+ import { eq as eq20, and as and13, desc as desc10, sql as sql8, inArray as inArray9 } from "drizzle-orm";
16880
17037
 
16881
17038
  // ../api-routes/src/gbp-summary.ts
16882
17039
  function computeMetricTotals(rows) {
@@ -17378,7 +17535,7 @@ async function inspectUrl(accessToken, inspectionUrl, siteUrl) {
17378
17535
  }
17379
17536
 
17380
17537
  // ../integration-google-analytics/src/ga4-client.ts
17381
- import crypto14 from "crypto";
17538
+ import crypto15 from "crypto";
17382
17539
 
17383
17540
  // ../integration-google-analytics/src/constants.ts
17384
17541
  var GA4_DATA_API_BASE = "https://analyticsdata.googleapis.com/v1beta";
@@ -17485,7 +17642,7 @@ function createServiceAccountJwt(clientEmail, privateKey, scope) {
17485
17642
  const headerB64 = encode(header);
17486
17643
  const payloadB64 = encode(payload);
17487
17644
  const signingInput = `${headerB64}.${payloadB64}`;
17488
- const sign = crypto14.createSign("RSA-SHA256");
17645
+ const sign = crypto15.createSign("RSA-SHA256");
17489
17646
  sign.update(signingInput);
17490
17647
  const signature = sign.sign(privateKey, "base64url");
17491
17648
  return `${signingInput}.${signature}`;
@@ -18325,7 +18482,7 @@ async function listPlaceActionLinks(accessToken, locationName, opts = {}) {
18325
18482
  }
18326
18483
 
18327
18484
  // ../integration-google-business-profile/src/lodging-client.ts
18328
- import crypto15 from "crypto";
18485
+ import crypto16 from "crypto";
18329
18486
  async function getLodging(accessToken, locationName, opts = {}) {
18330
18487
  const url = `${GBP_LODGING_BASE}/${locationName}/lodging?readMask=*`;
18331
18488
  try {
@@ -18352,7 +18509,7 @@ function isPopulated(value) {
18352
18509
  return true;
18353
18510
  }
18354
18511
  function hashLodging(lodging) {
18355
- return crypto15.createHash("sha256").update(stableStringify2(lodging)).digest("hex");
18512
+ return crypto16.createHash("sha256").update(stableStringify2(lodging)).digest("hex");
18356
18513
  }
18357
18514
  function stableStringify2(value) {
18358
18515
  if (value === null || typeof value !== "object") return JSON.stringify(value);
@@ -18374,7 +18531,7 @@ function scopesForConnectionType(type) {
18374
18531
  }
18375
18532
  }
18376
18533
  function signState(payload, secret) {
18377
- return crypto16.createHmac("sha256", secret).update(payload).digest("hex");
18534
+ return crypto17.createHmac("sha256", secret).update(payload).digest("hex");
18378
18535
  }
18379
18536
  function buildSignedState(data, secret) {
18380
18537
  const payload = JSON.stringify(data);
@@ -18385,7 +18542,7 @@ function verifySignedState(encoded, secret) {
18385
18542
  try {
18386
18543
  const { payload, sig } = JSON.parse(Buffer.from(encoded, "base64url").toString());
18387
18544
  const expected = signState(payload, secret);
18388
- if (!crypto16.timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"))) return null;
18545
+ if (!crypto17.timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"))) return null;
18389
18546
  return JSON.parse(payload);
18390
18547
  } catch {
18391
18548
  return null;
@@ -18540,7 +18697,7 @@ async function googleRoutes(app, opts) {
18540
18697
  if (!projectId) {
18541
18698
  return reply.status(400).send("Stale OAuth state \u2014 restart the connect flow.");
18542
18699
  }
18543
- const project = app.db.select().from(projects).where(eq19(projects.id, projectId)).get();
18700
+ const project = app.db.select().from(projects).where(eq20(projects.id, projectId)).get();
18544
18701
  if (!project) {
18545
18702
  return reply.status(400).send("Project no longer exists. Restart the connect flow.");
18546
18703
  }
@@ -18655,7 +18812,7 @@ async function googleRoutes(app, opts) {
18655
18812
  throw validationError('No GSC connection found for this domain. Run "canonry google connect" first.');
18656
18813
  }
18657
18814
  const now = (/* @__PURE__ */ new Date()).toISOString();
18658
- const runId = crypto16.randomUUID();
18815
+ const runId = crypto17.randomUUID();
18659
18816
  app.db.insert(runs).values({
18660
18817
  id: runId,
18661
18818
  projectId: project.id,
@@ -18668,14 +18825,14 @@ async function googleRoutes(app, opts) {
18668
18825
  if (opts.onGscSyncRequested) {
18669
18826
  opts.onGscSyncRequested(runId, project.id, { days, full });
18670
18827
  }
18671
- const run = app.db.select().from(runs).where(eq19(runs.id, runId)).get();
18828
+ const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
18672
18829
  return run;
18673
18830
  });
18674
18831
  app.get("/projects/:name/google/gsc/performance", async (request) => {
18675
18832
  const project = resolveProject(app.db, request.params.name);
18676
18833
  const { startDate, endDate, query, page, limit, offset } = request.query;
18677
18834
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
18678
- const conditions = [eq19(gscSearchData.projectId, project.id)];
18835
+ const conditions = [eq20(gscSearchData.projectId, project.id)];
18679
18836
  if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
18680
18837
  else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
18681
18838
  if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
@@ -18683,7 +18840,7 @@ async function googleRoutes(app, opts) {
18683
18840
  if (page) conditions.push(sql8`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
18684
18841
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
18685
18842
  const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
18686
- const rows = app.db.select().from(gscSearchData).where(and13(...conditions)).orderBy(desc9(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
18843
+ const rows = app.db.select().from(gscSearchData).where(and13(...conditions)).orderBy(desc10(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
18687
18844
  return rows.map((r) => ({
18688
18845
  date: r.date,
18689
18846
  query: r.query,
@@ -18700,7 +18857,7 @@ async function googleRoutes(app, opts) {
18700
18857
  const project = resolveProject(app.db, request.params.name);
18701
18858
  const { startDate, endDate } = request.query;
18702
18859
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
18703
- const conditions = [eq19(gscSearchData.projectId, project.id)];
18860
+ const conditions = [eq20(gscSearchData.projectId, project.id)];
18704
18861
  if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
18705
18862
  else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
18706
18863
  if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
@@ -18748,7 +18905,7 @@ async function googleRoutes(app, opts) {
18748
18905
  const mob = ir.mobileUsabilityResult;
18749
18906
  const rich = ir.richResultsResult;
18750
18907
  const now = (/* @__PURE__ */ new Date()).toISOString();
18751
- const id = crypto16.randomUUID();
18908
+ const id = crypto17.randomUUID();
18752
18909
  app.db.insert(gscUrlInspections).values({
18753
18910
  id,
18754
18911
  projectId: project.id,
@@ -18786,9 +18943,9 @@ async function googleRoutes(app, opts) {
18786
18943
  app.get("/projects/:name/google/gsc/inspections", async (request) => {
18787
18944
  const project = resolveProject(app.db, request.params.name);
18788
18945
  const { url, limit } = request.query;
18789
- const conditions = [eq19(gscUrlInspections.projectId, project.id)];
18790
- if (url) conditions.push(eq19(gscUrlInspections.url, url));
18791
- const rows = app.db.select().from(gscUrlInspections).where(and13(...conditions)).orderBy(desc9(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
18946
+ const conditions = [eq20(gscUrlInspections.projectId, project.id)];
18947
+ if (url) conditions.push(eq20(gscUrlInspections.url, url));
18948
+ const rows = app.db.select().from(gscUrlInspections).where(and13(...conditions)).orderBy(desc10(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
18792
18949
  return rows.map((r) => ({
18793
18950
  id: r.id,
18794
18951
  url: r.url,
@@ -18807,7 +18964,7 @@ async function googleRoutes(app, opts) {
18807
18964
  });
18808
18965
  app.get("/projects/:name/google/gsc/deindexed", async (request) => {
18809
18966
  const project = resolveProject(app.db, request.params.name);
18810
- const allInspections = app.db.select().from(gscUrlInspections).where(eq19(gscUrlInspections.projectId, project.id)).orderBy(desc9(gscUrlInspections.inspectedAt)).all();
18967
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq20(gscUrlInspections.projectId, project.id)).orderBy(desc10(gscUrlInspections.inspectedAt)).all();
18811
18968
  const byUrl = /* @__PURE__ */ new Map();
18812
18969
  for (const row of allInspections) {
18813
18970
  const existing = byUrl.get(row.url);
@@ -18835,7 +18992,7 @@ async function googleRoutes(app, opts) {
18835
18992
  });
18836
18993
  app.get("/projects/:name/google/gsc/coverage", async (request) => {
18837
18994
  const project = resolveProject(app.db, request.params.name);
18838
- const allInspections = app.db.select().from(gscUrlInspections).where(eq19(gscUrlInspections.projectId, project.id)).orderBy(desc9(gscUrlInspections.inspectedAt)).all();
18995
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq20(gscUrlInspections.projectId, project.id)).orderBy(desc10(gscUrlInspections.inspectedAt)).all();
18839
18996
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
18840
18997
  const latestByUrl = /* @__PURE__ */ new Map();
18841
18998
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -18884,7 +19041,7 @@ async function googleRoutes(app, opts) {
18884
19041
  const total = latestByUrl.size;
18885
19042
  const indexed = indexedUrls.length;
18886
19043
  const notIndexed = notIndexedUrls.length;
18887
- const latestSnapshot = app.db.select({ createdAt: gscCoverageSnapshots.createdAt }).from(gscCoverageSnapshots).where(eq19(gscCoverageSnapshots.projectId, project.id)).orderBy(desc9(gscCoverageSnapshots.createdAt)).limit(1).get();
19044
+ const latestSnapshot = app.db.select({ createdAt: gscCoverageSnapshots.createdAt }).from(gscCoverageSnapshots).where(eq20(gscCoverageSnapshots.projectId, project.id)).orderBy(desc10(gscCoverageSnapshots.createdAt)).limit(1).get();
18888
19045
  const lastSyncedAt = latestSnapshot?.createdAt ?? null;
18889
19046
  const formatRow = (r) => ({
18890
19047
  id: r.id,
@@ -18935,7 +19092,7 @@ async function googleRoutes(app, opts) {
18935
19092
  const project = resolveProject(app.db, request.params.name);
18936
19093
  const parsed = parseInt(request.query.limit ?? "90", 10);
18937
19094
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
18938
- const rows = app.db.select().from(gscCoverageSnapshots).where(eq19(gscCoverageSnapshots.projectId, project.id)).orderBy(desc9(gscCoverageSnapshots.date)).limit(limit).all();
19095
+ const rows = app.db.select().from(gscCoverageSnapshots).where(eq20(gscCoverageSnapshots.projectId, project.id)).orderBy(desc10(gscCoverageSnapshots.date)).limit(limit).all();
18939
19096
  return rows.map((r) => ({
18940
19097
  date: r.date,
18941
19098
  indexed: r.indexed,
@@ -18983,7 +19140,7 @@ async function googleRoutes(app, opts) {
18983
19140
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
18984
19141
  });
18985
19142
  const now = (/* @__PURE__ */ new Date()).toISOString();
18986
- const runId = crypto16.randomUUID();
19143
+ const runId = crypto17.randomUUID();
18987
19144
  app.db.insert(runs).values({
18988
19145
  id: runId,
18989
19146
  projectId: project.id,
@@ -18995,7 +19152,7 @@ async function googleRoutes(app, opts) {
18995
19152
  if (opts.onInspectSitemapRequested) {
18996
19153
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl });
18997
19154
  }
18998
- const run = app.db.select().from(runs).where(eq19(runs.id, runId)).get();
19155
+ const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
18999
19156
  return { sitemaps, primarySitemapUrl: sitemapUrl, run };
19000
19157
  });
19001
19158
  app.post("/projects/:name/google/gsc/inspect-sitemap", async (request) => {
@@ -19009,7 +19166,7 @@ async function googleRoutes(app, opts) {
19009
19166
  throw validationError("No GSC property configured for this connection");
19010
19167
  }
19011
19168
  const now = (/* @__PURE__ */ new Date()).toISOString();
19012
- const runId = crypto16.randomUUID();
19169
+ const runId = crypto17.randomUUID();
19013
19170
  app.db.insert(runs).values({
19014
19171
  id: runId,
19015
19172
  projectId: project.id,
@@ -19022,7 +19179,7 @@ async function googleRoutes(app, opts) {
19022
19179
  if (opts.onInspectSitemapRequested) {
19023
19180
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl: sitemapUrl ?? void 0 });
19024
19181
  }
19025
- const run = app.db.select().from(runs).where(eq19(runs.id, runId)).get();
19182
+ const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
19026
19183
  return run;
19027
19184
  });
19028
19185
  app.put("/projects/:name/google/connections/:type/sitemap", async (request) => {
@@ -19069,7 +19226,7 @@ async function googleRoutes(app, opts) {
19069
19226
  const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19070
19227
  let urlsToNotify = request.body?.urls ?? [];
19071
19228
  if (request.body?.allUnindexed) {
19072
- const allInspections = app.db.select().from(gscUrlInspections).where(eq19(gscUrlInspections.projectId, project.id)).orderBy(desc9(gscUrlInspections.inspectedAt)).all();
19229
+ const allInspections = app.db.select().from(gscUrlInspections).where(eq20(gscUrlInspections.projectId, project.id)).orderBy(desc10(gscUrlInspections.inspectedAt)).all();
19073
19230
  const latestByUrl = /* @__PURE__ */ new Map();
19074
19231
  for (const row of allInspections) {
19075
19232
  if (!latestByUrl.has(row.url)) {
@@ -19180,7 +19337,7 @@ async function googleRoutes(app, opts) {
19180
19337
  };
19181
19338
  }
19182
19339
  function listSelectionResponse(projectId) {
19183
- const rows = app.db.select().from(gbpLocations).where(eq19(gbpLocations.projectId, projectId)).all();
19340
+ const rows = app.db.select().from(gbpLocations).where(eq20(gbpLocations.projectId, projectId)).all();
19184
19341
  const dtos = rows.map(rowToDto2);
19185
19342
  return {
19186
19343
  locations: dtos,
@@ -19189,15 +19346,15 @@ async function googleRoutes(app, opts) {
19189
19346
  };
19190
19347
  }
19191
19348
  function clearGbpProjectData(tx, projectId) {
19192
- tx.delete(gbpDailyMetrics).where(eq19(gbpDailyMetrics.projectId, projectId)).run();
19193
- tx.delete(gbpKeywordImpressions).where(eq19(gbpKeywordImpressions.projectId, projectId)).run();
19194
- tx.delete(gbpKeywordMonthly).where(eq19(gbpKeywordMonthly.projectId, projectId)).run();
19195
- tx.delete(gbpPlaceActions).where(eq19(gbpPlaceActions.projectId, projectId)).run();
19196
- tx.delete(gbpLodgingSnapshots).where(eq19(gbpLodgingSnapshots.projectId, projectId)).run();
19197
- tx.delete(gbpLocations).where(eq19(gbpLocations.projectId, projectId)).run();
19349
+ tx.delete(gbpDailyMetrics).where(eq20(gbpDailyMetrics.projectId, projectId)).run();
19350
+ tx.delete(gbpKeywordImpressions).where(eq20(gbpKeywordImpressions.projectId, projectId)).run();
19351
+ tx.delete(gbpKeywordMonthly).where(eq20(gbpKeywordMonthly.projectId, projectId)).run();
19352
+ tx.delete(gbpPlaceActions).where(eq20(gbpPlaceActions.projectId, projectId)).run();
19353
+ tx.delete(gbpLodgingSnapshots).where(eq20(gbpLodgingSnapshots.projectId, projectId)).run();
19354
+ tx.delete(gbpLocations).where(eq20(gbpLocations.projectId, projectId)).run();
19198
19355
  }
19199
19356
  function currentProjectAccount(projectId) {
19200
- const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq19(gbpLocations.projectId, projectId)).limit(1).get();
19357
+ const row = app.db.select({ accountName: gbpLocations.accountName }).from(gbpLocations).where(eq20(gbpLocations.projectId, projectId)).limit(1).get();
19201
19358
  return row?.accountName ?? null;
19202
19359
  }
19203
19360
  app.post("/projects/:name/gbp/locations/discover", async (request) => {
@@ -19267,7 +19424,7 @@ async function googleRoutes(app, opts) {
19267
19424
  app.db.transaction((tx) => {
19268
19425
  if (switching) clearGbpProjectData(tx, project.id);
19269
19426
  for (const remote of remoteLocations) {
19270
- const existing = tx.select().from(gbpLocations).where(and13(eq19(gbpLocations.projectId, project.id), eq19(gbpLocations.locationName, remote.name))).get();
19427
+ const existing = tx.select().from(gbpLocations).where(and13(eq20(gbpLocations.projectId, project.id), eq20(gbpLocations.locationName, remote.name))).get();
19271
19428
  if (existing) {
19272
19429
  tx.update(gbpLocations).set({
19273
19430
  accountName,
@@ -19278,10 +19435,10 @@ async function googleRoutes(app, opts) {
19278
19435
  placeId: remote.metadata?.placeId ?? null,
19279
19436
  mapsUri: remote.metadata?.mapsUri ?? null,
19280
19437
  updatedAt: now
19281
- }).where(eq19(gbpLocations.id, existing.id)).run();
19438
+ }).where(eq20(gbpLocations.id, existing.id)).run();
19282
19439
  } else {
19283
19440
  tx.insert(gbpLocations).values({
19284
- id: crypto16.randomUUID(),
19441
+ id: crypto17.randomUUID(),
19285
19442
  projectId: project.id,
19286
19443
  accountName,
19287
19444
  locationName: remote.name,
@@ -19361,11 +19518,11 @@ async function googleRoutes(app, opts) {
19361
19518
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid selection request");
19362
19519
  }
19363
19520
  const { selected } = parsed.data;
19364
- const existing = app.db.select().from(gbpLocations).where(and13(eq19(gbpLocations.projectId, project.id), eq19(gbpLocations.locationName, locationName))).get();
19521
+ const existing = app.db.select().from(gbpLocations).where(and13(eq20(gbpLocations.projectId, project.id), eq20(gbpLocations.locationName, locationName))).get();
19365
19522
  if (!existing) throw notFound("GBP location", locationName);
19366
19523
  const now = (/* @__PURE__ */ new Date()).toISOString();
19367
19524
  app.db.transaction((tx) => {
19368
- tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq19(gbpLocations.id, existing.id)).run();
19525
+ tx.update(gbpLocations).set({ selected, updatedAt: now }).where(eq20(gbpLocations.id, existing.id)).run();
19369
19526
  writeAuditLog(tx, {
19370
19527
  projectId: project.id,
19371
19528
  actor: "api",
@@ -19374,7 +19531,7 @@ async function googleRoutes(app, opts) {
19374
19531
  entityId: locationName
19375
19532
  });
19376
19533
  });
19377
- const refreshed = app.db.select().from(gbpLocations).where(eq19(gbpLocations.id, existing.id)).get();
19534
+ const refreshed = app.db.select().from(gbpLocations).where(eq20(gbpLocations.id, existing.id)).get();
19378
19535
  return rowToDto2(refreshed);
19379
19536
  });
19380
19537
  app.delete("/projects/:name/gbp/connection", async (request, reply) => {
@@ -19404,7 +19561,7 @@ async function googleRoutes(app, opts) {
19404
19561
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid sync request");
19405
19562
  }
19406
19563
  const now = (/* @__PURE__ */ new Date()).toISOString();
19407
- const runId = crypto16.randomUUID();
19564
+ const runId = crypto17.randomUUID();
19408
19565
  app.db.insert(runs).values({
19409
19566
  id: runId,
19410
19567
  projectId: project.id,
@@ -19418,10 +19575,10 @@ async function googleRoutes(app, opts) {
19418
19575
  });
19419
19576
  app.get("/projects/:name/gbp/metrics", async (request) => {
19420
19577
  const project = resolveProject(app.db, request.params.name);
19421
- const conditions = [eq19(gbpDailyMetrics.projectId, project.id)];
19422
- if (request.query.locationName) conditions.push(eq19(gbpDailyMetrics.locationName, request.query.locationName));
19423
- if (request.query.metric) conditions.push(eq19(gbpDailyMetrics.metric, request.query.metric));
19424
- const rows = app.db.select().from(gbpDailyMetrics).where(and13(...conditions)).orderBy(desc9(gbpDailyMetrics.date)).all();
19578
+ const conditions = [eq20(gbpDailyMetrics.projectId, project.id)];
19579
+ if (request.query.locationName) conditions.push(eq20(gbpDailyMetrics.locationName, request.query.locationName));
19580
+ if (request.query.metric) conditions.push(eq20(gbpDailyMetrics.metric, request.query.metric));
19581
+ const rows = app.db.select().from(gbpDailyMetrics).where(and13(...conditions)).orderBy(desc10(gbpDailyMetrics.date)).all();
19425
19582
  return {
19426
19583
  metrics: rows.map((r) => ({ locationName: r.locationName, date: r.date, metric: r.metric, value: r.value })),
19427
19584
  total: rows.length
@@ -19429,8 +19586,8 @@ async function googleRoutes(app, opts) {
19429
19586
  });
19430
19587
  app.get("/projects/:name/gbp/keywords", async (request) => {
19431
19588
  const project = resolveProject(app.db, request.params.name);
19432
- const conditions = [eq19(gbpKeywordImpressions.projectId, project.id)];
19433
- if (request.query.locationName) conditions.push(eq19(gbpKeywordImpressions.locationName, request.query.locationName));
19589
+ const conditions = [eq20(gbpKeywordImpressions.projectId, project.id)];
19590
+ if (request.query.locationName) conditions.push(eq20(gbpKeywordImpressions.locationName, request.query.locationName));
19434
19591
  const rows = app.db.select().from(gbpKeywordImpressions).where(and13(...conditions)).all();
19435
19592
  rows.sort((a, b) => (b.valueCount ?? -1) - (a.valueCount ?? -1));
19436
19593
  const thresholded = rows.filter((r) => r.valueThreshold !== null).length;
@@ -19449,8 +19606,8 @@ async function googleRoutes(app, opts) {
19449
19606
  });
19450
19607
  app.get("/projects/:name/gbp/place-actions", async (request) => {
19451
19608
  const project = resolveProject(app.db, request.params.name);
19452
- const conditions = [eq19(gbpPlaceActions.projectId, project.id)];
19453
- if (request.query.locationName) conditions.push(eq19(gbpPlaceActions.locationName, request.query.locationName));
19609
+ const conditions = [eq20(gbpPlaceActions.projectId, project.id)];
19610
+ if (request.query.locationName) conditions.push(eq20(gbpPlaceActions.locationName, request.query.locationName));
19454
19611
  const rows = app.db.select().from(gbpPlaceActions).where(and13(...conditions)).all();
19455
19612
  return {
19456
19613
  placeActions: rows.map((r) => ({
@@ -19466,9 +19623,9 @@ async function googleRoutes(app, opts) {
19466
19623
  });
19467
19624
  app.get("/projects/:name/gbp/lodging", async (request) => {
19468
19625
  const project = resolveProject(app.db, request.params.name);
19469
- const conditions = [eq19(gbpLodgingSnapshots.projectId, project.id)];
19470
- if (request.query.locationName) conditions.push(eq19(gbpLodgingSnapshots.locationName, request.query.locationName));
19471
- const rows = app.db.select().from(gbpLodgingSnapshots).where(and13(...conditions)).orderBy(desc9(gbpLodgingSnapshots.syncedAt)).all();
19626
+ const conditions = [eq20(gbpLodgingSnapshots.projectId, project.id)];
19627
+ if (request.query.locationName) conditions.push(eq20(gbpLodgingSnapshots.locationName, request.query.locationName));
19628
+ const rows = app.db.select().from(gbpLodgingSnapshots).where(and13(...conditions)).orderBy(desc10(gbpLodgingSnapshots.syncedAt)).all();
19472
19629
  const latestByLocation = /* @__PURE__ */ new Map();
19473
19630
  for (const row of rows) {
19474
19631
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -19483,9 +19640,9 @@ async function googleRoutes(app, opts) {
19483
19640
  });
19484
19641
  app.get("/projects/:name/gbp/places", async (request) => {
19485
19642
  const project = resolveProject(app.db, request.params.name);
19486
- const conditions = [eq19(gbpPlaceDetails.projectId, project.id)];
19487
- if (request.query.locationName) conditions.push(eq19(gbpPlaceDetails.locationName, request.query.locationName));
19488
- const rows = app.db.select().from(gbpPlaceDetails).where(and13(...conditions)).orderBy(desc9(gbpPlaceDetails.syncedAt)).all();
19643
+ const conditions = [eq20(gbpPlaceDetails.projectId, project.id)];
19644
+ if (request.query.locationName) conditions.push(eq20(gbpPlaceDetails.locationName, request.query.locationName));
19645
+ const rows = app.db.select().from(gbpPlaceDetails).where(and13(...conditions)).orderBy(desc10(gbpPlaceDetails.syncedAt)).all();
19489
19646
  const latestByLocation = /* @__PURE__ */ new Map();
19490
19647
  for (const row of rows) {
19491
19648
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -19504,7 +19661,7 @@ async function googleRoutes(app, opts) {
19504
19661
  app.get("/projects/:name/gbp/summary", async (request) => {
19505
19662
  const project = resolveProject(app.db, request.params.name);
19506
19663
  const locationName = request.query.locationName ?? null;
19507
- const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and13(eq19(gbpLocations.projectId, project.id), eq19(gbpLocations.selected, true))).all().map((r) => r.n);
19664
+ const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and13(eq20(gbpLocations.projectId, project.id), eq20(gbpLocations.selected, true))).all().map((r) => r.n);
19508
19665
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
19509
19666
  if (locationNames.length === 0) {
19510
19667
  return buildGbpSummary({
@@ -19517,10 +19674,10 @@ async function googleRoutes(app, opts) {
19517
19674
  lodging: []
19518
19675
  });
19519
19676
  }
19520
- const metricRows = app.db.select().from(gbpDailyMetrics).where(and13(eq19(gbpDailyMetrics.projectId, project.id), inArray9(gbpDailyMetrics.locationName, locationNames))).all();
19521
- const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and13(eq19(gbpKeywordImpressions.projectId, project.id), inArray9(gbpKeywordImpressions.locationName, locationNames))).all();
19522
- const placeActionRows = app.db.select().from(gbpPlaceActions).where(and13(eq19(gbpPlaceActions.projectId, project.id), inArray9(gbpPlaceActions.locationName, locationNames))).all();
19523
- const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and13(eq19(gbpLodgingSnapshots.projectId, project.id), inArray9(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc9(gbpLodgingSnapshots.syncedAt)).all();
19677
+ const metricRows = app.db.select().from(gbpDailyMetrics).where(and13(eq20(gbpDailyMetrics.projectId, project.id), inArray9(gbpDailyMetrics.locationName, locationNames))).all();
19678
+ const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and13(eq20(gbpKeywordImpressions.projectId, project.id), inArray9(gbpKeywordImpressions.locationName, locationNames))).all();
19679
+ const placeActionRows = app.db.select().from(gbpPlaceActions).where(and13(eq20(gbpPlaceActions.projectId, project.id), inArray9(gbpPlaceActions.locationName, locationNames))).all();
19680
+ const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and13(eq20(gbpLodgingSnapshots.projectId, project.id), inArray9(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc10(gbpLodgingSnapshots.syncedAt)).all();
19524
19681
  const latestLodgingByLocation = /* @__PURE__ */ new Map();
19525
19682
  for (const row of lodgingRows) {
19526
19683
  if (!latestLodgingByLocation.has(row.locationName)) {
@@ -19540,8 +19697,8 @@ async function googleRoutes(app, opts) {
19540
19697
  }
19541
19698
 
19542
19699
  // ../api-routes/src/bing.ts
19543
- import crypto17 from "crypto";
19544
- import { eq as eq20, and as and14, desc as desc10 } from "drizzle-orm";
19700
+ import crypto18 from "crypto";
19701
+ import { eq as eq21, and as and14, desc as desc11 } from "drizzle-orm";
19545
19702
 
19546
19703
  // ../integration-bing/src/constants.ts
19547
19704
  var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
@@ -19869,7 +20026,7 @@ async function bingRoutes(app, opts) {
19869
20026
  const store = requireConnectionStore();
19870
20027
  const project = resolveProject(app.db, request.params.name);
19871
20028
  requireConnection(store, project.canonicalDomain);
19872
- const allInspections = app.db.select().from(bingUrlInspections).where(eq20(bingUrlInspections.projectId, project.id)).orderBy(desc10(bingUrlInspections.inspectedAt)).all();
20029
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq21(bingUrlInspections.projectId, project.id)).orderBy(desc11(bingUrlInspections.inspectedAt)).all();
19873
20030
  const latestByUrl = /* @__PURE__ */ new Map();
19874
20031
  const definitiveByUrl = /* @__PURE__ */ new Map();
19875
20032
  for (const row of allInspections) {
@@ -19926,7 +20083,7 @@ async function bingRoutes(app, opts) {
19926
20083
  const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
19927
20084
  const now = (/* @__PURE__ */ new Date()).toISOString();
19928
20085
  app.db.insert(bingCoverageSnapshots).values({
19929
- id: crypto17.randomUUID(),
20086
+ id: crypto18.randomUUID(),
19930
20087
  projectId: project.id,
19931
20088
  syncRunId: snapshotRunId,
19932
20089
  date: snapshotDate,
@@ -19958,7 +20115,7 @@ async function bingRoutes(app, opts) {
19958
20115
  const project = resolveProject(app.db, request.params.name);
19959
20116
  const parsed = parseInt(request.query.limit ?? "90", 10);
19960
20117
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
19961
- const rows = app.db.select().from(bingCoverageSnapshots).where(eq20(bingCoverageSnapshots.projectId, project.id)).orderBy(desc10(bingCoverageSnapshots.date)).limit(limit).all();
20118
+ const rows = app.db.select().from(bingCoverageSnapshots).where(eq21(bingCoverageSnapshots.projectId, project.id)).orderBy(desc11(bingCoverageSnapshots.date)).limit(limit).all();
19962
20119
  return rows.map((r) => ({
19963
20120
  date: r.date,
19964
20121
  indexed: r.indexed,
@@ -19970,8 +20127,8 @@ async function bingRoutes(app, opts) {
19970
20127
  requireConnectionStore();
19971
20128
  const project = resolveProject(app.db, request.params.name);
19972
20129
  const { url, limit } = request.query;
19973
- const whereClause = url ? and14(eq20(bingUrlInspections.projectId, project.id), eq20(bingUrlInspections.url, url)) : eq20(bingUrlInspections.projectId, project.id);
19974
- const filtered = app.db.select().from(bingUrlInspections).where(whereClause).orderBy(desc10(bingUrlInspections.inspectedAt)).limit(Math.max(1, Math.min(parseInt(limit ?? "100", 10) || 100, 1e3))).all();
20130
+ const whereClause = url ? and14(eq21(bingUrlInspections.projectId, project.id), eq21(bingUrlInspections.url, url)) : eq21(bingUrlInspections.projectId, project.id);
20131
+ const filtered = app.db.select().from(bingUrlInspections).where(whereClause).orderBy(desc11(bingUrlInspections.inspectedAt)).limit(Math.max(1, Math.min(parseInt(limit ?? "100", 10) || 100, 1e3))).all();
19975
20132
  return filtered.map((r) => ({
19976
20133
  id: r.id,
19977
20134
  url: r.url,
@@ -19997,7 +20154,7 @@ async function bingRoutes(app, opts) {
19997
20154
  throw validationError("url is required");
19998
20155
  }
19999
20156
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
20000
- const runId = crypto17.randomUUID();
20157
+ const runId = crypto18.randomUUID();
20001
20158
  app.db.insert(runs).values({
20002
20159
  id: runId,
20003
20160
  projectId: project.id,
@@ -20018,7 +20175,7 @@ async function bingRoutes(app, opts) {
20018
20175
  discoveryDate: result.DiscoveryDate ?? null
20019
20176
  });
20020
20177
  const now = (/* @__PURE__ */ new Date()).toISOString();
20021
- const id = crypto17.randomUUID();
20178
+ const id = crypto18.randomUUID();
20022
20179
  const httpCode = result.HttpStatus ?? result.HttpCode ?? null;
20023
20180
  const lastCrawledDate = parseBingDate(result.LastCrawledDate);
20024
20181
  const inIndexDate = parseBingDate(result.InIndexDate);
@@ -20060,7 +20217,7 @@ async function bingRoutes(app, opts) {
20060
20217
  anchorCount: result.AnchorCount ?? null,
20061
20218
  discoveryDate
20062
20219
  }).run();
20063
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq20(runs.id, runId)).run();
20220
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq21(runs.id, runId)).run();
20064
20221
  return {
20065
20222
  id,
20066
20223
  url,
@@ -20076,7 +20233,7 @@ async function bingRoutes(app, opts) {
20076
20233
  } catch (e) {
20077
20234
  const msg = e instanceof Error ? e.message : String(e);
20078
20235
  bingLog("error", "inspect-url.failed", { domain: project.canonicalDomain, url, error: msg });
20079
- app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq20(runs.id, runId)).run();
20236
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq21(runs.id, runId)).run();
20080
20237
  throw e;
20081
20238
  }
20082
20239
  });
@@ -20088,7 +20245,7 @@ async function bingRoutes(app, opts) {
20088
20245
  throw validationError('No Bing site configured. Run "canonry bing set-site <project> <url>" first.');
20089
20246
  }
20090
20247
  const now = (/* @__PURE__ */ new Date()).toISOString();
20091
- const runId = crypto17.randomUUID();
20248
+ const runId = crypto18.randomUUID();
20092
20249
  app.db.insert(runs).values({
20093
20250
  id: runId,
20094
20251
  projectId: project.id,
@@ -20103,7 +20260,7 @@ async function bingRoutes(app, opts) {
20103
20260
  } else {
20104
20261
  bingLog("warn", "inspect-sitemap.no-callback", { domain: project.canonicalDomain, runId });
20105
20262
  }
20106
- const run = app.db.select().from(runs).where(eq20(runs.id, runId)).get();
20263
+ const run = app.db.select().from(runs).where(eq21(runs.id, runId)).get();
20107
20264
  return run;
20108
20265
  });
20109
20266
  app.post("/projects/:name/bing/request-indexing", async (request) => {
@@ -20115,7 +20272,7 @@ async function bingRoutes(app, opts) {
20115
20272
  }
20116
20273
  let urlsToSubmit = request.body?.urls ?? [];
20117
20274
  if (request.body?.allUnindexed) {
20118
- const allInspections = app.db.select().from(bingUrlInspections).where(eq20(bingUrlInspections.projectId, project.id)).orderBy(desc10(bingUrlInspections.inspectedAt)).all();
20275
+ const allInspections = app.db.select().from(bingUrlInspections).where(eq21(bingUrlInspections.projectId, project.id)).orderBy(desc11(bingUrlInspections.inspectedAt)).all();
20119
20276
  const latestByUrl = /* @__PURE__ */ new Map();
20120
20277
  for (const row of allInspections) {
20121
20278
  if (!latestByUrl.has(row.url)) {
@@ -20202,14 +20359,14 @@ async function bingRoutes(app, opts) {
20202
20359
  import fs from "fs";
20203
20360
  import path from "path";
20204
20361
  import os from "os";
20205
- import { eq as eq21, and as and15 } from "drizzle-orm";
20362
+ import { eq as eq22, and as and15 } from "drizzle-orm";
20206
20363
  function getScreenshotDir() {
20207
20364
  return path.join(os.homedir(), ".canonry", "screenshots");
20208
20365
  }
20209
20366
  async function cdpRoutes(app, opts) {
20210
20367
  app.get("/screenshots/:snapshotId", async (request, reply) => {
20211
20368
  const { snapshotId } = request.params;
20212
- const snapshot = app.db.select({ screenshotPath: querySnapshots.screenshotPath }).from(querySnapshots).where(eq21(querySnapshots.id, snapshotId)).get();
20369
+ const snapshot = app.db.select({ screenshotPath: querySnapshots.screenshotPath }).from(querySnapshots).where(eq22(querySnapshots.id, snapshotId)).get();
20213
20370
  if (!snapshot?.screenshotPath) {
20214
20371
  const err = notFound("Screenshot", snapshotId);
20215
20372
  return reply.code(err.statusCode).send(err.toJSON());
@@ -20276,7 +20433,7 @@ async function cdpRoutes(app, opts) {
20276
20433
  async (request, reply) => {
20277
20434
  const project = resolveProject(app.db, request.params.name);
20278
20435
  const { runId } = request.params;
20279
- const run = app.db.select().from(runs).where(and15(eq21(runs.id, runId), eq21(runs.projectId, project.id))).get();
20436
+ const run = app.db.select().from(runs).where(and15(eq22(runs.id, runId), eq22(runs.projectId, project.id))).get();
20280
20437
  if (!run) {
20281
20438
  const err = notFound("Run", runId);
20282
20439
  return reply.code(err.statusCode).send(err.toJSON());
@@ -20289,8 +20446,8 @@ async function cdpRoutes(app, opts) {
20289
20446
  citedDomains: querySnapshots.citedDomains,
20290
20447
  screenshotPath: querySnapshots.screenshotPath,
20291
20448
  rawResponse: querySnapshots.rawResponse
20292
- }).from(querySnapshots).where(eq21(querySnapshots.runId, runId)).all());
20293
- const queryRows = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq21(queries.projectId, project.id)).all();
20449
+ }).from(querySnapshots).where(eq22(querySnapshots.runId, runId)).all());
20450
+ const queryRows = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq22(queries.projectId, project.id)).all();
20294
20451
  const queryMap = new Map(queryRows.map((q) => [q.id, q.query]));
20295
20452
  const byQuery = /* @__PURE__ */ new Map();
20296
20453
  for (const snap of snapshots) {
@@ -20372,8 +20529,8 @@ async function cdpRoutes(app, opts) {
20372
20529
  }
20373
20530
 
20374
20531
  // ../api-routes/src/ga.ts
20375
- import crypto18 from "crypto";
20376
- import { eq as eq22, desc as desc11, and as and16, sql as sql9 } from "drizzle-orm";
20532
+ import crypto19 from "crypto";
20533
+ import { eq as eq23, desc as desc12, and as and16, sql as sql9 } from "drizzle-orm";
20377
20534
  function gaLog(level, action, ctx) {
20378
20535
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
20379
20536
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -20580,10 +20737,10 @@ async function ga4Routes(app, opts) {
20580
20737
  if (!saConn && !oauthConn) {
20581
20738
  throw notFound("GA4 connection", project.name);
20582
20739
  }
20583
- app.db.delete(gaTrafficSnapshots).where(eq22(gaTrafficSnapshots.projectId, project.id)).run();
20584
- app.db.delete(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).run();
20585
- app.db.delete(gaAiReferrals).where(eq22(gaAiReferrals.projectId, project.id)).run();
20586
- app.db.delete(gaSocialReferrals).where(eq22(gaSocialReferrals.projectId, project.id)).run();
20740
+ app.db.delete(gaTrafficSnapshots).where(eq23(gaTrafficSnapshots.projectId, project.id)).run();
20741
+ app.db.delete(gaTrafficSummaries).where(eq23(gaTrafficSummaries.projectId, project.id)).run();
20742
+ app.db.delete(gaAiReferrals).where(eq23(gaAiReferrals.projectId, project.id)).run();
20743
+ app.db.delete(gaSocialReferrals).where(eq23(gaSocialReferrals.projectId, project.id)).run();
20587
20744
  const propertyId = saConn?.propertyId ?? oauthConn?.propertyId ?? null;
20588
20745
  opts.ga4CredentialStore?.deleteConnection(project.name);
20589
20746
  opts.googleConnectionStore?.deleteConnection(project.canonicalDomain, "ga4");
@@ -20604,7 +20761,7 @@ async function ga4Routes(app, opts) {
20604
20761
  if (!connected) {
20605
20762
  return { connected: false, propertyId: null, clientEmail: null, authMethod: null, lastSyncedAt: null };
20606
20763
  }
20607
- const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).orderBy(desc11(gaTrafficSummaries.syncedAt)).limit(1).get();
20764
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq23(gaTrafficSummaries.projectId, project.id)).orderBy(desc12(gaTrafficSummaries.syncedAt)).limit(1).get();
20608
20765
  return {
20609
20766
  connected: true,
20610
20767
  propertyId: saConn?.propertyId ?? oauthConn?.propertyId ?? null,
@@ -20628,7 +20785,7 @@ async function ga4Routes(app, opts) {
20628
20785
  const syncAi = !only || only === "ai";
20629
20786
  const syncSocial = !only || only === "social";
20630
20787
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
20631
- const runId = crypto18.randomUUID();
20788
+ const runId = crypto19.randomUUID();
20632
20789
  app.db.insert(runs).values({
20633
20790
  id: runId,
20634
20791
  projectId: project.id,
@@ -20669,14 +20826,14 @@ async function ga4Routes(app, opts) {
20669
20826
  if (syncTraffic) {
20670
20827
  tx.delete(gaTrafficSnapshots).where(
20671
20828
  and16(
20672
- eq22(gaTrafficSnapshots.projectId, project.id),
20829
+ eq23(gaTrafficSnapshots.projectId, project.id),
20673
20830
  sql9`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
20674
20831
  sql9`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
20675
20832
  )
20676
20833
  ).run();
20677
20834
  for (const row of rows) {
20678
20835
  tx.insert(gaTrafficSnapshots).values({
20679
- id: crypto18.randomUUID(),
20836
+ id: crypto19.randomUUID(),
20680
20837
  projectId: project.id,
20681
20838
  date: row.date,
20682
20839
  landingPage: row.landingPage,
@@ -20693,14 +20850,14 @@ async function ga4Routes(app, opts) {
20693
20850
  if (syncAi) {
20694
20851
  tx.delete(gaAiReferrals).where(
20695
20852
  and16(
20696
- eq22(gaAiReferrals.projectId, project.id),
20853
+ eq23(gaAiReferrals.projectId, project.id),
20697
20854
  sql9`${gaAiReferrals.date} >= ${summary.periodStart}`,
20698
20855
  sql9`${gaAiReferrals.date} <= ${summary.periodEnd}`
20699
20856
  )
20700
20857
  ).run();
20701
20858
  for (const row of aiReferrals) {
20702
20859
  tx.insert(gaAiReferrals).values({
20703
- id: crypto18.randomUUID(),
20860
+ id: crypto19.randomUUID(),
20704
20861
  projectId: project.id,
20705
20862
  date: row.date,
20706
20863
  source: row.source,
@@ -20719,14 +20876,14 @@ async function ga4Routes(app, opts) {
20719
20876
  if (syncSocial) {
20720
20877
  tx.delete(gaSocialReferrals).where(
20721
20878
  and16(
20722
- eq22(gaSocialReferrals.projectId, project.id),
20879
+ eq23(gaSocialReferrals.projectId, project.id),
20723
20880
  sql9`${gaSocialReferrals.date} >= ${summary.periodStart}`,
20724
20881
  sql9`${gaSocialReferrals.date} <= ${summary.periodEnd}`
20725
20882
  )
20726
20883
  ).run();
20727
20884
  for (const row of socialReferrals) {
20728
20885
  tx.insert(gaSocialReferrals).values({
20729
- id: crypto18.randomUUID(),
20886
+ id: crypto19.randomUUID(),
20730
20887
  projectId: project.id,
20731
20888
  date: row.date,
20732
20889
  source: row.source,
@@ -20740,9 +20897,9 @@ async function ga4Routes(app, opts) {
20740
20897
  }
20741
20898
  }
20742
20899
  if (syncSummary) {
20743
- tx.delete(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).run();
20900
+ tx.delete(gaTrafficSummaries).where(eq23(gaTrafficSummaries.projectId, project.id)).run();
20744
20901
  tx.insert(gaTrafficSummaries).values({
20745
- id: crypto18.randomUUID(),
20902
+ id: crypto19.randomUUID(),
20746
20903
  projectId: project.id,
20747
20904
  periodStart: summary.periodStart,
20748
20905
  periodEnd: summary.periodEnd,
@@ -20752,10 +20909,10 @@ async function ga4Routes(app, opts) {
20752
20909
  syncedAt: now,
20753
20910
  syncRunId: runId
20754
20911
  }).run();
20755
- tx.delete(gaTrafficWindowSummaries).where(eq22(gaTrafficWindowSummaries.projectId, project.id)).run();
20912
+ tx.delete(gaTrafficWindowSummaries).where(eq23(gaTrafficWindowSummaries.projectId, project.id)).run();
20756
20913
  for (const ws of windowSummaries) {
20757
20914
  tx.insert(gaTrafficWindowSummaries).values({
20758
- id: crypto18.randomUUID(),
20915
+ id: crypto19.randomUUID(),
20759
20916
  projectId: project.id,
20760
20917
  windowKey: ws.windowKey,
20761
20918
  periodStart: ws.periodStart,
@@ -20770,7 +20927,7 @@ async function ga4Routes(app, opts) {
20770
20927
  }
20771
20928
  }
20772
20929
  });
20773
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq22(runs.id, runId)).run();
20930
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: now }).where(eq23(runs.id, runId)).run();
20774
20931
  const syncedComponents = only ? [
20775
20932
  ...syncTraffic ? ["traffic"] : [],
20776
20933
  ...syncSummary ? ["summary"] : [],
@@ -20799,7 +20956,7 @@ async function ga4Routes(app, opts) {
20799
20956
  } catch (e) {
20800
20957
  const msg = e instanceof Error ? e.message : String(e);
20801
20958
  gaLog("error", "sync.fetch-failed", { projectId: project.id, runId, error: msg });
20802
- app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq22(runs.id, runId)).run();
20959
+ app.db.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq23(runs.id, runId)).run();
20803
20960
  throw e;
20804
20961
  }
20805
20962
  });
@@ -20810,11 +20967,11 @@ async function ga4Routes(app, opts) {
20810
20967
  const window = parseWindow(request.query.window);
20811
20968
  const cutoff = windowCutoff(window);
20812
20969
  const cutoffDate = cutoff?.slice(0, 10) ?? null;
20813
- const snapshotConditions = [eq22(gaTrafficSnapshots.projectId, project.id)];
20970
+ const snapshotConditions = [eq23(gaTrafficSnapshots.projectId, project.id)];
20814
20971
  if (cutoffDate) snapshotConditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
20815
- const aiConditions = [eq22(gaAiReferrals.projectId, project.id)];
20972
+ const aiConditions = [eq23(gaAiReferrals.projectId, project.id)];
20816
20973
  if (cutoffDate) aiConditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
20817
- const socialConditions = [eq22(gaSocialReferrals.projectId, project.id)];
20974
+ const socialConditions = [eq23(gaSocialReferrals.projectId, project.id)];
20818
20975
  if (cutoffDate) socialConditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
20819
20976
  const windowSummaryRow = cutoffDate ? app.db.select({
20820
20977
  totalSessions: gaTrafficWindowSummaries.totalSessions,
@@ -20823,8 +20980,8 @@ async function ga4Routes(app, opts) {
20823
20980
  totalUsers: gaTrafficWindowSummaries.totalUsers
20824
20981
  }).from(gaTrafficWindowSummaries).where(
20825
20982
  and16(
20826
- eq22(gaTrafficWindowSummaries.projectId, project.id),
20827
- eq22(gaTrafficWindowSummaries.windowKey, window)
20983
+ eq23(gaTrafficWindowSummaries.projectId, project.id),
20984
+ eq23(gaTrafficWindowSummaries.windowKey, window)
20828
20985
  )
20829
20986
  ).get() : null;
20830
20987
  const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
@@ -20836,14 +20993,14 @@ async function ga4Routes(app, opts) {
20836
20993
  totalSessions: gaTrafficSummaries.totalSessions,
20837
20994
  totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
20838
20995
  totalUsers: gaTrafficSummaries.totalUsers
20839
- }).from(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).get();
20996
+ }).from(gaTrafficSummaries).where(eq23(gaTrafficSummaries.projectId, project.id)).get();
20840
20997
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
20841
20998
  totalDirectSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
20842
20999
  }).from(gaTrafficSnapshots).where(and16(...snapshotConditions)).get();
20843
21000
  const summaryMeta = app.db.select({
20844
21001
  periodStart: gaTrafficSummaries.periodStart,
20845
21002
  periodEnd: gaTrafficSummaries.periodEnd
20846
- }).from(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).get();
21003
+ }).from(gaTrafficSummaries).where(eq23(gaTrafficSummaries.projectId, project.id)).get();
20847
21004
  const rows = app.db.select({
20848
21005
  landingPage: sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
20849
21006
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
@@ -20902,7 +21059,7 @@ async function ga4Routes(app, opts) {
20902
21059
  channelGroup: gaAiReferrals.channelGroup,
20903
21060
  sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
20904
21061
  users: sql9`COALESCE(SUM(${gaAiReferrals.users}), 0)`
20905
- }).from(gaAiReferrals).where(and16(...aiConditions, eq22(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
21062
+ }).from(gaAiReferrals).where(and16(...aiConditions, eq23(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
20906
21063
  const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
20907
21064
  let aiBySessionUsers = 0;
20908
21065
  for (const row of aiBySessionRows) {
@@ -20921,7 +21078,7 @@ async function ga4Routes(app, opts) {
20921
21078
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`,
20922
21079
  users: sql9`SUM(${gaSocialReferrals.users})`
20923
21080
  }).from(gaSocialReferrals).where(and16(...socialConditions)).get();
20924
- const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).orderBy(desc11(gaTrafficSummaries.syncedAt)).limit(1).get();
21081
+ const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq23(gaTrafficSummaries.projectId, project.id)).orderBy(desc12(gaTrafficSummaries.syncedAt)).limit(1).get();
20925
21082
  const total = summaryRow?.totalSessions ?? 0;
20926
21083
  const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
20927
21084
  const totalOrganicSessions = summaryRow?.totalOrganicSessions ?? 0;
@@ -21001,7 +21158,7 @@ async function ga4Routes(app, opts) {
21001
21158
  const project = resolveProject(app.db, request.params.name);
21002
21159
  requireGa4Connection(opts, project.name, project.canonicalDomain);
21003
21160
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
21004
- const conditions = [eq22(gaAiReferrals.projectId, project.id)];
21161
+ const conditions = [eq23(gaAiReferrals.projectId, project.id)];
21005
21162
  if (cutoffDate) conditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
21006
21163
  const rows = app.db.select({
21007
21164
  date: gaAiReferrals.date,
@@ -21024,7 +21181,7 @@ async function ga4Routes(app, opts) {
21024
21181
  const project = resolveProject(app.db, request.params.name);
21025
21182
  requireGa4Connection(opts, project.name, project.canonicalDomain);
21026
21183
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
21027
- const conditions = [eq22(gaSocialReferrals.projectId, project.id)];
21184
+ const conditions = [eq23(gaSocialReferrals.projectId, project.id)];
21028
21185
  if (cutoffDate) conditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
21029
21186
  const rows = app.db.select({
21030
21187
  date: gaSocialReferrals.date,
@@ -21047,7 +21204,7 @@ async function ga4Routes(app, opts) {
21047
21204
  return fmt(d);
21048
21205
  };
21049
21206
  const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and16(
21050
- eq22(gaSocialReferrals.projectId, project.id),
21207
+ eq23(gaSocialReferrals.projectId, project.id),
21051
21208
  sql9`${gaSocialReferrals.date} >= ${from}`,
21052
21209
  sql9`${gaSocialReferrals.date} < ${to}`
21053
21210
  )).get();
@@ -21060,7 +21217,7 @@ async function ga4Routes(app, opts) {
21060
21217
  source: gaSocialReferrals.source,
21061
21218
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`
21062
21219
  }).from(gaSocialReferrals).where(and16(
21063
- eq22(gaSocialReferrals.projectId, project.id),
21220
+ eq23(gaSocialReferrals.projectId, project.id),
21064
21221
  sql9`${gaSocialReferrals.date} >= ${daysAgo(7)}`,
21065
21222
  sql9`${gaSocialReferrals.date} < ${fmt(today)}`
21066
21223
  )).groupBy(gaSocialReferrals.source).all();
@@ -21068,7 +21225,7 @@ async function ga4Routes(app, opts) {
21068
21225
  source: gaSocialReferrals.source,
21069
21226
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`
21070
21227
  }).from(gaSocialReferrals).where(and16(
21071
- eq22(gaSocialReferrals.projectId, project.id),
21228
+ eq23(gaSocialReferrals.projectId, project.id),
21072
21229
  sql9`${gaSocialReferrals.date} >= ${daysAgo(14)}`,
21073
21230
  sql9`${gaSocialReferrals.date} < ${daysAgo(7)}`
21074
21231
  )).groupBy(gaSocialReferrals.source).all();
@@ -21109,16 +21266,16 @@ async function ga4Routes(app, opts) {
21109
21266
  return fmt(d);
21110
21267
  };
21111
21268
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
21112
- const sumTotal = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and16(eq22(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
21113
- const sumOrganic = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and16(eq22(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
21114
- const sumDirect = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and16(eq22(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
21269
+ const sumTotal = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and16(eq23(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
21270
+ const sumOrganic = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and16(eq23(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
21271
+ const sumDirect = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and16(eq23(gaTrafficSnapshots.projectId, project.id), sql9`${gaTrafficSnapshots.date} >= ${from}`, sql9`${gaTrafficSnapshots.date} < ${to}`)).get();
21115
21272
  const sumAi = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and16(
21116
- eq22(gaAiReferrals.projectId, project.id),
21273
+ eq23(gaAiReferrals.projectId, project.id),
21117
21274
  sql9`${gaAiReferrals.date} >= ${from}`,
21118
21275
  sql9`${gaAiReferrals.date} < ${to}`,
21119
- eq22(gaAiReferrals.sourceDimension, "session")
21276
+ eq23(gaAiReferrals.sourceDimension, "session")
21120
21277
  )).get();
21121
- const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and16(eq22(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${from}`, sql9`${gaSocialReferrals.date} < ${to}`)).get();
21278
+ const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and16(eq23(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${from}`, sql9`${gaSocialReferrals.date} < ${to}`)).get();
21122
21279
  const todayStr = fmt(today);
21123
21280
  const buildTrend = (sum) => {
21124
21281
  const c7 = sum(daysAgo(7), todayStr)?.sessions ?? 0;
@@ -21128,16 +21285,16 @@ async function ga4Routes(app, opts) {
21128
21285
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
21129
21286
  };
21130
21287
  const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and16(
21131
- eq22(gaAiReferrals.projectId, project.id),
21288
+ eq23(gaAiReferrals.projectId, project.id),
21132
21289
  sql9`${gaAiReferrals.date} >= ${daysAgo(7)}`,
21133
21290
  sql9`${gaAiReferrals.date} < ${todayStr}`,
21134
- eq22(gaAiReferrals.sourceDimension, "session")
21291
+ eq23(gaAiReferrals.sourceDimension, "session")
21135
21292
  )).groupBy(gaAiReferrals.source).all();
21136
21293
  const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and16(
21137
- eq22(gaAiReferrals.projectId, project.id),
21294
+ eq23(gaAiReferrals.projectId, project.id),
21138
21295
  sql9`${gaAiReferrals.date} >= ${daysAgo(14)}`,
21139
21296
  sql9`${gaAiReferrals.date} < ${daysAgo(7)}`,
21140
- eq22(gaAiReferrals.sourceDimension, "session")
21297
+ eq23(gaAiReferrals.sourceDimension, "session")
21141
21298
  )).groupBy(gaAiReferrals.source).all();
21142
21299
  const findBiggestMover = (current, prev) => {
21143
21300
  const prevMap = new Map(prev.map((r) => [r.source, r.sessions]));
@@ -21153,8 +21310,8 @@ async function ga4Routes(app, opts) {
21153
21310
  }
21154
21311
  return mover;
21155
21312
  };
21156
- const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql9`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and16(eq22(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql9`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
21157
- const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql9`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and16(eq22(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql9`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
21313
+ const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql9`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and16(eq23(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql9`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
21314
+ const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql9`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and16(eq23(gaSocialReferrals.projectId, project.id), sql9`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql9`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
21158
21315
  return {
21159
21316
  total: buildTrend(sumTotal),
21160
21317
  organic: buildTrend(sumOrganic),
@@ -21169,7 +21326,7 @@ async function ga4Routes(app, opts) {
21169
21326
  const project = resolveProject(app.db, request.params.name);
21170
21327
  requireGa4Connection(opts, project.name, project.canonicalDomain);
21171
21328
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
21172
- const conditions = [eq22(gaTrafficSnapshots.projectId, project.id)];
21329
+ const conditions = [eq23(gaTrafficSnapshots.projectId, project.id)];
21173
21330
  if (cutoffDate) conditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
21174
21331
  const rows = app.db.select({
21175
21332
  date: gaTrafficSnapshots.date,
@@ -21192,7 +21349,7 @@ async function ga4Routes(app, opts) {
21192
21349
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
21193
21350
  organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
21194
21351
  users: sql9`SUM(${gaTrafficSnapshots.users})`
21195
- }).from(gaTrafficSnapshots).where(eq22(gaTrafficSnapshots.projectId, project.id)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
21352
+ }).from(gaTrafficSnapshots).where(eq23(gaTrafficSnapshots.projectId, project.id)).groupBy(sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql9`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
21196
21353
  return {
21197
21354
  pages: trafficPages.map((r) => ({
21198
21355
  landingPage: r.landingPage,
@@ -21326,7 +21483,7 @@ function parseSchemaPageEntry(entry) {
21326
21483
  }
21327
21484
 
21328
21485
  // ../integration-wordpress/src/wordpress-client.ts
21329
- import crypto19 from "crypto";
21486
+ import crypto20 from "crypto";
21330
21487
  function validateUsername(username) {
21331
21488
  if (!username || typeof username !== "string" || username.trim().length === 0) {
21332
21489
  throw new WordpressApiError("AUTH_INVALID", "Username is required and must be a non-empty string", 400);
@@ -21539,7 +21696,7 @@ function buildSnippet(content) {
21539
21696
  return `${text2.slice(0, 157)}...`;
21540
21697
  }
21541
21698
  function contentHash(content) {
21542
- return crypto19.createHash("sha256").update(content).digest("hex");
21699
+ return crypto20.createHash("sha256").update(content).digest("hex");
21543
21700
  }
21544
21701
  function buildAmbiguousSlugMessage(slug, pages) {
21545
21702
  const candidates = pages.map((page) => {
@@ -22828,8 +22985,8 @@ async function wordpressRoutes(app, opts) {
22828
22985
  }
22829
22986
 
22830
22987
  // ../api-routes/src/backlinks.ts
22831
- import crypto20 from "crypto";
22832
- import { and as and18, asc as asc2, desc as desc12, eq as eq23, sql as sql10 } from "drizzle-orm";
22988
+ import crypto21 from "crypto";
22989
+ import { and as and18, asc as asc2, desc as desc13, eq as eq24, sql as sql10 } from "drizzle-orm";
22833
22990
 
22834
22991
  // ../integration-commoncrawl/src/constants.ts
22835
22992
  import os2 from "os";
@@ -23316,8 +23473,8 @@ function mapRunRow(row) {
23316
23473
  };
23317
23474
  }
23318
23475
  function latestSummaryForProject(db, projectId, release) {
23319
- const condition = release ? and18(eq23(backlinkSummaries.projectId, projectId), eq23(backlinkSummaries.release, release)) : eq23(backlinkSummaries.projectId, projectId);
23320
- return db.select().from(backlinkSummaries).where(condition).orderBy(desc12(backlinkSummaries.queriedAt)).limit(1).get();
23476
+ const condition = release ? and18(eq24(backlinkSummaries.projectId, projectId), eq24(backlinkSummaries.release, release)) : eq24(backlinkSummaries.projectId, projectId);
23477
+ return db.select().from(backlinkSummaries).where(condition).orderBy(desc13(backlinkSummaries.queriedAt)).limit(1).get();
23321
23478
  }
23322
23479
  function parseExcludeCrawlers(value) {
23323
23480
  if (!value) return false;
@@ -23326,8 +23483,8 @@ function parseExcludeCrawlers(value) {
23326
23483
  }
23327
23484
  function computeFilteredSummary(db, base) {
23328
23485
  const baseDomainCondition = and18(
23329
- eq23(backlinkDomains.projectId, base.projectId),
23330
- eq23(backlinkDomains.release, base.release)
23486
+ eq24(backlinkDomains.projectId, base.projectId),
23487
+ eq24(backlinkDomains.release, base.release)
23331
23488
  );
23332
23489
  const filteredCondition = and18(baseDomainCondition, backlinkCrawlerExclusionClause());
23333
23490
  const unfilteredAgg = db.select({
@@ -23338,7 +23495,7 @@ function computeFilteredSummary(db, base) {
23338
23495
  count: sql10`count(*)`,
23339
23496
  total: sql10`coalesce(sum(${backlinkDomains.numHosts}), 0)`
23340
23497
  }).from(backlinkDomains).where(filteredCondition).get();
23341
- const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc12(backlinkDomains.numHosts)).limit(10).all();
23498
+ const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc13(backlinkDomains.numHosts)).limit(10).all();
23342
23499
  const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
23343
23500
  const totalHosts = Number(filteredAgg?.total ?? 0);
23344
23501
  const unfilteredLinkingDomains = Number(unfilteredAgg?.count ?? 0);
@@ -23398,7 +23555,7 @@ async function backlinksRoutes(app, opts) {
23398
23555
  "@duckdb/node-api is not installed. Run `canonry backlinks install` to enable the backlinks feature."
23399
23556
  );
23400
23557
  }
23401
- const existing = app.db.select().from(ccReleaseSyncs).where(eq23(ccReleaseSyncs.release, release)).get();
23558
+ const existing = app.db.select().from(ccReleaseSyncs).where(eq24(ccReleaseSyncs.release, release)).get();
23402
23559
  const now = (/* @__PURE__ */ new Date()).toISOString();
23403
23560
  if (existing) {
23404
23561
  if (NON_TERMINAL_SYNC_STATUSES.has(existing.status)) {
@@ -23409,12 +23566,12 @@ async function backlinksRoutes(app, opts) {
23409
23566
  phaseDetail: null,
23410
23567
  error: null,
23411
23568
  updatedAt: now
23412
- }).where(eq23(ccReleaseSyncs.id, existing.id)).run();
23569
+ }).where(eq24(ccReleaseSyncs.id, existing.id)).run();
23413
23570
  opts.onReleaseSyncRequested(existing.id, release);
23414
- const refreshed = app.db.select().from(ccReleaseSyncs).where(eq23(ccReleaseSyncs.id, existing.id)).get();
23571
+ const refreshed = app.db.select().from(ccReleaseSyncs).where(eq24(ccReleaseSyncs.id, existing.id)).get();
23415
23572
  return reply.status(200).send(mapSyncRow(refreshed));
23416
23573
  }
23417
- const id = crypto20.randomUUID();
23574
+ const id = crypto21.randomUUID();
23418
23575
  app.db.insert(ccReleaseSyncs).values({
23419
23576
  id,
23420
23577
  release,
@@ -23423,15 +23580,15 @@ async function backlinksRoutes(app, opts) {
23423
23580
  updatedAt: now
23424
23581
  }).run();
23425
23582
  opts.onReleaseSyncRequested(id, release);
23426
- const inserted = app.db.select().from(ccReleaseSyncs).where(eq23(ccReleaseSyncs.id, id)).get();
23583
+ const inserted = app.db.select().from(ccReleaseSyncs).where(eq24(ccReleaseSyncs.id, id)).get();
23427
23584
  return reply.status(201).send(mapSyncRow(inserted));
23428
23585
  });
23429
23586
  app.get("/backlinks/syncs/latest", async (_request, reply) => {
23430
- const row = app.db.select().from(ccReleaseSyncs).orderBy(desc12(ccReleaseSyncs.updatedAt)).limit(1).get();
23587
+ const row = app.db.select().from(ccReleaseSyncs).orderBy(desc13(ccReleaseSyncs.updatedAt)).limit(1).get();
23431
23588
  return reply.send(row ? mapSyncRow(row) : null);
23432
23589
  });
23433
23590
  app.get("/backlinks/syncs", async (_request, reply) => {
23434
- const rows = app.db.select().from(ccReleaseSyncs).orderBy(desc12(ccReleaseSyncs.updatedAt)).all();
23591
+ const rows = app.db.select().from(ccReleaseSyncs).orderBy(desc13(ccReleaseSyncs.updatedAt)).all();
23435
23592
  return reply.send(rows.map(mapSyncRow));
23436
23593
  });
23437
23594
  app.get("/backlinks/releases", async (_request, reply) => {
@@ -23471,7 +23628,7 @@ async function backlinksRoutes(app, opts) {
23471
23628
  throw validationError("Invalid release id");
23472
23629
  }
23473
23630
  const now = (/* @__PURE__ */ new Date()).toISOString();
23474
- const runId = crypto20.randomUUID();
23631
+ const runId = crypto21.randomUUID();
23475
23632
  app.db.insert(runs).values({
23476
23633
  id: runId,
23477
23634
  projectId: project.id,
@@ -23481,7 +23638,7 @@ async function backlinksRoutes(app, opts) {
23481
23638
  createdAt: now
23482
23639
  }).run();
23483
23640
  opts.onBacklinkExtractRequested(runId, project.id, release);
23484
- const run = app.db.select().from(runs).where(eq23(runs.id, runId)).get();
23641
+ const run = app.db.select().from(runs).where(eq24(runs.id, runId)).get();
23485
23642
  return reply.status(201).send(mapRunRow(run));
23486
23643
  });
23487
23644
  app.get(
@@ -23506,15 +23663,15 @@ async function backlinksRoutes(app, opts) {
23506
23663
  const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
23507
23664
  const excludeCrawlers = parseExcludeCrawlers(request.query.excludeCrawlers);
23508
23665
  const baseDomainCondition = and18(
23509
- eq23(backlinkDomains.projectId, project.id),
23510
- eq23(backlinkDomains.release, targetRelease)
23666
+ eq24(backlinkDomains.projectId, project.id),
23667
+ eq24(backlinkDomains.release, targetRelease)
23511
23668
  );
23512
23669
  const domainCondition = excludeCrawlers ? and18(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
23513
23670
  const totalRow = app.db.select({ count: sql10`count(*)` }).from(backlinkDomains).where(domainCondition).get();
23514
23671
  const rows = app.db.select({
23515
23672
  linkingDomain: backlinkDomains.linkingDomain,
23516
23673
  numHosts: backlinkDomains.numHosts
23517
- }).from(backlinkDomains).where(domainCondition).orderBy(desc12(backlinkDomains.numHosts)).limit(limit).offset(offset).all();
23674
+ }).from(backlinkDomains).where(domainCondition).orderBy(desc13(backlinkDomains.numHosts)).limit(limit).offset(offset).all();
23518
23675
  let summary = null;
23519
23676
  if (summaryRow) {
23520
23677
  summary = excludeCrawlers ? computeFilteredSummary(app.db, summaryRow) : mapSummaryRow(summaryRow);
@@ -23530,7 +23687,7 @@ async function backlinksRoutes(app, opts) {
23530
23687
  "/projects/:name/backlinks/history",
23531
23688
  async (request, reply) => {
23532
23689
  const project = resolveProject(app.db, request.params.name);
23533
- const rows = app.db.select().from(backlinkSummaries).where(eq23(backlinkSummaries.projectId, project.id)).orderBy(asc2(backlinkSummaries.queriedAt)).all();
23690
+ const rows = app.db.select().from(backlinkSummaries).where(eq24(backlinkSummaries.projectId, project.id)).orderBy(asc2(backlinkSummaries.queriedAt)).all();
23534
23691
  const response = rows.map((r) => ({
23535
23692
  release: r.release,
23536
23693
  totalLinkingDomains: r.totalLinkingDomains,
@@ -23544,12 +23701,12 @@ async function backlinksRoutes(app, opts) {
23544
23701
  }
23545
23702
 
23546
23703
  // ../api-routes/src/traffic.ts
23547
- import crypto22 from "crypto";
23704
+ import crypto23 from "crypto";
23548
23705
  import { Agent as UndiciAgent } from "undici";
23549
- import { and as and19, desc as desc13, eq as eq24, gte as gte3, lte as lte2, sql as sql11 } from "drizzle-orm";
23706
+ import { and as and19, desc as desc14, eq as eq25, gte as gte3, lte as lte2, sql as sql11 } from "drizzle-orm";
23550
23707
 
23551
23708
  // ../integration-cloud-run/src/auth.ts
23552
- import crypto21 from "crypto";
23709
+ import crypto22 from "crypto";
23553
23710
  var GOOGLE_TOKEN_URL3 = "https://oauth2.googleapis.com/token";
23554
23711
  var CLOUD_LOGGING_READ_SCOPE = "https://www.googleapis.com/auth/logging.read";
23555
23712
  var TOKEN_REQUEST_TIMEOUT_MS = 3e4;
@@ -23578,7 +23735,7 @@ function createServiceAccountJwt2(clientEmail, privateKey, scope) {
23578
23735
  const headerB64 = encode(header);
23579
23736
  const payloadB64 = encode(payload);
23580
23737
  const signingInput = `${headerB64}.${payloadB64}`;
23581
- const sign = crypto21.createSign("RSA-SHA256");
23738
+ const sign = crypto22.createSign("RSA-SHA256");
23582
23739
  sign.update(signingInput);
23583
23740
  const signature = sign.sign(privateKey, "base64url");
23584
23741
  return `${signingInput}.${signature}`;
@@ -27339,8 +27496,8 @@ async function runBackfillTask(options) {
27339
27496
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
27340
27497
  try {
27341
27498
  app.db.transaction((tx) => {
27342
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq24(runs.id, runId)).run();
27343
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq24(trafficSources.id, sourceRow.id)).run();
27499
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq25(runs.id, runId)).run();
27500
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq25(trafficSources.id, sourceRow.id)).run();
27344
27501
  });
27345
27502
  } catch {
27346
27503
  }
@@ -27355,7 +27512,7 @@ async function runBackfillTask(options) {
27355
27512
  if (allEvents.length === 0) {
27356
27513
  const finishedAt2 = (/* @__PURE__ */ new Date()).toISOString();
27357
27514
  try {
27358
- app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq24(runs.id, runId)).run();
27515
+ app.db.update(runs).set({ status: RunStatuses.completed, finishedAt: finishedAt2 }).where(eq25(runs.id, runId)).run();
27359
27516
  } catch {
27360
27517
  }
27361
27518
  return;
@@ -27378,28 +27535,28 @@ async function runBackfillTask(options) {
27378
27535
  app.db.transaction((tx) => {
27379
27536
  tx.delete(crawlerEventsHourly).where(
27380
27537
  and19(
27381
- eq24(crawlerEventsHourly.sourceId, sourceRow.id),
27538
+ eq25(crawlerEventsHourly.sourceId, sourceRow.id),
27382
27539
  gte3(crawlerEventsHourly.tsHour, windowStartIso),
27383
27540
  lte2(crawlerEventsHourly.tsHour, windowEndIso)
27384
27541
  )
27385
27542
  ).run();
27386
27543
  tx.delete(aiUserFetchEventsHourly).where(
27387
27544
  and19(
27388
- eq24(aiUserFetchEventsHourly.sourceId, sourceRow.id),
27545
+ eq25(aiUserFetchEventsHourly.sourceId, sourceRow.id),
27389
27546
  gte3(aiUserFetchEventsHourly.tsHour, windowStartIso),
27390
27547
  lte2(aiUserFetchEventsHourly.tsHour, windowEndIso)
27391
27548
  )
27392
27549
  ).run();
27393
27550
  tx.delete(aiReferralEventsHourly).where(
27394
27551
  and19(
27395
- eq24(aiReferralEventsHourly.sourceId, sourceRow.id),
27552
+ eq25(aiReferralEventsHourly.sourceId, sourceRow.id),
27396
27553
  gte3(aiReferralEventsHourly.tsHour, windowStartIso),
27397
27554
  lte2(aiReferralEventsHourly.tsHour, windowEndIso)
27398
27555
  )
27399
27556
  ).run();
27400
27557
  tx.delete(rawEventSamples).where(
27401
27558
  and19(
27402
- eq24(rawEventSamples.sourceId, sourceRow.id),
27559
+ eq25(rawEventSamples.sourceId, sourceRow.id),
27403
27560
  gte3(rawEventSamples.ts, windowStartIso),
27404
27561
  lte2(rawEventSamples.ts, windowEndIso)
27405
27562
  )
@@ -27464,7 +27621,7 @@ async function runBackfillTask(options) {
27464
27621
  }
27465
27622
  })();
27466
27623
  tx.insert(rawEventSamples).values({
27467
- id: crypto22.randomUUID(),
27624
+ id: crypto23.randomUUID(),
27468
27625
  projectId: project.id,
27469
27626
  sourceId: sourceRow.id,
27470
27627
  ts: sample.observedAt,
@@ -27488,8 +27645,8 @@ async function runBackfillTask(options) {
27488
27645
  lastError: null,
27489
27646
  lastEventIds: newRingBuffer,
27490
27647
  updatedAt: finishedAt
27491
- }).where(eq24(trafficSources.id, sourceRow.id)).run();
27492
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq24(runs.id, runId)).run();
27648
+ }).where(eq25(trafficSources.id, sourceRow.id)).run();
27649
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq25(runs.id, runId)).run();
27493
27650
  });
27494
27651
  } catch (e) {
27495
27652
  markFailed(`Backfill rollup write failed: ${e instanceof Error ? e.message : String(e)}`);
@@ -27570,7 +27727,7 @@ async function trafficRoutes(app, opts) {
27570
27727
  createdAt: existing?.createdAt ?? now,
27571
27728
  updatedAt: now
27572
27729
  });
27573
- const activeSource = app.db.select().from(trafficSources).where(eq24(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes["cloud-run"] && row.status !== TrafficSourceStatuses.archived);
27730
+ const activeSource = app.db.select().from(trafficSources).where(eq25(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes["cloud-run"] && row.status !== TrafficSourceStatuses.archived);
27574
27731
  const config = {
27575
27732
  gcpProjectId,
27576
27733
  serviceName: serviceName ?? null,
@@ -27586,10 +27743,10 @@ async function trafficRoutes(app, opts) {
27586
27743
  lastError: null,
27587
27744
  configJson: config,
27588
27745
  updatedAt: now
27589
- }).where(eq24(trafficSources.id, activeSource.id)).run();
27590
- sourceRow = app.db.select().from(trafficSources).where(eq24(trafficSources.id, activeSource.id)).get();
27746
+ }).where(eq25(trafficSources.id, activeSource.id)).run();
27747
+ sourceRow = app.db.select().from(trafficSources).where(eq25(trafficSources.id, activeSource.id)).get();
27591
27748
  } else {
27592
- const newId = crypto22.randomUUID();
27749
+ const newId = crypto23.randomUUID();
27593
27750
  app.db.insert(trafficSources).values({
27594
27751
  id: newId,
27595
27752
  projectId: project.id,
@@ -27604,7 +27761,7 @@ async function trafficRoutes(app, opts) {
27604
27761
  createdAt: now,
27605
27762
  updatedAt: now
27606
27763
  }).run();
27607
- sourceRow = app.db.select().from(trafficSources).where(eq24(trafficSources.id, newId)).get();
27764
+ sourceRow = app.db.select().from(trafficSources).where(eq25(trafficSources.id, newId)).get();
27608
27765
  }
27609
27766
  writeAuditLog(app.db, {
27610
27767
  projectId: project.id,
@@ -27656,7 +27813,7 @@ async function trafficRoutes(app, opts) {
27656
27813
  createdAt: existing?.createdAt ?? now,
27657
27814
  updatedAt: now
27658
27815
  });
27659
- const activeSource = app.db.select().from(trafficSources).where(eq24(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
27816
+ const activeSource = app.db.select().from(trafficSources).where(eq25(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.wordpress && row.status !== TrafficSourceStatuses.archived);
27660
27817
  const config = { baseUrl, username };
27661
27818
  const fallbackName = displayName ?? `WordPress \xB7 ${new URL(baseUrl).host}`;
27662
27819
  let sourceRow;
@@ -27667,10 +27824,10 @@ async function trafficRoutes(app, opts) {
27667
27824
  lastError: null,
27668
27825
  configJson: config,
27669
27826
  updatedAt: now
27670
- }).where(eq24(trafficSources.id, activeSource.id)).run();
27671
- sourceRow = app.db.select().from(trafficSources).where(eq24(trafficSources.id, activeSource.id)).get();
27827
+ }).where(eq25(trafficSources.id, activeSource.id)).run();
27828
+ sourceRow = app.db.select().from(trafficSources).where(eq25(trafficSources.id, activeSource.id)).get();
27672
27829
  } else {
27673
- const newId = crypto22.randomUUID();
27830
+ const newId = crypto23.randomUUID();
27674
27831
  app.db.insert(trafficSources).values({
27675
27832
  id: newId,
27676
27833
  projectId: project.id,
@@ -27685,7 +27842,7 @@ async function trafficRoutes(app, opts) {
27685
27842
  createdAt: now,
27686
27843
  updatedAt: now
27687
27844
  }).run();
27688
- sourceRow = app.db.select().from(trafficSources).where(eq24(trafficSources.id, newId)).get();
27845
+ sourceRow = app.db.select().from(trafficSources).where(eq25(trafficSources.id, newId)).get();
27689
27846
  }
27690
27847
  writeAuditLog(app.db, {
27691
27848
  projectId: project.id,
@@ -27739,7 +27896,7 @@ async function trafficRoutes(app, opts) {
27739
27896
  createdAt: existing?.createdAt ?? now,
27740
27897
  updatedAt: now
27741
27898
  });
27742
- const activeSource = app.db.select().from(trafficSources).where(eq24(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.vercel && row.status !== TrafficSourceStatuses.archived);
27899
+ const activeSource = app.db.select().from(trafficSources).where(eq25(trafficSources.projectId, project.id)).all().find((row) => row.sourceType === TrafficSourceTypes.vercel && row.status !== TrafficSourceStatuses.archived);
27743
27900
  const config = { projectId, teamId, environment };
27744
27901
  const fallbackName = displayName ?? `Vercel \xB7 ${projectId}`;
27745
27902
  let sourceRow;
@@ -27750,10 +27907,10 @@ async function trafficRoutes(app, opts) {
27750
27907
  lastError: null,
27751
27908
  configJson: config,
27752
27909
  updatedAt: now
27753
- }).where(eq24(trafficSources.id, activeSource.id)).run();
27754
- sourceRow = app.db.select().from(trafficSources).where(eq24(trafficSources.id, activeSource.id)).get();
27910
+ }).where(eq25(trafficSources.id, activeSource.id)).run();
27911
+ sourceRow = app.db.select().from(trafficSources).where(eq25(trafficSources.id, activeSource.id)).get();
27755
27912
  } else {
27756
- const newId = crypto22.randomUUID();
27913
+ const newId = crypto23.randomUUID();
27757
27914
  app.db.insert(trafficSources).values({
27758
27915
  id: newId,
27759
27916
  projectId: project.id,
@@ -27776,7 +27933,7 @@ async function trafficRoutes(app, opts) {
27776
27933
  createdAt: now,
27777
27934
  updatedAt: now
27778
27935
  }).run();
27779
- sourceRow = app.db.select().from(trafficSources).where(eq24(trafficSources.id, newId)).get();
27936
+ sourceRow = app.db.select().from(trafficSources).where(eq25(trafficSources.id, newId)).get();
27780
27937
  }
27781
27938
  writeAuditLog(app.db, {
27782
27939
  projectId: project.id,
@@ -27789,7 +27946,7 @@ async function trafficRoutes(app, opts) {
27789
27946
  });
27790
27947
  app.post("/projects/:name/traffic/sources/:id/sync", async (request) => {
27791
27948
  const project = resolveProject(app.db, request.params.name);
27792
- const sourceRow = app.db.select().from(trafficSources).where(eq24(trafficSources.id, request.params.id)).get();
27949
+ const sourceRow = app.db.select().from(trafficSources).where(eq25(trafficSources.id, request.params.id)).get();
27793
27950
  if (!sourceRow || sourceRow.projectId !== project.id) {
27794
27951
  throw notFound("Traffic source", request.params.id);
27795
27952
  }
@@ -27801,7 +27958,7 @@ async function trafficRoutes(app, opts) {
27801
27958
  const windowEnd = /* @__PURE__ */ new Date();
27802
27959
  const startedAt = windowEnd.toISOString();
27803
27960
  const syncStartedAtMs = windowEnd.getTime();
27804
- const runId = crypto22.randomUUID();
27961
+ const runId = crypto23.randomUUID();
27805
27962
  app.db.insert(runs).values({
27806
27963
  id: runId,
27807
27964
  projectId: project.id,
@@ -27815,8 +27972,8 @@ async function trafficRoutes(app, opts) {
27815
27972
  const markFailed = (msg, errorCode) => {
27816
27973
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
27817
27974
  app.db.transaction((tx) => {
27818
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq24(runs.id, runId)).run();
27819
- tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq24(trafficSources.id, sourceRow.id)).run();
27975
+ tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq25(runs.id, runId)).run();
27976
+ tx.update(trafficSources).set({ status: TrafficSourceStatuses.error, lastError: msg, updatedAt: failedAt }).where(eq25(trafficSources.id, sourceRow.id)).run();
27820
27977
  });
27821
27978
  try {
27822
27979
  opts.onTrafficSynced?.({
@@ -27896,7 +28053,7 @@ async function trafficRoutes(app, opts) {
27896
28053
  }
27897
28054
  const credential = credentialStore.getConnection(project.name);
27898
28055
  if (!credential) {
27899
- app.db.delete(runs).where(eq24(runs.id, runId)).run();
28056
+ app.db.delete(runs).where(eq25(runs.id, runId)).run();
27900
28057
  throw validationError(
27901
28058
  `No WordPress credential found for project "${project.name}". Run "canonry traffic connect wordpress" first.`
27902
28059
  );
@@ -27945,12 +28102,12 @@ async function trafficRoutes(app, opts) {
27945
28102
  auditAction = "traffic.vercel.synced";
27946
28103
  const credentialStore = opts.vercelTrafficCredentialStore;
27947
28104
  if (!credentialStore) {
27948
- app.db.delete(runs).where(eq24(runs.id, runId)).run();
28105
+ app.db.delete(runs).where(eq25(runs.id, runId)).run();
27949
28106
  throw validationError("Vercel traffic credential storage is not configured for this deployment");
27950
28107
  }
27951
28108
  const credential = credentialStore.getConnection(project.name);
27952
28109
  if (!credential) {
27953
- app.db.delete(runs).where(eq24(runs.id, runId)).run();
28110
+ app.db.delete(runs).where(eq25(runs.id, runId)).run();
27954
28111
  throw validationError(
27955
28112
  `No Vercel credential found for project "${project.name}". Run "canonry traffic connect vercel" first.`
27956
28113
  );
@@ -28010,7 +28167,7 @@ async function trafficRoutes(app, opts) {
28010
28167
  let aiReferralHitsCount = 0;
28011
28168
  let unknownHitsCount = 0;
28012
28169
  app.db.transaction((tx) => {
28013
- const latestRow = tx.select().from(trafficSources).where(eq24(trafficSources.id, sourceRow.id)).get();
28170
+ const latestRow = tx.select().from(trafficSources).where(eq25(trafficSources.id, sourceRow.id)).get();
28014
28171
  const previousIds = latestRow.lastEventIds ?? [];
28015
28172
  const seenEventIds = new Set(previousIds);
28016
28173
  const dedupedEvents = seenEventIds.size === 0 ? allEvents : allEvents.filter((e) => !seenEventIds.has(e.eventId));
@@ -28143,7 +28300,7 @@ async function trafficRoutes(app, opts) {
28143
28300
  }
28144
28301
  })();
28145
28302
  tx.insert(rawEventSamples).values({
28146
- id: crypto22.randomUUID(),
28303
+ id: crypto23.randomUUID(),
28147
28304
  projectId: project.id,
28148
28305
  sourceId: sourceRow.id,
28149
28306
  ts: sample.observedAt,
@@ -28176,8 +28333,8 @@ async function trafficRoutes(app, opts) {
28176
28333
  if (sourceRow.sourceType === TrafficSourceTypes.wordpress) {
28177
28334
  sourceUpdate.lastCursor = nextCursor ?? null;
28178
28335
  }
28179
- tx.update(trafficSources).set(sourceUpdate).where(eq24(trafficSources.id, sourceRow.id)).run();
28180
- tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq24(runs.id, runId)).run();
28336
+ tx.update(trafficSources).set(sourceUpdate).where(eq25(trafficSources.id, sourceRow.id)).run();
28337
+ tx.update(runs).set({ status: RunStatuses.completed, finishedAt }).where(eq25(runs.id, runId)).run();
28181
28338
  });
28182
28339
  writeAuditLog(app.db, {
28183
28340
  projectId: project.id,
@@ -28227,7 +28384,7 @@ async function trafficRoutes(app, opts) {
28227
28384
  });
28228
28385
  app.post("/projects/:name/traffic/sources/:id/backfill", async (request) => {
28229
28386
  const project = resolveProject(app.db, request.params.name);
28230
- const sourceRow = app.db.select().from(trafficSources).where(eq24(trafficSources.id, request.params.id)).get();
28387
+ const sourceRow = app.db.select().from(trafficSources).where(eq25(trafficSources.id, request.params.id)).get();
28231
28388
  if (!sourceRow || sourceRow.projectId !== project.id) {
28232
28389
  throw notFound("Traffic source", request.params.id);
28233
28390
  }
@@ -28377,7 +28534,7 @@ async function trafficRoutes(app, opts) {
28377
28534
  };
28378
28535
  }
28379
28536
  const startedAt = windowEnd.toISOString();
28380
- const runId = crypto22.randomUUID();
28537
+ const runId = crypto23.randomUUID();
28381
28538
  app.db.insert(runs).values({
28382
28539
  id: runId,
28383
28540
  projectId: project.id,
@@ -28413,35 +28570,35 @@ async function trafficRoutes(app, opts) {
28413
28570
  function buildSourceDetail(projectId, row, since) {
28414
28571
  const crawlerTotals = app.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
28415
28572
  and19(
28416
- eq24(crawlerEventsHourly.sourceId, row.id),
28573
+ eq25(crawlerEventsHourly.sourceId, row.id),
28417
28574
  gte3(crawlerEventsHourly.tsHour, since)
28418
28575
  )
28419
28576
  ).get();
28420
28577
  const aiUserFetchTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
28421
28578
  and19(
28422
- eq24(aiUserFetchEventsHourly.sourceId, row.id),
28579
+ eq25(aiUserFetchEventsHourly.sourceId, row.id),
28423
28580
  gte3(aiUserFetchEventsHourly.tsHour, since)
28424
28581
  )
28425
28582
  ).get();
28426
28583
  const aiTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
28427
28584
  and19(
28428
- eq24(aiReferralEventsHourly.sourceId, row.id),
28585
+ eq25(aiReferralEventsHourly.sourceId, row.id),
28429
28586
  gte3(aiReferralEventsHourly.tsHour, since)
28430
28587
  )
28431
28588
  ).get();
28432
28589
  const sampleTotals = app.db.select({ total: sql11`COUNT(*)` }).from(rawEventSamples).where(
28433
28590
  and19(
28434
- eq24(rawEventSamples.sourceId, row.id),
28591
+ eq25(rawEventSamples.sourceId, row.id),
28435
28592
  gte3(rawEventSamples.ts, since)
28436
28593
  )
28437
28594
  ).get();
28438
28595
  const latestRun = app.db.select().from(runs).where(
28439
28596
  and19(
28440
- eq24(runs.projectId, projectId),
28441
- eq24(runs.kind, RunKinds["traffic-sync"]),
28442
- eq24(runs.sourceId, row.id)
28597
+ eq25(runs.projectId, projectId),
28598
+ eq25(runs.kind, RunKinds["traffic-sync"]),
28599
+ eq25(runs.sourceId, row.id)
28443
28600
  )
28444
- ).orderBy(desc13(runs.startedAt)).limit(1).get();
28601
+ ).orderBy(desc14(runs.startedAt)).limit(1).get();
28445
28602
  return {
28446
28603
  ...rowToDto(row),
28447
28604
  totals24h: {
@@ -28467,7 +28624,7 @@ async function trafficRoutes(app, opts) {
28467
28624
  "`advanceToNow` must be `true`. There is no implicit reset."
28468
28625
  );
28469
28626
  }
28470
- const sourceRow = app.db.select().from(trafficSources).where(and19(eq24(trafficSources.projectId, project.id), eq24(trafficSources.id, request.params.id))).get();
28627
+ const sourceRow = app.db.select().from(trafficSources).where(and19(eq25(trafficSources.projectId, project.id), eq25(trafficSources.id, request.params.id))).get();
28471
28628
  if (!sourceRow) {
28472
28629
  throw notFound("traffic source", request.params.id);
28473
28630
  }
@@ -28484,7 +28641,7 @@ async function trafficRoutes(app, opts) {
28484
28641
  status: TrafficSourceStatuses.connected,
28485
28642
  lastError: null,
28486
28643
  updatedAt: now
28487
- }).where(eq24(trafficSources.id, sourceRow.id)).run();
28644
+ }).where(eq25(trafficSources.id, sourceRow.id)).run();
28488
28645
  writeAuditLog(tx, auditFromRequest(request, {
28489
28646
  projectId: project.id,
28490
28647
  actor: "api",
@@ -28492,20 +28649,20 @@ async function trafficRoutes(app, opts) {
28492
28649
  entityType: "traffic_source",
28493
28650
  entityId: sourceRow.id
28494
28651
  }));
28495
- updatedRow = tx.select().from(trafficSources).where(eq24(trafficSources.id, sourceRow.id)).get();
28652
+ updatedRow = tx.select().from(trafficSources).where(eq25(trafficSources.id, sourceRow.id)).get();
28496
28653
  });
28497
28654
  return buildSourceDetail(project.id, updatedRow, new Date(Date.now() - 24 * 60 * 6e4).toISOString());
28498
28655
  });
28499
28656
  app.get("/projects/:name/traffic/sources", async (request) => {
28500
28657
  const project = resolveProject(app.db, request.params.name);
28501
- const rows = app.db.select().from(trafficSources).where(eq24(trafficSources.projectId, project.id)).orderBy(desc13(trafficSources.createdAt)).all();
28658
+ const rows = app.db.select().from(trafficSources).where(eq25(trafficSources.projectId, project.id)).orderBy(desc14(trafficSources.createdAt)).all();
28502
28659
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map(rowToDto);
28503
28660
  const response = { sources };
28504
28661
  return response;
28505
28662
  });
28506
28663
  app.get("/projects/:name/traffic/status", async (request) => {
28507
28664
  const project = resolveProject(app.db, request.params.name);
28508
- const rows = app.db.select().from(trafficSources).where(eq24(trafficSources.projectId, project.id)).orderBy(desc13(trafficSources.createdAt)).all();
28665
+ const rows = app.db.select().from(trafficSources).where(eq25(trafficSources.projectId, project.id)).orderBy(desc14(trafficSources.createdAt)).all();
28509
28666
  const since = new Date(Date.now() - 24 * 60 * 6e4).toISOString();
28510
28667
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map((row) => buildSourceDetail(project.id, row, since));
28511
28668
  const response = { sources };
@@ -28515,7 +28672,7 @@ async function trafficRoutes(app, opts) {
28515
28672
  "/projects/:name/traffic/sources/:id",
28516
28673
  async (request) => {
28517
28674
  const project = resolveProject(app.db, request.params.name);
28518
- const row = app.db.select().from(trafficSources).where(eq24(trafficSources.id, request.params.id)).get();
28675
+ const row = app.db.select().from(trafficSources).where(eq25(trafficSources.id, request.params.id)).get();
28519
28676
  if (!row || row.projectId !== project.id) {
28520
28677
  throw notFound("Traffic source", request.params.id);
28521
28678
  }
@@ -28566,15 +28723,15 @@ async function trafficRoutes(app, opts) {
28566
28723
  let aiReferralTotal = 0;
28567
28724
  if (kind === "all" || kind === TrafficEventKinds.crawler) {
28568
28725
  const crawlerFilters = [
28569
- eq24(crawlerEventsHourly.projectId, project.id),
28726
+ eq25(crawlerEventsHourly.projectId, project.id),
28570
28727
  gte3(crawlerEventsHourly.tsHour, sinceIso),
28571
28728
  lte2(crawlerEventsHourly.tsHour, untilIso)
28572
28729
  ];
28573
- if (sourceIdParam) crawlerFilters.push(eq24(crawlerEventsHourly.sourceId, sourceIdParam));
28730
+ if (sourceIdParam) crawlerFilters.push(eq25(crawlerEventsHourly.sourceId, sourceIdParam));
28574
28731
  const crawlerWhere = and19(...crawlerFilters);
28575
28732
  const total = app.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
28576
28733
  crawlerTotal = Number(total?.total ?? 0);
28577
- const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc13(crawlerEventsHourly.tsHour)).limit(limit).all();
28734
+ const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc14(crawlerEventsHourly.tsHour)).limit(limit).all();
28578
28735
  for (const r of rows) {
28579
28736
  events.push({
28580
28737
  kind: TrafficEventKinds.crawler,
@@ -28591,15 +28748,15 @@ async function trafficRoutes(app, opts) {
28591
28748
  }
28592
28749
  if (kind === "all" || kind === TrafficEventKinds["ai-user-fetch"]) {
28593
28750
  const userFetchFilters = [
28594
- eq24(aiUserFetchEventsHourly.projectId, project.id),
28751
+ eq25(aiUserFetchEventsHourly.projectId, project.id),
28595
28752
  gte3(aiUserFetchEventsHourly.tsHour, sinceIso),
28596
28753
  lte2(aiUserFetchEventsHourly.tsHour, untilIso)
28597
28754
  ];
28598
- if (sourceIdParam) userFetchFilters.push(eq24(aiUserFetchEventsHourly.sourceId, sourceIdParam));
28755
+ if (sourceIdParam) userFetchFilters.push(eq25(aiUserFetchEventsHourly.sourceId, sourceIdParam));
28599
28756
  const userFetchWhere = and19(...userFetchFilters);
28600
28757
  const total = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(userFetchWhere).get();
28601
28758
  aiUserFetchTotal = Number(total?.total ?? 0);
28602
- const rows = app.db.select().from(aiUserFetchEventsHourly).where(userFetchWhere).orderBy(desc13(aiUserFetchEventsHourly.tsHour)).limit(limit).all();
28759
+ const rows = app.db.select().from(aiUserFetchEventsHourly).where(userFetchWhere).orderBy(desc14(aiUserFetchEventsHourly.tsHour)).limit(limit).all();
28603
28760
  for (const r of rows) {
28604
28761
  events.push({
28605
28762
  kind: TrafficEventKinds["ai-user-fetch"],
@@ -28616,15 +28773,15 @@ async function trafficRoutes(app, opts) {
28616
28773
  }
28617
28774
  if (kind === "all" || kind === TrafficEventKinds["ai-referral"]) {
28618
28775
  const aiFilters = [
28619
- eq24(aiReferralEventsHourly.projectId, project.id),
28776
+ eq25(aiReferralEventsHourly.projectId, project.id),
28620
28777
  gte3(aiReferralEventsHourly.tsHour, sinceIso),
28621
28778
  lte2(aiReferralEventsHourly.tsHour, untilIso)
28622
28779
  ];
28623
- if (sourceIdParam) aiFilters.push(eq24(aiReferralEventsHourly.sourceId, sourceIdParam));
28780
+ if (sourceIdParam) aiFilters.push(eq25(aiReferralEventsHourly.sourceId, sourceIdParam));
28624
28781
  const aiWhere = and19(...aiFilters);
28625
28782
  const total = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
28626
28783
  aiReferralTotal = Number(total?.total ?? 0);
28627
- const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc13(aiReferralEventsHourly.tsHour)).limit(limit).all();
28784
+ const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc14(aiReferralEventsHourly.tsHour)).limit(limit).all();
28628
28785
  for (const r of rows) {
28629
28786
  events.push({
28630
28787
  kind: TrafficEventKinds["ai-referral"],
@@ -28657,7 +28814,7 @@ async function trafficRoutes(app, opts) {
28657
28814
  }
28658
28815
 
28659
28816
  // ../api-routes/src/doctor/checks/agent.ts
28660
- import crypto23 from "crypto";
28817
+ import crypto24 from "crypto";
28661
28818
  import fs6 from "fs";
28662
28819
  import path7 from "path";
28663
28820
  var REQUIRED_SKILLS = ["canonry", "aero"];
@@ -28810,7 +28967,7 @@ function isInstalled(dir) {
28810
28967
  }
28811
28968
  function hashInstalledFile(filePath) {
28812
28969
  try {
28813
- return crypto23.createHash("sha256").update(fs6.readFileSync(filePath)).digest("hex");
28970
+ return crypto24.createHash("sha256").update(fs6.readFileSync(filePath)).digest("hex");
28814
28971
  } catch {
28815
28972
  return void 0;
28816
28973
  }
@@ -29112,7 +29269,7 @@ var ga4ConnectionCheck = {
29112
29269
  var GA_AUTH_CHECKS = [ga4ConnectionCheck];
29113
29270
 
29114
29271
  // ../api-routes/src/doctor/checks/gbp-auth.ts
29115
- import { and as and20, eq as eq25 } from "drizzle-orm";
29272
+ import { and as and20, eq as eq26 } from "drizzle-orm";
29116
29273
  var RECENT_SYNC_WARN_DAYS = 7;
29117
29274
  var RECENT_SYNC_FAIL_DAYS = 30;
29118
29275
  function skippedNoProject() {
@@ -29345,7 +29502,7 @@ var recentSyncCheck = {
29345
29502
  title: "GBP recent sync",
29346
29503
  run: (ctx) => {
29347
29504
  if (!ctx.project) return skippedNoProject();
29348
- const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and20(eq25(gbpLocations.projectId, ctx.project.id), eq25(gbpLocations.selected, true))).all();
29505
+ const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and20(eq26(gbpLocations.projectId, ctx.project.id), eq26(gbpLocations.selected, true))).all();
29349
29506
  if (selected.length === 0) {
29350
29507
  return {
29351
29508
  status: CheckStatuses.skipped,
@@ -29405,7 +29562,7 @@ var GBP_AUTH_CHECK_BY_ID = Object.fromEntries(
29405
29562
  );
29406
29563
 
29407
29564
  // ../api-routes/src/doctor/checks/places.ts
29408
- import { eq as eq26 } from "drizzle-orm";
29565
+ import { eq as eq27 } from "drizzle-orm";
29409
29566
  var apiKeyCheck = {
29410
29567
  id: "gbp.places.api-key",
29411
29568
  category: CheckCategories.auth,
@@ -29450,7 +29607,7 @@ var apiKeyCheck = {
29450
29607
  details: { tier: cfg.tier }
29451
29608
  };
29452
29609
  }
29453
- const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq26(gbpLocations.projectId, ctx.project.id)).all();
29610
+ const rows = ctx.db.select({ placeId: gbpLocations.placeId, selected: gbpLocations.selected }).from(gbpLocations).where(eq27(gbpLocations.projectId, ctx.project.id)).all();
29454
29611
  const selected = rows.filter((r) => r.selected);
29455
29612
  const locationsWithPlaceId = selected.filter((r) => Boolean(r.placeId)).length;
29456
29613
  const details = {
@@ -29889,7 +30046,7 @@ var RUNTIME_STATE_CHECKS = [
29889
30046
  ];
29890
30047
 
29891
30048
  // ../api-routes/src/doctor/checks/traffic-source.ts
29892
- import { and as and21, eq as eq27, gte as gte4, ne as ne4, sql as sql12 } from "drizzle-orm";
30049
+ import { and as and21, eq as eq28, gte as gte4, ne as ne4, sql as sql12 } from "drizzle-orm";
29893
30050
  var RECENT_DATA_WARN_DAYS = 7;
29894
30051
  var RECENT_DATA_FAIL_DAYS = 30;
29895
30052
  function skippedNoProject3() {
@@ -29904,7 +30061,7 @@ function loadProbes(ctx) {
29904
30061
  if (!ctx.project) return [];
29905
30062
  const rows = ctx.db.select().from(trafficSources).where(
29906
30063
  and21(
29907
- eq27(trafficSources.projectId, ctx.project.id),
30064
+ eq28(trafficSources.projectId, ctx.project.id),
29908
30065
  ne4(trafficSources.status, TrafficSourceStatuses.archived)
29909
30066
  )
29910
30067
  ).all();
@@ -29985,7 +30142,7 @@ var recentDataCheck = {
29985
30142
  const recentCrawlers = Number(
29986
30143
  ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
29987
30144
  and21(
29988
- eq27(crawlerEventsHourly.projectId, ctx.project.id),
30145
+ eq28(crawlerEventsHourly.projectId, ctx.project.id),
29989
30146
  gte4(crawlerEventsHourly.tsHour, warnCutoff)
29990
30147
  )
29991
30148
  ).get()?.total ?? 0
@@ -29993,7 +30150,7 @@ var recentDataCheck = {
29993
30150
  const recentReferrals = Number(
29994
30151
  ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
29995
30152
  and21(
29996
- eq27(aiReferralEventsHourly.projectId, ctx.project.id),
30153
+ eq28(aiReferralEventsHourly.projectId, ctx.project.id),
29997
30154
  gte4(aiReferralEventsHourly.tsHour, warnCutoff)
29998
30155
  )
29999
30156
  ).get()?.total ?? 0
@@ -30009,7 +30166,7 @@ var recentDataCheck = {
30009
30166
  const olderCrawlers = Number(
30010
30167
  ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
30011
30168
  and21(
30012
- eq27(crawlerEventsHourly.projectId, ctx.project.id),
30169
+ eq28(crawlerEventsHourly.projectId, ctx.project.id),
30013
30170
  gte4(crawlerEventsHourly.tsHour, failCutoff)
30014
30171
  )
30015
30172
  ).get()?.total ?? 0
@@ -30017,7 +30174,7 @@ var recentDataCheck = {
30017
30174
  const olderReferrals = Number(
30018
30175
  ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
30019
30176
  and21(
30020
- eq27(aiReferralEventsHourly.projectId, ctx.project.id),
30177
+ eq28(aiReferralEventsHourly.projectId, ctx.project.id),
30021
30178
  gte4(aiReferralEventsHourly.tsHour, failCutoff)
30022
30179
  )
30023
30180
  ).get()?.total ?? 0
@@ -30395,8 +30552,8 @@ async function doctorRoutes(app, opts) {
30395
30552
  }
30396
30553
 
30397
30554
  // ../api-routes/src/discovery/routes.ts
30398
- import crypto24 from "crypto";
30399
- import { and as and22, desc as desc14, eq as eq28, gte as gte5, inArray as inArray10 } from "drizzle-orm";
30555
+ import crypto25 from "crypto";
30556
+ import { and as and22, desc as desc15, eq as eq29, gte as gte5, inArray as inArray10 } from "drizzle-orm";
30400
30557
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
30401
30558
  async function discoveryRoutes(app, opts) {
30402
30559
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -30429,20 +30586,20 @@ async function discoveryRoutes(app, opts) {
30429
30586
  const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
30430
30587
  const decision = app.db.transaction((tx) => {
30431
30588
  const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and22(
30432
- eq28(discoverySessions.projectId, project.id),
30433
- eq28(discoverySessions.icpDescription, icpDescription),
30589
+ eq29(discoverySessions.projectId, project.id),
30590
+ eq29(discoverySessions.icpDescription, icpDescription),
30434
30591
  inArray10(discoverySessions.status, [
30435
30592
  DiscoverySessionStatuses.queued,
30436
30593
  DiscoverySessionStatuses.seeding,
30437
30594
  DiscoverySessionStatuses.probing
30438
30595
  ]),
30439
30596
  gte5(discoverySessions.createdAt, ageFloorIso)
30440
- )).orderBy(desc14(discoverySessions.createdAt)).get();
30597
+ )).orderBy(desc15(discoverySessions.createdAt)).get();
30441
30598
  if (existing && existing.runId) {
30442
30599
  return { reused: true, sessionId: existing.id, runId: existing.runId };
30443
30600
  }
30444
- const sessionId = crypto24.randomUUID();
30445
- const runId = crypto24.randomUUID();
30601
+ const sessionId = crypto25.randomUUID();
30602
+ const runId = crypto25.randomUUID();
30446
30603
  tx.insert(discoverySessions).values({
30447
30604
  id: sessionId,
30448
30605
  projectId: project.id,
@@ -30500,7 +30657,7 @@ async function discoveryRoutes(app, opts) {
30500
30657
  const project = resolveProject(app.db, request.params.name);
30501
30658
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
30502
30659
  const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? 50 : parsedLimit;
30503
- const rows = app.db.select().from(discoverySessions).where(eq28(discoverySessions.projectId, project.id)).orderBy(desc14(discoverySessions.createdAt)).limit(limit).all();
30660
+ const rows = app.db.select().from(discoverySessions).where(eq29(discoverySessions.projectId, project.id)).orderBy(desc15(discoverySessions.createdAt)).limit(limit).all();
30504
30661
  return reply.send(rows.map(serializeSession));
30505
30662
  }
30506
30663
  );
@@ -30508,11 +30665,11 @@ async function discoveryRoutes(app, opts) {
30508
30665
  "/projects/:name/discover/sessions/:id",
30509
30666
  async (request, reply) => {
30510
30667
  const project = resolveProject(app.db, request.params.name);
30511
- const session = app.db.select().from(discoverySessions).where(eq28(discoverySessions.id, request.params.id)).get();
30668
+ const session = app.db.select().from(discoverySessions).where(eq29(discoverySessions.id, request.params.id)).get();
30512
30669
  if (!session || session.projectId !== project.id) {
30513
30670
  throw notFound("Discovery session", request.params.id);
30514
30671
  }
30515
- const probeRows = app.db.select().from(discoveryProbes).where(eq28(discoveryProbes.sessionId, session.id)).all();
30672
+ const probeRows = app.db.select().from(discoveryProbes).where(eq29(discoveryProbes.sessionId, session.id)).all();
30516
30673
  const detail = {
30517
30674
  ...serializeSession(session),
30518
30675
  probes: probeRows.map(serializeProbe)
@@ -30524,12 +30681,12 @@ async function discoveryRoutes(app, opts) {
30524
30681
  "/projects/:name/discover/sessions/:id/promote",
30525
30682
  async (request, reply) => {
30526
30683
  const project = resolveProject(app.db, request.params.name);
30527
- const session = app.db.select().from(discoverySessions).where(eq28(discoverySessions.id, request.params.id)).get();
30684
+ const session = app.db.select().from(discoverySessions).where(eq29(discoverySessions.id, request.params.id)).get();
30528
30685
  if (!session || session.projectId !== project.id) {
30529
30686
  throw notFound("Discovery session", request.params.id);
30530
30687
  }
30531
- const probeRows = app.db.select().from(discoveryProbes).where(eq28(discoveryProbes.sessionId, session.id)).all();
30532
- const existingCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq28(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase());
30688
+ const probeRows = app.db.select().from(discoveryProbes).where(eq29(discoveryProbes.sessionId, session.id)).all();
30689
+ const existingCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq29(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase());
30533
30690
  const seenCompetitors = new Set(existingCompetitors);
30534
30691
  const cited = /* @__PURE__ */ new Set();
30535
30692
  const aspirational = /* @__PURE__ */ new Set();
@@ -30558,7 +30715,7 @@ async function discoveryRoutes(app, opts) {
30558
30715
  );
30559
30716
  app.post("/projects/:name/discover/sessions/:id/promote", async (request, reply) => {
30560
30717
  const project = resolveProject(app.db, request.params.name);
30561
- const session = app.db.select().from(discoverySessions).where(eq28(discoverySessions.id, request.params.id)).get();
30718
+ const session = app.db.select().from(discoverySessions).where(eq29(discoverySessions.id, request.params.id)).get();
30562
30719
  if (!session || session.projectId !== project.id) {
30563
30720
  throw notFound("Discovery session", request.params.id);
30564
30721
  }
@@ -30581,7 +30738,7 @@ async function discoveryRoutes(app, opts) {
30581
30738
  const bucketSet = new Set(buckets);
30582
30739
  const includeCompetitors = parsed.data.includeCompetitors ?? true;
30583
30740
  const competitorTypes = parsed.data.competitorTypes ?? DEFAULT_DISCOVERY_PROMOTE_COMPETITOR_TYPES;
30584
- const probeRows = app.db.select().from(discoveryProbes).where(eq28(discoveryProbes.sessionId, session.id)).all();
30741
+ const probeRows = app.db.select().from(discoveryProbes).where(eq29(discoveryProbes.sessionId, session.id)).all();
30585
30742
  const candidateQueries = /* @__PURE__ */ new Set();
30586
30743
  for (const probe of probeRows) {
30587
30744
  if (!probe.bucket) continue;
@@ -30589,7 +30746,7 @@ async function discoveryRoutes(app, opts) {
30589
30746
  if (bucket.success && bucketSet.has(bucket.data)) candidateQueries.add(probe.query);
30590
30747
  }
30591
30748
  const existingQueries = new Set(
30592
- app.db.select({ query: queries.query }).from(queries).where(eq28(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
30749
+ app.db.select({ query: queries.query }).from(queries).where(eq29(queries.projectId, project.id)).all().map((r) => r.query.toLowerCase())
30593
30750
  );
30594
30751
  const promotedQueries = [];
30595
30752
  const skippedQueries = [];
@@ -30605,7 +30762,7 @@ async function discoveryRoutes(app, opts) {
30605
30762
  const skippedCompetitors = [];
30606
30763
  if (includeCompetitors) {
30607
30764
  const existingCompetitors = new Set(
30608
- app.db.select({ domain: competitors.domain }).from(competitors).where(eq28(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase())
30765
+ app.db.select({ domain: competitors.domain }).from(competitors).where(eq29(competitors.projectId, project.id)).all().map((r) => r.domain.toLowerCase())
30609
30766
  );
30610
30767
  const competitorMap = parseCompetitorMap(session.competitorMap);
30611
30768
  for (const entry of selectEligibleCompetitors(competitorMap, competitorTypes)) {
@@ -30624,7 +30781,7 @@ async function discoveryRoutes(app, opts) {
30624
30781
  app.db.transaction((tx) => {
30625
30782
  for (const query of promotedQueries) {
30626
30783
  tx.insert(queries).values({
30627
- id: crypto24.randomUUID(),
30784
+ id: crypto25.randomUUID(),
30628
30785
  projectId: project.id,
30629
30786
  query,
30630
30787
  provenance,
@@ -30633,7 +30790,7 @@ async function discoveryRoutes(app, opts) {
30633
30790
  }
30634
30791
  for (const domain of promotedCompetitors) {
30635
30792
  tx.insert(competitors).values({
30636
- id: crypto24.randomUUID(),
30793
+ id: crypto25.randomUUID(),
30637
30794
  projectId: project.id,
30638
30795
  domain,
30639
30796
  provenance,
@@ -30707,8 +30864,8 @@ function selectEligibleCompetitors(competitorMap, competitorTypes) {
30707
30864
  }
30708
30865
 
30709
30866
  // ../api-routes/src/discovery/orchestrate.ts
30710
- import crypto25 from "crypto";
30711
- import { eq as eq29 } from "drizzle-orm";
30867
+ import crypto26 from "crypto";
30868
+ import { eq as eq30 } from "drizzle-orm";
30712
30869
  var DEFAULT_DEDUP_THRESHOLD = 0.85;
30713
30870
  var DEFAULT_MAX_PROBES = 100;
30714
30871
  var ABSOLUTE_MAX_PROBES = 500;
@@ -30763,7 +30920,7 @@ async function executeDiscovery(opts) {
30763
30920
  status: DiscoverySessionStatuses.seeding,
30764
30921
  dedupThreshold,
30765
30922
  startedAt
30766
- }).where(eq29(discoverySessions.id, opts.sessionId)).run();
30923
+ }).where(eq30(discoverySessions.id, opts.sessionId)).run();
30767
30924
  const seedResult = await opts.deps.seed({
30768
30925
  project: opts.project,
30769
30926
  icpDescription: opts.icpDescription,
@@ -30783,7 +30940,7 @@ async function executeDiscovery(opts) {
30783
30940
  seedProvider: seedResult.provider,
30784
30941
  seedCountRaw,
30785
30942
  seedCount
30786
- }).where(eq29(discoverySessions.id, opts.sessionId)).run();
30943
+ }).where(eq30(discoverySessions.id, opts.sessionId)).run();
30787
30944
  const probeRows = [];
30788
30945
  const buckets = { cited: 0, aspirational: 0, "wasted-surface": 0 };
30789
30946
  for (const query of probedCanonicals) {
@@ -30796,7 +30953,7 @@ async function executeDiscovery(opts) {
30796
30953
  probeRows.push({ citedDomains: probe.citedDomains, bucket });
30797
30954
  buckets[bucket]++;
30798
30955
  opts.db.insert(discoveryProbes).values({
30799
- id: crypto25.randomUUID(),
30956
+ id: crypto26.randomUUID(),
30800
30957
  sessionId: opts.sessionId,
30801
30958
  projectId: opts.project.id,
30802
30959
  query,
@@ -30823,7 +30980,7 @@ async function executeDiscovery(opts) {
30823
30980
  wastedCount: buckets["wasted-surface"],
30824
30981
  competitorMap,
30825
30982
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
30826
- }).where(eq29(discoverySessions.id, opts.sessionId)).run();
30983
+ }).where(eq30(discoverySessions.id, opts.sessionId)).run();
30827
30984
  return {
30828
30985
  buckets,
30829
30986
  competitorMap,
@@ -30837,7 +30994,7 @@ function markSessionFailed(db, sessionId, error) {
30837
30994
  status: DiscoverySessionStatuses.failed,
30838
30995
  error,
30839
30996
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
30840
- }).where(eq29(discoverySessions.id, sessionId)).run();
30997
+ }).where(eq30(discoverySessions.id, sessionId)).run();
30841
30998
  }
30842
30999
  function dedupeStrings(input) {
30843
31000
  const seen = /* @__PURE__ */ new Set();
@@ -30944,6 +31101,7 @@ async function apiRoutes(app, opts) {
30944
31101
  bing: opts.bingSettingsSummary,
30945
31102
  onBingUpdate: opts.onBingSettingsUpdate
30946
31103
  });
31104
+ await api.register(keysRoutes);
30947
31105
  await api.register(snapshotRoutes, {
30948
31106
  onSnapshotRequested: opts.onSnapshotRequested
30949
31107
  });
@@ -31165,7 +31323,7 @@ function buildTrafficSourceValidators(opts) {
31165
31323
  }
31166
31324
 
31167
31325
  // src/intelligence-service.ts
31168
- import crypto26 from "crypto";
31326
+ import crypto27 from "crypto";
31169
31327
 
31170
31328
  // src/logger.ts
31171
31329
  var IS_TTY = process.stdout.isTTY === true;
@@ -31397,15 +31555,15 @@ var IntelligenceService = class {
31397
31555
  analyzeAndPersist(runId, projectId) {
31398
31556
  const recentRuns = this.db.select().from(runs).where(
31399
31557
  and23(
31400
- eq30(runs.projectId, projectId),
31401
- or5(eq30(runs.status, "completed"), eq30(runs.status, "partial")),
31558
+ eq31(runs.projectId, projectId),
31559
+ or5(eq31(runs.status, "completed"), eq31(runs.status, "partial")),
31402
31560
  // Defensive: RunCoordinator already skips probes before this is
31403
31561
  // called, but if a future call site invokes analyzeAndPersist
31404
31562
  // directly for a probe, probes still must not pollute the
31405
31563
  // intelligence window.
31406
31564
  ne5(runs.trigger, RunTriggers.probe)
31407
31565
  )
31408
- ).orderBy(desc15(runs.finishedAt), desc15(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
31566
+ ).orderBy(desc16(runs.finishedAt), desc16(runs.createdAt)).limit(HISTORY_WINDOW_RUNS).all();
31409
31567
  if (recentRuns.length === 0) {
31410
31568
  log.info("intelligence.skip", { runId, reason: "no completed runs" });
31411
31569
  return null;
@@ -31480,7 +31638,7 @@ var IntelligenceService = class {
31480
31638
  * Returns the persisted insights so the coordinator can count critical/high.
31481
31639
  */
31482
31640
  analyzeAndPersistGbp(runId, projectId) {
31483
- const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq30(runs.id, runId)).get();
31641
+ const runRow = this.db.select({ createdAt: runs.createdAt, startedAt: runs.startedAt, finishedAt: runs.finishedAt }).from(runs).where(eq31(runs.id, runId)).get();
31484
31642
  if (!runRow) {
31485
31643
  log.info("gbp-intelligence.skip", { runId, reason: "run not found" });
31486
31644
  this.persistGbpInsights(runId, projectId, [], []);
@@ -31489,8 +31647,8 @@ var IntelligenceService = class {
31489
31647
  const windowStart = runRow.startedAt ?? runRow.createdAt;
31490
31648
  const windowEnd = runRow.finishedAt ?? (/* @__PURE__ */ new Date()).toISOString();
31491
31649
  const selected = this.db.select().from(gbpLocations).where(and23(
31492
- eq30(gbpLocations.projectId, projectId),
31493
- eq30(gbpLocations.selected, true),
31650
+ eq31(gbpLocations.projectId, projectId),
31651
+ eq31(gbpLocations.selected, true),
31494
31652
  gte6(gbpLocations.syncedAt, windowStart),
31495
31653
  lte3(gbpLocations.syncedAt, windowEnd)
31496
31654
  )).all();
@@ -31525,10 +31683,10 @@ var IntelligenceService = class {
31525
31683
  }
31526
31684
  /** Build the per-location signal bundle the GBP analyzer consumes. */
31527
31685
  buildGbpLocationSignals(projectId, locationName, displayName, fallbackDate) {
31528
- const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and23(eq30(gbpDailyMetrics.projectId, projectId), eq30(gbpDailyMetrics.locationName, locationName))).all();
31529
- const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and23(eq30(gbpPlaceActions.projectId, projectId), eq30(gbpPlaceActions.locationName, locationName))).all();
31530
- const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and23(eq30(gbpLodgingSnapshots.projectId, projectId), eq30(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc15(gbpLodgingSnapshots.syncedAt)).limit(1).get();
31531
- const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and23(eq30(gbpPlaceDetails.projectId, projectId), eq30(gbpPlaceDetails.locationName, locationName))).orderBy(desc15(gbpPlaceDetails.syncedAt)).limit(1).get();
31686
+ const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and23(eq31(gbpDailyMetrics.projectId, projectId), eq31(gbpDailyMetrics.locationName, locationName))).all();
31687
+ const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and23(eq31(gbpPlaceActions.projectId, projectId), eq31(gbpPlaceActions.locationName, locationName))).all();
31688
+ const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and23(eq31(gbpLodgingSnapshots.projectId, projectId), eq31(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc16(gbpLodgingSnapshots.syncedAt)).limit(1).get();
31689
+ const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and23(eq31(gbpPlaceDetails.projectId, projectId), eq31(gbpPlaceDetails.locationName, locationName))).orderBy(desc16(gbpPlaceDetails.syncedAt)).limit(1).get();
31532
31690
  const placesAmenities = placeRow ? extractPlaceAmenities(placeRow.attributes) : [];
31533
31691
  const summary = buildGbpSummary({
31534
31692
  locationName,
@@ -31560,7 +31718,7 @@ var IntelligenceService = class {
31560
31718
  /** Build the month-over-month keyword series for a location from the
31561
31719
  * accumulating gbp_keyword_monthly table (latest complete month vs prior). */
31562
31720
  buildGbpKeywordTrend(projectId, locationName) {
31563
- const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and23(eq30(gbpKeywordMonthly.projectId, projectId), eq30(gbpKeywordMonthly.locationName, locationName))).all();
31721
+ const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and23(eq31(gbpKeywordMonthly.projectId, projectId), eq31(gbpKeywordMonthly.locationName, locationName))).all();
31564
31722
  if (rows.length === 0) return { recentMonth: null, priorMonth: null, points: [] };
31565
31723
  const months = [...new Set(rows.map((r) => r.month))].sort().reverse();
31566
31724
  const recentMonth = months[0] ?? null;
@@ -31591,7 +31749,7 @@ var IntelligenceService = class {
31591
31749
  */
31592
31750
  persistGbpInsights(runId, projectId, gbpInsights, coveredLocationNames) {
31593
31751
  const covered = new Set(coveredLocationNames);
31594
- const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and23(eq30(insights.projectId, projectId), eq30(insights.provider, GBP_INSIGHT_PROVIDER))).all();
31752
+ const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and23(eq31(insights.projectId, projectId), eq31(insights.provider, GBP_INSIGHT_PROVIDER))).all();
31595
31753
  const staleIds = [];
31596
31754
  const dismissedSlots = /* @__PURE__ */ new Set();
31597
31755
  for (const row of existing) {
@@ -31602,7 +31760,7 @@ var IntelligenceService = class {
31602
31760
  }
31603
31761
  this.db.transaction((tx) => {
31604
31762
  for (const id of staleIds) {
31605
- tx.delete(insights).where(eq30(insights.id, id)).run();
31763
+ tx.delete(insights).where(eq31(insights.id, id)).run();
31606
31764
  }
31607
31765
  for (const insight of gbpInsights) {
31608
31766
  const parsed = parseGbpInsightId(insight.id);
@@ -31680,7 +31838,7 @@ var IntelligenceService = class {
31680
31838
  * create per run + aggregate). DB is left untouched.
31681
31839
  */
31682
31840
  backfill(projectName, opts, onProgress) {
31683
- const project = this.db.select().from(projects).where(eq30(projects.name, projectName)).get();
31841
+ const project = this.db.select().from(projects).where(eq31(projects.name, projectName)).get();
31684
31842
  if (!project) {
31685
31843
  throw new Error(`Project "${projectName}" not found`);
31686
31844
  }
@@ -31694,8 +31852,8 @@ var IntelligenceService = class {
31694
31852
  }
31695
31853
  const allRuns = this.db.select().from(runs).where(
31696
31854
  and23(
31697
- eq30(runs.projectId, project.id),
31698
- or5(eq30(runs.status, "completed"), eq30(runs.status, "partial")),
31855
+ eq31(runs.projectId, project.id),
31856
+ or5(eq31(runs.status, "completed"), eq31(runs.status, "partial")),
31699
31857
  // Backfill must not replay probe runs as if they were real sweeps.
31700
31858
  ne5(runs.trigger, RunTriggers.probe)
31701
31859
  )
@@ -31774,7 +31932,7 @@ var IntelligenceService = class {
31774
31932
  return { processed, skipped, totalInsights };
31775
31933
  }
31776
31934
  loadTrackedCompetitors(projectId) {
31777
- return this.db.select({ domain: competitors.domain }).from(competitors).where(eq30(competitors.projectId, projectId)).all().map((r) => r.domain);
31935
+ return this.db.select({ domain: competitors.domain }).from(competitors).where(eq31(competitors.projectId, projectId)).all().map((r) => r.domain);
31778
31936
  }
31779
31937
  /**
31780
31938
  * Wipe transition signals from an analysis result while keeping health.
@@ -31795,15 +31953,15 @@ var IntelligenceService = class {
31795
31953
  }
31796
31954
  persistResult(result, runId, projectId) {
31797
31955
  const previouslyDismissed = /* @__PURE__ */ new Set();
31798
- const existingInsights = this.db.select({ query: insights.query, provider: insights.provider, type: insights.type, dismissed: insights.dismissed }).from(insights).where(eq30(insights.runId, runId)).all();
31956
+ const existingInsights = this.db.select({ query: insights.query, provider: insights.provider, type: insights.type, dismissed: insights.dismissed }).from(insights).where(eq31(insights.runId, runId)).all();
31799
31957
  for (const row of existingInsights) {
31800
31958
  if (row.dismissed) {
31801
31959
  previouslyDismissed.add(`${row.query}:${row.provider}:${row.type}`);
31802
31960
  }
31803
31961
  }
31804
31962
  this.db.transaction((tx) => {
31805
- tx.delete(insights).where(eq30(insights.runId, runId)).run();
31806
- tx.delete(healthSnapshots).where(eq30(healthSnapshots.runId, runId)).run();
31963
+ tx.delete(insights).where(eq31(insights.runId, runId)).run();
31964
+ tx.delete(healthSnapshots).where(eq31(healthSnapshots.runId, runId)).run();
31807
31965
  const now = (/* @__PURE__ */ new Date()).toISOString();
31808
31966
  for (const insight of result.insights) {
31809
31967
  const wasDismissed = previouslyDismissed.has(`${insight.query}:${insight.provider}:${insight.type}`);
@@ -31823,7 +31981,7 @@ var IntelligenceService = class {
31823
31981
  }).run();
31824
31982
  }
31825
31983
  tx.insert(healthSnapshots).values({
31826
- id: crypto26.randomUUID(),
31984
+ id: crypto27.randomUUID(),
31827
31985
  projectId,
31828
31986
  runId,
31829
31987
  overallCitedRate: String(result.health.overallCitedRate),
@@ -31854,14 +32012,14 @@ var IntelligenceService = class {
31854
32012
  applySeverityTiering(rawInsights, excludeRunId, projectId) {
31855
32013
  const regressions = rawInsights.filter((i) => i.type === "regression");
31856
32014
  if (regressions.length === 0) return rawInsights;
31857
- const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq30(gscSearchData.projectId, projectId)).all();
32015
+ const gscRows = this.db.select({ query: gscSearchData.query, impressions: gscSearchData.impressions }).from(gscSearchData).where(eq31(gscSearchData.projectId, projectId)).all();
31858
32016
  const gscConnected = gscRows.length > 0;
31859
32017
  const gscImpressionsByQuery = /* @__PURE__ */ new Map();
31860
32018
  for (const row of gscRows) {
31861
32019
  const key = row.query.toLowerCase();
31862
32020
  gscImpressionsByQuery.set(key, (gscImpressionsByQuery.get(key) ?? 0) + row.impressions);
31863
32021
  }
31864
- const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq30(projects.id, projectId)).get();
32022
+ const projectRow = this.db.select({ locations: projects.locations }).from(projects).where(eq31(projects.id, projectId)).get();
31865
32023
  const locationCount = Math.max(
31866
32024
  1,
31867
32025
  (projectRow?.locations ?? []).length
@@ -31869,13 +32027,13 @@ var IntelligenceService = class {
31869
32027
  const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
31870
32028
  const recentRunRows = this.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
31871
32029
  and23(
31872
- eq30(runs.projectId, projectId),
31873
- eq30(runs.kind, RunKinds["answer-visibility"]),
31874
- or5(eq30(runs.status, "completed"), eq30(runs.status, "partial")),
32030
+ eq31(runs.projectId, projectId),
32031
+ eq31(runs.kind, RunKinds["answer-visibility"]),
32032
+ or5(eq31(runs.status, "completed"), eq31(runs.status, "partial")),
31875
32033
  // Defensive — see top of file.
31876
32034
  ne5(runs.trigger, RunTriggers.probe)
31877
32035
  )
31878
- ).orderBy(desc15(runs.createdAt), desc15(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
32036
+ ).orderBy(desc16(runs.createdAt), desc16(runs.id)).limit((RECURRENCE_LOOKBACK_RUNS + 1) * ROWS_PER_GROUP_BUDGET).all();
31879
32037
  const recentGroups = groupRunsByCreatedAt(recentRunRows);
31880
32038
  const recentRunIds = [];
31881
32039
  const recentRunIdToCreatedAt = /* @__PURE__ */ new Map();
@@ -31891,7 +32049,7 @@ var IntelligenceService = class {
31891
32049
  const haveHistory = recentRunIds.length > 0;
31892
32050
  const priorRegressionsByPair = /* @__PURE__ */ new Map();
31893
32051
  if (haveHistory) {
31894
- const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and23(eq30(insights.type, "regression"), inArray11(insights.runId, recentRunIds))).all();
32052
+ const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and23(eq31(insights.type, "regression"), inArray11(insights.runId, recentRunIds))).all();
31895
32053
  const regressionGroups = /* @__PURE__ */ new Map();
31896
32054
  for (const row of priorRows) {
31897
32055
  if (!row.runId) continue;
@@ -31920,7 +32078,7 @@ var IntelligenceService = class {
31920
32078
  });
31921
32079
  }
31922
32080
  buildRunData(runId, projectId, completedAt, location = null) {
31923
- const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq30(projects.id, projectId)).get();
32081
+ const projectDomainRow = this.db.select({ canonicalDomain: projects.canonicalDomain, ownedDomains: projects.ownedDomains }).from(projects).where(eq31(projects.id, projectId)).get();
31924
32082
  const projectDomains = projectDomainRow ? effectiveDomains({
31925
32083
  canonicalDomain: projectDomainRow.canonicalDomain,
31926
32084
  ownedDomains: projectDomainRow.ownedDomains
@@ -31936,7 +32094,7 @@ var IntelligenceService = class {
31936
32094
  citedDomains: querySnapshots.citedDomains,
31937
32095
  competitorOverlap: querySnapshots.competitorOverlap,
31938
32096
  snapshotLocation: querySnapshots.location
31939
- }).from(querySnapshots).leftJoin(queries, eq30(querySnapshots.queryId, queries.id)).where(eq30(querySnapshots.runId, runId)).all();
32097
+ }).from(querySnapshots).leftJoin(queries, eq31(querySnapshots.queryId, queries.id)).where(eq31(querySnapshots.runId, runId)).all();
31940
32098
  const snapshots = [];
31941
32099
  let orphanCount = 0;
31942
32100
  for (const r of rows) {