@ainyc/canonry 4.68.1 → 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 (24) hide show
  1. package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +20 -1
  2. package/assets/assets/{BacklinksPage-B9Q2r9zM.js → BacklinksPage-DLsgGSXv.js} +1 -1
  3. package/assets/assets/{ChartPrimitives-D-YWOWK-.js → ChartPrimitives-CERLI8Z-.js} +1 -1
  4. package/assets/assets/{ProjectPage-BXgxWKyT.js → ProjectPage-CaRqQ1vH.js} +1 -1
  5. package/assets/assets/{RunRow-CJpjUaht.js → RunRow-C3x47chX.js} +1 -1
  6. package/assets/assets/{RunsPage-DVl_pgqe.js → RunsPage-DRIlEete.js} +1 -1
  7. package/assets/assets/{SettingsPage-CAiVL2mo.js → SettingsPage-CHaqBpnQ.js} +1 -1
  8. package/assets/assets/{TrafficPage-B8dnHFqk.js → TrafficPage-UbL0daLy.js} +1 -1
  9. package/assets/assets/{TrafficSourceDetailPage-37s8p8eZ.js → TrafficSourceDetailPage-CjLDbjf_.js} +1 -1
  10. package/assets/assets/{extract-error-message-DwqbPRoa.js → extract-error-message-BndXGmUh.js} +1 -1
  11. package/assets/assets/{index-DFo2OL9c.js → index-BCwC5OlW.js} +39 -39
  12. package/assets/assets/{index-BquJzH0t.css → index-BUNCrWTe.css} +1 -1
  13. package/assets/assets/{server-traffic-CMFP8-x2.js → server-traffic-DOnVZFEw.js} +1 -1
  14. package/assets/assets/{trash-2-C4sYVIa6.js → trash-2-HEZdy4sJ.js} +1 -1
  15. package/assets/index.html +2 -2
  16. package/dist/{chunk-D75O5A27.js → chunk-5FM7QRYD.js} +1558 -1526
  17. package/dist/{chunk-GZYLAE6M.js → chunk-SBYA3LEJ.js} +4 -4
  18. package/dist/{chunk-Y24OE7R3.js → chunk-WFM2O72W.js} +494 -309
  19. package/dist/{chunk-YYFBMDLC.js → chunk-ZNWMVYYU.js} +53 -1
  20. package/dist/cli.js +197 -80
  21. package/dist/index.js +4 -4
  22. package/dist/{intelligence-service-5V2JWQ6K.js → intelligence-service-DVVSE3G7.js} +2 -2
  23. package/dist/mcp.js +2 -2
  24. package/package.json +7 -7
@@ -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,
@@ -12190,17 +12202,30 @@ function loadSnapshotsByRunIds(app, runIds) {
12190
12202
  }
