@ainyc/canonry 4.188.4 → 4.189.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/assets/assets/{AuditHistoryPanel-Cn1xsaFZ.js → AuditHistoryPanel-CZ7ygQfy.js} +1 -1
  2. package/assets/assets/{BacklinksPage-CfTxBHZn.js → BacklinksPage-CYEYxjE8.js} +1 -1
  3. package/assets/assets/{HistoryPage-BvgKqrbW.js → HistoryPage-CvAllKzD.js} +1 -1
  4. package/assets/assets/{MeasurementPropertyPage-CGbNTIdz.js → MeasurementPropertyPage-BED9ak5a.js} +1 -1
  5. package/assets/assets/ProjectPage-Cgl5v16o.js +9 -0
  6. package/assets/assets/{RunRow-DzneLSnv.js → RunRow-DUlFaUBp.js} +1 -1
  7. package/assets/assets/{RunsPage-BgByi_t3.js → RunsPage-BcbBe_aQ.js} +1 -1
  8. package/assets/assets/{SettingsPage-DBv-GyPu.js → SettingsPage-gCyOuoD_.js} +1 -1
  9. package/assets/assets/{SiteHealthSection-DaNslx5J.js → SiteHealthSection-DJfFmT3n.js} +3 -3
  10. package/assets/assets/{TrafficPage-B6mfVlPe.js → TrafficPage-2ATExz7V.js} +1 -1
  11. package/assets/assets/{TrafficSourceDetailPage-BXwAVIDo.js → TrafficSourceDetailPage-0PBMk9UH.js} +1 -1
  12. package/assets/assets/{extract-error-message-DMJa2ku8.js → extract-error-message-D8N7K1pb.js} +1 -1
  13. package/assets/assets/index-Bhwxe6W_.js +86 -0
  14. package/assets/assets/{react-sigma_core.esm.min-DiTvmmQd.js → react-sigma_core.esm.min-CU1UA6VY.js} +1 -1
  15. package/assets/index.html +1 -1
  16. package/dist/{chunk-3HZELBZZ.js → chunk-CDI3YR57.js} +2587 -2569
  17. package/dist/{chunk-7NMMQ7QO.js → chunk-HG6NLN7H.js} +330 -30
  18. package/dist/{chunk-VJRQTKUB.js → chunk-J533DKYE.js} +2 -2
  19. package/dist/{chunk-AC3ZIRHR.js → chunk-QQLMRABM.js} +645 -502
  20. package/dist/{chunk-OXYM6VCJ.js → chunk-YDYB3H4O.js} +19 -1
  21. package/dist/cli.js +48 -22
  22. package/dist/index.js +4 -4
  23. package/dist/{intelligence-service-GMG2S55U.js → intelligence-service-M5TYY4TH.js} +2 -2
  24. package/dist/mcp.js +3 -3
  25. package/package.json +11 -11
  26. package/assets/assets/ProjectPage-BQ4lzu49.js +0 -9
  27. package/assets/assets/index-BRnD7hdh.js +0 -86
@@ -206,6 +206,7 @@ import {
206
206
  buildMeasurementRunManifestV1,
207
207
  calendarDateRange,
208
208
  calendarMonthBounds,
209
+ calendarRecurrenceSchema,
209
210
  canonicalMeasurementExecutionIdentityJson,
210
211
  canonicalMeasurementPlanJson,
211
212
  canonicalMeasurementPlanV2,
@@ -671,10 +672,10 @@ import {
671
672
  wordpressSchemaDeployResultDtoSchema,
672
673
  wordpressSchemaStatusResultDtoSchema,
673
674
  wordpressStatusDtoSchema
674
- } from "./chunk-3HZELBZZ.js";
675
+ } from "./chunk-CDI3YR57.js";
675
676
 
676
677
  // src/intelligence-service.ts
677
- import { eq as eq67, desc as desc29, asc as asc12, and as and56, ne as ne10, or as or14, inArray as inArray25, gte as gte18, lte as lte15, isNull as isNull12, sql as sql26, exists } from "drizzle-orm";
678
+ import { eq as eq67, desc as desc29, asc as asc12, and as and57, ne as ne10, or as or14, inArray as inArray25, gte as gte18, lte as lte15, isNull as isNull12, sql as sql26, exists } from "drizzle-orm";
678
679
 
679
680
  // ../db/src/client.ts
680
681
  import { mkdirSync } from "fs";
@@ -1270,6 +1271,7 @@ var schedules = sqliteTable("schedules", {
1270
1271
  // created before migration 53.
1271
1272
  kind: text("kind").notNull().default("answer-visibility"),
1272
1273
  cronExpr: text("cron_expr").notNull(),
1274
+ recurrence: text("recurrence", { mode: "json" }).$type(),
1273
1275
  preset: text("preset"),
1274
1276
  timezone: text("timezone").notNull().default("UTC"),
1275
1277
  enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
@@ -7152,6 +7154,13 @@ var MIGRATION_VERSIONS = [
7152
7154
  statements: [
7153
7155
  `ALTER TABLE research_runs ADD COLUMN initiated_by TEXT`
7154
7156
  ]
7157
+ },
7158
+ {
7159
+ version: 152,
7160
+ name: "schedule-calendar-recurrence",
7161
+ statements: [
7162
+ `ALTER TABLE schedules ADD COLUMN recurrence TEXT`
7163
+ ]
7155
7164
  }
7156
7165
  ];
7157
7166
  function addRunsMeasurementPlanVersionForeignKey(tx) {
@@ -11544,7 +11553,7 @@ function registerOAuthAdminRoutes(app, opts) {
11544
11553
 
11545
11554
  // ../api-routes/src/projects.ts
11546
11555
  import crypto7 from "crypto";
11547
- import { eq as eq5, sql as sql4 } from "drizzle-orm";
11556
+ import { and as and2, eq as eq5, sql as sql4 } from "drizzle-orm";
11548
11557
 
11549
11558
  // ../api-routes/src/helpers.ts
11550
11559
  import crypto6 from "crypto";
@@ -11627,18 +11636,22 @@ async function settingsRoutes(app, opts) {
11627
11636
  requireAdminSession(request);
11628
11637
  return {
11629
11638
  providers: opts.providerSummary ?? [],
11630
- providerCatalog: (opts.providerAdapters ?? []).map((adapter) => ({
11631
- name: adapter.name,
11632
- displayName: adapter.displayName,
11633
- mode: adapter.mode,
11634
- modelConfigurable: adapter.modelConfigurable,
11635
- defaultModel: adapter.defaultModel,
11636
- knownModels: adapter.knownModels,
11637
- modelValidationPattern: {
11638
- source: adapter.modelValidationPattern.source,
11639
- flags: adapter.modelValidationPattern.flags
11640
- },
11641
- modelValidationHint: adapter.modelValidationHint
11639
+ providerCatalog: await Promise.all((opts.providerAdapters ?? []).map(async (adapter) => {
11640
+ const configured = opts.providerSummary?.some((provider) => provider.name === adapter.name && provider.configured);
11641
+ const discovered = configured && opts.getProviderModels ? await opts.getProviderModels(adapter.name) : [];
11642
+ return {
11643
+ name: adapter.name,
11644
+ displayName: adapter.displayName,
11645
+ mode: adapter.mode,
11646
+ modelConfigurable: adapter.modelConfigurable,
11647
+ defaultModel: adapter.defaultModel,
11648
+ knownModels: discovered.length ? discovered : adapter.knownModels,
11649
+ modelValidationPattern: {
11650
+ source: adapter.modelValidationPattern.source,
11651
+ flags: adapter.modelValidationPattern.flags
11652
+ },
11653
+ modelValidationHint: adapter.modelValidationHint
11654
+ };
11642
11655
  })),
11643
11656
  google: opts.google ?? { configured: false },
11644
11657
  bing: opts.bing ?? { configured: false }
@@ -12150,7 +12163,10 @@ async function projectRoutes(app, opts) {
12150
12163
  const project = resolveProject(app.db, request.params.name);
12151
12164
  const qs = app.db.select().from(queries).where(eq5(queries.projectId, project.id)).all();
12152
12165
  const comps = app.db.select().from(competitors).where(eq5(competitors.projectId, project.id)).all();
12153
- const schedule = app.db.select().from(schedules).where(eq5(schedules.projectId, project.id)).get();
12166
+ const schedule = app.db.select().from(schedules).where(and2(
12167
+ eq5(schedules.projectId, project.id),
12168
+ eq5(schedules.kind, SchedulableRunKinds["answer-visibility"])
12169
+ )).get();
12154
12170
  const notificationRows = app.db.select().from(notifications).where(eq5(notifications.projectId, project.id)).all();
12155
12171
  const config = {
12156
12172
  apiVersion: "canonry/v1",
@@ -12184,9 +12200,10 @@ async function projectRoutes(app, opts) {
12184
12200
  }),
12185
12201
  ...schedule ? {
12186
12202
  schedule: {
12187
- ...schedule.preset ? { preset: schedule.preset } : { cron: schedule.cronExpr },
12203
+ ...schedule.recurrence ? { recurrence: schedule.recurrence } : schedule.preset ? { preset: schedule.preset } : { cron: schedule.cronExpr },
12188
12204
  timezone: schedule.timezone,
12189
- providers: schedule.providers
12205
+ providers: schedule.providers,
12206
+ enabled: schedule.enabled
12190
12207
  }
12191
12208
  } : {}
12192
12209
  }
@@ -12239,15 +12256,15 @@ function aliasArraysEqual(a, b) {
12239
12256
 
12240
12257
  // ../api-routes/src/queries.ts
12241
12258
  import crypto10 from "crypto";
12242
- import { and as and4, eq as eq8, inArray as inArray2, sql as sql5 } from "drizzle-orm";
12259
+ import { and as and5, eq as eq8, inArray as inArray2, sql as sql5 } from "drizzle-orm";
12243
12260
 
12244
12261
  // ../api-routes/src/query-replace.ts
12245
12262
  import crypto9 from "crypto";
12246
- import { and as and3, eq as eq7, inArray, isNull as isNull2 } from "drizzle-orm";
12263
+ import { and as and4, eq as eq7, inArray, isNull as isNull2 } from "drizzle-orm";
12247
12264
 
12248
12265
  // ../api-routes/src/measurement-draft-repo.ts
12249
12266
  import crypto8 from "crypto";
12250
- import { and as and2, eq as eq6, gt, lte as lte2 } from "drizzle-orm";
12267
+ import { and as and3, eq as eq6, gt, lte as lte2 } from "drizzle-orm";
12251
12268
  var MEASUREMENT_RECEIPT_TTL_MS = 24 * 60 * 60 * 1e3;
12252
12269
  function compareText(left, right) {
12253
12270
  return left < right ? -1 : left > right ? 1 : 0;
@@ -12333,7 +12350,7 @@ function draftCounts(authoring) {
12333
12350
  function activePlanVersionRow(db, projectId) {
12334
12351
  const pointer = db.select().from(measurementPlans).where(eq6(measurementPlans.projectId, projectId)).get();
12335
12352
  if (!pointer) return null;
12336
- const version = db.select().from(measurementPlanVersions).where(and2(
12353
+ const version = db.select().from(measurementPlanVersions).where(and3(
12337
12354
  eq6(measurementPlanVersions.projectId, projectId),
12338
12355
  eq6(measurementPlanVersions.id, pointer.activeVersionId)
12339
12356
  )).get();
@@ -12362,7 +12379,7 @@ function requireIdempotencyKey(request, operation) {
12362
12379
  return value;
12363
12380
  }
12364
12381
  function replayReceipt(db, projectId, lookup, reply) {
12365
- const existing = db.select().from(measurementOperationReceipts).where(and2(
12382
+ const existing = db.select().from(measurementOperationReceipts).where(and3(
12366
12383
  eq6(measurementOperationReceipts.projectId, projectId),
12367
12384
  eq6(measurementOperationReceipts.operation, lookup.operation),
12368
12385
  eq6(measurementOperationReceipts.idempotencyKey, lookup.key),
@@ -12392,9 +12409,9 @@ function sweepExpiredMeasurementReceipts(db, now) {
12392
12409
 
12393
12410
  // ../api-routes/src/query-replace.ts
12394
12411
  function preserveSnapshotQueryText(tx, projectId, queryIds) {
12395
- const candidates = queryIds && queryIds.length > 0 ? tx.select({ id: queries.id, text: queries.query }).from(queries).where(and3(eq7(queries.projectId, projectId), inArray(queries.id, queryIds))).all() : tx.select({ id: queries.id, text: queries.query }).from(queries).where(eq7(queries.projectId, projectId)).all();
12412
+ const candidates = queryIds && queryIds.length > 0 ? tx.select({ id: queries.id, text: queries.query }).from(queries).where(and4(eq7(queries.projectId, projectId), inArray(queries.id, queryIds))).all() : tx.select({ id: queries.id, text: queries.query }).from(queries).where(eq7(queries.projectId, projectId)).all();
12396
12413
  for (const q of candidates) {
12397
- tx.update(querySnapshots).set({ queryText: q.text }).where(and3(eq7(querySnapshots.queryId, q.id), isNull2(querySnapshots.queryText))).run();
12414
+ tx.update(querySnapshots).set({ queryText: q.text }).where(and4(eq7(querySnapshots.queryId, q.id), isNull2(querySnapshots.queryText))).run();
12398
12415
  }
12399
12416
  }
12400
12417
  function changedQueryIdsForReplace(diff) {
@@ -12411,7 +12428,7 @@ function assertQueryCatalogMutationAllowed(tx, scope, affectedQueryIds, addsQuer
12411
12428
  const protectedIds = /* @__PURE__ */ new Set();
12412
12429
  const activePlan = tx.select({ activeVersionId: measurementPlans.activeVersionId }).from(measurementPlans).where(eq7(measurementPlans.projectId, scope.projectId)).get();
12413
12430
  if (activePlan) {
12414
- const version = tx.select({ canonicalJson: measurementPlanVersions.canonicalJson }).from(measurementPlanVersions).where(and3(
12431
+ const version = tx.select({ canonicalJson: measurementPlanVersions.canonicalJson }).from(measurementPlanVersions).where(and4(
12415
12432
  eq7(measurementPlanVersions.projectId, scope.projectId),
12416
12433
  eq7(measurementPlanVersions.id, activePlan.activeVersionId)
12417
12434
  )).get();
@@ -12452,7 +12469,7 @@ function assertQueryCatalogMutationAllowed(tx, scope, affectedQueryIds, addsQuer
12452
12469
  assertNoActivePlanlessSweep(tx, scope);
12453
12470
  }
12454
12471
  function assertNoActivePlanlessSweep(tx, scope) {
12455
- const activeRun = tx.select({ id: runs.id }).from(runs).where(and3(
12472
+ const activeRun = tx.select({ id: runs.id }).from(runs).where(and4(
12456
12473
  eq7(runs.projectId, scope.projectId),
12457
12474
  eq7(runs.kind, RunKinds["answer-visibility"]),
12458
12475
  inArray(runs.status, [RunStatuses.queued, RunStatuses.running]),
@@ -12650,7 +12667,7 @@ async function queryRoutes(app, opts) {
12650
12667
  app.delete("/projects/:name/queries/:id", async (request, reply) => {
12651
12668
  const project = resolveProject(app.db, request.params.name);
12652
12669
  app.db.transaction((tx) => {
12653
- const query = tx.select().from(queries).where(and4(eq8(queries.projectId, project.id), eq8(queries.id, request.params.id))).get();
12670
+ const query = tx.select().from(queries).where(and5(eq8(queries.projectId, project.id), eq8(queries.id, request.params.id))).get();
12654
12671
  if (!query) throw notFound("Query", request.params.id);
12655
12672
  assertQueryCatalogMutationAllowed(
12656
12673
  tx,
@@ -12882,7 +12899,7 @@ async function queryRoutes(app, opts) {
12882
12899
 
12883
12900
  // ../api-routes/src/competitors.ts
12884
12901
  import crypto11 from "crypto";
12885
- import { and as and5, eq as eq9 } from "drizzle-orm";
12902
+ import { and as and6, eq as eq9 } from "drizzle-orm";
12886
12903
  function normalizeCompetitor(domain) {
12887
12904
  const reg = registrableDomain(domain);
12888
12905
  if (reg) return reg;
@@ -12996,7 +13013,7 @@ async function competitorRoutes(app) {
12996
13013
  });
12997
13014
  app.delete("/projects/:name/competitors/:id", async (request, reply) => {
12998
13015
  const project = resolveProject(app.db, request.params.name);
12999
- const competitor = app.db.select().from(competitors).where(and5(eq9(competitors.projectId, project.id), eq9(competitors.id, request.params.id))).get();
13016
+ const competitor = app.db.select().from(competitors).where(and6(eq9(competitors.projectId, project.id), eq9(competitors.id, request.params.id))).get();
13000
13017
  if (!competitor) {
13001
13018
  throw notFound("Competitor", request.params.id);
13002
13019
  }
@@ -13026,7 +13043,7 @@ function parseCompetitorBatch(value) {
13026
13043
  }
13027
13044
 
13028
13045
  // ../api-routes/src/competitor-landscape.ts
13029
- import { and as and8, eq as eq14, gte as gte2, inArray as inArray4 } from "drizzle-orm";
13046
+ import { and as and9, eq as eq14, gte as gte2, inArray as inArray4 } from "drizzle-orm";
13030
13047
 
13031
13048
  // ../api-routes/src/mention-share-inputs.ts
13032
13049
  import { eq as eq10 } from "drizzle-orm";
@@ -13127,7 +13144,7 @@ function shareOfVoiceFromLandscape(landscape, queryClass) {
13127
13144
 
13128
13145
  // ../api-routes/src/measurement-overview.ts
13129
13146
  import { createHash } from "crypto";
13130
- import { and as and7, eq as eq13 } from "drizzle-orm";
13147
+ import { and as and8, eq as eq13 } from "drizzle-orm";
13131
13148
 
13132
13149
  // ../api-routes/src/measurement-report.ts
13133
13150
  var compareText2 = (left, right) => left < right ? -1 : left > right ? 1 : 0;
@@ -13836,7 +13853,7 @@ function buildMeasurementReport(input, options = {}) {
13836
13853
  }
13837
13854
 
13838
13855
  // ../api-routes/src/measurement-report-adapter.ts
13839
- import { and as and6, desc, eq as eq11, gte, inArray as inArray3, isNull as isNull3, lt as lt2, lte as lte3, ne as ne2 } from "drizzle-orm";
13856
+ import { and as and7, desc, eq as eq11, gte, inArray as inArray3, isNull as isNull3, lt as lt2, lte as lte3, ne as ne2 } from "drizzle-orm";
13840
13857
  function compareText3(left, right) {
13841
13858
  return left < right ? -1 : left > right ? 1 : 0;
13842
13859
  }
@@ -14133,7 +14150,7 @@ function comparableMeasurementVersionIds(db, projectId, versionId) {
14133
14150
  const seen2 = new Set(ids);
14134
14151
  let cursor = versionId;
14135
14152
  for (let step = 0; step < MEASUREMENT_COMPARABLE_VERSION_WALK_LIMIT; step++) {
14136
- const row = db.select({ comparableToVersionId: measurementPlanVersions.comparableToVersionId }).from(measurementPlanVersions).where(and6(
14153
+ const row = db.select({ comparableToVersionId: measurementPlanVersions.comparableToVersionId }).from(measurementPlanVersions).where(and7(
14137
14154
  eq11(measurementPlanVersions.projectId, projectId),
14138
14155
  eq11(measurementPlanVersions.id, cursor)
14139
14156
  )).get();
@@ -14165,10 +14182,10 @@ function latestMeasurementRun(db, projectId, versionId, statuses, window = {}, o
14165
14182
  ];
14166
14183
  if (window.from !== void 0) conditions.push(gte(runs.createdAt, window.from));
14167
14184
  if (window.to !== void 0) conditions.push(lte3(runs.createdAt, window.to));
14168
- return db.select().from(runs).where(and6(...conditions)).orderBy(desc(runs.createdAt), desc(runs.id)).get();
14185
+ return db.select().from(runs).where(and7(...conditions)).orderBy(desc(runs.createdAt), desc(runs.id)).get();
14169
14186
  }
14170
14187
  function pinnedMeasurementRun(db, projectId, versionId, runId, opts = {}) {
14171
- return db.select().from(runs).where(and6(
14188
+ return db.select().from(runs).where(and7(
14172
14189
  eq11(runs.id, runId),
14173
14190
  eq11(runs.projectId, projectId),
14174
14191
  opts.exactVersion ? eq11(runs.measurementPlanVersionId, versionId) : inArray3(runs.measurementPlanVersionId, comparableMeasurementVersionIds(db, projectId, versionId)),
@@ -14207,7 +14224,7 @@ function storedMeasurementPlanV2Report(db, projectId, version, plan, runId) {
14207
14224
  };
14208
14225
  }
14209
14226
  function buildStoredMeasurementReport(db, projectId, revision, runId) {
14210
- const version = db.select().from(measurementPlanVersions).where(and6(
14227
+ const version = db.select().from(measurementPlanVersions).where(and7(
14211
14228
  eq11(measurementPlanVersions.projectId, projectId),
14212
14229
  eq11(measurementPlanVersions.revision, revision)
14213
14230
  )).get();
@@ -14217,7 +14234,7 @@ function buildStoredMeasurementReport(db, projectId, revision, runId) {
14217
14234
  return storedMeasurementPlanV2Report(db, projectId, version, stored, runId);
14218
14235
  }
14219
14236
  const plan = stored;
14220
- const run = runId ? pinnedMeasurementRun(db, projectId, version.id, runId, { exactVersion: true }) : db.select().from(runs).where(and6(
14237
+ const run = runId ? pinnedMeasurementRun(db, projectId, version.id, runId, { exactVersion: true }) : db.select().from(runs).where(and7(
14221
14238
  eq11(runs.projectId, projectId),
14222
14239
  eq11(runs.measurementPlanVersionId, version.id),
14223
14240
  eq11(runs.kind, RunKinds["answer-visibility"]),
@@ -14231,7 +14248,7 @@ function buildStoredMeasurementReport(db, projectId, revision, runId) {
14231
14248
  if (selectedRun.measurementManifest === null) manifestFailure(`missing for run ${selectedRun.id}`);
14232
14249
  manifest = parseManifest(selectedRun.measurementManifest, frozenExecutionNodes(plan));
14233
14250
  } else if (runId === void 0) {
14234
- selectedRun = db.select().from(runs).where(and6(
14251
+ selectedRun = db.select().from(runs).where(and7(
14235
14252
  eq11(runs.projectId, projectId),
14236
14253
  isNull3(runs.measurementPlanVersionId),
14237
14254
  eq11(runs.kind, RunKinds["answer-visibility"]),
@@ -14357,7 +14374,7 @@ function parseOverviewQuery(raw) {
14357
14374
  function activeMeasurementPlan(db, projectId) {
14358
14375
  const pointer = db.select().from(measurementPlans).where(eq13(measurementPlans.projectId, projectId)).get();
14359
14376
  if (!pointer) return null;
14360
- const version = db.select().from(measurementPlanVersions).where(and7(
14377
+ const version = db.select().from(measurementPlanVersions).where(and8(
14361
14378
  eq13(measurementPlanVersions.projectId, projectId),
14362
14379
  eq13(measurementPlanVersions.id, pointer.activeVersionId)
14363
14380
  )).get();
@@ -14626,7 +14643,7 @@ function selectDisplayedRun(db, projectId, active, query) {
14626
14643
  to: windowBound(query.to, true)
14627
14644
  });
14628
14645
  }
14629
- const run = db.select().from(runs).where(and7(eq13(runs.projectId, projectId), eq13(runs.id, selectedRunId))).get();
14646
+ const run = db.select().from(runs).where(and8(eq13(runs.projectId, projectId), eq13(runs.id, selectedRunId))).get();
14630
14647
  if (!run) throw notFound("Run", selectedRunId);
14631
14648
  if (runVersionServesActiveVersion(db, projectId, active.version.id, run.measurementPlanVersionId)) return run;
14632
14649
  const pinned = run.measurementPlanVersionId === null ? null : db.select({ revision: measurementPlanVersions.revision }).from(measurementPlanVersions).where(eq13(measurementPlanVersions.id, run.measurementPlanVersionId)).get()?.revision ?? null;
@@ -14951,7 +14968,7 @@ function readCompetitorLandscape(app, projectName, query, selection) {
14951
14968
  if (selection?.autoAdvanced && activeMeasurementPlan(app.db, project.id)?.plan.schemaVersion === 2) filters.scope = "all-markets";
14952
14969
  const window = parseWindow(filters.window);
14953
14970
  const cutoff = windowCutoff(window);
14954
- const candidateRuns = app.db.select().from(runs).where(and8(
14971
+ const candidateRuns = app.db.select().from(runs).where(and9(
14955
14972
  eq14(runs.projectId, project.id),
14956
14973
  eq14(runs.kind, RunKinds["answer-visibility"]),
14957
14974
  selection ? inArray4(runs.id, [...selection.runIds]) : void 0,
@@ -15139,7 +15156,7 @@ function resolveAdvancedScope(app, projectId, kind, groupKey, candidateRuns) {
15139
15156
  throw validationError(`Unknown Advanced Measurement group "${groupKey ?? ""}".`);
15140
15157
  }
15141
15158
  const planVersionIds = [...new Set(candidateRuns.map((run) => run.measurementPlanVersionId).filter((id) => typeof id === "string" && id.length > 0))];
15142
- const historicalVersions = planVersionIds.length === 0 ? [] : app.db.select().from(measurementPlanVersions).where(and8(
15159
+ const historicalVersions = planVersionIds.length === 0 ? [] : app.db.select().from(measurementPlanVersions).where(and9(
15143
15160
  eq14(measurementPlanVersions.projectId, projectId),
15144
15161
  inArray4(measurementPlanVersions.id, planVersionIds)
15145
15162
  )).all();
@@ -15279,16 +15296,16 @@ function mergePins(...collections) {
15279
15296
 
15280
15297
  // ../api-routes/src/runs.ts
15281
15298
  import crypto14 from "crypto";
15282
- import { and as and11, eq as eq17, asc, desc as desc3, inArray as inArray5, or as or2, sql as sql6 } from "drizzle-orm";
15299
+ import { and as and12, eq as eq17, asc, desc as desc3, inArray as inArray5, or as or2, sql as sql6 } from "drizzle-orm";
15283
15300
  import { gte as gte3 } from "drizzle-orm";
15284
15301
 
15285
15302
  // ../api-routes/src/run-queue.ts
15286
15303
  import crypto13 from "crypto";
15287
- import { and as and10, eq as eq16, or } from "drizzle-orm";
15304
+ import { and as and11, eq as eq16, or } from "drizzle-orm";
15288
15305
 
15289
15306
  // ../api-routes/src/query-basket.ts
15290
15307
  import crypto12 from "crypto";
15291
- import { and as and9, desc as desc2, eq as eq15 } from "drizzle-orm";
15308
+ import { and as and10, desc as desc2, eq as eq15 } from "drizzle-orm";
15292
15309
  function queryBasketMembers(db, projectId) {
15293
15310
  const rows = db.select({ query: queries.query }).from(queries).where(eq15(queries.projectId, projectId)).all();
15294
15311
  return [...new Set(rows.map((row) => normalizeQueryText(row.query)))].sort();
@@ -15539,7 +15556,7 @@ function measurementStamp(tx, params) {
15539
15556
  }
15540
15557
  return null;
15541
15558
  }
15542
- const version = tx.select().from(measurementPlanVersions).where(and10(
15559
+ const version = tx.select().from(measurementPlanVersions).where(and11(
15543
15560
  eq16(measurementPlanVersions.projectId, params.projectId),
15544
15561
  eq16(measurementPlanVersions.id, active.activeVersionId)
15545
15562
  )).get();
@@ -15617,18 +15634,40 @@ function queueRunIfProjectIdle(db, params) {
15617
15634
  const kind = params.kind ?? "answer-visibility";
15618
15635
  const trigger = params.trigger ?? "manual";
15619
15636
  const runId = crypto13.randomUUID();
15637
+ if (params.scheduleClaim && trigger !== RunTriggers.scheduled) {
15638
+ throw new Error("Schedule claims require a scheduled run trigger");
15639
+ }
15620
15640
  return db.transaction((tx) => {
15621
- const activeRun = tx.select().from(runs).where(
15622
- and10(
15641
+ const activeRun = () => tx.select().from(runs).where(
15642
+ and11(
15623
15643
  eq16(runs.projectId, params.projectId),
15624
15644
  eq16(runs.kind, kind),
15625
15645
  or(eq16(runs.status, "queued"), eq16(runs.status, "running"))
15626
15646
  )
15627
15647
  ).get();
15628
- if (activeRun) {
15629
- return { conflict: true, activeRunId: activeRun.id };
15648
+ if (!params.scheduleClaim) {
15649
+ const current2 = activeRun();
15650
+ if (current2) return { conflict: true, activeRunId: current2.id };
15630
15651
  }
15631
15652
  const stamp = measurementStamp(tx, params);
15653
+ if (params.scheduleClaim) {
15654
+ const claim = tx.update(schedules).set({
15655
+ nextRunAt: params.scheduleClaim.nextRunAt,
15656
+ updatedAt: nextScheduleUpdatedAt(params.scheduleClaim.expectedUpdatedAt)
15657
+ }).where(and11(
15658
+ eq16(schedules.id, params.scheduleClaim.scheduleId),
15659
+ eq16(schedules.projectId, params.projectId),
15660
+ eq16(schedules.kind, kind),
15661
+ eq16(schedules.enabled, true),
15662
+ eq16(schedules.nextRunAt, params.scheduleClaim.dueAt),
15663
+ eq16(schedules.updatedAt, params.scheduleClaim.expectedUpdatedAt)
15664
+ )).run();
15665
+ if (claim.changes !== 1) {
15666
+ return { conflict: true, activeRunId: params.scheduleClaim.scheduleId, scheduleClaimed: false };
15667
+ }
15668
+ }
15669
+ const current = activeRun();
15670
+ if (current) return { conflict: true, activeRunId: current.id };
15632
15671
  const scoped = params.queries != null || stamp?.scope != null;
15633
15672
  const basket = scoped ? null : ensureCurrentQueryBasketRevision(tx, params.projectId, createdAt);
15634
15673
  tx.insert(runs).values({
@@ -15746,7 +15785,7 @@ async function runRoutes(app, opts) {
15746
15785
  throw validationError("No locations configured for this project");
15747
15786
  }
15748
15787
  const result = app.db.transaction((tx) => {
15749
- const activeRun = tx.select({ id: runs.id }).from(runs).where(and11(
15788
+ const activeRun = tx.select({ id: runs.id }).from(runs).where(and12(
15750
15789
  eq17(runs.projectId, project.id),
15751
15790
  eq17(runs.kind, kind),
15752
15791
  or2(eq17(runs.status, "queued"), eq17(runs.status, "running"))
@@ -15824,15 +15863,15 @@ async function runRoutes(app, opts) {
15824
15863
  const parsedLimit = parseInt(request.query.limit ?? "", 10);
15825
15864
  const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? void 0 : parsedLimit;
15826
15865
  const kind = parseListKind(request.query.kind);
15827
- const where = kind ? and11(eq17(runs.projectId, project.id), eq17(runs.kind, kind)) : eq17(runs.projectId, project.id);
15866
+ const where = kind ? and12(eq17(runs.projectId, project.id), eq17(runs.kind, kind)) : eq17(runs.projectId, project.id);
15828
15867
  const rows = limit == null ? app.db.select().from(runs).where(where).orderBy(asc(runs.createdAt)).all() : app.db.select().from(runs).where(where).orderBy(desc3(runs.createdAt)).limit(limit).all().reverse();
15829
15868
  return reply.send(rows.map(formatRun));
15830
15869
  });
15831
15870
  app.get("/projects/:name/runs/latest", async (request, reply) => {
15832
15871
  const project = resolveProject(app.db, request.params.name);
15833
- const countRow = app.db.select({ count: sql6`count(*)` }).from(runs).where(and11(eq17(runs.projectId, project.id), notProbeRun())).get();
15872
+ const countRow = app.db.select({ count: sql6`count(*)` }).from(runs).where(and12(eq17(runs.projectId, project.id), notProbeRun())).get();
15834
15873
  const totalRuns = countRow?.count ?? 0;
15835
- const latestRun = app.db.select().from(runs).where(and11(eq17(runs.projectId, project.id), notProbeRun())).orderBy(desc3(runs.createdAt), desc3(runs.id)).limit(1).get();
15874
+ const latestRun = app.db.select().from(runs).where(and12(eq17(runs.projectId, project.id), notProbeRun())).orderBy(desc3(runs.createdAt), desc3(runs.id)).limit(1).get();
15836
15875
  if (!latestRun) {
15837
15876
  return reply.send({ totalRuns: 0, run: null });
15838
15877
  }
@@ -15851,7 +15890,7 @@ async function runRoutes(app, opts) {
15851
15890
  if (kind) filters.push(eq17(runs.kind, kind));
15852
15891
  const scopedProjectId = request.apiKey?.projectId;
15853
15892
  if (scopedProjectId) filters.push(eq17(runs.projectId, scopedProjectId));
15854
- const rows = app.db.select().from(runs).where(and11(...filters)).orderBy(desc3(runs.createdAt), desc3(runs.id)).limit(limit).all();
15893
+ const rows = app.db.select().from(runs).where(and12(...filters)).orderBy(desc3(runs.createdAt), desc3(runs.id)).limit(limit).all();
15855
15894
  return reply.send(rows.map(formatRun));
15856
15895
  });
15857
15896
  app.post("/runs", async (request, reply) => {
@@ -15982,7 +16021,7 @@ async function runRoutes(app, opts) {
15982
16021
  const terminalStatuses = /* @__PURE__ */ new Set(["completed", "partial", "failed", "cancelled"]);
15983
16022
  if (terminalStatuses.has(run.status)) throw runNotCancellable(run.id, run.status);
15984
16023
  const now = (/* @__PURE__ */ new Date()).toISOString();
15985
- const cancelled = app.db.update(runs).set({ status: "cancelled", finishedAt: now, error: serializeRunError({ message: "Cancelled by user" }) }).where(and11(eq17(runs.id, run.id), inArray5(runs.status, ["queued", "running"]))).run();
16024
+ const cancelled = app.db.update(runs).set({ status: "cancelled", finishedAt: now, error: serializeRunError({ message: "Cancelled by user" }) }).where(and12(eq17(runs.id, run.id), inArray5(runs.status, ["queued", "running"]))).run();
15986
16025
  if (cancelled.changes === 0) {
15987
16026
  const current = app.db.select().from(runs).where(eq17(runs.id, run.id)).get();
15988
16027
  if (!current) throw notFound("Run", run.id);
@@ -16183,7 +16222,7 @@ function loadRunDetail(app, run) {
16183
16222
 
16184
16223
  // ../api-routes/src/measurement-plan.ts
16185
16224
  import crypto15 from "crypto";
16186
- import { and as and12, desc as desc4, eq as eq18 } from "drizzle-orm";
16225
+ import { and as and13, desc as desc4, eq as eq18 } from "drizzle-orm";
16187
16226
  var MEASUREMENT_PLAN_WRITE_SCOPE = "measurement-plan.write";
16188
16227
  function parseStoredPlanAnyVersion(canonicalJson3) {
16189
16228
  return parseStoredMeasurementPlanAnyVersion(canonicalJson3);
@@ -16220,7 +16259,7 @@ function versionMetadata(row, activeVersionId) {
16220
16259
  function activePlanVersion(app, projectId) {
16221
16260
  const plan = app.db.select().from(measurementPlans).where(eq18(measurementPlans.projectId, projectId)).get();
16222
16261
  if (!plan) return null;
16223
- const version = app.db.select().from(measurementPlanVersions).where(and12(
16262
+ const version = app.db.select().from(measurementPlanVersions).where(and13(
16224
16263
  eq18(measurementPlanVersions.projectId, projectId),
16225
16264
  eq18(measurementPlanVersions.id, plan.activeVersionId)
16226
16265
  )).get();
@@ -16439,7 +16478,7 @@ async function measurementPlanRoutes(app, opts) {
16439
16478
  const desired = desiredSegmentKinds(compiled);
16440
16479
  const published = app.db.transaction((tx) => {
16441
16480
  const activePlan = tx.select().from(measurementPlans).where(eq18(measurementPlans.projectId, project.id)).get();
16442
- const activeVersion = activePlan ? tx.select().from(measurementPlanVersions).where(and12(
16481
+ const activeVersion = activePlan ? tx.select().from(measurementPlanVersions).where(and13(
16443
16482
  eq18(measurementPlanVersions.projectId, project.id),
16444
16483
  eq18(measurementPlanVersions.id, activePlan.activeVersionId)
16445
16484
  )).get() : null;
@@ -16508,7 +16547,7 @@ async function measurementPlanRoutes(app, opts) {
16508
16547
  app.post("/projects/:name/measurement-plan/segments/:stableKey/retire", async (request, reply) => {
16509
16548
  requireScope(request, MEASUREMENT_PLAN_WRITE_SCOPE);
16510
16549
  const project = resolveProject(app.db, request.params.name);
16511
- const segment = app.db.select().from(measurementSegments).where(and12(
16550
+ const segment = app.db.select().from(measurementSegments).where(and13(
16512
16551
  eq18(measurementSegments.projectId, project.id),
16513
16552
  eq18(measurementSegments.stableKey, request.params.stableKey)
16514
16553
  )).get();
@@ -16545,7 +16584,7 @@ async function measurementPlanRoutes(app, opts) {
16545
16584
  throw notFound("Measurement plan revision", request.params.revision);
16546
16585
  }
16547
16586
  const revision = Number(request.params.revision);
16548
- const row = app.db.select().from(measurementPlanVersions).where(and12(
16587
+ const row = app.db.select().from(measurementPlanVersions).where(and13(
16549
16588
  eq18(measurementPlanVersions.projectId, project.id),
16550
16589
  eq18(measurementPlanVersions.revision, revision)
16551
16590
  )).get();
@@ -17275,7 +17314,7 @@ async function measurementServiceRoutes(app, opts) {
17275
17314
 
17276
17315
  // ../api-routes/src/measurement-draft.ts
17277
17316
  import crypto18 from "crypto";
17278
- import { and as and13, asc as asc2, desc as desc5, eq as eq19, inArray as inArray6 } from "drizzle-orm";
17317
+ import { and as and14, asc as asc2, desc as desc5, eq as eq19, inArray as inArray6 } from "drizzle-orm";
17279
17318
 
17280
17319
  // ../api-routes/src/measurement-draft-compile.ts
17281
17320
  var MEASUREMENT_DRAFT_MAX_TARGETS = 1e3;
@@ -19372,7 +19411,7 @@ async function measurementDraftRoutes(app, opts = {}) {
19372
19411
  const active = activePlanVersionRow(app.db, project.id);
19373
19412
  const draft = draftRow(app.db, project.id);
19374
19413
  const activeSchemaVersion = active ? active.schemaVersion === 2 ? 2 : 1 : null;
19375
- const completedRun = active ? app.db.select({ id: runs.id }).from(runs).where(and13(
19414
+ const completedRun = active ? app.db.select({ id: runs.id }).from(runs).where(and14(
19376
19415
  eq19(runs.projectId, project.id),
19377
19416
  // The comparable chain, not the bare active id: a label-only
19378
19417
  // republish must not flip setup back to awaiting_first_run while
@@ -19556,7 +19595,7 @@ async function measurementDraftRoutes(app, opts = {}) {
19556
19595
  etagVersion,
19557
19596
  updatedBy: serializeActor(gate.actor),
19558
19597
  updatedAt: now.toISOString()
19559
- }).where(and13(
19598
+ }).where(and14(
19560
19599
  eq19(measurementPlanDrafts.id, row.id),
19561
19600
  eq19(measurementPlanDrafts.etagVersion, row.etagVersion)
19562
19601
  )).run();
@@ -19603,7 +19642,7 @@ async function measurementDraftRoutes(app, opts = {}) {
19603
19642
  etagVersion,
19604
19643
  updatedBy: serializeActor(gate.actor),
19605
19644
  updatedAt: now.toISOString()
19606
- }).where(and13(
19645
+ }).where(and14(
19607
19646
  eq19(measurementPlanDrafts.id, row.id),
19608
19647
  eq19(measurementPlanDrafts.etagVersion, row.etagVersion)
19609
19648
  )).run();
@@ -19726,7 +19765,7 @@ async function measurementDraftRoutes(app, opts = {}) {
19726
19765
  etagVersion,
19727
19766
  updatedBy: serializeActor(gate.actor),
19728
19767
  updatedAt: now.toISOString()
19729
- }).where(and13(
19768
+ }).where(and14(
19730
19769
  eq19(measurementPlanDrafts.id, row.id),
19731
19770
  eq19(measurementPlanDrafts.etagVersion, row.etagVersion)
19732
19771
  )).run();
@@ -19965,7 +20004,7 @@ async function measurementDraftRoutes(app, opts = {}) {
19965
20004
  });
19966
20005
  });
19967
20006
  function querySetDetail(projectId, setId) {
19968
- const set = app.db.select().from(measurementQuerySets).where(and13(
20007
+ const set = app.db.select().from(measurementQuerySets).where(and14(
19969
20008
  eq19(measurementQuerySets.projectId, projectId),
19970
20009
  eq19(measurementQuerySets.id, setId)
19971
20010
  )).get();
@@ -20021,7 +20060,7 @@ async function measurementDraftRoutes(app, opts = {}) {
20021
20060
  for (const queryId of parsed.data.queryIds) {
20022
20061
  if (!known.has(queryId)) throw notFound("Query", queryId);
20023
20062
  }
20024
- const existing = app.db.select().from(measurementQuerySets).where(and13(
20063
+ const existing = app.db.select().from(measurementQuerySets).where(and14(
20025
20064
  eq19(measurementQuerySets.projectId, project.id),
20026
20065
  eq19(measurementQuerySets.id, setId)
20027
20066
  )).get();
@@ -20063,7 +20102,7 @@ async function measurementDraftRoutes(app, opts = {}) {
20063
20102
  app.delete("/projects/:name/measurement-query-sets/:setId", async (request, reply) => {
20064
20103
  requireScope(request, MEASUREMENT_PLAN_WRITE_SCOPE);
20065
20104
  const project = resolveProject(app.db, request.params.name);
20066
- const existing = app.db.select().from(measurementQuerySets).where(and13(
20105
+ const existing = app.db.select().from(measurementQuerySets).where(and14(
20067
20106
  eq19(measurementQuerySets.projectId, project.id),
20068
20107
  eq19(measurementQuerySets.id, request.params.setId)
20069
20108
  )).get();
@@ -20108,7 +20147,7 @@ async function measurementDraftRoutes(app, opts = {}) {
20108
20147
  }
20109
20148
  }
20110
20149
  const templateId = request.params.templateId;
20111
- const existing = app.db.select().from(measurementQueryTemplates).where(and13(
20150
+ const existing = app.db.select().from(measurementQueryTemplates).where(and14(
20112
20151
  eq19(measurementQueryTemplates.projectId, project.id),
20113
20152
  eq19(measurementQueryTemplates.id, templateId)
20114
20153
  )).get();
@@ -20148,7 +20187,7 @@ async function measurementDraftRoutes(app, opts = {}) {
20148
20187
  app.delete("/projects/:name/measurement-query-templates/:templateId", async (request, reply) => {
20149
20188
  requireScope(request, MEASUREMENT_PLAN_WRITE_SCOPE);
20150
20189
  const project = resolveProject(app.db, request.params.name);
20151
- const existing = app.db.select().from(measurementQueryTemplates).where(and13(
20190
+ const existing = app.db.select().from(measurementQueryTemplates).where(and14(
20152
20191
  eq19(measurementQueryTemplates.projectId, project.id),
20153
20192
  eq19(measurementQueryTemplates.id, request.params.templateId)
20154
20193
  )).get();
@@ -20170,7 +20209,7 @@ async function measurementDraftRoutes(app, opts = {}) {
20170
20209
  if (gate.replay !== null) return gate.replay;
20171
20210
  const parsed = measurementQueryTemplateApplyRequestSchema.safeParse(request.body);
20172
20211
  if (!parsed.success) throw validationError("Invalid template apply payload", { issues: parsed.error.issues });
20173
- const template = app.db.select().from(measurementQueryTemplates).where(and13(
20212
+ const template = app.db.select().from(measurementQueryTemplates).where(and14(
20174
20213
  eq19(measurementQueryTemplates.projectId, gate.project.id),
20175
20214
  eq19(measurementQueryTemplates.id, request.params.templateId)
20176
20215
  )).get();
@@ -20196,7 +20235,7 @@ async function measurementDraftRoutes(app, opts = {}) {
20196
20235
  (found ? existing : created).push(entry);
20197
20236
  return entry;
20198
20237
  });
20199
- const querySet = parsed.data.querySetId ? app.db.select().from(measurementQuerySets).where(and13(
20238
+ const querySet = parsed.data.querySetId ? app.db.select().from(measurementQuerySets).where(and14(
20200
20239
  eq19(measurementQuerySets.projectId, gate.project.id),
20201
20240
  eq19(measurementQuerySets.id, parsed.data.querySetId)
20202
20241
  )).get() : void 0;
@@ -20241,7 +20280,7 @@ async function measurementDraftRoutes(app, opts = {}) {
20241
20280
 
20242
20281
  // ../api-routes/src/measurement-discovery-v2.ts
20243
20282
  import crypto19 from "crypto";
20244
- import { and as and14, eq as eq20 } from "drizzle-orm";
20283
+ import { and as and15, eq as eq20 } from "drizzle-orm";
20245
20284
  var MEASUREMENT_DISCOVERY_COMPILER_VERSION = "measurement-discovery/2.0";
20246
20285
  var MEASUREMENT_REBIND_THRESHOLD = 0.7;
20247
20286
  var DISCOVERY_URL_LIMIT = 1e4;
@@ -20384,7 +20423,7 @@ function requireIdempotencyKey2(request, operation) {
20384
20423
  return supplied;
20385
20424
  }
20386
20425
  function replayedResponse(app, projectId, operation, idempotencyKey, requestChecksum2) {
20387
- const receipt = app.db.select().from(measurementOperationReceipts).where(and14(
20426
+ const receipt = app.db.select().from(measurementOperationReceipts).where(and15(
20388
20427
  eq20(measurementOperationReceipts.projectId, projectId),
20389
20428
  eq20(measurementOperationReceipts.operation, operation),
20390
20429
  eq20(measurementOperationReceipts.idempotencyKey, idempotencyKey)
@@ -20426,7 +20465,7 @@ function commitDraft(app, input) {
20426
20465
  }).where(eq20(measurementPlanDrafts.id, input.draft.row.id)).run();
20427
20466
  }
20428
20467
  if (input.discovery) {
20429
- const existing = tx.select().from(measurementDiscoveryConfigs).where(and14(
20468
+ const existing = tx.select().from(measurementDiscoveryConfigs).where(and15(
20430
20469
  eq20(measurementDiscoveryConfigs.projectId, input.project.id),
20431
20470
  eq20(measurementDiscoveryConfigs.inputChecksum, input.discovery.inputChecksum)
20432
20471
  )).get();
@@ -20710,7 +20749,7 @@ async function measurementDiscoveryV2Routes(app) {
20710
20749
 
20711
20750
  // ../api-routes/src/measurement-property-evidence.ts
20712
20751
  import { createHash as createHash2 } from "crypto";
20713
- import { and as and15, eq as eq21 } from "drizzle-orm";
20752
+ import { and as and16, eq as eq21 } from "drizzle-orm";
20714
20753
  function parseEvidenceQuery(raw) {
20715
20754
  const candidate = { ...raw, ...raw.limit === void 0 ? {} : { limit: Number(raw.limit) } };
20716
20755
  const parsed = measurementPropertyEvidenceQuerySchema.safeParse(candidate);
@@ -20782,7 +20821,7 @@ function selectDisplayedRun2(db, projectId, active, runId) {
20782
20821
  if (runId === void 0) {
20783
20822
  return latestMeasurementRun(db, projectId, active.version.id, [RunStatuses.completed]);
20784
20823
  }
20785
- const run = db.select().from(runs).where(and15(eq21(runs.projectId, projectId), eq21(runs.id, runId))).get();
20824
+ const run = db.select().from(runs).where(and16(eq21(runs.projectId, projectId), eq21(runs.id, runId))).get();
20786
20825
  if (!run) throw notFound("Run", runId);
20787
20826
  if (runVersionServesActiveVersion(db, projectId, active.version.id, run.measurementPlanVersionId)) return run;
20788
20827
  const pinned = run.measurementPlanVersionId === null ? null : db.select({ revision: measurementPlanVersions.revision }).from(measurementPlanVersions).where(eq21(measurementPlanVersions.id, run.measurementPlanVersionId)).get()?.revision ?? null;
@@ -20897,10 +20936,10 @@ async function measurementPropertyEvidenceRoutes(app) {
20897
20936
  }
20898
20937
 
20899
20938
  // ../api-routes/src/measurement-portfolio-reads.ts
20900
- import { and as and17, desc as desc6, eq as eq23, inArray as inArray8, lt as lt3, or as or3 } from "drizzle-orm";
20939
+ import { and as and18, desc as desc6, eq as eq23, inArray as inArray8, lt as lt3, or as or3 } from "drizzle-orm";
20901
20940
 
20902
20941
  // ../api-routes/src/measurement-question-reads.ts
20903
- import { and as and16, eq as eq22, getTableColumns, inArray as inArray7, sql as sql7 } from "drizzle-orm";
20942
+ import { and as and17, eq as eq22, getTableColumns, inArray as inArray7, sql as sql7 } from "drizzle-orm";
20904
20943
  var DEFAULT_LIMIT2 = 50;
20905
20944
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
20906
20945
  RunStatuses.completed,
@@ -20978,7 +21017,7 @@ function selectMeasurementQuestionRun(db, projectId, active, runId) {
20978
21017
  if (runId === void 0) {
20979
21018
  return latestMeasurementRun(db, projectId, active.version.id, [RunStatuses.completed]);
20980
21019
  }
20981
- const run = db.select().from(runs).where(and16(
21020
+ const run = db.select().from(runs).where(and17(
20982
21021
  eq22(runs.id, runId),
20983
21022
  eq22(runs.projectId, projectId)
20984
21023
  )).get();
@@ -21021,7 +21060,7 @@ function selectedQuestionSnapshots(db, runId, manifest, scope) {
21021
21060
  if (scope?.provider !== void 0) {
21022
21061
  conditions.push(sql7`lower(trim(${querySnapshots.provider})) = ${normalizeText(scope.provider)}`);
21023
21062
  }
21024
- return db.select(measurementQuestionSnapshotColumns).from(querySnapshots).where(and16(...conditions)).all();
21063
+ return db.select(measurementQuestionSnapshotColumns).from(querySnapshots).where(and17(...conditions)).all();
21025
21064
  }
21026
21065
  function rawResponsesForLegacyQuestionSnapshots(db, runId, snapshots) {
21027
21066
  const legacyIds = snapshots.filter((snapshot) => snapshot.citedUrls === null).map((snapshot) => snapshot.id);
@@ -21029,7 +21068,7 @@ function rawResponsesForLegacyQuestionSnapshots(db, runId, snapshots) {
21029
21068
  return new Map(db.select({
21030
21069
  id: querySnapshots.id,
21031
21070
  rawResponse: querySnapshots.rawResponse
21032
- }).from(querySnapshots).where(and16(
21071
+ }).from(querySnapshots).where(and17(
21033
21072
  eq22(querySnapshots.runId, runId),
21034
21073
  inArray7(querySnapshots.id, legacyIds)
21035
21074
  )).all().map((snapshot) => [snapshot.id, snapshot.rawResponse]));
@@ -21222,7 +21261,7 @@ async function measurementQuestionReadRoutes(app) {
21222
21261
  const target = requireTarget2(plan, query.targetKey);
21223
21262
  const snapshot = app.db.select(measurementQuestionSnapshotColumns).from(querySnapshots).where(eq22(querySnapshots.id, query.resultId)).get();
21224
21263
  if (!snapshot) throw notFound("Measurement question result", query.resultId);
21225
- const run = app.db.select().from(runs).where(and16(
21264
+ const run = app.db.select().from(runs).where(and17(
21226
21265
  eq22(runs.id, snapshot.runId),
21227
21266
  eq22(runs.projectId, project.id)
21228
21267
  )).get();
@@ -21673,7 +21712,7 @@ function previousComparableRun(db, active, current) {
21673
21712
  trigger: runs.trigger,
21674
21713
  measurementScope: runs.measurementScope,
21675
21714
  measurementExecutionIdentity: runs.measurementExecutionIdentity
21676
- }).from(runs).where(and17(
21715
+ }).from(runs).where(and18(
21677
21716
  eq23(runs.projectId, current.projectId),
21678
21717
  // The comparable chain keeps period-over-period alive across a label-only
21679
21718
  // republish: a run pinned to a comparable prior revision measured exactly
@@ -21684,7 +21723,7 @@ function previousComparableRun(db, active, current) {
21684
21723
  inArray8(runs.status, [RunStatuses.completed, RunStatuses.partial]),
21685
21724
  or3(
21686
21725
  lt3(runs.createdAt, current.createdAt),
21687
- and17(eq23(runs.createdAt, current.createdAt), lt3(runs.id, current.id))
21726
+ and18(eq23(runs.createdAt, current.createdAt), lt3(runs.id, current.id))
21688
21727
  )
21689
21728
  )).orderBy(desc6(runs.createdAt), desc6(runs.id)).all().filter((candidate) => (
21690
21729
  // Full reporting never admits probes. A plan slice is itself stored as a
@@ -21707,7 +21746,7 @@ function previousComparableRun(db, active, current) {
21707
21746
  if (!previousCandidate) {
21708
21747
  return { state: "unavailable", reason: "incomplete" };
21709
21748
  }
21710
- const previous = db.select().from(runs).where(and17(
21749
+ const previous = db.select().from(runs).where(and18(
21711
21750
  eq23(runs.id, previousCandidate.id),
21712
21751
  eq23(runs.projectId, current.projectId)
21713
21752
  )).get();
@@ -21948,7 +21987,7 @@ async function measurementPortfolioReadRoutes(app) {
21948
21987
 
21949
21988
  // ../api-routes/src/visibility-report.ts
21950
21989
  import { createHash as createHash4 } from "crypto";
21951
- import { and as and18, desc as desc7, eq as eq24, gte as gte4, inArray as inArray9, isNotNull, isNull as isNull4, lte as lte4, ne as ne3 } from "drizzle-orm";
21990
+ import { and as and19, desc as desc7, eq as eq24, gte as gte4, inArray as inArray9, isNotNull, isNull as isNull4, lte as lte4, ne as ne3 } from "drizzle-orm";
21952
21991
 
21953
21992
  // ../api-routes/src/visibility-report-reader.ts
21954
21993
  import { createHash as createHash3 } from "crypto";
@@ -22972,7 +23011,7 @@ function unsupportedAdvancedResponse(query, active) {
22972
23011
  });
22973
23012
  }
22974
23013
  function completedVisibilityRuns(db, projectId, planless, query) {
22975
- return db.select().from(runs).where(and18(
23014
+ return db.select().from(runs).where(and19(
22976
23015
  eq24(runs.projectId, projectId),
22977
23016
  eq24(runs.kind, RunKinds["answer-visibility"]),
22978
23017
  inArray9(runs.status, [RunStatuses.completed, RunStatuses.partial]),
@@ -23087,7 +23126,7 @@ function advancedReaderInput(db, projectId, active, query) {
23087
23126
  }
23088
23127
  function simpleReaderInput(db, project, query) {
23089
23128
  const sourceRuns = completedVisibilityRuns(db, project.id, true, query);
23090
- const frozen = new Map(sourceRuns.length === 0 ? [] : db.select().from(simpleMeasurementDefinitions).where(and18(
23129
+ const frozen = new Map(sourceRuns.length === 0 ? [] : db.select().from(simpleMeasurementDefinitions).where(and19(
23091
23130
  eq24(simpleMeasurementDefinitions.projectId, project.id),
23092
23131
  inArray9(simpleMeasurementDefinitions.runId, sourceRuns.map((run) => run.id))
23093
23132
  )).all().map((row) => [row.runId, row.definition]));
@@ -23145,7 +23184,7 @@ async function visibilityReportRoutes(app) {
23145
23184
 
23146
23185
  // ../api-routes/src/query-tracking.ts
23147
23186
  import crypto20 from "crypto";
23148
- import { and as and19, desc as desc8, eq as eq25, inArray as inArray10, isNotNull as isNotNull2, isNull as isNull5, ne as ne4 } from "drizzle-orm";
23187
+ import { and as and20, desc as desc8, eq as eq25, inArray as inArray10, isNotNull as isNotNull2, isNull as isNull5, ne as ne4 } from "drizzle-orm";
23149
23188
  var SOURCE_LIMIT = 100;
23150
23189
  var MAX_REVIEW_AGE_MS = 15 * 60 * 1e3;
23151
23190
  var MAX_REVIEW_FUTURE_SKEW_MS = 60 * 1e3;
@@ -23324,7 +23363,7 @@ function simpleMeasuredAtByQuery(db, project, queryRows2, opts) {
23324
23363
  id: runs.id,
23325
23364
  finishedAt: runs.finishedAt,
23326
23365
  createdAt: runs.createdAt
23327
- }).from(runs).where(and19(
23366
+ }).from(runs).where(and20(
23328
23367
  eq25(runs.projectId, project.id),
23329
23368
  eq25(runs.kind, RunKinds["answer-visibility"]),
23330
23369
  inArray10(runs.status, [RunStatuses.completed, RunStatuses.partial]),
@@ -23337,7 +23376,7 @@ function simpleMeasuredAtByQuery(db, project, queryRows2, opts) {
23337
23376
  for (const row of db.select({
23338
23377
  runId: simpleMeasurementDefinitions.runId,
23339
23378
  definition: simpleMeasurementDefinitions.definition
23340
- }).from(simpleMeasurementDefinitions).where(and19(
23379
+ }).from(simpleMeasurementDefinitions).where(and20(
23341
23380
  eq25(simpleMeasurementDefinitions.projectId, project.id),
23342
23381
  inArray10(simpleMeasurementDefinitions.runId, eligibleRuns.map((run) => run.id))
23343
23382
  )).all()) {
@@ -23445,7 +23484,7 @@ function measuredAtByQuery(db, state, opts) {
23445
23484
  if (active === null) return simpleMeasuredAtByQuery(db, project, state.queryRows, opts);
23446
23485
  const activeSignatures = assignmentSignaturesByQuery(active.plan);
23447
23486
  if (activeSignatures.size === 0) return /* @__PURE__ */ new Map();
23448
- const eligibleRuns = db.select().from(runs).where(and19(
23487
+ const eligibleRuns = db.select().from(runs).where(and20(
23449
23488
  eq25(runs.projectId, project.id),
23450
23489
  eq25(runs.kind, RunKinds["answer-visibility"]),
23451
23490
  inArray10(runs.status, [RunStatuses.completed, RunStatuses.partial]),
@@ -23459,7 +23498,7 @@ function measuredAtByQuery(db, state, opts) {
23459
23498
  id: measurementPlanVersions.id,
23460
23499
  revision: measurementPlanVersions.revision,
23461
23500
  canonicalJson: measurementPlanVersions.canonicalJson
23462
- }).from(measurementPlanVersions).where(and19(
23501
+ }).from(measurementPlanVersions).where(and20(
23463
23502
  eq25(measurementPlanVersions.projectId, project.id),
23464
23503
  inArray10(measurementPlanVersions.id, versionIds)
23465
23504
  )).all().flatMap((row) => {
@@ -23767,7 +23806,7 @@ function sourceRecord(db, projectId, addition, manualCapturedAt) {
23767
23806
  id: researchRunQueries.id,
23768
23807
  queryText: researchRunQueries.queryText,
23769
23808
  createdAt: researchRunQueries.createdAt
23770
- }).from(researchRunQueries).innerJoin(researchRuns, eq25(researchRunQueries.researchRunId, researchRuns.id)).where(and19(eq25(researchRuns.projectId, projectId), eq25(researchRunQueries.id, addition.input.researchRunQueryId))).get();
23809
+ }).from(researchRunQueries).innerJoin(researchRuns, eq25(researchRunQueries.researchRunId, researchRuns.id)).where(and20(eq25(researchRuns.projectId, projectId), eq25(researchRunQueries.id, addition.input.researchRunQueryId))).get();
23771
23810
  if (!row) throw notFound("Saved research query", addition.input.researchRunQueryId);
23772
23811
  if (!row.queryText.trim()) throw validationError("Saved research query has no usable text.");
23773
23812
  return [{ queryText: row.queryText, provenance: { source: "research", sourceId: row.id, capturedAt: row.createdAt } }];
@@ -23777,7 +23816,7 @@ function sourceRecord(db, projectId, addition, manualCapturedAt) {
23777
23816
  id: discoveryProbes.id,
23778
23817
  queryText: discoveryProbes.query,
23779
23818
  createdAt: discoveryProbes.createdAt
23780
- }).from(discoveryProbes).innerJoin(discoverySessions, eq25(discoveryProbes.sessionId, discoverySessions.id)).where(and19(eq25(discoverySessions.projectId, projectId), eq25(discoveryProbes.id, addition.input.discoveryProbeId))).get();
23819
+ }).from(discoveryProbes).innerJoin(discoverySessions, eq25(discoveryProbes.sessionId, discoverySessions.id)).where(and20(eq25(discoverySessions.projectId, projectId), eq25(discoveryProbes.id, addition.input.discoveryProbeId))).get();
23781
23820
  if (!row) throw notFound("Saved discovery probe", addition.input.discoveryProbeId);
23782
23821
  if (!row.queryText.trim()) throw validationError("Saved discovery probe has no usable text.");
23783
23822
  return [{ queryText: row.queryText, provenance: { source: "discovery", sourceId: row.id, capturedAt: row.createdAt } }];
@@ -23788,7 +23827,7 @@ function sourceRecord(db, projectId, addition, manualCapturedAt) {
23788
23827
  }
23789
23828
  function templateRecords(db, state, addition, audience) {
23790
23829
  if (addition.input.source !== "template") return [];
23791
- const template = db.select().from(measurementQueryTemplates).where(and19(
23830
+ const template = db.select().from(measurementQueryTemplates).where(and20(
23792
23831
  eq25(measurementQueryTemplates.projectId, state.project.id),
23793
23832
  eq25(measurementQueryTemplates.id, addition.input.templateId)
23794
23833
  )).get();
@@ -24639,7 +24678,7 @@ async function queryTrackingRoutes(app, opts = {}) {
24639
24678
 
24640
24679
  // ../api-routes/src/apply.ts
24641
24680
  import crypto22 from "crypto";
24642
- import { and as and20, eq as eq26 } from "drizzle-orm";
24681
+ import { and as and21, eq as eq26 } from "drizzle-orm";
24643
24682
 
24644
24683
  // ../api-routes/src/schedule-utils.ts
24645
24684
  import { CronExpressionParser } from "cron-parser";
@@ -24740,6 +24779,80 @@ function nextRunFromCron(cronExpr, timezone, from = /* @__PURE__ */ new Date())
24740
24779
  return null;
24741
24780
  }
24742
24781
  }
24782
+ var MS_PER_CALENDAR_DAY = 24 * 60 * 60 * 1e3;
24783
+ function calendarOrdinal(date) {
24784
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return null;
24785
+ const parsed = /* @__PURE__ */ new Date(`${date}T00:00:00.000Z`);
24786
+ if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== date) return null;
24787
+ return Math.floor(parsed.getTime() / MS_PER_CALENDAR_DAY);
24788
+ }
24789
+ function dateFromOrdinal(ordinal) {
24790
+ const date = new Date(ordinal * MS_PER_CALENDAR_DAY);
24791
+ return Number.isNaN(date.getTime()) ? null : date.toISOString().slice(0, 10);
24792
+ }
24793
+ function utcMillis(year, month, day, hour = 0, minute = 0) {
24794
+ const date = /* @__PURE__ */ new Date(0);
24795
+ date.setUTCFullYear(year, month - 1, day);
24796
+ date.setUTCHours(hour, minute, 0, 0);
24797
+ return date.getTime();
24798
+ }
24799
+ function zonedParts(instant, timezone) {
24800
+ try {
24801
+ const parts = new Intl.DateTimeFormat("en-US", {
24802
+ timeZone: timezone,
24803
+ year: "numeric",
24804
+ month: "2-digit",
24805
+ day: "2-digit",
24806
+ hour: "2-digit",
24807
+ minute: "2-digit",
24808
+ hourCycle: "h23"
24809
+ }).formatToParts(instant);
24810
+ const values = Object.fromEntries(parts.filter((part) => part.type !== "literal").map((part) => [part.type, Number(part.value)]));
24811
+ const { year, month, day, hour, minute } = values;
24812
+ return Number.isInteger(year) && Number.isInteger(month) && Number.isInteger(day) && Number.isInteger(hour) && Number.isInteger(minute) ? { year, month, day, hour, minute } : null;
24813
+ } catch {
24814
+ return null;
24815
+ }
24816
+ }
24817
+ function recurrenceDate(recurrence, timezone, ordinal) {
24818
+ const date = dateFromOrdinal(ordinal);
24819
+ if (!date || !/^\d{2}:\d{2}$/.test(recurrence.time)) return null;
24820
+ const [year, month, day] = date.split("-").map(Number);
24821
+ const [hour, minute] = recurrence.time.split(":").map(Number);
24822
+ if ([year, month, day, hour, minute].some((value) => value === void 0 || Number.isNaN(value))) return null;
24823
+ const localAsUtc = utcMillis(year, month, day, hour, minute);
24824
+ const offsets = /* @__PURE__ */ new Set();
24825
+ for (const hours of [-36, -24, -12, 0, 12, 24, 36]) {
24826
+ const probe = new Date(localAsUtc + hours * 60 * 60 * 1e3);
24827
+ const local = zonedParts(probe, timezone);
24828
+ if (!local) continue;
24829
+ offsets.add(utcMillis(local.year, local.month, local.day, local.hour, local.minute) - probe.getTime());
24830
+ }
24831
+ const matches = [...offsets].map((offset) => new Date(localAsUtc - offset)).filter((candidate) => {
24832
+ const local = zonedParts(candidate, timezone);
24833
+ return local?.year === year && local.month === month && local.day === day && local.hour === hour && local.minute === minute;
24834
+ });
24835
+ return matches.length ? new Date(Math.min(...matches.map((candidate) => candidate.getTime()))) : null;
24836
+ }
24837
+ function nextRunFromRecurrence(recurrence, timezone, from = /* @__PURE__ */ new Date()) {
24838
+ if (!Number.isInteger(recurrence.everyDays) || recurrence.everyDays < 1 || recurrence.everyDays > 3650 || !isValidTimezone(timezone)) return null;
24839
+ const anchor = calendarOrdinal(recurrence.startDate);
24840
+ if (anchor === null || !/^\d{2}:\d{2}$/.test(recurrence.time)) return null;
24841
+ const [hour, minute] = recurrence.time.split(":").map(Number);
24842
+ if (hour === void 0 || minute === void 0 || hour > 23 || minute > 59) return null;
24843
+ const local = zonedParts(from, timezone);
24844
+ const localOrdinal = local ? calendarOrdinal(`${String(local.year).padStart(4, "0")}-${String(local.month).padStart(2, "0")}-${String(local.day).padStart(2, "0")}`) : null;
24845
+ if (localOrdinal === null) return null;
24846
+ let occurrenceIndex = Math.max(0, Math.floor((localOrdinal - anchor) / recurrence.everyDays));
24847
+ for (let attempts = 0; attempts < 8; attempts += 1, occurrenceIndex += 1) {
24848
+ const occurrence = recurrenceDate(recurrence, timezone, anchor + occurrenceIndex * recurrence.everyDays);
24849
+ if (occurrence && occurrence > from) return occurrence.toISOString();
24850
+ }
24851
+ return null;
24852
+ }
24853
+ function nextRunFromSchedule(schedule, from = /* @__PURE__ */ new Date()) {
24854
+ return schedule.recurrence ? nextRunFromRecurrence(schedule.recurrence, schedule.timezone, from) : nextRunFromCron(schedule.cronExpr, schedule.timezone, from);
24855
+ }
24743
24856
 
24744
24857
  // ../api-routes/src/webhooks.ts
24745
24858
  import crypto21 from "crypto";
@@ -24952,8 +25065,9 @@ async function applyRoutes(app, opts) {
24952
25065
  let deleteSchedule = false;
24953
25066
  if (config.spec.schedule) {
24954
25067
  const schedSpec = config.spec.schedule;
24955
- let cronExpr;
25068
+ let cronExpr = "";
24956
25069
  let preset = null;
25070
+ const recurrence = schedSpec.recurrence ?? null;
24957
25071
  if (schedSpec.preset) {
24958
25072
  preset = schedSpec.preset;
24959
25073
  try {
@@ -24965,12 +25079,10 @@ async function applyRoutes(app, opts) {
24965
25079
  } else if (schedSpec.cron) {
24966
25080
  cronExpr = schedSpec.cron;
24967
25081
  if (!validateCron(cronExpr)) throw validationError(`Invalid cron expression in schedule: ${cronExpr}`);
24968
- } else {
24969
- throw validationError('Schedule requires either "preset" or "cron"');
24970
25082
  }
24971
25083
  const timezone = schedSpec.timezone ?? "UTC";
24972
25084
  if (!isValidTimezone(timezone)) throw validationError(`Invalid timezone: ${timezone}`);
24973
- resolvedSchedule = { cronExpr, preset, timezone };
25085
+ resolvedSchedule = { cronExpr, preset, recurrence, timezone };
24974
25086
  } else {
24975
25087
  deleteSchedule = true;
24976
25088
  }
@@ -25096,13 +25208,20 @@ async function applyRoutes(app, opts) {
25096
25208
  });
25097
25209
  const AV_KIND = SchedulableRunKinds["answer-visibility"];
25098
25210
  if (resolvedSchedule) {
25099
- const existingSched = tx.select().from(schedules).where(and20(eq26(schedules.projectId, projectId), eq26(schedules.kind, AV_KIND))).get();
25211
+ const existingSched = tx.select().from(schedules).where(and21(eq26(schedules.projectId, projectId), eq26(schedules.kind, AV_KIND))).get();
25100
25212
  const specEnabled = config.spec.schedule?.enabled;
25213
+ const effectiveEnabled = specEnabled ?? existingSched?.enabled ?? true;
25214
+ const nextRunAt = effectiveEnabled ? nextRunFromSchedule(resolvedSchedule) : null;
25215
+ if (effectiveEnabled && resolvedSchedule.recurrence && !nextRunAt) {
25216
+ throw validationError("Calendar recurrence has no valid future occurrence");
25217
+ }
25101
25218
  if (existingSched) {
25102
25219
  tx.update(schedules).set({
25103
25220
  cronExpr: resolvedSchedule.cronExpr,
25104
25221
  preset: resolvedSchedule.preset,
25222
+ recurrence: resolvedSchedule.recurrence,
25105
25223
  timezone: resolvedSchedule.timezone,
25224
+ nextRunAt,
25106
25225
  providers: config.spec.schedule?.providers ?? [],
25107
25226
  ...specEnabled === void 0 ? {} : { enabled: specEnabled },
25108
25227
  updatedAt: nextScheduleUpdatedAt(existingSched.updatedAt, Date.parse(now))
@@ -25114,7 +25233,9 @@ async function applyRoutes(app, opts) {
25114
25233
  kind: AV_KIND,
25115
25234
  cronExpr: resolvedSchedule.cronExpr,
25116
25235
  preset: resolvedSchedule.preset,
25236
+ recurrence: resolvedSchedule.recurrence,
25117
25237
  timezone: resolvedSchedule.timezone,
25238
+ nextRunAt,
25118
25239
  enabled: specEnabled ?? true,
25119
25240
  providers: config.spec.schedule?.providers ?? [],
25120
25241
  createdAt: now,
@@ -25123,9 +25244,9 @@ async function applyRoutes(app, opts) {
25123
25244
  }
25124
25245
  scheduleAction = "upsert";
25125
25246
  } else if (deleteSchedule) {
25126
- const existingSched = tx.select().from(schedules).where(and20(eq26(schedules.projectId, projectId), eq26(schedules.kind, AV_KIND))).get();
25247
+ const existingSched = tx.select().from(schedules).where(and21(eq26(schedules.projectId, projectId), eq26(schedules.kind, AV_KIND))).get();
25127
25248
  if (existingSched) {
25128
- tx.delete(schedules).where(and20(eq26(schedules.projectId, projectId), eq26(schedules.kind, AV_KIND))).run();
25249
+ tx.delete(schedules).where(and21(eq26(schedules.projectId, projectId), eq26(schedules.kind, AV_KIND))).run();
25129
25250
  scheduleAction = "delete";
25130
25251
  }
25131
25252
  }
@@ -25214,7 +25335,7 @@ function normalizeCompetitorList2(domains) {
25214
25335
  }
25215
25336
 
25216
25337
  // ../api-routes/src/history.ts
25217
- import { and as and21, asc as asc3, count, desc as desc9, eq as eq27, gte as gte5, inArray as inArray11 } from "drizzle-orm";
25338
+ import { and as and22, asc as asc3, count, desc as desc9, eq as eq27, gte as gte5, inArray as inArray11 } from "drizzle-orm";
25218
25339
 
25219
25340
  // ../api-routes/src/notification-redaction.ts
25220
25341
  var REDACTED_URL = {
@@ -25266,11 +25387,11 @@ async function historyRoutes(app) {
25266
25387
  if (!parsed.success) throw validationError("Invalid history cleanup request", { issues: parsed.error.issues });
25267
25388
  const input = parsed.data;
25268
25389
  return app.db.transaction((tx) => {
25269
- const active = tx.select({ id: runs.id }).from(runs).where(and21(eq27(runs.projectId, project.id), eq27(runs.kind, RunKinds["answer-visibility"]), inArray11(runs.status, ["queued", "running"]))).get();
25270
- const activeResearch = tx.select({ id: researchRuns.id }).from(researchRuns).where(and21(eq27(researchRuns.projectId, project.id), inArray11(researchRuns.status, ["queued", "running"]))).get();
25390
+ const active = tx.select({ id: runs.id }).from(runs).where(and22(eq27(runs.projectId, project.id), eq27(runs.kind, RunKinds["answer-visibility"]), inArray11(runs.status, ["queued", "running"]))).get();
25391
+ const activeResearch = tx.select({ id: researchRuns.id }).from(researchRuns).where(and22(eq27(researchRuns.projectId, project.id), inArray11(researchRuns.status, ["queued", "running"]))).get();
25271
25392
  if (active || activeResearch) throw runInProgress(project.name, active ? "answer-visibility" : "research", (active ?? activeResearch).id);
25272
- const selectedRuns = tx.select({ id: runs.id, kind: runs.kind }).from(runs).where(and21(eq27(runs.projectId, project.id), inArray11(runs.id, input.runIds))).all();
25273
- const selectedResearch = tx.select({ id: researchRuns.id }).from(researchRuns).where(and21(eq27(researchRuns.projectId, project.id), inArray11(researchRuns.id, input.researchRunIds))).all();
25393
+ const selectedRuns = tx.select({ id: runs.id, kind: runs.kind }).from(runs).where(and22(eq27(runs.projectId, project.id), inArray11(runs.id, input.runIds))).all();
25394
+ const selectedResearch = tx.select({ id: researchRuns.id }).from(researchRuns).where(and22(eq27(researchRuns.projectId, project.id), inArray11(researchRuns.id, input.researchRunIds))).all();
25274
25395
  if (selectedRuns.length !== input.runIds.length || selectedResearch.length !== input.researchRunIds.length) throw notFound("Selected saved run in project", project.name);
25275
25396
  if (selectedRuns.some((run) => run.kind !== RunKinds["answer-visibility"])) throw validationError("Only answer-visibility runs and research batches can be cleared. Site Health and integration runs are preserved.");
25276
25397
  const result = {
@@ -25283,8 +25404,8 @@ async function historyRoutes(app) {
25283
25404
  healthSnapshots: tx.select({ value: count() }).from(healthSnapshots).where(inArray11(healthSnapshots.runId, input.runIds)).get().value
25284
25405
  };
25285
25406
  if (input.confirm) {
25286
- tx.delete(researchRuns).where(and21(eq27(researchRuns.projectId, project.id), inArray11(researchRuns.id, input.researchRunIds))).run();
25287
- tx.delete(runs).where(and21(eq27(runs.projectId, project.id), inArray11(runs.id, input.runIds))).run();
25407
+ tx.delete(researchRuns).where(and22(eq27(researchRuns.projectId, project.id), inArray11(researchRuns.id, input.researchRunIds))).run();
25408
+ tx.delete(runs).where(and22(eq27(runs.projectId, project.id), inArray11(runs.id, input.runIds))).run();
25288
25409
  writeAuditLog(tx, { projectId: project.id, actor: request.principal ? `${request.principal.kind}:${request.principal.id}` : "api", action: "results.cleared", entityType: "project", entityId: project.id, diff: result, userAgent: request.headers["user-agent"] });
25289
25410
  }
25290
25411
  return result;
@@ -25294,21 +25415,21 @@ async function historyRoutes(app) {
25294
25415
  const project = resolveProject(app.db, request.params.name);
25295
25416
  const filters = [eq27(auditLog.projectId, project.id)];
25296
25417
  addAuditHistoryFilters(filters, request.query);
25297
- const rows = app.db.select().from(auditLog).where(and21(...filters)).orderBy(desc9(auditLog.createdAt)).limit(parseBoundedInt(request.query.limit, 100, 500)).offset(parseBoundedInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER)).all();
25418
+ const rows = app.db.select().from(auditLog).where(and22(...filters)).orderBy(desc9(auditLog.createdAt)).limit(parseBoundedInt(request.query.limit, 100, 500)).offset(parseBoundedInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER)).all();
25298
25419
  return reply.send(rows.map(formatAuditEntry));
25299
25420
  });
25300
25421
  app.get("/history", async (request, reply) => {
25301
25422
  const scopedProjectId = request.apiKey?.projectId;
25302
25423
  const filters = scopedProjectId ? [eq27(auditLog.projectId, scopedProjectId)] : [];
25303
25424
  addAuditHistoryFilters(filters, request.query);
25304
- const rows = app.db.select().from(auditLog).where(filters.length > 0 ? and21(...filters) : void 0).orderBy(desc9(auditLog.createdAt)).limit(parseBoundedInt(request.query.limit, 100, 500)).offset(parseBoundedInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER)).all();
25425
+ const rows = app.db.select().from(auditLog).where(filters.length > 0 ? and22(...filters) : void 0).orderBy(desc9(auditLog.createdAt)).limit(parseBoundedInt(request.query.limit, 100, 500)).offset(parseBoundedInt(request.query.offset, 0, Number.MAX_SAFE_INTEGER)).all();
25305
25426
  return reply.send(rows.map(formatAuditEntry));
25306
25427
  });
25307
25428
  app.get("/projects/:name/snapshots", async (request, reply) => {
25308
25429
  const project = resolveProject(app.db, request.params.name);
25309
25430
  const limit = parseInt(request.query.limit ?? "50", 10);
25310
25431
  const offset = parseInt(request.query.offset ?? "0", 10);
25311
- const projectRuns = app.db.select({ id: runs.id }).from(runs).where(and21(eq27(runs.projectId, project.id), notProbeRun())).all();
25432
+ const projectRuns = app.db.select({ id: runs.id }).from(runs).where(and22(eq27(runs.projectId, project.id), notProbeRun())).all();
25312
25433
  if (projectRuns.length === 0) {
25313
25434
  return reply.send({ snapshots: [], total: 0 });
25314
25435
  }
@@ -25384,7 +25505,7 @@ async function historyRoutes(app) {
25384
25505
  app.get("/projects/:name/timeline", async (request, reply) => {
25385
25506
  const project = resolveProject(app.db, request.params.name);
25386
25507
  const projectQueries = app.db.select().from(queries).where(eq27(queries.projectId, project.id)).all();
25387
- const runKindFilter = and21(
25508
+ const runKindFilter = and22(
25388
25509
  eq27(runs.projectId, project.id),
25389
25510
  notProbeRun(),
25390
25511
  eq27(runs.kind, RunKinds["answer-visibility"])
@@ -25614,7 +25735,7 @@ function parseOptionalPositiveInt(value, max2) {
25614
25735
  }
25615
25736
 
25616
25737
  // ../api-routes/src/analytics.ts
25617
- import { and as and22, desc as desc10, eq as eq28, gte as gte6, inArray as inArray12, lt as lt4 } from "drizzle-orm";
25738
+ import { and as and23, desc as desc10, eq as eq28, gte as gte6, inArray as inArray12, lt as lt4 } from "drizzle-orm";
25618
25739
 
25619
25740
  // ../api-routes/src/analytics-model-attribution.ts
25620
25741
  function logicalSweeps(observations) {
@@ -25707,7 +25828,7 @@ async function analyticsRoutes(app) {
25707
25828
  const mentionShareScope = classifyQuery ? "non-brand" : "pooled";
25708
25829
  const window = parseWindow(request.query.window);
25709
25830
  const cutoff = windowCutoff(window);
25710
- const projectRuns = app.db.select().from(runs).where(and22(
25831
+ const projectRuns = app.db.select().from(runs).where(and23(
25711
25832
  eq28(runs.projectId, project.id),
25712
25833
  eq28(runs.kind, RunKinds["answer-visibility"]),
25713
25834
  inArray12(runs.status, [RunStatuses.completed, RunStatuses.partial]),
@@ -25801,7 +25922,7 @@ async function analyticsRoutes(app) {
25801
25922
  const servedAnchorUnavailable = /* @__PURE__ */ new Set();
25802
25923
  const windowProviders = new Set(allSnapshots.map((snapshot) => snapshot.provider));
25803
25924
  if (cutoff && windowProviders.size > 0) {
25804
- const anchorRunPredicate = and22(
25925
+ const anchorRunPredicate = and23(
25805
25926
  eq28(runs.projectId, project.id),
25806
25927
  eq28(runs.kind, RunKinds["answer-visibility"]),
25807
25928
  inArray12(runs.status, [RunStatuses.completed, RunStatuses.partial]),
@@ -25812,7 +25933,7 @@ async function analyticsRoutes(app) {
25812
25933
  const anchorScanTruncated = anchorSweepTimes.length > ANCHOR_SWEEP_SCAN_LIMIT;
25813
25934
  if (anchorScanTruncated) anchorSweepTimes.length = ANCHOR_SWEEP_SCAN_LIMIT;
25814
25935
  if (anchorSweepTimes.length > 0) {
25815
- const anchorRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and22(anchorRunPredicate, gte6(runs.createdAt, anchorSweepTimes[anchorSweepTimes.length - 1]))).all();
25936
+ const anchorRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and23(anchorRunPredicate, gte6(runs.createdAt, anchorSweepTimes[anchorSweepTimes.length - 1]))).all();
25816
25937
  const runIdsBySweep = /* @__PURE__ */ new Map();
25817
25938
  for (const run of anchorRuns) {
25818
25939
  const ids = runIdsBySweep.get(run.createdAt) ?? [];
@@ -25962,14 +26083,14 @@ async function analyticsRoutes(app) {
25962
26083
  const project = resolveProject(app.db, request.params.name);
25963
26084
  const window = parseWindow(request.query.window);
25964
26085
  const cutoff = windowCutoff(window);
25965
- const completedRuns = app.db.select().from(runs).where(and22(eq28(runs.projectId, project.id), eq28(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc10(runs.createdAt), desc10(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => measuredWhole(app.db, r));
26086
+ const completedRuns = app.db.select().from(runs).where(and23(eq28(runs.projectId, project.id), eq28(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc10(runs.createdAt), desc10(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => measuredWhole(app.db, r));
25966
26087
  const latestGroup = groupRunsByCreatedAt(completedRuns)[0] ?? [];
25967
26088
  const latestGroupRunIds = latestGroup.map((r) => r.id);
25968
26089
  const latestRun = pickGroupRepresentative(latestGroup);
25969
26090
  if (!latestRun) {
25970
26091
  return reply.send({ cited: [], gap: [], uncited: [], mentionedQueries: [], mentionGap: [], notMentioned: [], runId: "", window });
25971
26092
  }
25972
- const windowRuns = app.db.select().from(runs).where(and22(eq28(runs.projectId, project.id), eq28(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => measuredWhole(app.db, r)).filter((r) => !cutoff || r.createdAt >= cutoff);
26093
+ const windowRuns = app.db.select().from(runs).where(and23(eq28(runs.projectId, project.id), eq28(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => measuredWhole(app.db, r)).filter((r) => !cutoff || r.createdAt >= cutoff);
25973
26094
  const windowRunIds = windowRuns.map((r) => r.id);
25974
26095
  const competitorDomains = app.db.select({ domain: competitors.domain }).from(competitors).where(eq28(competitors.projectId, project.id)).all().map((c) => c.domain);
25975
26096
  const competitorMatchers = new Map(
@@ -26114,7 +26235,7 @@ async function analyticsRoutes(app) {
26114
26235
  const mapped = surfaceClassFromCompetitorType(row.competitorType);
26115
26236
  if (mapped) storedSurfaceClasses.set(normalizeProjectDomain(row.domain), mapped);
26116
26237
  }
26117
- const windowRuns = app.db.select().from(runs).where(and22(eq28(runs.projectId, project.id), eq28(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc10(runs.createdAt), desc10(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => measuredWhole(app.db, r)).filter((r) => !cutoff || r.createdAt >= cutoff);
26238
+ const windowRuns = app.db.select().from(runs).where(and23(eq28(runs.projectId, project.id), eq28(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc10(runs.createdAt), desc10(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => measuredWhole(app.db, r)).filter((r) => !cutoff || r.createdAt >= cutoff);
26118
26239
  if (windowRuns.length === 0) {
26119
26240
  return reply.send({
26120
26241
  overall: [],
@@ -26407,7 +26528,7 @@ function buildCategoryCounts(counts2) {
26407
26528
  }
26408
26529
 
26409
26530
  // ../api-routes/src/intelligence.ts
26410
- import { eq as eq29, desc as desc11, and as and23, inArray as inArray13, like } from "drizzle-orm";
26531
+ import { eq as eq29, desc as desc11, and as and24, inArray as inArray13, like } from "drizzle-orm";
26411
26532
  var SEVERITY_RANK = { low: 0, medium: 1, high: 2, critical: 3 };
26412
26533
  function severitiesAtOrAbove(min2) {
26413
26534
  const floor = SEVERITY_RANK[min2];
@@ -26547,7 +26668,7 @@ async function intelligenceRoutes(app) {
26547
26668
  }
26548
26669
  limit = parsed;
26549
26670
  }
26550
- const rows = app.db.select().from(insights).where(conditions.length === 1 ? conditions[0] : and23(...conditions)).orderBy(desc11(insights.createdAt)).all();
26671
+ const rows = app.db.select().from(insights).where(conditions.length === 1 ? conditions[0] : and24(...conditions)).orderBy(desc11(insights.createdAt)).all();
26551
26672
  const showDismissed = request.query.dismissed === "true";
26552
26673
  let result = rows.filter((r) => showDismissed || !r.dismissed).map(mapInsightRow);
26553
26674
  if (limit !== void 0) result = result.slice(0, limit);
@@ -26572,7 +26693,7 @@ async function intelligenceRoutes(app) {
26572
26693
  });
26573
26694
  app.get("/projects/:name/health/latest", async (request, reply) => {
26574
26695
  const project = resolveProject(app.db, request.params.name);
26575
- const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and23(
26696
+ const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and24(
26576
26697
  eq29(runs.projectId, project.id),
26577
26698
  eq29(runs.kind, RunKinds["answer-visibility"]),
26578
26699
  inArray13(runs.status, [RunStatuses.completed, RunStatuses.partial]),
@@ -26583,7 +26704,7 @@ async function intelligenceRoutes(app) {
26583
26704
  const latestGroup = groupRunsByCreatedAt(projectVisRuns)[0] ?? [];
26584
26705
  const latestGroupRunIds = latestGroup.map((r) => r.id);
26585
26706
  if (latestGroupRunIds.length > 0) {
26586
- const groupRows = app.db.select().from(healthSnapshots).where(and23(
26707
+ const groupRows = app.db.select().from(healthSnapshots).where(and24(
26587
26708
  eq29(healthSnapshots.projectId, project.id),
26588
26709
  inArray13(healthSnapshots.runId, latestGroupRunIds)
26589
26710
  )).all();
@@ -26607,13 +26728,13 @@ async function intelligenceRoutes(app) {
26607
26728
  }
26608
26729
 
26609
26730
  // ../api-routes/src/ai-referral-status.ts
26610
- import { and as and24, sql as sql8 } from "drizzle-orm";
26731
+ import { and as and25, sql as sql8 } from "drizzle-orm";
26611
26732
  var REDIRECT_LIST = sql8.join([...LOCATION_REDIRECT_STATUSES].map((s) => sql8`${s}`), sql8`, `);
26612
26733
  function referralLandedCondition() {
26613
26734
  return sql8`${aiReferralEventsHourly.status} NOT IN (${REDIRECT_LIST})`;
26614
26735
  }
26615
26736
  function countableReferralCondition() {
26616
- return and24(nonSubresourceReferralPathCondition(), referralLandedCondition());
26737
+ return and25(nonSubresourceReferralPathCondition(), referralLandedCondition());
26617
26738
  }
26618
26739
  function nonSubresourceReferralPathCondition() {
26619
26740
  return sql8`
@@ -26640,17 +26761,17 @@ function nonSubresourceReferralPathCondition() {
26640
26761
  }
26641
26762
 
26642
26763
  // ../api-routes/src/report.ts
26643
- import { and as and28, desc as desc14, eq as eq33, gte as gte8, inArray as inArray15, lt as lt5, lte as lte6, ne as ne5, or as or4, sql as sql10 } from "drizzle-orm";
26764
+ import { and as and29, desc as desc14, eq as eq33, gte as gte8, inArray as inArray15, lt as lt5, lte as lte6, ne as ne5, or as or4, sql as sql10 } from "drizzle-orm";
26644
26765
 
26645
26766
  // ../api-routes/src/content.ts
26646
26767
  import crypto23 from "crypto";
26647
- import { and as and27, desc as desc13, eq as eq32 } from "drizzle-orm";
26768
+ import { and as and28, desc as desc13, eq as eq32 } from "drizzle-orm";
26648
26769
 
26649
26770
  // ../api-routes/src/content-data.ts
26650
- import { and as and26, eq as eq31, desc as desc12, gte as gte7, inArray as inArray14, lte as lte5 } from "drizzle-orm";
26771
+ import { and as and27, eq as eq31, desc as desc12, gte as gte7, inArray as inArray14, lte as lte5 } from "drizzle-orm";
26651
26772
 
26652
26773
  // ../api-routes/src/gsc-totals.ts
26653
- import { and as and25, asc as asc4, eq as eq30, sql as sql9, max, min } from "drizzle-orm";
26774
+ import { and as and26, asc as asc4, eq as eq30, sql as sql9, max, min } from "drizzle-orm";
26654
26775
  var WINDOW_DAYS = { "7d": 7, "30d": 30, "90d": 90 };
26655
26776
  function resolveGscWindowRange(window, latestDataDate, today) {
26656
26777
  if (window === "all") {
@@ -26703,7 +26824,7 @@ function readGscDailyTotals(db, projectId, startDate, endDate) {
26703
26824
  impressions: gscDailyTotals.impressions,
26704
26825
  position: gscDailyTotals.position
26705
26826
  }).from(gscDailyTotals).where(
26706
- and25(
26827
+ and26(
26707
26828
  eq30(gscDailyTotals.projectId, projectId),
26708
26829
  sql9`${gscDailyTotals.date} >= ${startDate}`,
26709
26830
  sql9`${gscDailyTotals.date} <= ${endDate}`
@@ -26733,7 +26854,7 @@ function readGscQueryDailyRows(db, projectId, startDate, endDate) {
26733
26854
  impressions: gscQueryDailyTotals.impressions,
26734
26855
  position: gscQueryDailyTotals.position
26735
26856
  }).from(gscQueryDailyTotals).where(
26736
- and25(
26857
+ and26(
26737
26858
  eq30(gscQueryDailyTotals.projectId, projectId),
26738
26859
  sql9`${gscQueryDailyTotals.date} >= ${startDate}`,
26739
26860
  sql9`${gscQueryDailyTotals.date} <= ${endDate}`
@@ -26916,7 +27037,7 @@ function listCompetitorDomains(db, projectId) {
26916
27037
  }
26917
27038
  function listRecentAnswerVisibilityRunIds(db, projectId, limit, locationFilter) {
26918
27039
  const rows = db.select({ id: runs.id, location: runs.location }).from(runs).where(
26919
- and26(
27040
+ and27(
26920
27041
  eq31(runs.projectId, projectId),
26921
27042
  eq31(runs.kind, RunKinds["answer-visibility"]),
26922
27043
  // Queued/running/failed/cancelled runs may have partial or no
@@ -26948,7 +27069,7 @@ function buildGaTrafficByPage(db, projectId, window) {
26948
27069
  const rows = db.select({
26949
27070
  landingPage: gaTrafficSnapshots.landingPage,
26950
27071
  organicSessions: gaTrafficSnapshots.organicSessions
26951
- }).from(gaTrafficSnapshots).where(and26(
27072
+ }).from(gaTrafficSnapshots).where(and27(
26952
27073
  eq31(gaTrafficSnapshots.projectId, projectId),
26953
27074
  gte7(gaTrafficSnapshots.date, window.startDate),
26954
27075
  lte5(gaTrafficSnapshots.date, window.endDate)
@@ -26971,7 +27092,7 @@ function resolveContentGaWindow(db, projectId, windowDays2) {
26971
27092
  function sumAiReferralSessions(db, projectId, window) {
26972
27093
  if (!window) return 0;
26973
27094
  const rows = db.select({ sessions: gaAiReferrals.sessions }).from(gaAiReferrals).where(
26974
- and26(
27095
+ and27(
26975
27096
  eq31(gaAiReferrals.projectId, projectId),
26976
27097
  eq31(gaAiReferrals.sourceDimension, "session"),
26977
27098
  // Bounded for the same reason the per-page read is: this scales every
@@ -26998,7 +27119,7 @@ function buildCandidateQueries(opts) {
26998
27119
  snapshotsByQuery.set(row.queryId, list);
26999
27120
  }
27000
27121
  const gscWindow = resolveContentGscWindow(opts.db, opts.projectId, opts.gscWindowDays);
27001
- const pageRows = gscWindow ? opts.db.select().from(gscSearchData).where(and26(
27122
+ const pageRows = gscWindow ? opts.db.select().from(gscSearchData).where(and27(
27002
27123
  eq31(gscSearchData.projectId, opts.projectId),
27003
27124
  gte7(gscSearchData.date, gscWindow.startDate),
27004
27125
  lte5(gscSearchData.date, gscWindow.endDate)
@@ -27365,7 +27486,7 @@ async function contentRoutes(app, opts = {}) {
27365
27486
  dismissedAt: now
27366
27487
  }
27367
27488
  }).run();
27368
- const row = app.db.select().from(contentTargetDismissals).where(and27(
27489
+ const row = app.db.select().from(contentTargetDismissals).where(and28(
27369
27490
  eq32(contentTargetDismissals.projectId, project.id),
27370
27491
  eq32(contentTargetDismissals.targetRef, targetRef)
27371
27492
  )).get();
@@ -27375,7 +27496,7 @@ async function contentRoutes(app, opts = {}) {
27375
27496
  app.delete("/projects/:name/content/dismissals/:targetRef", async (request, reply) => {
27376
27497
  const project = resolveProject(app.db, request.params.name);
27377
27498
  const { targetRef } = request.params;
27378
- const result = app.db.delete(contentTargetDismissals).where(and27(
27499
+ const result = app.db.delete(contentTargetDismissals).where(and28(
27379
27500
  eq32(contentTargetDismissals.projectId, project.id),
27380
27501
  eq32(contentTargetDismissals.targetRef, targetRef)
27381
27502
  )).run();
@@ -27386,7 +27507,7 @@ async function contentRoutes(app, opts = {}) {
27386
27507
  });
27387
27508
  app.get("/projects/:name/content/recommendations/:targetRef/analysis", async (request, reply) => {
27388
27509
  const project = resolveProject(app.db, request.params.name);
27389
- const row = app.db.select().from(recommendationExplanations).where(and27(
27510
+ const row = app.db.select().from(recommendationExplanations).where(and28(
27390
27511
  eq32(recommendationExplanations.projectId, project.id),
27391
27512
  eq32(recommendationExplanations.targetRef, request.params.targetRef)
27392
27513
  )).orderBy(desc13(recommendationExplanations.generatedAt)).limit(1).get();
@@ -27414,7 +27535,7 @@ async function contentRoutes(app, opts = {}) {
27414
27535
  throw notFound("contentRecommendation", targetRef);
27415
27536
  }
27416
27537
  if (!body.forceRefresh) {
27417
- const cached = app.db.select().from(recommendationExplanations).where(and27(
27538
+ const cached = app.db.select().from(recommendationExplanations).where(and28(
27418
27539
  eq32(recommendationExplanations.projectId, project.id),
27419
27540
  eq32(recommendationExplanations.targetRef, targetRef)
27420
27541
  )).orderBy(desc13(recommendationExplanations.generatedAt)).limit(1).get();
@@ -27453,7 +27574,7 @@ async function contentRoutes(app, opts = {}) {
27453
27574
  generatedAt: now
27454
27575
  }
27455
27576
  }).run();
27456
- const row = app.db.select().from(recommendationExplanations).where(and27(
27577
+ const row = app.db.select().from(recommendationExplanations).where(and28(
27457
27578
  eq32(recommendationExplanations.projectId, project.id),
27458
27579
  eq32(recommendationExplanations.targetRef, targetRef),
27459
27580
  eq32(recommendationExplanations.promptVersion, result.promptVersion)
@@ -27537,7 +27658,7 @@ async function contentRoutes(app, opts = {}) {
27537
27658
  generatedAt: now
27538
27659
  }
27539
27660
  }).run();
27540
- const row = app.db.select().from(recommendationBriefs).where(and27(
27661
+ const row = app.db.select().from(recommendationBriefs).where(and28(
27541
27662
  eq32(recommendationBriefs.projectId, project.id),
27542
27663
  eq32(recommendationBriefs.targetRef, targetRef),
27543
27664
  eq32(recommendationBriefs.promptVersion, result.promptVersion)
@@ -27567,7 +27688,7 @@ function lookupCachedBrief(db, projectId, targetRef, promptVersion) {
27567
27688
  if (promptVersion !== void 0) {
27568
27689
  conditions.push(eq32(recommendationBriefs.promptVersion, promptVersion));
27569
27690
  }
27570
- return db.select().from(recommendationBriefs).where(and27(...conditions)).orderBy(desc13(recommendationBriefs.generatedAt)).limit(1).get();
27691
+ return db.select().from(recommendationBriefs).where(and28(...conditions)).orderBy(desc13(recommendationBriefs.generatedAt)).limit(1).get();
27571
27692
  }
27572
27693
  function parseLimitParam(raw) {
27573
27694
  if (raw === void 0) return void 0;
@@ -30094,7 +30215,7 @@ function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trac
30094
30215
  if (!maxDate) return null;
30095
30216
  const startDate = windowStartDate(maxDate, windowDays2);
30096
30217
  const rows = db.select().from(gscSearchData).where(
30097
- and28(
30218
+ and29(
30098
30219
  eq33(gscSearchData.projectId, projectId),
30099
30220
  gte8(gscSearchData.date, startDate),
30100
30221
  lte6(gscSearchData.date, maxDate)
@@ -30193,7 +30314,7 @@ function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trac
30193
30314
  function buildGaSection(db, projectId, windowDays2) {
30194
30315
  const gaWindowKey = GA_WINDOW_SUMMARY_KEYS[windowDays2];
30195
30316
  const windowSummary = gaWindowKey ? db.select().from(gaTrafficWindowSummaries).where(
30196
- and28(
30317
+ and29(
30197
30318
  eq33(gaTrafficWindowSummaries.projectId, projectId),
30198
30319
  eq33(gaTrafficWindowSummaries.windowKey, gaWindowKey)
30199
30320
  )
@@ -30206,7 +30327,7 @@ function buildGaSection(db, projectId, windowDays2) {
30206
30327
  const snapshotMaxInWindow = summaryPeriod?.periodEnd || snapshotMaxDate;
30207
30328
  const snapshotStartDate = summaryPeriod?.periodStart || (snapshotMaxDate ? windowStartDate(snapshotMaxDate, windowDays2) : "");
30208
30329
  const snapshotRows = snapshotStartDate ? db.select().from(gaTrafficSnapshots).where(
30209
- and28(
30330
+ and29(
30210
30331
  eq33(gaTrafficSnapshots.projectId, projectId),
30211
30332
  gte8(gaTrafficSnapshots.date, snapshotStartDate),
30212
30333
  lte6(gaTrafficSnapshots.date, snapshotMaxInWindow)
@@ -30243,7 +30364,7 @@ function buildGaSection(db, projectId, windowDays2) {
30243
30364
  trafficClass: gaAiReferrals.trafficClass,
30244
30365
  channelGroup: gaAiReferrals.channelGroup,
30245
30366
  sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`
30246
- }).from(gaAiReferrals).where(and28(...aiConditions)).groupBy(gaAiReferrals.trafficClass, gaAiReferrals.channelGroup).all();
30367
+ }).from(gaAiReferrals).where(and29(...aiConditions)).groupBy(gaAiReferrals.trafficClass, gaAiReferrals.channelGroup).all();
30247
30368
  let paidAiSessions = 0;
30248
30369
  let organicAiSessions = 0;
30249
30370
  let aiOrganicOverlap = 0;
@@ -30291,7 +30412,7 @@ function buildGaSection(db, projectId, windowDays2) {
30291
30412
  }
30292
30413
  function buildSocialReferrals(db, projectId, windowDays2) {
30293
30414
  const window = resolveAiReferralWindow(db, projectId, windowDays2);
30294
- const rows = db.select().from(gaSocialReferrals).where(and28(
30415
+ const rows = db.select().from(gaSocialReferrals).where(and29(
30295
30416
  eq33(gaSocialReferrals.projectId, projectId),
30296
30417
  gte8(gaSocialReferrals.date, window.start),
30297
30418
  lte6(gaSocialReferrals.date, window.end)
@@ -30328,7 +30449,7 @@ function buildSocialReferrals(db, projectId, windowDays2) {
30328
30449
  }
30329
30450
  function resolveAiReferralWindow(db, projectId, windowDays2) {
30330
30451
  const windowKey = GA_WINDOW_SUMMARY_KEYS[windowDays2];
30331
- const windowSummary = windowKey ? db.select({ periodStart: gaTrafficWindowSummaries.periodStart, periodEnd: gaTrafficWindowSummaries.periodEnd }).from(gaTrafficWindowSummaries).where(and28(
30452
+ const windowSummary = windowKey ? db.select({ periodStart: gaTrafficWindowSummaries.periodStart, periodEnd: gaTrafficWindowSummaries.periodEnd }).from(gaTrafficWindowSummaries).where(and29(
30332
30453
  eq33(gaTrafficWindowSummaries.projectId, projectId),
30333
30454
  eq33(gaTrafficWindowSummaries.windowKey, windowKey)
30334
30455
  )).get() : void 0;
@@ -30339,7 +30460,7 @@ function resolveAiReferralWindow(db, projectId, windowDays2) {
30339
30460
  }
30340
30461
  function buildAiReferrals(db, projectId, windowDays2) {
30341
30462
  const window = resolveAiReferralWindow(db, projectId, windowDays2);
30342
- const rows = db.select().from(gaAiReferrals).where(and28(
30463
+ const rows = db.select().from(gaAiReferrals).where(and29(
30343
30464
  eq33(gaAiReferrals.projectId, projectId),
30344
30465
  sql10`${gaAiReferrals.date} >= ${window.start}`,
30345
30466
  sql10`${gaAiReferrals.date} <= ${window.end}`
@@ -30412,7 +30533,7 @@ function buildAiReferrals(db, projectId, windowDays2) {
30412
30533
  }
30413
30534
  function buildServerActivity(db, projectId, windowDays2) {
30414
30535
  const sourceRows = db.select({ id: trafficSources.id }).from(trafficSources).where(
30415
- and28(
30536
+ and29(
30416
30537
  eq33(trafficSources.projectId, projectId),
30417
30538
  ne5(trafficSources.status, TrafficSourceStatuses.archived)
30418
30539
  )
@@ -30428,7 +30549,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30428
30549
  const trendStart = new Date(trendStartMs).toISOString();
30429
30550
  const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
30430
30551
  db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
30431
- and28(
30552
+ and29(
30432
30553
  eq33(crawlerEventsHourly.projectId, projectId),
30433
30554
  eq33(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
30434
30555
  gte8(crawlerEventsHourly.tsHour, windowStartIso),
@@ -30438,7 +30559,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30438
30559
  );
30439
30560
  const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
30440
30561
  db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
30441
- and28(
30562
+ and29(
30442
30563
  eq33(crawlerEventsHourly.projectId, projectId),
30443
30564
  ne5(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
30444
30565
  gte8(crawlerEventsHourly.tsHour, windowStartIso),
@@ -30452,7 +30573,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30452
30573
  paid: sql10`COALESCE(SUM(${aiReferralEventsHourly.paidSessionsOrHits}), 0)`,
30453
30574
  organic: sql10`COALESCE(SUM(${aiReferralEventsHourly.organicSessionsOrHits}), 0)`
30454
30575
  }).from(aiReferralEventsHourly).where(
30455
- and28(
30576
+ and29(
30456
30577
  eq33(aiReferralEventsHourly.projectId, projectId),
30457
30578
  countableReferralCondition(),
30458
30579
  gte8(aiReferralEventsHourly.tsHour, windowStartIso),
@@ -30463,7 +30584,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30463
30584
  };
30464
30585
  const sumUserFetches = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
30465
30586
  db.select({ total: sql10`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
30466
- and28(
30587
+ and29(
30467
30588
  eq33(aiUserFetchEventsHourly.projectId, projectId),
30468
30589
  gte8(aiUserFetchEventsHourly.tsHour, windowStartIso),
30469
30590
  exclusiveEnd ? lt5(aiUserFetchEventsHourly.tsHour, windowEndIso) : lte6(aiUserFetchEventsHourly.tsHour, windowEndIso)
@@ -30479,7 +30600,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30479
30600
  const referralCurrent = sumReferrals(headlineStart, headlineEnd);
30480
30601
  const referralAllCurrent = Number(
30481
30602
  db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
30482
- and28(
30603
+ and29(
30483
30604
  eq33(aiReferralEventsHourly.projectId, projectId),
30484
30605
  nonSubresourceReferralPathCondition(),
30485
30606
  gte8(aiReferralEventsHourly.tsHour, headlineStart),
@@ -30494,7 +30615,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30494
30615
  verificationStatus: crawlerEventsHourly.verificationStatus,
30495
30616
  hits: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
30496
30617
  }).from(crawlerEventsHourly).where(
30497
- and28(
30618
+ and29(
30498
30619
  eq33(crawlerEventsHourly.projectId, projectId),
30499
30620
  gte8(crawlerEventsHourly.tsHour, headlineStart),
30500
30621
  lte6(crawlerEventsHourly.tsHour, headlineEnd)
@@ -30504,7 +30625,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30504
30625
  operator: crawlerEventsHourly.operator,
30505
30626
  hits: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
30506
30627
  }).from(crawlerEventsHourly).where(
30507
- and28(
30628
+ and29(
30508
30629
  eq33(crawlerEventsHourly.projectId, projectId),
30509
30630
  gte8(crawlerEventsHourly.tsHour, priorStart),
30510
30631
  lt5(crawlerEventsHourly.tsHour, headlineStart)
@@ -30514,7 +30635,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30514
30635
  operator: aiReferralEventsHourly.operator,
30515
30636
  hits: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
30516
30637
  }).from(aiReferralEventsHourly).where(
30517
- and28(
30638
+ and29(
30518
30639
  eq33(aiReferralEventsHourly.projectId, projectId),
30519
30640
  countableReferralCondition(),
30520
30641
  gte8(aiReferralEventsHourly.tsHour, headlineStart),
@@ -30525,7 +30646,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30525
30646
  operator: aiUserFetchEventsHourly.operator,
30526
30647
  hits: sql10`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
30527
30648
  }).from(aiUserFetchEventsHourly).where(
30528
- and28(
30649
+ and29(
30529
30650
  eq33(aiUserFetchEventsHourly.projectId, projectId),
30530
30651
  gte8(aiUserFetchEventsHourly.tsHour, headlineStart),
30531
30652
  lte6(aiUserFetchEventsHourly.tsHour, headlineEnd)
@@ -30570,7 +30691,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30570
30691
  unverifiedHits: sql10`COALESCE(SUM(CASE WHEN ${crawlerEventsHourly.verificationStatus} <> ${VerificationStatuses.verified} THEN ${crawlerEventsHourly.hits} ELSE 0 END), 0)`,
30571
30692
  operators: sql10`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
30572
30693
  }).from(crawlerEventsHourly).where(
30573
- and28(
30694
+ and29(
30574
30695
  eq33(crawlerEventsHourly.projectId, projectId),
30575
30696
  gte8(crawlerEventsHourly.tsHour, headlineStart),
30576
30697
  lte6(crawlerEventsHourly.tsHour, headlineEnd)
@@ -30587,7 +30708,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30587
30708
  arrivals: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
30588
30709
  landingPaths: sql10`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
30589
30710
  }).from(aiReferralEventsHourly).where(
30590
- and28(
30711
+ and29(
30591
30712
  eq33(aiReferralEventsHourly.projectId, projectId),
30592
30713
  countableReferralCondition(),
30593
30714
  gte8(aiReferralEventsHourly.tsHour, headlineStart),
@@ -30604,7 +30725,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30604
30725
  arrivals: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
30605
30726
  products: sql10`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
30606
30727
  }).from(aiReferralEventsHourly).where(
30607
- and28(
30728
+ and29(
30608
30729
  eq33(aiReferralEventsHourly.projectId, projectId),
30609
30730
  countableReferralCondition(),
30610
30731
  gte8(aiReferralEventsHourly.tsHour, headlineStart),
@@ -30621,7 +30742,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30621
30742
  verifiedHits: sql10`COALESCE(SUM(CASE WHEN ${crawlerEventsHourly.verificationStatus} = ${VerificationStatuses.verified} THEN ${crawlerEventsHourly.hits} ELSE 0 END), 0)`,
30622
30743
  unverifiedHits: sql10`COALESCE(SUM(CASE WHEN ${crawlerEventsHourly.verificationStatus} <> ${VerificationStatuses.verified} THEN ${crawlerEventsHourly.hits} ELSE 0 END), 0)`
30623
30744
  }).from(crawlerEventsHourly).where(
30624
- and28(
30745
+ and29(
30625
30746
  eq33(crawlerEventsHourly.projectId, projectId),
30626
30747
  gte8(crawlerEventsHourly.tsHour, trendStart),
30627
30748
  lte6(crawlerEventsHourly.tsHour, headlineEnd)
@@ -30631,7 +30752,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30631
30752
  date: sql10`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
30632
30753
  hits: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
30633
30754
  }).from(aiReferralEventsHourly).where(
30634
- and28(
30755
+ and29(
30635
30756
  eq33(aiReferralEventsHourly.projectId, projectId),
30636
30757
  countableReferralCondition(),
30637
30758
  gte8(aiReferralEventsHourly.tsHour, trendStart),
@@ -30642,7 +30763,7 @@ function buildServerActivity(db, projectId, windowDays2) {
30642
30763
  date: sql10`SUBSTR(${aiUserFetchEventsHourly.tsHour}, 1, 10)`,
30643
30764
  hits: sql10`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
30644
30765
  }).from(aiUserFetchEventsHourly).where(
30645
- and28(
30766
+ and29(
30646
30767
  eq33(aiUserFetchEventsHourly.projectId, projectId),
30647
30768
  gte8(aiUserFetchEventsHourly.tsHour, trendStart),
30648
30769
  lte6(aiUserFetchEventsHourly.tsHour, headlineEnd)
@@ -30747,7 +30868,7 @@ function buildIndexingHealth(db, projectId) {
30747
30868
  return null;
30748
30869
  }
30749
30870
  function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
30750
- const visibilityRuns = db.select().from(runs).where(and28(eq33(runs.projectId, projectId), eq33(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter);
30871
+ const visibilityRuns = db.select().from(runs).where(and29(eq33(runs.projectId, projectId), eq33(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter);
30751
30872
  const totalQueries = queryLookup.byId.size;
30752
30873
  const points = [];
30753
30874
  for (const run of visibilityRuns) {
@@ -30795,7 +30916,7 @@ function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
30795
30916
  }
30796
30917
  function buildInsightList(db, projectId, locationFilter) {
30797
30918
  const recentRunIds = db.select({ id: runs.id, location: runs.location }).from(runs).where(
30798
- and28(
30919
+ and29(
30799
30920
  eq33(runs.projectId, projectId),
30800
30921
  eq33(runs.kind, RunKinds["answer-visibility"]),
30801
30922
  or4(eq33(runs.status, RunStatuses.completed), eq33(runs.status, RunStatuses.partial)),
@@ -30803,7 +30924,7 @@ function buildInsightList(db, projectId, locationFilter) {
30803
30924
  )
30804
30925
  ).orderBy(desc14(runs.createdAt)).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter).slice(0, INSIGHT_LOOKBACK_RUNS).map((r) => r.id);
30805
30926
  if (recentRunIds.length === 0) return [];
30806
- const rows = db.select().from(insights).where(and28(eq33(insights.projectId, projectId), inArray15(insights.runId, recentRunIds))).orderBy(desc14(insights.createdAt)).all();
30927
+ const rows = db.select().from(insights).where(and29(eq33(insights.projectId, projectId), inArray15(insights.runId, recentRunIds))).orderBy(desc14(insights.createdAt)).all();
30807
30928
  const severityRank = { critical: 0, high: 1, medium: 2, low: 3 };
30808
30929
  const flat = rows.filter((r) => !r.dismissed).map((r) => {
30809
30930
  const recommendation = r.recommendation;
@@ -31370,7 +31491,7 @@ function buildProjectReport(db, projectName, periodDays) {
31370
31491
  const project = resolveProject(db, projectName);
31371
31492
  const queryLookup = loadQueryLookup(db, project.id);
31372
31493
  const comparisonWindowDays = reportComparisonWindowDays(periodDays);
31373
- const allRuns = db.select().from(runs).where(and28(eq33(runs.projectId, project.id), notProbeRun())).orderBy(desc14(runs.createdAt), desc14(runs.id)).all();
31494
+ const allRuns = db.select().from(runs).where(and29(eq33(runs.projectId, project.id), notProbeRun())).orderBy(desc14(runs.createdAt), desc14(runs.id)).all();
31374
31495
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
31375
31496
  const completedVisRunGroups = groupRunsByCreatedAt(
31376
31497
  visibilityRuns.filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial)
@@ -31633,10 +31754,10 @@ async function reportRoutes(app) {
31633
31754
  }
31634
31755
 
31635
31756
  // ../api-routes/src/organic-evidence.ts
31636
- import { and as and30, desc as desc15, eq as eq35, gte as gte10, lte as lte8, or as or6, sql as sql12 } from "drizzle-orm";
31757
+ import { and as and31, desc as desc15, eq as eq35, gte as gte10, lte as lte8, or as or6, sql as sql12 } from "drizzle-orm";
31637
31758
 
31638
31759
  // ../api-routes/src/ga-measurement-analysis.ts
31639
- import { and as and29, eq as eq34, gte as gte9, lte as lte7, or as or5, sql as sql11 } from "drizzle-orm";
31760
+ import { and as and30, eq as eq34, gte as gte9, lte as lte7, or as or5, sql as sql11 } from "drizzle-orm";
31640
31761
  var UNKNOWN_LANDING_PAGE = "(not set)";
31641
31762
  function addDays(date, offset) {
31642
31763
  const value = /* @__PURE__ */ new Date(`${date}T00:00:00.000Z`);
@@ -31824,27 +31945,27 @@ function buildGaMeasurementAnalysis(db, projectName, options = {}) {
31824
31945
  };
31825
31946
  const acquisitionScope = scopedConditions(gaAcquisitionDaily.hostName, gaAcquisitionDaily.landingPageNormalized, gaAcquisitionDaily.landingPage);
31826
31947
  const leadLandingScope = scopedConditions(gaLeadEventsDaily.hostName, gaLeadEventsDaily.landingPageNormalized, gaLeadEventsDaily.landingPage);
31827
- const acquisitionAnchor = db.select({ date: sql11`max(${gaAcquisitionDaily.date})` }).from(gaAcquisitionDaily).where(and29(eq34(gaAcquisitionDaily.projectId, project.id), ...acquisitionScope)).get()?.date ?? null;
31828
- const leadAnchor = db.select({ date: sql11`max(${gaLeadEventsDaily.date})` }).from(gaLeadEventsDaily).where(and29(eq34(gaLeadEventsDaily.projectId, project.id), or5(
31948
+ const acquisitionAnchor = db.select({ date: sql11`max(${gaAcquisitionDaily.date})` }).from(gaAcquisitionDaily).where(and30(eq34(gaAcquisitionDaily.projectId, project.id), ...acquisitionScope)).get()?.date ?? null;
31949
+ const leadAnchor = db.select({ date: sql11`max(${gaLeadEventsDaily.date})` }).from(gaLeadEventsDaily).where(and30(eq34(gaLeadEventsDaily.projectId, project.id), or5(
31829
31950
  eq34(gaLeadEventsDaily.attributionScope, "channel"),
31830
- and29(eq34(gaLeadEventsDaily.attributionScope, "landing-page"), ...leadLandingScope)
31951
+ and30(eq34(gaLeadEventsDaily.attributionScope, "landing-page"), ...leadLandingScope)
31831
31952
  ))).get()?.date ?? null;
31832
31953
  const gaAnchor = [acquisitionAnchor, leadAnchor].filter((date) => date !== null).sort().at(-1) ?? null;
31833
31954
  const gaPeriods = gaAnchor ? buildPeriods(gaAnchor, days) : [];
31834
31955
  const gaStartDate = gaPeriods[0]?.startDate;
31835
- const acquisitionRows = gaStartDate ? db.select().from(gaAcquisitionDaily).where(and29(eq34(gaAcquisitionDaily.projectId, project.id), gte9(gaAcquisitionDaily.date, gaStartDate), lte7(gaAcquisitionDaily.date, gaAnchor))).all() : [];
31836
- const leadRows = gaStartDate ? db.select().from(gaLeadEventsDaily).where(and29(eq34(gaLeadEventsDaily.projectId, project.id), gte9(gaLeadEventsDaily.date, gaStartDate), lte7(gaLeadEventsDaily.date, gaAnchor))).all() : [];
31956
+ const acquisitionRows = gaStartDate ? db.select().from(gaAcquisitionDaily).where(and30(eq34(gaAcquisitionDaily.projectId, project.id), gte9(gaAcquisitionDaily.date, gaStartDate), lte7(gaAcquisitionDaily.date, gaAnchor))).all() : [];
31957
+ const leadRows = gaStartDate ? db.select().from(gaLeadEventsDaily).where(and30(eq34(gaLeadEventsDaily.projectId, project.id), gte9(gaLeadEventsDaily.date, gaStartDate), lte7(gaLeadEventsDaily.date, gaAnchor))).all() : [];
31837
31958
  const engagementAnchor = db.select({ date: sql11`max(${gaDailyTotals.date})` }).from(gaDailyTotals).where(eq34(gaDailyTotals.projectId, project.id)).get()?.date ?? null;
31838
31959
  const engagementPeriodWindow = engagementAnchor ? buildPeriods(engagementAnchor, days) : [];
31839
31960
  const engagementStartDate = engagementPeriodWindow[0]?.startDate;
31840
- const engagementRows = engagementStartDate ? db.select().from(gaDailyTotals).where(and29(
31961
+ const engagementRows = engagementStartDate ? db.select().from(gaDailyTotals).where(and30(
31841
31962
  eq34(gaDailyTotals.projectId, project.id),
31842
31963
  gte9(gaDailyTotals.date, engagementStartDate),
31843
31964
  lte7(gaDailyTotals.date, engagementAnchor)
31844
31965
  )).all() : [];
31845
31966
  const engagementAvailableFrom = db.select({
31846
31967
  date: sql11`min(${gaDailyTotals.date})`
31847
- }).from(gaDailyTotals).where(and29(
31968
+ }).from(gaDailyTotals).where(and30(
31848
31969
  eq34(gaDailyTotals.projectId, project.id),
31849
31970
  or5(
31850
31971
  sql11`${gaDailyTotals.engagementRate} is not null`,
@@ -31854,9 +31975,9 @@ function buildGaMeasurementAnalysis(db, projectName, options = {}) {
31854
31975
  const gscAnchor = db.select({ date: sql11`max(${gscDailyTotals.date})` }).from(gscDailyTotals).where(eq34(gscDailyTotals.projectId, project.id)).get()?.date ?? null;
31855
31976
  const gscPeriods = gscAnchor ? buildPeriods(gscAnchor, days) : [];
31856
31977
  const gscStartDate = gscPeriods[0]?.startDate;
31857
- const propertyRows = gscStartDate ? db.select().from(gscDailyTotals).where(and29(eq34(gscDailyTotals.projectId, project.id), gte9(gscDailyTotals.date, gscStartDate), lte7(gscDailyTotals.date, gscAnchor))).all() : [];
31858
- const queryRows2 = gscStartDate ? db.select().from(gscQueryDailyTotals).where(and29(eq34(gscQueryDailyTotals.projectId, project.id), gte9(gscQueryDailyTotals.date, gscStartDate), lte7(gscQueryDailyTotals.date, gscAnchor))).all() : [];
31859
- const rawPageRows = gscStartDate ? db.select().from(gscSearchData).where(and29(eq34(gscSearchData.projectId, project.id), gte9(gscSearchData.date, gscStartDate), lte7(gscSearchData.date, gscAnchor))).all() : [];
31978
+ const propertyRows = gscStartDate ? db.select().from(gscDailyTotals).where(and30(eq34(gscDailyTotals.projectId, project.id), gte9(gscDailyTotals.date, gscStartDate), lte7(gscDailyTotals.date, gscAnchor))).all() : [];
31979
+ const queryRows2 = gscStartDate ? db.select().from(gscQueryDailyTotals).where(and30(eq34(gscQueryDailyTotals.projectId, project.id), gte9(gscQueryDailyTotals.date, gscStartDate), lte7(gscQueryDailyTotals.date, gscAnchor))).all() : [];
31980
+ const rawPageRows = gscStartDate ? db.select().from(gscSearchData).where(and30(eq34(gscSearchData.projectId, project.id), gte9(gscSearchData.date, gscStartDate), lte7(gscSearchData.date, gscAnchor))).all() : [];
31860
31981
  const state = db.select().from(gaMeasurementSyncStates).where(eq34(gaMeasurementSyncStates.projectId, project.id)).get();
31861
31982
  const acquisition = acquisitionRows.filter((row) => {
31862
31983
  const landingPage = normalizeLandingPage(row.landingPageNormalized ?? row.landingPage);
@@ -32191,7 +32312,7 @@ function buildOrganicEvidence(db, projectName, periodDays) {
32191
32312
  startDate: sql12`min(${gaAcquisitionDaily.date})`,
32192
32313
  endDate: sql12`max(${gaAcquisitionDaily.date})`,
32193
32314
  observedDays: sql12`count(distinct ${gaAcquisitionDaily.date})`
32194
- }).from(gaAcquisitionDaily).where(and30(
32315
+ }).from(gaAcquisitionDaily).where(and31(
32195
32316
  eq35(gaAcquisitionDaily.projectId, project.id),
32196
32317
  or6(...normalizedMarketingHosts.map((candidate) => sql12`lower(trim(${gaAcquisitionDaily.hostName})) = ${candidate} or lower(trim(${gaAcquisitionDaily.hostName})) like ${`%.${candidate}`}`))
32197
32318
  )).get()) : null;
@@ -32206,17 +32327,17 @@ function buildOrganicEvidence(db, projectName, periodDays) {
32206
32327
  const startDate = gscStartDate ?? gaStartDate ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
32207
32328
  const endDate = gscEndDate ?? gaEndDate ?? startDate;
32208
32329
  const isInWindow = (date) => inRange(date, startDate, endDate);
32209
- const gscRows = gscStartDate && gscEndDate ? db.select().from(gscDailyTotals).where(and30(
32330
+ const gscRows = gscStartDate && gscEndDate ? db.select().from(gscDailyTotals).where(and31(
32210
32331
  eq35(gscDailyTotals.projectId, project.id),
32211
32332
  gte10(gscDailyTotals.date, gscStartDate),
32212
32333
  lte8(gscDailyTotals.date, gscEndDate)
32213
32334
  )).all() : [];
32214
- const gaRows = gaStartDate && gaEndDate ? db.select().from(gaTrafficSnapshots).where(and30(
32335
+ const gaRows = gaStartDate && gaEndDate ? db.select().from(gaTrafficSnapshots).where(and31(
32215
32336
  eq35(gaTrafficSnapshots.projectId, project.id),
32216
32337
  gte10(gaTrafficSnapshots.date, gaStartDate),
32217
32338
  lte8(gaTrafficSnapshots.date, gaEndDate)
32218
32339
  )).all() : [];
32219
- const nativeAcquisitionRows = nativeAcquisitionActive && nativeStartDate && nativeEndDate ? db.select().from(gaAcquisitionDaily).where(and30(
32340
+ const nativeAcquisitionRows = nativeAcquisitionActive && nativeStartDate && nativeEndDate ? db.select().from(gaAcquisitionDaily).where(and31(
32220
32341
  eq35(gaAcquisitionDaily.projectId, project.id),
32221
32342
  gte10(gaAcquisitionDaily.date, nativeStartDate),
32222
32343
  lte8(gaAcquisitionDaily.date, nativeEndDate)
@@ -32255,7 +32376,7 @@ function buildOrganicEvidence(db, projectName, periodDays) {
32255
32376
  project.canonicalDomain,
32256
32377
  [project.displayName, ...project.aliases]
32257
32378
  );
32258
- const namedRows = db.select().from(gscQueryDailyTotals).where(and30(
32379
+ const namedRows = db.select().from(gscQueryDailyTotals).where(and31(
32259
32380
  eq35(gscQueryDailyTotals.projectId, project.id),
32260
32381
  gte10(gscQueryDailyTotals.date, startDate),
32261
32382
  lte8(gscQueryDailyTotals.date, endDate)
@@ -32284,7 +32405,7 @@ function buildOrganicEvidence(db, projectName, periodDays) {
32284
32405
  totals: sumSearchRows(gscRows.filter((row) => inRange(row.date, cohort.startDate, cohort.endDate)))
32285
32406
  }))
32286
32407
  } : null;
32287
- const pageSearchRows = db.select().from(gscSearchData).where(and30(eq35(gscSearchData.projectId, project.id), gte10(gscSearchData.date, startDate), lte8(gscSearchData.date, endDate))).all().filter((row) => pageMatchesMarketingHost(row.page, marketingHosts));
32408
+ const pageSearchRows = db.select().from(gscSearchData).where(and31(eq35(gscSearchData.projectId, project.id), gte10(gscSearchData.date, startDate), lte8(gscSearchData.date, endDate))).all().filter((row) => pageMatchesMarketingHost(row.page, marketingHosts));
32288
32409
  const gaWindow = gaStartDate && gaEndDate ? gaRows.filter((row) => inRange(row.date, gaStartDate, gaEndDate)) : [];
32289
32410
  const legacyGaCohorts = gaCohorts.map((cohort) => ({
32290
32411
  ...cohort,
@@ -32311,7 +32432,7 @@ function buildOrganicEvidence(db, projectName, periodDays) {
32311
32432
  }))
32312
32433
  };
32313
32434
  }
32314
- const allGaAiRows = db.select().from(gaAiReferrals).where(and30(eq35(gaAiReferrals.projectId, project.id), gte10(gaAiReferrals.date, gaStartDate ?? startDate), lte8(gaAiReferrals.date, gaEndDate ?? endDate))).all().filter((row) => row.sourceDimension === "session");
32435
+ const allGaAiRows = db.select().from(gaAiReferrals).where(and31(eq35(gaAiReferrals.projectId, project.id), gte10(gaAiReferrals.date, gaStartDate ?? startDate), lte8(gaAiReferrals.date, gaEndDate ?? endDate))).all().filter((row) => row.sourceDimension === "session");
32315
32436
  const aiRows = gaStartDate && gaEndDate ? allGaAiRows.filter((row) => inRange(row.date, gaStartDate, gaEndDate)) : [];
32316
32437
  const gaAiReferralSummary = allGaAiRows.length ? {
32317
32438
  paidSessions: aiRows.filter((row) => row.trafficClass === AiReferralTrafficClasses.paid).reduce((count4, row) => count4 + row.sessions, 0),
@@ -32320,9 +32441,9 @@ function buildOrganicEvidence(db, projectName, periodDays) {
32320
32441
  const serverSources = db.select().from(trafficSources).where(eq35(trafficSources.projectId, project.id)).all();
32321
32442
  const serverStart = `${startDate}T00:00:00`;
32322
32443
  const serverEnd = `${endDate}T23:59:59.999Z`;
32323
- const allCrawlers = db.select().from(crawlerEventsHourly).where(and30(eq35(crawlerEventsHourly.projectId, project.id), gte10(crawlerEventsHourly.tsHour, serverStart), lte8(crawlerEventsHourly.tsHour, serverEnd))).all();
32324
- const allFetches = db.select().from(aiUserFetchEventsHourly).where(and30(eq35(aiUserFetchEventsHourly.projectId, project.id), gte10(aiUserFetchEventsHourly.tsHour, serverStart), lte8(aiUserFetchEventsHourly.tsHour, serverEnd))).all();
32325
- const allReferrals = db.select().from(aiReferralEventsHourly).where(and30(eq35(aiReferralEventsHourly.projectId, project.id), countableReferralCondition(), gte10(aiReferralEventsHourly.tsHour, serverStart), lte8(aiReferralEventsHourly.tsHour, serverEnd))).all();
32444
+ const allCrawlers = db.select().from(crawlerEventsHourly).where(and31(eq35(crawlerEventsHourly.projectId, project.id), gte10(crawlerEventsHourly.tsHour, serverStart), lte8(crawlerEventsHourly.tsHour, serverEnd))).all();
32445
+ const allFetches = db.select().from(aiUserFetchEventsHourly).where(and31(eq35(aiUserFetchEventsHourly.projectId, project.id), gte10(aiUserFetchEventsHourly.tsHour, serverStart), lte8(aiUserFetchEventsHourly.tsHour, serverEnd))).all();
32446
+ const allReferrals = db.select().from(aiReferralEventsHourly).where(and31(eq35(aiReferralEventsHourly.projectId, project.id), countableReferralCondition(), gte10(aiReferralEventsHourly.tsHour, serverStart), lte8(aiReferralEventsHourly.tsHour, serverEnd))).all();
32326
32447
  const [serverCoverageRow] = db.all(sql12`
32327
32448
  select
32328
32449
  min(day) as startDate,
@@ -32350,7 +32471,7 @@ function buildOrganicEvidence(db, projectName, periodDays) {
32350
32471
  const referrals = allReferrals.filter((row) => serverInWindow(row.tsHour));
32351
32472
  const serverSummary = summarizeServer(crawlers, fetches, referrals);
32352
32473
  const server = serverSources.length ? serverSummary.summary : null;
32353
- const latest = db.select().from(runs).where(and30(
32474
+ const latest = db.select().from(runs).where(and31(
32354
32475
  eq35(runs.projectId, project.id),
32355
32476
  eq35(runs.kind, RunKinds["answer-visibility"]),
32356
32477
  eq35(runs.status, RunStatuses.completed),
@@ -32595,7 +32716,7 @@ async function organicEvidenceRoutes(app) {
32595
32716
  }
32596
32717
 
32597
32718
  // ../api-routes/src/citations.ts
32598
- import { and as and31, eq as eq36, inArray as inArray16 } from "drizzle-orm";
32719
+ import { and as and32, eq as eq36, inArray as inArray16 } from "drizzle-orm";
32599
32720
  async function citationRoutes(app) {
32600
32721
  app.get("/projects/:name/citations/visibility", async (request, reply) => {
32601
32722
  const project = resolveProject(app.db, request.params.name);
@@ -32604,7 +32725,7 @@ async function citationRoutes(app) {
32604
32725
  if (projectQueries.length === 0) {
32605
32726
  return reply.send(emptyCitationVisibility("no-queries"));
32606
32727
  }
32607
- const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and31(eq36(runs.projectId, project.id), notProbeRun())).all();
32728
+ const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and32(eq36(runs.projectId, project.id), notProbeRun())).all();
32608
32729
  if (projectRuns.length === 0) {
32609
32730
  return reply.send(emptyCitationVisibility("no-runs-yet"));
32610
32731
  }
@@ -32756,7 +32877,7 @@ function computeCitationVisibility(input) {
32756
32877
  }
32757
32878
 
32758
32879
  // ../api-routes/src/visibility-stats.ts
32759
- import { and as and32, desc as desc16, eq as eq37, inArray as inArray17 } from "drizzle-orm";
32880
+ import { and as and33, desc as desc16, eq as eq37, inArray as inArray17 } from "drizzle-orm";
32760
32881
 
32761
32882
  // ../api-routes/src/visibility-compare.ts
32762
32883
  var VISIBILITY_COMPARE_MIN_RUNS = 5;
@@ -33183,7 +33304,7 @@ async function visibilityStatsRoutes(app) {
33183
33304
  lastRuns = n;
33184
33305
  }
33185
33306
  const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq37(queries.projectId, project.id)).all();
33186
- let projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and32(eq37(runs.projectId, project.id), eq37(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc16(runs.createdAt)).all().filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial);
33307
+ let projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and33(eq37(runs.projectId, project.id), eq37(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc16(runs.createdAt)).all().filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial);
33187
33308
  if (sinceMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) >= sinceMs);
33188
33309
  if (untilMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) <= untilMs);
33189
33310
  if (lastRuns !== null) projectRuns = projectRuns.slice(0, lastRuns);
@@ -33266,7 +33387,7 @@ async function visibilityStatsRoutes(app) {
33266
33387
  const loadMonth = (bounds) => {
33267
33388
  const sinceMs = Date.parse(bounds.since);
33268
33389
  const untilMs = Date.parse(bounds.until);
33269
- const monthRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and32(eq37(runs.projectId, project.id), eq37(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter(
33390
+ const monthRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and33(eq37(runs.projectId, project.id), eq37(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter(
33270
33391
  (r) => (r.status === RunStatuses.completed || r.status === RunStatuses.partial) && Date.parse(r.createdAt) >= sinceMs && Date.parse(r.createdAt) <= untilMs
33271
33392
  );
33272
33393
  const runIds = monthRuns.map((r) => r.id);
@@ -33297,7 +33418,7 @@ async function visibilityStatsRoutes(app) {
33297
33418
  }
33298
33419
 
33299
33420
  // ../api-routes/src/results-export.ts
33300
- import { and as and33, asc as asc5, eq as eq38, gte as gte11, lte as lte9 } from "drizzle-orm";
33421
+ import { and as and34, asc as asc5, eq as eq38, gte as gte11, lte as lte9 } from "drizzle-orm";
33301
33422
  var CSV_COLUMNS = [
33302
33423
  "export_schema_version",
33303
33424
  "project_id",
@@ -33467,7 +33588,7 @@ async function resultsExportRoutes(app) {
33467
33588
  recommendedCompetitors: querySnapshots.recommendedCompetitors,
33468
33589
  answerText: querySnapshots.answerText,
33469
33590
  rawResponse: querySnapshots.rawResponse
33470
- }).from(querySnapshots).innerJoin(runs, eq38(querySnapshots.runId, runs.id)).leftJoin(queries, eq38(querySnapshots.queryId, queries.id)).where(and33(...conditions)).orderBy(asc5(runs.createdAt), asc5(runs.id), asc5(querySnapshots.createdAt), asc5(querySnapshots.id)).all();
33591
+ }).from(querySnapshots).innerJoin(runs, eq38(querySnapshots.runId, runs.id)).leftJoin(queries, eq38(querySnapshots.queryId, queries.id)).where(and34(...conditions)).orderBy(asc5(runs.createdAt), asc5(runs.id), asc5(querySnapshots.createdAt), asc5(querySnapshots.id)).all();
33471
33592
  const records = rows.map((row) => {
33472
33593
  const evidence = readEvidence(row.rawResponse);
33473
33594
  const citationState = row.citationState === CitationStates.cited ? CitationStates.cited : CitationStates["not-cited"];
@@ -33535,7 +33656,7 @@ async function resultsExportRoutes(app) {
33535
33656
  }
33536
33657
 
33537
33658
  // ../api-routes/src/composites.ts
33538
- import { eq as eq39, and as and34, desc as desc17, sql as sql13, like as like2, or as or7, inArray as inArray18 } from "drizzle-orm";
33659
+ import { eq as eq39, and as and35, desc as desc17, sql as sql13, like as like2, or as or7, inArray as inArray18 } from "drizzle-orm";
33539
33660
  var TOP_INSIGHT_LIMIT = 5;
33540
33661
  var SEARCH_HIT_HARD_LIMIT = 50;
33541
33662
  var SEARCH_SNIPPET_RADIUS = 80;
@@ -33555,7 +33676,7 @@ async function compositeRoutes(app) {
33555
33676
  const project = resolveProject(app.db, request.params.name);
33556
33677
  const filterLocation = (request.query.location ?? "").trim() || null;
33557
33678
  const sinceIso = parseSinceFilter(request.query.since);
33558
- const allRunsRaw = app.db.select().from(runs).where(and34(eq39(runs.projectId, project.id), notProbeRun())).orderBy(desc17(runs.createdAt), desc17(runs.id)).all();
33679
+ const allRunsRaw = app.db.select().from(runs).where(and35(eq39(runs.projectId, project.id), notProbeRun())).orderBy(desc17(runs.createdAt), desc17(runs.id)).all();
33559
33680
  const allRuns = allRunsRaw.filter((r) => runMatchesFilters(r, filterLocation, sinceIso));
33560
33681
  const totalRuns = allRuns.length;
33561
33682
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
@@ -33730,7 +33851,7 @@ async function compositeRoutes(app) {
33730
33851
  rawResponse: querySnapshots.rawResponse,
33731
33852
  createdAt: querySnapshots.createdAt
33732
33853
  }).from(querySnapshots).innerJoin(queries, eq39(querySnapshots.queryId, queries.id)).where(
33733
- and34(
33854
+ and35(
33734
33855
  eq39(queries.projectId, project.id),
33735
33856
  or7(
33736
33857
  sql13`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
@@ -33742,7 +33863,7 @@ async function compositeRoutes(app) {
33742
33863
  )
33743
33864
  ).orderBy(desc17(querySnapshots.createdAt)).limit(limit + 1).all());
33744
33865
  const insightMatches = app.db.select().from(insights).where(
33745
- and34(
33866
+ and35(
33746
33867
  eq39(insights.projectId, project.id),
33747
33868
  or7(
33748
33869
  like2(insights.title, pattern),
@@ -33977,7 +34098,7 @@ function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
33977
34098
  // NULLIF guards the degenerate impressions=0 case (SQLite returns NULL,
33978
34099
  // which the JS coerces to 0 — caught by the impression floor anyway).
33979
34100
  avgPosition: sql13`COALESCE(SUM(${gscSearchData.position} * ${gscSearchData.impressions}) * 1.0 / NULLIF(SUM(${gscSearchData.impressions}), 0), 0)`
33980
- }).from(gscSearchData).where(and34(
34101
+ }).from(gscSearchData).where(and35(
33981
34102
  eq39(gscSearchData.projectId, projectId),
33982
34103
  sql13`${gscSearchData.date} >= ${cutoff}`,
33983
34104
  // Same upper bound as the accurate source below. The merge picks a source
@@ -34504,6 +34625,7 @@ var SCHEMA_TABLE = {
34504
34625
  RunDetailDto: runDetailDtoSchema,
34505
34626
  RunDto: runDtoSchema,
34506
34627
  SchedulableRunKind: schedulableRunKindSchema,
34628
+ CalendarRecurrence: calendarRecurrenceSchema,
34507
34629
  ScheduleDto: scheduleDtoSchema,
34508
34630
  SettingsDto: settingsDtoSchema,
34509
34631
  SiteAuditLivePageHealthDto: siteAuditLivePageHealthSchema,
@@ -37252,6 +37374,7 @@ var routeCatalog = [
37252
37374
  method: "put",
37253
37375
  path: "/api/v1/projects/{name}/schedule",
37254
37376
  summary: "Create or update a schedule",
37377
+ description: "Provide exactly one of preset, cron, or recurrence. Calendar recurrence repeats every N local calendar days from startDate at time in the IANA timezone; it retains the anchor across daylight-saving changes.",
37255
37378
  tags: ["schedules"],
37256
37379
  parameters: [nameParameter, scheduleKindQueryParameter],
37257
37380
  requestBody: {
@@ -37264,6 +37387,7 @@ var routeCatalog = [
37264
37387
  kind: { $ref: "#/components/schemas/SchedulableRunKind" },
37265
37388
  preset: stringSchema,
37266
37389
  cron: stringSchema,
37390
+ recurrence: { $ref: "#/components/schemas/CalendarRecurrence" },
37267
37391
  timezone: stringSchema,
37268
37392
  providers: stringArraySchema,
37269
37393
  enabled: booleanSchema,
@@ -42078,7 +42202,7 @@ async function telemetryRoutes(app, opts) {
42078
42202
 
42079
42203
  // ../api-routes/src/schedules.ts
42080
42204
  import crypto26 from "crypto";
42081
- import { and as and35, asc as asc7, eq as eq42 } from "drizzle-orm";
42205
+ import { and as and36, asc as asc7, eq as eq42 } from "drizzle-orm";
42082
42206
  function parseKindParam(raw) {
42083
42207
  if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
42084
42208
  const parsed = schedulableRunKindSchema.safeParse(raw);
@@ -42106,7 +42230,7 @@ async function scheduleRoutes(app, opts) {
42106
42230
  });
42107
42231
  }
42108
42232
  const kind = parsedBody.data.kind ?? parseKindParam(request.query?.kind);
42109
- const { preset, cron, timezone, providers, enabled, sourceId, expectedUpdatedAt } = parsedBody.data;
42233
+ const { preset, cron, recurrence, timezone, providers, enabled, sourceId, expectedUpdatedAt } = parsedBody.data;
42110
42234
  if (kind === SchedulableRunKinds["traffic-sync"]) {
42111
42235
  if (!sourceId) {
42112
42236
  throw validationError('"sourceId" is required when kind is "traffic-sync"');
@@ -42141,7 +42265,9 @@ async function scheduleRoutes(app, opts) {
42141
42265
  throw validationError(`Invalid timezone: ${timezone}`);
42142
42266
  }
42143
42267
  let cronExpr;
42144
- if (preset) {
42268
+ if (recurrence) {
42269
+ cronExpr = "";
42270
+ } else if (preset) {
42145
42271
  try {
42146
42272
  cronExpr = resolvePreset(preset);
42147
42273
  } catch (err) {
@@ -42155,8 +42281,12 @@ async function scheduleRoutes(app, opts) {
42155
42281
  }
42156
42282
  }
42157
42283
  const enabledBool = enabled !== false;
42284
+ const nextRunAt = enabledBool ? nextRunFromSchedule({ cronExpr, timezone, recurrence }) : null;
42285
+ if (recurrence && enabledBool && !nextRunAt) {
42286
+ throw validationError("Calendar recurrence has no valid future occurrence");
42287
+ }
42158
42288
  const mutation = app.db.transaction((tx) => {
42159
- const existing = tx.select().from(schedules).where(and35(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42289
+ const existing = tx.select().from(schedules).where(and36(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42160
42290
  const actualUpdatedAt = existing?.updatedAt ?? null;
42161
42291
  if (expectedUpdatedAt !== void 0 && expectedUpdatedAt !== actualUpdatedAt) {
42162
42292
  throw scheduleVersionConflict(expectedUpdatedAt, actualUpdatedAt);
@@ -42165,6 +42295,8 @@ async function scheduleRoutes(app, opts) {
42165
42295
  const scheduleId = existing?.id ?? crypto26.randomUUID();
42166
42296
  const values = {
42167
42297
  cronExpr,
42298
+ recurrence: recurrence ?? null,
42299
+ nextRunAt,
42168
42300
  preset: preset ?? null,
42169
42301
  timezone,
42170
42302
  providers: providers ?? [],
@@ -42174,7 +42306,7 @@ async function scheduleRoutes(app, opts) {
42174
42306
  };
42175
42307
  if (existing) {
42176
42308
  const changed = tx.update(schedules).set(values).where(
42177
- expectedUpdatedAt === void 0 ? eq42(schedules.id, existing.id) : and35(eq42(schedules.id, existing.id), eq42(schedules.updatedAt, expectedUpdatedAt ?? existing.updatedAt))
42309
+ expectedUpdatedAt === void 0 ? eq42(schedules.id, existing.id) : and36(eq42(schedules.id, existing.id), eq42(schedules.updatedAt, expectedUpdatedAt ?? existing.updatedAt))
42178
42310
  ).run();
42179
42311
  if (changed.changes !== 1) {
42180
42312
  const actual = tx.select().from(schedules).where(eq42(schedules.id, existing.id)).get();
@@ -42190,7 +42322,7 @@ async function scheduleRoutes(app, opts) {
42190
42322
  });
42191
42323
  const inserted = expectedUpdatedAt === null ? insert.onConflictDoNothing().run() : insert.run();
42192
42324
  if (expectedUpdatedAt === null && inserted.changes !== 1) {
42193
- const actual = tx.select().from(schedules).where(and35(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42325
+ const actual = tx.select().from(schedules).where(and36(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42194
42326
  throw scheduleVersionConflict(null, actual?.updatedAt ?? null);
42195
42327
  }
42196
42328
  }
@@ -42199,14 +42331,14 @@ async function scheduleRoutes(app, opts) {
42199
42331
  actor: "api",
42200
42332
  action: existing ? "schedule.updated" : "schedule.created",
42201
42333
  entityType: "schedule",
42202
- diff: { kind, cronExpr, preset, timezone, providers, sourceId }
42334
+ diff: { kind, cronExpr, preset, recurrence, timezone, providers, sourceId }
42203
42335
  });
42204
42336
  return {
42205
42337
  existed: existing !== void 0
42206
42338
  };
42207
42339
  });
42208
42340
  opts.onScheduleUpdated?.("upsert", project.id, kind);
42209
- const schedule = app.db.select().from(schedules).where(and35(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42341
+ const schedule = app.db.select().from(schedules).where(and36(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42210
42342
  return reply.status(mutation.existed ? 200 : 201).send(formatSchedule(schedule));
42211
42343
  });
42212
42344
  app.get("/projects/:name/schedules", async (request, reply) => {
@@ -42217,7 +42349,7 @@ async function scheduleRoutes(app, opts) {
42217
42349
  app.get("/projects/:name/schedule", async (request, reply) => {
42218
42350
  const project = resolveProject(app.db, request.params.name);
42219
42351
  const kind = parseKindParam(request.query?.kind);
42220
- const schedule = app.db.select().from(schedules).where(and35(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42352
+ const schedule = app.db.select().from(schedules).where(and36(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42221
42353
  if (!schedule) {
42222
42354
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
42223
42355
  }
@@ -42228,7 +42360,7 @@ async function scheduleRoutes(app, opts) {
42228
42360
  const kind = parseKindParam(request.query?.kind);
42229
42361
  const expectedUpdatedAt = parseExpectedUpdatedAtParam(request.query?.expectedUpdatedAt);
42230
42362
  app.db.transaction((tx) => {
42231
- const schedule = tx.select().from(schedules).where(and35(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42363
+ const schedule = tx.select().from(schedules).where(and36(eq42(schedules.projectId, project.id), eq42(schedules.kind, kind))).get();
42232
42364
  if (!schedule) {
42233
42365
  if (expectedUpdatedAt !== void 0) throw scheduleVersionConflict(expectedUpdatedAt, null);
42234
42366
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
@@ -42237,7 +42369,7 @@ async function scheduleRoutes(app, opts) {
42237
42369
  throw scheduleVersionConflict(expectedUpdatedAt, schedule.updatedAt);
42238
42370
  }
42239
42371
  const deleted = tx.delete(schedules).where(
42240
- expectedUpdatedAt === void 0 ? eq42(schedules.id, schedule.id) : and35(eq42(schedules.id, schedule.id), eq42(schedules.updatedAt, expectedUpdatedAt))
42372
+ expectedUpdatedAt === void 0 ? eq42(schedules.id, schedule.id) : and36(eq42(schedules.id, schedule.id), eq42(schedules.updatedAt, expectedUpdatedAt))
42241
42373
  ).run();
42242
42374
  if (deleted.changes !== 1) {
42243
42375
  const actual = tx.select().from(schedules).where(eq42(schedules.id, schedule.id)).get();
@@ -42262,6 +42394,7 @@ function formatSchedule(row) {
42262
42394
  projectId: row.projectId,
42263
42395
  kind: row.kind,
42264
42396
  cronExpr: row.cronExpr,
42397
+ recurrence: row.recurrence,
42265
42398
  preset: row.preset,
42266
42399
  timezone: row.timezone,
42267
42400
  enabled: row.enabled,
@@ -42609,7 +42742,7 @@ function formatNotification(row) {
42609
42742
 
42610
42743
  // ../api-routes/src/google.ts
42611
42744
  import crypto32 from "crypto";
42612
- import { eq as eq44, and as and36, desc as desc19, sql as sql14, inArray as inArray19 } from "drizzle-orm";
42745
+ import { eq as eq44, and as and37, desc as desc19, sql as sql14, inArray as inArray19 } from "drizzle-orm";
42613
42746
 
42614
42747
  // ../api-routes/src/gsc-period-comparison.ts
42615
42748
  var DAY_MS = 864e5;
@@ -45050,8 +45183,8 @@ async function googleRoutes(app, opts) {
45050
45183
  impressions: [desc19(gscSearchData.impressions), desc19(gscSearchData.date), gscSearchData.query],
45051
45184
  date: [desc19(gscSearchData.date), desc19(gscSearchData.clicks), gscSearchData.query]
45052
45185
  }[orderBy];
45053
- const rows = app.db.select().from(gscSearchData).where(and36(...conditions)).orderBy(...orderColumns).limit(limitVal).offset(offsetVal).all();
45054
- const totalMatching = app.db.select({ total: sql14`COUNT(*)` }).from(gscSearchData).where(and36(...conditions)).get()?.total ?? 0;
45186
+ const rows = app.db.select().from(gscSearchData).where(and37(...conditions)).orderBy(...orderColumns).limit(limitVal).offset(offsetVal).all();
45187
+ const totalMatching = app.db.select({ total: sql14`COUNT(*)` }).from(gscSearchData).where(and37(...conditions)).get()?.total ?? 0;
45055
45188
  const latestAvailableDate = app.db.select({ latest: sql14`MAX(${gscSearchData.date})` }).from(gscSearchData).where(eq44(gscSearchData.projectId, project.id)).get()?.latest ?? null;
45056
45189
  return {
45057
45190
  rows: rows.map((r) => ({
@@ -45102,7 +45235,7 @@ async function googleRoutes(app, opts) {
45102
45235
  date: gscSearchData.date,
45103
45236
  clicks: sql14`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
45104
45237
  impressions: sql14`COALESCE(SUM(${gscSearchData.impressions}), 0)`
45105
- }).from(gscSearchData).where(and36(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
45238
+ }).from(gscSearchData).where(and37(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
45106
45239
  const propertyDates = new Set(dailyTotals.map((d) => d.date));
45107
45240
  const comparisonDaily = mergeGscDailyTotalsWithFallback(
45108
45241
  dailyTotals,
@@ -45207,7 +45340,7 @@ async function googleRoutes(app, opts) {
45207
45340
  page: gscSearchData.page,
45208
45341
  clicks: sql14`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
45209
45342
  impressions: sql14`COALESCE(SUM(${gscSearchData.impressions}), 0)`
45210
- }).from(gscSearchData).where(and36(...conditions)).groupBy(gscSearchData.page).orderBy(desc19(sql14`SUM(${gscSearchData.clicks})`), desc19(sql14`SUM(${gscSearchData.impressions})`)).limit(limitVal).all();
45343
+ }).from(gscSearchData).where(and37(...conditions)).groupBy(gscSearchData.page).orderBy(desc19(sql14`SUM(${gscSearchData.clicks})`), desc19(sql14`SUM(${gscSearchData.impressions})`)).limit(limitVal).all();
45211
45344
  const windowStart = startDate ?? cutoffDate ?? "";
45212
45345
  const windowEnd = endDate ?? resolvedWindow.endDate ?? "9999-12-31";
45213
45346
  const dailyTotals = readGscDailyTotals(app.db, project.id, windowStart, windowEnd);
@@ -45216,7 +45349,7 @@ async function googleRoutes(app, opts) {
45216
45349
  const rankedSpan = app.db.select({
45217
45350
  first: sql14`MIN(${gscSearchData.date})`,
45218
45351
  last: sql14`MAX(${gscSearchData.date})`
45219
- }).from(gscSearchData).where(and36(...conditions)).get();
45352
+ }).from(gscSearchData).where(and37(...conditions)).get();
45220
45353
  const coveredFrom = dailyTotals.length > 0 ? dailyTotals[0].date : null;
45221
45354
  const coveredThrough = dailyTotals.length > 0 ? dailyTotals[dailyTotals.length - 1].date : null;
45222
45355
  const totalsComplete = Boolean(
@@ -45310,7 +45443,7 @@ async function googleRoutes(app, opts) {
45310
45443
  const { url, limit } = request.query;
45311
45444
  const conditions = [eq44(gscUrlInspections.projectId, project.id)];
45312
45445
  if (url) conditions.push(eq44(gscUrlInspections.url, url));
45313
- const rows = app.db.select().from(gscUrlInspections).where(and36(...conditions)).orderBy(desc19(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
45446
+ const rows = app.db.select().from(gscUrlInspections).where(and37(...conditions)).orderBy(desc19(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
45314
45447
  return rows.map((r) => ({
45315
45448
  id: r.id,
45316
45449
  url: r.url,
@@ -45876,7 +46009,7 @@ async function googleRoutes(app, opts) {
45876
46009
  app.db.transaction((tx) => {
45877
46010
  if (switching) clearGbpProjectData(tx, project.id);
45878
46011
  for (const remote of remoteLocations) {
45879
- const existing = tx.select().from(gbpLocations).where(and36(eq44(gbpLocations.projectId, project.id), eq44(gbpLocations.locationName, remote.name))).get();
46012
+ const existing = tx.select().from(gbpLocations).where(and37(eq44(gbpLocations.projectId, project.id), eq44(gbpLocations.locationName, remote.name))).get();
45880
46013
  const profile = buildLocationProfileFields(remote);
45881
46014
  if (existing) {
45882
46015
  tx.update(gbpLocations).set({
@@ -45974,7 +46107,7 @@ async function googleRoutes(app, opts) {
45974
46107
  throw validationError(parsed.error.issues[0]?.message ?? "Invalid selection request");
45975
46108
  }
45976
46109
  const { selected } = parsed.data;
45977
- const existing = app.db.select().from(gbpLocations).where(and36(eq44(gbpLocations.projectId, project.id), eq44(gbpLocations.locationName, locationName))).get();
46110
+ const existing = app.db.select().from(gbpLocations).where(and37(eq44(gbpLocations.projectId, project.id), eq44(gbpLocations.locationName, locationName))).get();
45978
46111
  if (!existing) throw notFound("GBP location", locationName);
45979
46112
  const now = (/* @__PURE__ */ new Date()).toISOString();
45980
46113
  app.db.transaction((tx) => {
@@ -46034,7 +46167,7 @@ async function googleRoutes(app, opts) {
46034
46167
  const conditions = [eq44(gbpDailyMetrics.projectId, project.id)];
46035
46168
  if (request.query.locationName) conditions.push(eq44(gbpDailyMetrics.locationName, request.query.locationName));
46036
46169
  if (request.query.metric) conditions.push(eq44(gbpDailyMetrics.metric, request.query.metric));
46037
- const rows = app.db.select().from(gbpDailyMetrics).where(and36(...conditions)).orderBy(desc19(gbpDailyMetrics.date)).all();
46170
+ const rows = app.db.select().from(gbpDailyMetrics).where(and37(...conditions)).orderBy(desc19(gbpDailyMetrics.date)).all();
46038
46171
  return {
46039
46172
  metrics: rows.map((r) => ({ locationName: r.locationName, date: r.date, metric: r.metric, value: r.value })),
46040
46173
  total: rows.length
@@ -46044,7 +46177,7 @@ async function googleRoutes(app, opts) {
46044
46177
  const project = resolveProject(app.db, request.params.name);
46045
46178
  const conditions = [eq44(gbpKeywordImpressions.projectId, project.id)];
46046
46179
  if (request.query.locationName) conditions.push(eq44(gbpKeywordImpressions.locationName, request.query.locationName));
46047
- const rows = app.db.select().from(gbpKeywordImpressions).where(and36(...conditions)).all();
46180
+ const rows = app.db.select().from(gbpKeywordImpressions).where(and37(...conditions)).all();
46048
46181
  rows.sort((a, b) => (b.valueCount ?? -1) - (a.valueCount ?? -1));
46049
46182
  const thresholded = rows.filter((r) => r.valueThreshold !== null).length;
46050
46183
  return {
@@ -46064,7 +46197,7 @@ async function googleRoutes(app, opts) {
46064
46197
  const project = resolveProject(app.db, request.params.name);
46065
46198
  const conditions = [eq44(gbpPlaceActions.projectId, project.id)];
46066
46199
  if (request.query.locationName) conditions.push(eq44(gbpPlaceActions.locationName, request.query.locationName));
46067
- const rows = app.db.select().from(gbpPlaceActions).where(and36(...conditions)).all();
46200
+ const rows = app.db.select().from(gbpPlaceActions).where(and37(...conditions)).all();
46068
46201
  return {
46069
46202
  placeActions: rows.map((r) => ({
46070
46203
  locationName: r.locationName,
@@ -46081,7 +46214,7 @@ async function googleRoutes(app, opts) {
46081
46214
  const project = resolveProject(app.db, request.params.name);
46082
46215
  const conditions = [eq44(gbpLodgingSnapshots.projectId, project.id)];
46083
46216
  if (request.query.locationName) conditions.push(eq44(gbpLodgingSnapshots.locationName, request.query.locationName));
46084
- const rows = app.db.select().from(gbpLodgingSnapshots).where(and36(...conditions)).orderBy(desc19(gbpLodgingSnapshots.syncedAt)).all();
46217
+ const rows = app.db.select().from(gbpLodgingSnapshots).where(and37(...conditions)).orderBy(desc19(gbpLodgingSnapshots.syncedAt)).all();
46085
46218
  const latestByLocation = /* @__PURE__ */ new Map();
46086
46219
  for (const row of rows) {
46087
46220
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -46098,7 +46231,7 @@ async function googleRoutes(app, opts) {
46098
46231
  const project = resolveProject(app.db, request.params.name);
46099
46232
  const conditions = [eq44(gbpAttributesSnapshots.projectId, project.id)];
46100
46233
  if (request.query.locationName) conditions.push(eq44(gbpAttributesSnapshots.locationName, request.query.locationName));
46101
- const rows = app.db.select().from(gbpAttributesSnapshots).where(and36(...conditions)).orderBy(desc19(gbpAttributesSnapshots.syncedAt)).all();
46234
+ const rows = app.db.select().from(gbpAttributesSnapshots).where(and37(...conditions)).orderBy(desc19(gbpAttributesSnapshots.syncedAt)).all();
46102
46235
  const latestByLocation = /* @__PURE__ */ new Map();
46103
46236
  for (const row of rows) {
46104
46237
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -46118,7 +46251,7 @@ async function googleRoutes(app, opts) {
46118
46251
  const project = resolveProject(app.db, request.params.name);
46119
46252
  const conditions = [eq44(gbpPlaceDetails.projectId, project.id)];
46120
46253
  if (request.query.locationName) conditions.push(eq44(gbpPlaceDetails.locationName, request.query.locationName));
46121
- const rows = app.db.select().from(gbpPlaceDetails).where(and36(...conditions)).orderBy(desc19(gbpPlaceDetails.syncedAt)).all();
46254
+ const rows = app.db.select().from(gbpPlaceDetails).where(and37(...conditions)).orderBy(desc19(gbpPlaceDetails.syncedAt)).all();
46122
46255
  const latestByLocation = /* @__PURE__ */ new Map();
46123
46256
  for (const row of rows) {
46124
46257
  if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
@@ -46137,7 +46270,7 @@ async function googleRoutes(app, opts) {
46137
46270
  app.get("/projects/:name/gbp/summary", async (request) => {
46138
46271
  const project = resolveProject(app.db, request.params.name);
46139
46272
  const locationName = request.query.locationName ?? null;
46140
- const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and36(eq44(gbpLocations.projectId, project.id), eq44(gbpLocations.selected, true))).all().map((r) => r.n);
46273
+ const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and37(eq44(gbpLocations.projectId, project.id), eq44(gbpLocations.selected, true))).all().map((r) => r.n);
46141
46274
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
46142
46275
  if (locationNames.length === 0) {
46143
46276
  return buildGbpSummary({
@@ -46151,10 +46284,10 @@ async function googleRoutes(app, opts) {
46151
46284
  locationProfiles: []
46152
46285
  });
46153
46286
  }
46154
- const metricRows = app.db.select().from(gbpDailyMetrics).where(and36(eq44(gbpDailyMetrics.projectId, project.id), inArray19(gbpDailyMetrics.locationName, locationNames))).all();
46155
- const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and36(eq44(gbpKeywordImpressions.projectId, project.id), inArray19(gbpKeywordImpressions.locationName, locationNames))).all();
46156
- const placeActionRows = app.db.select().from(gbpPlaceActions).where(and36(eq44(gbpPlaceActions.projectId, project.id), inArray19(gbpPlaceActions.locationName, locationNames))).all();
46157
- const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and36(eq44(gbpLodgingSnapshots.projectId, project.id), inArray19(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc19(gbpLodgingSnapshots.syncedAt)).all();
46287
+ const metricRows = app.db.select().from(gbpDailyMetrics).where(and37(eq44(gbpDailyMetrics.projectId, project.id), inArray19(gbpDailyMetrics.locationName, locationNames))).all();
46288
+ const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and37(eq44(gbpKeywordImpressions.projectId, project.id), inArray19(gbpKeywordImpressions.locationName, locationNames))).all();
46289
+ const placeActionRows = app.db.select().from(gbpPlaceActions).where(and37(eq44(gbpPlaceActions.projectId, project.id), inArray19(gbpPlaceActions.locationName, locationNames))).all();
46290
+ const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and37(eq44(gbpLodgingSnapshots.projectId, project.id), inArray19(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc19(gbpLodgingSnapshots.syncedAt)).all();
46158
46291
  const latestLodgingByLocation = /* @__PURE__ */ new Map();
46159
46292
  for (const row of lodgingRows) {
46160
46293
  if (!latestLodgingByLocation.has(row.locationName)) {
@@ -46168,7 +46301,7 @@ async function googleRoutes(app, opts) {
46168
46301
  regularHours: gbpLocations.regularHours,
46169
46302
  primaryPhone: gbpLocations.primaryPhone,
46170
46303
  openStatus: gbpLocations.openStatus
46171
- }).from(gbpLocations).where(and36(eq44(gbpLocations.projectId, project.id), inArray19(gbpLocations.locationName, locationNames))).all();
46304
+ }).from(gbpLocations).where(and37(eq44(gbpLocations.projectId, project.id), inArray19(gbpLocations.locationName, locationNames))).all();
46172
46305
  return buildGbpSummary({
46173
46306
  locationName,
46174
46307
  locationCount: locationNames.length,
@@ -46184,7 +46317,7 @@ async function googleRoutes(app, opts) {
46184
46317
 
46185
46318
  // ../api-routes/src/google-marketing.ts
46186
46319
  import crypto33 from "crypto";
46187
- import { and as and37, desc as desc20, eq as eq45, isNull as isNull6, lt as lt6, ne as ne6, or as or8, sql as sql15 } from "drizzle-orm";
46320
+ import { and as and38, desc as desc20, eq as eq45, isNull as isNull6, lt as lt6, ne as ne6, or as or8, sql as sql15 } from "drizzle-orm";
46188
46321
  import { z as z4 } from "zod";
46189
46322
  var snapshotPageQuerySchema = z4.object({
46190
46323
  limit: z4.coerce.number().int().min(1).max(GOOGLE_MARKETING_STORED_SNAPSHOT_PAGE_MAX).optional(),
@@ -46466,7 +46599,7 @@ function toGtmSnapshot(row) {
46466
46599
  }
46467
46600
  function selectedGoogleAdsCustomer(app, projectId, selection) {
46468
46601
  if (!selection.lastCustomerSnapshotId) return null;
46469
- const row = app.db.select().from(googleAdsRawSnapshots).where(and37(
46602
+ const row = app.db.select().from(googleAdsRawSnapshots).where(and38(
46470
46603
  eq45(googleAdsRawSnapshots.projectId, projectId),
46471
46604
  eq45(googleAdsRawSnapshots.id, selection.lastCustomerSnapshotId),
46472
46605
  eq45(googleAdsRawSnapshots.connectionId, selection.connectionId),
@@ -46489,7 +46622,7 @@ function hasMatchingGtmGraphEvidence(app, projectId, selection) {
46489
46622
  eq45(gtmRawSnapshots.capturedAt, selection.lastSnapshotAt)
46490
46623
  ];
46491
46624
  conditions.push(selection.workspaceId === null ? isNull6(gtmRawSnapshots.workspaceId) : eq45(gtmRawSnapshots.workspaceId, selection.workspaceId));
46492
- const row = app.db.select().from(gtmRawSnapshots).where(and37(...conditions)).get();
46625
+ const row = app.db.select().from(gtmRawSnapshots).where(and38(...conditions)).get();
46493
46626
  if (!row) return false;
46494
46627
  const snapshot = toGtmSnapshot(row);
46495
46628
  return Boolean(
@@ -46609,7 +46742,7 @@ function latestGoogleAdsSnapshot(app, projectId) {
46609
46742
  eq45(googleAdsRawSnapshots.kind, GoogleAdsSnapshotKinds.inventory),
46610
46743
  eq45(googleAdsRawSnapshots.capturedAt, connection.lastInventorySnapshotAt)
46611
46744
  ];
46612
- const row = app.db.select().from(googleAdsRawSnapshots).where(and37(...conditions)).get();
46745
+ const row = app.db.select().from(googleAdsRawSnapshots).where(and38(...conditions)).get();
46613
46746
  const snapshot = row ? toGoogleAdsSnapshot(row) : null;
46614
46747
  if (!snapshot || snapshot.metadata.id !== connection.lastInventorySnapshotId || snapshot.metadata.connectionId !== connection.id || snapshot.metadata.customerId !== connection.selectedCustomerId || snapshot.metadata.capturedAt !== connection.lastInventorySnapshotAt || snapshot.payload.kind !== GoogleAdsSnapshotKinds.inventory || snapshot.payload.data.customerId !== connection.selectedCustomerId) return null;
46615
46748
  return snapshot;
@@ -46630,7 +46763,7 @@ function latestGtmSnapshot(app, projectId) {
46630
46763
  eq45(gtmRawSnapshots.kind, GtmSnapshotKinds.container)
46631
46764
  ];
46632
46765
  conditions.push(connection.selectedWorkspaceId === null ? isNull6(gtmRawSnapshots.workspaceId) : eq45(gtmRawSnapshots.workspaceId, connection.selectedWorkspaceId));
46633
- const row = app.db.select().from(gtmRawSnapshots).where(and37(...conditions)).get();
46766
+ const row = app.db.select().from(gtmRawSnapshots).where(and38(...conditions)).get();
46634
46767
  const snapshot = row ? toGtmSnapshot(row) : null;
46635
46768
  if (!snapshot || snapshot.metadata.id !== connection.lastSnapshotId || snapshot.metadata.connectionId !== connection.id || snapshot.metadata.accountId !== connection.selectedAccountId || snapshot.metadata.containerId !== connection.selectedContainerId || snapshot.metadata.workspaceId !== connection.selectedWorkspaceId || snapshot.metadata.capturedAt !== connection.lastSnapshotAt || snapshot.payload.kind !== GtmSnapshotKinds.container) return null;
46636
46769
  return snapshot;
@@ -46680,7 +46813,7 @@ function latestGoogleAdsMetricsEvidence(app, projectId) {
46680
46813
  eq45(googleAdsRawSnapshots.kind, GoogleAdsSnapshotKinds["campaign-metrics"]),
46681
46814
  eq45(googleAdsRawSnapshots.capturedAt, connection.lastMetricsSnapshotAt)
46682
46815
  ];
46683
- const row = app.db.select().from(googleAdsRawSnapshots).where(and37(...conditions)).get();
46816
+ const row = app.db.select().from(googleAdsRawSnapshots).where(and38(...conditions)).get();
46684
46817
  const snapshot = row ? toGoogleAdsSnapshot(row) : null;
46685
46818
  if (!snapshot || snapshot.metadata.id !== connection.lastMetricsSnapshotId || snapshot.metadata.connectionId !== connection.id || snapshot.metadata.customerId !== connection.selectedCustomerId || snapshot.metadata.capturedAt !== connection.lastMetricsSnapshotAt || snapshot.payload.kind !== GoogleAdsSnapshotKinds["campaign-metrics"]) return null;
46686
46819
  return {
@@ -47396,7 +47529,7 @@ async function googleMarketingRoutes(app, opts) {
47396
47529
  let run = candidateRun;
47397
47530
  let queued = false;
47398
47531
  app.db.transaction((tx) => {
47399
- const existing = tx.select().from(runs).where(and37(
47532
+ const existing = tx.select().from(runs).where(and38(
47400
47533
  eq45(runs.projectId, project.id),
47401
47534
  eq45(runs.kind, RunKinds["google-ads-sync"]),
47402
47535
  or8(eq45(runs.status, RunStatuses.queued), eq45(runs.status, RunStatuses.running))
@@ -47432,7 +47565,7 @@ async function googleMarketingRoutes(app, opts) {
47432
47565
  let run = candidateRun;
47433
47566
  let queued = false;
47434
47567
  app.db.transaction((tx) => {
47435
- const existing = tx.select().from(runs).where(and37(
47568
+ const existing = tx.select().from(runs).where(and38(
47436
47569
  eq45(runs.projectId, project.id),
47437
47570
  eq45(runs.kind, RunKinds["gtm-sync"]),
47438
47571
  or8(eq45(runs.status, RunStatuses.queued), eq45(runs.status, RunStatuses.running))
@@ -47467,11 +47600,11 @@ async function googleMarketingRoutes(app, opts) {
47467
47600
  if (cursor) {
47468
47601
  conditions.push(or8(
47469
47602
  lt6(googleAdsRawSnapshots.capturedAt, cursor.capturedAt),
47470
- and37(eq45(googleAdsRawSnapshots.capturedAt, cursor.capturedAt), lt6(googleAdsRawSnapshots.id, cursor.id))
47603
+ and38(eq45(googleAdsRawSnapshots.capturedAt, cursor.capturedAt), lt6(googleAdsRawSnapshots.id, cursor.id))
47471
47604
  ));
47472
47605
  }
47473
47606
  const limit = query.limit ?? 50;
47474
- const rows = app.db.select().from(googleAdsRawSnapshots).where(and37(...conditions)).orderBy(desc20(googleAdsRawSnapshots.capturedAt), desc20(googleAdsRawSnapshots.id)).limit(limit + 1).all();
47607
+ const rows = app.db.select().from(googleAdsRawSnapshots).where(and38(...conditions)).orderBy(desc20(googleAdsRawSnapshots.capturedAt), desc20(googleAdsRawSnapshots.id)).limit(limit + 1).all();
47475
47608
  const page3 = rows.slice(0, limit);
47476
47609
  const total = app.db.select({ total: sql15`COUNT(*)`.mapWith(Number).as("total") }).from(googleAdsRawSnapshots).where(eq45(googleAdsRawSnapshots.projectId, project.id)).get();
47477
47610
  return {
@@ -47482,7 +47615,7 @@ async function googleMarketingRoutes(app, opts) {
47482
47615
  });
47483
47616
  app.get("/projects/:name/google-ads/snapshots/:snapshotId", async (request) => {
47484
47617
  const project = resolveProject(app.db, request.params.name);
47485
- const row = app.db.select().from(googleAdsRawSnapshots).where(and37(
47618
+ const row = app.db.select().from(googleAdsRawSnapshots).where(and38(
47486
47619
  eq45(googleAdsRawSnapshots.projectId, project.id),
47487
47620
  eq45(googleAdsRawSnapshots.id, request.params.snapshotId)
47488
47621
  )).get();
@@ -47499,11 +47632,11 @@ async function googleMarketingRoutes(app, opts) {
47499
47632
  if (cursor) {
47500
47633
  conditions.push(or8(
47501
47634
  lt6(gtmRawSnapshots.capturedAt, cursor.capturedAt),
47502
- and37(eq45(gtmRawSnapshots.capturedAt, cursor.capturedAt), lt6(gtmRawSnapshots.id, cursor.id))
47635
+ and38(eq45(gtmRawSnapshots.capturedAt, cursor.capturedAt), lt6(gtmRawSnapshots.id, cursor.id))
47503
47636
  ));
47504
47637
  }
47505
47638
  const limit = query.limit ?? 50;
47506
- const rows = app.db.select().from(gtmRawSnapshots).where(and37(...conditions)).orderBy(desc20(gtmRawSnapshots.capturedAt), desc20(gtmRawSnapshots.id)).limit(limit + 1).all();
47639
+ const rows = app.db.select().from(gtmRawSnapshots).where(and38(...conditions)).orderBy(desc20(gtmRawSnapshots.capturedAt), desc20(gtmRawSnapshots.id)).limit(limit + 1).all();
47507
47640
  const page3 = rows.slice(0, limit);
47508
47641
  const total = app.db.select({ total: sql15`COUNT(*)`.mapWith(Number).as("total") }).from(gtmRawSnapshots).where(eq45(gtmRawSnapshots.projectId, project.id)).get();
47509
47642
  return {
@@ -47514,7 +47647,7 @@ async function googleMarketingRoutes(app, opts) {
47514
47647
  });
47515
47648
  app.get("/projects/:name/gtm/snapshots/:snapshotId", async (request) => {
47516
47649
  const project = resolveProject(app.db, request.params.name);
47517
- const row = app.db.select().from(gtmRawSnapshots).where(and37(
47650
+ const row = app.db.select().from(gtmRawSnapshots).where(and38(
47518
47651
  eq45(gtmRawSnapshots.projectId, project.id),
47519
47652
  eq45(gtmRawSnapshots.id, request.params.snapshotId)
47520
47653
  )).get();
@@ -47639,7 +47772,7 @@ async function googleMarketingRoutes(app, opts) {
47639
47772
  parseBody2(conversionTrackingContractWriteRequestSchema, request.body, "conversion tracking contract")
47640
47773
  );
47641
47774
  const project = resolveProject(app.db, request.params.name);
47642
- const duplicate = app.db.select({ id: conversionTrackingContracts.id }).from(conversionTrackingContracts).where(and37(eq45(conversionTrackingContracts.projectId, project.id), eq45(conversionTrackingContracts.name, body.name))).get();
47775
+ const duplicate = app.db.select({ id: conversionTrackingContracts.id }).from(conversionTrackingContracts).where(and38(eq45(conversionTrackingContracts.projectId, project.id), eq45(conversionTrackingContracts.name, body.name))).get();
47643
47776
  if (duplicate) throw alreadyExists("Conversion tracking contract", body.name);
47644
47777
  const now = (/* @__PURE__ */ new Date()).toISOString();
47645
47778
  const contract = {
@@ -47664,7 +47797,7 @@ async function googleMarketingRoutes(app, opts) {
47664
47797
  });
47665
47798
  app.get("/projects/:name/conversion-tracking/contracts/:contractId", async (request) => {
47666
47799
  const project = resolveProject(app.db, request.params.name);
47667
- const row = app.db.select().from(conversionTrackingContracts).where(and37(
47800
+ const row = app.db.select().from(conversionTrackingContracts).where(and38(
47668
47801
  eq45(conversionTrackingContracts.projectId, project.id),
47669
47802
  eq45(conversionTrackingContracts.id, request.params.contractId)
47670
47803
  )).get();
@@ -47677,12 +47810,12 @@ async function googleMarketingRoutes(app, opts) {
47677
47810
  parseBody2(conversionTrackingContractWriteRequestSchema, request.body, "conversion tracking contract")
47678
47811
  );
47679
47812
  const project = resolveProject(app.db, request.params.name);
47680
- const existing = app.db.select().from(conversionTrackingContracts).where(and37(
47813
+ const existing = app.db.select().from(conversionTrackingContracts).where(and38(
47681
47814
  eq45(conversionTrackingContracts.projectId, project.id),
47682
47815
  eq45(conversionTrackingContracts.id, request.params.contractId)
47683
47816
  )).get();
47684
47817
  if (!existing) throw notFound("Conversion tracking contract", request.params.contractId);
47685
- const duplicate = app.db.select({ id: conversionTrackingContracts.id }).from(conversionTrackingContracts).where(and37(
47818
+ const duplicate = app.db.select({ id: conversionTrackingContracts.id }).from(conversionTrackingContracts).where(and38(
47686
47819
  eq45(conversionTrackingContracts.projectId, project.id),
47687
47820
  eq45(conversionTrackingContracts.name, body.name),
47688
47821
  ne6(conversionTrackingContracts.id, existing.id)
@@ -47719,7 +47852,7 @@ async function googleMarketingRoutes(app, opts) {
47719
47852
  app.delete("/projects/:name/conversion-tracking/contracts/:contractId", async (request, reply) => {
47720
47853
  requireWrite(request);
47721
47854
  const project = resolveProject(app.db, request.params.name);
47722
- const existing = app.db.select().from(conversionTrackingContracts).where(and37(
47855
+ const existing = app.db.select().from(conversionTrackingContracts).where(and38(
47723
47856
  eq45(conversionTrackingContracts.projectId, project.id),
47724
47857
  eq45(conversionTrackingContracts.id, request.params.contractId)
47725
47858
  )).get();
@@ -47739,7 +47872,7 @@ async function googleMarketingRoutes(app, opts) {
47739
47872
  });
47740
47873
  app.get("/projects/:name/conversion-tracking/contracts/:contractId/integrity", async (request) => {
47741
47874
  const project = resolveProject(app.db, request.params.name);
47742
- const row = app.db.select().from(conversionTrackingContracts).where(and37(
47875
+ const row = app.db.select().from(conversionTrackingContracts).where(and38(
47743
47876
  eq45(conversionTrackingContracts.projectId, project.id),
47744
47877
  eq45(conversionTrackingContracts.id, request.params.contractId)
47745
47878
  )).get();
@@ -47774,7 +47907,7 @@ async function googleMarketingRoutes(app, opts) {
47774
47907
 
47775
47908
  // ../api-routes/src/ads.ts
47776
47909
  import crypto36 from "crypto";
47777
- import { eq as eq47, and as and39, asc as asc9, desc as desc21, gt as gt3, gte as gte12, lt as lt7, lte as lte11, ne as ne7, inArray as inArray20, or as or10, isNull as isNull8, isNotNull as isNotNull4, sql as sql17 } from "drizzle-orm";
47910
+ import { eq as eq47, and as and40, asc as asc9, desc as desc21, gt as gt3, gte as gte12, lt as lt7, lte as lte11, ne as ne7, inArray as inArray20, or as or10, isNull as isNull8, isNotNull as isNotNull4, sql as sql17 } from "drizzle-orm";
47778
47911
 
47779
47912
  // ../api-routes/src/ads-activation.ts
47780
47913
  import crypto34 from "crypto";
@@ -49160,7 +49293,7 @@ async function executeApprovedAdsActivation(dependencies, request) {
49160
49293
 
49161
49294
  // ../api-routes/src/ads-activation-routes.ts
49162
49295
  import crypto35 from "crypto";
49163
- import { and as and38, asc as asc8, eq as eq46, gt as gt2, isNotNull as isNotNull3, isNull as isNull7, lte as lte10, or as or9, sql as sql16 } from "drizzle-orm";
49296
+ import { and as and39, asc as asc8, eq as eq46, gt as gt2, isNotNull as isNotNull3, isNull as isNull7, lte as lte10, or as or9, sql as sql16 } from "drizzle-orm";
49164
49297
  var MAX_ACTIVATION_GRANT_LIFETIME_MS = 24 * 60 * 60 * 1e3;
49165
49298
  var ACTIVATION_LEASE_MS = 5 * 6e4;
49166
49299
  var ACTIVATION_REVOCATION_SETTLEMENT_MS = ACTIVATION_LEASE_MS;
@@ -49258,7 +49391,7 @@ function createActivationStore(app, request) {
49258
49391
  const bindingRejection = validateGrantBinding(grantRow, input);
49259
49392
  if (bindingRejection) return bindingRejection;
49260
49393
  const grant = grantRow;
49261
- const existing = tx.select().from(adsOperations).where(and38(
49394
+ const existing = tx.select().from(adsOperations).where(and39(
49262
49395
  eq46(adsOperations.projectId, input.projectId),
49263
49396
  eq46(adsOperations.operationKey, input.operationKey)
49264
49397
  )).get();
@@ -49283,7 +49416,7 @@ function createActivationStore(app, request) {
49283
49416
  leaseOwner: input.leaseOwner,
49284
49417
  leaseExpiresAt: input.leaseExpiresAt,
49285
49418
  updatedAt: input.now
49286
- }).where(and38(
49419
+ }).where(and39(
49287
49420
  eq46(adsOperations.id, existing.id),
49288
49421
  eq46(adsOperations.state, AdsOperationStates.pending),
49289
49422
  or9(
@@ -49305,7 +49438,7 @@ function createActivationStore(app, request) {
49305
49438
  state: AdsActivationGrantStates.expired,
49306
49439
  expiredAt: input.now,
49307
49440
  updatedAt: input.now
49308
- }).where(and38(
49441
+ }).where(and39(
49309
49442
  eq46(adsActivationGrants.id, grant.id),
49310
49443
  eq46(adsActivationGrants.state, AdsActivationGrantStates.approved)
49311
49444
  )).run();
@@ -49341,7 +49474,7 @@ function createActivationStore(app, request) {
49341
49474
  operationId: input.operationId,
49342
49475
  executionStartedAt: input.now,
49343
49476
  updatedAt: input.now
49344
- }).where(and38(
49477
+ }).where(and39(
49345
49478
  eq46(adsActivationGrants.id, grant.id),
49346
49479
  eq46(adsActivationGrants.projectId, input.projectId),
49347
49480
  eq46(adsActivationGrants.adAccountId, input.adAccountId),
@@ -49377,7 +49510,7 @@ function createActivationStore(app, request) {
49377
49510
  const changed = tx.update(adsOperations).set({
49378
49511
  leaseExpiresAt: input.leaseExpiresAt,
49379
49512
  updatedAt: input.now
49380
- }).where(and38(
49513
+ }).where(and39(
49381
49514
  eq46(adsOperations.id, input.operationId),
49382
49515
  eq46(adsOperations.state, AdsOperationStates.pending),
49383
49516
  eq46(adsOperations.leaseOwner, input.leaseOwner),
@@ -49392,7 +49525,7 @@ function createActivationStore(app, request) {
49392
49525
  leaseOwner: null,
49393
49526
  leaseExpiresAt: null,
49394
49527
  updatedAt: input.now
49395
- }).where(and38(
49528
+ }).where(and39(
49396
49529
  eq46(adsOperations.id, input.operationId),
49397
49530
  eq46(adsOperations.state, AdsOperationStates.pending),
49398
49531
  eq46(adsOperations.leaseOwner, input.leaseOwner)
@@ -49405,7 +49538,7 @@ function createActivationStore(app, request) {
49405
49538
  if (input.fromState !== AdsOperationStepStates.pending || input.next.state !== AdsOperationStepStates.executing) {
49406
49539
  throw internalError("Ads activation forward authorization transition is invalid");
49407
49540
  }
49408
- const grant = tx.select().from(adsActivationGrants).where(and38(
49541
+ const grant = tx.select().from(adsActivationGrants).where(and39(
49409
49542
  eq46(adsActivationGrants.id, input.grantId),
49410
49543
  eq46(adsActivationGrants.operationId, input.operationId)
49411
49544
  )).get();
@@ -49421,7 +49554,7 @@ function createActivationStore(app, request) {
49421
49554
  if (!Number.isFinite(expiresAt) || expiresAt <= Date.parse(input.now)) {
49422
49555
  return { applied: false, operation, rejection: "expired" };
49423
49556
  }
49424
- const existing = tx.select().from(adsOperationSteps).where(and38(
49557
+ const existing = tx.select().from(adsOperationSteps).where(and39(
49425
49558
  eq46(adsOperationSteps.id, input.next.id),
49426
49559
  eq46(adsOperationSteps.operationId, input.operationId)
49427
49560
  )).get();
@@ -49433,7 +49566,7 @@ function createActivationStore(app, request) {
49433
49566
  const lease = tx.update(adsOperations).set({
49434
49567
  leaseExpiresAt: input.leaseExpiresAt,
49435
49568
  updatedAt: input.next.updatedAt
49436
- }).where(and38(
49569
+ }).where(and39(
49437
49570
  eq46(adsOperations.id, input.operationId),
49438
49571
  eq46(adsOperations.state, AdsOperationStates.pending),
49439
49572
  eq46(adsOperations.leaseOwner, input.leaseOwner),
@@ -49453,7 +49586,7 @@ function createActivationStore(app, request) {
49453
49586
  startedAt: input.next.startedAt,
49454
49587
  finishedAt: input.next.finishedAt,
49455
49588
  updatedAt: input.next.updatedAt
49456
- }).where(and38(
49589
+ }).where(and39(
49457
49590
  eq46(adsOperationSteps.id, input.next.id),
49458
49591
  eq46(adsOperationSteps.operationId, input.operationId),
49459
49592
  eq46(adsOperationSteps.state, input.fromState)
@@ -49463,7 +49596,7 @@ function createActivationStore(app, request) {
49463
49596
  return { applied: changed.changes === 1, operation: current };
49464
49597
  }, { behavior: "immediate" }),
49465
49598
  transitionStep: async (input) => app.db.transaction((tx) => {
49466
- const existing = tx.select().from(adsOperationSteps).where(and38(
49599
+ const existing = tx.select().from(adsOperationSteps).where(and39(
49467
49600
  eq46(adsOperationSteps.id, input.next.id),
49468
49601
  eq46(adsOperationSteps.operationId, input.operationId)
49469
49602
  )).get();
@@ -49475,7 +49608,7 @@ function createActivationStore(app, request) {
49475
49608
  const lease = tx.update(adsOperations).set({
49476
49609
  leaseExpiresAt: input.leaseExpiresAt,
49477
49610
  updatedAt: input.next.updatedAt
49478
- }).where(and38(
49611
+ }).where(and39(
49479
49612
  eq46(adsOperations.id, input.operationId),
49480
49613
  eq46(adsOperations.state, AdsOperationStates.pending),
49481
49614
  eq46(adsOperations.leaseOwner, input.leaseOwner),
@@ -49495,7 +49628,7 @@ function createActivationStore(app, request) {
49495
49628
  startedAt: input.next.startedAt,
49496
49629
  finishedAt: input.next.finishedAt,
49497
49630
  updatedAt: input.next.updatedAt
49498
- }).where(and38(
49631
+ }).where(and39(
49499
49632
  eq46(adsOperationSteps.id, input.next.id),
49500
49633
  eq46(adsOperationSteps.operationId, input.operationId),
49501
49634
  eq46(adsOperationSteps.state, input.fromState)
@@ -49505,7 +49638,7 @@ function createActivationStore(app, request) {
49505
49638
  return { applied: changed.changes === 1, operation };
49506
49639
  }, { behavior: "immediate" }),
49507
49640
  finishOperation: async (input) => app.db.transaction((tx) => {
49508
- const currentGrant = tx.select().from(adsActivationGrants).where(and38(
49641
+ const currentGrant = tx.select().from(adsActivationGrants).where(and39(
49509
49642
  eq46(adsActivationGrants.id, input.grantId),
49510
49643
  eq46(adsActivationGrants.operationId, input.operationId)
49511
49644
  )).get();
@@ -49526,7 +49659,7 @@ function createActivationStore(app, request) {
49526
49659
  );
49527
49660
  }
49528
49661
  const effectiveGrantState = input.operationState === AdsOperationStates.unknown ? AdsActivationGrantStates.unknown : input.grantState;
49529
- const campaignStep = tx.select().from(adsOperationSteps).where(and38(
49662
+ const campaignStep = tx.select().from(adsOperationSteps).where(and39(
49530
49663
  eq46(adsOperationSteps.operationId, input.operationId),
49531
49664
  eq46(adsOperationSteps.entityType, AdsActivationEntityTypes.campaign)
49532
49665
  )).get();
@@ -49538,7 +49671,7 @@ function createActivationStore(app, request) {
49538
49671
  leaseOwner: null,
49539
49672
  leaseExpiresAt: null,
49540
49673
  updatedAt: input.now
49541
- }).where(and38(
49674
+ }).where(and39(
49542
49675
  eq46(adsOperations.id, input.operationId),
49543
49676
  eq46(adsOperations.state, AdsOperationStates.pending),
49544
49677
  eq46(adsOperations.leaseOwner, input.leaseOwner),
@@ -49549,7 +49682,7 @@ function createActivationStore(app, request) {
49549
49682
  state: effectiveGrantState,
49550
49683
  consumedAt: effectiveGrantState === AdsActivationGrantStates.consumed ? input.now : null,
49551
49684
  updatedAt: input.now
49552
- }).where(and38(
49685
+ }).where(and39(
49553
49686
  eq46(adsActivationGrants.id, input.grantId),
49554
49687
  eq46(adsActivationGrants.operationId, input.operationId),
49555
49688
  eq46(adsActivationGrants.state, AdsActivationGrantStates.executing)
@@ -49718,14 +49851,14 @@ function selectWatchdogOperations(app, nowIso, batchSize, allowLiveRevocationLea
49718
49851
  );
49719
49852
  const eligibleState = allowLiveRevocationLease ? or9(
49720
49853
  eq46(adsOperations.state, AdsOperationStates.unknown),
49721
- and38(
49854
+ and39(
49722
49855
  eq46(adsOperations.state, AdsOperationStates.pending),
49723
49856
  or9(
49724
49857
  isNotNull3(adsActivationGrants.revocationRequestedAt),
49725
- and38(leaseAvailable, campaignHasMutationEvidence)
49858
+ and39(leaseAvailable, campaignHasMutationEvidence)
49726
49859
  )
49727
49860
  )
49728
- ) : and38(
49861
+ ) : and39(
49729
49862
  or9(
49730
49863
  eq46(adsOperations.state, AdsOperationStates.unknown),
49731
49864
  eq46(adsOperations.state, AdsOperationStates.pending)
@@ -49747,11 +49880,11 @@ function selectWatchdogOperations(app, nowIso, batchSize, allowLiveRevocationLea
49747
49880
  eq46(adsActivationGrants.operationId, adsOperations.id)
49748
49881
  ).leftJoin(
49749
49882
  adsOperationSteps,
49750
- and38(
49883
+ and39(
49751
49884
  eq46(adsOperationSteps.operationId, adsOperations.id),
49752
49885
  eq46(adsOperationSteps.entityType, AdsActivationEntityTypes.campaign)
49753
49886
  )
49754
- ).where(and38(
49887
+ ).where(and39(
49755
49888
  eq46(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
49756
49889
  eligibleState
49757
49890
  )).orderBy(
@@ -49778,7 +49911,7 @@ function finalizeContainedRevokedUnknownActivation(app, operation, grant) {
49778
49911
  leaseExpiresAt: null,
49779
49912
  lastReconciledAt: nowIso,
49780
49913
  updatedAt: nowIso
49781
- }).where(and38(
49914
+ }).where(and39(
49782
49915
  eq46(adsOperations.id, operation.id),
49783
49916
  eq46(adsOperations.state, AdsOperationStates.unknown)
49784
49917
  )).run();
@@ -49787,7 +49920,7 @@ function finalizeContainedRevokedUnknownActivation(app, operation, grant) {
49787
49920
  state: AdsActivationGrantStates.consumed,
49788
49921
  consumedAt: nowIso,
49789
49922
  updatedAt: nowIso
49790
- }).where(and38(
49923
+ }).where(and39(
49791
49924
  eq46(adsActivationGrants.id, grant.id),
49792
49925
  eq46(adsActivationGrants.operationId, operation.id),
49793
49926
  eq46(adsActivationGrants.state, AdsActivationGrantStates.unknown),
@@ -49868,7 +50001,7 @@ async function containUrgentAdsActivationCampaignsOnce(app, opts) {
49868
50001
  }
49869
50002
  app.db.update(adsOperations).set({
49870
50003
  lastReconciledAt: (/* @__PURE__ */ new Date()).toISOString()
49871
- }).where(and38(
50004
+ }).where(and39(
49872
50005
  eq46(adsOperations.id, operation.id),
49873
50006
  or9(
49874
50007
  eq46(adsOperations.state, AdsOperationStates.unknown),
@@ -49894,11 +50027,11 @@ async function sweepStaleAdsActivationsOnce(app, opts) {
49894
50027
  leaseOwner: null,
49895
50028
  leaseExpiresAt: null,
49896
50029
  updatedAt: nowIso
49897
- }).where(and38(
50030
+ }).where(and39(
49898
50031
  eq46(adsOperations.id, operation.id),
49899
50032
  or9(
49900
50033
  eq46(adsOperations.state, AdsOperationStates.unknown),
49901
- and38(
50034
+ and39(
49902
50035
  eq46(adsOperations.state, AdsOperationStates.pending),
49903
50036
  or9(
49904
50037
  isNull7(adsOperations.leaseOwner),
@@ -49912,7 +50045,7 @@ async function sweepStaleAdsActivationsOnce(app, opts) {
49912
50045
  tx.update(adsActivationGrants).set({
49913
50046
  state: AdsActivationGrantStates.unknown,
49914
50047
  updatedAt: nowIso
49915
- }).where(and38(
50048
+ }).where(and39(
49916
50049
  eq46(adsActivationGrants.id, grant.id),
49917
50050
  eq46(adsActivationGrants.operationId, operation.id),
49918
50051
  eq46(adsActivationGrants.state, AdsActivationGrantStates.executing)
@@ -49931,11 +50064,11 @@ async function sweepStaleAdsActivationsOnce(app, opts) {
49931
50064
  }));
49932
50065
  const markRecoveryIncomplete = (operation) => {
49933
50066
  const failedAt = (/* @__PURE__ */ new Date()).toISOString();
49934
- app.db.update(adsOperations).set({ updatedAt: failedAt }).where(and38(
50067
+ app.db.update(adsOperations).set({ updatedAt: failedAt }).where(and39(
49935
50068
  eq46(adsOperations.id, operation.id),
49936
50069
  or9(
49937
50070
  eq46(adsOperations.state, AdsOperationStates.unknown),
49938
- and38(
50071
+ and39(
49939
50072
  eq46(adsOperations.state, AdsOperationStates.pending),
49940
50073
  or9(
49941
50074
  isNull7(adsOperations.leaseOwner),
@@ -49974,7 +50107,7 @@ async function sweepStaleAdsActivationsOnce(app, opts) {
49974
50107
  lastReconciledAt: (/* @__PURE__ */ new Date()).toISOString(),
49975
50108
  errorMessage: safety.fullyVerified ? "Unknown activation was safety-paused; manual remediation is still required" : "Unknown activation safety pause could not be fully verified",
49976
50109
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
49977
- }).where(and38(
50110
+ }).where(and39(
49978
50111
  eq46(adsOperations.id, operation.id),
49979
50112
  eq46(adsOperations.state, AdsOperationStates.unknown)
49980
50113
  )).run();
@@ -50137,13 +50270,13 @@ function registerAdsActivationRoutes(app, opts) {
50137
50270
  adsActivationGrantRevokeRequestSchema,
50138
50271
  request.body === void 0 ? {} : request.body
50139
50272
  );
50140
- const existing = app.db.select().from(adsActivationGrants).where(and38(
50273
+ const existing = app.db.select().from(adsActivationGrants).where(and39(
50141
50274
  eq46(adsActivationGrants.id, request.params.grantId),
50142
50275
  eq46(adsActivationGrants.projectId, project.id)
50143
50276
  )).get();
50144
50277
  if (!existing) throw notFound("Ads activation grant", request.params.grantId);
50145
50278
  const grant = app.db.transaction((tx) => {
50146
- const current = tx.select().from(adsActivationGrants).where(and38(
50279
+ const current = tx.select().from(adsActivationGrants).where(and39(
50147
50280
  eq46(adsActivationGrants.id, request.params.grantId),
50148
50281
  eq46(adsActivationGrants.projectId, project.id)
50149
50282
  )).get();
@@ -50164,7 +50297,7 @@ function registerAdsActivationRoutes(app, opts) {
50164
50297
  tx.update(adsActivationGrants).set({
50165
50298
  revocationRequestedAt: nowIso,
50166
50299
  updatedAt: nowIso
50167
- }).where(and38(
50300
+ }).where(and39(
50168
50301
  eq46(adsActivationGrants.id, current.id),
50169
50302
  eq46(adsActivationGrants.state, current.state),
50170
50303
  isNull7(adsActivationGrants.revocationRequestedAt)
@@ -50175,7 +50308,7 @@ function registerAdsActivationRoutes(app, opts) {
50175
50308
  revokedAt: expired ? null : nowIso,
50176
50309
  expiredAt: expired ? nowIso : null,
50177
50310
  updatedAt: nowIso
50178
- }).where(and38(
50311
+ }).where(and39(
50179
50312
  eq46(adsActivationGrants.id, current.id),
50180
50313
  eq46(adsActivationGrants.state, AdsActivationGrantStates.approved)
50181
50314
  )).run();
@@ -50205,7 +50338,7 @@ function registerAdsActivationRoutes(app, opts) {
50205
50338
  const executor = requireAuthenticatedKey(request);
50206
50339
  const project = resolveProject(app.db, request.params.name);
50207
50340
  const body = parseBody3(adsActivateTreeRequestSchema, request.body);
50208
- const grant = app.db.select().from(adsActivationGrants).where(and38(
50341
+ const grant = app.db.select().from(adsActivationGrants).where(and39(
50209
50342
  eq46(adsActivationGrants.id, body.grantId),
50210
50343
  eq46(adsActivationGrants.projectId, project.id)
50211
50344
  )).get();
@@ -50252,14 +50385,14 @@ function registerAdsActivationRoutes(app, opts) {
50252
50385
  if (request.body !== void 0) {
50253
50386
  throw validationError("Ads activation recovery does not accept a request body");
50254
50387
  }
50255
- const operation = app.db.select().from(adsOperations).where(and38(
50388
+ const operation = app.db.select().from(adsOperations).where(and39(
50256
50389
  eq46(adsOperations.projectId, project.id),
50257
50390
  eq46(adsOperations.operationKey, request.params.operationKey)
50258
50391
  )).get();
50259
50392
  if (!operation || operation.kind !== AdsOperationKinds.campaign_tree_activate) {
50260
50393
  throw notFound("Ads activation operation", request.params.operationKey);
50261
50394
  }
50262
- const grant = app.db.select().from(adsActivationGrants).where(and38(
50395
+ const grant = app.db.select().from(adsActivationGrants).where(and39(
50263
50396
  eq46(adsActivationGrants.projectId, project.id),
50264
50397
  eq46(adsActivationGrants.operationId, operation.id)
50265
50398
  )).get();
@@ -50803,7 +50936,7 @@ function semanticallyMatchesMaterializedEntity(app, input) {
50803
50936
  return AdsOperationKinds.ad_update;
50804
50937
  }
50805
50938
  })();
50806
- const rows = app.db.select().from(adsOperations).where(and39(
50939
+ const rows = app.db.select().from(adsOperations).where(and40(
50807
50940
  eq47(adsOperations.projectId, input.projectId),
50808
50941
  eq47(adsOperations.entityType, input.entityType),
50809
50942
  eq47(adsOperations.entityId, input.entityId),
@@ -50992,7 +51125,7 @@ async function executeAdsOperation(app, request, input) {
50992
51125
  }).onConflictDoNothing({
50993
51126
  target: [adsOperations.projectId, adsOperations.operationKey]
50994
51127
  }).returning({ id: adsOperations.id }).all();
50995
- const canonical = app.db.select().from(adsOperations).where(and39(
51128
+ const canonical = app.db.select().from(adsOperations).where(and40(
50996
51129
  eq47(adsOperations.projectId, input.projectId),
50997
51130
  eq47(adsOperations.operationKey, input.operationKey)
50998
51131
  )).get();
@@ -51012,7 +51145,7 @@ async function executeAdsOperation(app, request, input) {
51012
51145
  entityId: result.id,
51013
51146
  upstreamUpdatedAt: result.updatedAt ?? null,
51014
51147
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
51015
- }).where(and39(
51148
+ }).where(and40(
51016
51149
  eq47(adsOperations.id, id),
51017
51150
  eq47(adsOperations.state, AdsOperationStates.pending),
51018
51151
  isNull8(adsOperations.leaseOwner)
@@ -51033,7 +51166,7 @@ async function executeAdsOperation(app, request, input) {
51033
51166
  errorCode: null,
51034
51167
  errorMessage: null,
51035
51168
  updatedAt
51036
- }).where(and39(
51169
+ }).where(and40(
51037
51170
  eq47(adsOperations.id, id),
51038
51171
  eq47(adsOperations.state, AdsOperationStates.pending),
51039
51172
  isNull8(adsOperations.leaseOwner)
@@ -51062,7 +51195,7 @@ async function executeAdsOperation(app, request, input) {
51062
51195
  errorCode: failure.code,
51063
51196
  errorMessage: failure.message,
51064
51197
  updatedAt
51065
- }).where(and39(
51198
+ }).where(and40(
51066
51199
  eq47(adsOperations.id, id),
51067
51200
  eq47(adsOperations.state, AdsOperationStates.pending),
51068
51201
  isNull8(adsOperations.leaseOwner)
@@ -51080,7 +51213,7 @@ async function executeAdsOperation(app, request, input) {
51080
51213
  return update.changes > 0;
51081
51214
  });
51082
51215
  if (!finalized) {
51083
- const canonical2 = app.db.select().from(adsOperations).where(and39(
51216
+ const canonical2 = app.db.select().from(adsOperations).where(and40(
51084
51217
  eq47(adsOperations.id, id),
51085
51218
  eq47(adsOperations.projectId, input.projectId)
51086
51219
  )).get();
@@ -51097,7 +51230,7 @@ async function executeAdsOperation(app, request, input) {
51097
51230
  }
51098
51231
  }
51099
51232
  function readOperationByKey(app, projectId, operationKey) {
51100
- return app.db.select().from(adsOperations).where(and39(
51233
+ return app.db.select().from(adsOperations).where(and40(
51101
51234
  eq47(adsOperations.projectId, projectId),
51102
51235
  eq47(adsOperations.operationKey, operationKey)
51103
51236
  )).get();
@@ -51115,13 +51248,13 @@ function claimOperationForReconciliation(app, row, leaseOwner, now, leaseMs, pol
51115
51248
  const pendingCutoff = new Date(now.getTime() - policy.pendingMinIdleMs).toISOString();
51116
51249
  const backoffMs = policy.backoffBaseMs * 2 ** Math.max(0, row.reconcileAttempts - 1);
51117
51250
  const unknownCutoff = new Date(now.getTime() - backoffMs).toISOString();
51118
- const claimableState = row.state === AdsOperationStates.pending ? and39(
51251
+ const claimableState = row.state === AdsOperationStates.pending ? and40(
51119
51252
  eq47(adsOperations.state, AdsOperationStates.pending),
51120
51253
  lte11(adsOperations.updatedAt, pendingCutoff)
51121
- ) : row.state === AdsOperationStates.unknown ? and39(
51254
+ ) : row.state === AdsOperationStates.unknown ? and40(
51122
51255
  eq47(adsOperations.state, AdsOperationStates.unknown),
51123
51256
  enforceBackoff ? lte11(adsOperations.updatedAt, unknownCutoff) : void 0
51124
- ) : and39(
51257
+ ) : and40(
51125
51258
  eq47(adsOperations.state, AdsOperationStates.reconciling),
51126
51259
  or10(isNull8(adsOperations.leaseExpiresAt), lte11(adsOperations.leaseExpiresAt, nowIso))
51127
51260
  );
@@ -51131,7 +51264,7 @@ function claimOperationForReconciliation(app, row, leaseOwner, now, leaseMs, pol
51131
51264
  leaseExpiresAt: leaseExpiresAt2,
51132
51265
  reconcileAttempts: sql17`${adsOperations.reconcileAttempts} + 1`,
51133
51266
  updatedAt: nowIso
51134
- }).where(and39(
51267
+ }).where(and40(
51135
51268
  eq47(adsOperations.id, row.id),
51136
51269
  eq47(adsOperations.projectId, row.projectId),
51137
51270
  eq47(adsOperations.reconcileAttempts, row.reconcileAttempts),
@@ -51185,7 +51318,7 @@ function finishReconciliation(app, row, leaseOwner, context, outcome, maxAttempt
51185
51318
  leaseOwner: null,
51186
51319
  leaseExpiresAt: null,
51187
51320
  updatedAt: now
51188
- }).where(and39(
51321
+ }).where(and40(
51189
51322
  eq47(adsOperations.id, row.id),
51190
51323
  eq47(adsOperations.projectId, row.projectId),
51191
51324
  eq47(adsOperations.leaseOwner, leaseOwner),
@@ -51198,7 +51331,7 @@ function finishReconciliation(app, row, leaseOwner, context, outcome, maxAttempt
51198
51331
  }));
51199
51332
  }
51200
51333
  });
51201
- const canonical = app.db.select().from(adsOperations).where(and39(
51334
+ const canonical = app.db.select().from(adsOperations).where(and40(
51202
51335
  eq47(adsOperations.id, row.id),
51203
51336
  eq47(adsOperations.projectId, row.projectId)
51204
51337
  )).get();
@@ -51240,7 +51373,7 @@ function quarantineExhaustedReconciliation(app, row, now, policy, context) {
51240
51373
  leaseOwner: null,
51241
51374
  leaseExpiresAt: null,
51242
51375
  updatedAt: nowIso
51243
- }).where(and39(
51376
+ }).where(and40(
51244
51377
  eq47(adsOperations.id, row.id),
51245
51378
  eq47(adsOperations.projectId, row.projectId),
51246
51379
  gte12(adsOperations.reconcileAttempts, policy.maxAttempts),
@@ -51439,32 +51572,32 @@ async function sweepAdsOperationsOnce(app, opts, config) {
51439
51572
  { length: config.policy.maxAttempts },
51440
51573
  (_, reconcileAttempts) => {
51441
51574
  const backoffMs = config.policy.backoffBaseMs * 2 ** Math.max(0, reconcileAttempts - 1);
51442
- return and39(
51575
+ return and40(
51443
51576
  eq47(adsOperations.reconcileAttempts, reconcileAttempts),
51444
51577
  lte11(adsOperations.updatedAt, new Date(now.getTime() - backoffMs).toISOString())
51445
51578
  );
51446
51579
  }
51447
51580
  );
51448
51581
  const retryableStateWhere = or10(
51449
- and39(
51582
+ and40(
51450
51583
  eq47(adsOperations.state, AdsOperationStates.pending),
51451
51584
  lte11(adsOperations.updatedAt, pendingCutoff)
51452
51585
  ),
51453
- and39(
51586
+ and40(
51454
51587
  eq47(adsOperations.state, AdsOperationStates.unknown),
51455
51588
  or10(...unknownRetryWindows)
51456
51589
  ),
51457
- and39(
51590
+ and40(
51458
51591
  eq47(adsOperations.state, AdsOperationStates.reconciling),
51459
51592
  or10(isNull8(adsOperations.leaseExpiresAt), lte11(adsOperations.leaseExpiresAt, nowIso))
51460
51593
  )
51461
51594
  );
51462
- const staleReconcileWhere = and39(
51595
+ const staleReconcileWhere = and40(
51463
51596
  lt7(adsOperations.reconcileAttempts, config.policy.maxAttempts),
51464
51597
  ne7(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
51465
51598
  or10(
51466
51599
  eq47(adsOperations.reconcileStrategy, AdsReconcileStrategies.known_entity),
51467
- and39(
51600
+ and40(
51468
51601
  eq47(adsOperations.reconcileStrategy, AdsReconcileStrategies.create_fingerprint),
51469
51602
  or10(
51470
51603
  isNotNull4(adsOperations.entityId),
@@ -51477,16 +51610,16 @@ async function sweepAdsOperationsOnce(app, opts, config) {
51477
51610
  ),
51478
51611
  retryableStateWhere
51479
51612
  );
51480
- const exhaustedRows = app.db.select().from(adsOperations).where(and39(
51613
+ const exhaustedRows = app.db.select().from(adsOperations).where(and40(
51481
51614
  ne7(adsOperations.kind, AdsOperationKinds.campaign_tree_activate),
51482
51615
  gte12(adsOperations.reconcileAttempts, config.policy.maxAttempts),
51483
51616
  or10(
51484
- and39(
51617
+ and40(
51485
51618
  eq47(adsOperations.state, AdsOperationStates.pending),
51486
51619
  lte11(adsOperations.updatedAt, pendingCutoff)
51487
51620
  ),
51488
51621
  eq47(adsOperations.state, AdsOperationStates.unknown),
51489
- and39(
51622
+ and40(
51490
51623
  eq47(adsOperations.state, AdsOperationStates.reconciling),
51491
51624
  or10(isNull8(adsOperations.leaseExpiresAt), lte11(adsOperations.leaseExpiresAt, nowIso))
51492
51625
  )
@@ -51515,7 +51648,7 @@ async function sweepAdsOperationsOnce(app, opts, config) {
51515
51648
  }
51516
51649
  const eligibleProjectIds = [...credentialsByProjectId.keys()];
51517
51650
  if (eligibleProjectIds.length === 0) return;
51518
- const rows = app.db.select().from(adsOperations).where(and39(
51651
+ const rows = app.db.select().from(adsOperations).where(and40(
51519
51652
  inArray20(adsOperations.projectId, eligibleProjectIds),
51520
51653
  staleReconcileWhere
51521
51654
  )).orderBy(asc9(adsOperations.updatedAt)).limit(config.batchSize).all();
@@ -51836,12 +51969,12 @@ async function adsRoutes(app, opts) {
51836
51969
  throw validationError("Invalid ads operation list query", { issues: parsed.error.issues });
51837
51970
  }
51838
51971
  const cursor = parsed.data.cursor ? decodeAdsOperationCursor(parsed.data.cursor, project.id, parsed.data.state) : void 0;
51839
- const rows = app.db.select().from(adsOperations).where(and39(
51972
+ const rows = app.db.select().from(adsOperations).where(and40(
51840
51973
  eq47(adsOperations.projectId, project.id),
51841
51974
  inArray20(adsOperations.state, parsed.data.state),
51842
51975
  cursor ? or10(
51843
51976
  gt3(adsOperations.createdAt, cursor.createdAt),
51844
- and39(
51977
+ and40(
51845
51978
  eq47(adsOperations.createdAt, cursor.createdAt),
51846
51979
  gt3(adsOperations.id, cursor.id)
51847
51980
  )
@@ -51885,7 +52018,7 @@ async function adsRoutes(app, opts) {
51885
52018
  "/projects/:name/ads/operations/:operationKey",
51886
52019
  async (request) => {
51887
52020
  const project = resolveProject(app.db, request.params.name);
51888
- const row = app.db.select().from(adsOperations).where(and39(
52021
+ const row = app.db.select().from(adsOperations).where(and40(
51889
52022
  eq47(adsOperations.projectId, project.id),
51890
52023
  eq47(adsOperations.operationKey, request.params.operationKey)
51891
52024
  )).get();
@@ -52269,7 +52402,7 @@ async function adsRoutes(app, opts) {
52269
52402
  if (!row) {
52270
52403
  throw validationError('No ads connection for this project. Run "canonry ads connect" first.');
52271
52404
  }
52272
- const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and39(
52405
+ const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and40(
52273
52406
  eq47(runs.projectId, project.id),
52274
52407
  eq47(runs.kind, RunKinds["ads-sync"]),
52275
52408
  inArray20(runs.status, [RunStatuses.queued, RunStatuses.running])
@@ -52366,7 +52499,7 @@ async function adsRoutes(app, opts) {
52366
52499
  if (entityId) conditions.push(eq47(adsInsightsDaily.entityId, entityId));
52367
52500
  if (from) conditions.push(gte12(adsInsightsDaily.date, from));
52368
52501
  if (to) conditions.push(lte11(adsInsightsDaily.date, to));
52369
- const rows = app.db.select().from(adsInsightsDaily).where(and39(...conditions)).orderBy(asc9(adsInsightsDaily.date)).all();
52502
+ const rows = app.db.select().from(adsInsightsDaily).where(and40(...conditions)).orderBy(asc9(adsInsightsDaily.date)).all();
52370
52503
  const conn = app.db.select().from(adsConnections).where(eq47(adsConnections.projectId, project.id)).get();
52371
52504
  const accountToday = adsAccountToday(conn?.timezone, (/* @__PURE__ */ new Date()).toISOString());
52372
52505
  const dtoRows = rows.map((row) => ({
@@ -52393,11 +52526,11 @@ async function adsRoutes(app, opts) {
52393
52526
  const entityRows = [...campaignRows, ...adGroupRows, ...adRows];
52394
52527
  const hasEntityWithoutSyncRunId = entityRows.some((row) => row.syncRunId === null);
52395
52528
  const entitySyncRunIds = new Set(entityRows.flatMap((row) => row.syncRunId === null ? [] : [row.syncRunId]));
52396
- const sourceSync = entitySyncRunIds.size === 1 ? app.db.select({ id: runs.id, kind: runs.kind, status: runs.status }).from(runs).where(and39(
52529
+ const sourceSync = entitySyncRunIds.size === 1 ? app.db.select({ id: runs.id, kind: runs.kind, status: runs.status }).from(runs).where(and40(
52397
52530
  eq47(runs.id, [...entitySyncRunIds][0]),
52398
52531
  eq47(runs.projectId, project.id)
52399
52532
  )).get() : void 0;
52400
- const latestAdsSync = app.db.select({ id: runs.id, kind: runs.kind, status: runs.status }).from(runs).where(and39(
52533
+ const latestAdsSync = app.db.select({ id: runs.id, kind: runs.kind, status: runs.status }).from(runs).where(and40(
52401
52534
  eq47(runs.projectId, project.id),
52402
52535
  eq47(runs.kind, RunKinds["ads-sync"])
52403
52536
  )).orderBy(desc21(runs.createdAt)).get();
@@ -52416,7 +52549,7 @@ async function adsRoutes(app, opts) {
52416
52549
  clicks: adsInsightsDaily.clicks,
52417
52550
  spendMicros: adsInsightsDaily.spendMicros,
52418
52551
  conversions: adsInsightsDaily.conversions
52419
- }).from(adsInsightsDaily).where(and39(
52552
+ }).from(adsInsightsDaily).where(and40(
52420
52553
  eq47(adsInsightsDaily.projectId, project.id),
52421
52554
  eq47(adsInsightsDaily.level, AdsInsightLevels.campaign)
52422
52555
  )).all();
@@ -52569,11 +52702,11 @@ async function adsRoutes(app, opts) {
52569
52702
  const scopedCampaigns = campaignId === void 0 ? liveCampaigns : liveCampaigns.filter((campaign) => campaign.id === campaignId);
52570
52703
  if (scopedCampaigns.length > ADS_LIVE_MAX_CAMPAIGNS) truncated = true;
52571
52704
  const walkedCampaigns = scopedCampaigns.slice(0, ADS_LIVE_MAX_CAMPAIGNS);
52572
- const storedCampaignRows = app.db.select().from(adsCampaigns).where(and39(
52705
+ const storedCampaignRows = app.db.select().from(adsCampaigns).where(and40(
52573
52706
  eq47(adsCampaigns.projectId, project.id),
52574
52707
  campaignId === void 0 ? void 0 : eq47(adsCampaigns.id, campaignId)
52575
52708
  )).all();
52576
- const storedAdGroupRows = app.db.select().from(adsAdGroups).where(and39(
52709
+ const storedAdGroupRows = app.db.select().from(adsAdGroups).where(and40(
52577
52710
  eq47(adsAdGroups.projectId, project.id),
52578
52711
  campaignId === void 0 ? void 0 : eq47(adsAdGroups.campaignId, campaignId)
52579
52712
  )).all();
@@ -52755,7 +52888,7 @@ async function adsRoutes(app, opts) {
52755
52888
  const campaignCount = app.db.select().from(adsCampaigns).where(eq47(adsCampaigns.projectId, project.id)).all().length;
52756
52889
  const adGroupCount = app.db.select().from(adsAdGroups).where(eq47(adsAdGroups.projectId, project.id)).all().length;
52757
52890
  const adCount = app.db.select().from(adsAds).where(eq47(adsAds.projectId, project.id)).all().length;
52758
- const campaignInsights = app.db.select().from(adsInsightsDaily).where(and39(
52891
+ const campaignInsights = app.db.select().from(adsInsightsDaily).where(and40(
52759
52892
  eq47(adsInsightsDaily.projectId, project.id),
52760
52893
  eq47(adsInsightsDaily.level, "campaign")
52761
52894
  )).all();
@@ -52804,7 +52937,7 @@ async function adsRoutes(app, opts) {
52804
52937
 
52805
52938
  // ../api-routes/src/bing.ts
52806
52939
  import crypto38 from "crypto";
52807
- import { eq as eq48, and as and40, desc as desc22 } from "drizzle-orm";
52940
+ import { eq as eq48, and as and41, desc as desc22 } from "drizzle-orm";
52808
52941
 
52809
52942
  // ../integration-bing/src/bing-client.ts
52810
52943
  import crypto37 from "crypto";
@@ -53324,7 +53457,7 @@ async function bingRoutes(app, opts) {
53324
53457
  requireConnectionStore();
53325
53458
  const project = resolveProject(app.db, request.params.name);
53326
53459
  const { url, limit } = request.query;
53327
- const whereClause = url ? and40(eq48(bingUrlInspections.projectId, project.id), eq48(bingUrlInspections.url, url)) : eq48(bingUrlInspections.projectId, project.id);
53460
+ const whereClause = url ? and41(eq48(bingUrlInspections.projectId, project.id), eq48(bingUrlInspections.url, url)) : eq48(bingUrlInspections.projectId, project.id);
53328
53461
  const filtered = app.db.select().from(bingUrlInspections).where(whereClause).orderBy(desc22(bingUrlInspections.inspectedAt)).limit(Math.max(1, Math.min(parseInt(limit ?? "100", 10) || 100, 1e3))).all();
53329
53462
  return filtered.map((r) => ({
53330
53463
  id: r.id,
@@ -53556,7 +53689,7 @@ async function bingRoutes(app, opts) {
53556
53689
  import fs from "fs";
53557
53690
  import path from "path";
53558
53691
  import os from "os";
53559
- import { eq as eq49, and as and41 } from "drizzle-orm";
53692
+ import { eq as eq49, and as and42 } from "drizzle-orm";
53560
53693
  function getScreenshotDir() {
53561
53694
  return path.join(os.homedir(), ".canonry", "screenshots");
53562
53695
  }
@@ -53631,7 +53764,7 @@ async function cdpRoutes(app, opts) {
53631
53764
  async (request, reply) => {
53632
53765
  const project = resolveProject(app.db, request.params.name);
53633
53766
  const { runId } = request.params;
53634
- const run = app.db.select().from(runs).where(and41(eq49(runs.id, runId), eq49(runs.projectId, project.id))).get();
53767
+ const run = app.db.select().from(runs).where(and42(eq49(runs.id, runId), eq49(runs.projectId, project.id))).get();
53635
53768
  if (!run) {
53636
53769
  const err = notFound("Run", runId);
53637
53770
  return reply.code(err.statusCode).send(err.toJSON());
@@ -53728,7 +53861,7 @@ async function cdpRoutes(app, opts) {
53728
53861
 
53729
53862
  // ../api-routes/src/ga.ts
53730
53863
  import crypto39 from "crypto";
53731
- import { eq as eq50, desc as desc23, and as and42, sql as sql18 } from "drizzle-orm";
53864
+ import { eq as eq50, desc as desc23, and as and43, sql as sql18 } from "drizzle-orm";
53732
53865
 
53733
53866
  // ../api-routes/src/ga-session-history.ts
53734
53867
  function buildSessionHistory(landingPageDays, dailyTotals) {
@@ -53820,7 +53953,7 @@ function keepWinningDimensionRows(rows, groupKey) {
53820
53953
  function persistAcquisitionMeasurement(db, input) {
53821
53954
  const { startDate, endDate } = input.report;
53822
53955
  db.transaction((tx) => {
53823
- tx.delete(gaAcquisitionDaily).where(and42(
53956
+ tx.delete(gaAcquisitionDaily).where(and43(
53824
53957
  eq50(gaAcquisitionDaily.projectId, input.projectId),
53825
53958
  sql18`${gaAcquisitionDaily.date} >= ${startDate}`,
53826
53959
  sql18`${gaAcquisitionDaily.date} <= ${endDate}`
@@ -53862,7 +53995,7 @@ function persistAcquisitionMeasurement(db, input) {
53862
53995
  function persistLeadMeasurement(db, input) {
53863
53996
  const { startDate, endDate } = input.report;
53864
53997
  db.transaction((tx) => {
53865
- tx.delete(gaLeadEventsDaily).where(and42(
53998
+ tx.delete(gaLeadEventsDaily).where(and43(
53866
53999
  eq50(gaLeadEventsDaily.projectId, input.projectId),
53867
54000
  sql18`${gaLeadEventsDaily.date} >= ${startDate}`,
53868
54001
  sql18`${gaLeadEventsDaily.date} <= ${endDate}`
@@ -54238,7 +54371,7 @@ async function ga4Routes(app, opts) {
54238
54371
  app.db.transaction((tx) => {
54239
54372
  if (syncTraffic) {
54240
54373
  tx.delete(gaTrafficSnapshots).where(
54241
- and42(
54374
+ and43(
54242
54375
  eq50(gaTrafficSnapshots.projectId, project.id),
54243
54376
  sql18`${gaTrafficSnapshots.date} >= ${summary2.periodStart}`,
54244
54377
  sql18`${gaTrafficSnapshots.date} <= ${summary2.periodEnd}`
@@ -54261,7 +54394,7 @@ async function ga4Routes(app, opts) {
54261
54394
  }
54262
54395
  }
54263
54396
  tx.delete(gaDailyTotals).where(
54264
- and42(
54397
+ and43(
54265
54398
  eq50(gaDailyTotals.projectId, project.id),
54266
54399
  sql18`${gaDailyTotals.date} >= ${summary2.periodStart}`,
54267
54400
  sql18`${gaDailyTotals.date} <= ${summary2.periodEnd}`
@@ -54287,7 +54420,7 @@ async function ga4Routes(app, opts) {
54287
54420
  }
54288
54421
  if (syncAi) {
54289
54422
  tx.delete(gaAiReferrals).where(
54290
- and42(
54423
+ and43(
54291
54424
  eq50(gaAiReferrals.projectId, project.id),
54292
54425
  sql18`${gaAiReferrals.date} >= ${summary2.periodStart}`,
54293
54426
  sql18`${gaAiReferrals.date} <= ${summary2.periodEnd}`
@@ -54319,7 +54452,7 @@ async function ga4Routes(app, opts) {
54319
54452
  }
54320
54453
  if (syncSocial) {
54321
54454
  tx.delete(gaSocialReferrals).where(
54322
- and42(
54455
+ and43(
54323
54456
  eq50(gaSocialReferrals.projectId, project.id),
54324
54457
  sql18`${gaSocialReferrals.date} >= ${summary2.periodStart}`,
54325
54458
  sql18`${gaSocialReferrals.date} <= ${summary2.periodEnd}`
@@ -54491,7 +54624,7 @@ async function ga4Routes(app, opts) {
54491
54624
  totalDirectSessions: gaTrafficWindowSummaries.totalDirectSessions,
54492
54625
  totalUsers: gaTrafficWindowSummaries.totalUsers
54493
54626
  }).from(gaTrafficWindowSummaries).where(
54494
- and42(
54627
+ and43(
54495
54628
  eq50(gaTrafficWindowSummaries.projectId, project.id),
54496
54629
  eq50(gaTrafficWindowSummaries.windowKey, range.window)
54497
54630
  )
@@ -54523,7 +54656,7 @@ async function ga4Routes(app, opts) {
54523
54656
  totalSessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
54524
54657
  totalOrganicSessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
54525
54658
  totalUsers: sql18`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
54526
- }).from(gaTrafficSnapshots).where(and42(...snapshotConditions)).get();
54659
+ }).from(gaTrafficSnapshots).where(and43(...snapshotConditions)).get();
54527
54660
  return {
54528
54661
  totalSessions: summed?.totalSessions ?? 0,
54529
54662
  totalOrganicSessions: summed?.totalOrganicSessions ?? 0,
@@ -54536,21 +54669,21 @@ async function ga4Routes(app, opts) {
54536
54669
  const summaryRow = denominatorRow ?? snapshotTotalsRow;
54537
54670
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
54538
54671
  totalDirectSessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
54539
- }).from(gaTrafficSnapshots).where(and42(...snapshotConditions)).get();
54672
+ }).from(gaTrafficSnapshots).where(and43(...snapshotConditions)).get();
54540
54673
  const rows = app.db.select({
54541
54674
  landingPage: sql18`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
54542
54675
  sessions: sql18`SUM(${gaTrafficSnapshots.sessions})`,
54543
54676
  organicSessions: sql18`SUM(${gaTrafficSnapshots.organicSessions})`,
54544
54677
  directSessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
54545
54678
  users: sql18`SUM(${gaTrafficSnapshots.users})`
54546
- }).from(gaTrafficSnapshots).where(and42(...snapshotConditions)).groupBy(sql18`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql18`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
54679
+ }).from(gaTrafficSnapshots).where(and43(...snapshotConditions)).groupBy(sql18`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql18`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
54547
54680
  const aiReferralRows = app.db.select({
54548
54681
  source: gaAiReferrals.source,
54549
54682
  medium: gaAiReferrals.medium,
54550
54683
  trafficClass: gaAiReferrals.trafficClass,
54551
54684
  sourceDimension: gaAiReferrals.sourceDimension,
54552
54685
  sessions: sql18`SUM(${gaAiReferrals.sessions})`
54553
- }).from(gaAiReferrals).where(and42(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.trafficClass, gaAiReferrals.sourceDimension).all();
54686
+ }).from(gaAiReferrals).where(and43(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.trafficClass, gaAiReferrals.sourceDimension).all();
54554
54687
  const aiReferralLandingPageRows = app.db.select({
54555
54688
  source: gaAiReferrals.source,
54556
54689
  medium: gaAiReferrals.medium,
@@ -54558,7 +54691,7 @@ async function ga4Routes(app, opts) {
54558
54691
  sourceDimension: gaAiReferrals.sourceDimension,
54559
54692
  landingPage: sql18`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
54560
54693
  sessions: sql18`SUM(${gaAiReferrals.sessions})`
54561
- }).from(gaAiReferrals).where(and42(...aiConditions)).groupBy(
54694
+ }).from(gaAiReferrals).where(and43(...aiConditions)).groupBy(
54562
54695
  gaAiReferrals.source,
54563
54696
  gaAiReferrals.medium,
54564
54697
  gaAiReferrals.trafficClass,
@@ -54573,7 +54706,7 @@ async function ga4Routes(app, opts) {
54573
54706
  sourceDimension: gaAiReferrals.sourceDimension,
54574
54707
  channelGroup: gaAiReferrals.channelGroup,
54575
54708
  sessions: gaAiReferrals.sessions
54576
- }).from(gaAiReferrals).where(and42(...aiConditions)).all();
54709
+ }).from(gaAiReferrals).where(and43(...aiConditions)).all();
54577
54710
  const aiReferrals = keepWinningDimensionRows(aiReferralRows, (r) => r.source);
54578
54711
  const aiReferralLandingPages = keepWinningDimensionRows(
54579
54712
  aiReferralLandingPageRows,
@@ -54590,10 +54723,10 @@ async function ga4Routes(app, opts) {
54590
54723
  // dates counts a returning visitor once per day they returned. Same
54591
54724
  // conclusion the AI-referral surfaces reached in 4.135.0.
54592
54725
  sessions: sql18`SUM(${gaSocialReferrals.sessions})`
54593
- }).from(gaSocialReferrals).where(and42(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql18`SUM(${gaSocialReferrals.sessions}) DESC`).all();
54726
+ }).from(gaSocialReferrals).where(and43(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql18`SUM(${gaSocialReferrals.sessions}) DESC`).all();
54594
54727
  const socialTotals = app.db.select({
54595
54728
  sessions: sql18`SUM(${gaSocialReferrals.sessions})`
54596
- }).from(gaSocialReferrals).where(and42(...socialConditions)).get();
54729
+ }).from(gaSocialReferrals).where(and43(...socialConditions)).get();
54597
54730
  const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq50(gaTrafficSummaries.projectId, project.id)).orderBy(desc23(gaTrafficSummaries.syncedAt)).limit(1).get();
54598
54731
  const total = summaryRow?.totalSessions ?? 0;
54599
54732
  const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
@@ -54714,7 +54847,7 @@ async function ga4Routes(app, opts) {
54714
54847
  // added up. ga4AiReferralDtoSchema.users was deprecated for this in
54715
54848
  // 4.135.0; the history endpoint kept emitting one.
54716
54849
  sessions: sql18`SUM(${gaAiReferrals.sessions})`
54717
- }).from(gaAiReferrals).where(and42(...conditions)).groupBy(
54850
+ }).from(gaAiReferrals).where(and43(...conditions)).groupBy(
54718
54851
  gaAiReferrals.date,
54719
54852
  gaAiReferrals.source,
54720
54853
  gaAiReferrals.medium,
@@ -54737,7 +54870,7 @@ async function ga4Routes(app, opts) {
54737
54870
  sourceDimension: gaAiReferrals.sourceDimension,
54738
54871
  channelGroup: gaAiReferrals.channelGroup,
54739
54872
  sessions: gaAiReferrals.sessions
54740
- }).from(gaAiReferrals).where(and42(...conditions)).all();
54873
+ }).from(gaAiReferrals).where(and43(...conditions)).all();
54741
54874
  return buildAiReferralDailySeries(rows);
54742
54875
  });
54743
54876
  app.get("/projects/:name/ga/social-referral-history", async (request, _reply) => {
@@ -54752,7 +54885,7 @@ async function ga4Routes(app, opts) {
54752
54885
  channelGroup: gaSocialReferrals.channelGroup,
54753
54886
  sessions: gaSocialReferrals.sessions,
54754
54887
  users: gaSocialReferrals.users
54755
- }).from(gaSocialReferrals).where(and42(...conditions)).orderBy(gaSocialReferrals.date).all();
54888
+ }).from(gaSocialReferrals).where(and43(...conditions)).orderBy(gaSocialReferrals.date).all();
54756
54889
  return rows;
54757
54890
  });
54758
54891
  app.get("/projects/:name/ga/social-referral-trend", async (request, _reply) => {
@@ -54765,7 +54898,7 @@ async function ga4Routes(app, opts) {
54765
54898
  d.setDate(d.getDate() - n);
54766
54899
  return fmt(d);
54767
54900
  };
54768
- const sumSocial = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and42(
54901
+ const sumSocial = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and43(
54769
54902
  eq50(gaSocialReferrals.projectId, project.id),
54770
54903
  sql18`${gaSocialReferrals.date} >= ${from}`,
54771
54904
  sql18`${gaSocialReferrals.date} < ${to}`
@@ -54777,7 +54910,7 @@ async function ga4Routes(app, opts) {
54777
54910
  const sourceCurrent = app.db.select({
54778
54911
  source: gaSocialReferrals.source,
54779
54912
  sessions: sql18`SUM(${gaSocialReferrals.sessions})`
54780
- }).from(gaSocialReferrals).where(and42(
54913
+ }).from(gaSocialReferrals).where(and43(
54781
54914
  eq50(gaSocialReferrals.projectId, project.id),
54782
54915
  sql18`${gaSocialReferrals.date} >= ${daysAgo(7)}`,
54783
54916
  sql18`${gaSocialReferrals.date} < ${fmt(today)}`
@@ -54785,7 +54918,7 @@ async function ga4Routes(app, opts) {
54785
54918
  const sourcePrev = app.db.select({
54786
54919
  source: gaSocialReferrals.source,
54787
54920
  sessions: sql18`SUM(${gaSocialReferrals.sessions})`
54788
- }).from(gaSocialReferrals).where(and42(
54921
+ }).from(gaSocialReferrals).where(and43(
54789
54922
  eq50(gaSocialReferrals.projectId, project.id),
54790
54923
  sql18`${gaSocialReferrals.date} >= ${daysAgo(14)}`,
54791
54924
  sql18`${gaSocialReferrals.date} < ${daysAgo(7)}`
@@ -54826,16 +54959,16 @@ async function ga4Routes(app, opts) {
54826
54959
  d.setDate(d.getDate() - n);
54827
54960
  return fmt(d);
54828
54961
  };
54829
- const sumTotal = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and42(eq50(gaTrafficSnapshots.projectId, project.id), sql18`${gaTrafficSnapshots.date} >= ${from}`, sql18`${gaTrafficSnapshots.date} < ${to}`)).get();
54830
- const sumOrganic = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and42(eq50(gaTrafficSnapshots.projectId, project.id), sql18`${gaTrafficSnapshots.date} >= ${from}`, sql18`${gaTrafficSnapshots.date} < ${to}`)).get();
54831
- const sumDirect = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and42(eq50(gaTrafficSnapshots.projectId, project.id), sql18`${gaTrafficSnapshots.date} >= ${from}`, sql18`${gaTrafficSnapshots.date} < ${to}`)).get();
54832
- const sumAi = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and42(
54962
+ const sumTotal = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and43(eq50(gaTrafficSnapshots.projectId, project.id), sql18`${gaTrafficSnapshots.date} >= ${from}`, sql18`${gaTrafficSnapshots.date} < ${to}`)).get();
54963
+ const sumOrganic = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and43(eq50(gaTrafficSnapshots.projectId, project.id), sql18`${gaTrafficSnapshots.date} >= ${from}`, sql18`${gaTrafficSnapshots.date} < ${to}`)).get();
54964
+ const sumDirect = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and43(eq50(gaTrafficSnapshots.projectId, project.id), sql18`${gaTrafficSnapshots.date} >= ${from}`, sql18`${gaTrafficSnapshots.date} < ${to}`)).get();
54965
+ const sumAi = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and43(
54833
54966
  eq50(gaAiReferrals.projectId, project.id),
54834
54967
  sql18`${gaAiReferrals.date} >= ${from}`,
54835
54968
  sql18`${gaAiReferrals.date} < ${to}`,
54836
54969
  eq50(gaAiReferrals.sourceDimension, "session")
54837
54970
  )).get();
54838
- const sumSocial = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and42(eq50(gaSocialReferrals.projectId, project.id), sql18`${gaSocialReferrals.date} >= ${from}`, sql18`${gaSocialReferrals.date} < ${to}`)).get();
54971
+ const sumSocial = (from, to) => app.db.select({ sessions: sql18`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and43(eq50(gaSocialReferrals.projectId, project.id), sql18`${gaSocialReferrals.date} >= ${from}`, sql18`${gaSocialReferrals.date} < ${to}`)).get();
54839
54972
  const todayStr = fmt(today);
54840
54973
  const buildTrend = (sum) => {
54841
54974
  const c7 = sum(daysAgo(7), todayStr)?.sessions ?? 0;
@@ -54844,13 +54977,13 @@ async function ga4Routes(app, opts) {
54844
54977
  const p30 = sum(daysAgo(60), daysAgo(30))?.sessions ?? 0;
54845
54978
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: deltaPercent(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: deltaPercent(c30, p30) };
54846
54979
  };
54847
- const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql18`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and42(
54980
+ const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql18`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and43(
54848
54981
  eq50(gaAiReferrals.projectId, project.id),
54849
54982
  sql18`${gaAiReferrals.date} >= ${daysAgo(7)}`,
54850
54983
  sql18`${gaAiReferrals.date} < ${todayStr}`,
54851
54984
  eq50(gaAiReferrals.sourceDimension, "session")
54852
54985
  )).groupBy(gaAiReferrals.source).all();
54853
- const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql18`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and42(
54986
+ const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql18`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and43(
54854
54987
  eq50(gaAiReferrals.projectId, project.id),
54855
54988
  sql18`${gaAiReferrals.date} >= ${daysAgo(14)}`,
54856
54989
  sql18`${gaAiReferrals.date} < ${daysAgo(7)}`,
@@ -54870,8 +55003,8 @@ async function ga4Routes(app, opts) {
54870
55003
  }
54871
55004
  return mover;
54872
55005
  };
54873
- const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql18`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and42(eq50(gaSocialReferrals.projectId, project.id), sql18`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql18`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
54874
- const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql18`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and42(eq50(gaSocialReferrals.projectId, project.id), sql18`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql18`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
55006
+ const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql18`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and43(eq50(gaSocialReferrals.projectId, project.id), sql18`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql18`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
55007
+ const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql18`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and43(eq50(gaSocialReferrals.projectId, project.id), sql18`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql18`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
54875
55008
  return {
54876
55009
  total: buildTrend(sumTotal),
54877
55010
  organic: buildTrend(sumOrganic),
@@ -54892,9 +55025,9 @@ async function ga4Routes(app, opts) {
54892
55025
  sessions: sql18`SUM(${gaTrafficSnapshots.sessions})`,
54893
55026
  organicSessions: sql18`SUM(${gaTrafficSnapshots.organicSessions})`,
54894
55027
  users: sql18`SUM(${gaTrafficSnapshots.users})`
54895
- }).from(gaTrafficSnapshots).where(and42(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
55028
+ }).from(gaTrafficSnapshots).where(and43(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
54896
55029
  const totalConditions = [eq50(gaDailyTotals.projectId, project.id), ...dateRangeConditions(gaDailyTotals.date, range)];
54897
- const totals = app.db.select({ date: gaDailyTotals.date, users: gaDailyTotals.users }).from(gaDailyTotals).where(and42(...totalConditions)).all();
55030
+ const totals = app.db.select({ date: gaDailyTotals.date, users: gaDailyTotals.users }).from(gaDailyTotals).where(and43(...totalConditions)).all();
54898
55031
  return buildSessionHistory(
54899
55032
  rows.map((r) => ({
54900
55033
  date: r.date,
@@ -56568,7 +56701,7 @@ async function wordpressRoutes(app, opts) {
56568
56701
 
56569
56702
  // ../api-routes/src/backlinks.ts
56570
56703
  import crypto41 from "crypto";
56571
- import { and as and44, asc as asc10, desc as desc24, eq as eq51, sql as sql19 } from "drizzle-orm";
56704
+ import { and as and45, asc as asc10, desc as desc24, eq as eq51, sql as sql19 } from "drizzle-orm";
56572
56705
 
56573
56706
  // ../integration-commoncrawl/src/constants.ts
56574
56707
  import os2 from "os";
@@ -56971,7 +57104,7 @@ function pruneCachedRelease(release, opts = {}) {
56971
57104
  }
56972
57105
 
56973
57106
  // ../api-routes/src/backlinks-filter.ts
56974
- import { and as and43, ne as ne8, notLike } from "drizzle-orm";
57107
+ import { and as and44, ne as ne8, notLike } from "drizzle-orm";
56975
57108
  var BACKLINK_FILTER_PATTERNS = [
56976
57109
  "*.google.com",
56977
57110
  "*.googleusercontent.com",
@@ -56994,7 +57127,7 @@ function backlinkCrawlerExclusionClause() {
56994
57127
  conditions.push(ne8(backlinkDomains.linkingDomain, pattern));
56995
57128
  }
56996
57129
  }
56997
- const combined = and43(...conditions);
57130
+ const combined = and44(...conditions);
56998
57131
  if (!combined) throw new Error("BACKLINK_FILTER_PATTERNS is unexpectedly empty");
56999
57132
  return combined;
57000
57133
  }
@@ -57069,7 +57202,7 @@ function latestSummaryForProject(db, projectId, source, release) {
57069
57202
  eq51(backlinkSummaries.source, source)
57070
57203
  ];
57071
57204
  if (release) conditions.push(eq51(backlinkSummaries.release, release));
57072
- return db.select().from(backlinkSummaries).where(and44(...conditions)).orderBy(desc24(backlinkSummaries.queriedAt)).limit(1).get();
57205
+ return db.select().from(backlinkSummaries).where(and45(...conditions)).orderBy(desc24(backlinkSummaries.queriedAt)).limit(1).get();
57073
57206
  }
57074
57207
  function parseExcludeCrawlers(value) {
57075
57208
  if (!value) return false;
@@ -57077,12 +57210,12 @@ function parseExcludeCrawlers(value) {
57077
57210
  return lower === "1" || lower === "true" || lower === "yes";
57078
57211
  }
57079
57212
  function computeFilteredSummary(db, base) {
57080
- const baseDomainCondition = and44(
57213
+ const baseDomainCondition = and45(
57081
57214
  eq51(backlinkDomains.projectId, base.projectId),
57082
57215
  eq51(backlinkDomains.source, base.source),
57083
57216
  eq51(backlinkDomains.release, base.release)
57084
57217
  );
57085
- const filteredCondition = and44(baseDomainCondition, backlinkCrawlerExclusionClause());
57218
+ const filteredCondition = and45(baseDomainCondition, backlinkCrawlerExclusionClause());
57086
57219
  const unfilteredAgg = db.select({
57087
57220
  count: sql19`count(*)`,
57088
57221
  total: sql19`coalesce(sum(${backlinkDomains.numHosts}), 0)`
@@ -57112,10 +57245,10 @@ function computeFilteredSummary(db, base) {
57112
57245
  };
57113
57246
  }
57114
57247
  function buildSourceAvailability(db, projectId, source, connected, excludeCrawlers) {
57115
- const summary2 = db.select().from(backlinkSummaries).where(and44(eq51(backlinkSummaries.projectId, projectId), eq51(backlinkSummaries.source, source))).orderBy(desc24(backlinkSummaries.queriedAt)).limit(1).get();
57248
+ const summary2 = db.select().from(backlinkSummaries).where(and45(eq51(backlinkSummaries.projectId, projectId), eq51(backlinkSummaries.source, source))).orderBy(desc24(backlinkSummaries.queriedAt)).limit(1).get();
57116
57249
  let totalLinkingDomains = summary2?.totalLinkingDomains ?? 0;
57117
57250
  if (summary2 && excludeCrawlers) {
57118
- const filtered = db.select({ count: sql19`count(*)` }).from(backlinkDomains).where(and44(
57251
+ const filtered = db.select({ count: sql19`count(*)` }).from(backlinkDomains).where(and45(
57119
57252
  eq51(backlinkDomains.projectId, projectId),
57120
57253
  eq51(backlinkDomains.source, source),
57121
57254
  eq51(backlinkDomains.release, summary2.release),
@@ -57297,12 +57430,12 @@ async function backlinksRoutes(app, opts) {
57297
57430
  const limit = Math.min(Math.max(parseInt(request.query.limit ?? "50", 10) || 50, 1), 500);
57298
57431
  const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
57299
57432
  const excludeCrawlers = parseExcludeCrawlers(request.query.excludeCrawlers);
57300
- const baseDomainCondition = and44(
57433
+ const baseDomainCondition = and45(
57301
57434
  eq51(backlinkDomains.projectId, project.id),
57302
57435
  eq51(backlinkDomains.source, source),
57303
57436
  eq51(backlinkDomains.release, targetRelease)
57304
57437
  );
57305
- const domainCondition = excludeCrawlers ? and44(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
57438
+ const domainCondition = excludeCrawlers ? and45(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
57306
57439
  const totalRow = app.db.select({ count: sql19`count(*)` }).from(backlinkDomains).where(domainCondition).get();
57307
57440
  const rows = app.db.select({
57308
57441
  linkingDomain: backlinkDomains.linkingDomain,
@@ -57326,7 +57459,7 @@ async function backlinksRoutes(app, opts) {
57326
57459
  async (request, reply) => {
57327
57460
  const project = resolveProject(app.db, request.params.name);
57328
57461
  const source = parseSourceParam(request.query.source);
57329
- const rows = app.db.select().from(backlinkSummaries).where(and44(eq51(backlinkSummaries.projectId, project.id), eq51(backlinkSummaries.source, source))).orderBy(asc10(backlinkSummaries.queriedAt)).all();
57462
+ const rows = app.db.select().from(backlinkSummaries).where(and45(eq51(backlinkSummaries.projectId, project.id), eq51(backlinkSummaries.source, source))).orderBy(asc10(backlinkSummaries.queriedAt)).all();
57330
57463
  const response = rows.map((r) => ({
57331
57464
  release: r.release,
57332
57465
  totalLinkingDomains: r.totalLinkingDomains,
@@ -57354,7 +57487,7 @@ import crypto44 from "crypto";
57354
57487
  import { isIP } from "net";
57355
57488
  import { isDeepStrictEqual } from "util";
57356
57489
  import { Agent as UndiciAgent } from "undici";
57357
- import { and as and48, desc as desc25, eq as eq55, gte as gte14, inArray as inArray21, lte as lte14, sql as sql22 } from "drizzle-orm";
57490
+ import { and as and49, desc as desc25, eq as eq55, gte as gte14, inArray as inArray21, lte as lte14, sql as sql22 } from "drizzle-orm";
57358
57491
 
57359
57492
  // ../api-routes/src/cloudflare-worker-version.ts
57360
57493
  var CURRENT_CLOUDFLARE_WORKER_VERSION = "1.0.1";
@@ -74025,10 +74158,10 @@ async function ackCloudflareQueueMessages(client, options) {
74025
74158
 
74026
74159
  // ../api-routes/src/traffic-event-ingest.ts
74027
74160
  import crypto43 from "crypto";
74028
- import { and as and46, eq as eq53, gte as gte13, lt as lt9, lte as lte12, sql as sql21 } from "drizzle-orm";
74161
+ import { and as and47, eq as eq53, gte as gte13, lt as lt9, lte as lte12, sql as sql21 } from "drizzle-orm";
74029
74162
 
74030
74163
  // ../api-routes/src/raw-event-sample-retention.ts
74031
- import { and as and45, eq as eq52, lt as lt8, sql as sql20 } from "drizzle-orm";
74164
+ import { and as and46, eq as eq52, lt as lt8, sql as sql20 } from "drizzle-orm";
74032
74165
  var RAW_EVENT_SAMPLE_RETENTION_MS = 30 * 24 * 60 * 6e4;
74033
74166
  var RAW_EVENT_SAMPLE_RETENTION_SWEEP_INTERVAL_MS = 24 * 60 * 6e4;
74034
74167
  var CANONICAL_UTC_TIMESTAMP_GLOB = "[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]T[0-9][0-9]:[0-9][0-9]:[0-9][0-9].[0-9][0-9][0-9]Z";
@@ -74039,13 +74172,13 @@ function rawEventSampleRetentionCutoff(referenceAt) {
74039
74172
  }
74040
74173
  function deleteExpiredRawEventSamples(db, cutoff, sourceId) {
74041
74174
  const sourceScope = sourceId === void 0 ? void 0 : eq52(rawEventSamples.sourceId, sourceId);
74042
- db.delete(rawEventSamples).where(and45(sourceScope, sql20`julianday(${rawEventSamples.ts}) IS NULL`)).run();
74043
- db.delete(rawEventSamples).where(and45(
74175
+ db.delete(rawEventSamples).where(and46(sourceScope, sql20`julianday(${rawEventSamples.ts}) IS NULL`)).run();
74176
+ db.delete(rawEventSamples).where(and46(
74044
74177
  sourceScope,
74045
74178
  sql20`${rawEventSamples.ts} GLOB ${CANONICAL_UTC_TIMESTAMP_GLOB}`,
74046
74179
  lt8(rawEventSamples.ts, cutoff)
74047
74180
  )).run();
74048
- db.delete(rawEventSamples).where(and45(
74181
+ db.delete(rawEventSamples).where(and46(
74049
74182
  sourceScope,
74050
74183
  sql20`${rawEventSamples.ts} NOT GLOB ${CANONICAL_UTC_TIMESTAMP_GLOB}`,
74051
74184
  sql20`julianday(${rawEventSamples.ts}) < julianday(${cutoff})`
@@ -74102,7 +74235,7 @@ function writeTrafficEventBatch(opts) {
74102
74235
  opts.sourceId,
74103
74236
  opts.receivedAt
74104
74237
  );
74105
- tx.delete(trafficEventReceipts).where(and46(
74238
+ tx.delete(trafficEventReceipts).where(and47(
74106
74239
  eq53(trafficEventReceipts.sourceId, opts.sourceId),
74107
74240
  lte12(trafficEventReceipts.expiresAt, opts.receivedAt)
74108
74241
  )).run();
@@ -74302,7 +74435,7 @@ function writeTrafficEventBatch(opts) {
74302
74435
  if (!hour || hourlySampleLimit === 0) continue;
74303
74436
  let remaining = remainingSamplesByHour.get(hour.start);
74304
74437
  if (remaining === void 0) {
74305
- const existing = tx.select({ total: sql21`COUNT(*)` }).from(rawEventSamples).where(and46(
74438
+ const existing = tx.select({ total: sql21`COUNT(*)` }).from(rawEventSamples).where(and47(
74306
74439
  eq53(rawEventSamples.sourceId, opts.sourceId),
74307
74440
  gte13(rawEventSamples.ts, hour.start),
74308
74441
  lt9(rawEventSamples.ts, hour.end)
@@ -74362,7 +74495,7 @@ function writeTrafficEventBatch(opts) {
74362
74495
  }
74363
74496
 
74364
74497
  // ../api-routes/src/traffic-sync-lease.ts
74365
- import { and as and47, eq as eq54, isNull as isNull9, lte as lte13, or as or11 } from "drizzle-orm";
74498
+ import { and as and48, eq as eq54, isNull as isNull9, lte as lte13, or as or11 } from "drizzle-orm";
74366
74499
  function leaseExpiresAt(now, ttlMs) {
74367
74500
  const nowMs = Date.parse(now);
74368
74501
  if (!Number.isFinite(nowMs)) throw new RangeError("Traffic sync lease now must be an ISO instant");
@@ -74375,7 +74508,7 @@ function tryClaimTrafficSyncLease(input) {
74375
74508
  const changed = tx.update(trafficSources).set({
74376
74509
  syncLeaseOwner: input.owner,
74377
74510
  syncLeaseExpiresAt: expiresAt
74378
- }).where(and47(
74511
+ }).where(and48(
74379
74512
  eq54(trafficSources.id, input.sourceId),
74380
74513
  or11(
74381
74514
  isNull9(trafficSources.syncLeaseOwner),
@@ -74391,7 +74524,7 @@ function releaseTrafficSyncLease(input) {
74391
74524
  const changed = tx.update(trafficSources).set({
74392
74525
  syncLeaseOwner: null,
74393
74526
  syncLeaseExpiresAt: null
74394
- }).where(and47(
74527
+ }).where(and48(
74395
74528
  eq54(trafficSources.id, input.sourceId),
74396
74529
  eq54(trafficSources.syncLeaseOwner, input.owner)
74397
74530
  )).run();
@@ -74741,7 +74874,7 @@ function wordpressBaseUrlIdentity(value) {
74741
74874
  }
74742
74875
  }
74743
74876
  function bindTrafficSyncSchedule(tx, projectId, sourceId, now, createIfMissing = true) {
74744
- const schedule = tx.select().from(schedules).where(and48(
74877
+ const schedule = tx.select().from(schedules).where(and49(
74745
74878
  eq55(schedules.projectId, projectId),
74746
74879
  eq55(schedules.kind, SchedulableRunKinds["traffic-sync"])
74747
74880
  )).get();
@@ -74873,28 +75006,28 @@ async function runBackfillTask(options) {
74873
75006
  finishedAt
74874
75007
  );
74875
75008
  tx.delete(crawlerEventsHourly).where(
74876
- and48(
75009
+ and49(
74877
75010
  eq55(crawlerEventsHourly.sourceId, sourceRow2.id),
74878
75011
  gte14(crawlerEventsHourly.tsHour, windowStartIso),
74879
75012
  lte14(crawlerEventsHourly.tsHour, windowEndIso)
74880
75013
  )
74881
75014
  ).run();
74882
75015
  tx.delete(aiUserFetchEventsHourly).where(
74883
- and48(
75016
+ and49(
74884
75017
  eq55(aiUserFetchEventsHourly.sourceId, sourceRow2.id),
74885
75018
  gte14(aiUserFetchEventsHourly.tsHour, windowStartIso),
74886
75019
  lte14(aiUserFetchEventsHourly.tsHour, windowEndIso)
74887
75020
  )
74888
75021
  ).run();
74889
75022
  tx.delete(aiReferralEventsHourly).where(
74890
- and48(
75023
+ and49(
74891
75024
  eq55(aiReferralEventsHourly.sourceId, sourceRow2.id),
74892
75025
  gte14(aiReferralEventsHourly.tsHour, windowStartIso),
74893
75026
  lte14(aiReferralEventsHourly.tsHour, windowEndIso)
74894
75027
  )
74895
75028
  ).run();
74896
75029
  tx.delete(rawEventSamples).where(
74897
- and48(
75030
+ and49(
74898
75031
  eq55(rawEventSamples.sourceId, sourceRow2.id),
74899
75032
  gte14(rawEventSamples.ts, windowStartIso),
74900
75033
  lte14(rawEventSamples.ts, windowEndIso)
@@ -75756,7 +75889,7 @@ async function trafficRoutes(app, opts) {
75756
75889
  }
75757
75890
  let removed = false;
75758
75891
  if (row.status === TrafficSourceStatuses.connected) {
75759
- const schedule = tx.select().from(schedules).where(and48(
75892
+ const schedule = tx.select().from(schedules).where(and49(
75760
75893
  eq55(schedules.projectId, project.id),
75761
75894
  eq55(schedules.kind, SchedulableRunKinds["traffic-sync"])
75762
75895
  )).get();
@@ -75842,7 +75975,7 @@ async function trafficRoutes(app, opts) {
75842
75975
  bindTrafficSyncSchedule(tx, project.id, target.id, now);
75843
75976
  scheduleAction = "upsert";
75844
75977
  } else {
75845
- const schedule = tx.select().from(schedules).where(and48(
75978
+ const schedule = tx.select().from(schedules).where(and49(
75846
75979
  eq55(schedules.projectId, project.id),
75847
75980
  eq55(schedules.kind, SchedulableRunKinds["traffic-sync"])
75848
75981
  )).get();
@@ -76191,7 +76324,7 @@ async function trafficRoutes(app, opts) {
76191
76324
  tx.update(runs).set({ status: RunStatuses.failed, error: safeError, finishedAt: failedAt }).where(eq55(runs.id, runId)).run();
76192
76325
  const latestSource = tx.select().from(trafficSources).where(eq55(trafficSources.id, sourceRow2.id)).get();
76193
76326
  if (latestSource && latestSource.status === TrafficSourceStatuses.connected && latestSource.syncLeaseOwner === leaseOwner && isDeepStrictEqual(latestSource.configJson, sourceRow2.configJson)) {
76194
- tx.update(trafficSources).set({ lastError: safeError, updatedAt: failedAt }).where(and48(
76327
+ tx.update(trafficSources).set({ lastError: safeError, updatedAt: failedAt }).where(and49(
76195
76328
  eq55(trafficSources.id, sourceRow2.id),
76196
76329
  eq55(trafficSources.status, TrafficSourceStatuses.connected),
76197
76330
  eq55(trafficSources.syncLeaseOwner, leaseOwner)
@@ -77059,7 +77192,7 @@ async function trafficRoutes(app, opts) {
77059
77192
  pathNormalized: crawlerEventsHourly.pathNormalized,
77060
77193
  hits: sql22`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
77061
77194
  }).from(crawlerEventsHourly).where(
77062
- and48(
77195
+ and49(
77063
77196
  eq55(crawlerEventsHourly.sourceId, row.id),
77064
77197
  gte14(crawlerEventsHourly.tsHour, since)
77065
77198
  )
@@ -77069,7 +77202,7 @@ async function trafficRoutes(app, opts) {
77069
77202
  );
77070
77203
  const crawlerTotal = crawlerSegments.content + crawlerSegments.sitemap + crawlerSegments.robots + crawlerSegments.asset + crawlerSegments.other;
77071
77204
  const aiUserFetchTotals = app.db.select({ total: sql22`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
77072
- and48(
77205
+ and49(
77073
77206
  eq55(aiUserFetchEventsHourly.sourceId, row.id),
77074
77207
  gte14(aiUserFetchEventsHourly.tsHour, since)
77075
77208
  )
@@ -77079,19 +77212,19 @@ async function trafficRoutes(app, opts) {
77079
77212
  nonhop: sql22`COALESCE(SUM(CASE WHEN ${referralLandedCondition()} THEN ${aiReferralEventsHourly.sessionsOrHits} ELSE 0 END), 0)`,
77080
77213
  landed: sql22`COALESCE(SUM(CASE WHEN ${countableReferralCondition()} THEN ${aiReferralEventsHourly.sessionsOrHits} ELSE 0 END), 0)`
77081
77214
  }).from(aiReferralEventsHourly).where(
77082
- and48(
77215
+ and49(
77083
77216
  eq55(aiReferralEventsHourly.sourceId, row.id),
77084
77217
  gte14(aiReferralEventsHourly.tsHour, since)
77085
77218
  )
77086
77219
  ).get();
77087
77220
  const sampleTotals = app.db.select({ total: sql22`COUNT(*)` }).from(rawEventSamples).where(
77088
- and48(
77221
+ and49(
77089
77222
  eq55(rawEventSamples.sourceId, row.id),
77090
77223
  gte14(rawEventSamples.ts, since)
77091
77224
  )
77092
77225
  ).get();
77093
77226
  const latestRun = app.db.select().from(runs).where(
77094
- and48(
77227
+ and49(
77095
77228
  eq55(runs.projectId, projectId),
77096
77229
  eq55(runs.kind, RunKinds["traffic-sync"]),
77097
77230
  eq55(runs.sourceId, row.id)
@@ -77130,7 +77263,7 @@ async function trafficRoutes(app, opts) {
77130
77263
  const now = (/* @__PURE__ */ new Date()).toISOString();
77131
77264
  let updatedRow;
77132
77265
  app.db.transaction((tx) => {
77133
- const sourceRow2 = tx.select().from(trafficSources).where(and48(eq55(trafficSources.projectId, project.id), eq55(trafficSources.id, request.params.id))).get();
77266
+ const sourceRow2 = tx.select().from(trafficSources).where(and49(eq55(trafficSources.projectId, project.id), eq55(trafficSources.id, request.params.id))).get();
77134
77267
  if (!sourceRow2) {
77135
77268
  throw notFound("traffic source", request.params.id);
77136
77269
  }
@@ -77270,7 +77403,7 @@ async function trafficRoutes(app, opts) {
77270
77403
  lte14(crawlerEventsHourly.tsHour, untilIso)
77271
77404
  ];
77272
77405
  if (sourceIdParam) crawlerFilters.push(eq55(crawlerEventsHourly.sourceId, sourceIdParam));
77273
- const crawlerWhere = and48(...crawlerFilters);
77406
+ const crawlerWhere = and49(...crawlerFilters);
77274
77407
  const pathTotals = app.db.select({
77275
77408
  pathNormalized: crawlerEventsHourly.pathNormalized,
77276
77409
  hits: sql22`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
@@ -77345,7 +77478,7 @@ async function trafficRoutes(app, opts) {
77345
77478
  lte14(aiUserFetchEventsHourly.tsHour, untilIso)
77346
77479
  ];
77347
77480
  if (sourceIdParam) userFetchFilters.push(eq55(aiUserFetchEventsHourly.sourceId, sourceIdParam));
77348
- const userFetchWhere = and48(...userFetchFilters);
77481
+ const userFetchWhere = and49(...userFetchFilters);
77349
77482
  const total = app.db.select({
77350
77483
  total: sql22`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`,
77351
77484
  rows: sql22`COUNT(*)`
@@ -77384,7 +77517,7 @@ async function trafficRoutes(app, opts) {
77384
77517
  lte14(aiReferralEventsHourly.tsHour, untilIso)
77385
77518
  ];
77386
77519
  if (sourceIdParam) aiFilters.push(eq55(aiReferralEventsHourly.sourceId, sourceIdParam));
77387
- const aiWhere = and48(...aiFilters);
77520
+ const aiWhere = and49(...aiFilters);
77388
77521
  const total = app.db.select({
77389
77522
  total: sql22`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
77390
77523
  nonhop: sql22`COALESCE(SUM(CASE WHEN ${referralLandedCondition()} THEN ${aiReferralEventsHourly.sessionsOrHits} ELSE 0 END), 0)`,
@@ -77459,7 +77592,7 @@ async function trafficRoutes(app, opts) {
77459
77592
  windowEnd: untilIso,
77460
77593
  series: (() => {
77461
77594
  const points = completeTrafficSeries(since, until, granularity, seriesByBucket);
77462
- const coverageScope = (col, srcCol) => sourceIdParam ? and48(eq55(col, project.id), eq55(srcCol, sourceIdParam)) : eq55(col, project.id);
77595
+ const coverageScope = (col, srcCol) => sourceIdParam ? and49(eq55(col, project.id), eq55(srcCol, sourceIdParam)) : eq55(col, project.id);
77463
77596
  const firsts = [
77464
77597
  app.db.select({ v: sql22`MIN(${crawlerEventsHourly.tsHour})` }).from(crawlerEventsHourly).where(coverageScope(crawlerEventsHourly.projectId, crawlerEventsHourly.sourceId)).get()?.v,
77465
77598
  app.db.select({ v: sql22`MIN(${aiUserFetchEventsHourly.tsHour})` }).from(aiUserFetchEventsHourly).where(coverageScope(aiUserFetchEventsHourly.projectId, aiUserFetchEventsHourly.sourceId)).get()?.v,
@@ -77861,7 +77994,7 @@ var skillsTriggerSurfaceCheck = {
77861
77994
  var AGENT_CHECKS = [skillsInstalledCheck, skillsCurrentCheck, skillsTriggerSurfaceCheck];
77862
77995
 
77863
77996
  // ../api-routes/src/doctor/checks/backlinks.ts
77864
- import { and as and49, eq as eq56 } from "drizzle-orm";
77997
+ import { and as and50, eq as eq56 } from "drizzle-orm";
77865
77998
  function skippedNoProject() {
77866
77999
  return {
77867
78000
  status: CheckStatuses.skipped,
@@ -77891,7 +78024,7 @@ var BACKLINKS_CHECKS = [
77891
78024
  details: { commoncrawl: ccConnected }
77892
78025
  };
77893
78026
  }
77894
- const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and49(
78027
+ const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and50(
77895
78028
  eq56(backlinkSummaries.projectId, ctx.project.id),
77896
78029
  eq56(backlinkSummaries.source, BacklinkSources.commoncrawl)
77897
78030
  )).limit(1).get() : false;
@@ -78416,7 +78549,7 @@ var ga4ConnectionCheck = {
78416
78549
  var GA_AUTH_CHECKS = [ga4ConnectionCheck];
78417
78550
 
78418
78551
  // ../api-routes/src/doctor/checks/gbp-auth.ts
78419
- import { and as and50, eq as eq59 } from "drizzle-orm";
78552
+ import { and as and51, eq as eq59 } from "drizzle-orm";
78420
78553
  var RECENT_SYNC_WARN_DAYS2 = 7;
78421
78554
  var RECENT_SYNC_FAIL_DAYS2 = 30;
78422
78555
  function skippedNoProject3() {
@@ -78649,7 +78782,7 @@ var recentSyncCheck = {
78649
78782
  title: "GBP recent sync",
78650
78783
  run: (ctx) => {
78651
78784
  if (!ctx.project) return skippedNoProject3();
78652
- const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and50(eq59(gbpLocations.projectId, ctx.project.id), eq59(gbpLocations.selected, true))).all();
78785
+ const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and51(eq59(gbpLocations.projectId, ctx.project.id), eq59(gbpLocations.selected, true))).all();
78653
78786
  if (selected.length === 0) {
78654
78787
  return {
78655
78788
  status: CheckStatuses.skipped,
@@ -79269,7 +79402,7 @@ var RUNTIME_STATE_CHECKS = [
79269
79402
  ];
79270
79403
 
79271
79404
  // ../api-routes/src/doctor/checks/traffic-source.ts
79272
- import { and as and51, eq as eq61, gte as gte15, inArray as inArray22, ne as ne9, sql as sql23 } from "drizzle-orm";
79405
+ import { and as and52, eq as eq61, gte as gte15, inArray as inArray22, ne as ne9, sql as sql23 } from "drizzle-orm";
79273
79406
  var RECENT_DATA_WARN_DAYS = 7;
79274
79407
  var RECENT_DATA_FAIL_DAYS = 30;
79275
79408
  function isCloudflareDirectPush(source) {
@@ -79308,7 +79441,7 @@ function skippedNoProject5() {
79308
79441
  function loadProbes(ctx) {
79309
79442
  if (!ctx.project) return [];
79310
79443
  const rows = ctx.db.select().from(trafficSources).where(
79311
- and51(
79444
+ and52(
79312
79445
  eq61(trafficSources.projectId, ctx.project.id),
79313
79446
  ne9(trafficSources.status, TrafficSourceStatuses.archived)
79314
79447
  )
@@ -79415,7 +79548,7 @@ var recentDataCheck = {
79415
79548
  const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
79416
79549
  const recentCrawlers = Number(
79417
79550
  ctx.db.select({ total: sql23`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
79418
- and51(
79551
+ and52(
79419
79552
  eq61(crawlerEventsHourly.projectId, ctx.project.id),
79420
79553
  inArray22(crawlerEventsHourly.sourceId, activeSourceIds),
79421
79554
  gte15(crawlerEventsHourly.tsHour, warnCutoff)
@@ -79424,7 +79557,7 @@ var recentDataCheck = {
79424
79557
  );
79425
79558
  const recentReferrals = Number(
79426
79559
  ctx.db.select({ total: sql23`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
79427
- and51(
79560
+ and52(
79428
79561
  eq61(aiReferralEventsHourly.projectId, ctx.project.id),
79429
79562
  inArray22(aiReferralEventsHourly.sourceId, activeSourceIds),
79430
79563
  gte15(aiReferralEventsHourly.tsHour, warnCutoff)
@@ -79433,7 +79566,7 @@ var recentDataCheck = {
79433
79566
  );
79434
79567
  const recentUserFetches = Number(
79435
79568
  ctx.db.select({ total: sql23`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
79436
- and51(
79569
+ and52(
79437
79570
  eq61(aiUserFetchEventsHourly.projectId, ctx.project.id),
79438
79571
  inArray22(aiUserFetchEventsHourly.sourceId, activeSourceIds),
79439
79572
  gte15(aiUserFetchEventsHourly.tsHour, warnCutoff)
@@ -79459,7 +79592,7 @@ var recentDataCheck = {
79459
79592
  }
79460
79593
  const olderCrawlers = Number(
79461
79594
  ctx.db.select({ total: sql23`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
79462
- and51(
79595
+ and52(
79463
79596
  eq61(crawlerEventsHourly.projectId, ctx.project.id),
79464
79597
  inArray22(crawlerEventsHourly.sourceId, activeSourceIds),
79465
79598
  gte15(crawlerEventsHourly.tsHour, failCutoff)
@@ -79468,7 +79601,7 @@ var recentDataCheck = {
79468
79601
  );
79469
79602
  const olderReferrals = Number(
79470
79603
  ctx.db.select({ total: sql23`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
79471
- and51(
79604
+ and52(
79472
79605
  eq61(aiReferralEventsHourly.projectId, ctx.project.id),
79473
79606
  inArray22(aiReferralEventsHourly.sourceId, activeSourceIds),
79474
79607
  gte15(aiReferralEventsHourly.tsHour, failCutoff)
@@ -79477,7 +79610,7 @@ var recentDataCheck = {
79477
79610
  );
79478
79611
  const olderUserFetches = Number(
79479
79612
  ctx.db.select({ total: sql23`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
79480
- and51(
79613
+ and52(
79481
79614
  eq61(aiUserFetchEventsHourly.projectId, ctx.project.id),
79482
79615
  inArray22(aiUserFetchEventsHourly.sourceId, activeSourceIds),
79483
79616
  gte15(aiUserFetchEventsHourly.tsHour, failCutoff)
@@ -80395,7 +80528,7 @@ async function doctorRoutes(app, opts) {
80395
80528
 
80396
80529
  // ../api-routes/src/discovery/routes.ts
80397
80530
  import crypto46 from "crypto";
80398
- import { and as and52, desc as desc26, eq as eq62, gte as gte16, inArray as inArray23, isNull as isNull10, or as or12 } from "drizzle-orm";
80531
+ import { and as and53, desc as desc26, eq as eq62, gte as gte16, inArray as inArray23, isNull as isNull10, or as or12 } from "drizzle-orm";
80399
80532
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
80400
80533
  async function discoveryRoutes(app, opts) {
80401
80534
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -80429,7 +80562,7 @@ async function discoveryRoutes(app, opts) {
80429
80562
  const now = (/* @__PURE__ */ new Date()).toISOString();
80430
80563
  const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
80431
80564
  const decision = app.db.transaction((tx) => {
80432
- const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and52(
80565
+ const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and53(
80433
80566
  eq62(discoverySessions.projectId, project.id),
80434
80567
  eq62(discoverySessions.icpDescription, icpDescription),
80435
80568
  // Buyer is part of session identity: it changes the seed prompt's
@@ -81075,7 +81208,7 @@ function dedupeStrings(input) {
81075
81208
 
81076
81209
  // ../api-routes/src/technical-aeo.ts
81077
81210
  import crypto48 from "crypto";
81078
- import { and as and53, asc as asc11, count as count2, desc as desc27, eq as eq64, inArray as inArray24, isNotNull as isNotNull5, isNull as isNull11, lt as lt10, or as or13, sql as sql24 } from "drizzle-orm";
81211
+ import { and as and54, asc as asc11, count as count2, desc as desc27, eq as eq64, inArray as inArray24, isNotNull as isNotNull5, isNull as isNull11, lt as lt10, or as or13, sql as sql24 } from "drizzle-orm";
81079
81212
  import { alias } from "drizzle-orm/sqlite-core";
81080
81213
  var FETCHED_SITE_CRAWL_STATES = /* @__PURE__ */ new Set([
81081
81214
  ...SiteCrawlFetchedStates,
@@ -81405,25 +81538,25 @@ async function technicalAeoRoutes(app, opts) {
81405
81538
  } else {
81406
81539
  filters.push(eq64(siteCrawlSnapshots.complete, true), eq64(runs.status, RunStatuses.completed));
81407
81540
  }
81408
- return app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq64(siteCrawlSnapshots.runId, runs.id)).where(and53(...filters)).orderBy(desc27(siteCrawlSnapshots.createdAt), desc27(siteCrawlSnapshots.runId)).limit(1).get();
81541
+ return app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq64(siteCrawlSnapshots.runId, runs.id)).where(and54(...filters)).orderBy(desc27(siteCrawlSnapshots.createdAt), desc27(siteCrawlSnapshots.runId)).limit(1).get();
81409
81542
  };
81410
81543
  const detailScopeFor = (projectId, snapshot) => snapshot.detailsAvailable && snapshot.attemptId ? { projectId, runId: snapshot.runId, attemptId: snapshot.attemptId } : null;
81411
81544
  const pageInScope = (scope, selector) => {
81412
81545
  if (!selector.nodeKey && !selector.url) return null;
81413
- return app.db.select().from(siteCrawlPages).where(and53(
81546
+ return app.db.select().from(siteCrawlPages).where(and54(
81414
81547
  eq64(siteCrawlPages.projectId, scope.projectId),
81415
81548
  eq64(siteCrawlPages.runId, scope.runId),
81416
81549
  eq64(siteCrawlPages.attemptId, scope.attemptId),
81417
81550
  selector.nodeKey ? eq64(siteCrawlPages.nodeKey, selector.nodeKey) : eq64(siteCrawlPages.url, selector.url)
81418
81551
  )).orderBy(asc11(siteCrawlPages.nodeKey)).limit(1).get() ?? null;
81419
81552
  };
81420
- const rootPageInScope = (scope, rootUrl) => pageInScope(scope, { url: rootUrl }) ?? app.db.select().from(siteCrawlPages).where(and53(
81553
+ const rootPageInScope = (scope, rootUrl) => pageInScope(scope, { url: rootUrl }) ?? app.db.select().from(siteCrawlPages).where(and54(
81421
81554
  eq64(siteCrawlPages.projectId, scope.projectId),
81422
81555
  eq64(siteCrawlPages.runId, scope.runId),
81423
81556
  eq64(siteCrawlPages.attemptId, scope.attemptId),
81424
81557
  eq64(siteCrawlPages.depth, 0)
81425
81558
  )).orderBy(asc11(siteCrawlPages.nodeKey)).limit(1).get() ?? null;
81426
- const isSurfaceableAuditRun = (projectId, runId) => Boolean(app.db.select({ id: runs.id }).from(runs).where(and53(
81559
+ const isSurfaceableAuditRun = (projectId, runId) => Boolean(app.db.select({ id: runs.id }).from(runs).where(and54(
81427
81560
  eq64(runs.id, runId),
81428
81561
  eq64(runs.projectId, projectId),
81429
81562
  eq64(runs.kind, RunKinds["site-audit"]),
@@ -81433,7 +81566,7 @@ async function technicalAeoRoutes(app, opts) {
81433
81566
  const assertKnownAuditRun = (projectId, runId) => {
81434
81567
  if (runId && !isSurfaceableAuditRun(projectId, runId)) throw notFound("Site crawl run", runId);
81435
81568
  };
81436
- const hasLegacyAudit = (projectId) => Boolean(app.db.select({ runId: siteAuditSnapshots.runId }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and53(
81569
+ const hasLegacyAudit = (projectId) => Boolean(app.db.select({ runId: siteAuditSnapshots.runId }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and54(
81437
81570
  eq64(siteAuditSnapshots.projectId, projectId),
81438
81571
  eq64(runs.projectId, projectId),
81439
81572
  eq64(runs.kind, RunKinds["site-audit"]),
@@ -81465,13 +81598,13 @@ async function technicalAeoRoutes(app, opts) {
81465
81598
  notProbeRun()
81466
81599
  ];
81467
81600
  const targetFilters = request.query.runId ? [...baseFilters, eq64(siteAuditSnapshots.runId, request.query.runId)] : baseFilters;
81468
- const latest = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and53(...targetFilters)).orderBy(desc27(siteAuditSnapshots.createdAt)).limit(1).get();
81601
+ const latest = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and54(...targetFilters)).orderBy(desc27(siteAuditSnapshots.createdAt)).limit(1).get();
81469
81602
  if (!latest) {
81470
81603
  if (request.query.runId) throw notFound("Site audit run", request.query.runId);
81471
81604
  return emptyScore(project.name);
81472
81605
  }
81473
81606
  const snap = latest.snap;
81474
- const previous = app.db.select({ snap: siteAuditSnapshots }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and53(...baseFilters, lt10(siteAuditSnapshots.createdAt, snap.createdAt))).orderBy(desc27(siteAuditSnapshots.createdAt)).limit(1).get()?.snap ?? null;
81607
+ const previous = app.db.select({ snap: siteAuditSnapshots }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and54(...baseFilters, lt10(siteAuditSnapshots.createdAt, snap.createdAt))).orderBy(desc27(siteAuditSnapshots.createdAt)).limit(1).get()?.snap ?? null;
81475
81608
  const deltaScore = previous ? snap.aggregateScore - previous.aggregateScore : null;
81476
81609
  const trend = deltaScore == null ? null : deltaScore > 0 ? SiteAuditTrendDirections.up : deltaScore < 0 ? SiteAuditTrendDirections.down : SiteAuditTrendDirections.flat;
81477
81610
  return {
@@ -81505,7 +81638,7 @@ async function technicalAeoRoutes(app, opts) {
81505
81638
  notProbeRun()
81506
81639
  ];
81507
81640
  if (request.query.runId) targetFilters.push(eq64(siteAuditSnapshots.runId, request.query.runId));
81508
- const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and53(...targetFilters)).orderBy(desc27(siteAuditSnapshots.createdAt)).limit(1).get();
81641
+ const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and54(...targetFilters)).orderBy(desc27(siteAuditSnapshots.createdAt)).limit(1).get();
81509
81642
  if (!latest && request.query.runId) {
81510
81643
  throw notFound("Site audit run", request.query.runId);
81511
81644
  }
@@ -81515,7 +81648,7 @@ async function technicalAeoRoutes(app, opts) {
81515
81648
  const statusFilter = request.query.status === "success" || request.query.status === "error" ? request.query.status : null;
81516
81649
  const conds = [eq64(siteAuditPages.projectId, project.id), eq64(siteAuditPages.runId, latest.runId)];
81517
81650
  if (statusFilter) conds.push(eq64(siteAuditPages.status, statusFilter));
81518
- const where = and53(...conds);
81651
+ const where = and54(...conds);
81519
81652
  const totalRow = app.db.select({ value: count2() }).from(siteAuditPages).where(where).get();
81520
81653
  const total = totalRow?.value ?? 0;
81521
81654
  const limit = parsePositiveInt(request.query.limit, 100, 500);
@@ -81539,7 +81672,7 @@ async function technicalAeoRoutes(app, opts) {
81539
81672
  auditedAt: siteAuditSnapshots.auditedAt,
81540
81673
  aggregateScore: siteAuditSnapshots.aggregateScore,
81541
81674
  pagesAudited: siteAuditSnapshots.pagesAudited
81542
- }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and53(
81675
+ }).from(siteAuditSnapshots).innerJoin(runs, eq64(siteAuditSnapshots.runId, runs.id)).where(and54(
81543
81676
  eq64(siteAuditSnapshots.projectId, project.id),
81544
81677
  eq64(runs.projectId, project.id),
81545
81678
  eq64(runs.kind, RunKinds["site-audit"]),
@@ -81637,7 +81770,7 @@ async function technicalAeoRoutes(app, opts) {
81637
81770
  { projectId: project.id, runId: snapshot.runId, attemptId: snapshot.attemptId },
81638
81771
  snapshot.rootUrl
81639
81772
  )?.nodeKey ?? null;
81640
- const persistedLayout = app.db.select().from(siteCrawlGraphLayouts).where(and53(
81773
+ const persistedLayout = app.db.select().from(siteCrawlGraphLayouts).where(and54(
81641
81774
  eq64(siteCrawlGraphLayouts.projectId, project.id),
81642
81775
  eq64(siteCrawlGraphLayouts.runId, snapshot.runId),
81643
81776
  eq64(siteCrawlGraphLayouts.attemptId, snapshot.attemptId)
@@ -81712,12 +81845,12 @@ async function technicalAeoRoutes(app, opts) {
81712
81845
  linkScoreNormalized: siteCrawlPages.linkScoreNormalized,
81713
81846
  x: siteCrawlGraphNodes.x,
81714
81847
  y: siteCrawlGraphNodes.y
81715
- }).from(siteCrawlGraphNodes).innerJoin(siteCrawlPages, and53(
81848
+ }).from(siteCrawlGraphNodes).innerJoin(siteCrawlPages, and54(
81716
81849
  eq64(siteCrawlPages.projectId, siteCrawlGraphNodes.projectId),
81717
81850
  eq64(siteCrawlPages.runId, siteCrawlGraphNodes.runId),
81718
81851
  eq64(siteCrawlPages.attemptId, siteCrawlGraphNodes.attemptId),
81719
81852
  eq64(siteCrawlPages.nodeKey, siteCrawlGraphNodes.nodeKey)
81720
- )).where(and53(...graphScope, lt10(siteCrawlGraphNodes.sampleRank, maxNodes))).orderBy(asc11(siteCrawlGraphNodes.sampleRank)).all();
81853
+ )).where(and54(...graphScope, lt10(siteCrawlGraphNodes.sampleRank, maxNodes))).orderBy(asc11(siteCrawlGraphNodes.sampleRank)).all();
81721
81854
  const nodes = nodeRows.map(({ indexabilityReasons, canonicalNodeKey, ...row }) => ({
81722
81855
  ...row,
81723
81856
  healthState: deriveSiteHealthState({ ...row, indexabilityReasons, canonicalNodeKey })
@@ -81730,19 +81863,19 @@ async function technicalAeoRoutes(app, opts) {
81730
81863
  followable: siteCrawlGraphEdges.followable,
81731
81864
  occurrences: siteCrawlGraphEdges.occurrences,
81732
81865
  isTemplate: siteCrawlGraphEdges.isTemplate
81733
- }).from(siteCrawlGraphEdges).innerJoin(graphSourceNode, and53(
81866
+ }).from(siteCrawlGraphEdges).innerJoin(graphSourceNode, and54(
81734
81867
  eq64(graphSourceNode.projectId, siteCrawlGraphEdges.projectId),
81735
81868
  eq64(graphSourceNode.runId, siteCrawlGraphEdges.runId),
81736
81869
  eq64(graphSourceNode.attemptId, siteCrawlGraphEdges.attemptId),
81737
81870
  eq64(graphSourceNode.nodeKey, siteCrawlGraphEdges.sourceNodeKey),
81738
81871
  lt10(graphSourceNode.sampleRank, maxNodes)
81739
- )).innerJoin(graphTargetNode, and53(
81872
+ )).innerJoin(graphTargetNode, and54(
81740
81873
  eq64(graphTargetNode.projectId, siteCrawlGraphEdges.projectId),
81741
81874
  eq64(graphTargetNode.runId, siteCrawlGraphEdges.runId),
81742
81875
  eq64(graphTargetNode.attemptId, siteCrawlGraphEdges.attemptId),
81743
81876
  eq64(graphTargetNode.nodeKey, siteCrawlGraphEdges.targetNodeKey),
81744
81877
  lt10(graphTargetNode.sampleRank, maxNodes)
81745
- )).where(and53(
81878
+ )).where(and54(
81746
81879
  eq64(siteCrawlGraphEdges.projectId, project.id),
81747
81880
  eq64(siteCrawlGraphEdges.runId, snapshot.runId),
81748
81881
  eq64(siteCrawlGraphEdges.attemptId, snapshot.attemptId),
@@ -81839,7 +81972,7 @@ async function technicalAeoRoutes(app, opts) {
81839
81972
  let frontier = [focus.nodeKey];
81840
81973
  let queryTruncated = false;
81841
81974
  for (let distance = 0; distance < hops && frontier.length > 0; distance += 1) {
81842
- const candidates = app.db.select().from(siteCrawlEdges).where(and53(
81975
+ const candidates = app.db.select().from(siteCrawlEdges).where(and54(
81843
81976
  eq64(siteCrawlEdges.projectId, scope.projectId),
81844
81977
  eq64(siteCrawlEdges.runId, scope.runId),
81845
81978
  eq64(siteCrawlEdges.attemptId, scope.attemptId),
@@ -81880,7 +82013,7 @@ async function technicalAeoRoutes(app, opts) {
81880
82013
  frontier = [...next].sort();
81881
82014
  }
81882
82015
  const nodeKeys = [...distances.keys()];
81883
- const pageRows = nodeKeys.length === 0 ? [] : app.db.select().from(siteCrawlPages).where(and53(
82016
+ const pageRows = nodeKeys.length === 0 ? [] : app.db.select().from(siteCrawlPages).where(and54(
81884
82017
  eq64(siteCrawlPages.projectId, scope.projectId),
81885
82018
  eq64(siteCrawlPages.runId, scope.runId),
81886
82019
  eq64(siteCrawlPages.attemptId, scope.attemptId),
@@ -81888,7 +82021,7 @@ async function technicalAeoRoutes(app, opts) {
81888
82021
  )).all();
81889
82022
  const persistedNodeKeys = new Set(pageRows.map((row) => row.nodeKey));
81890
82023
  const outermostNodeKeys = pageRows.filter((row) => distances.get(row.nodeKey) === hops).map((row) => row.nodeKey);
81891
- const omittedOutermostEdge = outermostNodeKeys.length === 0 ? void 0 : app.db.select({ edgeKey: siteCrawlEdges.edgeKey }).from(siteCrawlEdges).where(and53(
82024
+ const omittedOutermostEdge = outermostNodeKeys.length === 0 ? void 0 : app.db.select({ edgeKey: siteCrawlEdges.edgeKey }).from(siteCrawlEdges).where(and54(
81892
82025
  eq64(siteCrawlEdges.projectId, scope.projectId),
81893
82026
  eq64(siteCrawlEdges.runId, scope.runId),
81894
82027
  eq64(siteCrawlEdges.attemptId, scope.attemptId),
@@ -81990,7 +82123,7 @@ async function technicalAeoRoutes(app, opts) {
81990
82123
  let found = fromPage.nodeKey === toPage.nodeKey;
81991
82124
  let truncated = false;
81992
82125
  for (let depth = 0; depth < maxDepth && frontier.length > 0 && !found; depth += 1) {
81993
- const candidates = app.db.select().from(siteCrawlEdges).where(and53(
82126
+ const candidates = app.db.select().from(siteCrawlEdges).where(and54(
81994
82127
  eq64(siteCrawlEdges.projectId, scope.projectId),
81995
82128
  eq64(siteCrawlEdges.runId, scope.runId),
81996
82129
  eq64(siteCrawlEdges.attemptId, scope.attemptId),
@@ -82002,7 +82135,7 @@ async function technicalAeoRoutes(app, opts) {
82002
82135
  )).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(SITE_HEALTH_PATH_MAX_VISITED_NODES + 1).all();
82003
82136
  if (candidates.length > SITE_HEALTH_PATH_MAX_VISITED_NODES) truncated = true;
82004
82137
  const candidateTargetKeys = [...new Set(candidates.map((edge) => edge.targetNodeKey).filter((nodeKey) => nodeKey != null))];
82005
- const persistedTargetKeys = new Set(candidateTargetKeys.length === 0 ? [] : app.db.select({ nodeKey: siteCrawlPages.nodeKey }).from(siteCrawlPages).where(and53(
82138
+ const persistedTargetKeys = new Set(candidateTargetKeys.length === 0 ? [] : app.db.select({ nodeKey: siteCrawlPages.nodeKey }).from(siteCrawlPages).where(and54(
82006
82139
  eq64(siteCrawlPages.projectId, scope.projectId),
82007
82140
  eq64(siteCrawlPages.runId, scope.runId),
82008
82141
  eq64(siteCrawlPages.attemptId, scope.attemptId),
@@ -82048,7 +82181,7 @@ async function technicalAeoRoutes(app, opts) {
82048
82181
  pathEdges.unshift(step.edge);
82049
82182
  pathKeys.unshift(step.nodeKey);
82050
82183
  }
82051
- const pathRows = app.db.select().from(siteCrawlPages).where(and53(
82184
+ const pathRows = app.db.select().from(siteCrawlPages).where(and54(
82052
82185
  eq64(siteCrawlPages.projectId, scope.projectId),
82053
82186
  eq64(siteCrawlPages.runId, scope.runId),
82054
82187
  eq64(siteCrawlPages.attemptId, scope.attemptId),
@@ -82088,13 +82221,13 @@ async function technicalAeoRoutes(app, opts) {
82088
82221
  eq64(siteCrawlSnapshots.complete, true),
82089
82222
  notProbeRun()
82090
82223
  ];
82091
- const afterTarget = request.query.toRunId ? resolveCrawl(project.id, request.query.toRunId) : app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq64(siteCrawlSnapshots.runId, runs.id)).where(and53(...completeSnapshotFilters)).orderBy(desc27(siteCrawlSnapshots.createdAt), desc27(siteCrawlSnapshots.runId)).limit(1).get();
82224
+ const afterTarget = request.query.toRunId ? resolveCrawl(project.id, request.query.toRunId) : app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq64(siteCrawlSnapshots.runId, runs.id)).where(and54(...completeSnapshotFilters)).orderBy(desc27(siteCrawlSnapshots.createdAt), desc27(siteCrawlSnapshots.runId)).limit(1).get();
82092
82225
  if (request.query.toRunId && !afterTarget) throw notFound("Site crawl run", request.query.toRunId);
82093
- const beforeTarget = request.query.fromRunId ? resolveCrawl(project.id, request.query.fromRunId) : afterTarget ? app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq64(siteCrawlSnapshots.runId, runs.id)).where(and53(
82226
+ const beforeTarget = request.query.fromRunId ? resolveCrawl(project.id, request.query.fromRunId) : afterTarget ? app.db.select({ snapshot: siteCrawlSnapshots, runStatus: runs.status }).from(siteCrawlSnapshots).innerJoin(runs, eq64(siteCrawlSnapshots.runId, runs.id)).where(and54(
82094
82227
  ...completeSnapshotFilters,
82095
82228
  or13(
82096
82229
  lt10(siteCrawlSnapshots.createdAt, afterTarget.snapshot.createdAt),
82097
- and53(
82230
+ and54(
82098
82231
  eq64(siteCrawlSnapshots.createdAt, afterTarget.snapshot.createdAt),
82099
82232
  lt10(siteCrawlSnapshots.runId, afterTarget.snapshot.runId)
82100
82233
  )
@@ -82330,13 +82463,13 @@ async function technicalAeoRoutes(app, opts) {
82330
82463
  const visibleKeys = keyRows.slice(0, limit);
82331
82464
  const pageKeys = visibleKeys.filter((row) => row.entity === "page").map((row) => row.key);
82332
82465
  const linkKeys = visibleKeys.filter((row) => row.entity === "link").map((row) => row.key);
82333
- const pageRowsFor = (scope) => pageKeys.length === 0 ? [] : app.db.select().from(siteCrawlPages).where(and53(
82466
+ const pageRowsFor = (scope) => pageKeys.length === 0 ? [] : app.db.select().from(siteCrawlPages).where(and54(
82334
82467
  eq64(siteCrawlPages.projectId, scope.projectId),
82335
82468
  eq64(siteCrawlPages.runId, scope.runId),
82336
82469
  eq64(siteCrawlPages.attemptId, scope.attemptId),
82337
82470
  inArray24(siteCrawlPages.nodeKey, pageKeys)
82338
82471
  )).all();
82339
- const edgeRowsFor = (scope) => linkKeys.length === 0 ? [] : app.db.select().from(siteCrawlEdges).where(and53(
82472
+ const edgeRowsFor = (scope) => linkKeys.length === 0 ? [] : app.db.select().from(siteCrawlEdges).where(and54(
82340
82473
  eq64(siteCrawlEdges.projectId, scope.projectId),
82341
82474
  eq64(siteCrawlEdges.runId, scope.runId),
82342
82475
  eq64(siteCrawlEdges.attemptId, scope.attemptId),
@@ -82482,7 +82615,7 @@ async function technicalAeoRoutes(app, opts) {
82482
82615
  const orderBy = request.query.sort === "score-desc" ? [desc27(siteCrawlPages.auditScore), asc11(siteCrawlPages.nodeKey)] : request.query.sort === "score-asc" ? [asc11(siteCrawlPages.auditScore), asc11(siteCrawlPages.nodeKey)] : request.query.sort === "path" ? [asc11(siteCrawlPages.path), asc11(siteCrawlPages.nodeKey)] : [asc11(siteCrawlPages.url), asc11(siteCrawlPages.nodeKey)];
82483
82616
  let healthStateFilter = null;
82484
82617
  if (healthState) {
82485
- const legacyRow = app.db.select({ id: siteCrawlPages.id }).from(siteCrawlPages).where(and53(
82618
+ const legacyRow = app.db.select({ id: siteCrawlPages.id }).from(siteCrawlPages).where(and54(
82486
82619
  eq64(siteCrawlPages.projectId, project.id),
82487
82620
  eq64(siteCrawlPages.runId, snapshot.runId),
82488
82621
  eq64(siteCrawlPages.attemptId, snapshot.attemptId),
@@ -82502,7 +82635,7 @@ async function technicalAeoRoutes(app, opts) {
82502
82635
  pages: []
82503
82636
  };
82504
82637
  }
82505
- const where = and53(...filters);
82638
+ const where = and54(...filters);
82506
82639
  const total = app.db.select({ value: count2() }).from(siteCrawlPages).where(where).get()?.value ?? 0;
82507
82640
  const rows = app.db.select().from(siteCrawlPages).where(where).orderBy(...orderBy).limit(limit).offset(offset).all();
82508
82641
  const nextOffset = offset + rows.length;
@@ -82533,7 +82666,7 @@ async function technicalAeoRoutes(app, opts) {
82533
82666
  url: siteCrawlPages.url,
82534
82667
  inventoryEligible: siteCrawlPages.inventoryEligible,
82535
82668
  fetchState: siteCrawlPages.fetchState
82536
- }).from(siteCrawlPages).where(and53(
82669
+ }).from(siteCrawlPages).where(and54(
82537
82670
  eq64(siteCrawlPages.projectId, project.id),
82538
82671
  eq64(siteCrawlPages.runId, snapshot.runId),
82539
82672
  eq64(siteCrawlPages.attemptId, snapshot.attemptId)
@@ -82625,7 +82758,7 @@ async function technicalAeoRoutes(app, opts) {
82625
82758
  if (request.query.targetUrl) filters.push(eq64(siteCrawlEdges.targetUrl, request.query.targetUrl));
82626
82759
  const followable = parseBoolean2(request.query.followable);
82627
82760
  if (followable != null) filters.push(eq64(siteCrawlEdges.followable, followable));
82628
- const where = and53(...filters);
82761
+ const where = and54(...filters);
82629
82762
  const total = app.db.select({ value: count2() }).from(siteCrawlEdges).where(where).get()?.value ?? 0;
82630
82763
  const limit = parseBoundedLimit(request.query.limit, 100, 200);
82631
82764
  const offset = decodeCursor(request.query.cursor);
@@ -82692,8 +82825,8 @@ async function technicalAeoRoutes(app, opts) {
82692
82825
  const inboundMatch = request.query.nodeKey && request.query.url ? or13(eq64(siteCrawlEdges.targetNodeKey, request.query.nodeKey), eq64(siteCrawlEdges.targetUrl, request.query.url)) : request.query.nodeKey ? eq64(siteCrawlEdges.targetNodeKey, request.query.nodeKey) : eq64(siteCrawlEdges.targetUrl, request.query.url);
82693
82826
  const outboundMatch = request.query.nodeKey && request.query.url ? or13(eq64(siteCrawlEdges.sourceNodeKey, request.query.nodeKey), eq64(siteCrawlEdges.sourceUrl, request.query.url)) : request.query.nodeKey ? eq64(siteCrawlEdges.sourceNodeKey, request.query.nodeKey) : eq64(siteCrawlEdges.sourceUrl, request.query.url);
82694
82827
  const limit = parseBoundedLimit(request.query.limit, 50, 100);
82695
- const inboundRows = app.db.select().from(siteCrawlEdges).where(and53(...scope, inboundMatch)).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
82696
- const outboundRows = app.db.select().from(siteCrawlEdges).where(and53(...scope, outboundMatch)).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
82828
+ const inboundRows = app.db.select().from(siteCrawlEdges).where(and54(...scope, inboundMatch)).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
82829
+ const outboundRows = app.db.select().from(siteCrawlEdges).where(and54(...scope, outboundMatch)).orderBy(asc11(siteCrawlEdges.edgeKey)).limit(limit + 1).all();
82697
82830
  return {
82698
82831
  project: project.name,
82699
82832
  hasCrawlData: true,
@@ -82726,7 +82859,7 @@ async function technicalAeoRoutes(app, opts) {
82726
82859
  if (!snapshot.attemptId) {
82727
82860
  return { project: project.name, runId: snapshot.runId, state: "partial", checkDeadLinks: true, checked: snapshot.deadLinksChecked, found: snapshot.deadLinksFound, unverified: snapshot.deadLinksUnverified, total: 0, nextCursor: null, deadLinks: [] };
82728
82861
  }
82729
- const where = and53(
82862
+ const where = and54(
82730
82863
  eq64(siteCrawlFindings.projectId, project.id),
82731
82864
  eq64(siteCrawlFindings.runId, snapshot.runId),
82732
82865
  eq64(siteCrawlFindings.attemptId, snapshot.attemptId),
@@ -82779,13 +82912,13 @@ async function technicalAeoRoutes(app, opts) {
82779
82912
  createdAt: runs.createdAt,
82780
82913
  startedAt: runs.startedAt,
82781
82914
  finishedAt: runs.finishedAt
82782
- }).from(runs).where(and53(
82915
+ }).from(runs).where(and54(
82783
82916
  eq64(runs.projectId, project.id),
82784
82917
  eq64(runs.kind, RunKinds["site-audit"]),
82785
82918
  notProbeRun()
82786
82919
  )).orderBy(desc27(runs.createdAt), desc27(runs.id)).limit(limit).all();
82787
82920
  const runIds = rows.map((row) => row.runId);
82788
- const crawlRunIds = new Set(runIds.length === 0 ? [] : app.db.select({ runId: siteCrawlSnapshots.runId }).from(siteCrawlSnapshots).innerJoin(runs, eq64(siteCrawlSnapshots.runId, runs.id)).where(and53(
82921
+ const crawlRunIds = new Set(runIds.length === 0 ? [] : app.db.select({ runId: siteCrawlSnapshots.runId }).from(siteCrawlSnapshots).innerJoin(runs, eq64(siteCrawlSnapshots.runId, runs.id)).where(and54(
82789
82922
  eq64(siteCrawlSnapshots.projectId, project.id),
82790
82923
  eq64(runs.projectId, project.id),
82791
82924
  eq64(runs.kind, RunKinds["site-audit"]),
@@ -82804,18 +82937,18 @@ async function technicalAeoRoutes(app, opts) {
82804
82937
  });
82805
82938
  app.get("/projects/:name/technical-aeo/runs/:runId/progress", async (request) => {
82806
82939
  const project = resolveProject(app.db, request.params.name);
82807
- const run = app.db.select().from(runs).where(and53(
82940
+ const run = app.db.select().from(runs).where(and54(
82808
82941
  eq64(runs.id, request.params.runId),
82809
82942
  eq64(runs.projectId, project.id),
82810
82943
  eq64(runs.kind, RunKinds["site-audit"]),
82811
82944
  notProbeRun()
82812
82945
  )).get();
82813
82946
  if (!run) throw notFound("Site audit run", request.params.runId);
82814
- const attempt = app.db.select().from(siteCrawlAttempts).where(and53(
82947
+ const attempt = app.db.select().from(siteCrawlAttempts).where(and54(
82815
82948
  eq64(siteCrawlAttempts.projectId, project.id),
82816
82949
  eq64(siteCrawlAttempts.runId, run.id)
82817
82950
  )).orderBy(desc27(siteCrawlAttempts.attemptNumber), desc27(siteCrawlAttempts.updatedAt)).limit(1).get();
82818
- const persistedLayout = attempt ? app.db.select().from(siteCrawlGraphLayouts).where(and53(
82951
+ const persistedLayout = attempt ? app.db.select().from(siteCrawlGraphLayouts).where(and54(
82819
82952
  eq64(siteCrawlGraphLayouts.projectId, project.id),
82820
82953
  eq64(siteCrawlGraphLayouts.runId, run.id),
82821
82954
  eq64(siteCrawlGraphLayouts.attemptId, attempt.id)
@@ -82851,14 +82984,14 @@ async function technicalAeoRoutes(app, opts) {
82851
82984
  app.get("/projects/:name/technical-aeo/runs/:runId/page-health-preview", async (request) => app.db.transaction((tx) => {
82852
82985
  const project = tx.select().from(projects).where(eq64(projects.name, request.params.name)).get();
82853
82986
  if (!project) throw notFound("Project", request.params.name);
82854
- const run = tx.select().from(runs).where(and53(
82987
+ const run = tx.select().from(runs).where(and54(
82855
82988
  eq64(runs.id, request.params.runId),
82856
82989
  eq64(runs.projectId, project.id),
82857
82990
  eq64(runs.kind, RunKinds["site-audit"]),
82858
82991
  notProbeRun()
82859
82992
  )).get();
82860
82993
  if (!run) throw notFound("Site audit run", request.params.runId);
82861
- const attempt = tx.select().from(siteCrawlAttempts).where(and53(
82994
+ const attempt = tx.select().from(siteCrawlAttempts).where(and54(
82862
82995
  eq64(siteCrawlAttempts.projectId, project.id),
82863
82996
  eq64(siteCrawlAttempts.runId, run.id)
82864
82997
  )).orderBy(desc27(siteCrawlAttempts.attemptNumber), desc27(siteCrawlAttempts.updatedAt)).limit(1).get();
@@ -82874,7 +83007,7 @@ async function technicalAeoRoutes(app, opts) {
82874
83007
  if (!attempt) {
82875
83008
  return { ...base, pagesAudited: 0, examples: [] };
82876
83009
  }
82877
- const auditedWhere = and53(
83010
+ const auditedWhere = and54(
82878
83011
  eq64(siteCrawlPages.projectId, project.id),
82879
83012
  eq64(siteCrawlPages.runId, run.id),
82880
83013
  eq64(siteCrawlPages.attemptId, attempt.id),
@@ -82889,7 +83022,7 @@ async function technicalAeoRoutes(app, opts) {
82889
83022
  url: siteCrawlPages.url,
82890
83023
  auditScore: siteCrawlPages.auditScore,
82891
83024
  auditFields: siteCrawlPages.auditFields
82892
- }).from(siteCrawlPages).where(and53(
83025
+ }).from(siteCrawlPages).where(and54(
82893
83026
  auditedWhere,
82894
83027
  isNotNull5(siteCrawlPages.auditScore),
82895
83028
  lt10(siteCrawlPages.auditScore, 70)
@@ -82925,10 +83058,10 @@ async function technicalAeoRoutes(app, opts) {
82925
83058
  status: runs.status,
82926
83059
  identityKey: siteCrawlRunRequests.identityKey,
82927
83060
  effectiveOptions: siteCrawlRunRequests.effectiveOptions
82928
- }).from(runs).leftJoin(siteCrawlRunRequests, and53(
83061
+ }).from(runs).leftJoin(siteCrawlRunRequests, and54(
82929
83062
  eq64(siteCrawlRunRequests.projectId, runs.projectId),
82930
83063
  eq64(siteCrawlRunRequests.runId, runs.id)
82931
- )).where(and53(
83064
+ )).where(and54(
82932
83065
  eq64(runs.projectId, project.id),
82933
83066
  eq64(runs.kind, RunKinds["site-audit"]),
82934
83067
  inArray24(runs.status, [RunStatuses.queued, RunStatuses.running])
@@ -82984,7 +83117,7 @@ async function technicalAeoRoutes(app, opts) {
82984
83117
 
82985
83118
  // ../api-routes/src/research.ts
82986
83119
  import crypto49 from "crypto";
82987
- import { and as and54, count as count3, desc as desc28, eq as eq65, gte as gte17, lt as lt11, sql as sql25 } from "drizzle-orm";
83120
+ import { and as and55, count as count3, desc as desc28, eq as eq65, gte as gte17, lt as lt11, sql as sql25 } from "drizzle-orm";
82988
83121
  var sameLocation = (a, b) => a.label === b.label && a.city === b.city && a.region === b.region && a.country === b.country && a.timezone === b.timezone;
82989
83122
  async function researchRoutes(app, opts) {
82990
83123
  const viewerDailyRunLimit = opts.viewerDailyRunLimit ?? DEFAULT_VIEWER_RESEARCH_DAILY_RUN_LIMIT;
@@ -83012,7 +83145,7 @@ async function researchRoutes(app, opts) {
83012
83145
  const location = input.location === void 0 ? project.defaultLocation ? project.locations.find((item2) => item2.label === project.defaultLocation) ?? null : null : input.location;
83013
83146
  if (location && !project.locations.some((item2) => sameLocation(item2, location))) throw validationError("Research location must exactly match a configured project location.", { location });
83014
83147
  const requestedModel2 = input.model ?? null;
83015
- const resolvedModel = requestedModel2 ?? (project.providerModels[providerName] || adapter.defaultModel);
83148
+ const resolvedModel = requestedModel2 ?? (project.providerModels[providerName] || opts.getEffectiveProviderModels?.()[providerName] || adapter.defaultModel);
83016
83149
  adapter.modelValidationPattern.lastIndex = 0;
83017
83150
  if (!adapter.modelValidationPattern.test(resolvedModel)) throw validationError(`Invalid resolved model "${resolvedModel}" for provider "${providerName}".`, { provider: providerName, model: resolvedModel, hint: adapter.modelValidationHint });
83018
83151
  if (new Set(input.queries.map((query) => query.toLocaleLowerCase())).size !== input.queries.length) throw validationError("Research queries must be unique within a batch.");
@@ -83022,7 +83155,7 @@ async function researchRoutes(app, opts) {
83022
83155
  const initiatedBy = researchPrincipal(request);
83023
83156
  const decision = app.db.transaction((tx) => {
83024
83157
  if (input.idempotencyKey) {
83025
- const existing = tx.select().from(researchRuns).where(and54(eq65(researchRuns.projectId, project.id), eq65(researchRuns.idempotencyKey, input.idempotencyKey))).get();
83158
+ const existing = tx.select().from(researchRuns).where(and55(eq65(researchRuns.projectId, project.id), eq65(researchRuns.idempotencyKey, input.idempotencyKey))).get();
83026
83159
  if (existing) {
83027
83160
  if (existing.requestHash !== requestHash2) throw alreadyExists("Research idempotency key", input.idempotencyKey);
83028
83161
  return { reused: true, id: existing.id, shouldDispatch: existing.status === ResearchRunStatuses.queued };
@@ -83030,7 +83163,7 @@ async function researchRoutes(app, opts) {
83030
83163
  }
83031
83164
  if (initiatedBy?.kind === "user" && initiatedBy.role === UserRoles.viewer) {
83032
83165
  const { start, end, date } = utcDayBounds(now);
83033
- const used = tx.select({ value: count3() }).from(researchRuns).where(and54(
83166
+ const used = tx.select({ value: count3() }).from(researchRuns).where(and55(
83034
83167
  eq65(researchRuns.projectId, project.id),
83035
83168
  gte17(researchRuns.createdAt, start),
83036
83169
  lt11(researchRuns.createdAt, end),
@@ -83057,12 +83190,18 @@ async function researchRoutes(app, opts) {
83057
83190
  const limit = Number.isInteger(requested) && requested > 0 ? Math.min(requested, 100) : 20;
83058
83191
  const runs2 = app.db.select().from(researchRuns).where(eq65(researchRuns.projectId, project.id)).orderBy(desc28(researchRuns.createdAt)).limit(limit).all().map(serializeRun);
83059
83192
  const configured = new Set(opts.configuredProviderNames ?? []);
83060
- const providers = (opts.providerAdapters ?? []).filter((adapter) => adapter.mode === "api" && !isBrowserProvider(adapter.name) && configured.has(adapter.name)).map((adapter) => ({
83061
- name: adapter.name,
83062
- displayName: adapter.displayName,
83063
- modelConfigurable: adapter.modelConfigurable,
83064
- defaultModel: project.providerModels[adapter.name] || adapter.defaultModel,
83065
- knownModels: adapter.knownModels.map((model) => ({ id: model.id, displayName: model.displayName }))
83193
+ const effectiveModels2 = opts.getEffectiveProviderModels?.() ?? {};
83194
+ const providers = await Promise.all((opts.providerAdapters ?? []).filter((adapter) => adapter.mode === "api" && !isBrowserProvider(adapter.name) && configured.has(adapter.name)).map(async (adapter) => {
83195
+ const defaultModel = project.providerModels[adapter.name] || effectiveModels2[adapter.name] || adapter.defaultModel;
83196
+ const discovered = opts.getProviderModels ? await opts.getProviderModels(adapter.name) : [];
83197
+ const models = discovered.length ? discovered : adapter.knownModels;
83198
+ return {
83199
+ name: adapter.name,
83200
+ displayName: adapter.displayName,
83201
+ modelConfigurable: adapter.modelConfigurable,
83202
+ defaultModel,
83203
+ knownModels: [...new Map([{ id: defaultModel, displayName: defaultModel }, ...models].map((model) => [model.id, { id: model.id, displayName: model.displayName }])).values()]
83204
+ };
83066
83205
  }));
83067
83206
  return { runs: runs2, providers };
83068
83207
  });
@@ -83072,7 +83211,7 @@ async function researchRoutes(app, opts) {
83072
83211
  });
83073
83212
  }
83074
83213
  function getDetail(app, projectId, id) {
83075
- const row = app.db.select().from(researchRuns).where(and54(eq65(researchRuns.id, id), eq65(researchRuns.projectId, projectId))).get();
83214
+ const row = app.db.select().from(researchRuns).where(and55(eq65(researchRuns.id, id), eq65(researchRuns.projectId, projectId))).get();
83076
83215
  if (!row) throw notFound("Research run", id);
83077
83216
  const queries2 = app.db.select().from(researchRunQueries).where(eq65(researchRunQueries.researchRunId, id)).orderBy(researchRunQueries.position).all().map(serializeQuery);
83078
83217
  return { ...serializeRun(row), queries: queries2 };
@@ -83102,7 +83241,7 @@ function utcDayBounds(now) {
83102
83241
 
83103
83242
  // ../api-routes/src/simple-measurement-definitions.ts
83104
83243
  import crypto50 from "crypto";
83105
- import { and as and55, eq as eq66 } from "drizzle-orm";
83244
+ import { and as and56, eq as eq66 } from "drizzle-orm";
83106
83245
  function captureSimpleMeasurementDefinition(db, input) {
83107
83246
  return db.transaction((tx) => {
83108
83247
  const run = tx.select({
@@ -83110,7 +83249,7 @@ function captureSimpleMeasurementDefinition(db, input) {
83110
83249
  trigger: runs.trigger,
83111
83250
  status: runs.status,
83112
83251
  measurementPlanVersionId: runs.measurementPlanVersionId
83113
- }).from(runs).where(and55(eq66(runs.projectId, input.projectId), eq66(runs.id, input.runId))).get();
83252
+ }).from(runs).where(and56(eq66(runs.projectId, input.projectId), eq66(runs.id, input.runId))).get();
83114
83253
  if (!run) throw notFound("Run", input.runId);
83115
83254
  if (run.kind !== RunKinds["answer-visibility"] || run.trigger === RunTriggers.probe || run.measurementPlanVersionId !== null) {
83116
83255
  return null;
@@ -83120,7 +83259,7 @@ function captureSimpleMeasurementDefinition(db, input) {
83120
83259
  if (definition.queries.some((query) => query.queryClass !== (classifier?.classify(query.queryText) ?? null))) {
83121
83260
  throw validationError("Query classes must match the captured identity and text.");
83122
83261
  }
83123
- const existing = tx.select().from(simpleMeasurementDefinitions).where(and55(eq66(simpleMeasurementDefinitions.projectId, input.projectId), eq66(simpleMeasurementDefinitions.runId, input.runId))).get();
83262
+ const existing = tx.select().from(simpleMeasurementDefinitions).where(and56(eq66(simpleMeasurementDefinitions.projectId, input.projectId), eq66(simpleMeasurementDefinitions.runId, input.runId))).get();
83124
83263
  if (existing) {
83125
83264
  const frozen = simpleMeasurementDefinitionSchema.parse(existing.definition);
83126
83265
  const replayDefinition = {
@@ -83307,6 +83446,7 @@ async function apiRoutes(app, opts) {
83307
83446
  briefPromptVersion: opts.briefPromptVersion
83308
83447
  });
83309
83448
  await api.register(settingsRoutes, {
83449
+ getProviderModels: opts.getProviderModels,
83310
83450
  providerSummary: opts.providerSummary,
83311
83451
  providerAdapters: opts.providerAdapters,
83312
83452
  onProviderUpdate: opts.onProviderUpdate,
@@ -83429,6 +83569,8 @@ async function apiRoutes(app, opts) {
83429
83569
  embedQueries: opts.embedQueries
83430
83570
  });
83431
83571
  await api.register(researchRoutes, {
83572
+ getProviderModels: opts.getProviderModels,
83573
+ getEffectiveProviderModels: opts.getEffectiveProviderModels,
83432
83574
  providerAdapters: opts.providerAdapters,
83433
83575
  configuredProviderNames: opts.providerSummary?.filter((provider) => provider.configured).map((provider) => provider.name),
83434
83576
  onResearchRunRequested: opts.onResearchRunRequested,
@@ -83915,14 +84057,14 @@ var IntelligenceService = class {
83915
84057
  * Returns the analysis result for the coordinator to inspect (e.g. for webhook dispatch).
83916
84058
  */
83917
84059
  analyzeAndPersist(runId, projectId) {
83918
- const currentRunRecord = this.db.select().from(runs).where(and56(eq67(runs.id, runId), ...analyzableRunPredicates(projectId))).get();
84060
+ const currentRunRecord = this.db.select().from(runs).where(and57(eq67(runs.id, runId), ...analyzableRunPredicates(projectId))).get();
83919
84061
  if (!currentRunRecord) {
83920
84062
  log.info("intelligence.skip", { runId, reason: "run not eligible for analysis" });
83921
84063
  return null;
83922
84064
  }
83923
84065
  const currentLocation = currentRunRecord.location ?? null;
83924
84066
  const recentRuns = this.db.select().from(runs).where(
83925
- and56(
84067
+ and57(
83926
84068
  ...analyzableRunPredicates(projectId),
83927
84069
  currentLocation === null ? isNull12(runs.location) : eq67(runs.location, currentLocation),
83928
84070
  // Never look forward. The window is this run's own past, so
@@ -84015,7 +84157,7 @@ var IntelligenceService = class {
84015
84157
  }
84016
84158
  const windowStart = runRow.startedAt ?? runRow.createdAt;
84017
84159
  const windowEnd = runRow.finishedAt ?? (/* @__PURE__ */ new Date()).toISOString();
84018
- const selected = this.db.select().from(gbpLocations).where(and56(
84160
+ const selected = this.db.select().from(gbpLocations).where(and57(
84019
84161
  eq67(gbpLocations.projectId, projectId),
84020
84162
  eq67(gbpLocations.selected, true),
84021
84163
  gte18(gbpLocations.syncedAt, windowStart),
@@ -84052,12 +84194,12 @@ var IntelligenceService = class {
84052
84194
  }
84053
84195
  /** Build the per-location signal bundle the GBP analyzer consumes. */
84054
84196
  buildGbpLocationSignals(projectId, locationName, displayName, fallbackDate) {
84055
- const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and56(eq67(gbpDailyMetrics.projectId, projectId), eq67(gbpDailyMetrics.locationName, locationName))).all();
84056
- const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and56(eq67(gbpPlaceActions.projectId, projectId), eq67(gbpPlaceActions.locationName, locationName))).all();
84057
- const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and56(eq67(gbpLodgingSnapshots.projectId, projectId), eq67(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc29(gbpLodgingSnapshots.syncedAt)).limit(1).get();
84058
- const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(and56(eq67(gbpLocations.projectId, projectId), eq67(gbpLocations.locationName, locationName))).get();
84197
+ const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and57(eq67(gbpDailyMetrics.projectId, projectId), eq67(gbpDailyMetrics.locationName, locationName))).all();
84198
+ const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and57(eq67(gbpPlaceActions.projectId, projectId), eq67(gbpPlaceActions.locationName, locationName))).all();
84199
+ const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and57(eq67(gbpLodgingSnapshots.projectId, projectId), eq67(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc29(gbpLodgingSnapshots.syncedAt)).limit(1).get();
84200
+ const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(and57(eq67(gbpLocations.projectId, projectId), eq67(gbpLocations.locationName, locationName))).get();
84059
84201
  const descriptionMissing = !(ownerRow?.description ?? "").trim();
84060
- const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and56(eq67(gbpPlaceDetails.projectId, projectId), eq67(gbpPlaceDetails.locationName, locationName))).orderBy(desc29(gbpPlaceDetails.syncedAt)).limit(1).get();
84202
+ const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and57(eq67(gbpPlaceDetails.projectId, projectId), eq67(gbpPlaceDetails.locationName, locationName))).orderBy(desc29(gbpPlaceDetails.syncedAt)).limit(1).get();
84061
84203
  const placesAmenities = placeRow ? extractPlaceAmenities(placeRow.attributes) : [];
84062
84204
  const summary2 = buildGbpSummary({
84063
84205
  locationName,
@@ -84093,7 +84235,7 @@ var IntelligenceService = class {
84093
84235
  /** Build the month-over-month keyword series for a location from the
84094
84236
  * accumulating gbp_keyword_monthly table (latest complete month vs prior). */
84095
84237
  buildGbpKeywordTrend(projectId, locationName) {
84096
- const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and56(eq67(gbpKeywordMonthly.projectId, projectId), eq67(gbpKeywordMonthly.locationName, locationName))).all();
84238
+ const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and57(eq67(gbpKeywordMonthly.projectId, projectId), eq67(gbpKeywordMonthly.locationName, locationName))).all();
84097
84239
  if (rows.length === 0) return { recentMonth: null, priorMonth: null, points: [] };
84098
84240
  const months = [...new Set(rows.map((r) => r.month))].sort().reverse();
84099
84241
  const recentMonth = months[0] ?? null;
@@ -84124,7 +84266,7 @@ var IntelligenceService = class {
84124
84266
  */
84125
84267
  persistGbpInsights(runId, projectId, gbpInsights, coveredLocationNames) {
84126
84268
  const covered = new Set(coveredLocationNames);
84127
- const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and56(eq67(insights.projectId, projectId), eq67(insights.provider, GBP_INSIGHT_PROVIDER))).all();
84269
+ const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and57(eq67(insights.projectId, projectId), eq67(insights.provider, GBP_INSIGHT_PROVIDER))).all();
84128
84270
  const staleIds = [];
84129
84271
  const dismissedSlots = /* @__PURE__ */ new Set();
84130
84272
  for (const row of existing) {
@@ -84233,7 +84375,7 @@ var IntelligenceService = class {
84233
84375
  }
84234
84376
  sinceTimestamp = parsed;
84235
84377
  }
84236
- const allRuns = this.db.select().from(runs).where(and56(...analyzableRunPredicates(project.id))).orderBy(asc12(runs.finishedAt)).all();
84378
+ const allRuns = this.db.select().from(runs).where(and57(...analyzableRunPredicates(project.id))).orderBy(asc12(runs.finishedAt)).all();
84237
84379
  const measuredRunIds = this.runIdsWithSnapshots(allRuns.map((r) => r.id));
84238
84380
  const measuredRuns = allRuns.filter((r) => measuredRunIds.has(r.id));
84239
84381
  let startIdx = 0;
@@ -84396,7 +84538,7 @@ var IntelligenceService = class {
84396
84538
  clicks: gscSearchData.clicks,
84397
84539
  impressions: gscSearchData.impressions,
84398
84540
  position: gscSearchData.position
84399
- }).from(gscSearchData).where(and56(
84541
+ }).from(gscSearchData).where(and57(
84400
84542
  eq67(gscSearchData.projectId, projectId),
84401
84543
  gte18(gscSearchData.date, window.startDate),
84402
84544
  lte15(gscSearchData.date, window.endDate)
@@ -84457,7 +84599,7 @@ var IntelligenceService = class {
84457
84599
  );
84458
84600
  const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
84459
84601
  const recentRunRows = this.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
84460
- and56(
84602
+ and57(
84461
84603
  eq67(runs.projectId, projectId),
84462
84604
  eq67(runs.kind, RunKinds["answer-visibility"]),
84463
84605
  or14(eq67(runs.status, "completed"), eq67(runs.status, "partial")),
@@ -84480,7 +84622,7 @@ var IntelligenceService = class {
84480
84622
  const haveHistory = recentRunIds.length > 0;
84481
84623
  const priorRegressionsByPair = /* @__PURE__ */ new Map();
84482
84624
  if (haveHistory) {
84483
- const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and56(eq67(insights.type, "regression"), inArray25(insights.runId, recentRunIds))).all();
84625
+ const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and57(eq67(insights.type, "regression"), inArray25(insights.runId, recentRunIds))).all();
84484
84626
  const regressionGroups = /* @__PURE__ */ new Map();
84485
84627
  for (const row of priorRows) {
84486
84628
  if (!row.runId) continue;
@@ -84670,6 +84812,7 @@ export {
84670
84812
  queueRunIfProjectIdle,
84671
84813
  resolveMeasurementSitemapTarget,
84672
84814
  nextRunFromCron,
84815
+ nextRunFromSchedule,
84673
84816
  resolveWebhookTarget,
84674
84817
  deliverWebhook,
84675
84818
  redactNotificationUrl,