@ainyc/canonry 4.46.1 → 4.50.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.
@@ -5,7 +5,7 @@ import {
5
5
  loadConfig,
6
6
  loadConfigRaw,
7
7
  saveConfigPatch
8
- } from "./chunk-PRNO52YC.js";
8
+ } from "./chunk-MYDJ25GO.js";
9
9
  import {
10
10
  DEFAULT_RUN_HISTORY_LIMIT,
11
11
  IntelligenceService,
@@ -80,10 +80,13 @@ import {
80
80
  smoothedRunDelta,
81
81
  trafficSources,
82
82
  usageCounters
83
- } from "./chunk-K2TOOQ4F.js";
83
+ } from "./chunk-M4USKXJ4.js";
84
84
  import {
85
85
  AGENT_MEMORY_VALUE_MAX_BYTES,
86
86
  AGENT_PROVIDER_IDS,
87
+ AI_ENGINE_DOMAINS,
88
+ AI_ENGINE_SELF_DOMAINS,
89
+ AI_PROVIDER_INFRA_DOMAINS,
87
90
  AgentProviderIds,
88
91
  AppError,
89
92
  CcReleaseSyncStatuses,
@@ -110,24 +113,50 @@ import {
110
113
  TrafficSourceAuthModes,
111
114
  TrafficSourceStatuses,
112
115
  TrafficSourceTypes,
116
+ VERTEX_AI_SEARCH_PROXY_DOMAIN,
113
117
  VerificationStatuses,
114
118
  absolutizeProjectUrl,
115
119
  actionConfidenceLabel,
116
120
  agentBusy,
117
121
  agentMemoryDeleteRequestSchema,
118
122
  agentMemoryUpsertRequestSchema,
123
+ agentProvidersResponseDtoSchema,
124
+ auditLogEntrySchema,
119
125
  authInvalid,
120
126
  authRequired,
127
+ backlinkHistoryEntrySchema,
128
+ backlinkListResponseSchema,
129
+ backlinkSummaryDtoSchema,
130
+ backlinksInstallResultDtoSchema,
131
+ backlinksInstallStatusDtoSchema,
132
+ bingConnectResponseDtoSchema,
133
+ bingCoverageSnapshotDtoSchema,
134
+ bingCoverageSummaryDtoSchema,
135
+ bingIndexingRequestResponseDtoSchema,
136
+ bingKeywordStatsDtoSchema,
137
+ bingSetSiteResponseDtoSchema,
138
+ bingSitesResponseDtoSchema,
139
+ bingStatusDtoSchema,
140
+ bingUrlInspectionDtoSchema,
121
141
  brandKeyFromText,
122
142
  brandLabelFromDomain,
123
143
  buildRunErrorFromMessages,
124
144
  categorizeSource,
125
145
  categoryLabel,
146
+ ccAvailableReleaseSchema,
147
+ ccCachedReleaseSchema,
148
+ ccReleaseSyncDtoSchema,
149
+ cdpStatusDtoSchema,
126
150
  citationStateSchema,
127
151
  citationStateToCited,
152
+ citationVisibilityResponseSchema,
128
153
  clusterByCosine,
129
154
  competitorBatchRequestSchema,
155
+ competitorDtoSchema,
130
156
  contentActionLabel,
157
+ contentGapsResponseDtoSchema,
158
+ contentSourcesResponseDtoSchema,
159
+ contentTargetsResponseDtoSchema,
131
160
  dedupeReportActions,
132
161
  dedupeReportOpportunities,
133
162
  deliveryFailed,
@@ -135,8 +164,13 @@ import {
135
164
  deltaTone,
136
165
  determineAnswerMentioned,
137
166
  discoveryBucketSchema,
167
+ discoveryPromotePreviewSchema,
138
168
  discoveryPromoteRequestSchema,
169
+ discoveryPromoteResultSchema,
139
170
  discoveryRunRequestSchema,
171
+ discoverySessionDetailDtoSchema,
172
+ discoverySessionDtoSchema,
173
+ doctorReportSchema,
140
174
  effectiveBrandNames,
141
175
  effectiveDomains,
142
176
  emptyCitationVisibility,
@@ -148,12 +182,29 @@ import {
148
182
  formatIsoDate,
149
183
  formatNumber,
150
184
  formatRatio,
185
+ ga4AiReferralHistoryEntrySchema,
186
+ ga4SessionHistoryEntrySchema,
187
+ ga4SocialReferralHistoryEntrySchema,
188
+ ga4StatusDtoSchema,
189
+ ga4SyncResponseDtoSchema,
151
190
  getProviderLocationHandling,
191
+ googleConnectionDtoSchema,
192
+ gscCoverageSnapshotDtoSchema,
193
+ gscCoverageSummaryDtoSchema,
194
+ gscDeindexedRowSchema,
195
+ gscPerformanceDailyDtoSchema,
196
+ gscSearchDataDtoSchema,
197
+ gscSiteListResponseDtoSchema,
198
+ gscSitemapListResponseDtoSchema,
199
+ gscUrlInspectionDtoSchema,
152
200
  hasLocationLabel,
201
+ indexingRequestResponseDtoSchema,
153
202
  internalError,
154
203
  isAgentProviderId,
155
204
  isBrowserProvider,
205
+ keywordDtoSchema,
156
206
  keywordGenerateRequestSchema,
207
+ latestProjectRunDtoSchema,
157
208
  locationContextSchema,
158
209
  mentionStateFromAnswerMentioned,
159
210
  missingDependency,
@@ -162,12 +213,16 @@ import {
162
213
  normalizeUrlPath,
163
214
  notFound,
164
215
  notImplemented,
216
+ notificationDtoSchema,
165
217
  parseRunError,
166
218
  parseWindow,
167
219
  pickClusterRepresentative,
168
220
  projectConfigSchema,
221
+ projectDtoSchema,
222
+ projectReportDtoSchema,
169
223
  projectUpsertRequestSchema,
170
224
  providerError,
225
+ queryDtoSchema,
171
226
  queryGenerateRequestSchema,
172
227
  registrableDomain,
173
228
  reportActionCategoryLabel,
@@ -178,22 +233,47 @@ import {
178
233
  resolveConfigSpecQueries,
179
234
  resolveLocations,
180
235
  resolveSnapshotRequestQueries,
236
+ runDetailDtoSchema,
237
+ runDtoSchema,
181
238
  runInProgress,
182
239
  runNotCancellable,
183
240
  runTriggerRequestSchema,
184
241
  schedulableRunKindSchema,
242
+ scheduleDtoSchema,
185
243
  scheduleUpsertRequestSchema,
186
244
  serializeRunError,
245
+ settingsDtoSchema,
246
+ snapshotDiffResponseSchema,
247
+ snapshotListResponseSchema,
248
+ snapshotReportSchema,
187
249
  snapshotRequestSchema,
188
250
  summarizeCheckResults,
251
+ trafficBackfillResponseSchema,
189
252
  trafficConnectVercelRequestSchema,
190
253
  trafficConnectWordpressRequestSchema,
254
+ trafficEventsResponseSchema,
255
+ trafficSourceDetailDtoSchema,
256
+ trafficSourceDtoSchema,
257
+ trafficSourceListResponseSchema,
258
+ trafficStatusResponseSchema,
259
+ trafficSyncResponseSchema,
191
260
  unsupportedKind,
192
261
  validationError,
193
262
  visibilityStateFromAnswerMentioned,
194
263
  windowCutoff,
195
- wordpressEnvSchema
196
- } from "./chunk-MM3A2CJK.js";
264
+ wordpressAuditPageDtoSchema,
265
+ wordpressBulkMetaResultDtoSchema,
266
+ wordpressDiffDtoSchema,
267
+ wordpressEnvSchema,
268
+ wordpressManualAssistDtoSchema,
269
+ wordpressOnboardResultDtoSchema,
270
+ wordpressPageDetailDtoSchema,
271
+ wordpressPageSummaryDtoSchema,
272
+ wordpressSchemaBlockDtoSchema,
273
+ wordpressSchemaDeployResultDtoSchema,
274
+ wordpressSchemaStatusResultDtoSchema,
275
+ wordpressStatusDtoSchema
276
+ } from "./chunk-QIG3TKL4.js";
197
277
 
198
278
  // src/telemetry.ts
199
279
  import crypto from "crypto";
@@ -559,7 +639,10 @@ import { eq as eq3, sql as sql2 } from "drizzle-orm";
559
639
 
560
640
  // ../api-routes/src/helpers.ts
561
641
  import crypto3 from "crypto";
562
- import { eq as eq2, sql } from "drizzle-orm";
642
+ import { eq as eq2, ne, sql } from "drizzle-orm";
643
+ function notProbeRun() {
644
+ return ne(runs.trigger, RunTriggers.probe);
645
+ }
563
646
  function resolveProject(db, name) {
564
647
  const project = db.select().from(projects).where(eq2(projects.name, name)).get();
565
648
  if (!project) {
@@ -2262,7 +2345,7 @@ function normalizeCompetitorList2(domains) {
2262
2345
  }
2263
2346
 
2264
2347
  // ../api-routes/src/history.ts
2265
- import { eq as eq9, desc as desc2, inArray as inArray2 } from "drizzle-orm";
2348
+ import { and as and4, eq as eq9, desc as desc2, inArray as inArray2 } from "drizzle-orm";
2266
2349
 
2267
2350
  // ../api-routes/src/notification-redaction.ts
2268
2351
  var REDACTED_URL = {
@@ -2319,7 +2402,7 @@ async function historyRoutes(app) {
2319
2402
  const project = resolveProject(app.db, request.params.name);
2320
2403
  const limit = parseInt(request.query.limit ?? "50", 10);
2321
2404
  const offset = parseInt(request.query.offset ?? "0", 10);
2322
- const projectRuns = app.db.select({ id: runs.id }).from(runs).where(eq9(runs.projectId, project.id)).all();
2405
+ const projectRuns = app.db.select({ id: runs.id }).from(runs).where(and4(eq9(runs.projectId, project.id), notProbeRun())).all();
2323
2406
  if (projectRuns.length === 0) {
2324
2407
  return reply.send({ snapshots: [], total: 0 });
2325
2408
  }
@@ -2368,7 +2451,7 @@ async function historyRoutes(app) {
2368
2451
  app.get("/projects/:name/timeline", async (request, reply) => {
2369
2452
  const project = resolveProject(app.db, request.params.name);
2370
2453
  const projectQueries = app.db.select().from(queries).where(eq9(queries.projectId, project.id)).all();
2371
- const projectRuns = app.db.select().from(runs).where(eq9(runs.projectId, project.id)).orderBy(runs.createdAt).all();
2454
+ const projectRuns = app.db.select().from(runs).where(and4(eq9(runs.projectId, project.id), notProbeRun())).orderBy(runs.createdAt).all();
2372
2455
  if (projectRuns.length === 0 || projectQueries.length === 0) {
2373
2456
  return reply.send([]);
2374
2457
  }
@@ -2553,13 +2636,13 @@ function formatAuditEntry(row) {
2553
2636
  }
2554
2637
 
2555
2638
  // ../api-routes/src/analytics.ts
2556
- import { eq as eq10, desc as desc3, inArray as inArray3 } from "drizzle-orm";
2639
+ import { and as and5, eq as eq10, desc as desc3, inArray as inArray3 } from "drizzle-orm";
2557
2640
  async function analyticsRoutes(app) {
2558
2641
  app.get("/projects/:name/analytics/metrics", async (request, reply) => {
2559
2642
  const project = resolveProject(app.db, request.params.name);
2560
2643
  const window = parseWindow(request.query.window);
2561
2644
  const cutoff = windowCutoff(window);
2562
- const projectRuns = app.db.select().from(runs).where(eq10(runs.projectId, project.id)).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
2645
+ const projectRuns = app.db.select().from(runs).where(and5(eq10(runs.projectId, project.id), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
2563
2646
  if (projectRuns.length === 0) {
2564
2647
  return reply.send({
2565
2648
  window,
@@ -2607,14 +2690,14 @@ async function analyticsRoutes(app) {
2607
2690
  const project = resolveProject(app.db, request.params.name);
2608
2691
  const window = parseWindow(request.query.window);
2609
2692
  const cutoff = windowCutoff(window);
2610
- const completedRuns = app.db.select().from(runs).where(eq10(runs.projectId, project.id)).orderBy(desc3(runs.createdAt), desc3(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial");
2693
+ const completedRuns = app.db.select().from(runs).where(and5(eq10(runs.projectId, project.id), notProbeRun())).orderBy(desc3(runs.createdAt), desc3(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial");
2611
2694
  const latestGroup = groupRunsByCreatedAt(completedRuns)[0] ?? [];
2612
2695
  const latestGroupRunIds = latestGroup.map((r) => r.id);
2613
2696
  const latestRun = pickGroupRepresentative(latestGroup);
2614
2697
  if (!latestRun) {
2615
2698
  return reply.send({ cited: [], gap: [], uncited: [], mentionedQueries: [], mentionGap: [], notMentioned: [], runId: "", window });
2616
2699
  }
2617
- const windowRuns = app.db.select().from(runs).where(eq10(runs.projectId, project.id)).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
2700
+ const windowRuns = app.db.select().from(runs).where(and5(eq10(runs.projectId, project.id), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
2618
2701
  const windowRunIds = windowRuns.map((r) => r.id);
2619
2702
  const runIdToCreatedAt = new Map(windowRuns.map((r) => [r.id, r.createdAt]));
2620
2703
  const consistencyMap = /* @__PURE__ */ new Map();
@@ -2730,7 +2813,7 @@ async function analyticsRoutes(app) {
2730
2813
  const project = resolveProject(app.db, request.params.name);
2731
2814
  const window = parseWindow(request.query.window);
2732
2815
  const cutoff = windowCutoff(window);
2733
- const windowRuns = app.db.select().from(runs).where(eq10(runs.projectId, project.id)).orderBy(desc3(runs.createdAt), desc3(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
2816
+ const windowRuns = app.db.select().from(runs).where(and5(eq10(runs.projectId, project.id), notProbeRun())).orderBy(desc3(runs.createdAt), desc3(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
2734
2817
  if (windowRuns.length === 0) {
2735
2818
  return reply.send({ overall: [], byQuery: {}, runId: "", window });
2736
2819
  }
@@ -2764,16 +2847,10 @@ async function analyticsRoutes(app) {
2764
2847
  return reply.send({ overall, byQuery, runId: latestRunId, window });
2765
2848
  });
2766
2849
  }
2767
- var PROVIDER_INFRA_DOMAINS = /* @__PURE__ */ new Set([
2768
- "vertexaisearch.cloud.google.com",
2769
- "openai.com",
2770
- "anthropic.com",
2771
- "googleapis.com"
2772
- ]);
2773
2850
  function isProviderInfraDomain(uri) {
2774
2851
  try {
2775
2852
  const host = new URL(uri).hostname.toLowerCase();
2776
- for (const blocked of PROVIDER_INFRA_DOMAINS) {
2853
+ for (const blocked of AI_PROVIDER_INFRA_DOMAINS) {
2777
2854
  if (host === blocked || host.endsWith(`.${blocked}`)) return true;
2778
2855
  }
2779
2856
  } catch {
@@ -2900,7 +2977,7 @@ function buildCategoryCounts(counts) {
2900
2977
  }
2901
2978
 
2902
2979
  // ../api-routes/src/intelligence.ts
2903
- import { eq as eq11, desc as desc4, and as and4, inArray as inArray4 } from "drizzle-orm";
2980
+ import { eq as eq11, desc as desc4, and as and6, inArray as inArray4 } from "drizzle-orm";
2904
2981
  function emptyHealthSnapshot(projectId) {
2905
2982
  return {
2906
2983
  id: `no-data:${projectId}`,
@@ -2989,7 +3066,7 @@ async function intelligenceRoutes(app) {
2989
3066
  if (request.query.runId) {
2990
3067
  conditions.push(eq11(insights.runId, request.query.runId));
2991
3068
  }
2992
- const rows = app.db.select().from(insights).where(conditions.length === 1 ? conditions[0] : and4(...conditions)).orderBy(desc4(insights.createdAt)).all();
3069
+ const rows = app.db.select().from(insights).where(conditions.length === 1 ? conditions[0] : and6(...conditions)).orderBy(desc4(insights.createdAt)).all();
2993
3070
  const showDismissed = request.query.dismissed === "true";
2994
3071
  const result = rows.filter((r) => showDismissed || !r.dismissed).map(mapInsightRow);
2995
3072
  return reply.send(result);
@@ -3013,15 +3090,18 @@ async function intelligenceRoutes(app) {
3013
3090
  });
3014
3091
  app.get("/projects/:name/health/latest", async (request, reply) => {
3015
3092
  const project = resolveProject(app.db, request.params.name);
3016
- const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and4(
3093
+ const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and6(
3017
3094
  eq11(runs.projectId, project.id),
3018
3095
  eq11(runs.kind, RunKinds["answer-visibility"]),
3019
- inArray4(runs.status, [RunStatuses.completed, RunStatuses.partial])
3096
+ inArray4(runs.status, [RunStatuses.completed, RunStatuses.partial]),
3097
+ // Health-latest is the dashboard headline; probe runs must not
3098
+ // displace the most recent real visibility sweep.
3099
+ notProbeRun()
3020
3100
  )).orderBy(desc4(runs.createdAt), desc4(runs.id)).all();
3021
3101
  const latestGroup = groupRunsByCreatedAt(projectVisRuns)[0] ?? [];
3022
3102
  const latestGroupRunIds = latestGroup.map((r) => r.id);
3023
3103
  if (latestGroupRunIds.length > 0) {
3024
- const groupRows = app.db.select().from(healthSnapshots).where(and4(
3104
+ const groupRows = app.db.select().from(healthSnapshots).where(and6(
3025
3105
  eq11(healthSnapshots.projectId, project.id),
3026
3106
  inArray4(healthSnapshots.runId, latestGroupRunIds)
3027
3107
  )).all();
@@ -3045,7 +3125,7 @@ async function intelligenceRoutes(app) {
3045
3125
  }
3046
3126
 
3047
3127
  // ../api-routes/src/report.ts
3048
- import { and as and6, desc as desc6, eq as eq13, gte, inArray as inArray6, lt, lte, ne, or as or3, sql as sql5 } from "drizzle-orm";
3128
+ import { and as and8, desc as desc6, eq as eq13, gte, inArray as inArray6, lt, lte, ne as ne2, or as or3, sql as sql5 } from "drizzle-orm";
3049
3129
 
3050
3130
  // ../api-routes/src/report-renderer.ts
3051
3131
  var COLORS = {
@@ -5285,7 +5365,7 @@ function renderReportHtml(report, opts = {}) {
5285
5365
  }
5286
5366
 
5287
5367
  // ../api-routes/src/content-data.ts
5288
- import { and as and5, eq as eq12, desc as desc5, inArray as inArray5 } from "drizzle-orm";
5368
+ import { and as and7, eq as eq12, desc as desc5, inArray as inArray5 } from "drizzle-orm";
5289
5369
  var RECENT_RUNS_WINDOW = 5;
5290
5370
  function loadOrchestratorInput(db, project, locationFilter = void 0) {
5291
5371
  const projectId = project.id;
@@ -5409,13 +5489,16 @@ function listCompetitorDomains(db, projectId) {
5409
5489
  }
5410
5490
  function listRecentAnswerVisibilityRunIds(db, projectId, limit, locationFilter) {
5411
5491
  const rows = db.select({ id: runs.id, location: runs.location }).from(runs).where(
5412
- and5(
5492
+ and7(
5413
5493
  eq12(runs.projectId, projectId),
5414
5494
  eq12(runs.kind, RunKinds["answer-visibility"]),
5415
5495
  // Queued/running/failed/cancelled runs may have partial or no
5416
5496
  // snapshots; including them risks pointing latestRunId at a run with
5417
5497
  // no usable evidence.
5418
- inArray5(runs.status, [RunStatuses.completed, RunStatuses.partial])
5498
+ inArray5(runs.status, [RunStatuses.completed, RunStatuses.partial]),
5499
+ // Probe runs are operator/agent test runs; they must not poison the
5500
+ // recent-runs window the content engine uses to recommend actions.
5501
+ notProbeRun()
5419
5502
  )
5420
5503
  ).orderBy(desc5(runs.createdAt)).all();
5421
5504
  const filtered = locationFilter === void 0 ? rows : rows.filter((r) => (r.location ?? null) === locationFilter);
@@ -5776,7 +5859,7 @@ function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trac
5776
5859
  }
5777
5860
  function buildGaSection(db, projectId) {
5778
5861
  const windowSummary = db.select().from(gaTrafficWindowSummaries).where(
5779
- and6(
5862
+ and8(
5780
5863
  eq13(gaTrafficWindowSummaries.projectId, projectId),
5781
5864
  eq13(gaTrafficWindowSummaries.windowKey, "30d")
5782
5865
  )
@@ -5954,9 +6037,9 @@ function nonSubresourceReferralPathCondition() {
5954
6037
  }
5955
6038
  function buildServerActivity(db, projectId) {
5956
6039
  const sourceRows = db.select({ id: trafficSources.id }).from(trafficSources).where(
5957
- and6(
6040
+ and8(
5958
6041
  eq13(trafficSources.projectId, projectId),
5959
- ne(trafficSources.status, TrafficSourceStatuses.archived)
6042
+ ne2(trafficSources.status, TrafficSourceStatuses.archived)
5960
6043
  )
5961
6044
  ).all();
5962
6045
  if (sourceRows.length === 0) return null;
@@ -5970,7 +6053,7 @@ function buildServerActivity(db, projectId) {
5970
6053
  const trendStart = new Date(trendStartMs).toISOString();
5971
6054
  const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
5972
6055
  db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5973
- and6(
6056
+ and8(
5974
6057
  eq13(crawlerEventsHourly.projectId, projectId),
5975
6058
  eq13(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
5976
6059
  gte(crawlerEventsHourly.tsHour, windowStartIso),
@@ -5980,9 +6063,9 @@ function buildServerActivity(db, projectId) {
5980
6063
  );
5981
6064
  const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
5982
6065
  db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
5983
- and6(
6066
+ and8(
5984
6067
  eq13(crawlerEventsHourly.projectId, projectId),
5985
- ne(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
6068
+ ne2(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
5986
6069
  gte(crawlerEventsHourly.tsHour, windowStartIso),
5987
6070
  exclusiveEnd ? lt(crawlerEventsHourly.tsHour, windowEndIso) : lte(crawlerEventsHourly.tsHour, windowEndIso)
5988
6071
  )
@@ -5990,7 +6073,7 @@ function buildServerActivity(db, projectId) {
5990
6073
  );
5991
6074
  const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
5992
6075
  db.select({ total: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
5993
- and6(
6076
+ and8(
5994
6077
  eq13(aiReferralEventsHourly.projectId, projectId),
5995
6078
  nonSubresourceReferralPathCondition(),
5996
6079
  gte(aiReferralEventsHourly.tsHour, windowStartIso),
@@ -6009,7 +6092,7 @@ function buildServerActivity(db, projectId) {
6009
6092
  verificationStatus: crawlerEventsHourly.verificationStatus,
6010
6093
  hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
6011
6094
  }).from(crawlerEventsHourly).where(
6012
- and6(
6095
+ and8(
6013
6096
  eq13(crawlerEventsHourly.projectId, projectId),
6014
6097
  gte(crawlerEventsHourly.tsHour, headlineStart),
6015
6098
  lte(crawlerEventsHourly.tsHour, headlineEnd)
@@ -6019,7 +6102,7 @@ function buildServerActivity(db, projectId) {
6019
6102
  operator: crawlerEventsHourly.operator,
6020
6103
  hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
6021
6104
  }).from(crawlerEventsHourly).where(
6022
- and6(
6105
+ and8(
6023
6106
  eq13(crawlerEventsHourly.projectId, projectId),
6024
6107
  eq13(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
6025
6108
  gte(crawlerEventsHourly.tsHour, priorStart),
@@ -6030,7 +6113,7 @@ function buildServerActivity(db, projectId) {
6030
6113
  operator: aiReferralEventsHourly.operator,
6031
6114
  hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
6032
6115
  }).from(aiReferralEventsHourly).where(
6033
- and6(
6116
+ and8(
6034
6117
  eq13(aiReferralEventsHourly.projectId, projectId),
6035
6118
  nonSubresourceReferralPathCondition(),
6036
6119
  gte(aiReferralEventsHourly.tsHour, headlineStart),
@@ -6071,7 +6154,7 @@ function buildServerActivity(db, projectId) {
6071
6154
  hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
6072
6155
  operators: sql5`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
6073
6156
  }).from(crawlerEventsHourly).where(
6074
- and6(
6157
+ and8(
6075
6158
  eq13(crawlerEventsHourly.projectId, projectId),
6076
6159
  eq13(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
6077
6160
  gte(crawlerEventsHourly.tsHour, headlineStart),
@@ -6088,7 +6171,7 @@ function buildServerActivity(db, projectId) {
6088
6171
  arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6089
6172
  landingPaths: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
6090
6173
  }).from(aiReferralEventsHourly).where(
6091
- and6(
6174
+ and8(
6092
6175
  eq13(aiReferralEventsHourly.projectId, projectId),
6093
6176
  nonSubresourceReferralPathCondition(),
6094
6177
  gte(aiReferralEventsHourly.tsHour, headlineStart),
@@ -6105,7 +6188,7 @@ function buildServerActivity(db, projectId) {
6105
6188
  arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
6106
6189
  products: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
6107
6190
  }).from(aiReferralEventsHourly).where(
6108
- and6(
6191
+ and8(
6109
6192
  eq13(aiReferralEventsHourly.projectId, projectId),
6110
6193
  nonSubresourceReferralPathCondition(),
6111
6194
  gte(aiReferralEventsHourly.tsHour, headlineStart),
@@ -6121,7 +6204,7 @@ function buildServerActivity(db, projectId) {
6121
6204
  date: sql5`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
6122
6205
  hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
6123
6206
  }).from(crawlerEventsHourly).where(
6124
- and6(
6207
+ and8(
6125
6208
  eq13(crawlerEventsHourly.projectId, projectId),
6126
6209
  eq13(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
6127
6210
  gte(crawlerEventsHourly.tsHour, trendStart),
@@ -6132,7 +6215,7 @@ function buildServerActivity(db, projectId) {
6132
6215
  date: sql5`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
6133
6216
  hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
6134
6217
  }).from(aiReferralEventsHourly).where(
6135
- and6(
6218
+ and8(
6136
6219
  eq13(aiReferralEventsHourly.projectId, projectId),
6137
6220
  nonSubresourceReferralPathCondition(),
6138
6221
  gte(aiReferralEventsHourly.tsHour, trendStart),
@@ -6207,7 +6290,7 @@ function buildIndexingHealth(db, projectId) {
6207
6290
  return null;
6208
6291
  }
6209
6292
  function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
6210
- const visibilityRuns = db.select().from(runs).where(and6(eq13(runs.projectId, projectId), eq13(runs.kind, RunKinds["answer-visibility"]))).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter);
6293
+ const visibilityRuns = db.select().from(runs).where(and8(eq13(runs.projectId, projectId), eq13(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter);
6211
6294
  const totalQueries = queryLookup.byId.size;
6212
6295
  const points = [];
6213
6296
  for (const run of visibilityRuns) {
@@ -6253,14 +6336,15 @@ function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
6253
6336
  }
6254
6337
  function buildInsightList(db, projectId, locationFilter) {
6255
6338
  const recentRunIds = db.select({ id: runs.id, location: runs.location }).from(runs).where(
6256
- and6(
6339
+ and8(
6257
6340
  eq13(runs.projectId, projectId),
6258
6341
  eq13(runs.kind, RunKinds["answer-visibility"]),
6259
- or3(eq13(runs.status, RunStatuses.completed), eq13(runs.status, RunStatuses.partial))
6342
+ or3(eq13(runs.status, RunStatuses.completed), eq13(runs.status, RunStatuses.partial)),
6343
+ notProbeRun()
6260
6344
  )
6261
6345
  ).orderBy(desc6(runs.createdAt)).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter).slice(0, INSIGHT_LOOKBACK_RUNS).map((r) => r.id);
6262
6346
  if (recentRunIds.length === 0) return [];
6263
- const rows = db.select().from(insights).where(and6(eq13(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc6(insights.createdAt)).all();
6347
+ const rows = db.select().from(insights).where(and8(eq13(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc6(insights.createdAt)).all();
6264
6348
  const severityRank = { critical: 0, high: 1, medium: 2, low: 3 };
6265
6349
  const flat = rows.filter((r) => !r.dismissed).map((r) => {
6266
6350
  const recommendation = parseJsonColumn(r.recommendation, null);
@@ -6805,7 +6889,7 @@ function buildWhatsChanged(input) {
6805
6889
  function buildProjectReport(db, projectName) {
6806
6890
  const project = resolveProject(db, projectName);
6807
6891
  const queryLookup = loadQueryLookup(db, project.id);
6808
- const allRuns = db.select().from(runs).where(eq13(runs.projectId, project.id)).orderBy(desc6(runs.createdAt), desc6(runs.id)).all();
6892
+ const allRuns = db.select().from(runs).where(and8(eq13(runs.projectId, project.id), notProbeRun())).orderBy(desc6(runs.createdAt), desc6(runs.id)).all();
6809
6893
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
6810
6894
  const completedVisRunGroups = groupRunsByCreatedAt(
6811
6895
  visibilityRuns.filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial)
@@ -7033,7 +7117,7 @@ async function reportRoutes(app) {
7033
7117
  }
7034
7118
 
7035
7119
  // ../api-routes/src/citations.ts
7036
- import { eq as eq14, inArray as inArray7 } from "drizzle-orm";
7120
+ import { and as and9, eq as eq14, inArray as inArray7 } from "drizzle-orm";
7037
7121
  async function citationRoutes(app) {
7038
7122
  app.get("/projects/:name/citations/visibility", async (request, reply) => {
7039
7123
  const project = resolveProject(app.db, request.params.name);
@@ -7042,7 +7126,7 @@ async function citationRoutes(app) {
7042
7126
  if (projectQueries.length === 0) {
7043
7127
  return reply.send(emptyCitationVisibility("no-queries"));
7044
7128
  }
7045
- const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(eq14(runs.projectId, project.id)).all();
7129
+ const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and9(eq14(runs.projectId, project.id), notProbeRun())).all();
7046
7130
  if (projectRuns.length === 0) {
7047
7131
  return reply.send(emptyCitationVisibility("no-runs-yet"));
7048
7132
  }
@@ -7198,7 +7282,7 @@ function normalizeDomain2(domain) {
7198
7282
  }
7199
7283
 
7200
7284
  // ../api-routes/src/composites.ts
7201
- import { eq as eq15, and as and7, desc as desc7, sql as sql6, like, or as or4, inArray as inArray8 } from "drizzle-orm";
7285
+ import { eq as eq15, and as and10, desc as desc7, sql as sql6, like, or as or4, inArray as inArray8 } from "drizzle-orm";
7202
7286
  var TOP_INSIGHT_LIMIT = 5;
7203
7287
  var SEARCH_HIT_HARD_LIMIT = 50;
7204
7288
  var SEARCH_SNIPPET_RADIUS = 80;
@@ -7216,7 +7300,7 @@ async function compositeRoutes(app) {
7216
7300
  const project = resolveProject(app.db, request.params.name);
7217
7301
  const filterLocation = (request.query.location ?? "").trim() || null;
7218
7302
  const sinceIso = parseSinceFilter(request.query.since);
7219
- const allRunsRaw = app.db.select().from(runs).where(eq15(runs.projectId, project.id)).orderBy(desc7(runs.createdAt), desc7(runs.id)).all();
7303
+ const allRunsRaw = app.db.select().from(runs).where(and10(eq15(runs.projectId, project.id), notProbeRun())).orderBy(desc7(runs.createdAt), desc7(runs.id)).all();
7220
7304
  const allRuns = allRunsRaw.filter((r) => runMatchesFilters(r, filterLocation, sinceIso));
7221
7305
  const totalRuns = allRuns.length;
7222
7306
  const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
@@ -7345,7 +7429,7 @@ async function compositeRoutes(app) {
7345
7429
  rawResponse: querySnapshots.rawResponse,
7346
7430
  createdAt: querySnapshots.createdAt
7347
7431
  }).from(querySnapshots).innerJoin(queries, eq15(querySnapshots.queryId, queries.id)).where(
7348
- and7(
7432
+ and10(
7349
7433
  eq15(queries.projectId, project.id),
7350
7434
  or4(
7351
7435
  sql6`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
@@ -7356,7 +7440,7 @@ async function compositeRoutes(app) {
7356
7440
  )
7357
7441
  ).orderBy(desc7(querySnapshots.createdAt)).limit(limit + 1).all());
7358
7442
  const insightMatches = app.db.select().from(insights).where(
7359
- and7(
7443
+ and10(
7360
7444
  eq15(insights.projectId, project.id),
7361
7445
  or4(
7362
7446
  like(insights.title, pattern),
@@ -7529,7 +7613,7 @@ function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
7529
7613
  // NULLIF guards the degenerate impressions=0 case (SQLite returns NULL,
7530
7614
  // which the JS coerces to 0 — caught by the impression floor anyway).
7531
7615
  avgPosition: sql6`COALESCE(SUM(${gscSearchData.position} * ${gscSearchData.impressions}) * 1.0 / NULLIF(SUM(${gscSearchData.impressions}), 0), 0)`
7532
- }).from(gscSearchData).where(and7(
7616
+ }).from(gscSearchData).where(and10(
7533
7617
  eq15(gscSearchData.projectId, projectId),
7534
7618
  sql6`${gscSearchData.date} >= ${cutoff}`,
7535
7619
  sql6`${gscSearchData.impressions} > 0`
@@ -7718,6 +7802,7 @@ function formatProject2(row) {
7718
7802
  language: row.language,
7719
7803
  tags: parseJsonColumn(row.tags, []),
7720
7804
  labels: parseJsonColumn(row.labels, {}),
7805
+ providers: parseJsonColumn(row.providers, []),
7721
7806
  locations: parseJsonColumn(row.locations, []),
7722
7807
  defaultLocation: row.defaultLocation,
7723
7808
  autoExtractBacklinks: row.autoExtractBacklinks === 1,
@@ -7864,6 +7949,141 @@ function parseLimitParam(raw) {
7864
7949
  return parsed;
7865
7950
  }
7866
7951
 
7952
+ // ../api-routes/src/openapi-schemas.ts
7953
+ import { z } from "zod";
7954
+ var SCHEMA_TABLE = {
7955
+ AgentProvidersResponseDto: agentProvidersResponseDtoSchema,
7956
+ AuditLogEntry: auditLogEntrySchema,
7957
+ BacklinkHistoryEntry: backlinkHistoryEntrySchema,
7958
+ BacklinkListResponse: backlinkListResponseSchema,
7959
+ BacklinkSummaryDto: backlinkSummaryDtoSchema,
7960
+ BacklinksInstallResultDto: backlinksInstallResultDtoSchema,
7961
+ BacklinksInstallStatusDto: backlinksInstallStatusDtoSchema,
7962
+ BingConnectResponseDto: bingConnectResponseDtoSchema,
7963
+ BingCoverageSnapshotDto: bingCoverageSnapshotDtoSchema,
7964
+ BingCoverageSummaryDto: bingCoverageSummaryDtoSchema,
7965
+ BingIndexingRequestResponseDto: bingIndexingRequestResponseDtoSchema,
7966
+ BingKeywordStatsDto: bingKeywordStatsDtoSchema,
7967
+ BingSetSiteResponseDto: bingSetSiteResponseDtoSchema,
7968
+ BingSitesResponseDto: bingSitesResponseDtoSchema,
7969
+ BingStatusDto: bingStatusDtoSchema,
7970
+ BingUrlInspectionDto: bingUrlInspectionDtoSchema,
7971
+ CcAvailableRelease: ccAvailableReleaseSchema,
7972
+ CcCachedRelease: ccCachedReleaseSchema,
7973
+ CcReleaseSyncDto: ccReleaseSyncDtoSchema,
7974
+ CdpStatusDto: cdpStatusDtoSchema,
7975
+ CitationVisibilityResponse: citationVisibilityResponseSchema,
7976
+ CompetitorDto: competitorDtoSchema,
7977
+ ContentGapsResponseDto: contentGapsResponseDtoSchema,
7978
+ ContentSourcesResponseDto: contentSourcesResponseDtoSchema,
7979
+ ContentTargetsResponseDto: contentTargetsResponseDtoSchema,
7980
+ DiscoveryPromotePreview: discoveryPromotePreviewSchema,
7981
+ DiscoveryPromoteResult: discoveryPromoteResultSchema,
7982
+ DiscoverySessionDetailDto: discoverySessionDetailDtoSchema,
7983
+ DiscoverySessionDto: discoverySessionDtoSchema,
7984
+ DoctorReportDto: doctorReportSchema,
7985
+ GA4AiReferralHistoryEntry: ga4AiReferralHistoryEntrySchema,
7986
+ GA4SessionHistoryEntry: ga4SessionHistoryEntrySchema,
7987
+ GA4SocialReferralHistoryEntry: ga4SocialReferralHistoryEntrySchema,
7988
+ GA4StatusDto: ga4StatusDtoSchema,
7989
+ GA4SyncResponseDto: ga4SyncResponseDtoSchema,
7990
+ GoogleConnectionDto: googleConnectionDtoSchema,
7991
+ GscCoverageSnapshotDto: gscCoverageSnapshotDtoSchema,
7992
+ GscCoverageSummaryDto: gscCoverageSummaryDtoSchema,
7993
+ GscDeindexedRowDto: gscDeindexedRowSchema,
7994
+ GscPerformanceDailyDto: gscPerformanceDailyDtoSchema,
7995
+ GscSearchDataDto: gscSearchDataDtoSchema,
7996
+ GscSiteListResponseDto: gscSiteListResponseDtoSchema,
7997
+ GscSitemapListResponseDto: gscSitemapListResponseDtoSchema,
7998
+ GscUrlInspectionDto: gscUrlInspectionDtoSchema,
7999
+ IndexingRequestResponseDto: indexingRequestResponseDtoSchema,
8000
+ KeywordDto: keywordDtoSchema,
8001
+ LatestProjectRunDto: latestProjectRunDtoSchema,
8002
+ LocationContext: locationContextSchema,
8003
+ NotificationDto: notificationDtoSchema,
8004
+ ProjectDto: projectDtoSchema,
8005
+ ProjectReportDto: projectReportDtoSchema,
8006
+ QueryDto: queryDtoSchema,
8007
+ RunDetailDto: runDetailDtoSchema,
8008
+ RunDto: runDtoSchema,
8009
+ ScheduleDto: scheduleDtoSchema,
8010
+ SettingsDto: settingsDtoSchema,
8011
+ SnapshotDiffResponse: snapshotDiffResponseSchema,
8012
+ SnapshotListResponse: snapshotListResponseSchema,
8013
+ SnapshotReportDto: snapshotReportSchema,
8014
+ TrafficBackfillResponse: trafficBackfillResponseSchema,
8015
+ TrafficEventsResponse: trafficEventsResponseSchema,
8016
+ TrafficSourceDetailDto: trafficSourceDetailDtoSchema,
8017
+ TrafficSourceDto: trafficSourceDtoSchema,
8018
+ TrafficSourceListResponse: trafficSourceListResponseSchema,
8019
+ TrafficStatusResponse: trafficStatusResponseSchema,
8020
+ TrafficSyncResponse: trafficSyncResponseSchema,
8021
+ WordpressAuditPageDto: wordpressAuditPageDtoSchema,
8022
+ WordpressBulkMetaResultDto: wordpressBulkMetaResultDtoSchema,
8023
+ WordpressDiffDto: wordpressDiffDtoSchema,
8024
+ WordpressManualAssistDto: wordpressManualAssistDtoSchema,
8025
+ WordpressOnboardResultDto: wordpressOnboardResultDtoSchema,
8026
+ WordpressPageDetailDto: wordpressPageDetailDtoSchema,
8027
+ WordpressPageSummaryDto: wordpressPageSummaryDtoSchema,
8028
+ WordpressSchemaBlockDto: wordpressSchemaBlockDtoSchema,
8029
+ WordpressSchemaDeployResultDto: wordpressSchemaDeployResultDtoSchema,
8030
+ WordpressSchemaStatusResultDto: wordpressSchemaStatusResultDtoSchema,
8031
+ WordpressStatusDto: wordpressStatusDtoSchema,
8032
+ // Shared envelope used by every 4xx/5xx response. Defined locally because
8033
+ // the API never returns this from a typed handler — it's emitted by the
8034
+ // global error handler. Codegen consumers reference it through the
8035
+ // ErrorEnvelope ref so error shape is part of the public contract.
8036
+ ErrorEnvelope: z.object({
8037
+ error: z.object({
8038
+ code: z.string(),
8039
+ message: z.string(),
8040
+ details: z.unknown().optional()
8041
+ })
8042
+ })
8043
+ };
8044
+ function buildComponentSchemas() {
8045
+ const out = {};
8046
+ for (const [name, schema] of Object.entries(SCHEMA_TABLE)) {
8047
+ out[name] = z.toJSONSchema(schema, { target: "openapi-3.0" });
8048
+ }
8049
+ return out;
8050
+ }
8051
+ function jsonResponse(description, schemaName) {
8052
+ return {
8053
+ description,
8054
+ content: {
8055
+ "application/json": {
8056
+ schema: { $ref: `#/components/schemas/${schemaName}` }
8057
+ }
8058
+ }
8059
+ };
8060
+ }
8061
+ function jsonArrayResponse(description, schemaName) {
8062
+ return {
8063
+ description,
8064
+ content: {
8065
+ "application/json": {
8066
+ schema: {
8067
+ type: "array",
8068
+ items: { $ref: `#/components/schemas/${schemaName}` }
8069
+ }
8070
+ }
8071
+ }
8072
+ };
8073
+ }
8074
+ function rawJsonResponse(description, schema) {
8075
+ return {
8076
+ description,
8077
+ content: {
8078
+ "application/json": { schema }
8079
+ }
8080
+ };
8081
+ }
8082
+ var looseObjectSchema = { type: "object", additionalProperties: true };
8083
+ function errorResponse(description) {
8084
+ return jsonResponse(description, "ErrorEnvelope");
8085
+ }
8086
+
7867
8087
  // ../api-routes/src/openapi.ts
7868
8088
  var stringSchema = { type: "string" };
7869
8089
  var booleanSchema = { type: "boolean" };
@@ -7996,7 +8216,7 @@ var routeCatalog = [
7996
8216
  tags: ["meta"],
7997
8217
  auth: false,
7998
8218
  responses: {
7999
- 200: { description: "OpenAPI document." }
8219
+ 200: rawJsonResponse("OpenAPI document.", looseObjectSchema)
8000
8220
  }
8001
8221
  },
8002
8222
  {
@@ -8031,8 +8251,8 @@ var routeCatalog = [
8031
8251
  }
8032
8252
  },
8033
8253
  responses: {
8034
- 200: { description: "Project updated." },
8035
- 201: { description: "Project created." }
8254
+ 200: jsonResponse("Project updated.", "ProjectDto"),
8255
+ 201: jsonResponse("Project created.", "ProjectDto")
8036
8256
  }
8037
8257
  },
8038
8258
  {
@@ -8041,7 +8261,7 @@ var routeCatalog = [
8041
8261
  summary: "List projects",
8042
8262
  tags: ["projects"],
8043
8263
  responses: {
8044
- 200: { description: "Projects returned." }
8264
+ 200: jsonArrayResponse("Projects returned.", "ProjectDto")
8045
8265
  }
8046
8266
  },
8047
8267
  {
@@ -8051,8 +8271,8 @@ var routeCatalog = [
8051
8271
  tags: ["projects"],
8052
8272
  parameters: [nameParameter],
8053
8273
  responses: {
8054
- 200: { description: "Project returned." },
8055
- 404: { description: "Project not found." }
8274
+ 200: jsonResponse("Project returned.", "ProjectDto"),
8275
+ 404: errorResponse("Project not found.")
8056
8276
  }
8057
8277
  },
8058
8278
  {
@@ -8063,7 +8283,7 @@ var routeCatalog = [
8063
8283
  parameters: [nameParameter],
8064
8284
  responses: {
8065
8285
  204: { description: "Project deleted." },
8066
- 404: { description: "Project not found." }
8286
+ 404: errorResponse("Project not found.")
8067
8287
  }
8068
8288
  },
8069
8289
  {
@@ -8074,8 +8294,9 @@ var routeCatalog = [
8074
8294
  tags: ["projects"],
8075
8295
  parameters: [nameParameter],
8076
8296
  responses: {
8077
- 200: { description: "Preview of cascade impact." },
8078
- 404: { description: "Project not found." }
8297
+ // TODO: Define `ProjectDeletePreviewDto` Zod schema in contracts and reference here.
8298
+ 200: rawJsonResponse("Preview of cascade impact.", looseObjectSchema),
8299
+ 404: errorResponse("Project not found.")
8079
8300
  }
8080
8301
  },
8081
8302
  {
@@ -8093,9 +8314,9 @@ var routeCatalog = [
8093
8314
  }
8094
8315
  },
8095
8316
  responses: {
8096
- 201: { description: "Location created." },
8097
- 400: { description: "Invalid location." },
8098
- 404: { description: "Project not found." }
8317
+ 201: jsonResponse("Location created.", "LocationContext"),
8318
+ 400: errorResponse("Invalid location."),
8319
+ 404: errorResponse("Project not found.")
8099
8320
  }
8100
8321
  },
8101
8322
  {
@@ -8105,8 +8326,9 @@ var routeCatalog = [
8105
8326
  tags: ["projects"],
8106
8327
  parameters: [nameParameter],
8107
8328
  responses: {
8108
- 200: { description: "Locations returned." },
8109
- 404: { description: "Project not found." }
8329
+ // TODO: Define `ProjectLocationsResponse` Zod schema (`{ locations: LocationContext[]; defaultLocation: string | null }`) in contracts.
8330
+ 200: rawJsonResponse("Locations returned.", looseObjectSchema),
8331
+ 404: errorResponse("Project not found.")
8110
8332
  }
8111
8333
  },
8112
8334
  {
@@ -8117,8 +8339,8 @@ var routeCatalog = [
8117
8339
  parameters: [nameParameter, locationLabelParameter],
8118
8340
  responses: {
8119
8341
  204: { description: "Location removed." },
8120
- 400: { description: "Invalid location." },
8121
- 404: { description: "Project or location not found." }
8342
+ 400: errorResponse("Invalid location."),
8343
+ 404: errorResponse("Project or location not found.")
8122
8344
  }
8123
8345
  },
8124
8346
  {
@@ -8142,9 +8364,9 @@ var routeCatalog = [
8142
8364
  }
8143
8365
  },
8144
8366
  responses: {
8145
- 200: { description: "Default location updated." },
8146
- 400: { description: "Invalid location." },
8147
- 404: { description: "Project not found." }
8367
+ 200: jsonResponse("Default location updated.", "ProjectDto"),
8368
+ 400: errorResponse("Invalid location."),
8369
+ 404: errorResponse("Project not found.")
8148
8370
  }
8149
8371
  },
8150
8372
  {
@@ -8154,8 +8376,9 @@ var routeCatalog = [
8154
8376
  tags: ["projects"],
8155
8377
  parameters: [nameParameter],
8156
8378
  responses: {
8157
- 200: { description: "Project configuration returned." },
8158
- 404: { description: "Project not found." }
8379
+ // TODO: Define an `ExportedProjectConfig` Zod schema in contracts (mirrors canonry.yaml shape).
8380
+ 200: rawJsonResponse("Project configuration returned.", looseObjectSchema),
8381
+ 404: errorResponse("Project not found.")
8159
8382
  }
8160
8383
  },
8161
8384
  {
@@ -8165,7 +8388,7 @@ var routeCatalog = [
8165
8388
  tags: ["queries"],
8166
8389
  parameters: [nameParameter],
8167
8390
  responses: {
8168
- 200: { description: "Queries returned." }
8391
+ 200: jsonArrayResponse("Queries returned.", "QueryDto")
8169
8392
  }
8170
8393
  },
8171
8394
  {
@@ -8189,7 +8412,7 @@ var routeCatalog = [
8189
8412
  }
8190
8413
  },
8191
8414
  responses: {
8192
- 200: { description: "Queries replaced." }
8415
+ 200: jsonArrayResponse("Queries replaced.", "QueryDto")
8193
8416
  }
8194
8417
  },
8195
8418
  {
@@ -8213,8 +8436,8 @@ var routeCatalog = [
8213
8436
  }
8214
8437
  },
8215
8438
  responses: {
8216
- 200: { description: "Remaining queries returned." },
8217
- 400: { description: "Invalid query delete request." }
8439
+ 200: jsonArrayResponse("Remaining queries returned.", "QueryDto"),
8440
+ 400: errorResponse("Invalid query delete request.")
8218
8441
  }
8219
8442
  },
8220
8443
  {
@@ -8238,7 +8461,7 @@ var routeCatalog = [
8238
8461
  }
8239
8462
  },
8240
8463
  responses: {
8241
- 200: { description: "Queries appended." }
8464
+ 200: jsonArrayResponse("Queries appended.", "QueryDto")
8242
8465
  }
8243
8466
  },
8244
8467
  {
@@ -8263,8 +8486,9 @@ var routeCatalog = [
8263
8486
  }
8264
8487
  },
8265
8488
  responses: {
8266
- 200: { description: "Replace preview returned." },
8267
- 404: { description: "Project not found." }
8489
+ // TODO: Add `QueriesReplacePreviewDto` Zod schema in contracts.
8490
+ 200: rawJsonResponse("Replace preview returned.", looseObjectSchema),
8491
+ 404: errorResponse("Project not found.")
8268
8492
  }
8269
8493
  },
8270
8494
  {
@@ -8289,8 +8513,8 @@ var routeCatalog = [
8289
8513
  }
8290
8514
  },
8291
8515
  responses: {
8292
- 200: { description: "Query suggestions returned." },
8293
- 501: { description: "Query generation is not available." }
8516
+ 200: rawJsonResponse("Query suggestions returned.", { type: "object", properties: { suggestions: { type: "array", items: { type: "string" } } } }),
8517
+ 501: errorResponse("Query generation is not available.")
8294
8518
  }
8295
8519
  },
8296
8520
  {
@@ -8300,7 +8524,7 @@ var routeCatalog = [
8300
8524
  tags: ["queries"],
8301
8525
  parameters: [nameParameter],
8302
8526
  responses: {
8303
- 200: { description: "Legacy keyword-shaped queries returned." }
8527
+ 200: jsonArrayResponse("Legacy keyword-shaped queries returned.", "KeywordDto")
8304
8528
  }
8305
8529
  },
8306
8530
  {
@@ -8324,7 +8548,7 @@ var routeCatalog = [
8324
8548
  }
8325
8549
  },
8326
8550
  responses: {
8327
- 200: { description: "Legacy keyword-shaped queries replaced." }
8551
+ 200: jsonArrayResponse("Legacy keyword-shaped queries replaced.", "KeywordDto")
8328
8552
  }
8329
8553
  },
8330
8554
  {
@@ -8348,8 +8572,8 @@ var routeCatalog = [
8348
8572
  }
8349
8573
  },
8350
8574
  responses: {
8351
- 200: { description: "Remaining legacy keyword-shaped queries returned." },
8352
- 400: { description: "Invalid legacy keyword delete request." }
8575
+ 200: jsonArrayResponse("Remaining legacy keyword-shaped queries returned.", "KeywordDto"),
8576
+ 400: errorResponse("Invalid legacy keyword delete request.")
8353
8577
  }
8354
8578
  },
8355
8579
  {
@@ -8373,7 +8597,7 @@ var routeCatalog = [
8373
8597
  }
8374
8598
  },
8375
8599
  responses: {
8376
- 200: { description: "Legacy keyword-shaped queries appended." }
8600
+ 200: jsonArrayResponse("Legacy keyword-shaped queries appended.", "KeywordDto")
8377
8601
  }
8378
8602
  },
8379
8603
  {
@@ -8398,8 +8622,9 @@ var routeCatalog = [
8398
8622
  }
8399
8623
  },
8400
8624
  responses: {
8401
- 200: { description: "Legacy keyword suggestions returned." },
8402
- 501: { description: "Legacy keyword generation is not available." }
8625
+ // TODO: Add `KeywordGenerateResponse` Zod schema (`{ suggestions: string[] }`) in contracts.
8626
+ 200: rawJsonResponse("Legacy keyword suggestions returned.", looseObjectSchema),
8627
+ 501: errorResponse("Legacy keyword generation is not available.")
8403
8628
  }
8404
8629
  },
8405
8630
  {
@@ -8409,7 +8634,7 @@ var routeCatalog = [
8409
8634
  tags: ["competitors"],
8410
8635
  parameters: [nameParameter],
8411
8636
  responses: {
8412
- 200: { description: "Competitors returned." }
8637
+ 200: jsonArrayResponse("Competitors returned.", "CompetitorDto")
8413
8638
  }
8414
8639
  },
8415
8640
  {
@@ -8433,7 +8658,7 @@ var routeCatalog = [
8433
8658
  }
8434
8659
  },
8435
8660
  responses: {
8436
- 200: { description: "Competitors replaced." }
8661
+ 200: jsonArrayResponse("Competitors replaced.", "CompetitorDto")
8437
8662
  }
8438
8663
  },
8439
8664
  {
@@ -8457,8 +8682,8 @@ var routeCatalog = [
8457
8682
  }
8458
8683
  },
8459
8684
  responses: {
8460
- 200: { description: "Competitors appended." },
8461
- 400: { description: "Invalid competitor append request." }
8685
+ 200: jsonArrayResponse("Competitors appended.", "CompetitorDto"),
8686
+ 400: errorResponse("Invalid competitor append request.")
8462
8687
  }
8463
8688
  },
8464
8689
  {
@@ -8482,8 +8707,8 @@ var routeCatalog = [
8482
8707
  }
8483
8708
  },
8484
8709
  responses: {
8485
- 200: { description: "Remaining competitors returned." },
8486
- 400: { description: "Invalid competitor delete request." }
8710
+ 200: jsonArrayResponse("Remaining competitors returned.", "CompetitorDto"),
8711
+ 400: errorResponse("Invalid competitor delete request.")
8487
8712
  }
8488
8713
  },
8489
8714
  {
@@ -8511,8 +8736,8 @@ var routeCatalog = [
8511
8736
  }
8512
8737
  },
8513
8738
  responses: {
8514
- 201: { description: "Run queued." },
8515
- 409: { description: "Run already in progress." }
8739
+ 201: jsonResponse("Run queued.", "RunDto"),
8740
+ 409: errorResponse("Run already in progress.")
8516
8741
  }
8517
8742
  },
8518
8743
  {
@@ -8522,7 +8747,7 @@ var routeCatalog = [
8522
8747
  tags: ["runs"],
8523
8748
  parameters: [nameParameter, limitQueryParameter],
8524
8749
  responses: {
8525
- 200: { description: "Runs returned." }
8750
+ 200: jsonArrayResponse("Runs returned.", "RunDto")
8526
8751
  }
8527
8752
  },
8528
8753
  {
@@ -8532,7 +8757,7 @@ var routeCatalog = [
8532
8757
  tags: ["runs"],
8533
8758
  parameters: [nameParameter],
8534
8759
  responses: {
8535
- 200: { description: "Latest run returned." }
8760
+ 200: jsonResponse("Latest run returned.", "LatestProjectRunDto")
8536
8761
  }
8537
8762
  },
8538
8763
  {
@@ -8541,7 +8766,7 @@ var routeCatalog = [
8541
8766
  summary: "List all runs",
8542
8767
  tags: ["runs"],
8543
8768
  responses: {
8544
- 200: { description: "Runs returned." }
8769
+ 200: jsonArrayResponse("Runs returned.", "RunDto")
8545
8770
  }
8546
8771
  },
8547
8772
  {
@@ -8563,7 +8788,8 @@ var routeCatalog = [
8563
8788
  }
8564
8789
  },
8565
8790
  responses: {
8566
- 207: { description: "Run results returned." }
8791
+ // TODO: Add `TriggerAllRunsResponse` Zod schema in contracts.
8792
+ 207: rawJsonResponse("Run results returned.", looseObjectSchema)
8567
8793
  }
8568
8794
  },
8569
8795
  {
@@ -8573,8 +8799,8 @@ var routeCatalog = [
8573
8799
  tags: ["runs"],
8574
8800
  parameters: [runIdParameter],
8575
8801
  responses: {
8576
- 200: { description: "Run returned." },
8577
- 404: { description: "Run not found." }
8802
+ 200: jsonResponse("Run returned.", "RunDetailDto"),
8803
+ 404: errorResponse("Run not found.")
8578
8804
  }
8579
8805
  },
8580
8806
  {
@@ -8584,9 +8810,9 @@ var routeCatalog = [
8584
8810
  tags: ["runs"],
8585
8811
  parameters: [runIdParameter],
8586
8812
  responses: {
8587
- 200: { description: "Run cancelled." },
8588
- 404: { description: "Run not found." },
8589
- 409: { description: "Run is not cancellable." }
8813
+ 200: jsonResponse("Run cancelled.", "RunDto"),
8814
+ 404: errorResponse("Run not found."),
8815
+ 409: errorResponse("Run is not cancellable.")
8590
8816
  }
8591
8817
  },
8592
8818
  {
@@ -8604,8 +8830,9 @@ var routeCatalog = [
8604
8830
  }
8605
8831
  },
8606
8832
  responses: {
8607
- 200: { description: "Config applied." },
8608
- 400: { description: "Invalid config." }
8833
+ // TODO: Add `ApplyResultDto` Zod schema in contracts (single-doc apply result).
8834
+ 200: jsonResponse("Config applied.", "ProjectDto"),
8835
+ 400: errorResponse("Invalid config.")
8609
8836
  }
8610
8837
  },
8611
8838
  {
@@ -8615,7 +8842,7 @@ var routeCatalog = [
8615
8842
  tags: ["history"],
8616
8843
  parameters: [nameParameter],
8617
8844
  responses: {
8618
- 200: { description: "Audit history returned." }
8845
+ 200: jsonArrayResponse("Audit history returned.", "AuditLogEntry")
8619
8846
  }
8620
8847
  },
8621
8848
  {
@@ -8624,7 +8851,7 @@ var routeCatalog = [
8624
8851
  summary: "Get global audit history",
8625
8852
  tags: ["history"],
8626
8853
  responses: {
8627
- 200: { description: "Audit history returned." }
8854
+ 200: jsonArrayResponse("Audit history returned.", "AuditLogEntry")
8628
8855
  }
8629
8856
  },
8630
8857
  {
@@ -8639,7 +8866,7 @@ var routeCatalog = [
8639
8866
  locationQueryParameter
8640
8867
  ],
8641
8868
  responses: {
8642
- 200: { description: "Snapshots returned." }
8869
+ 200: jsonResponse("Snapshots returned.", "SnapshotListResponse")
8643
8870
  }
8644
8871
  },
8645
8872
  {
@@ -8649,7 +8876,8 @@ var routeCatalog = [
8649
8876
  tags: ["history"],
8650
8877
  parameters: [nameParameter, locationQueryParameter],
8651
8878
  responses: {
8652
- 200: { description: "Timeline returned." }
8879
+ // TODO: Add `ProjectTimelineDto` Zod schema in contracts.
8880
+ 200: rawJsonResponse("Timeline returned.", looseObjectSchema)
8653
8881
  }
8654
8882
  },
8655
8883
  {
@@ -8659,8 +8887,9 @@ var routeCatalog = [
8659
8887
  tags: ["analytics"],
8660
8888
  parameters: [nameParameter, analyticsWindowParameter],
8661
8889
  responses: {
8662
- 200: { description: "Citation metrics returned." },
8663
- 404: { description: "Project not found." }
8890
+ // TODO: Add `BrandMetricsDto` Zod schema in contracts.
8891
+ 200: rawJsonResponse("Citation metrics returned.", looseObjectSchema),
8892
+ 404: errorResponse("Project not found.")
8664
8893
  }
8665
8894
  },
8666
8895
  {
@@ -8670,8 +8899,9 @@ var routeCatalog = [
8670
8899
  tags: ["analytics"],
8671
8900
  parameters: [nameParameter, analyticsWindowParameter],
8672
8901
  responses: {
8673
- 200: { description: "Gap analysis returned." },
8674
- 404: { description: "Project not found." }
8902
+ // TODO: Add `GapAnalysisDto` Zod schema in contracts.
8903
+ 200: rawJsonResponse("Gap analysis returned.", looseObjectSchema),
8904
+ 404: errorResponse("Project not found.")
8675
8905
  }
8676
8906
  },
8677
8907
  {
@@ -8681,8 +8911,9 @@ var routeCatalog = [
8681
8911
  tags: ["analytics"],
8682
8912
  parameters: [nameParameter, analyticsWindowParameter],
8683
8913
  responses: {
8684
- 200: { description: "Source breakdown returned." },
8685
- 404: { description: "Project not found." }
8914
+ // TODO: Add `SourceBreakdownDto` Zod schema in contracts.
8915
+ 200: rawJsonResponse("Source breakdown returned.", looseObjectSchema),
8916
+ 404: errorResponse("Project not found.")
8686
8917
  }
8687
8918
  },
8688
8919
  {
@@ -8708,8 +8939,8 @@ var routeCatalog = [
8708
8939
  }
8709
8940
  ],
8710
8941
  responses: {
8711
- 200: { description: "Diff returned." },
8712
- 400: { description: "Missing run IDs." }
8942
+ 200: jsonResponse("Diff returned.", "SnapshotDiffResponse"),
8943
+ 400: errorResponse("Missing run IDs.")
8713
8944
  }
8714
8945
  },
8715
8946
  {
@@ -8718,7 +8949,7 @@ var routeCatalog = [
8718
8949
  summary: "Get provider settings summary",
8719
8950
  tags: ["settings"],
8720
8951
  responses: {
8721
- 200: { description: "Settings returned." }
8952
+ 200: jsonResponse("Settings returned.", "SettingsDto")
8722
8953
  }
8723
8954
  },
8724
8955
  {
@@ -8744,9 +8975,10 @@ var routeCatalog = [
8744
8975
  }
8745
8976
  },
8746
8977
  responses: {
8747
- 200: { description: "Provider updated." },
8748
- 400: { description: "Invalid provider settings." },
8749
- 501: { description: "Provider updates are not supported." }
8978
+ // TODO: Add `ProviderSettingsDto` Zod schema in contracts.
8979
+ 200: rawJsonResponse("Provider updated.", looseObjectSchema),
8980
+ 400: errorResponse("Invalid provider settings."),
8981
+ 501: errorResponse("Provider updates are not supported.")
8750
8982
  }
8751
8983
  },
8752
8984
  {
@@ -8770,9 +9002,10 @@ var routeCatalog = [
8770
9002
  }
8771
9003
  },
8772
9004
  responses: {
8773
- 200: { description: "Google settings updated." },
8774
- 400: { description: "Invalid Google settings." },
8775
- 501: { description: "Google settings updates are not supported." }
9005
+ // TODO: Add `GoogleSettingsDto` Zod schema in contracts.
9006
+ 200: rawJsonResponse("Google settings updated.", looseObjectSchema),
9007
+ 400: errorResponse("Invalid Google settings."),
9008
+ 501: errorResponse("Google settings updates are not supported.")
8776
9009
  }
8777
9010
  },
8778
9011
  {
@@ -8798,9 +9031,9 @@ var routeCatalog = [
8798
9031
  }
8799
9032
  },
8800
9033
  responses: {
8801
- 200: { description: "Snapshot report returned." },
8802
- 400: { description: "Invalid snapshot input." },
8803
- 501: { description: "Snapshot reporting is not supported." }
9034
+ 200: jsonResponse("Snapshot report returned.", "SnapshotReportDto"),
9035
+ 400: errorResponse("Invalid snapshot input."),
9036
+ 501: errorResponse("Snapshot reporting is not supported.")
8804
9037
  }
8805
9038
  },
8806
9039
  {
@@ -8823,9 +9056,10 @@ var routeCatalog = [
8823
9056
  }
8824
9057
  },
8825
9058
  responses: {
8826
- 200: { description: "Bing settings updated." },
8827
- 400: { description: "Invalid Bing settings." },
8828
- 501: { description: "Bing settings updates are not supported." }
9059
+ // TODO: Add `BingSettingsDto` Zod schema in contracts.
9060
+ 200: rawJsonResponse("Bing settings updated.", looseObjectSchema),
9061
+ 400: errorResponse("Invalid Bing settings."),
9062
+ 501: errorResponse("Bing settings updates are not supported.")
8829
9063
  }
8830
9064
  },
8831
9065
  {
@@ -8849,9 +9083,10 @@ var routeCatalog = [
8849
9083
  }
8850
9084
  },
8851
9085
  responses: {
8852
- 200: { description: "CDP endpoint updated." },
8853
- 400: { description: "Invalid CDP settings." },
8854
- 501: { description: "CDP updates are not supported." }
9086
+ // TODO: Add `CdpEndpointConfigDto` Zod schema in contracts.
9087
+ 200: rawJsonResponse("CDP endpoint updated.", looseObjectSchema),
9088
+ 400: errorResponse("Invalid CDP settings."),
9089
+ 501: errorResponse("CDP updates are not supported.")
8855
9090
  }
8856
9091
  },
8857
9092
  {
@@ -8880,9 +9115,9 @@ var routeCatalog = [
8880
9115
  }
8881
9116
  },
8882
9117
  responses: {
8883
- 200: { description: "Schedule updated." },
8884
- 201: { description: "Schedule created." },
8885
- 400: { description: "Invalid payload (e.g. sourceId missing for kind=traffic-sync, or providers set for kind=traffic-sync)." }
9118
+ 200: jsonResponse("Schedule updated.", "ScheduleDto"),
9119
+ 201: jsonResponse("Schedule created.", "ScheduleDto"),
9120
+ 400: errorResponse("Invalid payload (e.g. sourceId missing for kind=traffic-sync, or providers set for kind=traffic-sync).")
8886
9121
  }
8887
9122
  },
8888
9123
  {
@@ -8892,8 +9127,8 @@ var routeCatalog = [
8892
9127
  tags: ["schedules"],
8893
9128
  parameters: [nameParameter, scheduleKindQueryParameter],
8894
9129
  responses: {
8895
- 200: { description: "Schedule returned." },
8896
- 404: { description: "Schedule not found." }
9130
+ 200: jsonResponse("Schedule returned.", "ScheduleDto"),
9131
+ 404: errorResponse("Schedule not found.")
8897
9132
  }
8898
9133
  },
8899
9134
  {
@@ -8904,7 +9139,7 @@ var routeCatalog = [
8904
9139
  parameters: [nameParameter, scheduleKindQueryParameter],
8905
9140
  responses: {
8906
9141
  204: { description: "Schedule deleted." },
8907
- 404: { description: "Schedule not found." }
9142
+ 404: errorResponse("Schedule not found.")
8908
9143
  }
8909
9144
  },
8910
9145
  {
@@ -8913,7 +9148,7 @@ var routeCatalog = [
8913
9148
  summary: "List notification event types",
8914
9149
  tags: ["notifications"],
8915
9150
  responses: {
8916
- 200: { description: "Events returned." }
9151
+ 200: rawJsonResponse("Events returned.", { type: "array", items: stringSchema })
8917
9152
  }
8918
9153
  },
8919
9154
  {
@@ -8939,7 +9174,7 @@ var routeCatalog = [
8939
9174
  }
8940
9175
  },
8941
9176
  responses: {
8942
- 201: { description: "Notification created." }
9177
+ 201: jsonResponse("Notification created.", "NotificationDto")
8943
9178
  }
8944
9179
  },
8945
9180
  {
@@ -8949,7 +9184,7 @@ var routeCatalog = [
8949
9184
  tags: ["notifications"],
8950
9185
  parameters: [nameParameter],
8951
9186
  responses: {
8952
- 200: { description: "Notifications returned." }
9187
+ 200: jsonArrayResponse("Notifications returned.", "NotificationDto")
8953
9188
  }
8954
9189
  },
8955
9190
  {
@@ -8960,7 +9195,7 @@ var routeCatalog = [
8960
9195
  parameters: [nameParameter, notificationIdParameter],
8961
9196
  responses: {
8962
9197
  204: { description: "Notification deleted." },
8963
- 404: { description: "Notification not found." }
9198
+ 404: errorResponse("Notification not found.")
8964
9199
  }
8965
9200
  },
8966
9201
  {
@@ -8970,10 +9205,11 @@ var routeCatalog = [
8970
9205
  tags: ["notifications"],
8971
9206
  parameters: [nameParameter, notificationIdParameter],
8972
9207
  responses: {
8973
- 200: { description: "Test notification sent." },
8974
- 400: { description: "Stored notification config is invalid." },
8975
- 404: { description: "Notification not found." },
8976
- 502: { description: "Notification delivery failed." }
9208
+ // TODO: Add `NotificationTestResult` Zod schema in contracts.
9209
+ 200: rawJsonResponse("Test notification sent.", looseObjectSchema),
9210
+ 400: errorResponse("Stored notification config is invalid."),
9211
+ 404: errorResponse("Notification not found."),
9212
+ 502: errorResponse("Notification delivery failed.")
8977
9213
  }
8978
9214
  },
8979
9215
  {
@@ -8982,8 +9218,9 @@ var routeCatalog = [
8982
9218
  summary: "Get telemetry status",
8983
9219
  tags: ["telemetry"],
8984
9220
  responses: {
8985
- 200: { description: "Telemetry status returned." },
8986
- 501: { description: "Telemetry status is not available." }
9221
+ // TODO: Add `TelemetryStatusDto` Zod schema in contracts.
9222
+ 200: rawJsonResponse("Telemetry status returned.", looseObjectSchema),
9223
+ 501: errorResponse("Telemetry status is not available.")
8987
9224
  }
8988
9225
  },
8989
9226
  {
@@ -9006,9 +9243,10 @@ var routeCatalog = [
9006
9243
  }
9007
9244
  },
9008
9245
  responses: {
9009
- 200: { description: "Telemetry updated." },
9010
- 400: { description: "Invalid telemetry request." },
9011
- 501: { description: "Telemetry configuration is not available." }
9246
+ // TODO: Add `TelemetryStatusDto` Zod schema in contracts.
9247
+ 200: rawJsonResponse("Telemetry updated.", looseObjectSchema),
9248
+ 400: errorResponse("Invalid telemetry request."),
9249
+ 501: errorResponse("Telemetry configuration is not available.")
9012
9250
  }
9013
9251
  },
9014
9252
  {
@@ -9018,8 +9256,9 @@ var routeCatalog = [
9018
9256
  tags: ["cdp"],
9019
9257
  parameters: [snapshotIdParameter],
9020
9258
  responses: {
9021
- 200: { description: "Screenshot returned." },
9022
- 404: { description: "Screenshot not found." }
9259
+ // Returns image bytes, not JSON. Codegen consumers should treat this as a binary stream.
9260
+ 200: { description: "Screenshot returned.", content: { "image/png": { schema: { type: "string", format: "binary" } } } },
9261
+ 404: errorResponse("Screenshot not found.")
9023
9262
  }
9024
9263
  },
9025
9264
  {
@@ -9028,8 +9267,8 @@ var routeCatalog = [
9028
9267
  summary: "Get CDP connection status",
9029
9268
  tags: ["cdp"],
9030
9269
  responses: {
9031
- 200: { description: "CDP status returned." },
9032
- 501: { description: "CDP is not configured." }
9270
+ 200: jsonResponse("CDP status returned.", "CdpStatusDto"),
9271
+ 501: errorResponse("CDP is not configured.")
9033
9272
  }
9034
9273
  },
9035
9274
  {
@@ -9053,9 +9292,10 @@ var routeCatalog = [
9053
9292
  }
9054
9293
  },
9055
9294
  responses: {
9056
- 200: { description: "CDP screenshot results returned." },
9057
- 400: { description: "Invalid CDP screenshot request." },
9058
- 501: { description: "CDP screenshot support is not available." }
9295
+ // TODO: Add `CdpScreenshotResultDto` Zod schema in contracts.
9296
+ 200: rawJsonResponse("CDP screenshot results returned.", looseObjectSchema),
9297
+ 400: errorResponse("Invalid CDP screenshot request."),
9298
+ 501: errorResponse("CDP screenshot support is not available.")
9059
9299
  }
9060
9300
  },
9061
9301
  {
@@ -9065,8 +9305,9 @@ var routeCatalog = [
9065
9305
  tags: ["cdp", "runs"],
9066
9306
  parameters: [nameParameter, projectRunIdParameter],
9067
9307
  responses: {
9068
- 200: { description: "Browser diff returned." },
9069
- 404: { description: "Project or run not found." }
9308
+ // TODO: Add `BrowserDiffDto` Zod schema in contracts.
9309
+ 200: rawJsonResponse("Browser diff returned.", looseObjectSchema),
9310
+ 404: errorResponse("Project or run not found.")
9070
9311
  }
9071
9312
  },
9072
9313
  {
@@ -9081,9 +9322,9 @@ var routeCatalog = [
9081
9322
  { name: "error", in: "query", description: "OAuth error code.", schema: stringSchema }
9082
9323
  ],
9083
9324
  responses: {
9084
- 200: { description: "OAuth callback handled." },
9085
- 400: { description: "Invalid callback request." },
9086
- 500: { description: "OAuth configuration is incomplete." }
9325
+ 200: rawJsonResponse("OAuth callback handled.", { type: "object", properties: { status: { type: "string" } } }),
9326
+ 400: errorResponse("Invalid callback request."),
9327
+ 500: errorResponse("OAuth configuration is incomplete.")
9087
9328
  }
9088
9329
  },
9089
9330
  {
@@ -9099,9 +9340,9 @@ var routeCatalog = [
9099
9340
  { name: "error", in: "query", description: "OAuth error code.", schema: stringSchema }
9100
9341
  ],
9101
9342
  responses: {
9102
- 200: { description: "OAuth callback handled." },
9103
- 400: { description: "Invalid callback request." },
9104
- 500: { description: "OAuth configuration is incomplete." }
9343
+ 200: rawJsonResponse("OAuth callback handled.", { type: "object", properties: { status: { type: "string" } } }),
9344
+ 400: errorResponse("Invalid callback request."),
9345
+ 500: errorResponse("OAuth configuration is incomplete.")
9105
9346
  }
9106
9347
  },
9107
9348
  {
@@ -9111,8 +9352,8 @@ var routeCatalog = [
9111
9352
  tags: ["google"],
9112
9353
  parameters: [nameParameter],
9113
9354
  responses: {
9114
- 200: { description: "Google connections returned." },
9115
- 404: { description: "Project not found." }
9355
+ 200: jsonArrayResponse("Google connections returned.", "GoogleConnectionDto"),
9356
+ 404: errorResponse("Project not found.")
9116
9357
  }
9117
9358
  },
9118
9359
  {
@@ -9138,8 +9379,8 @@ var routeCatalog = [
9138
9379
  }
9139
9380
  },
9140
9381
  responses: {
9141
- 200: { description: "Google auth URL returned." },
9142
- 400: { description: "Invalid Google connection request." }
9382
+ 200: rawJsonResponse("Google auth URL returned.", { type: "object", properties: { url: { type: "string" } } }),
9383
+ 400: errorResponse("Invalid Google connection request.")
9143
9384
  }
9144
9385
  },
9145
9386
  {
@@ -9150,7 +9391,7 @@ var routeCatalog = [
9150
9391
  parameters: [nameParameter, googleTypeParameter],
9151
9392
  responses: {
9152
9393
  204: { description: "Google connection deleted." },
9153
- 404: { description: "Project or connection not found." }
9394
+ 404: errorResponse("Project or connection not found.")
9154
9395
  }
9155
9396
  },
9156
9397
  {
@@ -9160,9 +9401,9 @@ var routeCatalog = [
9160
9401
  tags: ["google"],
9161
9402
  parameters: [nameParameter],
9162
9403
  responses: {
9163
- 200: { description: "Google properties returned." },
9164
- 400: { description: "Google OAuth is not configured." },
9165
- 404: { description: "Project not found." }
9404
+ 200: jsonResponse("Google properties returned.", "GscSiteListResponseDto"),
9405
+ 400: errorResponse("Google OAuth is not configured."),
9406
+ 404: errorResponse("Project not found.")
9166
9407
  }
9167
9408
  },
9168
9409
  {
@@ -9186,9 +9427,9 @@ var routeCatalog = [
9186
9427
  }
9187
9428
  },
9188
9429
  responses: {
9189
- 200: { description: "Google property updated." },
9190
- 400: { description: "Invalid property request." },
9191
- 404: { description: "Project or connection not found." }
9430
+ 200: jsonResponse("Google property updated.", "GoogleConnectionDto"),
9431
+ 400: errorResponse("Invalid property request."),
9432
+ 404: errorResponse("Project or connection not found.")
9192
9433
  }
9193
9434
  },
9194
9435
  {
@@ -9212,9 +9453,9 @@ var routeCatalog = [
9212
9453
  }
9213
9454
  },
9214
9455
  responses: {
9215
- 200: { description: "Google sitemap updated." },
9216
- 400: { description: "Invalid sitemap request." },
9217
- 404: { description: "Project or connection not found." }
9456
+ 200: jsonResponse("Google sitemap updated.", "GoogleConnectionDto"),
9457
+ 400: errorResponse("Invalid sitemap request."),
9458
+ 404: errorResponse("Project or connection not found.")
9218
9459
  }
9219
9460
  },
9220
9461
  {
@@ -9237,9 +9478,9 @@ var routeCatalog = [
9237
9478
  }
9238
9479
  },
9239
9480
  responses: {
9240
- 200: { description: "GSC sync run returned." },
9241
- 400: { description: "Invalid GSC sync request." },
9242
- 404: { description: "Project or connection not found." }
9481
+ 200: jsonResponse("GSC sync run returned.", "RunDto"),
9482
+ 400: errorResponse("Invalid GSC sync request."),
9483
+ 404: errorResponse("Project or connection not found.")
9243
9484
  }
9244
9485
  },
9245
9486
  {
@@ -9258,8 +9499,11 @@ var routeCatalog = [
9258
9499
  analyticsWindowParameter
9259
9500
  ],
9260
9501
  responses: {
9261
- 200: { description: "GSC performance rows returned." },
9262
- 404: { description: "Project not found." }
9502
+ // Handler returns an array of GscSearchDataDto rows (web's
9503
+ // ApiGscPerformanceRow[] confirms). Was incorrectly spec'd as a
9504
+ // single object, which silently truncated client types to one row.
9505
+ 200: jsonArrayResponse("GSC performance rows returned.", "GscSearchDataDto"),
9506
+ 404: errorResponse("Project not found.")
9263
9507
  }
9264
9508
  },
9265
9509
  {
@@ -9274,8 +9518,8 @@ var routeCatalog = [
9274
9518
  analyticsWindowParameter
9275
9519
  ],
9276
9520
  responses: {
9277
- 200: { description: "Daily aggregate (date \u2192 clicks/impressions/ctr) plus window totals." },
9278
- 404: { description: "Project not found." }
9521
+ 200: jsonResponse("Daily aggregate (date \u2192 clicks/impressions/ctr) plus window totals.", "GscPerformanceDailyDto"),
9522
+ 404: errorResponse("Project not found.")
9279
9523
  }
9280
9524
  },
9281
9525
  {
@@ -9299,9 +9543,9 @@ var routeCatalog = [
9299
9543
  }
9300
9544
  },
9301
9545
  responses: {
9302
- 200: { description: "GSC inspection result returned." },
9303
- 400: { description: "Invalid inspection request." },
9304
- 404: { description: "Project or connection not found." }
9546
+ 200: jsonResponse("GSC inspection result returned.", "GscUrlInspectionDto"),
9547
+ 400: errorResponse("Invalid inspection request."),
9548
+ 404: errorResponse("Project or connection not found.")
9305
9549
  }
9306
9550
  },
9307
9551
  {
@@ -9311,8 +9555,8 @@ var routeCatalog = [
9311
9555
  tags: ["google"],
9312
9556
  parameters: [nameParameter, { name: "url", in: "query", description: "Filter by URL.", schema: stringSchema }, limitQueryParameter],
9313
9557
  responses: {
9314
- 200: { description: "GSC inspections returned." },
9315
- 404: { description: "Project not found." }
9558
+ 200: jsonArrayResponse("GSC inspections returned.", "GscUrlInspectionDto"),
9559
+ 404: errorResponse("Project not found.")
9316
9560
  }
9317
9561
  },
9318
9562
  {
@@ -9322,8 +9566,8 @@ var routeCatalog = [
9322
9566
  tags: ["google"],
9323
9567
  parameters: [nameParameter],
9324
9568
  responses: {
9325
- 200: { description: "Deindexed pages returned." },
9326
- 404: { description: "Project not found." }
9569
+ 200: jsonArrayResponse("Deindexed pages returned.", "GscDeindexedRowDto"),
9570
+ 404: errorResponse("Project not found.")
9327
9571
  }
9328
9572
  },
9329
9573
  {
@@ -9333,8 +9577,8 @@ var routeCatalog = [
9333
9577
  tags: ["google"],
9334
9578
  parameters: [nameParameter],
9335
9579
  responses: {
9336
- 200: { description: "GSC coverage returned." },
9337
- 404: { description: "Project not found." }
9580
+ 200: jsonResponse("GSC coverage returned.", "GscCoverageSummaryDto"),
9581
+ 404: errorResponse("Project not found.")
9338
9582
  }
9339
9583
  },
9340
9584
  {
@@ -9344,8 +9588,8 @@ var routeCatalog = [
9344
9588
  tags: ["google"],
9345
9589
  parameters: [nameParameter, limitQueryParameter],
9346
9590
  responses: {
9347
- 200: { description: "GSC coverage history returned." },
9348
- 404: { description: "Project not found." }
9591
+ 200: jsonArrayResponse("GSC coverage history returned.", "GscCoverageSnapshotDto"),
9592
+ 404: errorResponse("Project not found.")
9349
9593
  }
9350
9594
  },
9351
9595
  {
@@ -9355,9 +9599,9 @@ var routeCatalog = [
9355
9599
  tags: ["google"],
9356
9600
  parameters: [nameParameter],
9357
9601
  responses: {
9358
- 200: { description: "GSC sitemaps returned." },
9359
- 400: { description: "Invalid sitemap request." },
9360
- 404: { description: "Project or connection not found." }
9602
+ 200: jsonResponse("GSC sitemaps returned.", "GscSitemapListResponseDto"),
9603
+ 400: errorResponse("Invalid sitemap request."),
9604
+ 404: errorResponse("Project or connection not found.")
9361
9605
  }
9362
9606
  },
9363
9607
  {
@@ -9367,9 +9611,10 @@ var routeCatalog = [
9367
9611
  tags: ["google"],
9368
9612
  parameters: [nameParameter],
9369
9613
  responses: {
9370
- 200: { description: "Discovered sitemaps and queued run returned." },
9371
- 400: { description: "Invalid sitemap discovery request." },
9372
- 404: { description: "Project or connection not found." }
9614
+ // TODO: Add `DiscoverSitemapsResponse` Zod schema in contracts.
9615
+ 200: rawJsonResponse("Discovered sitemaps and queued run returned.", looseObjectSchema),
9616
+ 400: errorResponse("Invalid sitemap discovery request."),
9617
+ 404: errorResponse("Project or connection not found.")
9373
9618
  }
9374
9619
  },
9375
9620
  {
@@ -9391,9 +9636,9 @@ var routeCatalog = [
9391
9636
  }
9392
9637
  },
9393
9638
  responses: {
9394
- 200: { description: "Sitemap inspection run returned." },
9395
- 400: { description: "Invalid sitemap inspection request." },
9396
- 404: { description: "Project or connection not found." }
9639
+ 200: jsonResponse("Sitemap inspection run returned.", "RunDto"),
9640
+ 400: errorResponse("Invalid sitemap inspection request."),
9641
+ 404: errorResponse("Project or connection not found.")
9397
9642
  }
9398
9643
  },
9399
9644
  {
@@ -9417,9 +9662,9 @@ var routeCatalog = [
9417
9662
  }
9418
9663
  },
9419
9664
  responses: {
9420
- 200: { description: "Indexing request results returned." },
9421
- 400: { description: "Invalid indexing request." },
9422
- 404: { description: "Project or connection not found." }
9665
+ 200: jsonResponse("Indexing request results returned.", "IndexingRequestResponseDto"),
9666
+ 400: errorResponse("Invalid indexing request."),
9667
+ 404: errorResponse("Project or connection not found.")
9423
9668
  }
9424
9669
  },
9425
9670
  {
@@ -9443,9 +9688,9 @@ var routeCatalog = [
9443
9688
  }
9444
9689
  },
9445
9690
  responses: {
9446
- 200: { description: "Bing connection returned." },
9447
- 400: { description: "Invalid Bing connection request." },
9448
- 404: { description: "Project not found." }
9691
+ 200: jsonResponse("Bing connection returned.", "BingConnectResponseDto"),
9692
+ 400: errorResponse("Invalid Bing connection request."),
9693
+ 404: errorResponse("Project not found.")
9449
9694
  }
9450
9695
  },
9451
9696
  {
@@ -9456,7 +9701,7 @@ var routeCatalog = [
9456
9701
  parameters: [nameParameter],
9457
9702
  responses: {
9458
9703
  204: { description: "Bing connection deleted." },
9459
- 404: { description: "Project or connection not found." }
9704
+ 404: errorResponse("Project or connection not found.")
9460
9705
  }
9461
9706
  },
9462
9707
  {
@@ -9466,8 +9711,8 @@ var routeCatalog = [
9466
9711
  tags: ["bing"],
9467
9712
  parameters: [nameParameter],
9468
9713
  responses: {
9469
- 200: { description: "Bing status returned." },
9470
- 404: { description: "Project not found." }
9714
+ 200: jsonResponse("Bing status returned.", "BingStatusDto"),
9715
+ 404: errorResponse("Project not found.")
9471
9716
  }
9472
9717
  },
9473
9718
  {
@@ -9477,9 +9722,9 @@ var routeCatalog = [
9477
9722
  tags: ["bing"],
9478
9723
  parameters: [nameParameter],
9479
9724
  responses: {
9480
- 200: { description: "Bing sites returned." },
9481
- 400: { description: "Bing is not configured for this project." },
9482
- 404: { description: "Project not found." }
9725
+ 200: jsonResponse("Bing sites returned.", "BingSitesResponseDto"),
9726
+ 400: errorResponse("Bing is not configured for this project."),
9727
+ 404: errorResponse("Project not found.")
9483
9728
  }
9484
9729
  },
9485
9730
  {
@@ -9503,9 +9748,9 @@ var routeCatalog = [
9503
9748
  }
9504
9749
  },
9505
9750
  responses: {
9506
- 200: { description: "Active Bing site updated." },
9507
- 400: { description: "Invalid Bing site request." },
9508
- 404: { description: "Project or connection not found." }
9751
+ 200: jsonResponse("Active Bing site updated.", "BingSetSiteResponseDto"),
9752
+ 400: errorResponse("Invalid Bing site request."),
9753
+ 404: errorResponse("Project or connection not found.")
9509
9754
  }
9510
9755
  },
9511
9756
  {
@@ -9515,9 +9760,13 @@ var routeCatalog = [
9515
9760
  tags: ["bing"],
9516
9761
  parameters: [nameParameter],
9517
9762
  responses: {
9518
- 200: { description: "Bing coverage returned." },
9519
- 400: { description: "Bing is not configured for this project." },
9520
- 404: { description: "Project not found." }
9763
+ // Was incorrectly mapped to `BingCoverageSnapshotDto` (the daily
9764
+ // history snapshot 4 fields). The /coverage handler actually
9765
+ // returns the nested summary shape with indexed/notIndexed/unknown
9766
+ // arrays. `BingCoverageSummaryDto` is the right ref.
9767
+ 200: jsonResponse("Bing coverage returned.", "BingCoverageSummaryDto"),
9768
+ 400: errorResponse("Bing is not configured for this project."),
9769
+ 404: errorResponse("Project not found.")
9521
9770
  }
9522
9771
  },
9523
9772
  {
@@ -9527,9 +9776,9 @@ var routeCatalog = [
9527
9776
  tags: ["bing"],
9528
9777
  parameters: [nameParameter, limitQueryParameter],
9529
9778
  responses: {
9530
- 200: { description: "Bing coverage history returned." },
9531
- 400: { description: "Bing is not configured for this project." },
9532
- 404: { description: "Project not found." }
9779
+ 200: jsonArrayResponse("Bing coverage history returned.", "BingCoverageSnapshotDto"),
9780
+ 400: errorResponse("Bing is not configured for this project."),
9781
+ 404: errorResponse("Project not found.")
9533
9782
  }
9534
9783
  },
9535
9784
  {
@@ -9539,9 +9788,9 @@ var routeCatalog = [
9539
9788
  tags: ["bing"],
9540
9789
  parameters: [nameParameter, { name: "url", in: "query", description: "Filter by URL.", schema: stringSchema }, limitQueryParameter],
9541
9790
  responses: {
9542
- 200: { description: "Bing inspections returned." },
9543
- 400: { description: "Bing is not configured for this project." },
9544
- 404: { description: "Project not found." }
9791
+ 200: jsonArrayResponse("Bing inspections returned.", "BingUrlInspectionDto"),
9792
+ 400: errorResponse("Bing is not configured for this project."),
9793
+ 404: errorResponse("Project not found.")
9545
9794
  }
9546
9795
  },
9547
9796
  {
@@ -9565,9 +9814,9 @@ var routeCatalog = [
9565
9814
  }
9566
9815
  },
9567
9816
  responses: {
9568
- 200: { description: "Bing inspection result returned." },
9569
- 400: { description: "Invalid inspection request." },
9570
- 404: { description: "Project or connection not found." }
9817
+ 200: jsonResponse("Bing inspection result returned.", "BingUrlInspectionDto"),
9818
+ 400: errorResponse("Invalid inspection request."),
9819
+ 404: errorResponse("Project or connection not found.")
9571
9820
  }
9572
9821
  },
9573
9822
  {
@@ -9590,9 +9839,9 @@ var routeCatalog = [
9590
9839
  }
9591
9840
  },
9592
9841
  responses: {
9593
- 200: { description: "Sitemap inspection run queued." },
9594
- 400: { description: "Bing is not configured for this project." },
9595
- 404: { description: "Project not found." }
9842
+ 200: jsonResponse("Sitemap inspection run queued.", "RunDto"),
9843
+ 400: errorResponse("Bing is not configured for this project."),
9844
+ 404: errorResponse("Project not found.")
9596
9845
  }
9597
9846
  },
9598
9847
  {
@@ -9616,9 +9865,9 @@ var routeCatalog = [
9616
9865
  }
9617
9866
  },
9618
9867
  responses: {
9619
- 200: { description: "Bing indexing request results returned." },
9620
- 400: { description: "Invalid indexing request." },
9621
- 404: { description: "Project or connection not found." }
9868
+ 200: jsonResponse("Bing indexing request results returned.", "BingIndexingRequestResponseDto"),
9869
+ 400: errorResponse("Invalid indexing request."),
9870
+ 404: errorResponse("Project or connection not found.")
9622
9871
  }
9623
9872
  },
9624
9873
  {
@@ -9628,9 +9877,9 @@ var routeCatalog = [
9628
9877
  tags: ["bing"],
9629
9878
  parameters: [nameParameter, limitQueryParameter],
9630
9879
  responses: {
9631
- 200: { description: "Bing performance returned." },
9632
- 400: { description: "Bing is not configured for this project." },
9633
- 404: { description: "Project not found." }
9880
+ 200: jsonArrayResponse("Bing performance returned.", "BingKeywordStatsDto"),
9881
+ 400: errorResponse("Bing is not configured for this project."),
9882
+ 404: errorResponse("Project not found.")
9634
9883
  }
9635
9884
  },
9636
9885
  {
@@ -9658,9 +9907,9 @@ var routeCatalog = [
9658
9907
  }
9659
9908
  },
9660
9909
  responses: {
9661
- 200: { description: "WordPress connection status returned." },
9662
- 400: { description: "Invalid WordPress connection request." },
9663
- 404: { description: "Project not found." }
9910
+ 200: jsonResponse("WordPress connection status returned.", "WordpressStatusDto"),
9911
+ 400: errorResponse("Invalid WordPress connection request."),
9912
+ 404: errorResponse("Project not found.")
9664
9913
  }
9665
9914
  },
9666
9915
  {
@@ -9671,7 +9920,7 @@ var routeCatalog = [
9671
9920
  parameters: [nameParameter],
9672
9921
  responses: {
9673
9922
  204: { description: "WordPress connection deleted." },
9674
- 404: { description: "Project or connection not found." }
9923
+ 404: errorResponse("Project or connection not found.")
9675
9924
  }
9676
9925
  },
9677
9926
  {
@@ -9681,8 +9930,8 @@ var routeCatalog = [
9681
9930
  tags: ["wordpress"],
9682
9931
  parameters: [nameParameter],
9683
9932
  responses: {
9684
- 200: { description: "WordPress status returned." },
9685
- 404: { description: "Project not found." }
9933
+ 200: jsonResponse("WordPress status returned.", "WordpressStatusDto"),
9934
+ 404: errorResponse("Project not found.")
9686
9935
  }
9687
9936
  },
9688
9937
  {
@@ -9692,9 +9941,9 @@ var routeCatalog = [
9692
9941
  tags: ["wordpress"],
9693
9942
  parameters: [nameParameter, wordpressEnvQueryParameter],
9694
9943
  responses: {
9695
- 200: { description: "WordPress pages returned." },
9696
- 400: { description: "Invalid environment or missing connection." },
9697
- 404: { description: "Project not found." }
9944
+ 200: jsonArrayResponse("WordPress pages returned.", "WordpressPageSummaryDto"),
9945
+ 400: errorResponse("Invalid environment or missing connection."),
9946
+ 404: errorResponse("Project not found.")
9698
9947
  }
9699
9948
  },
9700
9949
  {
@@ -9704,9 +9953,9 @@ var routeCatalog = [
9704
9953
  tags: ["wordpress"],
9705
9954
  parameters: [nameParameter, wordpressSlugQueryParameter, wordpressEnvQueryParameter],
9706
9955
  responses: {
9707
- 200: { description: "WordPress page returned." },
9708
- 400: { description: "Invalid slug or environment." },
9709
- 404: { description: "Project, connection, or page not found." }
9956
+ 200: jsonResponse("WordPress page returned.", "WordpressPageDetailDto"),
9957
+ 400: errorResponse("Invalid slug or environment."),
9958
+ 404: errorResponse("Project, connection, or page not found.")
9710
9959
  }
9711
9960
  },
9712
9961
  {
@@ -9734,9 +9983,9 @@ var routeCatalog = [
9734
9983
  }
9735
9984
  },
9736
9985
  responses: {
9737
- 200: { description: "WordPress page created." },
9738
- 400: { description: "Invalid page creation request." },
9739
- 404: { description: "Project or connection not found." }
9986
+ 200: jsonResponse("WordPress page created.", "WordpressPageDetailDto"),
9987
+ 400: errorResponse("Invalid page creation request."),
9988
+ 404: errorResponse("Project or connection not found.")
9740
9989
  }
9741
9990
  },
9742
9991
  {
@@ -9765,9 +10014,9 @@ var routeCatalog = [
9765
10014
  }
9766
10015
  },
9767
10016
  responses: {
9768
- 200: { description: "WordPress page updated." },
9769
- 400: { description: "Invalid page update request." },
9770
- 404: { description: "Project, connection, or page not found." }
10017
+ 200: jsonResponse("WordPress page updated.", "WordpressPageDetailDto"),
10018
+ 400: errorResponse("Invalid page update request."),
10019
+ 404: errorResponse("Project, connection, or page not found.")
9771
10020
  }
9772
10021
  },
9773
10022
  {
@@ -9795,9 +10044,10 @@ var routeCatalog = [
9795
10044
  }
9796
10045
  },
9797
10046
  responses: {
9798
- 200: { description: "WordPress SEO meta updated." },
9799
- 400: { description: "SEO meta is unsupported or the request is invalid." },
9800
- 404: { description: "Project, connection, or page not found." }
10047
+ // TODO: Add `WordpressSeoStateDto` to the schema table (already in contracts).
10048
+ 200: rawJsonResponse("WordPress SEO meta updated.", looseObjectSchema),
10049
+ 400: errorResponse("SEO meta is unsupported or the request is invalid."),
10050
+ 404: errorResponse("Project, connection, or page not found.")
9801
10051
  }
9802
10052
  },
9803
10053
  {
@@ -9834,9 +10084,9 @@ var routeCatalog = [
9834
10084
  }
9835
10085
  },
9836
10086
  responses: {
9837
- 200: { description: "Bulk SEO meta update results returned." },
9838
- 400: { description: "Invalid entries or environment." },
9839
- 404: { description: "Project or connection not found." }
10087
+ 200: jsonResponse("Bulk SEO meta update results returned.", "WordpressBulkMetaResultDto"),
10088
+ 400: errorResponse("Invalid entries or environment."),
10089
+ 404: errorResponse("Project or connection not found.")
9840
10090
  }
9841
10091
  },
9842
10092
  {
@@ -9846,9 +10096,9 @@ var routeCatalog = [
9846
10096
  tags: ["wordpress"],
9847
10097
  parameters: [nameParameter, wordpressSlugQueryParameter, wordpressEnvQueryParameter],
9848
10098
  responses: {
9849
- 200: { description: "WordPress schema blocks returned." },
9850
- 400: { description: "Invalid slug or environment." },
9851
- 404: { description: "Project, connection, or page not found." }
10099
+ 200: jsonArrayResponse("WordPress schema blocks returned.", "WordpressSchemaBlockDto"),
10100
+ 400: errorResponse("Invalid slug or environment."),
10101
+ 404: errorResponse("Project, connection, or page not found.")
9852
10102
  }
9853
10103
  },
9854
10104
  {
@@ -9875,9 +10125,9 @@ var routeCatalog = [
9875
10125
  }
9876
10126
  },
9877
10127
  responses: {
9878
- 200: { description: "Manual schema instructions returned." },
9879
- 400: { description: "Invalid schema request." },
9880
- 404: { description: "Project, connection, or page not found." }
10128
+ 200: jsonResponse("Manual schema instructions returned.", "WordpressManualAssistDto"),
10129
+ 400: errorResponse("Invalid schema request."),
10130
+ 404: errorResponse("Project, connection, or page not found.")
9881
10131
  }
9882
10132
  },
9883
10133
  {
@@ -9905,9 +10155,9 @@ var routeCatalog = [
9905
10155
  }
9906
10156
  },
9907
10157
  responses: {
9908
- 200: { description: "Schema deployment results returned." },
9909
- 400: { description: "Invalid profile or environment." },
9910
- 404: { description: "Project or connection not found." }
10158
+ 200: jsonResponse("Schema deployment results returned.", "WordpressSchemaDeployResultDto"),
10159
+ 400: errorResponse("Invalid profile or environment."),
10160
+ 404: errorResponse("Project or connection not found.")
9911
10161
  }
9912
10162
  },
9913
10163
  {
@@ -9917,9 +10167,9 @@ var routeCatalog = [
9917
10167
  tags: ["wordpress"],
9918
10168
  parameters: [nameParameter, wordpressEnvQueryParameter],
9919
10169
  responses: {
9920
- 200: { description: "Schema status per page returned." },
9921
- 400: { description: "Invalid environment." },
9922
- 404: { description: "Project or connection not found." }
10170
+ 200: jsonResponse("Schema status per page returned.", "WordpressSchemaStatusResultDto"),
10171
+ 400: errorResponse("Invalid environment."),
10172
+ 404: errorResponse("Project or connection not found.")
9923
10173
  }
9924
10174
  },
9925
10175
  {
@@ -9929,9 +10179,10 @@ var routeCatalog = [
9929
10179
  tags: ["wordpress"],
9930
10180
  parameters: [nameParameter, wordpressEnvQueryParameter],
9931
10181
  responses: {
9932
- 200: { description: "llms.txt returned." },
9933
- 400: { description: "Invalid environment or missing connection." },
9934
- 404: { description: "Project not found." }
10182
+ // Returns raw text/plain content of llms.txt.
10183
+ 200: { description: "llms.txt returned.", content: { "text/plain": { schema: { type: "string" } } } },
10184
+ 400: errorResponse("Invalid environment or missing connection."),
10185
+ 404: errorResponse("Project not found.")
9935
10186
  }
9936
10187
  },
9937
10188
  {
@@ -9956,9 +10207,9 @@ var routeCatalog = [
9956
10207
  }
9957
10208
  },
9958
10209
  responses: {
9959
- 200: { description: "Manual llms.txt instructions returned." },
9960
- 400: { description: "Invalid llms.txt request." },
9961
- 404: { description: "Project or connection not found." }
10210
+ 200: jsonResponse("Manual llms.txt instructions returned.", "WordpressManualAssistDto"),
10211
+ 400: errorResponse("Invalid llms.txt request."),
10212
+ 404: errorResponse("Project or connection not found.")
9962
10213
  }
9963
10214
  },
9964
10215
  {
@@ -9968,9 +10219,9 @@ var routeCatalog = [
9968
10219
  tags: ["wordpress"],
9969
10220
  parameters: [nameParameter, wordpressEnvQueryParameter],
9970
10221
  responses: {
9971
- 200: { description: "WordPress audit returned." },
9972
- 400: { description: "Invalid environment or missing connection." },
9973
- 404: { description: "Project not found." }
10222
+ 200: jsonArrayResponse("WordPress audit returned.", "WordpressAuditPageDto"),
10223
+ 400: errorResponse("Invalid environment or missing connection."),
10224
+ 404: errorResponse("Project not found.")
9974
10225
  }
9975
10226
  },
9976
10227
  {
@@ -9980,9 +10231,9 @@ var routeCatalog = [
9980
10231
  tags: ["wordpress"],
9981
10232
  parameters: [nameParameter, wordpressSlugQueryParameter],
9982
10233
  responses: {
9983
- 200: { description: "WordPress diff returned." },
9984
- 400: { description: "Invalid slug or missing staging configuration." },
9985
- 404: { description: "Project, connection, or page not found." }
10234
+ 200: jsonResponse("WordPress diff returned.", "WordpressDiffDto"),
10235
+ 400: errorResponse("Invalid slug or missing staging configuration."),
10236
+ 404: errorResponse("Project, connection, or page not found.")
9986
10237
  }
9987
10238
  },
9988
10239
  {
@@ -9992,9 +10243,10 @@ var routeCatalog = [
9992
10243
  tags: ["wordpress"],
9993
10244
  parameters: [nameParameter],
9994
10245
  responses: {
9995
- 200: { description: "WordPress staging status returned." },
9996
- 400: { description: "WordPress is not configured for this project." },
9997
- 404: { description: "Project not found." }
10246
+ // TODO: Add `WordpressSiteStatusDto` to the schema table (already in contracts).
10247
+ 200: rawJsonResponse("WordPress staging status returned.", looseObjectSchema),
10248
+ 400: errorResponse("WordPress is not configured for this project."),
10249
+ 404: errorResponse("Project not found.")
9998
10250
  }
9999
10251
  },
10000
10252
  {
@@ -10004,9 +10256,9 @@ var routeCatalog = [
10004
10256
  tags: ["wordpress"],
10005
10257
  parameters: [nameParameter],
10006
10258
  responses: {
10007
- 200: { description: "Manual staging push instructions returned." },
10008
- 400: { description: "Missing staging configuration." },
10009
- 404: { description: "Project or connection not found." }
10259
+ 200: jsonResponse("Manual staging push instructions returned.", "WordpressManualAssistDto"),
10260
+ 400: errorResponse("Missing staging configuration."),
10261
+ 404: errorResponse("Project or connection not found.")
10010
10262
  }
10011
10263
  },
10012
10264
  {
@@ -10037,9 +10289,9 @@ var routeCatalog = [
10037
10289
  }
10038
10290
  },
10039
10291
  responses: {
10040
- 200: { description: "Onboarding result with step-by-step status." },
10041
- 400: { description: "Invalid onboarding request." },
10042
- 404: { description: "Project not found." }
10292
+ 200: jsonResponse("Onboarding result with step-by-step status.", "WordpressOnboardResultDto"),
10293
+ 400: errorResponse("Invalid onboarding request."),
10294
+ 404: errorResponse("Project not found.")
10043
10295
  }
10044
10296
  },
10045
10297
  // GA4 routes
@@ -10065,9 +10317,10 @@ var routeCatalog = [
10065
10317
  }
10066
10318
  },
10067
10319
  responses: {
10068
- 200: { description: "GA4 connection established." },
10069
- 400: { description: "Invalid GA4 connection request." },
10070
- 404: { description: "Project not found." }
10320
+ // TODO: Add `GaConnectResponse` Zod schema in contracts.
10321
+ 200: rawJsonResponse("GA4 connection established.", looseObjectSchema),
10322
+ 400: errorResponse("Invalid GA4 connection request."),
10323
+ 404: errorResponse("Project not found.")
10071
10324
  }
10072
10325
  },
10073
10326
  {
@@ -10078,7 +10331,7 @@ var routeCatalog = [
10078
10331
  parameters: [nameParameter],
10079
10332
  responses: {
10080
10333
  204: { description: "GA4 connection deleted." },
10081
- 404: { description: "Project or connection not found." }
10334
+ 404: errorResponse("Project or connection not found.")
10082
10335
  }
10083
10336
  },
10084
10337
  {
@@ -10088,8 +10341,8 @@ var routeCatalog = [
10088
10341
  tags: ["ga4"],
10089
10342
  parameters: [nameParameter],
10090
10343
  responses: {
10091
- 200: { description: "GA4 status returned." },
10092
- 404: { description: "Project not found." }
10344
+ 200: jsonResponse("GA4 status returned.", "GA4StatusDto"),
10345
+ 404: errorResponse("Project not found.")
10093
10346
  }
10094
10347
  },
10095
10348
  {
@@ -10112,9 +10365,9 @@ var routeCatalog = [
10112
10365
  }
10113
10366
  },
10114
10367
  responses: {
10115
- 200: { description: "GA4 sync completed." },
10116
- 400: { description: "GA4 is not connected." },
10117
- 404: { description: "Project not found." }
10368
+ 200: jsonResponse("GA4 sync completed.", "GA4SyncResponseDto"),
10369
+ 400: errorResponse("GA4 is not connected."),
10370
+ 404: errorResponse("Project not found.")
10118
10371
  }
10119
10372
  },
10120
10373
  {
@@ -10124,9 +10377,10 @@ var routeCatalog = [
10124
10377
  tags: ["ga4"],
10125
10378
  parameters: [nameParameter, limitQueryParameter, analyticsWindowParameter],
10126
10379
  responses: {
10127
- 200: { description: "GA4 traffic data returned." },
10128
- 400: { description: "GA4 is not connected." },
10129
- 404: { description: "Project not found." }
10380
+ // TODO: Add `GaTrafficResponse` Zod schema in contracts.
10381
+ 200: rawJsonResponse("GA4 traffic data returned.", looseObjectSchema),
10382
+ 400: errorResponse("GA4 is not connected."),
10383
+ 404: errorResponse("Project not found.")
10130
10384
  }
10131
10385
  },
10132
10386
  {
@@ -10136,9 +10390,9 @@ var routeCatalog = [
10136
10390
  tags: ["ga4"],
10137
10391
  parameters: [nameParameter, analyticsWindowParameter],
10138
10392
  responses: {
10139
- 200: { description: "AI referral history returned." },
10140
- 400: { description: "GA4 is not connected." },
10141
- 404: { description: "Project not found." }
10393
+ 200: jsonArrayResponse("AI referral history returned.", "GA4AiReferralHistoryEntry"),
10394
+ 400: errorResponse("GA4 is not connected."),
10395
+ 404: errorResponse("Project not found.")
10142
10396
  }
10143
10397
  },
10144
10398
  {
@@ -10148,9 +10402,9 @@ var routeCatalog = [
10148
10402
  tags: ["ga4"],
10149
10403
  parameters: [nameParameter, analyticsWindowParameter],
10150
10404
  responses: {
10151
- 200: { description: "Social referral history returned." },
10152
- 400: { description: "GA4 is not connected." },
10153
- 404: { description: "Project not found." }
10405
+ 200: jsonArrayResponse("Social referral history returned.", "GA4SocialReferralHistoryEntry"),
10406
+ 400: errorResponse("GA4 is not connected."),
10407
+ 404: errorResponse("Project not found.")
10154
10408
  }
10155
10409
  },
10156
10410
  {
@@ -10160,9 +10414,10 @@ var routeCatalog = [
10160
10414
  tags: ["ga4"],
10161
10415
  parameters: [nameParameter],
10162
10416
  responses: {
10163
- 200: { description: "Social referral trend returned." },
10164
- 400: { description: "GA4 is not connected." },
10165
- 404: { description: "Project not found." }
10417
+ // TODO: Add `GaSocialReferralTrendResponse` Zod schema in contracts.
10418
+ 200: rawJsonResponse("Social referral trend returned.", looseObjectSchema),
10419
+ 400: errorResponse("GA4 is not connected."),
10420
+ 404: errorResponse("Project not found.")
10166
10421
  }
10167
10422
  },
10168
10423
  {
@@ -10172,9 +10427,10 @@ var routeCatalog = [
10172
10427
  tags: ["ga4"],
10173
10428
  parameters: [nameParameter],
10174
10429
  responses: {
10175
- 200: { description: "Attribution trend returned." },
10176
- 400: { description: "GA4 is not connected." },
10177
- 404: { description: "Project not found." }
10430
+ // TODO: Add `GaAttributionTrendResponse` Zod schema in contracts.
10431
+ 200: rawJsonResponse("Attribution trend returned.", looseObjectSchema),
10432
+ 400: errorResponse("GA4 is not connected."),
10433
+ 404: errorResponse("Project not found.")
10178
10434
  }
10179
10435
  },
10180
10436
  {
@@ -10184,9 +10440,9 @@ var routeCatalog = [
10184
10440
  tags: ["ga4"],
10185
10441
  parameters: [nameParameter, analyticsWindowParameter],
10186
10442
  responses: {
10187
- 200: { description: "Session history returned." },
10188
- 400: { description: "GA4 is not connected." },
10189
- 404: { description: "Project not found." }
10443
+ 200: jsonArrayResponse("Session history returned.", "GA4SessionHistoryEntry"),
10444
+ 400: errorResponse("GA4 is not connected."),
10445
+ 404: errorResponse("Project not found.")
10190
10446
  }
10191
10447
  },
10192
10448
  {
@@ -10196,9 +10452,10 @@ var routeCatalog = [
10196
10452
  tags: ["ga4"],
10197
10453
  parameters: [nameParameter],
10198
10454
  responses: {
10199
- 200: { description: "GA4 coverage data returned." },
10200
- 400: { description: "GA4 is not connected." },
10201
- 404: { description: "Project not found." }
10455
+ // TODO: Add `GaCoverageResponse` Zod schema in contracts.
10456
+ 200: rawJsonResponse("GA4 coverage data returned.", looseObjectSchema),
10457
+ 400: errorResponse("GA4 is not connected."),
10458
+ 404: errorResponse("Project not found.")
10202
10459
  }
10203
10460
  },
10204
10461
  // Intelligence
@@ -10213,8 +10470,9 @@ var routeCatalog = [
10213
10470
  { name: "runId", in: "query", description: "Filter by run ID.", schema: stringSchema }
10214
10471
  ],
10215
10472
  responses: {
10216
- 200: { description: "Insights returned." },
10217
- 404: { description: "Project not found." }
10473
+ // TODO: Add `InsightDto` Zod schema in contracts.
10474
+ 200: rawJsonResponse("Insights returned.", { type: "array", items: looseObjectSchema }),
10475
+ 404: errorResponse("Project not found.")
10218
10476
  }
10219
10477
  },
10220
10478
  {
@@ -10227,8 +10485,9 @@ var routeCatalog = [
10227
10485
  { name: "id", in: "path", required: true, description: "Insight ID.", schema: stringSchema }
10228
10486
  ],
10229
10487
  responses: {
10230
- 200: { description: "Insight returned." },
10231
- 404: { description: "Insight not found." }
10488
+ // TODO: Add `InsightDto` Zod schema in contracts.
10489
+ 200: rawJsonResponse("Insight returned.", looseObjectSchema),
10490
+ 404: errorResponse("Insight not found.")
10232
10491
  }
10233
10492
  },
10234
10493
  {
@@ -10241,8 +10500,9 @@ var routeCatalog = [
10241
10500
  { name: "id", in: "path", required: true, description: "Insight ID.", schema: stringSchema }
10242
10501
  ],
10243
10502
  responses: {
10244
- 200: { description: "Insight dismissed." },
10245
- 404: { description: "Insight not found." }
10503
+ // TODO: Add `InsightDto` Zod schema in contracts.
10504
+ 200: rawJsonResponse("Insight dismissed.", looseObjectSchema),
10505
+ 404: errorResponse("Insight not found.")
10246
10506
  }
10247
10507
  },
10248
10508
  {
@@ -10253,8 +10513,8 @@ var routeCatalog = [
10253
10513
  description: "Bundles every section the canonry-report HTML output needs (executive summary, client summary, agency diagnostics, action plan, citation scorecard, competitor landscape \u2014 citation + mention landscapes, AI citation sources, GSC, GA4, social/AI referrals, indexing health, citations trend, insights, and recommended next steps) into a single canonical JSON payload. Backs `canonry report <project>` and MCP report reads.",
10254
10514
  parameters: [nameParameter],
10255
10515
  responses: {
10256
- 200: { description: "Report returned." },
10257
- 404: { description: "Project not found." }
10516
+ 200: jsonResponse("Report returned.", "ProjectReportDto"),
10517
+ 404: errorResponse("Project not found.")
10258
10518
  }
10259
10519
  },
10260
10520
  {
@@ -10265,8 +10525,8 @@ var routeCatalog = [
10265
10525
  description: "Server-rendered self-contained HTML version of the project report. Same data as `/projects/{name}/report` (JSON), rendered through the canonry HTML report renderer in agency or client mode. Returns `text/html` with `Content-Disposition: attachment` so browsers download it as `canonry-report-<project>-<audience>-YYYY-MM-DD.html`. Open in a browser and Print \u2192 Save as PDF for a PDF copy.",
10266
10526
  parameters: [nameParameter, reportAudienceQueryParameter],
10267
10527
  responses: {
10268
- 200: { description: "HTML report returned." },
10269
- 404: { description: "Project not found." }
10528
+ 200: { description: "HTML report returned.", content: { "text/html": { schema: { type: "string" } } } },
10529
+ 404: errorResponse("Project not found.")
10270
10530
  }
10271
10531
  },
10272
10532
  {
@@ -10277,8 +10537,9 @@ var routeCatalog = [
10277
10537
  tags: ["intelligence"],
10278
10538
  parameters: [nameParameter],
10279
10539
  responses: {
10280
- 200: { description: "Health snapshot or no-data sentinel returned." },
10281
- 404: { description: "Project not found." }
10540
+ // TODO: Add `HealthSnapshotDto` Zod schema in contracts.
10541
+ 200: rawJsonResponse("Health snapshot or no-data sentinel returned.", looseObjectSchema),
10542
+ 404: errorResponse("Project not found.")
10282
10543
  }
10283
10544
  },
10284
10545
  {
@@ -10291,8 +10552,9 @@ var routeCatalog = [
10291
10552
  { name: "limit", in: "query", description: "Max results.", schema: stringSchema }
10292
10553
  ],
10293
10554
  responses: {
10294
- 200: { description: "Health history returned." },
10295
- 404: { description: "Project not found." }
10555
+ // TODO: Add `HealthSnapshotDto` Zod schema in contracts.
10556
+ 200: rawJsonResponse("Health history returned.", { type: "array", items: looseObjectSchema }),
10557
+ 404: errorResponse("Project not found.")
10296
10558
  }
10297
10559
  },
10298
10560
  {
@@ -10303,8 +10565,8 @@ var routeCatalog = [
10303
10565
  tags: ["intelligence"],
10304
10566
  parameters: [nameParameter],
10305
10567
  responses: {
10306
- 200: { description: "Citation visibility report or no-data sentinel returned." },
10307
- 404: { description: "Project not found." }
10568
+ 200: jsonResponse("Citation visibility report or no-data sentinel returned.", "CitationVisibilityResponse"),
10569
+ 404: errorResponse("Project not found.")
10308
10570
  }
10309
10571
  },
10310
10572
  // Content opportunity engine
@@ -10320,9 +10582,9 @@ var routeCatalog = [
10320
10582
  { name: "include-in-progress", in: "query", description: "Include rows with in-flight tracked actions.", schema: stringSchema }
10321
10583
  ],
10322
10584
  responses: {
10323
- 200: { description: "Targets returned." },
10324
- 400: { description: "Invalid limit." },
10325
- 404: { description: "Project not found." }
10585
+ 200: jsonResponse("Targets returned.", "ContentTargetsResponseDto"),
10586
+ 400: errorResponse("Invalid limit."),
10587
+ 404: errorResponse("Project not found.")
10326
10588
  }
10327
10589
  },
10328
10590
  {
@@ -10333,8 +10595,8 @@ var routeCatalog = [
10333
10595
  tags: ["content"],
10334
10596
  parameters: [nameParameter],
10335
10597
  responses: {
10336
- 200: { description: "Sources returned." },
10337
- 404: { description: "Project not found." }
10598
+ 200: jsonResponse("Sources returned.", "ContentSourcesResponseDto"),
10599
+ 404: errorResponse("Project not found.")
10338
10600
  }
10339
10601
  },
10340
10602
  {
@@ -10345,8 +10607,8 @@ var routeCatalog = [
10345
10607
  tags: ["content"],
10346
10608
  parameters: [nameParameter],
10347
10609
  responses: {
10348
- 200: { description: "Gaps returned." },
10349
- 404: { description: "Project not found." }
10610
+ 200: jsonResponse("Gaps returned.", "ContentGapsResponseDto"),
10611
+ 404: errorResponse("Project not found.")
10350
10612
  }
10351
10613
  },
10352
10614
  {
@@ -10357,8 +10619,9 @@ var routeCatalog = [
10357
10619
  tags: ["intelligence"],
10358
10620
  parameters: [nameParameter],
10359
10621
  responses: {
10360
- 200: { description: "Overview returned." },
10361
- 404: { description: "Project not found." }
10622
+ // TODO: Add `ProjectOverviewDto` Zod schema in contracts.
10623
+ 200: rawJsonResponse("Overview returned.", looseObjectSchema),
10624
+ 404: errorResponse("Project not found.")
10362
10625
  }
10363
10626
  },
10364
10627
  {
@@ -10373,9 +10636,10 @@ var routeCatalog = [
10373
10636
  { name: "limit", in: "query", description: "Max combined hits (1-50, default 25).", schema: stringSchema }
10374
10637
  ],
10375
10638
  responses: {
10376
- 200: { description: "Search hits returned." },
10377
- 400: { description: "Query string missing or too short." },
10378
- 404: { description: "Project not found." }
10639
+ // TODO: Add `ProjectSearchResponseDto` Zod schema in contracts (projectSearchResponseSchema exists).
10640
+ 200: rawJsonResponse("Search hits returned.", looseObjectSchema),
10641
+ 400: errorResponse("Query string missing or too short."),
10642
+ 404: errorResponse("Project not found.")
10379
10643
  }
10380
10644
  },
10381
10645
  {
@@ -10393,7 +10657,7 @@ var routeCatalog = [
10393
10657
  }
10394
10658
  ],
10395
10659
  responses: {
10396
- 200: { description: "Doctor report returned." }
10660
+ 200: jsonResponse("Doctor report returned.", "DoctorReportDto")
10397
10661
  }
10398
10662
  },
10399
10663
  {
@@ -10412,8 +10676,8 @@ var routeCatalog = [
10412
10676
  }
10413
10677
  ],
10414
10678
  responses: {
10415
- 200: { description: "Doctor report returned." },
10416
- 404: { description: "Project not found." }
10679
+ 200: jsonResponse("Doctor report returned.", "DoctorReportDto"),
10680
+ 404: errorResponse("Project not found.")
10417
10681
  }
10418
10682
  },
10419
10683
  {
@@ -10423,8 +10687,8 @@ var routeCatalog = [
10423
10687
  description: "Reports whether @duckdb/node-api is installed in the local plugin dir. Returns MISSING_DEPENDENCY (422) on deployments that cannot host the plugin (e.g. the cloud API).",
10424
10688
  tags: ["backlinks"],
10425
10689
  responses: {
10426
- 200: { description: "Install status returned." },
10427
- 422: { description: "Backlinks feature is not available on this deployment." }
10690
+ 200: jsonResponse("Install status returned.", "BacklinksInstallStatusDto"),
10691
+ 422: errorResponse("Backlinks feature is not available on this deployment.")
10428
10692
  }
10429
10693
  },
10430
10694
  {
@@ -10434,8 +10698,8 @@ var routeCatalog = [
10434
10698
  description: "Idempotently installs DuckDB into the canonry plugin dir. Returns MISSING_DEPENDENCY (422) when the host cannot perform the install.",
10435
10699
  tags: ["backlinks"],
10436
10700
  responses: {
10437
- 200: { description: "Installed (or already present)." },
10438
- 422: { description: "Backlinks feature is not available on this deployment." }
10701
+ 200: jsonResponse("Installed (or already present).", "BacklinksInstallResultDto"),
10702
+ 422: errorResponse("Backlinks feature is not available on this deployment.")
10439
10703
  }
10440
10704
  },
10441
10705
  {
@@ -10458,10 +10722,10 @@ var routeCatalog = [
10458
10722
  }
10459
10723
  },
10460
10724
  responses: {
10461
- 200: { description: "Existing in-flight sync returned." },
10462
- 201: { description: "Sync queued." },
10463
- 400: { description: "Invalid release id." },
10464
- 422: { description: "Backlinks feature is not available on this deployment." }
10725
+ 200: jsonResponse("Existing in-flight sync returned.", "CcReleaseSyncDto"),
10726
+ 201: jsonResponse("Sync queued.", "CcReleaseSyncDto"),
10727
+ 400: errorResponse("Invalid release id."),
10728
+ 422: errorResponse("Backlinks feature is not available on this deployment.")
10465
10729
  }
10466
10730
  },
10467
10731
  {
@@ -10471,7 +10735,7 @@ var routeCatalog = [
10471
10735
  description: "Returns syncs ordered by updatedAt DESC \u2014 re-queued rows surface ahead of untouched newer rows.",
10472
10736
  tags: ["backlinks"],
10473
10737
  responses: {
10474
- 200: { description: "Sync history returned." }
10738
+ 200: jsonArrayResponse("Sync history returned.", "CcReleaseSyncDto")
10475
10739
  }
10476
10740
  },
10477
10741
  {
@@ -10480,7 +10744,10 @@ var routeCatalog = [
10480
10744
  summary: "Get the most recently-updated Common Crawl release sync",
10481
10745
  tags: ["backlinks"],
10482
10746
  responses: {
10483
- 200: { description: "Latest sync returned, or null when no sync exists." }
10747
+ // Returns CcReleaseSyncDto | null
10748
+ 200: rawJsonResponse("Latest sync returned, or null when no sync exists.", {
10749
+ oneOf: [{ $ref: "#/components/schemas/CcReleaseSyncDto" }, { type: "null" }]
10750
+ })
10484
10751
  }
10485
10752
  },
10486
10753
  {
@@ -10489,7 +10756,7 @@ var routeCatalog = [
10489
10756
  summary: "List cached Common Crawl releases on the local filesystem",
10490
10757
  tags: ["backlinks"],
10491
10758
  responses: {
10492
- 200: { description: "Cached release metadata returned." }
10759
+ 200: jsonArrayResponse("Cached release metadata returned.", "CcCachedRelease")
10493
10760
  }
10494
10761
  },
10495
10762
  {
@@ -10499,8 +10766,10 @@ var routeCatalog = [
10499
10766
  description: "Probes Common Crawl by HEAD-checking quarterly release slugs and returns the newest one published. The local server caches the result for ~5 minutes so repeated calls do not hammer Common Crawl.",
10500
10767
  tags: ["backlinks"],
10501
10768
  responses: {
10502
- 200: { description: "Latest available release, or null when no candidate slug responded." },
10503
- 422: { description: "Backlinks feature is not available on this deployment." }
10769
+ 200: rawJsonResponse("Latest available release, or null when no candidate slug responded.", {
10770
+ oneOf: [{ $ref: "#/components/schemas/CcAvailableRelease" }, { type: "null" }]
10771
+ }),
10772
+ 422: errorResponse("Backlinks feature is not available on this deployment.")
10504
10773
  }
10505
10774
  },
10506
10775
  {
@@ -10518,9 +10787,10 @@ var routeCatalog = [
10518
10787
  }
10519
10788
  ],
10520
10789
  responses: {
10521
- 200: { description: "Cache pruned." },
10522
- 400: { description: "Invalid release id." },
10523
- 422: { description: "Backlinks feature is not available on this deployment." }
10790
+ // TODO: Add `BacklinksCachePruneResultDto` Zod schema in contracts.
10791
+ 200: rawJsonResponse("Cache pruned.", looseObjectSchema),
10792
+ 400: errorResponse("Invalid release id."),
10793
+ 422: errorResponse("Backlinks feature is not available on this deployment.")
10524
10794
  }
10525
10795
  },
10526
10796
  {
@@ -10544,10 +10814,10 @@ var routeCatalog = [
10544
10814
  }
10545
10815
  },
10546
10816
  responses: {
10547
- 201: { description: "Extract run queued." },
10548
- 400: { description: "Invalid release id." },
10549
- 404: { description: "Project not found." },
10550
- 422: { description: "Backlinks feature is not available on this deployment." }
10817
+ 201: jsonResponse("Extract run queued.", "RunDto"),
10818
+ 400: errorResponse("Invalid release id."),
10819
+ 404: errorResponse("Project not found."),
10820
+ 422: errorResponse("Backlinks feature is not available on this deployment.")
10551
10821
  }
10552
10822
  },
10553
10823
  {
@@ -10560,8 +10830,10 @@ var routeCatalog = [
10560
10830
  { name: "release", in: "query", description: "Release id filter.", schema: stringSchema }
10561
10831
  ],
10562
10832
  responses: {
10563
- 200: { description: "Summary returned, or null when no backlinks exist." },
10564
- 404: { description: "Project not found." }
10833
+ 200: rawJsonResponse("Summary returned, or null when no backlinks exist.", {
10834
+ oneOf: [{ $ref: "#/components/schemas/BacklinkSummaryDto" }, { type: "null" }]
10835
+ }),
10836
+ 404: errorResponse("Project not found.")
10565
10837
  }
10566
10838
  },
10567
10839
  {
@@ -10576,8 +10848,8 @@ var routeCatalog = [
10576
10848
  { name: "offset", in: "query", description: "Pagination offset.", schema: stringSchema }
10577
10849
  ],
10578
10850
  responses: {
10579
- 200: { description: "Domain list returned." },
10580
- 404: { description: "Project not found." }
10851
+ 200: jsonResponse("Domain list returned.", "BacklinkListResponse"),
10852
+ 404: errorResponse("Project not found.")
10581
10853
  }
10582
10854
  },
10583
10855
  {
@@ -10587,8 +10859,8 @@ var routeCatalog = [
10587
10859
  tags: ["backlinks"],
10588
10860
  parameters: [nameParameter],
10589
10861
  responses: {
10590
- 200: { description: "History returned oldest-first by queriedAt." },
10591
- 404: { description: "Project not found." }
10862
+ 200: jsonArrayResponse("History returned oldest-first by queriedAt.", "BacklinkHistoryEntry"),
10863
+ 404: errorResponse("Project not found.")
10592
10864
  }
10593
10865
  },
10594
10866
  {
@@ -10617,9 +10889,9 @@ var routeCatalog = [
10617
10889
  }
10618
10890
  },
10619
10891
  responses: {
10620
- 200: { description: "Traffic source DTO returned." },
10621
- 400: { description: "Invalid Cloud Run connection request." },
10622
- 404: { description: "Project not found." }
10892
+ 200: jsonResponse("Traffic source DTO returned.", "TrafficSourceDto"),
10893
+ 400: errorResponse("Invalid Cloud Run connection request."),
10894
+ 404: errorResponse("Project not found.")
10623
10895
  }
10624
10896
  },
10625
10897
  {
@@ -10647,10 +10919,10 @@ var routeCatalog = [
10647
10919
  }
10648
10920
  },
10649
10921
  responses: {
10650
- 200: { description: "Traffic source DTO returned." },
10651
- 400: { description: "Invalid WordPress connection request." },
10652
- 404: { description: "Project not found." },
10653
- 502: { description: "WordPress plugin endpoint probe failed (bad credentials, unreachable host, etc.)." }
10922
+ 200: jsonResponse("Traffic source DTO returned.", "TrafficSourceDto"),
10923
+ 400: errorResponse("Invalid WordPress connection request."),
10924
+ 404: errorResponse("Project not found."),
10925
+ 502: errorResponse("WordPress plugin endpoint probe failed (bad credentials, unreachable host, etc.).")
10654
10926
  }
10655
10927
  },
10656
10928
  {
@@ -10679,10 +10951,10 @@ var routeCatalog = [
10679
10951
  }
10680
10952
  },
10681
10953
  responses: {
10682
- 200: { description: "Traffic source DTO returned." },
10683
- 400: { description: "Invalid Vercel connection request." },
10684
- 404: { description: "Project not found." },
10685
- 502: { description: "Vercel request-logs endpoint probe failed (bad token, wrong project / team id, unreachable host, etc.)." }
10954
+ 200: jsonResponse("Traffic source DTO returned.", "TrafficSourceDto"),
10955
+ 400: errorResponse("Invalid Vercel connection request."),
10956
+ 404: errorResponse("Project not found."),
10957
+ 502: errorResponse("Vercel request-logs endpoint probe failed (bad token, wrong project / team id, unreachable host, etc.).")
10686
10958
  }
10687
10959
  },
10688
10960
  {
@@ -10709,10 +10981,10 @@ var routeCatalog = [
10709
10981
  }
10710
10982
  },
10711
10983
  responses: {
10712
- 200: { description: "Sync summary returned." },
10713
- 400: { description: "Invalid sync request or missing credentials." },
10714
- 404: { description: "Project or traffic source not found." },
10715
- 502: { description: "Upstream Cloud Run pull or auth-token resolution failed." }
10984
+ 200: jsonResponse("Sync summary returned.", "TrafficSyncResponse"),
10985
+ 400: errorResponse("Invalid sync request or missing credentials."),
10986
+ 404: errorResponse("Project or traffic source not found."),
10987
+ 502: errorResponse("Upstream Cloud Run pull or auth-token resolution failed.")
10716
10988
  }
10717
10989
  },
10718
10990
  {
@@ -10739,9 +11011,9 @@ var routeCatalog = [
10739
11011
  }
10740
11012
  },
10741
11013
  responses: {
10742
- 200: { description: "Backfill submitted; poll the returned runId for completion." },
10743
- 400: { description: "Invalid backfill request or missing credentials." },
10744
- 404: { description: "Project or traffic source not found." }
11014
+ 200: jsonResponse("Backfill submitted; poll the returned runId for completion.", "TrafficBackfillResponse"),
11015
+ 400: errorResponse("Invalid backfill request or missing credentials."),
11016
+ 404: errorResponse("Project or traffic source not found.")
10745
11017
  }
10746
11018
  },
10747
11019
  {
@@ -10751,8 +11023,8 @@ var routeCatalog = [
10751
11023
  tags: ["traffic"],
10752
11024
  parameters: [nameParameter],
10753
11025
  responses: {
10754
- 200: { description: "Source list returned." },
10755
- 404: { description: "Project not found." }
11026
+ 200: jsonResponse("Source list returned.", "TrafficSourceListResponse"),
11027
+ 404: errorResponse("Project not found.")
10756
11028
  }
10757
11029
  },
10758
11030
  {
@@ -10763,8 +11035,8 @@ var routeCatalog = [
10763
11035
  tags: ["traffic"],
10764
11036
  parameters: [nameParameter],
10765
11037
  responses: {
10766
- 200: { description: "Status returned." },
10767
- 404: { description: "Project not found." }
11038
+ 200: jsonResponse("Status returned.", "TrafficStatusResponse"),
11039
+ 404: errorResponse("Project not found.")
10768
11040
  }
10769
11041
  },
10770
11042
  {
@@ -10777,8 +11049,8 @@ var routeCatalog = [
10777
11049
  { name: "id", in: "path", required: true, description: "Traffic source ID.", schema: stringSchema }
10778
11050
  ],
10779
11051
  responses: {
10780
- 200: { description: "Source detail returned." },
10781
- 404: { description: "Project or source not found." }
11052
+ 200: jsonResponse("Source detail returned.", "TrafficSourceDetailDto"),
11053
+ 404: errorResponse("Project or source not found.")
10782
11054
  }
10783
11055
  },
10784
11056
  {
@@ -10796,9 +11068,9 @@ var routeCatalog = [
10796
11068
  { name: "sourceId", in: "query", description: "Restrict to a single traffic source.", schema: stringSchema }
10797
11069
  ],
10798
11070
  responses: {
10799
- 200: { description: "Events returned with windowed totals." },
10800
- 400: { description: "Invalid query parameters." },
10801
- 404: { description: "Project not found." }
11071
+ 200: jsonResponse("Events returned with windowed totals.", "TrafficEventsResponse"),
11072
+ 400: errorResponse("Invalid query parameters."),
11073
+ 404: errorResponse("Project not found.")
10802
11074
  }
10803
11075
  },
10804
11076
  {
@@ -10829,10 +11101,11 @@ var routeCatalog = [
10829
11101
  }
10830
11102
  },
10831
11103
  responses: {
10832
- 200: { description: "An in-flight session with the same project + ICP was reused; returns { runId, sessionId, status, consolidated: true }. The request's dedupThreshold / maxProbes are ignored." },
10833
- 201: { description: "New discovery session enqueued; returns { runId, sessionId, status, consolidated: false }." },
10834
- 400: { description: "Missing or invalid ICP / parameters." },
10835
- 404: { description: "Project not found." }
11104
+ // TODO: Add `DiscoveryRunResponse` Zod schema in contracts (`{ runId, sessionId, status, consolidated }`).
11105
+ 200: rawJsonResponse("An in-flight session with the same project + ICP was reused; returns { runId, sessionId, status, consolidated: true }. The request's dedupThreshold / maxProbes are ignored.", looseObjectSchema),
11106
+ 201: rawJsonResponse("New discovery session enqueued; returns { runId, sessionId, status, consolidated: false }.", looseObjectSchema),
11107
+ 400: errorResponse("Missing or invalid ICP / parameters."),
11108
+ 404: errorResponse("Project not found.")
10836
11109
  }
10837
11110
  },
10838
11111
  {
@@ -10846,8 +11119,8 @@ var routeCatalog = [
10846
11119
  { name: "limit", in: "query", description: "Max sessions returned. Default 50.", schema: stringSchema }
10847
11120
  ],
10848
11121
  responses: {
10849
- 200: { description: "Sessions returned." },
10850
- 404: { description: "Project not found." }
11122
+ 200: jsonArrayResponse("Sessions returned.", "DiscoverySessionDto"),
11123
+ 404: errorResponse("Project not found.")
10851
11124
  }
10852
11125
  },
10853
11126
  {
@@ -10861,8 +11134,8 @@ var routeCatalog = [
10861
11134
  { name: "id", in: "path", required: true, description: "Discovery session ID.", schema: stringSchema }
10862
11135
  ],
10863
11136
  responses: {
10864
- 200: { description: "Session detail returned." },
10865
- 404: { description: "Project or session not found." }
11137
+ 200: jsonResponse("Session detail returned.", "DiscoverySessionDetailDto"),
11138
+ 404: errorResponse("Project or session not found.")
10866
11139
  }
10867
11140
  },
10868
11141
  {
@@ -10876,8 +11149,8 @@ var routeCatalog = [
10876
11149
  { name: "id", in: "path", required: true, description: "Discovery session ID.", schema: stringSchema }
10877
11150
  ],
10878
11151
  responses: {
10879
- 200: { description: "Promote preview returned." },
10880
- 404: { description: "Project or session not found." }
11152
+ 200: jsonResponse("Promote preview returned.", "DiscoveryPromotePreview"),
11153
+ 404: errorResponse("Project or session not found.")
10881
11154
  }
10882
11155
  },
10883
11156
  {
@@ -10920,9 +11193,9 @@ var routeCatalog = [
10920
11193
  }
10921
11194
  },
10922
11195
  responses: {
10923
- 200: { description: "Promotion applied; returns promoted + skipped query/competitor lists." },
10924
- 400: { description: "Session is not completed, or invalid request body." },
10925
- 404: { description: "Project or session not found." }
11196
+ 200: jsonResponse("Promotion applied; returns promoted + skipped query/competitor lists.", "DiscoveryPromoteResult"),
11197
+ 400: errorResponse("Session is not completed, or invalid request body."),
11198
+ 404: errorResponse("Project or session not found.")
10926
11199
  }
10927
11200
  }
10928
11201
  ];
@@ -10935,8 +11208,9 @@ var canonryLocalRouteCatalog = [
10935
11208
  tags: ["agent"],
10936
11209
  parameters: [nameParameter],
10937
11210
  responses: {
10938
- 200: { description: "Transcript returned." },
10939
- 404: { description: "Project not found." }
11211
+ // TODO: Add `AgentTranscriptDto` Zod schema in contracts.
11212
+ 200: rawJsonResponse("Transcript returned.", looseObjectSchema),
11213
+ 404: errorResponse("Project not found.")
10940
11214
  }
10941
11215
  },
10942
11216
  {
@@ -10947,8 +11221,9 @@ var canonryLocalRouteCatalog = [
10947
11221
  tags: ["agent"],
10948
11222
  parameters: [nameParameter],
10949
11223
  responses: {
10950
- 200: { description: "Session reset." },
10951
- 404: { description: "Project not found." }
11224
+ // Returns { status: 'reset' } sentinel.
11225
+ 200: rawJsonResponse("Session reset.", { type: "object", properties: { status: { type: "string", enum: ["reset"] } } }),
11226
+ 404: errorResponse("Project not found.")
10952
11227
  }
10953
11228
  },
10954
11229
  {
@@ -10959,8 +11234,9 @@ var canonryLocalRouteCatalog = [
10959
11234
  tags: ["agent"],
10960
11235
  parameters: [nameParameter],
10961
11236
  responses: {
10962
- 200: { description: "Memory entries returned." },
10963
- 404: { description: "Project not found." }
11237
+ // TODO: Add `AgentMemoryListResponse` Zod schema in contracts.
11238
+ 200: rawJsonResponse("Memory entries returned.", looseObjectSchema),
11239
+ 404: errorResponse("Project not found.")
10964
11240
  }
10965
11241
  },
10966
11242
  {
@@ -10986,9 +11262,10 @@ var canonryLocalRouteCatalog = [
10986
11262
  }
10987
11263
  },
10988
11264
  responses: {
10989
- 200: { description: "Entry upserted." },
10990
- 400: { description: "Validation failed (key length, value size, reserved prefix)." },
10991
- 404: { description: "Project not found." }
11265
+ // TODO: Add `AgentMemoryEntryDto` Zod schema in contracts.
11266
+ 200: rawJsonResponse("Entry upserted.", looseObjectSchema),
11267
+ 400: errorResponse("Validation failed (key length, value size, reserved prefix)."),
11268
+ 404: errorResponse("Project not found.")
10992
11269
  }
10993
11270
  },
10994
11271
  {
@@ -11013,9 +11290,10 @@ var canonryLocalRouteCatalog = [
11013
11290
  }
11014
11291
  },
11015
11292
  responses: {
11016
- 200: { description: "Entry removed or already absent." },
11017
- 400: { description: "Validation failed (reserved prefix)." },
11018
- 404: { description: "Project not found." }
11293
+ // Returns { status: 'removed' | 'missing' } sentinel.
11294
+ 200: rawJsonResponse("Entry removed or already absent.", { type: "object", properties: { status: { type: "string", enum: ["removed", "missing"] } } }),
11295
+ 400: errorResponse("Validation failed (reserved prefix)."),
11296
+ 404: errorResponse("Project not found.")
11019
11297
  }
11020
11298
  },
11021
11299
  {
@@ -11026,8 +11304,8 @@ var canonryLocalRouteCatalog = [
11026
11304
  tags: ["agent"],
11027
11305
  parameters: [nameParameter],
11028
11306
  responses: {
11029
- 200: { description: "Providers returned." },
11030
- 404: { description: "Project not found." }
11307
+ 200: jsonResponse("Providers returned.", "AgentProvidersResponseDto"),
11308
+ 404: errorResponse("Project not found.")
11031
11309
  }
11032
11310
  },
11033
11311
  {
@@ -11066,10 +11344,11 @@ var canonryLocalRouteCatalog = [
11066
11344
  }
11067
11345
  },
11068
11346
  responses: {
11069
- 200: { description: "SSE stream of AgentEvent frames." },
11070
- 400: { description: "Missing or empty prompt." },
11071
- 404: { description: "Project not found." },
11072
- 409: { description: "Another Aero turn is already in flight." }
11347
+ // Returns text/event-stream codegen consumers should treat as a stream.
11348
+ 200: { description: "SSE stream of AgentEvent frames.", content: { "text/event-stream": { schema: { type: "string" } } } },
11349
+ 400: errorResponse("Missing or empty prompt."),
11350
+ 404: errorResponse("Project not found."),
11351
+ 409: errorResponse("Another Aero turn is already in flight.")
11073
11352
  }
11074
11353
  }
11075
11354
  ];
@@ -11095,8 +11374,14 @@ function buildOpenApiDocument(info = {}) {
11095
11374
  acc[fullPath] = pathItem;
11096
11375
  return acc;
11097
11376
  }, {});
11377
+ const schemas = buildComponentSchemas();
11098
11378
  return {
11099
- openapi: "3.1.0",
11379
+ // OpenAPI 3.0 (not 3.1) so `nullable: true` on emitted schemas is the
11380
+ // canonical nullability marker. `z.toJSONSchema(..., { target: 'openapi-3.0' })`
11381
+ // outputs `nullable: true`; declaring 3.1 would tell consumers (and the
11382
+ // hey-api codegen) to expect 3.1-style `type: ["string", "null"]` instead,
11383
+ // and they'd silently strip the `null` from optional fields.
11384
+ openapi: "3.0.0",
11100
11385
  info: {
11101
11386
  title: info.title ?? "Canonry API",
11102
11387
  version: info.version ?? "0.0.0",
@@ -11115,7 +11400,8 @@ function buildOpenApiDocument(info = {}) {
11115
11400
  scheme: "bearer",
11116
11401
  bearerFormat: "API key"
11117
11402
  }
11118
- }
11403
+ },
11404
+ schemas
11119
11405
  },
11120
11406
  paths
11121
11407
  };
@@ -11290,12 +11576,12 @@ async function telemetryRoutes(app, opts) {
11290
11576
 
11291
11577
  // ../api-routes/src/schedules.ts
11292
11578
  import crypto11 from "crypto";
11293
- import { and as and8, eq as eq16 } from "drizzle-orm";
11579
+ import { and as and11, eq as eq16 } from "drizzle-orm";
11294
11580
  function parseKindParam(raw) {
11295
11581
  if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
11296
11582
  const parsed = schedulableRunKindSchema.safeParse(raw);
11297
11583
  if (!parsed.success) {
11298
- throw validationError(`Invalid kind "${String(raw)}". Must be one of: ${Object.values(SchedulableRunKinds).join(", ")}`);
11584
+ throw validationError(`Invalid kind "${JSON.stringify(raw)}". Must be one of: ${Object.values(SchedulableRunKinds).join(", ")}`);
11299
11585
  }
11300
11586
  return parsed.data;
11301
11587
  }
@@ -11356,7 +11642,7 @@ async function scheduleRoutes(app, opts) {
11356
11642
  }
11357
11643
  const now = (/* @__PURE__ */ new Date()).toISOString();
11358
11644
  const enabledInt = enabled === false ? 0 : 1;
11359
- const existing = app.db.select().from(schedules).where(and8(eq16(schedules.projectId, project.id), eq16(schedules.kind, kind))).get();
11645
+ const existing = app.db.select().from(schedules).where(and11(eq16(schedules.projectId, project.id), eq16(schedules.kind, kind))).get();
11360
11646
  if (existing) {
11361
11647
  app.db.update(schedules).set({
11362
11648
  cronExpr,
@@ -11390,13 +11676,13 @@ async function scheduleRoutes(app, opts) {
11390
11676
  diff: { kind, cronExpr, preset, timezone, providers, sourceId }
11391
11677
  });
11392
11678
  opts.onScheduleUpdated?.("upsert", project.id, kind);
11393
- const schedule = app.db.select().from(schedules).where(and8(eq16(schedules.projectId, project.id), eq16(schedules.kind, kind))).get();
11679
+ const schedule = app.db.select().from(schedules).where(and11(eq16(schedules.projectId, project.id), eq16(schedules.kind, kind))).get();
11394
11680
  return reply.status(existing ? 200 : 201).send(formatSchedule(schedule));
11395
11681
  });
11396
11682
  app.get("/projects/:name/schedule", async (request, reply) => {
11397
11683
  const project = resolveProject(app.db, request.params.name);
11398
11684
  const kind = parseKindParam(request.query?.kind);
11399
- const schedule = app.db.select().from(schedules).where(and8(eq16(schedules.projectId, project.id), eq16(schedules.kind, kind))).get();
11685
+ const schedule = app.db.select().from(schedules).where(and11(eq16(schedules.projectId, project.id), eq16(schedules.kind, kind))).get();
11400
11686
  if (!schedule) {
11401
11687
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
11402
11688
  }
@@ -11405,7 +11691,7 @@ async function scheduleRoutes(app, opts) {
11405
11691
  app.delete("/projects/:name/schedule", async (request, reply) => {
11406
11692
  const project = resolveProject(app.db, request.params.name);
11407
11693
  const kind = parseKindParam(request.query?.kind);
11408
- const schedule = app.db.select().from(schedules).where(and8(eq16(schedules.projectId, project.id), eq16(schedules.kind, kind))).get();
11694
+ const schedule = app.db.select().from(schedules).where(and11(eq16(schedules.projectId, project.id), eq16(schedules.kind, kind))).get();
11409
11695
  if (!schedule) {
11410
11696
  throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
11411
11697
  }
@@ -11562,7 +11848,7 @@ function formatNotification(row) {
11562
11848
 
11563
11849
  // ../api-routes/src/google.ts
11564
11850
  import crypto14 from "crypto";
11565
- import { eq as eq18, and as and9, desc as desc8, sql as sql7 } from "drizzle-orm";
11851
+ import { eq as eq18, and as and12, desc as desc8, sql as sql7 } from "drizzle-orm";
11566
11852
 
11567
11853
  // ../integration-google/src/constants.ts
11568
11854
  var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
@@ -11946,6 +12232,22 @@ var GA4_DEFAULT_SYNC_DAYS = 30;
11946
12232
  var GA4_MAX_SYNC_DAYS = 90;
11947
12233
  var GA4_REQUEST_TIMEOUT_MS = 3e4;
11948
12234
  var GA4_MAX_PAGES = 50;
12235
+ var GA4_DIMENSIONS = {
12236
+ date: "date",
12237
+ landingPagePlusQueryString: "landingPagePlusQueryString",
12238
+ sessionSource: "sessionSource",
12239
+ sessionMedium: "sessionMedium",
12240
+ sessionManualSource: "sessionManualSource",
12241
+ sessionManualMedium: "sessionManualMedium",
12242
+ firstUserSource: "firstUserSource",
12243
+ firstUserMedium: "firstUserMedium",
12244
+ sessionDefaultChannelGrouping: "sessionDefaultChannelGrouping",
12245
+ sessionDefaultChannelGroup: "sessionDefaultChannelGroup"
12246
+ };
12247
+ var GA4_METRICS = {
12248
+ sessions: "sessions",
12249
+ totalUsers: "totalUsers"
12250
+ };
11949
12251
 
11950
12252
  // ../integration-google-analytics/src/types.ts
11951
12253
  var GA4ApiError = class extends Error {
@@ -12137,8 +12439,8 @@ var AI_REFERRAL_SOURCE_FILTERS = [
12137
12439
  { matchType: "CONTAINS", value: "anthropic" },
12138
12440
  { matchType: "CONTAINS", value: "copilot" },
12139
12441
  { matchType: "CONTAINS", value: "phind" },
12140
- { matchType: "EXACT", value: "you.com" },
12141
- { matchType: "CONTAINS", value: "meta.ai" }
12442
+ { matchType: "EXACT", value: AI_ENGINE_DOMAINS.you },
12443
+ { matchType: "CONTAINS", value: AI_ENGINE_DOMAINS.metaAi }
12142
12444
  ];
12143
12445
  async function fetchTrafficByLandingPage(accessToken, propertyId, days) {
12144
12446
  validateAccessToken2(accessToken);
@@ -12157,12 +12459,12 @@ async function fetchTrafficByLandingPage(accessToken, propertyId, days) {
12157
12459
  const request = {
12158
12460
  dateRanges: [{ startDate: formatDate2(startDate), endDate: formatDate2(endDate) }],
12159
12461
  dimensions: [
12160
- { name: "date" },
12161
- { name: "landingPagePlusQueryString" }
12462
+ { name: GA4_DIMENSIONS.date },
12463
+ { name: GA4_DIMENSIONS.landingPagePlusQueryString }
12162
12464
  ],
12163
12465
  metrics: [
12164
- { name: "sessions" },
12165
- { name: "totalUsers" }
12466
+ { name: GA4_METRICS.sessions },
12467
+ { name: GA4_METRICS.totalUsers }
12166
12468
  ],
12167
12469
  limit: PAGE_SIZE,
12168
12470
  offset
@@ -12191,11 +12493,11 @@ async function fetchTrafficByLandingPage(accessToken, propertyId, days) {
12191
12493
  organicPageCount++;
12192
12494
  const organicRequest = {
12193
12495
  dateRanges: [{ startDate: formatDate2(startDate), endDate: formatDate2(endDate) }],
12194
- dimensions: [{ name: "date" }, { name: "landingPagePlusQueryString" }],
12195
- metrics: [{ name: "sessions" }],
12496
+ dimensions: [{ name: GA4_DIMENSIONS.date }, { name: GA4_DIMENSIONS.landingPagePlusQueryString }],
12497
+ metrics: [{ name: GA4_METRICS.sessions }],
12196
12498
  dimensionFilter: {
12197
12499
  filter: {
12198
- fieldName: "sessionDefaultChannelGrouping",
12500
+ fieldName: GA4_DIMENSIONS.sessionDefaultChannelGrouping,
12199
12501
  stringFilter: { matchType: "EXACT", value: "Organic Search" }
12200
12502
  }
12201
12503
  },
@@ -12219,11 +12521,11 @@ async function fetchTrafficByLandingPage(accessToken, propertyId, days) {
12219
12521
  directPageCount++;
12220
12522
  const directRequest = {
12221
12523
  dateRanges: [{ startDate: formatDate2(startDate), endDate: formatDate2(endDate) }],
12222
- dimensions: [{ name: "date" }, { name: "landingPagePlusQueryString" }],
12223
- metrics: [{ name: "sessions" }],
12524
+ dimensions: [{ name: GA4_DIMENSIONS.date }, { name: GA4_DIMENSIONS.landingPagePlusQueryString }],
12525
+ metrics: [{ name: GA4_METRICS.sessions }],
12224
12526
  dimensionFilter: {
12225
12527
  filter: {
12226
- fieldName: "sessionDefaultChannelGrouping",
12528
+ fieldName: GA4_DIMENSIONS.sessionDefaultChannelGrouping,
12227
12529
  stringFilter: { matchType: "EXACT", value: "Direct" }
12228
12530
  }
12229
12531
  },
@@ -12268,8 +12570,8 @@ async function verifyConnectionWithToken(accessToken, propertyId) {
12268
12570
  startDate.setDate(startDate.getDate() - 1);
12269
12571
  await runReport(accessToken, propertyId, {
12270
12572
  dateRanges: [{ startDate: formatDate2(startDate), endDate: formatDate2(endDate) }],
12271
- dimensions: [{ name: "date" }],
12272
- metrics: [{ name: "sessions" }],
12573
+ dimensions: [{ name: GA4_DIMENSIONS.date }],
12574
+ metrics: [{ name: GA4_METRICS.sessions }],
12273
12575
  limit: 1
12274
12576
  });
12275
12577
  return true;
@@ -12287,16 +12589,16 @@ async function fetchAggregateSummary(accessToken, propertyId, days) {
12287
12589
  {
12288
12590
  dateRanges: [dateRange],
12289
12591
  dimensions: [],
12290
- metrics: [{ name: "sessions" }, { name: "totalUsers" }],
12592
+ metrics: [{ name: GA4_METRICS.sessions }, { name: GA4_METRICS.totalUsers }],
12291
12593
  limit: 1
12292
12594
  },
12293
12595
  {
12294
12596
  dateRanges: [dateRange],
12295
12597
  dimensions: [],
12296
- metrics: [{ name: "sessions" }],
12598
+ metrics: [{ name: GA4_METRICS.sessions }],
12297
12599
  dimensionFilter: {
12298
12600
  filter: {
12299
- fieldName: "sessionDefaultChannelGrouping",
12601
+ fieldName: GA4_DIMENSIONS.sessionDefaultChannelGrouping,
12300
12602
  stringFilter: { matchType: "EXACT", value: "Organic Search" }
12301
12603
  }
12302
12604
  },
@@ -12332,16 +12634,16 @@ async function fetchWindowSummary(accessToken, propertyId, windowKey) {
12332
12634
  {
12333
12635
  dateRanges: [dateRange],
12334
12636
  dimensions: [],
12335
- metrics: [{ name: "sessions" }, { name: "totalUsers" }],
12637
+ metrics: [{ name: GA4_METRICS.sessions }, { name: GA4_METRICS.totalUsers }],
12336
12638
  limit: 1
12337
12639
  },
12338
12640
  {
12339
12641
  dateRanges: [dateRange],
12340
12642
  dimensions: [],
12341
- metrics: [{ name: "sessions" }],
12643
+ metrics: [{ name: GA4_METRICS.sessions }],
12342
12644
  dimensionFilter: {
12343
12645
  filter: {
12344
- fieldName: "sessionDefaultChannelGrouping",
12646
+ fieldName: GA4_DIMENSIONS.sessionDefaultChannelGrouping,
12345
12647
  stringFilter: { matchType: "EXACT", value: "Organic Search" }
12346
12648
  }
12347
12649
  },
@@ -12350,10 +12652,10 @@ async function fetchWindowSummary(accessToken, propertyId, windowKey) {
12350
12652
  {
12351
12653
  dateRanges: [dateRange],
12352
12654
  dimensions: [],
12353
- metrics: [{ name: "sessions" }],
12655
+ metrics: [{ name: GA4_METRICS.sessions }],
12354
12656
  dimensionFilter: {
12355
12657
  filter: {
12356
- fieldName: "sessionDefaultChannelGrouping",
12658
+ fieldName: GA4_DIMENSIONS.sessionDefaultChannelGrouping,
12357
12659
  stringFilter: { matchType: "EXACT", value: "Direct" }
12358
12660
  }
12359
12661
  },
@@ -12386,9 +12688,9 @@ async function fetchAiReferrals(accessToken, propertyId, days) {
12386
12688
  const PAGE_SIZE = 1e3;
12387
12689
  const rows = [];
12388
12690
  const dimensionPairs = [
12389
- ["sessionSource", "sessionMedium", "session"],
12390
- ["firstUserSource", "firstUserMedium", "first_user"],
12391
- ["sessionManualSource", "sessionManualMedium", "manual_utm"]
12691
+ [GA4_DIMENSIONS.sessionSource, GA4_DIMENSIONS.sessionMedium, "session"],
12692
+ [GA4_DIMENSIONS.firstUserSource, GA4_DIMENSIONS.firstUserMedium, "first_user"],
12693
+ [GA4_DIMENSIONS.sessionManualSource, GA4_DIMENSIONS.sessionManualMedium, "manual_utm"]
12392
12694
  ];
12393
12695
  for (const [sourceDim, mediumDim, dimLabel] of dimensionPairs) {
12394
12696
  let offset = 0;
@@ -12398,15 +12700,15 @@ async function fetchAiReferrals(accessToken, propertyId, days) {
12398
12700
  const request = {
12399
12701
  dateRanges: [{ startDate: formatDate2(startDate), endDate: formatDate2(endDate) }],
12400
12702
  dimensions: [
12401
- { name: "date" },
12703
+ { name: GA4_DIMENSIONS.date },
12402
12704
  { name: sourceDim },
12403
12705
  { name: mediumDim },
12404
- { name: "sessionDefaultChannelGroup" },
12405
- { name: "landingPagePlusQueryString" }
12706
+ { name: GA4_DIMENSIONS.sessionDefaultChannelGrouping },
12707
+ { name: GA4_DIMENSIONS.landingPagePlusQueryString }
12406
12708
  ],
12407
12709
  metrics: [
12408
- { name: "sessions" },
12409
- { name: "totalUsers" }
12710
+ { name: GA4_METRICS.sessions },
12711
+ { name: GA4_METRICS.totalUsers }
12410
12712
  ],
12411
12713
  dimensionFilter: {
12412
12714
  orGroup: {
@@ -12479,20 +12781,20 @@ async function fetchSocialReferrals(accessToken, propertyId, days) {
12479
12781
  const request = {
12480
12782
  dateRanges: [{ startDate: formatDate2(startDate), endDate: formatDate2(endDate) }],
12481
12783
  dimensions: [
12482
- { name: "date" },
12483
- { name: "sessionSource" },
12484
- { name: "sessionMedium" },
12485
- { name: "sessionDefaultChannelGroup" }
12784
+ { name: GA4_DIMENSIONS.date },
12785
+ { name: GA4_DIMENSIONS.sessionSource },
12786
+ { name: GA4_DIMENSIONS.sessionMedium },
12787
+ { name: GA4_DIMENSIONS.sessionDefaultChannelGroup }
12486
12788
  ],
12487
12789
  metrics: [
12488
- { name: "sessions" },
12489
- { name: "totalUsers" }
12790
+ { name: GA4_METRICS.sessions },
12791
+ { name: GA4_METRICS.totalUsers }
12490
12792
  ],
12491
12793
  dimensionFilter: {
12492
12794
  orGroup: {
12493
12795
  expressions: SOCIAL_CHANNEL_GROUPS.map((value) => ({
12494
12796
  filter: {
12495
- fieldName: "sessionDefaultChannelGroup",
12797
+ fieldName: GA4_DIMENSIONS.sessionDefaultChannelGroup,
12496
12798
  stringFilter: { matchType: "EXACT", value }
12497
12799
  }
12498
12800
  }))
@@ -12662,7 +12964,8 @@ async function googleRoutes(app, opts) {
12662
12964
  <li>Under "Authorized redirect URIs", add:<br><code style="background:#1e1e1e;color:#e0e0e0;padding:4px 8px;border-radius:4px;display:inline-block;margin-top:4px">${request.query.state ? (() => {
12663
12965
  try {
12664
12966
  const s = verifySignedState(request.query.state, stateSecret);
12665
- return escapeHtml2(String(s?.redirectUri ?? "Could not determine URI"));
12967
+ const uri = s?.redirectUri;
12968
+ return escapeHtml2(typeof uri === "string" ? uri : "Could not determine URI");
12666
12969
  } catch {
12667
12970
  return "Could not determine URI";
12668
12971
  }
@@ -12799,7 +13102,7 @@ async function googleRoutes(app, opts) {
12799
13102
  if (page) conditions.push(sql7`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
12800
13103
  const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
12801
13104
  const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
12802
- const rows = app.db.select().from(gscSearchData).where(and9(...conditions)).orderBy(desc8(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
13105
+ const rows = app.db.select().from(gscSearchData).where(and12(...conditions)).orderBy(desc8(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
12803
13106
  return rows.map((r) => ({
12804
13107
  date: r.date,
12805
13108
  query: r.query,
@@ -12824,7 +13127,7 @@ async function googleRoutes(app, opts) {
12824
13127
  date: gscSearchData.date,
12825
13128
  clicks: sql7`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
12826
13129
  impressions: sql7`COALESCE(SUM(${gscSearchData.impressions}), 0)`
12827
- }).from(gscSearchData).where(and9(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
13130
+ }).from(gscSearchData).where(and12(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
12828
13131
  const daily = rows.map((r) => ({
12829
13132
  date: r.date,
12830
13133
  clicks: r.clicks,
@@ -12904,7 +13207,7 @@ async function googleRoutes(app, opts) {
12904
13207
  const { url, limit } = request.query;
12905
13208
  const conditions = [eq18(gscUrlInspections.projectId, project.id)];
12906
13209
  if (url) conditions.push(eq18(gscUrlInspections.url, url));
12907
- const rows = app.db.select().from(gscUrlInspections).where(and9(...conditions)).orderBy(desc8(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
13210
+ const rows = app.db.select().from(gscUrlInspections).where(and12(...conditions)).orderBy(desc8(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
12908
13211
  return rows.map((r) => ({
12909
13212
  id: r.id,
12910
13213
  url: r.url,
@@ -13256,7 +13559,7 @@ async function googleRoutes(app, opts) {
13256
13559
 
13257
13560
  // ../api-routes/src/bing.ts
13258
13561
  import crypto15 from "crypto";
13259
- import { eq as eq19, and as and10, desc as desc9 } from "drizzle-orm";
13562
+ import { eq as eq19, and as and13, desc as desc9 } from "drizzle-orm";
13260
13563
 
13261
13564
  // ../integration-bing/src/constants.ts
13262
13565
  var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
@@ -13670,7 +13973,7 @@ async function bingRoutes(app, opts) {
13670
13973
  requireConnectionStore();
13671
13974
  const project = resolveProject(app.db, request.params.name);
13672
13975
  const { url, limit } = request.query;
13673
- const whereClause = url ? and10(eq19(bingUrlInspections.projectId, project.id), eq19(bingUrlInspections.url, url)) : eq19(bingUrlInspections.projectId, project.id);
13976
+ const whereClause = url ? and13(eq19(bingUrlInspections.projectId, project.id), eq19(bingUrlInspections.url, url)) : eq19(bingUrlInspections.projectId, project.id);
13674
13977
  const filtered = app.db.select().from(bingUrlInspections).where(whereClause).orderBy(desc9(bingUrlInspections.inspectedAt)).limit(Math.max(1, Math.min(parseInt(limit ?? "100", 10) || 100, 1e3))).all();
13675
13978
  return filtered.map((r) => ({
13676
13979
  id: r.id,
@@ -13902,7 +14205,7 @@ async function bingRoutes(app, opts) {
13902
14205
  import fs from "fs";
13903
14206
  import path from "path";
13904
14207
  import os2 from "os";
13905
- import { eq as eq20, and as and11 } from "drizzle-orm";
14208
+ import { eq as eq20, and as and14 } from "drizzle-orm";
13906
14209
  function getScreenshotDir() {
13907
14210
  return path.join(os2.homedir(), ".canonry", "screenshots");
13908
14211
  }
@@ -13975,7 +14278,7 @@ async function cdpRoutes(app, opts) {
13975
14278
  async (request, reply) => {
13976
14279
  const project = resolveProject(app.db, request.params.name);
13977
14280
  const { runId } = request.params;
13978
- const run = app.db.select().from(runs).where(and11(eq20(runs.id, runId), eq20(runs.projectId, project.id))).get();
14281
+ const run = app.db.select().from(runs).where(and14(eq20(runs.id, runId), eq20(runs.projectId, project.id))).get();
13979
14282
  if (!run) {
13980
14283
  const err = notFound("Run", runId);
13981
14284
  return reply.code(err.statusCode).send(err.toJSON());
@@ -14072,7 +14375,7 @@ async function cdpRoutes(app, opts) {
14072
14375
 
14073
14376
  // ../api-routes/src/ga.ts
14074
14377
  import crypto16 from "crypto";
14075
- import { eq as eq21, desc as desc10, and as and12, sql as sql8 } from "drizzle-orm";
14378
+ import { eq as eq21, desc as desc10, and as and15, sql as sql8 } from "drizzle-orm";
14076
14379
  function gaLog(level, action, ctx) {
14077
14380
  const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
14078
14381
  const stream = level === "error" ? process.stderr : process.stdout;
@@ -14367,7 +14670,7 @@ async function ga4Routes(app, opts) {
14367
14670
  app.db.transaction((tx) => {
14368
14671
  if (syncTraffic) {
14369
14672
  tx.delete(gaTrafficSnapshots).where(
14370
- and12(
14673
+ and15(
14371
14674
  eq21(gaTrafficSnapshots.projectId, project.id),
14372
14675
  sql8`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
14373
14676
  sql8`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
@@ -14391,7 +14694,7 @@ async function ga4Routes(app, opts) {
14391
14694
  }
14392
14695
  if (syncAi) {
14393
14696
  tx.delete(gaAiReferrals).where(
14394
- and12(
14697
+ and15(
14395
14698
  eq21(gaAiReferrals.projectId, project.id),
14396
14699
  sql8`${gaAiReferrals.date} >= ${summary.periodStart}`,
14397
14700
  sql8`${gaAiReferrals.date} <= ${summary.periodEnd}`
@@ -14417,7 +14720,7 @@ async function ga4Routes(app, opts) {
14417
14720
  }
14418
14721
  if (syncSocial) {
14419
14722
  tx.delete(gaSocialReferrals).where(
14420
- and12(
14723
+ and15(
14421
14724
  eq21(gaSocialReferrals.projectId, project.id),
14422
14725
  sql8`${gaSocialReferrals.date} >= ${summary.periodStart}`,
14423
14726
  sql8`${gaSocialReferrals.date} <= ${summary.periodEnd}`
@@ -14521,7 +14824,7 @@ async function ga4Routes(app, opts) {
14521
14824
  totalDirectSessions: gaTrafficWindowSummaries.totalDirectSessions,
14522
14825
  totalUsers: gaTrafficWindowSummaries.totalUsers
14523
14826
  }).from(gaTrafficWindowSummaries).where(
14524
- and12(
14827
+ and15(
14525
14828
  eq21(gaTrafficWindowSummaries.projectId, project.id),
14526
14829
  eq21(gaTrafficWindowSummaries.windowKey, window)
14527
14830
  )
@@ -14530,7 +14833,7 @@ async function ga4Routes(app, opts) {
14530
14833
  totalSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
14531
14834
  totalOrganicSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
14532
14835
  totalUsers: sql8`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
14533
- }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get() : null;
14836
+ }).from(gaTrafficSnapshots).where(and15(...snapshotConditions)).get() : null;
14534
14837
  const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
14535
14838
  totalSessions: gaTrafficSummaries.totalSessions,
14536
14839
  totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
@@ -14538,7 +14841,7 @@ async function ga4Routes(app, opts) {
14538
14841
  }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
14539
14842
  const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
14540
14843
  totalDirectSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
14541
- }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get();
14844
+ }).from(gaTrafficSnapshots).where(and15(...snapshotConditions)).get();
14542
14845
  const summaryMeta = app.db.select({
14543
14846
  periodStart: gaTrafficSummaries.periodStart,
14544
14847
  periodEnd: gaTrafficSummaries.periodEnd
@@ -14549,14 +14852,14 @@ async function ga4Routes(app, opts) {
14549
14852
  organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
14550
14853
  directSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
14551
14854
  users: sql8`SUM(${gaTrafficSnapshots.users})`
14552
- }).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
14855
+ }).from(gaTrafficSnapshots).where(and15(...snapshotConditions)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
14553
14856
  const aiReferralRows = app.db.select({
14554
14857
  source: gaAiReferrals.source,
14555
14858
  medium: gaAiReferrals.medium,
14556
14859
  sourceDimension: gaAiReferrals.sourceDimension,
14557
14860
  sessions: sql8`SUM(${gaAiReferrals.sessions})`,
14558
14861
  users: sql8`SUM(${gaAiReferrals.users})`
14559
- }).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
14862
+ }).from(gaAiReferrals).where(and15(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
14560
14863
  const aiReferralLandingPageRows = app.db.select({
14561
14864
  source: gaAiReferrals.source,
14562
14865
  medium: gaAiReferrals.medium,
@@ -14564,7 +14867,7 @@ async function ga4Routes(app, opts) {
14564
14867
  landingPage: sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
14565
14868
  sessions: sql8`SUM(${gaAiReferrals.sessions})`,
14566
14869
  users: sql8`SUM(${gaAiReferrals.users})`
14567
- }).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(
14870
+ }).from(gaAiReferrals).where(and15(...aiConditions)).groupBy(
14568
14871
  gaAiReferrals.source,
14569
14872
  gaAiReferrals.medium,
14570
14873
  gaAiReferrals.sourceDimension,
@@ -14601,7 +14904,7 @@ async function ga4Routes(app, opts) {
14601
14904
  channelGroup: gaAiReferrals.channelGroup,
14602
14905
  sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
14603
14906
  users: sql8`COALESCE(SUM(${gaAiReferrals.users}), 0)`
14604
- }).from(gaAiReferrals).where(and12(...aiConditions, eq21(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
14907
+ }).from(gaAiReferrals).where(and15(...aiConditions, eq21(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
14605
14908
  const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
14606
14909
  let aiBySessionUsers = 0;
14607
14910
  for (const row of aiBySessionRows) {
@@ -14615,11 +14918,11 @@ async function ga4Routes(app, opts) {
14615
14918
  channelGroup: gaSocialReferrals.channelGroup,
14616
14919
  sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
14617
14920
  users: sql8`SUM(${gaSocialReferrals.users})`
14618
- }).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql8`SUM(${gaSocialReferrals.sessions}) DESC`).all();
14921
+ }).from(gaSocialReferrals).where(and15(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql8`SUM(${gaSocialReferrals.sessions}) DESC`).all();
14619
14922
  const socialTotals = app.db.select({
14620
14923
  sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
14621
14924
  users: sql8`SUM(${gaSocialReferrals.users})`
14622
- }).from(gaSocialReferrals).where(and12(...socialConditions)).get();
14925
+ }).from(gaSocialReferrals).where(and15(...socialConditions)).get();
14623
14926
  const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).orderBy(desc10(gaTrafficSummaries.syncedAt)).limit(1).get();
14624
14927
  const total = summaryRow?.totalSessions ?? 0;
14625
14928
  const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
@@ -14710,7 +15013,7 @@ async function ga4Routes(app, opts) {
14710
15013
  sourceDimension: gaAiReferrals.sourceDimension,
14711
15014
  sessions: sql8`SUM(${gaAiReferrals.sessions})`,
14712
15015
  users: sql8`SUM(${gaAiReferrals.users})`
14713
- }).from(gaAiReferrals).where(and12(...conditions)).groupBy(
15016
+ }).from(gaAiReferrals).where(and15(...conditions)).groupBy(
14714
15017
  gaAiReferrals.date,
14715
15018
  gaAiReferrals.source,
14716
15019
  gaAiReferrals.medium,
@@ -14732,7 +15035,7 @@ async function ga4Routes(app, opts) {
14732
15035
  channelGroup: gaSocialReferrals.channelGroup,
14733
15036
  sessions: gaSocialReferrals.sessions,
14734
15037
  users: gaSocialReferrals.users
14735
- }).from(gaSocialReferrals).where(and12(...conditions)).orderBy(gaSocialReferrals.date).all();
15038
+ }).from(gaSocialReferrals).where(and15(...conditions)).orderBy(gaSocialReferrals.date).all();
14736
15039
  return rows;
14737
15040
  });
14738
15041
  app.get("/projects/:name/ga/social-referral-trend", async (request, _reply) => {
@@ -14745,7 +15048,7 @@ async function ga4Routes(app, opts) {
14745
15048
  d.setDate(d.getDate() - n);
14746
15049
  return fmt(d);
14747
15050
  };
14748
- const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(
15051
+ const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and15(
14749
15052
  eq21(gaSocialReferrals.projectId, project.id),
14750
15053
  sql8`${gaSocialReferrals.date} >= ${from}`,
14751
15054
  sql8`${gaSocialReferrals.date} < ${to}`
@@ -14758,7 +15061,7 @@ async function ga4Routes(app, opts) {
14758
15061
  const sourceCurrent = app.db.select({
14759
15062
  source: gaSocialReferrals.source,
14760
15063
  sessions: sql8`SUM(${gaSocialReferrals.sessions})`
14761
- }).from(gaSocialReferrals).where(and12(
15064
+ }).from(gaSocialReferrals).where(and15(
14762
15065
  eq21(gaSocialReferrals.projectId, project.id),
14763
15066
  sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
14764
15067
  sql8`${gaSocialReferrals.date} < ${fmt(today)}`
@@ -14766,7 +15069,7 @@ async function ga4Routes(app, opts) {
14766
15069
  const sourcePrev = app.db.select({
14767
15070
  source: gaSocialReferrals.source,
14768
15071
  sessions: sql8`SUM(${gaSocialReferrals.sessions})`
14769
- }).from(gaSocialReferrals).where(and12(
15072
+ }).from(gaSocialReferrals).where(and15(
14770
15073
  eq21(gaSocialReferrals.projectId, project.id),
14771
15074
  sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
14772
15075
  sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`
@@ -14808,16 +15111,16 @@ async function ga4Routes(app, opts) {
14808
15111
  return fmt(d);
14809
15112
  };
14810
15113
  const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
14811
- const sumTotal = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
14812
- const sumOrganic = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
14813
- const sumDirect = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
14814
- const sumAi = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
15114
+ const sumTotal = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and15(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
15115
+ const sumOrganic = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and15(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
15116
+ const sumDirect = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and15(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
15117
+ const sumAi = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and15(
14815
15118
  eq21(gaAiReferrals.projectId, project.id),
14816
15119
  sql8`${gaAiReferrals.date} >= ${from}`,
14817
15120
  sql8`${gaAiReferrals.date} < ${to}`,
14818
15121
  eq21(gaAiReferrals.sourceDimension, "session")
14819
15122
  )).get();
14820
- const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${from}`, sql8`${gaSocialReferrals.date} < ${to}`)).get();
15123
+ const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and15(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${from}`, sql8`${gaSocialReferrals.date} < ${to}`)).get();
14821
15124
  const todayStr = fmt(today);
14822
15125
  const buildTrend = (sum) => {
14823
15126
  const c7 = sum(daysAgo2(7), todayStr)?.sessions ?? 0;
@@ -14826,13 +15129,13 @@ async function ga4Routes(app, opts) {
14826
15129
  const p30 = sum(daysAgo2(60), daysAgo2(30))?.sessions ?? 0;
14827
15130
  return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
14828
15131
  };
14829
- const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
15132
+ const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and15(
14830
15133
  eq21(gaAiReferrals.projectId, project.id),
14831
15134
  sql8`${gaAiReferrals.date} >= ${daysAgo2(7)}`,
14832
15135
  sql8`${gaAiReferrals.date} < ${todayStr}`,
14833
15136
  eq21(gaAiReferrals.sourceDimension, "session")
14834
15137
  )).groupBy(gaAiReferrals.source).all();
14835
- const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
15138
+ const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and15(
14836
15139
  eq21(gaAiReferrals.projectId, project.id),
14837
15140
  sql8`${gaAiReferrals.date} >= ${daysAgo2(14)}`,
14838
15141
  sql8`${gaAiReferrals.date} < ${daysAgo2(7)}`,
@@ -14852,8 +15155,8 @@ async function ga4Routes(app, opts) {
14852
15155
  }
14853
15156
  return mover;
14854
15157
  };
14855
- const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql8`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
14856
- const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
15158
+ const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and15(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql8`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
15159
+ const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and15(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
14857
15160
  return {
14858
15161
  total: buildTrend(sumTotal),
14859
15162
  organic: buildTrend(sumOrganic),
@@ -14875,7 +15178,7 @@ async function ga4Routes(app, opts) {
14875
15178
  sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
14876
15179
  organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
14877
15180
  users: sql8`SUM(${gaTrafficSnapshots.users})`
14878
- }).from(gaTrafficSnapshots).where(and12(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
15181
+ }).from(gaTrafficSnapshots).where(and15(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
14879
15182
  return rows.map((r) => ({
14880
15183
  date: r.date,
14881
15184
  sessions: r.sessions ?? 0,
@@ -15669,7 +15972,7 @@ async function deploySchema(connection, slug, schemas, env) {
15669
15972
  return {
15670
15973
  slug,
15671
15974
  status: "stripped",
15672
- schemasInjected: schemas.map((s) => String(s["@type"] ?? "Unknown")),
15975
+ schemasInjected: schemas.map((s) => typeof s["@type"] === "string" ? s["@type"] : "Unknown"),
15673
15976
  manualAssist: {
15674
15977
  manualRequired: true,
15675
15978
  targetUrl: page.link ?? `${site.siteUrl}/${slug}`,
@@ -15686,7 +15989,7 @@ async function deploySchema(connection, slug, schemas, env) {
15686
15989
  return {
15687
15990
  slug,
15688
15991
  status: "deployed",
15689
- schemasInjected: schemas.map((s) => String(s["@type"] ?? "Unknown"))
15992
+ schemasInjected: schemas.map((s) => typeof s["@type"] === "string" ? s["@type"] : "Unknown")
15690
15993
  };
15691
15994
  } catch (error) {
15692
15995
  if (error instanceof WordpressApiError && error.code === "NOT_FOUND") {
@@ -15746,7 +16049,7 @@ async function getSchemaStatus(connection, env) {
15746
16049
  while ((jsonMatch = jsonLdRegex.exec(match[1])) !== null) {
15747
16050
  try {
15748
16051
  const parsed = JSON.parse(jsonMatch[1].trim());
15749
- canonrySchemas.push(String(parsed["@type"] ?? "Unknown"));
16052
+ canonrySchemas.push(typeof parsed["@type"] === "string" ? parsed["@type"] : "Unknown");
15750
16053
  } catch {
15751
16054
  }
15752
16055
  }
@@ -16477,7 +16780,7 @@ async function wordpressRoutes(app, opts) {
16477
16780
  steps.push({ name: "google-submit", status: "completed", summary: `${succeeded}/${pageUrls.length} URLs submitted` });
16478
16781
  } else {
16479
16782
  const body = JSON.parse(googleRes.body);
16480
- const msg = body.message || body.error || `HTTP ${googleRes.statusCode}`;
16783
+ const msg = body.message ?? body.error ?? `HTTP ${googleRes.statusCode}`;
16481
16784
  if (googleRes.statusCode === 400 || googleRes.statusCode === 404) {
16482
16785
  steps.push({ name: "google-submit", status: "skipped", summary: msg });
16483
16786
  } else {
@@ -16502,7 +16805,7 @@ async function wordpressRoutes(app, opts) {
16502
16805
  steps.push({ name: "bing-submit", status: "completed", summary: `${succeeded}/${pageUrls.length} URLs submitted` });
16503
16806
  } else {
16504
16807
  const body = JSON.parse(bingRes.body);
16505
- const msg = body.message || body.error || `HTTP ${bingRes.statusCode}`;
16808
+ const msg = body.message ?? body.error ?? `HTTP ${bingRes.statusCode}`;
16506
16809
  if (bingRes.statusCode === 400 || bingRes.statusCode === 404) {
16507
16810
  steps.push({ name: "bing-submit", status: "skipped", summary: msg });
16508
16811
  } else {
@@ -16528,7 +16831,7 @@ async function wordpressRoutes(app, opts) {
16528
16831
 
16529
16832
  // ../api-routes/src/backlinks.ts
16530
16833
  import crypto18 from "crypto";
16531
- import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as sql9 } from "drizzle-orm";
16834
+ import { and as and17, asc as asc2, desc as desc11, eq as eq22, sql as sql9 } from "drizzle-orm";
16532
16835
 
16533
16836
  // ../integration-commoncrawl/src/constants.ts
16534
16837
  import os3 from "os";
@@ -16925,7 +17228,7 @@ function pruneCachedRelease(release, opts = {}) {
16925
17228
  }
16926
17229
 
16927
17230
  // ../api-routes/src/backlinks-filter.ts
16928
- import { and as and13, ne as ne2, notLike } from "drizzle-orm";
17231
+ import { and as and16, ne as ne3, notLike } from "drizzle-orm";
16929
17232
  var BACKLINK_FILTER_PATTERNS = [
16930
17233
  "*.google.com",
16931
17234
  "*.googleusercontent.com",
@@ -16942,13 +17245,13 @@ function backlinkCrawlerExclusionClause() {
16942
17245
  for (const pattern of BACKLINK_FILTER_PATTERNS) {
16943
17246
  if (pattern.startsWith("*.")) {
16944
17247
  const suffix = pattern.slice(2);
16945
- conditions.push(ne2(backlinkDomains.linkingDomain, suffix));
17248
+ conditions.push(ne3(backlinkDomains.linkingDomain, suffix));
16946
17249
  conditions.push(notLike(backlinkDomains.linkingDomain, `%.${suffix}`));
16947
17250
  } else {
16948
- conditions.push(ne2(backlinkDomains.linkingDomain, pattern));
17251
+ conditions.push(ne3(backlinkDomains.linkingDomain, pattern));
16949
17252
  }
16950
17253
  }
16951
- const combined = and13(...conditions);
17254
+ const combined = and16(...conditions);
16952
17255
  if (!combined) throw new Error("BACKLINK_FILTER_PATTERNS is unexpectedly empty");
16953
17256
  return combined;
16954
17257
  }
@@ -17009,7 +17312,7 @@ function mapRunRow(row) {
17009
17312
  };
17010
17313
  }
17011
17314
  function latestSummaryForProject(db, projectId, release) {
17012
- const condition = release ? and14(eq22(backlinkSummaries.projectId, projectId), eq22(backlinkSummaries.release, release)) : eq22(backlinkSummaries.projectId, projectId);
17315
+ const condition = release ? and17(eq22(backlinkSummaries.projectId, projectId), eq22(backlinkSummaries.release, release)) : eq22(backlinkSummaries.projectId, projectId);
17013
17316
  return db.select().from(backlinkSummaries).where(condition).orderBy(desc11(backlinkSummaries.queriedAt)).limit(1).get();
17014
17317
  }
17015
17318
  function parseExcludeCrawlers(value) {
@@ -17018,11 +17321,11 @@ function parseExcludeCrawlers(value) {
17018
17321
  return lower === "1" || lower === "true" || lower === "yes";
17019
17322
  }
17020
17323
  function computeFilteredSummary(db, base) {
17021
- const baseDomainCondition = and14(
17324
+ const baseDomainCondition = and17(
17022
17325
  eq22(backlinkDomains.projectId, base.projectId),
17023
17326
  eq22(backlinkDomains.release, base.release)
17024
17327
  );
17025
- const filteredCondition = and14(baseDomainCondition, backlinkCrawlerExclusionClause());
17328
+ const filteredCondition = and17(baseDomainCondition, backlinkCrawlerExclusionClause());
17026
17329
  const unfilteredAgg = db.select({
17027
17330
  count: sql9`count(*)`,
17028
17331
  total: sql9`coalesce(sum(${backlinkDomains.numHosts}), 0)`
@@ -17198,11 +17501,11 @@ async function backlinksRoutes(app, opts) {
17198
17501
  const limit = Math.min(Math.max(parseInt(request.query.limit ?? "50", 10) || 50, 1), 500);
17199
17502
  const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
17200
17503
  const excludeCrawlers = parseExcludeCrawlers(request.query.excludeCrawlers);
17201
- const baseDomainCondition = and14(
17504
+ const baseDomainCondition = and17(
17202
17505
  eq22(backlinkDomains.projectId, project.id),
17203
17506
  eq22(backlinkDomains.release, targetRelease)
17204
17507
  );
17205
- const domainCondition = excludeCrawlers ? and14(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
17508
+ const domainCondition = excludeCrawlers ? and17(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
17206
17509
  const totalRow = app.db.select({ count: sql9`count(*)` }).from(backlinkDomains).where(domainCondition).get();
17207
17510
  const rows = app.db.select({
17208
17511
  linkingDomain: backlinkDomains.linkingDomain,
@@ -17238,7 +17541,7 @@ async function backlinksRoutes(app, opts) {
17238
17541
 
17239
17542
  // ../api-routes/src/traffic.ts
17240
17543
  import crypto20 from "crypto";
17241
- import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as sql10 } from "drizzle-orm";
17544
+ import { and as and18, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as sql10 } from "drizzle-orm";
17242
17545
 
17243
17546
  // ../integration-cloud-run/src/auth.ts
17244
17547
  import crypto19 from "crypto";
@@ -17521,6 +17824,7 @@ async function listCloudRunTrafficEvents(accessToken, options) {
17521
17824
  }
17522
17825
 
17523
17826
  // ../integration-traffic/src/rules.ts
17827
+ var LEGACY_CHATGPT_DOMAIN = "chat.openai.com";
17524
17828
  var DEFAULT_AI_CRAWLER_RULES = [
17525
17829
  {
17526
17830
  id: "openai-gptbot",
@@ -17615,15 +17919,15 @@ var DEFAULT_AI_CRAWLER_RULES = [
17615
17919
  }
17616
17920
  ];
17617
17921
  var DEFAULT_AI_REFERRER_RULES = [
17618
- { domain: "chatgpt.com", operator: "OpenAI", product: "ChatGPT" },
17619
- { domain: "chat.openai.com", operator: "OpenAI", product: "ChatGPT" },
17620
- { domain: "perplexity.ai", operator: "Perplexity", product: "Perplexity" },
17621
- { domain: "claude.ai", operator: "Anthropic", product: "Claude" },
17622
- { domain: "gemini.google.com", operator: "Google", product: "Gemini" },
17623
- { domain: "copilot.microsoft.com", operator: "Microsoft", product: "Copilot" },
17624
- { domain: "phind.com", operator: "Phind", product: "Phind" },
17625
- { domain: "you.com", operator: "You.com", product: "You.com" },
17626
- { domain: "meta.ai", operator: "Meta", product: "Meta AI" }
17922
+ { domain: AI_ENGINE_DOMAINS.chatgpt, operator: "OpenAI", product: "ChatGPT" },
17923
+ { domain: LEGACY_CHATGPT_DOMAIN, operator: "OpenAI", product: "ChatGPT" },
17924
+ { domain: AI_ENGINE_DOMAINS.perplexity, operator: "Perplexity", product: "Perplexity" },
17925
+ { domain: AI_ENGINE_DOMAINS.claude, operator: "Anthropic", product: "Claude" },
17926
+ { domain: AI_ENGINE_DOMAINS.gemini, operator: "Google", product: "Gemini" },
17927
+ { domain: AI_ENGINE_DOMAINS.copilotMicrosoft, operator: "Microsoft", product: "Copilot" },
17928
+ { domain: AI_ENGINE_DOMAINS.phind, operator: "Phind", product: "Phind" },
17929
+ { domain: AI_ENGINE_DOMAINS.you, operator: "You.com", product: "You.com" },
17930
+ { domain: AI_ENGINE_DOMAINS.metaAi, operator: "Meta", product: "Meta AI" }
17627
17931
  ];
17628
17932
 
17629
17933
  // ../integration-traffic/src/classifier.ts
@@ -18386,21 +18690,21 @@ async function runBackfillTask(options) {
18386
18690
  try {
18387
18691
  app.db.transaction((tx) => {
18388
18692
  tx.delete(crawlerEventsHourly).where(
18389
- and15(
18693
+ and18(
18390
18694
  eq23(crawlerEventsHourly.sourceId, sourceRow.id),
18391
18695
  gte2(crawlerEventsHourly.tsHour, windowStartIso),
18392
18696
  lte2(crawlerEventsHourly.tsHour, windowEndIso)
18393
18697
  )
18394
18698
  ).run();
18395
18699
  tx.delete(aiReferralEventsHourly).where(
18396
- and15(
18700
+ and18(
18397
18701
  eq23(aiReferralEventsHourly.sourceId, sourceRow.id),
18398
18702
  gte2(aiReferralEventsHourly.tsHour, windowStartIso),
18399
18703
  lte2(aiReferralEventsHourly.tsHour, windowEndIso)
18400
18704
  )
18401
18705
  ).run();
18402
18706
  tx.delete(rawEventSamples).where(
18403
- and15(
18707
+ and18(
18404
18708
  eq23(rawEventSamples.sourceId, sourceRow.id),
18405
18709
  gte2(rawEventSamples.ts, windowStartIso),
18406
18710
  lte2(rawEventSamples.ts, windowEndIso)
@@ -19264,25 +19568,25 @@ async function trafficRoutes(app, opts) {
19264
19568
  });
19265
19569
  function buildSourceDetail(projectId, row, since) {
19266
19570
  const crawlerTotals = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
19267
- and15(
19571
+ and18(
19268
19572
  eq23(crawlerEventsHourly.sourceId, row.id),
19269
19573
  gte2(crawlerEventsHourly.tsHour, since)
19270
19574
  )
19271
19575
  ).get();
19272
19576
  const aiTotals = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
19273
- and15(
19577
+ and18(
19274
19578
  eq23(aiReferralEventsHourly.sourceId, row.id),
19275
19579
  gte2(aiReferralEventsHourly.tsHour, since)
19276
19580
  )
19277
19581
  ).get();
19278
19582
  const sampleTotals = app.db.select({ total: sql10`COUNT(*)` }).from(rawEventSamples).where(
19279
- and15(
19583
+ and18(
19280
19584
  eq23(rawEventSamples.sourceId, row.id),
19281
19585
  gte2(rawEventSamples.ts, since)
19282
19586
  )
19283
19587
  ).get();
19284
19588
  const latestRun = app.db.select().from(runs).where(
19285
- and15(
19589
+ and18(
19286
19590
  eq23(runs.projectId, projectId),
19287
19591
  eq23(runs.kind, RunKinds["traffic-sync"]),
19288
19592
  eq23(runs.sourceId, row.id)
@@ -19376,7 +19680,7 @@ async function trafficRoutes(app, opts) {
19376
19680
  lte2(crawlerEventsHourly.tsHour, untilIso)
19377
19681
  ];
19378
19682
  if (sourceIdParam) crawlerFilters.push(eq23(crawlerEventsHourly.sourceId, sourceIdParam));
19379
- const crawlerWhere = and15(...crawlerFilters);
19683
+ const crawlerWhere = and18(...crawlerFilters);
19380
19684
  const total = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
19381
19685
  crawlerTotal = Number(total?.total ?? 0);
19382
19686
  const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc12(crawlerEventsHourly.tsHour)).limit(limit).all();
@@ -19401,7 +19705,7 @@ async function trafficRoutes(app, opts) {
19401
19705
  lte2(aiReferralEventsHourly.tsHour, untilIso)
19402
19706
  ];
19403
19707
  if (sourceIdParam) aiFilters.push(eq23(aiReferralEventsHourly.sourceId, sourceIdParam));
19404
- const aiWhere = and15(...aiFilters);
19708
+ const aiWhere = and18(...aiFilters);
19405
19709
  const total = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
19406
19710
  aiReferralTotal = Number(total?.total ?? 0);
19407
19711
  const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc12(aiReferralEventsHourly.tsHour)).limit(limit).all();
@@ -20125,7 +20429,7 @@ var providersConfiguredCheck = {
20125
20429
  var PROVIDERS_CHECKS = [providersConfiguredCheck];
20126
20430
 
20127
20431
  // ../api-routes/src/doctor/checks/traffic-source.ts
20128
- import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as sql11 } from "drizzle-orm";
20432
+ import { and as and19, eq as eq24, gte as gte3, ne as ne4, sql as sql11 } from "drizzle-orm";
20129
20433
  var RECENT_DATA_WARN_DAYS = 7;
20130
20434
  var RECENT_DATA_FAIL_DAYS = 30;
20131
20435
  function skippedNoProject2() {
@@ -20139,9 +20443,9 @@ function skippedNoProject2() {
20139
20443
  function loadProbes(ctx) {
20140
20444
  if (!ctx.project) return [];
20141
20445
  const rows = ctx.db.select().from(trafficSources).where(
20142
- and16(
20446
+ and19(
20143
20447
  eq24(trafficSources.projectId, ctx.project.id),
20144
- ne3(trafficSources.status, TrafficSourceStatuses.archived)
20448
+ ne4(trafficSources.status, TrafficSourceStatuses.archived)
20145
20449
  )
20146
20450
  ).all();
20147
20451
  return rows.map((r) => ({
@@ -20220,7 +20524,7 @@ var recentDataCheck = {
20220
20524
  const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
20221
20525
  const recentCrawlers = Number(
20222
20526
  ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
20223
- and16(
20527
+ and19(
20224
20528
  eq24(crawlerEventsHourly.projectId, ctx.project.id),
20225
20529
  gte3(crawlerEventsHourly.tsHour, warnCutoff)
20226
20530
  )
@@ -20228,7 +20532,7 @@ var recentDataCheck = {
20228
20532
  );
20229
20533
  const recentReferrals = Number(
20230
20534
  ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
20231
- and16(
20535
+ and19(
20232
20536
  eq24(aiReferralEventsHourly.projectId, ctx.project.id),
20233
20537
  gte3(aiReferralEventsHourly.tsHour, warnCutoff)
20234
20538
  )
@@ -20244,7 +20548,7 @@ var recentDataCheck = {
20244
20548
  }
20245
20549
  const olderCrawlers = Number(
20246
20550
  ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
20247
- and16(
20551
+ and19(
20248
20552
  eq24(crawlerEventsHourly.projectId, ctx.project.id),
20249
20553
  gte3(crawlerEventsHourly.tsHour, failCutoff)
20250
20554
  )
@@ -20252,7 +20556,7 @@ var recentDataCheck = {
20252
20556
  );
20253
20557
  const olderReferrals = Number(
20254
20558
  ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
20255
- and16(
20559
+ and19(
20256
20560
  eq24(aiReferralEventsHourly.projectId, ctx.project.id),
20257
20561
  gte3(aiReferralEventsHourly.tsHour, failCutoff)
20258
20562
  )
@@ -20436,9 +20740,6 @@ var ALL_CHECKS = [
20436
20740
  ...TRAFFIC_SOURCE_CHECKS,
20437
20741
  ...AGENT_CHECKS
20438
20742
  ];
20439
- var CHECK_BY_ID = Object.fromEntries(
20440
- ALL_CHECKS.map((check) => [check.id, check])
20441
- );
20442
20743
 
20443
20744
  // ../api-routes/src/doctor/runner.ts
20444
20745
  function matchesCheckId(checkId, filters) {
@@ -20551,7 +20852,7 @@ async function doctorRoutes(app, opts) {
20551
20852
 
20552
20853
  // ../api-routes/src/discovery/routes.ts
20553
20854
  import crypto21 from "crypto";
20554
- import { and as and17, desc as desc13, eq as eq25, gte as gte4, inArray as inArray9 } from "drizzle-orm";
20855
+ import { and as and20, desc as desc13, eq as eq25, gte as gte4, inArray as inArray9 } from "drizzle-orm";
20555
20856
  var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
20556
20857
  async function discoveryRoutes(app, opts) {
20557
20858
  app.post("/projects/:name/discover/run", async (request, reply) => {
@@ -20583,7 +20884,7 @@ async function discoveryRoutes(app, opts) {
20583
20884
  const now = (/* @__PURE__ */ new Date()).toISOString();
20584
20885
  const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
20585
20886
  const decision = app.db.transaction((tx) => {
20586
- const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and17(
20887
+ const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and20(
20587
20888
  eq25(discoverySessions.projectId, project.id),
20588
20889
  eq25(discoverySessions.icpDescription, icpDescription),
20589
20890
  inArray9(discoverySessions.status, [
@@ -21560,10 +21861,10 @@ function extractDomainFromUri(uri) {
21560
21861
  try {
21561
21862
  const url = new URL(uri);
21562
21863
  const hostname = url.hostname.replace(/^www\./, "").toLowerCase();
21563
- if (hostname.includes("chatgpt.com") || hostname.includes("openai.com")) {
21864
+ if (AI_ENGINE_SELF_DOMAINS.chatgpt.some((self) => hostname.includes(self))) {
21564
21865
  return null;
21565
21866
  }
21566
- if (hostname === "vertexaisearch.cloud.google.com") {
21867
+ if (hostname === VERTEX_AI_SEARCH_PROXY_DOMAIN) {
21567
21868
  const redirectPath = url.pathname.replace(/^\/grounding-api-redirect\//, "");
21568
21869
  if (redirectPath && redirectPath !== url.pathname) {
21569
21870
  try {
@@ -21831,7 +22132,7 @@ async function healthcheck2(config) {
21831
22132
  async function executeTrackedQuery2(input) {
21832
22133
  const model = input.config.model ?? DEFAULT_MODEL2;
21833
22134
  const client = new OpenAI({ apiKey: input.config.apiKey });
21834
- const webSearchTool = { type: "web_search_preview" };
22135
+ const webSearchTool = { type: "web_search" };
21835
22136
  if (input.location) {
21836
22137
  webSearchTool.user_location = {
21837
22138
  type: "approximate",
@@ -21993,7 +22294,7 @@ function extractDomainFromUri2(uri) {
21993
22294
  try {
21994
22295
  const url = new URL(uri);
21995
22296
  const hostname = url.hostname.replace(/^www\./, "").toLowerCase();
21996
- if (hostname.includes("chatgpt.com") || hostname.includes("openai.com")) {
22297
+ if (AI_ENGINE_SELF_DOMAINS.chatgpt.some((self) => hostname.includes(self))) {
21997
22298
  return null;
21998
22299
  }
21999
22300
  return hostname;
@@ -22372,7 +22673,7 @@ function extractDomainFromUri3(uri) {
22372
22673
  try {
22373
22674
  const url = new URL(uri);
22374
22675
  const hostname = url.hostname.replace(/^www\./, "").toLowerCase();
22375
- if (hostname.includes("chatgpt.com") || hostname.includes("openai.com")) {
22676
+ if (AI_ENGINE_SELF_DOMAINS.chatgpt.some((self) => hostname.includes(self))) {
22376
22677
  return null;
22377
22678
  }
22378
22679
  return hostname;
@@ -22917,8 +23218,8 @@ async function waitForStabilization(client, selector, opts = {}) {
22917
23218
  // ../provider-cdp/src/targets/chatgpt.ts
22918
23219
  var chatgptTarget = {
22919
23220
  name: "chatgpt",
22920
- baseUrl: "https://chatgpt.com",
22921
- newConversationUrl: "https://chatgpt.com/?model=auto",
23221
+ baseUrl: `https://${AI_ENGINE_DOMAINS.chatgpt}`,
23222
+ newConversationUrl: `https://${AI_ENGINE_DOMAINS.chatgpt}/?model=auto`,
22922
23223
  responseSelector: '[data-testid="conversation-turn-3"], article:last-of-type, .agent-turn:last-of-type',
22923
23224
  async submitQuery(client, query) {
22924
23225
  const inputReady = await waitForElement(
@@ -23022,8 +23323,10 @@ var chatgptTarget = {
23022
23323
  },
23023
23324
  extractCitations(client) {
23024
23325
  return (async () => {
23326
+ const selfDomainsLiteral = JSON.stringify(AI_ENGINE_SELF_DOMAINS.chatgpt);
23025
23327
  const { result } = await client.Runtime.evaluate({
23026
23328
  expression: `(() => {
23329
+ const SELF_DOMAINS = ${selfDomainsLiteral};
23027
23330
  const sources = [];
23028
23331
  const seen = new Set();
23029
23332
  const turns = document.querySelectorAll('[data-message-author-role="assistant"]');
@@ -23047,7 +23350,7 @@ var chatgptTarget = {
23047
23350
  }
23048
23351
  }
23049
23352
 
23050
- if (!seen.has(href) && hostname !== 'chatgpt.com' && hostname !== 'openai.com') {
23353
+ if (!seen.has(href) && !SELF_DOMAINS.includes(hostname)) {
23051
23354
  seen.add(href);
23052
23355
  sources.push({
23053
23356
  uri: href,
@@ -23064,7 +23367,7 @@ var chatgptTarget = {
23064
23367
  if (href && !seen.has(href)) {
23065
23368
  let hostname = '';
23066
23369
  try { hostname = new URL(href).hostname.replace(/^www\\./, ''); } catch {}
23067
- if (hostname !== 'chatgpt.com' && hostname !== 'openai.com') {
23370
+ if (!SELF_DOMAINS.includes(hostname)) {
23068
23371
  seen.add(href);
23069
23372
  sources.push({ uri: href, title: title || hostname || href });
23070
23373
  }
@@ -23154,7 +23457,7 @@ function extractCitedDomains(groundingSources) {
23154
23457
  try {
23155
23458
  const url = new URL(source.uri);
23156
23459
  const domain = url.hostname.replace(/^www\./, "").toLowerCase();
23157
- if (!domain.includes("chatgpt.com") && !domain.includes("openai.com")) {
23460
+ if (!AI_ENGINE_SELF_DOMAINS.chatgpt.some((self) => domain.includes(self))) {
23158
23461
  domains.add(domain);
23159
23462
  }
23160
23463
  } catch {
@@ -23539,7 +23842,7 @@ function extractDomainFromUri4(uri) {
23539
23842
  try {
23540
23843
  const url = new URL(uri);
23541
23844
  const hostname = url.hostname.replace(/^www\./, "").toLowerCase();
23542
- if (hostname.includes("chatgpt.com") || hostname.includes("openai.com")) {
23845
+ if (AI_ENGINE_SELF_DOMAINS.chatgpt.some((self) => hostname.includes(self))) {
23543
23846
  return null;
23544
23847
  }
23545
23848
  return hostname;
@@ -23904,7 +24207,7 @@ import crypto24 from "crypto";
23904
24207
  import fs8 from "fs";
23905
24208
  import path10 from "path";
23906
24209
  import os5 from "os";
23907
- import { and as and18, eq as eq27, inArray as inArray10, sql as sql12 } from "drizzle-orm";
24210
+ import { and as and21, eq as eq27, inArray as inArray10, sql as sql12 } from "drizzle-orm";
23908
24211
 
23909
24212
  // src/run-telemetry.ts
23910
24213
  import crypto23 from "crypto";
@@ -24287,7 +24590,7 @@ var JobRunner = class {
24287
24590
  throw new Error(`Run ${runId} is not executable from status '${existingRun.status}'`);
24288
24591
  }
24289
24592
  if (existingRun.status === "queued") {
24290
- this.db.update(runs).set({ status: "running", startedAt: now }).where(and18(eq27(runs.id, runId), eq27(runs.status, "queued"))).run();
24593
+ this.db.update(runs).set({ status: "running", startedAt: now }).where(and21(eq27(runs.id, runId), eq27(runs.status, "queued"))).run();
24291
24594
  }
24292
24595
  this.throwIfRunCancelled(runId);
24293
24596
  const project = this.db.select().from(projects).where(eq27(projects.id, projectId)).get();
@@ -24312,7 +24615,7 @@ var JobRunner = class {
24312
24615
  }
24313
24616
  log.info("run.dispatch", { runId, providerCount: activeProviders.length, providers: activeProviders.map((p) => p.adapter.name) });
24314
24617
  const scopedQueryNames = parseJsonColumn(existingRun.queries, null);
24315
- projectQueries = scopedQueryNames ? this.db.select().from(queries).where(and18(eq27(queries.projectId, projectId), inArray10(queries.query, scopedQueryNames))).all() : this.db.select().from(queries).where(eq27(queries.projectId, projectId)).all();
24618
+ projectQueries = scopedQueryNames ? this.db.select().from(queries).where(and21(eq27(queries.projectId, projectId), inArray10(queries.query, scopedQueryNames))).all() : this.db.select().from(queries).where(eq27(queries.projectId, projectId)).all();
24316
24619
  const projectCompetitors = this.db.select().from(competitors).where(eq27(competitors.projectId, projectId)).all();
24317
24620
  const competitorDomains = projectCompetitors.map((c) => c.domain);
24318
24621
  const allDomains = effectiveDomains({
@@ -24655,7 +24958,7 @@ function buildPhases(input) {
24655
24958
 
24656
24959
  // src/gsc-sync.ts
24657
24960
  import crypto25 from "crypto";
24658
- import { eq as eq28, and as and19, sql as sql13 } from "drizzle-orm";
24961
+ import { eq as eq28, and as and22, sql as sql13 } from "drizzle-orm";
24659
24962
  var log2 = createLogger("GscSync");
24660
24963
  function formatDate3(d) {
24661
24964
  return d.toISOString().split("T")[0];
@@ -24707,7 +25010,7 @@ async function executeGscSync(db, runId, projectId, opts) {
24707
25010
  });
24708
25011
  log2.info("fetch.complete", { runId, projectId, rowCount: rows.length });
24709
25012
  db.delete(gscSearchData).where(
24710
- and19(
25013
+ and22(
24711
25014
  eq28(gscSearchData.projectId, projectId),
24712
25015
  sql13`${gscSearchData.date} >= ${startDate}`,
24713
25016
  sql13`${gscSearchData.date} <= ${endDate}`
@@ -24796,7 +25099,7 @@ async function executeGscSync(db, runId, projectId, opts) {
24796
25099
  }
24797
25100
  }
24798
25101
  const snapshotDate = formatDate3(/* @__PURE__ */ new Date());
24799
- db.delete(gscCoverageSnapshots).where(and19(eq28(gscCoverageSnapshots.projectId, projectId), eq28(gscCoverageSnapshots.date, snapshotDate))).run();
25102
+ db.delete(gscCoverageSnapshots).where(and22(eq28(gscCoverageSnapshots.projectId, projectId), eq28(gscCoverageSnapshots.date, snapshotDate))).run();
24800
25103
  db.insert(gscCoverageSnapshots).values({
24801
25104
  id: crypto25.randomUUID(),
24802
25105
  projectId,
@@ -24819,7 +25122,7 @@ async function executeGscSync(db, runId, projectId, opts) {
24819
25122
 
24820
25123
  // src/gsc-inspect-sitemap.ts
24821
25124
  import crypto26 from "crypto";
24822
- import { eq as eq29, and as and20 } from "drizzle-orm";
25125
+ import { eq as eq29, and as and23 } from "drizzle-orm";
24823
25126
 
24824
25127
  // src/sitemap-parser.ts
24825
25128
  var log3 = createLogger("SitemapParser");
@@ -25037,7 +25340,7 @@ async function executeInspectSitemap(db, runId, projectId, opts) {
25037
25340
  }
25038
25341
  }
25039
25342
  const snapshotDate = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
25040
- db.delete(gscCoverageSnapshots).where(and20(eq29(gscCoverageSnapshots.projectId, projectId), eq29(gscCoverageSnapshots.date, snapshotDate))).run();
25343
+ db.delete(gscCoverageSnapshots).where(and23(eq29(gscCoverageSnapshots.projectId, projectId), eq29(gscCoverageSnapshots.date, snapshotDate))).run();
25041
25344
  db.insert(gscCoverageSnapshots).values({
25042
25345
  id: crypto26.randomUUID(),
25043
25346
  projectId,
@@ -25248,7 +25551,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
25248
25551
  // src/commoncrawl-sync.ts
25249
25552
  import crypto28 from "crypto";
25250
25553
  import path11 from "path";
25251
- import { and as and21, eq as eq31, sql as sql14 } from "drizzle-orm";
25554
+ import { and as and24, eq as eq31, sql as sql14 } from "drizzle-orm";
25252
25555
  var log6 = createLogger("CommonCrawlSync");
25253
25556
  var INSERT_CHUNK_SIZE = 1e4;
25254
25557
  function defaultDeps() {
@@ -25439,7 +25742,7 @@ function computeSummary(rows) {
25439
25742
  // src/backlink-extract.ts
25440
25743
  import crypto29 from "crypto";
25441
25744
  import fs9 from "fs";
25442
- import { and as and22, desc as desc15, eq as eq32 } from "drizzle-orm";
25745
+ import { and as and25, desc as desc15, eq as eq32 } from "drizzle-orm";
25443
25746
  var log7 = createLogger("BacklinkExtract");
25444
25747
  function defaultDeps2() {
25445
25748
  return {
@@ -25485,7 +25788,7 @@ async function executeBacklinkExtract(db, runId, projectId, opts = {}) {
25485
25788
  const targetDomain = project.canonicalDomain;
25486
25789
  db.transaction((tx) => {
25487
25790
  tx.delete(backlinkDomains).where(
25488
- and22(eq32(backlinkDomains.projectId, projectId), eq32(backlinkDomains.release, release))
25791
+ and25(eq32(backlinkDomains.projectId, projectId), eq32(backlinkDomains.release, release))
25489
25792
  ).run();
25490
25793
  if (rows.length > 0) {
25491
25794
  const values = rows.map((r) => ({
@@ -25556,7 +25859,7 @@ function computeSummary2(rows) {
25556
25859
 
25557
25860
  // src/discovery-run.ts
25558
25861
  import crypto30 from "crypto";
25559
- import { and as and23, eq as eq33 } from "drizzle-orm";
25862
+ import { and as and26, eq as eq33 } from "drizzle-orm";
25560
25863
  var log8 = createLogger("DiscoveryRun");
25561
25864
  var DEFAULT_SEED_COUNT = 30;
25562
25865
  var QUERIES_PER_INTENT_BUCKET = 6;
@@ -25816,7 +26119,7 @@ function writeDiscoveryInsight(db, input) {
25816
26119
  totalProbes
25817
26120
  });
25818
26121
  db.transaction((tx) => {
25819
- tx.update(insights).set({ dismissed: true }).where(and23(
26122
+ tx.update(insights).set({ dismissed: true }).where(and26(
25820
26123
  eq33(insights.projectId, input.projectId),
25821
26124
  eq33(insights.type, "discovery.basket-divergence"),
25822
26125
  eq33(insights.dismissed, false)
@@ -25860,7 +26163,7 @@ function buildDiscoveryInsightTitle(input) {
25860
26163
  }
25861
26164
 
25862
26165
  // src/commands/backfill.ts
25863
- import { and as and24, eq as eq34, inArray as inArray11 } from "drizzle-orm";
26166
+ import { and as and27, eq as eq34, inArray as inArray11 } from "drizzle-orm";
25864
26167
  var SNAPSHOT_BATCH_SIZE = 500;
25865
26168
  async function backfillAnswerVisibilityCommand(opts) {
25866
26169
  const config = loadConfig();
@@ -25876,7 +26179,7 @@ async function backfillAnswerVisibilityCommand(opts) {
25876
26179
  let reparsed = 0;
25877
26180
  let providerErrors = 0;
25878
26181
  if (scopedProjects.length > 0) {
25879
- const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and24(
26182
+ const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and27(
25880
26183
  eq34(runs.kind, RunKinds["answer-visibility"]),
25881
26184
  inArray11(runs.projectId, scopedProjects.map((project) => project.id))
25882
26185
  )).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq34(runs.kind, RunKinds["answer-visibility"])).all();
@@ -26034,7 +26337,7 @@ function backfillNormalizedPaths(db, opts) {
26034
26337
  id: gaTrafficSnapshots.id,
26035
26338
  landingPage: gaTrafficSnapshots.landingPage,
26036
26339
  landingPageNormalized: gaTrafficSnapshots.landingPageNormalized
26037
- }).from(gaTrafficSnapshots).where(baseConditions.length > 0 ? and24(...baseConditions) : void 0).all();
26340
+ }).from(gaTrafficSnapshots).where(baseConditions.length > 0 ? and27(...baseConditions) : void 0).all();
26038
26341
  let updated = 0;
26039
26342
  let unchanged = 0;
26040
26343
  if (rows.length > 0) {
@@ -26106,7 +26409,7 @@ function backfillAiReferralPaths(db, opts) {
26106
26409
  id: gaAiReferrals.id,
26107
26410
  landingPage: gaAiReferrals.landingPage,
26108
26411
  landingPageNormalized: gaAiReferrals.landingPageNormalized
26109
- }).from(gaAiReferrals).where(baseConditions.length > 0 ? and24(...baseConditions) : void 0).all();
26412
+ }).from(gaAiReferrals).where(baseConditions.length > 0 ? and27(...baseConditions) : void 0).all();
26110
26413
  let updated = 0;
26111
26414
  let unchanged = 0;
26112
26415
  if (rows.length > 0) {
@@ -26174,7 +26477,7 @@ function backfillProjectAnswerMentions(db, projectId, opts) {
26174
26477
  const project = db.select().from(projects).where(eq34(projects.id, projectId)).get();
26175
26478
  if (!project) return { examined: 0, updated: 0, mentioned: 0 };
26176
26479
  const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, projectId)).all().map((row) => row.domain);
26177
- const runRows = db.select({ id: runs.id }).from(runs).where(and24(eq34(runs.kind, RunKinds["answer-visibility"]), eq34(runs.projectId, projectId))).all();
26480
+ const runRows = db.select({ id: runs.id }).from(runs).where(and27(eq34(runs.kind, RunKinds["answer-visibility"]), eq34(runs.projectId, projectId))).all();
26178
26481
  const runIds = runRows.map((r) => r.id);
26179
26482
  let examined = 0;
26180
26483
  let updated = 0;
@@ -26325,7 +26628,7 @@ function readStoredGroundingSources(rawResponse) {
26325
26628
  return result;
26326
26629
  }
26327
26630
  async function backfillInsightsCommand(project, opts) {
26328
- const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-R3KDVKIB.js");
26631
+ const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-TY7IPRST.js");
26329
26632
  const config = loadConfig();
26330
26633
  const db = createClient(config.database);
26331
26634
  migrate(db);
@@ -26474,7 +26777,7 @@ var ProviderRegistry = class {
26474
26777
 
26475
26778
  // src/scheduler.ts
26476
26779
  import cron from "node-cron";
26477
- import { and as and25, eq as eq35 } from "drizzle-orm";
26780
+ import { and as and28, eq as eq35 } from "drizzle-orm";
26478
26781
  var log9 = createLogger("Scheduler");
26479
26782
  function taskKey(projectId, kind) {
26480
26783
  return `${projectId}::${kind}`;
@@ -26519,7 +26822,7 @@ var Scheduler = class {
26519
26822
  this.stopTask(key, existing, "Stopped");
26520
26823
  this.tasks.delete(key);
26521
26824
  }
26522
- const schedule = this.db.select().from(schedules).where(and25(eq35(schedules.projectId, projectId), eq35(schedules.kind, kind))).get();
26825
+ const schedule = this.db.select().from(schedules).where(and28(eq35(schedules.projectId, projectId), eq35(schedules.kind, kind))).get();
26523
26826
  if (schedule && schedule.enabled === 1) {
26524
26827
  this.registerCronTask(schedule);
26525
26828
  }
@@ -26540,8 +26843,8 @@ var Scheduler = class {
26540
26843
  }
26541
26844
  }
26542
26845
  stopTask(key, task, verb) {
26543
- task.stop();
26544
- task.destroy();
26846
+ void task.stop();
26847
+ void task.destroy();
26545
26848
  log9.info(`task.${verb.toLowerCase()}`, { key });
26546
26849
  }
26547
26850
  registerCronTask(schedule) {
@@ -26643,7 +26946,7 @@ var Scheduler = class {
26643
26946
  };
26644
26947
 
26645
26948
  // src/notifier.ts
26646
- import { eq as eq36, desc as desc16, and as and26, inArray as inArray12, or as or5 } from "drizzle-orm";
26949
+ import { eq as eq36, desc as desc16, and as and29, inArray as inArray12, or as or5 } from "drizzle-orm";
26647
26950
  import crypto31 from "crypto";
26648
26951
  var log10 = createLogger("Notifier");
26649
26952
  var Notifier = class {
@@ -26750,7 +27053,7 @@ var Notifier = class {
26750
27053
  computeTransitions(runId, projectId) {
26751
27054
  const thisRun = this.db.select().from(runs).where(eq36(runs.id, runId)).get();
26752
27055
  if (!thisRun) return [];
26753
- const groupSiblings = this.db.select().from(runs).where(and26(
27056
+ const groupSiblings = this.db.select().from(runs).where(and29(
26754
27057
  eq36(runs.projectId, projectId),
26755
27058
  eq36(runs.kind, thisRun.kind),
26756
27059
  eq36(runs.createdAt, thisRun.createdAt)
@@ -26776,7 +27079,7 @@ var Notifier = class {
26776
27079
  );
26777
27080
  const RECENT_FETCH_LIMIT = Math.max(8, locationCount * 4);
26778
27081
  const recentRuns = this.db.select().from(runs).where(
26779
- and26(
27082
+ and29(
26780
27083
  eq36(runs.projectId, projectId),
26781
27084
  eq36(runs.kind, thisRun.kind),
26782
27085
  or5(eq36(runs.status, "completed"), eq36(runs.status, "partial"))
@@ -26889,6 +27192,10 @@ var RunCoordinator = class {
26889
27192
  async onRunCompleted(runId, projectId) {
26890
27193
  const runRow = this.db.select().from(runs).where(eq37(runs.id, runId)).get();
26891
27194
  const kind = runRow?.kind ?? RunKinds["answer-visibility"];
27195
+ if (runRow?.trigger === RunTriggers.probe) {
27196
+ log11.info("probe.skip-side-effects", { runId, projectId, kind });
27197
+ return;
27198
+ }
26892
27199
  let insightCount = 0;
26893
27200
  let criticalOrHigh = 0;
26894
27201
  if (kind === RunKinds["answer-visibility"]) {
@@ -27315,7 +27622,7 @@ function resolveSessionProviderAndModel(config, opts) {
27315
27622
 
27316
27623
  // src/agent/memory-store.ts
27317
27624
  import crypto32 from "crypto";
27318
- import { and as and27, desc as desc17, eq as eq38, like as like2, sql as sql15 } from "drizzle-orm";
27625
+ import { and as and30, desc as desc17, eq as eq38, like as like2, sql as sql15 } from "drizzle-orm";
27319
27626
  var COMPACTION_KEY_PREFIX = "compaction:";
27320
27627
  var COMPACTION_NOTES_PER_SESSION = 3;
27321
27628
  function rowToDto2(row) {
@@ -27360,12 +27667,12 @@ function upsertMemoryEntry(db, args) {
27360
27667
  updatedAt: now
27361
27668
  }
27362
27669
  }).run();
27363
- const row = db.select().from(agentMemory).where(and27(eq38(agentMemory.projectId, args.projectId), eq38(agentMemory.key, args.key))).get();
27670
+ const row = db.select().from(agentMemory).where(and30(eq38(agentMemory.projectId, args.projectId), eq38(agentMemory.key, args.key))).get();
27364
27671
  if (!row) throw new Error("memory upsert produced no row");
27365
27672
  return rowToDto2(row);
27366
27673
  }
27367
27674
  function deleteMemoryEntry(db, projectId, key) {
27368
- const result = db.delete(agentMemory).where(and27(eq38(agentMemory.projectId, projectId), eq38(agentMemory.key, key))).run();
27675
+ const result = db.delete(agentMemory).where(and30(eq38(agentMemory.projectId, projectId), eq38(agentMemory.key, key))).run();
27369
27676
  const changes = result.changes ?? 0;
27370
27677
  return changes > 0;
27371
27678
  }
@@ -27394,7 +27701,7 @@ function writeCompactionNote(db, args) {
27394
27701
  }).run();
27395
27702
  const sessionPrefix = `${COMPACTION_KEY_PREFIX}${args.sessionId}:`;
27396
27703
  const existing = tx.select({ id: agentMemory.id, updatedAt: agentMemory.updatedAt }).from(agentMemory).where(
27397
- and27(
27704
+ and30(
27398
27705
  eq38(agentMemory.projectId, args.projectId),
27399
27706
  like2(agentMemory.key, `${sessionPrefix}%`)
27400
27707
  )
@@ -27403,7 +27710,7 @@ function writeCompactionNote(db, args) {
27403
27710
  if (stale.length > 0) {
27404
27711
  tx.delete(agentMemory).where(sql15`${agentMemory.id} IN (${sql15.join(stale.map((s) => sql15`${s}`), sql15`, `)})`).run();
27405
27712
  }
27406
- const row = tx.select().from(agentMemory).where(and27(eq38(agentMemory.projectId, args.projectId), eq38(agentMemory.key, key))).get();
27713
+ const row = tx.select().from(agentMemory).where(and30(eq38(agentMemory.projectId, args.projectId), eq38(agentMemory.key, key))).get();
27407
27714
  if (row) inserted = rowToDto2(row);
27408
27715
  });
27409
27716
  if (!inserted) throw new Error("compaction note write produced no row");