12191
12203
  function summarizeFromSnapshots(snapshots) {
12192
12204
  const empty = {
12193
- queryCounts: { totalQueries: 0, citedQueries: 0, notCitedQueries: 0, citedRate: 0 },
12205
+ queryCounts: {
12206
+ totalQueries: 0,
12207
+ citedQueries: 0,
12208
+ notCitedQueries: 0,
12209
+ citedRate: 0,
12210
+ mentionedQueries: 0,
12211
+ notMentionedQueries: 0,
12212
+ mentionRate: 0
12213
+ },
12194
12214
  providers: []
12195
12215
  };
12196
12216
  if (snapshots.length === 0) return empty;
12197
12217
  const perQuery = /* @__PURE__ */ new Map();
12218
+ const perQueryMentioned = /* @__PURE__ */ new Map();
12198
12219
  const perProvider = /* @__PURE__ */ new Map();
12199
12220
  for (const snap of snapshots) {
12200
12221
  const cited = snap.citationState === CitationStates.cited;
12201
12222
  if (!perQuery.has(snap.queryId) || cited) {
12202
12223
  perQuery.set(snap.queryId, cited);
12203
12224
  }
12225
+ const mentioned = snap.answerMentioned === true;
12226
+ if (!perQueryMentioned.has(snap.queryId) || mentioned) {
12227
+ perQueryMentioned.set(snap.queryId, mentioned);
12228
+ }
12204
12229
  const bucket = perProvider.get(snap.provider) ?? { cited: 0, total: 0 };
12205
12230
  bucket.total += 1;
12206
12231
  if (cited) bucket.cited += 1;
@@ -12213,6 +12238,12 @@ function summarizeFromSnapshots(snapshots) {
12213
12238
  }
12214
12239
  const notCitedQueries = totalQueries - citedQueries;
12215
12240
  const citedRate = totalQueries === 0 ? 0 : Number((citedQueries / totalQueries).toFixed(4));
12241
+ let mentionedQueries = 0;
12242
+ for (const wasMentioned of perQueryMentioned.values()) {
12243
+ if (wasMentioned) mentionedQueries += 1;
12244
+ }
12245
+ const notMentionedQueries = totalQueries - mentionedQueries;
12246
+ const mentionRate = totalQueries === 0 ? 0 : Number((mentionedQueries / totalQueries).toFixed(4));
12216
12247
  const providers = [...perProvider.entries()].map(([provider, { cited, total }]) => ({
12217
12248
  provider,
12218
12249
  cited,
@@ -12220,7 +12251,15 @@ function summarizeFromSnapshots(snapshots) {
12220
12251
  citedRate: total === 0 ? 0 : Number((cited / total).toFixed(4))
12221
12252
  })).sort((a, b) => a.provider.localeCompare(b.provider));
12222
12253
  return {
12223
- queryCounts: { totalQueries, citedQueries, notCitedQueries, citedRate },
12254
+ queryCounts: {
12255
+ totalQueries,
12256
+ citedQueries,
12257
+ notCitedQueries,
12258
+ citedRate,
12259
+ mentionedQueries,
12260
+ notMentionedQueries,
12261
+ mentionRate
12262
+ },
12224
12263
  providers
12225
12264
  };
12226
12265
  }
@@ -12548,6 +12587,8 @@ function makeSnippet(text2, query) {
12548
12587
  import { z } from "zod";
12549
12588
  var SCHEMA_TABLE = {
12550
12589
  AgentProvidersResponseDto: agentProvidersResponseDtoSchema,
12590
+ ApiKeyDto: apiKeyDtoSchema,
12591
+ ApiKeyListDto: apiKeyListDtoSchema,
12551
12592
  AuditLogEntry: auditLogEntrySchema,
12552
12593
  BacklinkHistoryEntry: backlinkHistoryEntrySchema,
12553
12594
  BacklinkListResponse: backlinkListResponseSchema,
@@ -12575,6 +12616,8 @@ var SCHEMA_TABLE = {
12575
12616
  ContentTargetDismissalDto: contentTargetDismissalDtoSchema,
12576
12617
  ContentTargetDismissalsResponseDto: contentTargetDismissalsResponseDtoSchema,
12577
12618
  ContentTargetsResponseDto: contentTargetsResponseDtoSchema,
12619
+ CreateApiKeyRequest: createApiKeyRequestSchema,
12620
+ CreatedApiKeyDto: createdApiKeyDtoSchema,
12578
12621
  RecommendationExplanationDto: recommendationExplanationDtoSchema,
12579
12622
  DiscoveryPromotePreview: discoveryPromotePreviewSchema,
12580
12623
  DiscoveryPromoteResult: discoveryPromoteResultSchema,
@@ -12742,6 +12785,13 @@ var notificationIdParameter = {
12742
12785
  description: "Notification ID.",
12743
12786
  schema: stringSchema
12744
12787
  };
12788
+ var keyIdParameter = {
12789
+ name: "id",
12790
+ in: "path",
12791
+ required: true,
12792
+ description: "API key ID.",
12793
+ schema: stringSchema
12794
+ };
12745
12795
  var providerNameParameter = {
12746
12796
  name: "name",
12747
12797
  in: "path",
@@ -13665,6 +13715,50 @@ var routeCatalog = [
13665
13715
  501: errorResponse("Google settings updates are not supported.")
13666
13716
  }
13667
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
+ },
13668
13762
  {
13669
13763
  method: "post",
13670
13764
  path: "/api/v1/snapshot",
@@ -16514,6 +16608,96 @@ async function settingsRoutes(app, opts) {
16514
16608
  });
16515
16609
  }
16516
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
+
16517
16701
  // ../api-routes/src/snapshot.ts
16518
16702
  async function snapshotRoutes(app, opts) {
16519
16703
  app.post("/snapshot", async (request) => {
@@ -16573,8 +16757,8 @@ async function telemetryRoutes(app, opts) {
16573
16757
  }
16574
16758
 
16575
16759
  // ../api-routes/src/schedules.ts
16576
- import crypto12 from "crypto";
16577
- 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";
16578
16762
  function parseKindParam(raw) {
16579
16763
  if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
16580
16764
  const parsed = schedulableRunKindSchema.safeParse(raw);
@@ -16601,7 +16785,7 @@ async function scheduleRoutes(app, opts) {
16601
16785
  if (!sourceId) {
16602
16786
  throw validationError('"sourceId" is required when kind is "traffic-sync"');
16603
16787
  }
16604
- 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();
16605
16789
  if (!sourceRow || sourceRow.projectId !== project.id) {
16606
16790
  throw notFound("Traffic source", sourceId);
16607
16791
  }
@@ -16643,7 +16827,7 @@ async function scheduleRoutes(app, opts) {
16643
16827
  }
16644
16828
  const now = (/* @__PURE__ */ new Date()).toISOString();
16645
16829
  const enabledBool = enabled !== false;
16646
- 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();
16647
16831
  if (existing) {
16648
16832
  app.db.update(schedules).set({
16649
16833
  cronExpr,
@@ -16653,10 +16837,10 @@ async function scheduleRoutes(app, opts) {
16653
16837
  sourceId: sourceId ?? null,
16654
16838
  enabled: enabledBool,
16655
16839
  updatedAt: now
16656
- }).where(eq17(schedules.id, existing.id)).run();
16840
+ }).where(eq18(schedules.id, existing.id)).run();
16657
16841
  } else {
16658
16842
  app.db.insert(schedules).values({
16659
- id: crypto12.randomUUID(),
16843
+ id: crypto13.randomUUID(),
16660
16844
  projectId: project.id,
16661
16845
  kind,
16662
16846
  cronExpr,
@@ -16677,13 +16861,13 @@ async function scheduleRoutes(app, opts) {
16677
16861
  diff: { kind, cronExpr, preset, timezone, providers, sourceId }
16678
16862
  });
16679
16863
  opts.onScheduleUpdated?.("upsert", project.id, kind);
16680
- 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();
16681
16865
  return reply.status(existing ? 200 : 201).send(formatSchedule(schedule));
16682
16866
  });
16683
16867
  app.get("/projects/:name/schedule", async (request, reply) => {
16684
16868
  const project = resolveProject(app.db, request.params.name);
16685
16869
  const kind = parseKindParam(request.query?.kind);
16686
- 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();
16687
16871
  if (!schedule) {
16688
16872
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
16689
16873
  }
@@ -16692,11 +16876,11 @@ async function scheduleRoutes(app, opts) {
16692
16876
  app.delete("/projects/:name/schedule", async (request, reply) => {
16693
16877
  const project = resolveProject(app.db, request.params.name);
16694
16878
  const kind = parseKindParam(request.query?.kind);
16695
- 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();
16696
16880
  if (!schedule) {
16697
16881
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
16698
16882
  }
16699
- app.db.delete(schedules).where(eq17(schedules.id, schedule.id)).run();
16883
+ app.db.delete(schedules).where(eq18(schedules.id, schedule.id)).run();
16700
16884
  writeAuditLog(app.db, {
16701
16885
  projectId: project.id,
16702
16886
  actor: "api",
@@ -16728,8 +16912,8 @@ function formatSchedule(row) {
16728
16912
  }
16729
16913
 
16730
16914
  // ../api-routes/src/notifications.ts
16731
- import crypto13 from "crypto";
16732
- import { eq as eq18 } from "drizzle-orm";
16915
+ import crypto14 from "crypto";
16916
+ import { eq as eq19 } from "drizzle-orm";
16733
16917
  var VALID_EVENTS = ["citation.lost", "citation.gained", "run.completed", "run.failed", "insight.critical", "insight.high"];
16734
16918
  async function notificationRoutes(app, opts = {}) {
16735
16919
  const allowLoopback = opts.allowLoopbackWebhooks === true;
@@ -16748,8 +16932,8 @@ async function notificationRoutes(app, opts = {}) {
16748
16932
  throw validationError(`Invalid event(s): ${invalid.join(", ")}. Must be one of: ${VALID_EVENTS.join(", ")}`);
16749
16933
  }
16750
16934
  const now = (/* @__PURE__ */ new Date()).toISOString();
16751
- const id = crypto13.randomUUID();
16752
- const webhookSecret = crypto13.randomBytes(32).toString("hex");
16935
+ const id = crypto14.randomUUID();
16936
+ const webhookSecret = crypto14.randomBytes(32).toString("hex");
16753
16937
  app.db.insert(notifications).values({
16754
16938
  id,
16755
16939
  projectId: project.id,
@@ -16769,22 +16953,22 @@ async function notificationRoutes(app, opts = {}) {
16769
16953
  diff: { channel, ...redactNotificationUrl(url), events }
16770
16954
  });
16771
16955
  return reply.status(201).send({
16772
- ...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()),
16773
16957
  webhookSecret
16774
16958
  });
16775
16959
  });
16776
16960
  app.get("/projects/:name/notifications", async (request, reply) => {
16777
16961
  const project = resolveProject(app.db, request.params.name);
16778
- 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();
16779
16963
  return reply.send(rows.map(formatNotification));
16780
16964
  });
16781
16965
  app.delete("/projects/:name/notifications/:id", async (request, reply) => {
16782
16966
  const project = resolveProject(app.db, request.params.name);
16783
- 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();
16784
16968
  if (!notification || notification.projectId !== project.id) {
16785
16969
  throw notFound("Notification", request.params.id);
16786
16970
  }
16787
- app.db.delete(notifications).where(eq18(notifications.id, notification.id)).run();
16971
+ app.db.delete(notifications).where(eq19(notifications.id, notification.id)).run();
16788
16972
  writeAuditLog(app.db, {
16789
16973
  projectId: project.id,
16790
16974
  actor: "api",
@@ -16796,7 +16980,7 @@ async function notificationRoutes(app, opts = {}) {
16796
16980
  });
16797
16981
  app.post("/projects/:name/notifications/:id/test", async (request, reply) => {
16798
16982
  const project = resolveProject(app.db, request.params.name);
16799
- 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();
16800
16984
  if (!notification || notification.projectId !== project.id) {
16801
16985
  throw notFound("Notification", request.params.id);
16802
16986
  }
@@ -16848,8 +17032,8 @@ function formatNotification(row) {
16848
17032
  }
16849
17033
 
16850
17034
  // ../api-routes/src/google.ts
16851
- import crypto16 from "crypto";
16852
- 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";
16853
17037
 
16854
17038
  // ../api-routes/src/gbp-summary.ts
16855
17039
  function computeMetricTotals(rows) {
@@ -17351,7 +17535,7 @@ async function inspectUrl(accessToken, inspectionUrl, siteUrl) {
17351
17535
  }
17352
17536
 
17353
17537
  // ../integration-google-analytics/src/ga4-client.ts
17354
- import crypto14 from "crypto";
17538
+ import crypto15 from "crypto";
17355
17539
 
17356
17540
  // ../integration-google-analytics/src/constants.ts
17357
17541
  var GA4_DATA_API_BASE = "https://analyticsdata.googleapis.com/v1beta";
@@ -17458,7 +17642,7 @@ function createServiceAccountJwt(clientEmail, privateKey, scope) {
17458
17642
  const headerB64 = encode(header);
17459
17643
  const payloadB64 = encode(payload);
17460
17644
  const signingInput = `${headerB64}.${payloadB64}`;
17461
- const sign = crypto14.createSign("RSA-SHA256");
17645
+ const sign = crypto15.createSign("RSA-SHA256");
17462
17646
  sign.update(signingInput);
17463
17647
  const signature = sign.sign(privateKey, "base64url");
17464
17648
  return `${signingInput}.${signature}`;
@@ -18298,7 +18482,7 @@ async function listPlaceActionLinks(accessToken, locationName, opts = {}) {
18298
18482
  }
18299
18483
 
18300
18484
  // ../integration-google-business-profile/src/lodging-client.ts
18301
- import crypto15 from "crypto";
18485
+ import crypto16 from "crypto";
18302
18486
  async function getLodging(accessToken, locationName, opts = {}) {
18303
18487
  const url = `${GBP_LODGING_BASE}/${locationName}/lodging?readMask=*`;
18304
18488
  try {
@@ -18325,7 +18509,7 @@ function isPopulated(value) {
18325
18509
  return true;
18326
18510
  }
18327
18511
  function hashLodging(lodging) {
18328
- return crypto15.createHash("sha256").update(stableStringify2(lodging)).digest("hex");
18512
+ return crypto16.createHash("sha256").update(stableStringify2(lodging)).digest("hex");
18329
18513
  }
18330
18514
  function stableStringify2(value) {
18331
18515
  if (value === null || typeof value !== "object") return JSON.stringify(value);
@@ -18347,7 +18531,7 @@ function scopesForConnectionType(type) {
18347
18531
  }
18348
18532
  }
18349
18533
  function signState(payload, secret) {
18350
- return crypto16.createHmac("sha256", secret).update(payload).digest("hex");
18534
+ return crypto17.createHmac("sha256", secret).update(payload).digest("hex");
18351
18535
  }
18352
18536
  function buildSignedState(data, secret) {
18353
18537
  const payload = JSON.stringify(data);
@@ -18358,7 +18542,7 @@ function verifySignedState(encoded, secret) {
18358
18542
  try {
18359
18543
  const { payload, sig } = JSON.parse(Buffer.from(encoded, "base64url").toString());
18360
18544
  const expected = signState(payload, secret);
18361
- 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;
18362
18546
  return JSON.parse(payload);
18363
18547
  } catch {
18364
18548
  return null;
@@ -18513,7 +18697,7 @@ async function googleRoutes(app, opts) {
18513
18697
  if (!projectId) {
18514
18698
  return reply.status(400).send("Stale OAuth state \u2014 restart the connect flow.");
18515
18699
  }
18516
- 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();
18517
18701
  if (!project) {
18518
18702
  return reply.status(400).send("Project no longer exists. Restart the connect flow.");
18519
18703
  }
@@ -18628,7 +18812,7 @@ async function googleRoutes(app, opts) {
18628
18812
  throw validationError('No GSC connection found for this domain. Run "canonry google connect" first.');
18629
18813
  }
18630
18814
  const now = (/* @__PURE__ */ new Date()).toISOString();
18631
- const runId = crypto16.randomUUID();
18815
+ const runId = crypto17.randomUUID();
18632
18816
  app.db.insert(runs).values({
18633
18817
  id: runId,
18634
18818
  projectId: project.id,
@@ -18641,14 +18825,14 @@ async function googleRoutes(app, opts) {
18641
18825
  if (opts.onGscSyncRequested) {
18642
18826
  opts.onGscSyncRequested(runId, project.id, { days, full });
18643
18827
  }
18644
- 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();
18645
18829
  return run;
18646
18830
  });
18647
18831
  app.get("/projects/:name/google/gsc/performance", async (request) => {
18648
18832
  const project = resolveProject(app.db, request.params.name);
18649
18833
  const { startDate, endDate, query, page, limit, offset } = request.query;
18650
18834
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
18651
- const conditions = [eq19(gscSearchData.projectId, project.id)];
18835
+ const conditions = [eq20(gscSearchData.projectId, project.id)];
18652
18836
  if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
18653
18837
  else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
18654
18838
  if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
@@ -18656,7 +18840,7 @@ async function googleRoutes(app, opts) {
18656
18840
  if (page) conditions.push(sql8`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
18657
18841
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
18658
18842
  const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
18659
- 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();
18660
18844
  return rows.map((r) => ({
18661
18845
  date: r.date,
18662
18846
  query: r.query,
@@ -18673,7 +18857,7 @@ async function googleRoutes(app, opts) {
18673
18857
  const project = resolveProject(app.db, request.params.name);
18674
18858
  const { startDate, endDate } = request.query;
18675
18859
  const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
18676
- const conditions = [eq19(gscSearchData.projectId, project.id)];
18860
+ const conditions = [eq20(gscSearchData.projectId, project.id)];
18677
18861
  if (startDate) conditions.push(sql8`${gscSearchData.date} >= ${startDate}`);
18678
18862
  else if (cutoffDate) conditions.push(sql8`${gscSearchData.date} >= ${cutoffDate}`);
18679
18863
  if (endDate) conditions.push(sql8`${gscSearchData.date} <= ${endDate}`);
@@ -18721,7 +18905,7 @@ async function googleRoutes(app, opts) {
18721
18905
  const mob = ir.mobileUsabilityResult;
18722
18906
  const rich = ir.richResultsResult;
18723
18907
  const now = (/* @__PURE__ */ new Date()).toISOString();
18724
- const id = crypto16.randomUUID();
18908
+ const id = crypto17.randomUUID();
18725
18909
  app.db.insert(gscUrlInspections).values({
18726
18910
  id,
18727
18911
  projectId: project.id,
@@ -18759,9 +18943,9 @@ async function googleRoutes(app, opts) {
18759
18943
  app.get("/projects/:name/google/gsc/inspections", async (request) => {
18760
18944
  const project = resolveProject(app.db, request.params.name);
18761
18945
  const { url, limit } = request.query;
18762
- const conditions = [eq19(gscUrlInspections.projectId, project.id)];
18763
- if (url) conditions.push(eq19(gscUrlInspections.url, url));
18764
- 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();
18765
18949
  return rows.map((r) => ({
18766
18950
  id: r.id,
18767
18951
  url: r.url,
@@ -18780,7 +18964,7 @@ async function googleRoutes(app, opts) {
18780
18964
  });
18781
18965
  app.get("/projects/:name/google/gsc/deindexed", async (request) => {
18782
18966
  const project = resolveProject(app.db, request.params.name);
18783
- 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();
18784
18968
  const byUrl = /* @__PURE__ */ new Map();
18785
18969
  for (const row of allInspections) {
18786
18970
  const existing = byUrl.get(row.url);
@@ -18808,7 +18992,7 @@ async function googleRoutes(app, opts) {
18808
18992
  });
18809
18993
  app.get("/projects/:name/google/gsc/coverage", async (request) => {
18810
18994
  const project = resolveProject(app.db, request.params.name);
18811
- 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();
18812
18996
  const canonicalUrl = (url) => url.replace(/^http:\/\//, "https://");
18813
18997
  const latestByUrl = /* @__PURE__ */ new Map();
18814
18998
  const historyByUrl = /* @__PURE__ */ new Map();
@@ -18857,7 +19041,7 @@ async function googleRoutes(app, opts) {
18857
19041
  const total = latestByUrl.size;
18858
19042
  const indexed = indexedUrls.length;
18859
19043
  const notIndexed = notIndexedUrls.length;
18860
- 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();
18861
19045
  const lastSyncedAt = latestSnapshot?.createdAt ?? null;
18862
19046
  const formatRow = (r) => ({
18863
19047
  id: r.id,
@@ -18908,7 +19092,7 @@ async function googleRoutes(app, opts) {
18908
19092
  const project = resolveProject(app.db, request.params.name);
18909
19093
  const parsed = parseInt(request.query.limit ?? "90", 10);
18910
19094
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
18911
- 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();
18912
19096
  return rows.map((r) => ({
18913
19097
  date: r.date,
18914
19098
  indexed: r.indexed,
@@ -18956,7 +19140,7 @@ async function googleRoutes(app, opts) {
18956
19140
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
18957
19141
  });
18958
19142
  const now = (/* @__PURE__ */ new Date()).toISOString();
18959
- const runId = crypto16.randomUUID();
19143
+ const runId = crypto17.randomUUID();
18960
19144
  app.db.insert(runs).values({
18961
19145
  id: runId,
18962
19146
  projectId: project.id,
@@ -18968,7 +19152,7 @@ async function googleRoutes(app, opts) {
18968
19152
  if (opts.onInspectSitemapRequested) {
18969
19153
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl });
18970
19154
  }
18971
- 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();
18972
19156
  return { sitemaps, primarySitemapUrl: sitemapUrl, run };
18973
19157
  });
18974
19158
  app.post("/projects/:name/google/gsc/inspect-sitemap", async (request) => {
@@ -18982,7 +19166,7 @@ async function googleRoutes(app, opts) {
18982
19166
  throw validationError("No GSC property configured for this connection");
18983
19167
  }
18984
19168
  const now = (/* @__PURE__ */ new Date()).toISOString();
18985
- const runId = crypto16.randomUUID();
19169
+ const runId = crypto17.randomUUID();
18986
19170
  app.db.insert(runs).values({
18987
19171
  id: runId,
18988
19172
  projectId: project.id,
@@ -18995,7 +19179,7 @@ async function googleRoutes(app, opts) {
18995
19179
  if (opts.onInspectSitemapRequested) {
18996
19180
  opts.onInspectSitemapRequested(runId, project.id, { sitemapUrl: sitemapUrl ?? void 0 });
18997
19181
  }
18998
- 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();
18999
19183
  return run;
19000
19184
  });
19001
19185
  app.put("/projects/:name/google/connections/:type/sitemap", async (request) => {
@@ -19042,7 +19226,7 @@ async function googleRoutes(app, opts) {
19042
19226
  const { accessToken } = await getValidToken(store, project.canonicalDomain, "gsc", googleClientId, googleClientSecret);
19043
19227
  let urlsToNotify = request.body?.urls ?? [];
19044
19228
  if (request.body?.allUnindexed) {
19045
- 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();
19046
19230
  const latestByUrl = /* @__PURE__ */ new Map();
19047
19231
  for (const row of allInspections) {
19048
19232
  if (!latestByUrl.has(row.url)) {
@@ -19153,7 +19337,7 @@ async function googleRoutes(app, opts) {
19153
19337
  };
19154
19338
  }
19155
19339
  function listSelectionResponse(projectId) {
19156
- 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();
19157
19341
  const dtos = rows.map(rowToDto2);
19158
19342
  return {
19159
19343
  locations: dtos,
@@ -19162,15 +19346,15 @@ async function googleRoutes(app, opts) {
19162
19346
  };
19163
19347
  }
19164
19348
  function clearGbpProjectData(tx, projectId) {
19165
- tx.delete(gbpDailyMetrics).where(eq19(gbpDailyMetrics.projectId, projectId)).run();
19166
- tx.delete(gbpKeywordImpressions).where(eq19(gbpKeywordImpressions.projectId, projectId)).run();
19167
- tx.delete(gbpKeywordMonthly).where(eq19(gbpKeywordMonthly.projectId, projectId)).run();
19168
- tx.delete(gbpPlaceActions).where(eq19(gbpPlaceActions.projectId, projectId)).run();
19169
- tx.delete(gbpLodgingSnapshots).where(eq19(gbpLodgingSnapshots.projectId, projectId)).run();
19170
- 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();
19171
19355
  }
19172
19356
  function currentProjectAccount(projectId) {
19173
- 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();
19174
19358
  return row?.accountName ?? null;
19175
19359
  }
19176
19360
  app.post("/projects/:name/gbp/locations/discover", async (request) => {
@@ -19240,7 +19424,7 @@ async function googleRoutes(app, opts) {
19240
19424
  app.db.transaction((tx) => {
19241
19425
  if (switching) clearGbpProjectData(tx, project.id);
19242
19426
  for (const remote of remoteLocations) {
19243
- 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();
19244
19428
  if (existing) {
19245
19429
  tx.update(gbpLocations).set({
19246
19430
  accountName,
@@ -19251,10 +19435,10 @@ async function googleRoutes(app, opts) {
19251
19435
  placeId: remote.metadata?.placeId ?? null,
19252
19436
  mapsUri: remote.metadata?.mapsUri ?? null,
19253
19437
  updatedAt: now
19254
- }).where(eq19(gbpLocations.id, existing.id)).run();
19438
+ }).where(eq20(gbpLocations.id, existing.id)).run();
19255
19439
  } else {
19256
19440
  tx.insert(gbpLocations).values({
19257
- id: crypto16.randomUUID(),
19441
+ id: crypto17.randomUUID(),
19258
19442
  projectId: project.id,
19259
19443
  accountName,
19260
19444
  locationName: remote.name,
@@ -19334,11 +19518,11 @@ async function googleRoutes(app, opts) {
19334
19518
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid selection request");
19335
19519
  }
19336
19520
  const { selected } = parsed.data;
19337
- 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();
19338
19522
  if (!existing) throw notFound("GBP location", locationName);
19339
19523
  const now = (/* @__PURE__ */ new Date()).toISOString();
19340
19524
  app.db.transaction((tx) => {
19341
- 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();
19342
19526
  writeAuditLog(tx, {
19343
19527
  projectId: project.id,
19344
19528
  actor: "api",
@@ -19347,7 +19531,7 @@ async function googleRoutes(app, opts) {
19347
19531
  entityId: locationName
19348
19532
  });
19349
19533
  });
19350
- 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();
19351
19535
  return rowToDto2(refreshed);
19352
19536
  });
19353
19537
  app.delete("/projects/:name/gbp/connection", async (request, reply) => {
@@ -19377,7 +19561,7 @@ async function googleRoutes(app, opts) {
19377
19561
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid sync request");
19378
19562
  }
19379
19563
  const now = (/* @__PURE__ */ new Date()).toISOString();
19380
- const runId = crypto16.randomUUID();
19564
+ const runId = crypto17.randomUUID();
19381
19565
  app.db.insert(runs).values({
19382
19566
  id: runId,
19383
19567
  projectId: project.id,
@@ -19391,10 +19575,10 @@ async function googleRoutes(app, opts) {
19391
19575
  });
19392
19576
  app.get("/projects/:name/gbp/metrics", async (request) => {
19393
19577
  const project = resolveProject(app.db, request.params.name);
19394
- const conditions = [eq19(gbpDailyMetrics.projectId, project.id)];
19395
- if (request.query.locationName) conditions.push(eq19(gbpDailyMetrics.locationName, request.query.locationName));
19396
- if (request.query.metric) conditions.push(eq19(gbpDailyMetrics.metric, request.query.metric));
19397
- 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();
19398
19582
  return {
19399
19583
  metrics: rows.map((r) => ({ locationName: r.locationName, date: r.date, metric: r.metric, value: r.value })),
19400
19584
  total: rows.length
@@ -19402,8 +19586,8 @@ async function googleRoutes(app, opts) {
19402
19586
  });
19403
19587
  app.get("/projects/:name/gbp/keywords", async (request) => {
19404
19588
  const project = resolveProject(app.db, request.params.name);
19405
- const conditions = [eq19(gbpKeywordImpressions.projectId, project.id)];
19406
- 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));
19407
19591
  const rows = app.db.select().from(gbpKeywordImpressions).where(and13(...conditions)).all();
19408
19592
  rows.sort((a, b) => (b.valueCount ?? -1) - (a.valueCount ?? -1));
19409
19593
  const thresholded = rows.filter((r) => r.valueThreshold !== null).length;
@@ -19422,8 +19606,8 @@ async function googleRoutes(app, opts) {
19422
19606
  });
19423
19607
  app.get("/projects/:name/gbp/place-actions", async (request) => {
19424
19608
  const project = resolveProject(app.db, request.params.name);
19425
- const conditions = [eq19(gbpPlaceActions.projectId, project.id)];
19426
- 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));
19427
19611
  const rows = app.db.select().from(gbpPlaceActions).where(and13(...conditions)).all();
19428
19612
  return {
19429
19613
  placeActions: rows.map((r) => ({
@@ -19439,9 +19623,9 @@ async function googleRoutes(app, opts) {
19439
19623
  });
19440
19624
  app.get("/projects/:name/gbp/lodging", async (request) => {
19441
19625
  const project = resolveProject(app.db, request.params.name);
19442
- const conditions = [eq19(gbpLodgingSnapshots.projectId, project.id)];
19443
- if (request.query.locationName) conditions.push(eq19(gbpLodgingSnapshots.locationName, request.query.locationName));
19444
- 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();
19445
19629
  const latestByLocation = /* @__PURE__ */ new Map();
19446
19630
  for (const row of rows) {
19447
19631
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -19456,9 +19640,9 @@ async function googleRoutes(app, opts) {
19456
19640
  });
19457
19641
  app.get("/projects/:name/gbp/places", async (request) => {
19458
19642
  const project = resolveProject(app.db, request.params.name);
19459
- const conditions = [eq19(gbpPlaceDetails.projectId, project.id)];
19460
- if (request.query.locationName) conditions.push(eq19(gbpPlaceDetails.locationName, request.query.locationName));
19461
- 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();
19462
19646
  const latestByLocation = /* @__PURE__ */ new Map();
19463
19647
  for (const row of rows) {
19464
19648
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -19477,7 +19661,7 @@ async function googleRoutes(app, opts) {
19477
19661
  app.get("/projects/:name/gbp/summary", async (request) => {
19478
19662
  const project = resolveProject(app.db, request.params.name);
19479
19663
  const locationName = request.query.locationName ?? null;
19480
- 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);
19481
19665
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
19482
19666
  if (locationNames.length === 0) {
19483
19667
  return buildGbpSummary({
@@ -19490,10 +19674,10 @@ async function googleRoutes(app, opts) {
19490
19674
  lodging: []
19491
19675
  });
19492
19676
  }
19493
- const metricRows = app.db.select().from(gbpDailyMetrics).where(and13(eq19(gbpDailyMetrics.projectId, project.id), inArray9(gbpDailyMetrics.locationName, locationNames))).all();
19494
- const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and13(eq19(gbpKeywordImpressions.projectId, project.id), inArray9(gbpKeywordImpressions.locationName, locationNames))).all();
19495
- const placeActionRows = app.db.select().from(gbpPlaceActions).where(and13(eq19(gbpPlaceActions.projectId, project.id), inArray9(gbpPlaceActions.locationName, locationNames))).all();
19496
- 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();
19497
19681
  const latestLodgingByLocation = /* @__PURE__ */ new Map();
19498
19682
  for (const row of lodgingRows) {
19499
19683
  if (!latestLodgingByLocation.has(row.locationName)) {
@@ -19513,8 +19697,8 @@ async function googleRoutes(app, opts) {
19513
19697
  }
19514
19698
 
19515
19699
  // ../api-routes/src/bing.ts
19516
- import crypto17 from "crypto";
19517
- 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";
19518
19702
 
19519
19703
  // ../integration-bing/src/constants.ts
19520
19704
  var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
@@ -19842,7 +20026,7 @@ async function bingRoutes(app, opts) {
19842
20026
  const store = requireConnectionStore();
19843
20027
  const project = resolveProject(app.db, request.params.name);
19844
20028
  requireConnection(store, project.canonicalDomain);
19845
- 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();
19846
20030
  const latestByUrl = /* @__PURE__ */ new Map();
19847
20031
  const definitiveByUrl = /* @__PURE__ */ new Map();
19848
20032
  for (const row of allInspections) {
@@ -19899,7 +20083,7 @@ async function bingRoutes(app, opts) {
19899
20083
  const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
19900
20084
  const now = (/* @__PURE__ */ new Date()).toISOString();
19901
20085
  app.db.insert(bingCoverageSnapshots).values({
19902
- id: crypto17.randomUUID(),
20086
+ id: crypto18.randomUUID(),
19903
20087
  projectId: project.id,
19904
20088
  syncRunId: snapshotRunId,
19905
20089
  date: snapshotDate,
@@ -19931,7 +20115,7 @@ async function bingRoutes(app, opts) {
19931
20115
  const project = resolveProject(app.db, request.params.name);
19932
20116
  const parsed = parseInt(request.query.limit ?? "90", 10);
19933
20117
  const limit = Number.isNaN(parsed) || parsed <= 0 ? 90 : parsed;
19934
- 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();
19935
20119
  return rows.map((r) => ({
19936
20120
  date: r.date,
19937
20121
  indexed: r.indexed,
@@ -19943,8 +20127,8 @@ async function bingRoutes(app, opts) {
19943
20127
  requireConnectionStore();
19944
20128
  const project = resolveProject(app.db, request.params.name);
19945
20129
  const { url, limit } = request.query;
19946
- const whereClause = url ? and14(eq20(bingUrlInspections.projectId, project.id), eq20(bingUrlInspections.url, url)) : eq20(bingUrlInspections.projectId, project.id);
19947
- 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();
19948
20132
  return filtered.map((r) => ({
19949
20133
  id: r.id,
19950
20134
  url: r.url,
@@ -19970,7 +20154,7 @@ async function bingRoutes(app, opts) {
19970
20154
  throw validationError("url is required");
19971
20155
  }
19972
20156
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
19973
- const runId = crypto17.randomUUID();
20157
+ const runId = crypto18.randomUUID();
19974
20158
  app.db.insert(runs).values({
19975
20159
  id: runId,
19976
20160
  projectId: project.id,
@@ -19991,7 +20175,7 @@ async function bingRoutes(app, opts) {
19991
20175
  discoveryDate: result.DiscoveryDate ?? null
19992
20176
  });
19993
20177
  const now = (/* @__PURE__ */ new Date()).toISOString();
19994
- const id = crypto17.randomUUID();
20178
+ const id = crypto18.randomUUID();
19995
20179
  const httpCode = result.HttpStatus ?? result.HttpCode ?? null;
19996
20180
  const lastCrawledDate = parseBingDate(result.LastCrawledDate);
19997
20181
  const inIndexDate = parseBingDate(result.InIndexDate);
@@ -20033,7 +20217,7 @@ async function bingRoutes(app, opts) {
20033
20217
  anchorCount: result.AnchorCount ?? null,
20034
20218
  discoveryDate
20035
20219
  }).run();
20036
- 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();
20037
20221
  return {
20038
20222
  id,
20039
20223
  url,
@@ -20049,7 +20233,7 @@ async function bingRoutes(app, opts) {
20049
20233
  } catch (e) {
20050
20234
  const msg = e instanceof Error ? e.message : String(e);
20051
20235
  bingLog("error", "inspect-url.failed", { domain: project.canonicalDomain, url, error: msg });
20052
- 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();
20053
20237
  throw e;
20054
20238
  }
20055
20239
  });
@@ -20061,7 +20245,7 @@ async function bingRoutes(app, opts) {
20061
20245
  throw validationError('No Bing site configured. Run "canonry bing set-site <project> <url>" first.');
20062
20246
  }
20063
20247
  const now = (/* @__PURE__ */ new Date()).toISOString();
20064
- const runId = crypto17.randomUUID();
20248
+ const runId = crypto18.randomUUID();
20065
20249
  app.db.insert(runs).values({
20066
20250
  id: runId,
20067
20251
  projectId: project.id,
@@ -20076,7 +20260,7 @@ async function bingRoutes(app, opts) {
20076
20260
  } else {
20077
20261
  bingLog("warn", "inspect-sitemap.no-callback", { domain: project.canonicalDomain, runId });
20078
20262
  }
20079
- 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();
20080
20264
  return run;
20081
20265
  });
20082
20266
  app.post("/projects/:name/bing/request-indexing", async (request) => {
@@ -20088,7 +20272,7 @@ async function bingRoutes(app, opts) {
20088
20272
  }
20089
20273
  let urlsToSubmit = request.body?.urls ?? [];
20090
20274
  if (request.body?.allUnindexed) {
20091
- 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();
20092
20276
  const latestByUrl = /* @__PURE__ */ new Map();
20093
20277
  for (const row of allInspections) {
20094
20278
  if (!latestByUrl.has(row.url)) {
@@ -20175,14 +20359,14 @@ async function bingRoutes(app, opts) {
20175
20359
  import fs from "fs";
20176
20360
  import path from "path";
20177
20361
  import os from "os";
20178
- import { eq as eq21, and as and15 } from "drizzle-orm";
20362
+ import { eq as eq22, and as and15 } from "drizzle-orm";
20179
20363
  function getScreenshotDir() {
20180
20364
  return path.join(os.homedir(), ".canonry", "screenshots");
20181
20365
  }
20182
20366
  async function cdpRoutes(app, opts) {
20183
20367
  app.get("/screenshots/:snapshotId", async (request, reply) => {
20184
20368
  const { snapshotId } = request.params;
20185
- 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();
20186
20370
  if (!snapshot?.screenshotPath) {
20187
20371
  const err = notFound("Screenshot", snapshotId);
20188
20372
  return reply.code(err.statusCode).send(err.toJSON());
@@ -20249,7 +20433,7 @@ async function cdpRoutes(app, opts) {
20249
20433
  async (request, reply) => {
20250
20434
  const project = resolveProject(app.db, request.params.name);
20251
20435
  const { runId } = request.params;
20252
- 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();
20253
20437
  if (!run) {
20254
20438
  const err = notFound("Run", runId);
20255
20439
  return reply.code(err.statusCode).send(err.toJSON());
@@ -20262,8 +20446,8 @@ async function cdpRoutes(app, opts) {
20262
20446
  citedDomains: querySnapshots.citedDomains,
20263
20447
  screenshotPath: querySnapshots.screenshotPath,
20264
20448
  rawResponse: querySnapshots.rawResponse
20265
- }).from(querySnapshots).where(eq21(querySnapshots.runId, runId)).all());
20266
- 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();
20267
20451
  const queryMap = new Map(queryRows.map((q) => [q.id, q.query]));
20268
20452
  const byQuery = /* @__PURE__ */ new Map();
20269
20453
  for (const snap of snapshots) {
@@ -20345,8 +20529,8 @@ async function cdpRoutes(app, opts) {
20345
20529
  }
20346
20530
 
20347
20531
  // ../api-routes/src/ga.ts
20348
- import crypto18 from "crypto";
20349
- 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";
20350
20534
  function gaLog(level, action, ctx) {
20351
20535
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
20352
20536
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -20553,10 +20737,10 @@ async function ga4Routes(app, opts) {
20553
20737
  if (!saConn && !oauthConn) {
20554
20738
  throw notFound("GA4 connection", project.name);
20555
20739
  }
20556
- app.db.delete(gaTrafficSnapshots).where(eq22(gaTrafficSnapshots.projectId, project.id)).run();
20557
- app.db.delete(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).run();
20558
- app.db.delete(gaAiReferrals).where(eq22(gaAiReferrals.projectId, project.id)).run();
20559
- 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();
20560
20744
  const propertyId = saConn?.propertyId ?? oauthConn?.propertyId ?? null;
20561
20745
  opts.ga4CredentialStore?.deleteConnection(project.name);
20562
20746
  opts.googleConnectionStore?.deleteConnection(project.canonicalDomain, "ga4");
@@ -20577,7 +20761,7 @@ async function ga4Routes(app, opts) {
20577
20761
  if (!connected) {
20578
20762
  return { connected: false, propertyId: null, clientEmail: null, authMethod: null, lastSyncedAt: null };
20579
20763
  }
20580
- 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();
20581
20765
  return {
20582
20766
  connected: true,
20583
20767
  propertyId: saConn?.propertyId ?? oauthConn?.propertyId ?? null,
@@ -20601,7 +20785,7 @@ async function ga4Routes(app, opts) {
20601
20785
  const syncAi = !only || only === "ai";
20602
20786
  const syncSocial = !only || only === "social";
20603
20787
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
20604
- const runId = crypto18.randomUUID();
20788
+ const runId = crypto19.randomUUID();
20605
20789
  app.db.insert(runs).values({
20606
20790
  id: runId,
20607
20791
  projectId: project.id,
@@ -20642,14 +20826,14 @@ async function ga4Routes(app, opts) {
20642
20826
  if (syncTraffic) {
20643
20827
  tx.delete(gaTrafficSnapshots).where(
20644
20828
  and16(
20645
- eq22(gaTrafficSnapshots.projectId, project.id),
20829
+ eq23(gaTrafficSnapshots.projectId, project.id),
20646
20830
  sql9`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
20647
20831
  sql9`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
20648
20832
  )
20649
20833
  ).run();
20650
20834
  for (const row of rows) {
20651
20835
  tx.insert(gaTrafficSnapshots).values({
20652
- id: crypto18.randomUUID(),
20836
+ id: crypto19.randomUUID(),
20653
20837
  projectId: project.id,
20654
20838
  date: row.date,
20655
20839
  landingPage: row.landingPage,
@@ -20666,14 +20850,14 @@ async function ga4Routes(app, opts) {
20666
20850
  if (syncAi) {
20667
20851
  tx.delete(gaAiReferrals).where(
20668
20852
  and16(
20669
- eq22(gaAiReferrals.projectId, project.id),
20853
+ eq23(gaAiReferrals.projectId, project.id),
20670
20854
  sql9`${gaAiReferrals.date} >= ${summary.periodStart}`,
20671
20855
  sql9`${gaAiReferrals.date} <= ${summary.periodEnd}`
20672
20856
  )
20673
20857
  ).run();
20674
20858
  for (const row of aiReferrals) {
20675
20859
  tx.insert(gaAiReferrals).values({
20676
- id: crypto18.randomUUID(),
20860
+ id: crypto19.randomUUID(),
20677
20861
  projectId: project.id,
20678
20862
  date: row.date,
20679
20863
  source: row.source,
@@ -20692,14 +20876,14 @@ async function ga4Routes(app, opts) {
20692
20876
  if (syncSocial) {
20693
20877
  tx.delete(gaSocialReferrals).where(
20694
20878
  and16(
20695
- eq22(gaSocialReferrals.projectId, project.id),
20879
+ eq23(gaSocialReferrals.projectId, project.id),
20696
20880
  sql9`${gaSocialReferrals.date} >= ${summary.periodStart}`,
20697
20881
  sql9`${gaSocialReferrals.date} <= ${summary.periodEnd}`
20698
20882
  )
20699
20883
  ).run();
20700
20884
  for (const row of socialReferrals) {
20701
20885
  tx.insert(gaSocialReferrals).values({
20702
- id: crypto18.randomUUID(),
20886
+ id: crypto19.randomUUID(),
20703
20887
  projectId: project.id,
20704
20888
  date: row.date,
20705
20889
  source: row.source,
@@ -20713,9 +20897,9 @@ async function ga4Routes(app, opts) {
20713
20897
  }
20714
20898
  }
20715
20899
  if (syncSummary) {
20716
- tx.delete(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).run();
20900
+ tx.delete(gaTrafficSummaries).where(eq23(gaTrafficSummaries.projectId, project.id)).run();
20717
20901
  tx.insert(gaTrafficSummaries).values({
20718
- id: crypto18.randomUUID(),
20902
+ id: crypto19.randomUUID(),
20719
20903
  projectId: project.id,
20720
20904
  periodStart: summary.periodStart,
20721
20905
  periodEnd: summary.periodEnd,
@@ -20725,10 +20909,10 @@ async function ga4Routes(app, opts) {
20725
20909
  syncedAt: now,
20726
20910
  syncRunId: runId
20727
20911
  }).run();
20728
- tx.delete(gaTrafficWindowSummaries).where(eq22(gaTrafficWindowSummaries.projectId, project.id)).run();
20912
+ tx.delete(gaTrafficWindowSummaries).where(eq23(gaTrafficWindowSummaries.projectId, project.id)).run();
20729
20913
  for (const ws of windowSummaries) {
20730
20914
  tx.insert(gaTrafficWindowSummaries).values({
20731
- id: crypto18.randomUUID(),
20915
+ id: crypto19.randomUUID(),
20732
20916
  projectId: project.id,
20733
20917
  windowKey: ws.windowKey,
20734
20918
  periodStart: ws.periodStart,
@@ -20743,7 +20927,7 @@ async function ga4Routes(app, opts) {
20743
20927
  }
20744
20928
  }
20745
20929
  });
20746
- 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();
20747
20931
  const syncedComponents = only ? [
20748
20932
  ...syncTraffic ? ["traffic"] : [],
20749
20933
  ...syncSummary ? ["summary"] : [],
@@ -20772,7 +20956,7 @@ async function ga4Routes(app, opts) {
20772
20956
  } catch (e) {
20773
20957
  const msg = e instanceof Error ? e.message : String(e);
20774
20958
  gaLog("error", "sync.fetch-failed", { projectId: project.id, runId, error: msg });
20775
- 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();
20776
20960
  throw e;
20777
20961
  }
20778
20962
  });
@@ -20783,11 +20967,11 @@ async function ga4Routes(app, opts) {
20783
20967
  const window = parseWindow(request.query.window);
20784
20968
  const cutoff = windowCutoff(window);
20785
20969
  const cutoffDate = cutoff?.slice(0, 10) ?? null;
20786
- const snapshotConditions = [eq22(gaTrafficSnapshots.projectId, project.id)];
20970
+ const snapshotConditions = [eq23(gaTrafficSnapshots.projectId, project.id)];
20787
20971
  if (cutoffDate) snapshotConditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
20788
- const aiConditions = [eq22(gaAiReferrals.projectId, project.id)];
20972
+ const aiConditions = [eq23(gaAiReferrals.projectId, project.id)];
20789
20973
  if (cutoffDate) aiConditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
20790
- const socialConditions = [eq22(gaSocialReferrals.projectId, project.id)];
20974
+ const socialConditions = [eq23(gaSocialReferrals.projectId, project.id)];
20791
20975
  if (cutoffDate) socialConditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
20792
20976
  const windowSummaryRow = cutoffDate ? app.db.select({
20793
20977
  totalSessions: gaTrafficWindowSummaries.totalSessions,
@@ -20796,8 +20980,8 @@ async function ga4Routes(app, opts) {
20796
20980
  totalUsers: gaTrafficWindowSummaries.totalUsers
20797
20981
  }).from(gaTrafficWindowSummaries).where(
20798
20982
  and16(
20799
- eq22(gaTrafficWindowSummaries.projectId, project.id),
20800
- eq22(gaTrafficWindowSummaries.windowKey, window)
20983
+ eq23(gaTrafficWindowSummaries.projectId, project.id),
20984
+ eq23(gaTrafficWindowSummaries.windowKey, window)
20801
20985
  )
20802
20986
  ).get() : null;
20803
20987
  const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
@@ -20809,14 +20993,14 @@ async function ga4Routes(app, opts) {
20809
20993
  totalSessions: gaTrafficSummaries.totalSessions,
20810
20994
  totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
20811
20995
  totalUsers: gaTrafficSummaries.totalUsers
20812
- }).from(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).get();
20996
+ }).from(gaTrafficSummaries).where(eq23(gaTrafficSummaries.projectId, project.id)).get();
20813
20997
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
20814
20998
  totalDirectSessions: sql9`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
20815
20999
  }).from(gaTrafficSnapshots).where(and16(...snapshotConditions)).get();
20816
21000
  const summaryMeta = app.db.select({
20817
21001
  periodStart: gaTrafficSummaries.periodStart,
20818
21002
  periodEnd: gaTrafficSummaries.periodEnd
20819
- }).from(gaTrafficSummaries).where(eq22(gaTrafficSummaries.projectId, project.id)).get();
21003
+ }).from(gaTrafficSummaries).where(eq23(gaTrafficSummaries.projectId, project.id)).get();
20820
21004
  const rows = app.db.select({
20821
21005
  landingPage: sql9`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
20822
21006
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
@@ -20875,7 +21059,7 @@ async function ga4Routes(app, opts) {
20875
21059
  channelGroup: gaAiReferrals.channelGroup,
20876
21060
  sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
20877
21061
  users: sql9`COALESCE(SUM(${gaAiReferrals.users}), 0)`
20878
- }).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();
20879
21063
  const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
20880
21064
  let aiBySessionUsers = 0;
20881
21065
  for (const row of aiBySessionRows) {
@@ -20894,7 +21078,7 @@ async function ga4Routes(app, opts) {
20894
21078
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`,
20895
21079
  users: sql9`SUM(${gaSocialReferrals.users})`
20896
21080
  }).from(gaSocialReferrals).where(and16(...socialConditions)).get();
20897
- 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();
20898
21082
  const total = summaryRow?.totalSessions ?? 0;
20899
21083
  const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
20900
21084
  const totalOrganicSessions = summaryRow?.totalOrganicSessions ?? 0;
@@ -20974,7 +21158,7 @@ async function ga4Routes(app, opts) {
20974
21158
  const project = resolveProject(app.db, request.params.name);
20975
21159
  requireGa4Connection(opts, project.name, project.canonicalDomain);
20976
21160
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
20977
- const conditions = [eq22(gaAiReferrals.projectId, project.id)];
21161
+ const conditions = [eq23(gaAiReferrals.projectId, project.id)];
20978
21162
  if (cutoffDate) conditions.push(sql9`${gaAiReferrals.date} >= ${cutoffDate}`);
20979
21163
  const rows = app.db.select({
20980
21164
  date: gaAiReferrals.date,
@@ -20997,7 +21181,7 @@ async function ga4Routes(app, opts) {
20997
21181
  const project = resolveProject(app.db, request.params.name);
20998
21182
  requireGa4Connection(opts, project.name, project.canonicalDomain);
20999
21183
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
21000
- const conditions = [eq22(gaSocialReferrals.projectId, project.id)];
21184
+ const conditions = [eq23(gaSocialReferrals.projectId, project.id)];
21001
21185
  if (cutoffDate) conditions.push(sql9`${gaSocialReferrals.date} >= ${cutoffDate}`);
21002
21186
  const rows = app.db.select({
21003
21187
  date: gaSocialReferrals.date,
@@ -21020,7 +21204,7 @@ async function ga4Routes(app, opts) {
21020
21204
  return fmt(d);
21021
21205
  };
21022
21206
  const sumSocial = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and16(
21023
- eq22(gaSocialReferrals.projectId, project.id),
21207
+ eq23(gaSocialReferrals.projectId, project.id),
21024
21208
  sql9`${gaSocialReferrals.date} >= ${from}`,
21025
21209
  sql9`${gaSocialReferrals.date} < ${to}`
21026
21210
  )).get();
@@ -21033,7 +21217,7 @@ async function ga4Routes(app, opts) {
21033
21217
  source: gaSocialReferrals.source,
21034
21218
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`
21035
21219
  }).from(gaSocialReferrals).where(and16(
21036
- eq22(gaSocialReferrals.projectId, project.id),
21220
+ eq23(gaSocialReferrals.projectId, project.id),
21037
21221
  sql9`${gaSocialReferrals.date} >= ${daysAgo(7)}`,
21038
21222
  sql9`${gaSocialReferrals.date} < ${fmt(today)}`
21039
21223
  )).groupBy(gaSocialReferrals.source).all();
@@ -21041,7 +21225,7 @@ async function ga4Routes(app, opts) {
21041
21225
  source: gaSocialReferrals.source,
21042
21226
  sessions: sql9`SUM(${gaSocialReferrals.sessions})`
21043
21227
  }).from(gaSocialReferrals).where(and16(
21044
- eq22(gaSocialReferrals.projectId, project.id),
21228
+ eq23(gaSocialReferrals.projectId, project.id),
21045
21229
  sql9`${gaSocialReferrals.date} >= ${daysAgo(14)}`,
21046
21230
  sql9`${gaSocialReferrals.date} < ${daysAgo(7)}`
21047
21231
  )).groupBy(gaSocialReferrals.source).all();
@@ -21082,16 +21266,16 @@ async function ga4Routes(app, opts) {
21082
21266
  return fmt(d);
21083
21267
  };
21084
21268
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
21085
- 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();
21086
- 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();
21087
- 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();
21088
21272
  const sumAi = (from, to) => app.db.select({ sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and16(
21089
- eq22(gaAiReferrals.projectId, project.id),
21273
+ eq23(gaAiReferrals.projectId, project.id),
21090
21274
  sql9`${gaAiReferrals.date} >= ${from}`,
21091
21275
  sql9`${gaAiReferrals.date} < ${to}`,
21092
- eq22(gaAiReferrals.sourceDimension, "session")
21276
+ eq23(gaAiReferrals.sourceDimension, "session")
21093
21277
  )).get();
21094
- 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();
21095
21279
  const todayStr = fmt(today);
21096
21280
  const buildTrend = (sum) => {
21097
21281
  const c7 = sum(daysAgo(7), todayStr)?.sessions ?? 0;
@@ -21101,16 +21285,16 @@ async function ga4Routes(app, opts) {
21101
21285
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
21102
21286
  };
21103
21287
  const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and16(
21104
- eq22(gaAiReferrals.projectId, project.id),
21288
+ eq23(gaAiReferrals.projectId, project.id),
21105
21289
  sql9`${gaAiReferrals.date} >= ${daysAgo(7)}`,
21106
21290
  sql9`${gaAiReferrals.date} < ${todayStr}`,
21107
- eq22(gaAiReferrals.sourceDimension, "session")
21291
+ eq23(gaAiReferrals.sourceDimension, "session")
21108
21292
  )).groupBy(gaAiReferrals.source).all();
21109
21293
  const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql9`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and16(
21110
- eq22(gaAiReferrals.projectId, project.id),
21294
+ eq23(gaAiReferrals.projectId, project.id),
21111
21295
  sql9`${gaAiReferrals.date} >= ${daysAgo(14)}`,
21112
21296
  sql9`${gaAiReferrals.date} < ${daysAgo(7)}`,
21113
- eq22(gaAiReferrals.sourceDimension, "session")
21297
+ eq23(gaAiReferrals.sourceDimension, "session")
21114
21298
  )).groupBy(gaAiReferrals.source).all();
21115
21299
  const findBiggestMover = (current, prev) => {
21116
21300
  const prevMap = new Map(prev.map((r) => [r.source, r.sessions]));
@@ -21126,8 +21310,8 @@ async function ga4Routes(app, opts) {
21126
21310
  }
21127
21311
  return mover;
21128
21312
  };
21129
- 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();
21130
- 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();
21131
21315
  return {
21132
21316
  total: buildTrend(sumTotal),
21133
21317
  organic: buildTrend(sumOrganic),
@@ -21142,7 +21326,7 @@ async function ga4Routes(app, opts) {
21142
21326
  const project = resolveProject(app.db, request.params.name);
21143
21327
  requireGa4Connection(opts, project.name, project.canonicalDomain);
21144
21328
  const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
21145
- const conditions = [eq22(gaTrafficSnapshots.projectId, project.id)];
21329
+ const conditions = [eq23(gaTrafficSnapshots.projectId, project.id)];
21146
21330
  if (cutoffDate) conditions.push(sql9`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
21147
21331
  const rows = app.db.select({
21148
21332
  date: gaTrafficSnapshots.date,
@@ -21165,7 +21349,7 @@ async function ga4Routes(app, opts) {
21165
21349
  sessions: sql9`SUM(${gaTrafficSnapshots.sessions})`,
21166
21350
  organicSessions: sql9`SUM(${gaTrafficSnapshots.organicSessions})`,
21167
21351
  users: sql9`SUM(${gaTrafficSnapshots.users})`
21168
- }).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();
21169
21353
  return {
21170
21354
  pages: trafficPages.map((r) => ({
21171
21355
  landingPage: r.landingPage,
@@ -21299,7 +21483,7 @@ function parseSchemaPageEntry(entry) {
21299
21483
  }
21300
21484
 
21301
21485
  // ../integration-wordpress/src/wordpress-client.ts
21302
- import crypto19 from "crypto";
21486
+ import crypto20 from "crypto";
21303
21487
  function validateUsername(username) {
21304
21488
  if (!username || typeof username !== "string" || username.trim().length === 0) {
21305
21489
  throw new WordpressApiError("AUTH_INVALID", "Username is required and must be a non-empty string", 400);
@@ -21512,7 +21696,7 @@ function buildSnippet(content) {
21512
21696
  return `${text2.slice(0, 157)}...`;
21513
21697
  }
21514
21698
  function contentHash(content) {
21515
- return crypto19.createHash("sha256").update(content).digest("hex");
21699
+ return crypto20.createHash("sha256").update(content).digest("hex");
21516
21700
  }
21517
21701
  function buildAmbiguousSlugMessage(slug, pages) {
21518
21702
  const candidates = pages.map((page) => {
@@ -22801,8 +22985,8 @@ async function wordpressRoutes(app, opts) {
22801
22985
  }
22802
22986
 
22803
22987
  // ../api-routes/src/backlinks.ts
22804
- import crypto20 from "crypto";
22805
- 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";
22806
22990
 
22807
22991
  // ../integration-commoncrawl/src/constants.ts
22808
22992
  import os2 from "os";
@@ -23289,8 +23473,8 @@ function mapRunRow(row) {
23289
23473
  };
23290
23474
  }
23291
23475
  function latestSummaryForProject(db, projectId, release) {
23292
- const condition = release ? and18(eq23(backlinkSummaries.projectId, projectId), eq23(backlinkSummaries.release, release)) : eq23(backlinkSummaries.projectId, projectId);
23293
- 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();
23294
23478
  }
23295
23479
  function parseExcludeCrawlers(value) {
23296
23480
  if (!value) return false;
@@ -23299,8 +23483,8 @@ function parseExcludeCrawlers(value) {
23299
23483
  }
23300
23484
  function computeFilteredSummary(db, base) {
23301
23485
  const baseDomainCondition = and18(
23302
- eq23(backlinkDomains.projectId, base.projectId),
23303
- eq23(backlinkDomains.release, base.release)
23486
+ eq24(backlinkDomains.projectId, base.projectId),
23487
+ eq24(backlinkDomains.release, base.release)
23304
23488
  );
23305
23489
  const filteredCondition = and18(baseDomainCondition, backlinkCrawlerExclusionClause());
23306
23490
  const unfilteredAgg = db.select({
@@ -23311,7 +23495,7 @@ function computeFilteredSummary(db, base) {
23311
23495
  count: sql10`count(*)`,
23312
23496
  total: sql10`coalesce(sum(${backlinkDomains.numHosts}), 0)`
23313
23497
  }).from(backlinkDomains).where(filteredCondition).get();
23314
- 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();
23315
23499
  const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
23316
23500
  const totalHosts = Number(filteredAgg?.total ?? 0);
23317
23501
  const unfilteredLinkingDomains = Number(unfilteredAgg?.count ?? 0);
@@ -23371,7 +23555,7 @@ async function backlinksRoutes(app, opts) {
23371
23555
  "@duckdb/node-api is not installed. Run `canonry backlinks install` to enable the backlinks feature."
23372
23556
  );
23373
23557
  }
23374
- 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();
23375
23559
  const now = (/* @__PURE__ */ new Date()).toISOString();
23376
23560
  if (existing) {
23377
23561
  if (NON_TERMINAL_SYNC_STATUSES.has(existing.status)) {
@@ -23382,12 +23566,12 @@ async function backlinksRoutes(app, opts) {
23382
23566
  phaseDetail: null,
23383
23567
  error: null,
23384
23568
  updatedAt: now
23385
- }).where(eq23(ccReleaseSyncs.id, existing.id)).run();
23569
+ }).where(eq24(ccReleaseSyncs.id, existing.id)).run();
23386
23570
  opts.onReleaseSyncRequested(existing.id, release);
23387
- 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();
23388
23572
  return reply.status(200).send(mapSyncRow(refreshed));
23389
23573
  }
23390
- const id = crypto20.randomUUID();
23574
+ const id = crypto21.randomUUID();
23391
23575
  app.db.insert(ccReleaseSyncs).values({
23392
23576
  id,
23393
23577
  release,
@@ -23396,15 +23580,15 @@ async function backlinksRoutes(app, opts) {
23396
23580
  updatedAt: now
23397
23581
  }).run();
23398
23582
  opts.onReleaseSyncRequested(id, release);
23399
- 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();
23400
23584
  return reply.status(201).send(mapSyncRow(inserted));
23401
23585
  });
23402
23586
  app.get("/backlinks/syncs/latest", async (_request, reply) => {
23403
- 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();
23404
23588
  return reply.send(row ? mapSyncRow(row) : null);
23405
23589
  });
23406
23590
  app.get("/backlinks/syncs", async (_request, reply) => {
23407
- 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();
23408
23592
  return reply.send(rows.map(mapSyncRow));
23409
23593
  });
23410
23594
  app.get("/backlinks/releases", async (_request, reply) => {
@@ -23444,7 +23628,7 @@ async function backlinksRoutes(app, opts) {
23444
23628
  throw validationError("Invalid release id");
23445
23629
  }
23446
23630
  const now = (/* @__PURE__ */ new Date()).toISOString();
23447
- const runId = crypto20.randomUUID();
23631
+ const runId = crypto21.randomUUID();
23448
23632
  app.db.insert(runs).values({
23449
23633
  id: runId,
23450
23634
  projectId: project.id,
@@ -23454,7 +23638,7 @@ async function backlinksRoutes(app, opts) {
23454
23638
  createdAt: now
23455
23639
  }).run();
23456
23640
  opts.onBacklinkExtractRequested(runId, project.id, release);
23457
- 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();
23458
23642
  return reply.status(201).send(mapRunRow(run));
23459
23643
  });
23460
23644
  app.get(
@@ -23479,15 +23663,15 @@ async function backlinksRoutes(app, opts) {
23479
23663
  const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
23480
23664
  const excludeCrawlers = parseExcludeCrawlers(request.query.excludeCrawlers);
23481
23665
  const baseDomainCondition = and18(
23482
- eq23(backlinkDomains.projectId, project.id),
23483
- eq23(backlinkDomains.release, targetRelease)
23666
+ eq24(backlinkDomains.projectId, project.id),
23667
+ eq24(backlinkDomains.release, targetRelease)
23484
23668
  );
23485
23669
  const domainCondition = excludeCrawlers ? and18(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
23486
23670
  const totalRow = app.db.select({ count: sql10`count(*)` }).from(backlinkDomains).where(domainCondition).get();
23487
23671
  const rows = app.db.select({
23488
23672
  linkingDomain: backlinkDomains.linkingDomain,
23489
23673
  numHosts: backlinkDomains.numHosts
23490
- }).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();
23491
23675
  let summary = null;
23492
23676
  if (summaryRow) {
23493
23677
  summary = excludeCrawlers ? computeFilteredSummary(app.db, summaryRow) : mapSummaryRow(summaryRow);
@@ -23503,7 +23687,7 @@ async function backlinksRoutes(app, opts) {
23503
23687
  "/projects/:name/backlinks/history",
23504
23688
  async (request, reply) => {
23505
23689
  const project = resolveProject(app.db, request.params.name);
23506
- 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();
23507
23691
  const response = rows.map((r) => ({
23508
23692
  release: r.release,
23509
23693
  totalLinkingDomains: r.totalLinkingDomains,
@@ -23517,12 +23701,12 @@ async function backlinksRoutes(app, opts) {
23517
23701
  }
23518
23702
 
23519
23703
  // ../api-routes/src/traffic.ts
23520
- import crypto22 from "crypto";
23704
+ import crypto23 from "crypto";
23521
23705
  import { Agent as UndiciAgent } from "undici";
23522
- 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";
23523
23707
 
23524
23708
  // ../integration-cloud-run/src/auth.ts
23525
- import crypto21 from "crypto";
23709
+ import crypto22 from "crypto";
23526
23710
  var GOOGLE_TOKEN_URL3 = "https://oauth2.googleapis.com/token";
23527
23711
  var CLOUD_LOGGING_READ_SCOPE = "https://www.googleapis.com/auth/logging.read";
23528
23712
  var TOKEN_REQUEST_TIMEOUT_MS = 3e4;
@@ -23551,7 +23735,7 @@ function createServiceAccountJwt2(clientEmail, privateKey, scope) {
23551
23735
  const headerB64 = encode(header);
23552
23736
  const payloadB64 = encode(payload);
23553
23737
  const signingInput = `${headerB64}.${payloadB64}`;
23554
- const sign = crypto21.createSign("RSA-SHA256");
23738
+ const sign = crypto22.createSign("RSA-SHA256");
23555
23739
  sign.update(signingInput);
23556
23740
  const signature = sign.sign(privateKey, "base64url");
23557
23741
  return `${signingInput}.${signature}`;
@@ -27312,8 +27496,8 @@ async function runBackfillTask(options) {
27312
27496
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
27313
27497
  try {
27314
27498
  app.db.transaction((tx) => {
27315
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq24(runs.id, runId)).run();
27316
- 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();
27317
27501
  });
27318
27502
  } catch {
27319
27503
  }
@@ -27328,7 +27512,7 @@ async function runBackfillTask(options) {
27328
27512
  if (allEvents.length === 0) {
27329
27513
  const finishedAt2 = (/* @__PURE__ */ new Date()).toISOString();
27330
27514
  try {
27331
- 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();
27332
27516
  } catch {
27333
27517
  }
27334
27518
  return;
@@ -27351,28 +27535,28 @@ async function runBackfillTask(options) {
27351
27535
  app.db.transaction((tx) => {
27352
27536
  tx.delete(crawlerEventsHourly).where(
27353
27537
  and19(
27354
- eq24(crawlerEventsHourly.sourceId, sourceRow.id),
27538
+ eq25(crawlerEventsHourly.sourceId, sourceRow.id),
27355
27539
  gte3(crawlerEventsHourly.tsHour, windowStartIso),
27356
27540
  lte2(crawlerEventsHourly.tsHour, windowEndIso)
27357
27541
  )
27358
27542
  ).run();
27359
27543
  tx.delete(aiUserFetchEventsHourly).where(
27360
27544
  and19(
27361
- eq24(aiUserFetchEventsHourly.sourceId, sourceRow.id),
27545
+ eq25(aiUserFetchEventsHourly.sourceId, sourceRow.id),
27362
27546
  gte3(aiUserFetchEventsHourly.tsHour, windowStartIso),
27363
27547
  lte2(aiUserFetchEventsHourly.tsHour, windowEndIso)
27364
27548
  )
27365
27549
  ).run();
27366
27550
  tx.delete(aiReferralEventsHourly).where(
27367
27551
  and19(
27368
- eq24(aiReferralEventsHourly.sourceId, sourceRow.id),
27552
+ eq25(aiReferralEventsHourly.sourceId, sourceRow.id),
27369
27553
  gte3(aiReferralEventsHourly.tsHour, windowStartIso),
27370
27554
  lte2(aiReferralEventsHourly.tsHour, windowEndIso)
27371
27555
  )
27372
27556
  ).run();
27373
27557
  tx.delete(rawEventSamples).where(
27374
27558
  and19(
27375
- eq24(rawEventSamples.sourceId, sourceRow.id),
27559
+ eq25(rawEventSamples.sourceId, sourceRow.id),
27376
27560
  gte3(rawEventSamples.ts, windowStartIso),
27377
27561
  lte2(rawEventSamples.ts, windowEndIso)
27378
27562
  )
@@ -27437,7 +27621,7 @@ async function runBackfillTask(options) {
27437
27621
  }
27438
27622
  })();
27439
27623
  tx.insert(rawEventSamples).values({
27440
- id: crypto22.randomUUID(),
27624
+ id: crypto23.randomUUID(),
27441
27625
  projectId: project.id,
27442
27626
  sourceId: sourceRow.id,
27443
27627
  ts: sample.observedAt,
@@ -27461,8 +27645,8 @@ async function runBackfillTask(options) {
27461
27645
  lastError: null,
27462
27646
  lastEventIds: newRingBuffer,
27463
27647
  updatedAt: finishedAt
27464
- }).where(eq24(trafficSources.id, sourceRow.id)).run();
27465
- 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();
27466
27650
  });
27467
27651
  } catch (e) {
27468
27652
  markFailed(`Backfill rollup write failed: ${e instanceof Error ? e.message : String(e)}`);
@@ -27543,7 +27727,7 @@ async function trafficRoutes(app, opts) {
27543
27727
  createdAt: existing?.createdAt ?? now,
27544
27728
  updatedAt: now
27545
27729
  });
27546
- 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);
27547
27731
  const config = {
27548
27732
  gcpProjectId,
27549
27733
  serviceName: serviceName ?? null,
@@ -27559,10 +27743,10 @@ async function trafficRoutes(app, opts) {
27559
27743
  lastError: null,
27560
27744
  configJson: config,
27561
27745
  updatedAt: now
27562
- }).where(eq24(trafficSources.id, activeSource.id)).run();
27563
- 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();
27564
27748
  } else {
27565
- const newId = crypto22.randomUUID();
27749
+ const newId = crypto23.randomUUID();
27566
27750
  app.db.insert(trafficSources).values({
27567
27751
  id: newId,
27568
27752
  projectId: project.id,
@@ -27577,7 +27761,7 @@ async function trafficRoutes(app, opts) {
27577
27761
  createdAt: now,
27578
27762
  updatedAt: now
27579
27763
  }).run();
27580
- 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();
27581
27765
  }
27582
27766
  writeAuditLog(app.db, {
27583
27767
  projectId: project.id,
@@ -27629,7 +27813,7 @@ async function trafficRoutes(app, opts) {
27629
27813
  createdAt: existing?.createdAt ?? now,
27630
27814
  updatedAt: now
27631
27815
  });
27632
- 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);
27633
27817
  const config = { baseUrl, username };
27634
27818
  const fallbackName = displayName ?? `WordPress \xB7 ${new URL(baseUrl).host}`;
27635
27819
  let sourceRow;
@@ -27640,10 +27824,10 @@ async function trafficRoutes(app, opts) {
27640
27824
  lastError: null,
27641
27825
  configJson: config,
27642
27826
  updatedAt: now
27643
- }).where(eq24(trafficSources.id, activeSource.id)).run();
27644
- 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();
27645
27829
  } else {
27646
- const newId = crypto22.randomUUID();
27830
+ const newId = crypto23.randomUUID();
27647
27831
  app.db.insert(trafficSources).values({
27648
27832
  id: newId,
27649
27833
  projectId: project.id,
@@ -27658,7 +27842,7 @@ async function trafficRoutes(app, opts) {
27658
27842
  createdAt: now,
27659
27843
  updatedAt: now
27660
27844
  }).run();
27661
- 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();
27662
27846
  }
27663
27847
  writeAuditLog(app.db, {
27664
27848
  projectId: project.id,
@@ -27712,7 +27896,7 @@ async function trafficRoutes(app, opts) {
27712
27896
  createdAt: existing?.createdAt ?? now,
27713
27897
  updatedAt: now
27714
27898
  });
27715
- 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);
27716
27900
  const config = { projectId, teamId, environment };
27717
27901
  const fallbackName = displayName ?? `Vercel \xB7 ${projectId}`;
27718
27902
  let sourceRow;
@@ -27723,10 +27907,10 @@ async function trafficRoutes(app, opts) {
27723
27907
  lastError: null,
27724
27908
  configJson: config,
27725
27909
  updatedAt: now
27726
- }).where(eq24(trafficSources.id, activeSource.id)).run();
27727
- 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();
27728
27912
  } else {
27729
- const newId = crypto22.randomUUID();
27913
+ const newId = crypto23.randomUUID();
27730
27914
  app.db.insert(trafficSources).values({
27731
27915
  id: newId,
27732
27916
  projectId: project.id,
@@ -27749,7 +27933,7 @@ async function trafficRoutes(app, opts) {
27749
27933
  createdAt: now,
27750
27934
  updatedAt: now
27751
27935
  }).run();
27752
- 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();
27753
27937
  }
27754
27938
  writeAuditLog(app.db, {
27755
27939
  projectId: project.id,
@@ -27762,7 +27946,7 @@ async function trafficRoutes(app, opts) {
27762
27946
  });
27763
27947
  app.post("/projects/:name/traffic/sources/:id/sync", async (request) => {
27764
27948
  const project = resolveProject(app.db, request.params.name);
27765
- 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();
27766
27950
  if (!sourceRow || sourceRow.projectId !== project.id) {
27767
27951
  throw notFound("Traffic source", request.params.id);
27768
27952
  }
@@ -27774,7 +27958,7 @@ async function trafficRoutes(app, opts) {
27774
27958
  const windowEnd = /* @__PURE__ */ new Date();
27775
27959
  const startedAt = windowEnd.toISOString();
27776
27960
  const syncStartedAtMs = windowEnd.getTime();
27777
- const runId = crypto22.randomUUID();
27961
+ const runId = crypto23.randomUUID();
27778
27962
  app.db.insert(runs).values({
27779
27963
  id: runId,
27780
27964
  projectId: project.id,
@@ -27788,8 +27972,8 @@ async function trafficRoutes(app, opts) {
27788
27972
  const markFailed = (msg, errorCode) => {
27789
27973
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
27790
27974
  app.db.transaction((tx) => {
27791
- tx.update(runs).set({ status: RunStatuses.failed, error: msg, finishedAt: failedAt }).where(eq24(runs.id, runId)).run();
27792
- 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();
27793
27977
  });
27794
27978
  try {
27795
27979
  opts.onTrafficSynced?.({
@@ -27869,7 +28053,7 @@ async function trafficRoutes(app, opts) {
27869
28053
  }
27870
28054
  const credential = credentialStore.getConnection(project.name);
27871
28055
  if (!credential) {
27872
- app.db.delete(runs).where(eq24(runs.id, runId)).run();
28056
+ app.db.delete(runs).where(eq25(runs.id, runId)).run();
27873
28057
  throw validationError(
27874
28058
  `No WordPress credential found for project "${project.name}". Run "canonry traffic connect wordpress" first.`
27875
28059
  );
@@ -27918,12 +28102,12 @@ async function trafficRoutes(app, opts) {
27918
28102
  auditAction = "traffic.vercel.synced";
27919
28103
  const credentialStore = opts.vercelTrafficCredentialStore;
27920
28104
  if (!credentialStore) {
27921
- app.db.delete(runs).where(eq24(runs.id, runId)).run();
28105
+ app.db.delete(runs).where(eq25(runs.id, runId)).run();
27922
28106
  throw validationError("Vercel traffic credential storage is not configured for this deployment");
27923
28107
  }
27924
28108
  const credential = credentialStore.getConnection(project.name);
27925
28109
  if (!credential) {
27926
- app.db.delete(runs).where(eq24(runs.id, runId)).run();
28110
+ app.db.delete(runs).where(eq25(runs.id, runId)).run();
27927
28111
  throw validationError(
27928
28112
  `No Vercel credential found for project "${project.name}". Run "canonry traffic connect vercel" first.`
27929
28113
  );
@@ -27983,7 +28167,7 @@ async function trafficRoutes(app, opts) {
27983
28167
  let aiReferralHitsCount = 0;
27984
28168
  let unknownHitsCount = 0;
27985
28169
  app.db.transaction((tx) => {
27986
- 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();
27987
28171
  const previousIds = latestRow.lastEventIds ?? [];
27988
28172
  const seenEventIds = new Set(previousIds);
27989
28173
  const dedupedEvents = seenEventIds.size === 0 ? allEvents : allEvents.filter((e) => !seenEventIds.has(e.eventId));
@@ -28116,7 +28300,7 @@ async function trafficRoutes(app, opts) {
28116
28300
  }
28117
28301
  })();
28118
28302
  tx.insert(rawEventSamples).values({
28119
- id: crypto22.randomUUID(),
28303
+ id: crypto23.randomUUID(),
28120
28304
  projectId: project.id,
28121
28305
  sourceId: sourceRow.id,
28122
28306
  ts: sample.observedAt,
@@ -28149,8 +28333,8 @@ async function trafficRoutes(app, opts) {
28149
28333
  if (sourceRow.sourceType === TrafficSourceTypes.wordpress) {
28150
28334
  sourceUpdate.lastCursor = nextCursor ?? null;
28151
28335
  }
28152
- tx.update(trafficSources).set(sourceUpdate).where(eq24(trafficSources.id, sourceRow.id)).run();
28153
- 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();
28154
28338
  });
28155
28339
  writeAuditLog(app.db, {
28156
28340
  projectId: project.id,
@@ -28200,7 +28384,7 @@ async function trafficRoutes(app, opts) {
28200
28384
  });
28201
28385
  app.post("/projects/:name/traffic/sources/:id/backfill", async (request) => {
28202
28386
  const project = resolveProject(app.db, request.params.name);
28203
- 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();
28204
28388
  if (!sourceRow || sourceRow.projectId !== project.id) {
28205
28389
  throw notFound("Traffic source", request.params.id);
28206
28390
  }
@@ -28350,7 +28534,7 @@ async function trafficRoutes(app, opts) {
28350
28534
  };
28351
28535
  }
28352
28536
  const startedAt = windowEnd.toISOString();
28353
- const runId = crypto22.randomUUID();
28537
+ const runId = crypto23.randomUUID();
28354
28538
  app.db.insert(runs).values({
28355
28539
  id: runId,
28356
28540
  projectId: project.id,
@@ -28386,35 +28570,35 @@ async function trafficRoutes(app, opts) {
28386
28570
  function buildSourceDetail(projectId, row, since) {
28387
28571
  const crawlerTotals = app.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
28388
28572
  and19(
28389
- eq24(crawlerEventsHourly.sourceId, row.id),
28573
+ eq25(crawlerEventsHourly.sourceId, row.id),
28390
28574
  gte3(crawlerEventsHourly.tsHour, since)
28391
28575
  )
28392
28576
  ).get();
28393
28577
  const aiUserFetchTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
28394
28578
  and19(
28395
- eq24(aiUserFetchEventsHourly.sourceId, row.id),
28579
+ eq25(aiUserFetchEventsHourly.sourceId, row.id),
28396
28580
  gte3(aiUserFetchEventsHourly.tsHour, since)
28397
28581
  )
28398
28582
  ).get();
28399
28583
  const aiTotals = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
28400
28584
  and19(
28401
- eq24(aiReferralEventsHourly.sourceId, row.id),
28585
+ eq25(aiReferralEventsHourly.sourceId, row.id),
28402
28586
  gte3(aiReferralEventsHourly.tsHour, since)
28403
28587
  )
28404
28588
  ).get();
28405
28589
  const sampleTotals = app.db.select({ total: sql11`COUNT(*)` }).from(rawEventSamples).where(
28406
28590
  and19(
28407
- eq24(rawEventSamples.sourceId, row.id),
28591
+ eq25(rawEventSamples.sourceId, row.id),
28408
28592
  gte3(rawEventSamples.ts, since)
28409
28593
  )
28410
28594
  ).get();
28411
28595
  const latestRun = app.db.select().from(runs).where(
28412
28596
  and19(
28413
- eq24(runs.projectId, projectId),
28414
- eq24(runs.kind, RunKinds["traffic-sync"]),
28415
- eq24(runs.sourceId, row.id)
28597
+ eq25(runs.projectId, projectId),
28598
+ eq25(runs.kind, RunKinds["traffic-sync"]),
28599
+ eq25(runs.sourceId, row.id)
28416
28600
  )
28417
- ).orderBy(desc13(runs.startedAt)).limit(1).get();
28601
+ ).orderBy(desc14(runs.startedAt)).limit(1).get();
28418
28602
  return {
28419
28603
  ...rowToDto(row),
28420
28604
  totals24h: {
@@ -28440,7 +28624,7 @@ async function trafficRoutes(app, opts) {
28440
28624
  "`advanceToNow` must be `true`. There is no implicit reset."
28441
28625
  );
28442
28626
  }
28443
- 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();
28444
28628
  if (!sourceRow) {
28445
28629
  throw notFound("traffic source", request.params.id);
28446
28630
  }
@@ -28457,7 +28641,7 @@ async function trafficRoutes(app, opts) {
28457
28641
  status: TrafficSourceStatuses.connected,
28458
28642
  lastError: null,
28459
28643
  updatedAt: now
28460
- }).where(eq24(trafficSources.id, sourceRow.id)).run();
28644
+ }).where(eq25(trafficSources.id, sourceRow.id)).run();
28461
28645
  writeAuditLog(tx, auditFromRequest(request, {
28462
28646
  projectId: project.id,
28463
28647
  actor: "api",
@@ -28465,20 +28649,20 @@ async function trafficRoutes(app, opts) {
28465
28649
  entityType: "traffic_source",
28466
28650
  entityId: sourceRow.id
28467
28651
  }));
28468
- 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();
28469
28653
  });
28470
28654
  return buildSourceDetail(project.id, updatedRow, new Date(Date.now() - 24 * 60 * 6e4).toISOString());
28471
28655
  });
28472
28656
  app.get("/projects/:name/traffic/sources", async (request) => {
28473
28657
  const project = resolveProject(app.db, request.params.name);
28474
- 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();
28475
28659
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map(rowToDto);
28476
28660
  const response = { sources };
28477
28661
  return response;
28478
28662
  });
28479
28663
  app.get("/projects/:name/traffic/status", async (request) => {
28480
28664
  const project = resolveProject(app.db, request.params.name);
28481
- 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();
28482
28666
  const since = new Date(Date.now() - 24 * 60 * 6e4).toISOString();
28483
28667
  const sources = rows.filter((row) => row.status !== TrafficSourceStatuses.archived).map((row) => buildSourceDetail(project.id, row, since));
28484
28668
  const response = { sources };
@@ -28488,7 +28672,7 @@ async function trafficRoutes(app, opts) {
28488
28672
  "/projects/:name/traffic/sources/:id",
28489
28673
  async (request) => {
28490
28674
  const project = resolveProject(app.db, request.params.name);
28491
- 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();
28492
28676
  if (!row || row.projectId !== project.id) {
28493
28677
  throw notFound("Traffic source", request.params.id);
28494
28678
  }
@@ -28539,15 +28723,15 @@ async function trafficRoutes(app, opts) {
28539
28723
  let aiReferralTotal = 0;
28540
28724
  if (kind === "all" || kind === TrafficEventKinds.crawler) {
28541
28725
  const crawlerFilters = [
28542
- eq24(crawlerEventsHourly.projectId, project.id),
28726
+ eq25(crawlerEventsHourly.projectId, project.id),
28543
28727
  gte3(crawlerEventsHourly.tsHour, sinceIso),
28544
28728
  lte2(crawlerEventsHourly.tsHour, untilIso)
28545
28729
  ];
28546
- if (sourceIdParam) crawlerFilters.push(eq24(crawlerEventsHourly.sourceId, sourceIdParam));
28730
+ if (sourceIdParam) crawlerFilters.push(eq25(crawlerEventsHourly.sourceId, sourceIdParam));
28547
28731
  const crawlerWhere = and19(...crawlerFilters);
28548
28732
  const total = app.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
28549
28733
  crawlerTotal = Number(total?.total ?? 0);
28550
- 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();
28551
28735
  for (const r of rows) {
28552
28736
  events.push({
28553
28737
  kind: TrafficEventKinds.crawler,
@@ -28564,15 +28748,15 @@ async function trafficRoutes(app, opts) {
28564
28748
  }
28565
28749
  if (kind === "all" || kind === TrafficEventKinds["ai-user-fetch"]) {
28566
28750
  const userFetchFilters = [
28567
- eq24(aiUserFetchEventsHourly.projectId, project.id),
28751
+ eq25(aiUserFetchEventsHourly.projectId, project.id),
28568
28752
  gte3(aiUserFetchEventsHourly.tsHour, sinceIso),
28569
28753
  lte2(aiUserFetchEventsHourly.tsHour, untilIso)
28570
28754
  ];
28571
- if (sourceIdParam) userFetchFilters.push(eq24(aiUserFetchEventsHourly.sourceId, sourceIdParam));
28755
+ if (sourceIdParam) userFetchFilters.push(eq25(aiUserFetchEventsHourly.sourceId, sourceIdParam));
28572
28756
  const userFetchWhere = and19(...userFetchFilters);
28573
28757
  const total = app.db.select({ total: sql11`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(userFetchWhere).get();
28574
28758
  aiUserFetchTotal = Number(total?.total ?? 0);
28575
- 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();
28576
28760
  for (const r of rows) {
28577
28761
  events.push({
28578
28762
  kind: TrafficEventKinds["ai-user-fetch"],
@@ -28589,15 +28773,15 @@ async function trafficRoutes(app, opts) {
28589
28773
  }
28590
28774
  if (kind === "all" || kind === TrafficEventKinds["ai-referral"]) {
28591
28775
  const aiFilters = [
28592
- eq24(aiReferralEventsHourly.projectId, project.id),
28776
+ eq25(aiReferralEventsHourly.projectId, project.id),
28593
28777
  gte3(aiReferralEventsHourly.tsHour, sinceIso),
28594
28778
  lte2(aiReferralEventsHourly.tsHour, untilIso)
28595
28779
  ];
28596
- if (sourceIdParam) aiFilters.push(eq24(aiReferralEventsHourly.sourceId, sourceIdParam));
28780
+ if (sourceIdParam) aiFilters.push(eq25(aiReferralEventsHourly.sourceId, sourceIdParam));
28597
28781
  const aiWhere = and19(...aiFilters);
28598
28782
  const total = app.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
28599
28783
  aiReferralTotal = Number(total?.total ?? 0);
28600
- 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();
28601
28785
  for (const r of rows) {
28602
28786
  events.push({
28603
28787
  kind: TrafficEventKinds["ai-referral"],
@@ -28630,7 +28814,7 @@ async function trafficRoutes(app, opts) {
28630
28814
  }
28631
28815
 
28632
28816
  // ../api-routes/src/doctor/checks/agent.ts
28633
- import crypto23 from "crypto";
28817
+ import crypto24 from "crypto";
28634
28818
  import fs6 from "fs";
28635
28819
  import path7 from "path";
28636
28820
  var REQUIRED_SKILLS = ["canonry", "aero"];
@@ -28783,7 +28967,7 @@ function isInstalled(dir) {
28783
28967
  }
28784
28968
  function hashInstalledFile(filePath) {
28785
28969
  try {
28786
- return crypto23.createHash("sha256").update(fs6.readFileSync(filePath)).digest("hex");
28970
+ return crypto24.createHash("sha256").update(fs6.readFileSync(filePath)).digest("hex");
28787
28971
  } catch {
28788
28972
  return void 0;
28789
28973
  }
@@ -29085,7 +29269,7 @@ var ga4ConnectionCheck = {
29085
29269
  var GA_AUTH_CHECKS = [ga4ConnectionCheck];
29086
29270
 
29087
29271
  // ../api-routes/src/doctor/checks/gbp-auth.ts
29088
- import { and as and20, eq as eq25 } from "drizzle-orm";
29272
+ import { and as and20, eq as eq26 } from "drizzle-orm";
29089
29273
  var RECENT_SYNC_WARN_DAYS = 7;
29090
29274
  var RECENT_SYNC_FAIL_DAYS = 30;
29091
29275
  function skippedNoProject() {
@@ -29318,7 +29502,7 @@ var recentSyncCheck = {
29318
29502
  title: "GBP recent sync",
29319
29503
  run: (ctx) => {
29320
29504
  if (!ctx.project) return skippedNoProject();
29321
- 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();
29322
29506
  if (selected.length === 0) {
29323
29507
  return {
29324
29508
  status: CheckStatuses.skipped,
@@ -29378,7 +29562,7 @@ var GBP_AUTH_CHECK_BY_ID = Object.fromEntries(
29378
29562
  );
29379
29563
 
29380
29564
  // ../api-routes/src/doctor/checks/places.ts
29381
- import { eq as eq26 } from "drizzle-orm";
29565
+ import { eq as eq27 } from "drizzle-orm";
29382
29566
  var apiKeyCheck = {
29383
29567
  id: "gbp.places.api-key",
29384
29568
  category: CheckCategories.auth,
@@ -29423,7 +29607,7 @@ var apiKeyCheck = {
29423
29607
  details: { tier: cfg.tier }
29424
29608
  };
29425
29609
  }
29426
- 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();
29427
29611
  const selected = rows.filter((r) => r.selected);
29428
29612
  const locationsWithPlaceId = selected.filter((r) => Boolean(r.placeId)).length;
29429
29613
  const details = {
@@ -29862,7 +30046,7 @@ var RUNTIME_STATE_CHECKS = [
29862
30046
  ];
29863
30047
 
29864
30048
  // ../api-routes/src/doctor/checks/traffic-source.ts
29865
- 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";
29866
30050
  var RECENT_DATA_WARN_DAYS = 7;
29867
30051
  var RECENT_DATA_FAIL_DAYS = 30;
29868
30052
  function skippedNoProject3() {
@@ -29877,7 +30061,7 @@ function loadProbes(ctx) {
29877
30061
  if (!ctx.project) return [];
29878
30062
  const rows = ctx.db.select().from(trafficSources).where(
29879
30063
  and21(
29880
- eq27(trafficSources.projectId, ctx.project.id),
30064
+ eq28(trafficSources.projectId, ctx.project.id),
29881
30065
  ne4(trafficSources.status, TrafficSourceStatuses.archived)
29882
30066
  )
29883
30067
  ).all();
@@ -29958,7 +30142,7 @@ var recentDataCheck = {
29958
30142
  const recentCrawlers = Number(
29959
30143
  ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
29960
30144
  and21(
29961
- eq27(crawlerEventsHourly.projectId, ctx.project.id),
30145
+ eq28(crawlerEventsHourly.projectId, ctx.project.id),
29962
30146
  gte4(crawlerEventsHourly.tsHour, warnCutoff)
29963
30147
  )
29964
30148
  ).get()?.total ?? 0
@@ -29966,7 +30150,7 @@ var recentDataCheck = {
29966
30150
  const recentReferrals = Number(
29967
30151
  ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
29968
30152
  and21(
29969
- eq27(aiReferralEventsHourly.projectId, ctx.project.id),
30153
+ eq28(aiReferralEventsHourly.projectId, ctx.project.id),
29970
30154
  gte4(aiReferralEventsHourly.tsHour, warnCutoff)
29971
30155
  )
29972
30156
  ).get()?.total ?? 0
@@ -29982,7 +30166,7 @@ var recentDataCheck = {
29982
30166
  const olderCrawlers = Number(
29983
30167
  ctx.db.select({ total: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
29984
30168
  and21(
29985
- eq27(crawlerEventsHourly.projectId, ctx.project.id),
30169
+ eq28(crawlerEventsHourly.projectId, ctx.project.id),
29986
30170
  gte4(crawlerEventsHourly.tsHour, failCutoff)
29987
30171
  )
29988
30172
  ).get()?.total ?? 0
@@ -29990,7 +30174,7 @@ var recentDataCheck = {
29990
30174
  const olderReferrals = Number(
29991
30175
  ctx.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
29992
30176
  and21(
29993
- eq27(aiReferralEventsHourly.projectId, ctx.project.id),
30177
+ eq28(aiReferralEventsHourly.projectId, ctx.project.id),
29994
30178
  gte4(aiReferralEventsHourly.tsHour, failCutoff)
29995
30179
  )
29996
30180
  ).get()?.total ?? 0
@@ -30368,8 +30552,8 @@ async function doctorRoutes(app, opts) {
30368
30552
  }
30369
30553
 
30370
30554
  // ../api-routes/src/discovery/routes.ts
30371
- import crypto24 from "crypto";
30372
- 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";
30373
30557
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
30374
30558
  async function discoveryRoutes(app, opts) {
30375
30559
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -30402,20 +30586,20 @@ async function discoveryRoutes(app, opts) {
30402
30586
  const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
30403
30587
  const decision = app.db.transaction((tx) => {
30404
30588
  const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and22(
30405
- eq28(discoverySessions.projectId, project.id),
30406
- eq28(discoverySessions.icpDescription, icpDescription),
30589
+ eq29(discoverySessions.projectId, project.id),
30590
+ eq29(discoverySessions.icpDescription, icpDescription),
30407
30591
  inArray10(discoverySessions.status, [
30408
30592
  DiscoverySessionStatuses.queued,
30409
30593
  DiscoverySessionStatuses.seeding,
30410
30594
  DiscoverySessionStatuses.probing
30411
30595
  ]),
30412
30596
  gte5(discoverySessions.createdAt, ageFloorIso)
30413
- )).orderBy(desc14(discoverySessions.createdAt)).get();
30597
+ )).orderBy(desc15(discoverySessions.createdAt)).get();
30414
30598
  if (existing && existing.runId) {
30415
30599
  return { reused: true, sessionId: existing.id, runId: existing.runId };
30416
30600
  }
30417
- const sessionId = crypto24.randomUUID();
30418
- const runId = crypto24.randomUUID();
30601
+ const sessionId = crypto25.randomUUID();
30602
+ const runId = crypto25.randomUUID();
30419
30603
  tx.insert(discoverySessions).values({
30420
30604
  id: sessionId,
30421
30605
  projectId: project.id,
@@ -30473,7 +30657,7 @@ async function discoveryRoutes(app, opts) {
30473
30657
  const project = resolveProject(app.db, request.params.name);
30474
30658
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
30475
30659
  const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? 50 : parsedLimit;
30476
- 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();
30477
30661
  return reply.send(rows.map(serializeSession));
30478
30662
  }
30479
30663
  );
@@ -30481,11 +30665,11 @@ async function discoveryRoutes(app, opts) {
30481
30665
  "/projects/:name/discover/sessions/:id",
30482
30666
  async (request, reply) => {
30483
30667
  const project = resolveProject(app.db, request.params.name);
30484
- 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();
30485
30669
  if (!session || session.projectId !== project.id) {
30486
30670
  throw notFound("Discovery session", request.params.id);
30487
30671
  }
30488
- 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();
30489
30673
  const detail = {
30490
30674
  ...serializeSession(session),
30491
30675
  probes: probeRows.map(serializeProbe)
@@ -30497,12 +30681,12 @@ async function discoveryRoutes(app, opts) {
30497
30681
  "/projects/:name/discover/sessions/:id/promote",
30498
30682
  async (request, reply) => {
30499
30683
  const project = resolveProject(app.db, request.params.name);
30500
- 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();
30501
30685
  if (!session || session.projectId !== project.id) {
30502
30686
  throw notFound("Discovery session", request.params.id);
30503
30687
  }
30504
- const probeRows = app.db.select().from(discoveryProbes).where(eq28(discoveryProbes.sessionId, session.id)).all();
30505
- 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());
30506
30690
  const seenCompetitors = new Set(existingCompetitors);
30507
30691
  const cited = /* @__PURE__ */ new Set();
30508
30692
  const aspirational = /* @__PURE__ */ new Set();
@@ -30531,7 +30715,7 @@ async function discoveryRoutes(app, opts) {
30531
30715
  );
30532
30716
  app.post("/projects/:name/discover/sessions/:id/promote", async (request, reply) => {
30533
30717
  const project = resolveProject(app.db, request.params.name);
30534
- 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();
30535
30719
  if (!session || session.projectId !== project.id) {
30536
30720
  throw notFound("Discovery session", request.params.id);
30537
30721
  }
@@ -30554,7 +30738,7 @@ async function discoveryRoutes(app, opts) {
30554
30738
  const bucketSet = new Set(buckets);
30555
30739
  const includeCompetitors = parsed.data.includeCompetitors ?? true;
30556
30740
  const competitorTypes = parsed.data.competitorTypes ?? DEFAULT_DISCOVERY_PROMOTE_COMPETITOR_TYPES;
30557
- 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();
30558
30742
  const candidateQueries = /* @__PURE__ */ new Set();
30559
30743
  for (const probe of probeRows) {
30560
30744
  if (!probe.bucket) continue;
@@ -30562,7 +30746,7 @@ async function discoveryRoutes(app, opts) {
30562
30746
  if (bucket.success && bucketSet.has(bucket.data)) candidateQueries.add(probe.query);
30563
30747
  }
30564
30748
  const existingQueries = new Set(
30565
- 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())
30566
30750
  );
30567
30751
  const promotedQueries = [];
30568
30752
  const skippedQueries = [];
@@ -30578,7 +30762,7 @@ async function discoveryRoutes(app, opts) {
30578
30762
  const skippedCompetitors = [];
30579
30763
  if (includeCompetitors) {
30580
30764
  const existingCompetitors = new Set(
30581
- 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())
30582
30766
  );
30583
30767
  const competitorMap = parseCompetitorMap(session.competitorMap);
30584
30768
  for (const entry of selectEligibleCompetitors(competitorMap, competitorTypes)) {
@@ -30597,7 +30781,7 @@ async function discoveryRoutes(app, opts) {
30597
30781
  app.db.transaction((tx) => {
30598
30782
  for (const query of promotedQueries) {
30599
30783
  tx.insert(queries).values({
30600
- id: crypto24.randomUUID(),
30784
+ id: crypto25.randomUUID(),
30601
30785
  projectId: project.id,
30602
30786
  query,
30603
30787
  provenance,
@@ -30606,7 +30790,7 @@ async function discoveryRoutes(app, opts) {
30606
30790
  }
30607
30791
  for (const domain of promotedCompetitors) {
30608
30792
  tx.insert(competitors).values({
30609
- id: crypto24.randomUUID(),
30793
+ id: crypto25.randomUUID(),
30610
30794
  projectId: project.id,
30611
30795
  domain,
30612
30796
  provenance,
@@ -30680,8 +30864,8 @@ function selectEligibleCompetitors(competitorMap, competitorTypes) {
30680
30864
  }
30681
30865
 
30682
30866
  // ../api-routes/src/discovery/orchestrate.ts
30683
- import crypto25 from "crypto";
30684
- import { eq as eq29 } from "drizzle-orm";
30867
+ import crypto26 from "crypto";
30868
+ import { eq as eq30 } from "drizzle-orm";
30685
30869
  var DEFAULT_DEDUP_THRESHOLD = 0.85;
30686
30870
  var DEFAULT_MAX_PROBES = 100;
30687
30871
  var ABSOLUTE_MAX_PROBES = 500;
@@ -30736,7 +30920,7 @@ async function executeDiscovery(opts) {
30736
30920
  status: DiscoverySessionStatuses.seeding,
30737
30921
  dedupThreshold,
30738
30922
  startedAt
30739
- }).where(eq29(discoverySessions.id, opts.sessionId)).run();
30923
+ }).where(eq30(discoverySessions.id, opts.sessionId)).run();
30740
30924
  const seedResult = await opts.deps.seed({
30741
30925
  project: opts.project,
30742
30926
  icpDescription: opts.icpDescription,
@@ -30756,7 +30940,7 @@ async function executeDiscovery(opts) {
30756
30940
  seedProvider: seedResult.provider,
30757
30941
  seedCountRaw,
30758
30942
  seedCount
30759
- }).where(eq29(discoverySessions.id, opts.sessionId)).run();
30943
+ }).where(eq30(discoverySessions.id, opts.sessionId)).run();
30760
30944
  const probeRows = [];
30761
30945
  const buckets = { cited: 0, aspirational: 0, "wasted-surface": 0 };
30762
30946
  for (const query of probedCanonicals) {
@@ -30769,7 +30953,7 @@ async function executeDiscovery(opts) {
30769
30953
  probeRows.push({ citedDomains: probe.citedDomains, bucket });
30770
30954
  buckets[bucket]++;
30771
30955
  opts.db.insert(discoveryProbes).values({
30772
- id: crypto25.randomUUID(),
30956
+ id: crypto26.randomUUID(),
30773
30957
  sessionId: opts.sessionId,
30774
30958
  projectId: opts.project.id,
30775
30959
  query,
@@ -30796,7 +30980,7 @@ async function executeDiscovery(opts) {
30796
30980
  wastedCount: buckets["wasted-surface"],
30797
30981
  competitorMap,
30798
30982
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
30799
- }).where(eq29(discoverySessions.id, opts.sessionId)).run();
30983
+ }).where(eq30(discoverySessions.id, opts.sessionId)).run();
30800
30984
  return {
30801
30985
  buckets,
30802
30986
  competitorMap,
@@ -30810,7 +30994,7 @@ function markSessionFailed(db, sessionId, error) {
30810
30994
  status: DiscoverySessionStatuses.failed,
30811
30995
  error,
30812
30996
  finishedAt: (/* @__PURE__ */ new Date()).toISOString()
30813
- }).where(eq29(discoverySessions.id, sessionId)).run();
30997
+ }).where(eq30(discoverySessions.id, sessionId)).run();
30814
30998
  }
30815
30999
  function dedupeStrings(input) {
30816
31000
  const seen = /* @__PURE__ */ new Set();
@@ -30917,6 +31101,7 @@ async function apiRoutes(app, opts) {
30917
31101
  bing: opts.bingSettingsSummary,
30918
31102
  onBingUpdate: opts.onBingSettingsUpdate
30919
31103
  });
31104
+ await api.register(keysRoutes);
30920
31105
  await api.register(snapshotRoutes, {
30921
31106
  onSnapshotRequested: opts.onSnapshotRequested
30922
31107
  });
@@ -31138,7 +31323,7 @@ function buildTrafficSourceValidators(opts) {
31138
31323
  }
31139
31324
 
31140
31325
  // src/intelligence-service.ts
31141
- import crypto26 from "crypto";
31326
+ import crypto27 from "crypto";
31142
31327
 
31143
31328
  // src/logger.ts
31144
31329
  var IS_TTY = process.stdout.isTTY === true;
@@ -31370,15 +31555,15 @@ var IntelligenceService = class {
31370
31555
  analyzeAndPersist(runId, projectId) {
31371
31556
  const recentRuns = this.db.select().from(runs).where(
31372
31557
  and23(
31373
- eq30(runs.projectId, projectId),
31374
- or5(eq30(runs.status, "completed"), eq30(runs.status, "partial")),
31558
+ eq31(runs.projectId, projectId),
31559
+ or5(eq31(runs.status, "completed"), eq31(runs.status, "partial")),
31375
31560
  // Defensive: RunCoordinator already skips probes before this is
31376
31561
  // called, but if a future call site invokes analyzeAndPersist
31377
31562
  // directly for a probe, probes still must not pollute the
31378
31563
  // intelligence window.
31379
31564
  ne5(runs.trigger, RunTriggers.probe)
31380
31565
  )
31381
- ).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();
31382
31567
  if (recentRuns.length === 0) {
31383
31568
  log.info("intelligence.skip", { runId, reason: "no completed runs" });
31384
31569
  return null;
@@ -31453,7 +31638,7 @@ var IntelligenceService = class {
31453
31638
  * Returns the persisted insights so the coordinator can count critical/high.
31454
31639
  */
31455
31640
  analyzeAndPersistGbp(runId, projectId) {
31456
- 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();
31457
31642
  if (!runRow) {
31458
31643
  log.info("gbp-intelligence.skip", { runId, reason: "run not found" });
31459
31644
  this.persistGbpInsights(runId, projectId, [], []);
@@ -31462,8 +31647,8 @@ var IntelligenceService = class {
31462
31647
  const windowStart = runRow.startedAt ?? runRow.createdAt;
31463
31648
  const windowEnd = runRow.finishedAt ?? (/* @__PURE__ */ new Date()).toISOString();
31464
31649
  const selected = this.db.select().from(gbpLocations).where(and23(
31465
- eq30(gbpLocations.projectId, projectId),
31466
- eq30(gbpLocations.selected, true),
31650
+ eq31(gbpLocations.projectId, projectId),
31651
+ eq31(gbpLocations.selected, true),
31467
31652
  gte6(gbpLocations.syncedAt, windowStart),
31468
31653
  lte3(gbpLocations.syncedAt, windowEnd)
31469
31654
  )).all();
@@ -31498,10 +31683,10 @@ var IntelligenceService = class {
31498
31683
  }
31499
31684
  /** Build the per-location signal bundle the GBP analyzer consumes. */
31500
31685
  buildGbpLocationSignals(projectId, locationName, displayName, fallbackDate) {
31501
- 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();
31502
- const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and23(eq30(gbpPlaceActions.projectId, projectId), eq30(gbpPlaceActions.locationName, locationName))).all();
31503
- 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();
31504
- 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();
31505
31690
  const placesAmenities = placeRow ? extractPlaceAmenities(placeRow.attributes) : [];
31506
31691
  const summary = buildGbpSummary({
31507
31692
  locationName,
@@ -31533,7 +31718,7 @@ var IntelligenceService = class {
31533
31718
  /** Build the month-over-month keyword series for a location from the
31534
31719
  * accumulating gbp_keyword_monthly table (latest complete month vs prior). */
31535
31720
  buildGbpKeywordTrend(projectId, locationName) {
31536
- 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();
31537
31722
  if (rows.length === 0) return { recentMonth: null, priorMonth: null, points: [] };
31538
31723
  const months = [...new Set(rows.map((r) => r.month))].sort().reverse();
31539
31724
  const recentMonth = months[0] ?? null;
@@ -31564,7 +31749,7 @@ var IntelligenceService = class {
31564
31749
  */
31565
31750
  persistGbpInsights(runId, projectId, gbpInsights, coveredLocationNames) {
31566
31751
  const covered = new Set(coveredLocationNames);
31567
- 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();
31568
31753
  const staleIds = [];
31569
31754
  const dismissedSlots = /* @__PURE__ */ new Set();
31570
31755
  for (const row of existing) {
@@ -31575,7 +31760,7 @@ var IntelligenceService = class {
31575
31760
  }
31576
31761
  this.db.transaction((tx) => {
31577
31762
  for (const id of staleIds) {
31578
- tx.delete(insights).where(eq30(insights.id, id)).run();
31763
+ tx.delete(insights).where(eq31(insights.id, id)).run();
31579
31764
  }
31580
31765
  for (const insight of gbpInsights) {
31581
31766
  const parsed = parseGbpInsightId(insight.id);
@@ -31653,7 +31838,7 @@ var IntelligenceService = class {
31653
31838
  * create per run + aggregate). DB is left untouched.
31654
31839
  */
31655
31840
  backfill(projectName, opts, onProgress) {
31656
- 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();
31657
31842
  if (!project) {
31658
31843
  throw new Error(`Project "${projectName}" not found`);
31659
31844
  }
@@ -31667,8 +31852,8 @@ var IntelligenceService = class {
31667
31852
  }
31668
31853
  const allRuns = this.db.select().from(runs).where(
31669
31854
  and23(
31670
- eq30(runs.projectId, project.id),
31671
- 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")),
31672
31857
  // Backfill must not replay probe runs as if they were real sweeps.
31673
31858
  ne5(runs.trigger, RunTriggers.probe)
31674
31859
  )
@@ -31747,7 +31932,7 @@ var IntelligenceService = class {
31747
31932
  return { processed, skipped, totalInsights };
31748
31933
  }
31749
31934
  loadTrackedCompetitors(projectId) {
31750
- 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);
31751
31936
  }
31752
31937
  /**
31753
31938
  * Wipe transition signals from an analysis result while keeping health.
@@ -31768,15 +31953,15 @@ var IntelligenceService = class {
31768
31953
  }
31769
31954
  persistResult(result, runId, projectId) {
31770
31955
  const previouslyDismissed = /* @__PURE__ */ new Set();
31771
- 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();
31772
31957
  for (const row of existingInsights) {
31773
31958
  if (row.dismissed) {
31774
31959
  previouslyDismissed.add(`${row.query}:${row.provider}:${row.type}`);
31775
31960
  }
31776
31961
  }
31777
31962
  this.db.transaction((tx) => {
31778
- tx.delete(insights).where(eq30(insights.runId, runId)).run();
31779
- 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();
31780
31965
  const now = (/* @__PURE__ */ new Date()).toISOString();
31781
31966
  for (const insight of result.insights) {
31782
31967
  const wasDismissed = previouslyDismissed.has(`${insight.query}:${insight.provider}:${insight.type}`);
@@ -31796,7 +31981,7 @@ var IntelligenceService = class {
31796
31981
  }).run();
31797
31982
  }
31798
31983
  tx.insert(healthSnapshots).values({
31799
- id: crypto26.randomUUID(),
31984
+ id: crypto27.randomUUID(),
31800
31985
  projectId,
31801
31986
  runId,
31802
31987
  overallCitedRate: String(result.health.overallCitedRate),
@@ -31827,14 +32012,14 @@ var IntelligenceService = class {
31827
32012
  applySeverityTiering(rawInsights, excludeRunId, projectId) {
31828
32013
  const regressions = rawInsights.filter((i) => i.type === "regression");
31829
32014
  if (regressions.length === 0) return rawInsights;
31830
- 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();
31831
32016
  const gscConnected = gscRows.length > 0;
31832
32017
  const gscImpressionsByQuery = /* @__PURE__ */ new Map();
31833
32018
  for (const row of gscRows) {
31834
32019
  const key = row.query.toLowerCase();
31835
32020
  gscImpressionsByQuery.set(key, (gscImpressionsByQuery.get(key) ?? 0) + row.impressions);
31836
32021
  }
31837
- 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();
31838
32023
  const locationCount = Math.max(
31839
32024
  1,
31840
32025
  (projectRow?.locations ?? []).length
@@ -31842,13 +32027,13 @@ var IntelligenceService = class {
31842
32027
  const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
31843
32028
  const recentRunRows = this.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
31844
32029
  and23(
31845
- eq30(runs.projectId, projectId),
31846
- eq30(runs.kind, RunKinds["answer-visibility"]),
31847
- 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")),
31848
32033
  // Defensive — see top of file.
31849
32034
  ne5(runs.trigger, RunTriggers.probe)
31850
32035
  )
31851
- ).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();
31852
32037
  const recentGroups = groupRunsByCreatedAt(recentRunRows);
31853
32038
  const recentRunIds = [];
31854
32039
  const recentRunIdToCreatedAt = /* @__PURE__ */ new Map();
@@ -31864,7 +32049,7 @@ var IntelligenceService = class {
31864
32049
  const haveHistory = recentRunIds.length > 0;
31865
32050
  const priorRegressionsByPair = /* @__PURE__ */ new Map();
31866
32051
  if (haveHistory) {
31867
- 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();
31868
32053
  const regressionGroups = /* @__PURE__ */ new Map();
31869
32054
  for (const row of priorRows) {
31870
32055
  if (!row.runId) continue;
@@ -31893,7 +32078,7 @@ var IntelligenceService = class {
31893
32078
  });
31894
32079
  }
31895
32080
  buildRunData(runId, projectId, completedAt, location = null) {
31896
- 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();
31897
32082
  const projectDomains = projectDomainRow ? effectiveDomains({
31898
32083
  canonicalDomain: projectDomainRow.canonicalDomain,
31899
32084
  ownedDomains: projectDomainRow.ownedDomains
@@ -31909,7 +32094,7 @@ var IntelligenceService = class {
31909
32094
  citedDomains: querySnapshots.citedDomains,
31910
32095
  competitorOverlap: querySnapshots.competitorOverlap,
31911
32096
  snapshotLocation: querySnapshots.location
31912
- }).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();
31913
32098
  const snapshots = [];
31914
32099
  let orphanCount = 0;
31915
32100
  for (const r of rows) {