@ainyc/canonry 4.112.7 → 4.114.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.
- package/assets/assets/{BacklinksPage-AYTE7he1.js → BacklinksPage-pxm5QdkH.js} +1 -1
- package/assets/assets/{ChartPrimitives-J0xH87kk.js → ChartPrimitives-lKnj_JyP.js} +1 -1
- package/assets/assets/ProjectPage-38ZIpkmq.js +7 -0
- package/assets/assets/{RunRow-Duz6GoZ0.js → RunRow-BCinrp7o.js} +1 -1
- package/assets/assets/{RunsPage-DpocjSZ-.js → RunsPage-CRoqsHH6.js} +1 -1
- package/assets/assets/SettingsPage-BEJ39Z1y.js +1 -0
- package/assets/assets/{TrafficPage-B5c8opWF.js → TrafficPage-DwlIys8k.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-D9JPwzH-.js → TrafficSourceDetailPage-BxZdMbPs.js} +1 -1
- package/assets/assets/{arrow-left-DMukIffh.js → arrow-left-Bh1K7Q5q.js} +1 -1
- package/assets/assets/{extract-error-message-CVln-Uf_.js → extract-error-message-CC-RB1fH.js} +1 -1
- package/assets/assets/index--YS3kkht.js +210 -0
- package/assets/assets/index-CReX5Jn9.css +1 -0
- package/assets/assets/{trash-2-BbhOWhnf.js → trash-2-uW1qyzpW.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-D5PEINWZ.js → chunk-3VSTTH4M.js} +303 -185
- package/dist/{chunk-K2XOEBSH.js → chunk-DUBZH5JW.js} +1 -1
- package/dist/{chunk-DBZLOLYT.js → chunk-VEUBAZRG.js} +8 -1
- package/dist/{chunk-FVELOZTK.js → chunk-WMWZW34J.js} +71 -9
- package/dist/cli.js +4 -4
- package/dist/index.d.ts +9 -0
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-ZPY7NIKR.js → intelligence-service-I5EQ77KZ.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +9 -9
- package/assets/assets/ProjectPage-BcqfsS5X.js +0 -7
- package/assets/assets/SettingsPage-CboDfSRx.js +0 -1
- package/assets/assets/index-DNoISJxr.js +0 -210
- package/assets/assets/index-l-PAIV0A.css +0 -1
|
@@ -179,6 +179,7 @@ import {
|
|
|
179
179
|
mapWithConcurrency,
|
|
180
180
|
mentionStateFromAnswerMentioned,
|
|
181
181
|
missingDependency,
|
|
182
|
+
normalizeIdTokens,
|
|
182
183
|
normalizeProjectAliases,
|
|
183
184
|
normalizeProjectDomain,
|
|
184
185
|
normalizeQueryText,
|
|
@@ -237,6 +238,7 @@ import {
|
|
|
237
238
|
snapshotReportSchema,
|
|
238
239
|
snapshotRequestSchema,
|
|
239
240
|
sourceBreakdownDtoSchema,
|
|
241
|
+
splitList,
|
|
240
242
|
sumInfraHits,
|
|
241
243
|
summarizeCheckResults,
|
|
242
244
|
surfaceClassFromCompetitorType,
|
|
@@ -271,10 +273,10 @@ import {
|
|
|
271
273
|
wordpressSchemaDeployResultDtoSchema,
|
|
272
274
|
wordpressSchemaStatusResultDtoSchema,
|
|
273
275
|
wordpressStatusDtoSchema
|
|
274
|
-
} from "./chunk-
|
|
276
|
+
} from "./chunk-VEUBAZRG.js";
|
|
275
277
|
|
|
276
278
|
// src/intelligence-service.ts
|
|
277
|
-
import { eq as eq38, desc as desc18, asc as asc6, and as
|
|
279
|
+
import { eq as eq38, desc as desc18, asc as asc6, and as and29, ne as ne5, or as or6, inArray as inArray14, gte as gte7, lte as lte4 } from "drizzle-orm";
|
|
278
280
|
|
|
279
281
|
// ../db/src/client.ts
|
|
280
282
|
import { mkdirSync } from "fs";
|
|
@@ -5847,6 +5849,82 @@ function parseCookies(header) {
|
|
|
5847
5849
|
return cookies;
|
|
5848
5850
|
}, {});
|
|
5849
5851
|
}
|
|
5852
|
+
function queryValue(request, key) {
|
|
5853
|
+
const raw = request.query?.[key];
|
|
5854
|
+
if (typeof raw === "string") return raw;
|
|
5855
|
+
if (Array.isArray(raw) && typeof raw[0] === "string") return raw[0];
|
|
5856
|
+
return void 0;
|
|
5857
|
+
}
|
|
5858
|
+
function requestEmbedProjectTabs(request, fallback) {
|
|
5859
|
+
const headerTabs = normalizeIdTokens(splitList(request.headers["x-canonry-embed-tabs"]));
|
|
5860
|
+
if (headerTabs) return headerTabs;
|
|
5861
|
+
return fallback && fallback.length > 0 ? [...fallback] : void 0;
|
|
5862
|
+
}
|
|
5863
|
+
function projectRouteRest(url) {
|
|
5864
|
+
const match = url.match(/\/projects\/[^/]+(?:\/([^?#]*))?$/);
|
|
5865
|
+
if (!match) return null;
|
|
5866
|
+
return match[1] ?? "";
|
|
5867
|
+
}
|
|
5868
|
+
function isGlobalAnswerVisibilityRunsList(request, url) {
|
|
5869
|
+
if (!url.endsWith("/runs")) return false;
|
|
5870
|
+
const kind = queryValue(request, "kind");
|
|
5871
|
+
return kind === RunKinds["answer-visibility"];
|
|
5872
|
+
}
|
|
5873
|
+
function isAnswerVisibilityRunsList(request, rest) {
|
|
5874
|
+
if (rest !== "runs") return false;
|
|
5875
|
+
const kind = queryValue(request, "kind");
|
|
5876
|
+
return kind === RunKinds["answer-visibility"];
|
|
5877
|
+
}
|
|
5878
|
+
function isAnswerVisibilityRunDetail(request, url) {
|
|
5879
|
+
const runMatch = url.match(/\/runs\/([^/?#]+)$/);
|
|
5880
|
+
if (!runMatch) return false;
|
|
5881
|
+
const run = request.server.db.select({ kind: runs.kind }).from(runs).where(eq(runs.id, decodeURIComponent(runMatch[1]))).get();
|
|
5882
|
+
return !run || run.kind === RunKinds["answer-visibility"];
|
|
5883
|
+
}
|
|
5884
|
+
function isProjectShellRead(request, url) {
|
|
5885
|
+
if (url.endsWith("/projects")) return true;
|
|
5886
|
+
if (isGlobalAnswerVisibilityRunsList(request, url)) return true;
|
|
5887
|
+
const rest = projectRouteRest(url);
|
|
5888
|
+
if (rest === null) return isAnswerVisibilityRunDetail(request, url);
|
|
5889
|
+
return rest === "" || isAnswerVisibilityRunsList(request, rest);
|
|
5890
|
+
}
|
|
5891
|
+
function isOverviewRead(url) {
|
|
5892
|
+
const rest = projectRouteRest(url);
|
|
5893
|
+
if (rest === null) return false;
|
|
5894
|
+
return (/* @__PURE__ */ new Set([
|
|
5895
|
+
"queries",
|
|
5896
|
+
"competitors",
|
|
5897
|
+
"timeline",
|
|
5898
|
+
"overview",
|
|
5899
|
+
"analytics/metrics",
|
|
5900
|
+
"google/gsc/coverage",
|
|
5901
|
+
"bing/coverage",
|
|
5902
|
+
"insights",
|
|
5903
|
+
"citations/visibility"
|
|
5904
|
+
])).has(rest);
|
|
5905
|
+
}
|
|
5906
|
+
function isTechnicalAeoRead(url) {
|
|
5907
|
+
const rest = projectRouteRest(url);
|
|
5908
|
+
return rest === "technical-aeo" || rest === "technical-aeo/pages" || rest === "technical-aeo/trend";
|
|
5909
|
+
}
|
|
5910
|
+
function isReportRead(url) {
|
|
5911
|
+
const rest = projectRouteRest(url);
|
|
5912
|
+
return rest === "report" || rest === "report.html";
|
|
5913
|
+
}
|
|
5914
|
+
function enforceEmbedProjectTabs(request, configuredTabs) {
|
|
5915
|
+
const tabs = requestEmbedProjectTabs(request, configuredTabs);
|
|
5916
|
+
if (!tabs || tabs.length === 0) return;
|
|
5917
|
+
if (request.method === "OPTIONS") return;
|
|
5918
|
+
if (WRITE_METHODS.has(request.method)) {
|
|
5919
|
+
throw forbidden("This endpoint is not available in embed mode.");
|
|
5920
|
+
}
|
|
5921
|
+
const url = request.url.split("?")[0];
|
|
5922
|
+
if (isProjectShellRead(request, url)) return;
|
|
5923
|
+
if (tabs.includes("overview") && isOverviewRead(url)) return;
|
|
5924
|
+
if (tabs.includes("technical-aeo") && isTechnicalAeoRead(url)) return;
|
|
5925
|
+
if (tabs.includes("report") && isReportRead(url)) return;
|
|
5926
|
+
throw forbidden("This endpoint is not available for the configured embed tabs.");
|
|
5927
|
+
}
|
|
5850
5928
|
async function authPlugin(app, opts = {}) {
|
|
5851
5929
|
app.addHook("onRequest", async (request) => {
|
|
5852
5930
|
const url = request.url.split("?")[0];
|
|
@@ -5884,6 +5962,7 @@ async function authPlugin(app, opts = {}) {
|
|
|
5884
5962
|
if (isReadOnlyKey(scopes) && WRITE_METHODS.has(request.method)) {
|
|
5885
5963
|
throw forbidden("This API key is read-only and cannot perform write operations.");
|
|
5886
5964
|
}
|
|
5965
|
+
enforceEmbedProjectTabs(request, opts.embedProjectTabs);
|
|
5887
5966
|
if (key.projectId) {
|
|
5888
5967
|
const match = url.match(/\/projects\/([^/?#]+)/);
|
|
5889
5968
|
if (match) {
|
|
@@ -6291,7 +6370,7 @@ function aliasArraysEqual(a, b) {
|
|
|
6291
6370
|
|
|
6292
6371
|
// ../api-routes/src/queries.ts
|
|
6293
6372
|
import crypto5 from "crypto";
|
|
6294
|
-
import { eq as eq4, inArray, sql as sql4 } from "drizzle-orm";
|
|
6373
|
+
import { and, eq as eq4, inArray, sql as sql4 } from "drizzle-orm";
|
|
6295
6374
|
function preserveSnapshotQueryText(tx, projectId, queryIds) {
|
|
6296
6375
|
const candidates = queryIds && queryIds.length > 0 ? tx.select({ id: queries.id, text: queries.query }).from(queries).where(inArray(queries.id, queryIds)).all() : tx.select({ id: queries.id, text: queries.query }).from(queries).where(eq4(queries.projectId, projectId)).all();
|
|
6297
6376
|
for (const q of candidates) {
|
|
@@ -6391,6 +6470,26 @@ async function queryRoutes(app, opts) {
|
|
|
6391
6470
|
const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
|
|
6392
6471
|
return reply.send(rows.map((r) => ({ id: r.id, query: r.query, createdAt: r.createdAt })));
|
|
6393
6472
|
});
|
|
6473
|
+
app.delete("/projects/:name/queries/:id", async (request, reply) => {
|
|
6474
|
+
const project = resolveProject(app.db, request.params.name);
|
|
6475
|
+
const query = app.db.select().from(queries).where(and(eq4(queries.projectId, project.id), eq4(queries.id, request.params.id))).get();
|
|
6476
|
+
if (!query) {
|
|
6477
|
+
throw notFound("Query", request.params.id);
|
|
6478
|
+
}
|
|
6479
|
+
app.db.transaction((tx) => {
|
|
6480
|
+
preserveSnapshotQueryText(tx, project.id, [query.id]);
|
|
6481
|
+
tx.delete(queries).where(eq4(queries.id, query.id)).run();
|
|
6482
|
+
writeAuditLog(tx, auditFromRequest(request, {
|
|
6483
|
+
projectId: project.id,
|
|
6484
|
+
actor: "api",
|
|
6485
|
+
action: "queries.deleted",
|
|
6486
|
+
entityType: "query",
|
|
6487
|
+
entityId: query.id,
|
|
6488
|
+
diff: { deleted: [query.query] }
|
|
6489
|
+
}));
|
|
6490
|
+
});
|
|
6491
|
+
return reply.status(204).send();
|
|
6492
|
+
});
|
|
6394
6493
|
app.post("/projects/:name/queries", async (request, reply) => {
|
|
6395
6494
|
const project = resolveProject(app.db, request.params.name);
|
|
6396
6495
|
const body = request.body;
|
|
@@ -6730,12 +6829,12 @@ function parseCompetitorBatch(value) {
|
|
|
6730
6829
|
|
|
6731
6830
|
// ../api-routes/src/runs.ts
|
|
6732
6831
|
import crypto8 from "crypto";
|
|
6733
|
-
import { and as
|
|
6832
|
+
import { and as and3, eq as eq7, asc, desc, or as or2, sql as sql5 } from "drizzle-orm";
|
|
6734
6833
|
import { gte } from "drizzle-orm";
|
|
6735
6834
|
|
|
6736
6835
|
// ../api-routes/src/run-queue.ts
|
|
6737
6836
|
import crypto7 from "crypto";
|
|
6738
|
-
import { and, eq as eq6, or } from "drizzle-orm";
|
|
6837
|
+
import { and as and2, eq as eq6, or } from "drizzle-orm";
|
|
6739
6838
|
function queueRunIfProjectIdle(db, params) {
|
|
6740
6839
|
const createdAt = params.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
6741
6840
|
const kind = params.kind ?? "answer-visibility";
|
|
@@ -6743,7 +6842,7 @@ function queueRunIfProjectIdle(db, params) {
|
|
|
6743
6842
|
const runId = crypto7.randomUUID();
|
|
6744
6843
|
return db.transaction((tx) => {
|
|
6745
6844
|
const activeRun = tx.select().from(runs).where(
|
|
6746
|
-
|
|
6845
|
+
and2(
|
|
6747
6846
|
eq6(runs.projectId, params.projectId),
|
|
6748
6847
|
or(eq6(runs.status, "queued"), eq6(runs.status, "running"))
|
|
6749
6848
|
)
|
|
@@ -6826,7 +6925,7 @@ async function runRoutes(app, opts) {
|
|
|
6826
6925
|
throw validationError("No locations configured for this project");
|
|
6827
6926
|
}
|
|
6828
6927
|
const result = app.db.transaction((tx) => {
|
|
6829
|
-
const activeRun = tx.select({ id: runs.id }).from(runs).where(
|
|
6928
|
+
const activeRun = tx.select({ id: runs.id }).from(runs).where(and3(
|
|
6830
6929
|
eq7(runs.projectId, project.id),
|
|
6831
6930
|
or2(eq7(runs.status, "queued"), eq7(runs.status, "running"))
|
|
6832
6931
|
)).get();
|
|
@@ -6899,15 +6998,15 @@ async function runRoutes(app, opts) {
|
|
|
6899
6998
|
const parsedLimit = parseInt(request.query.limit ?? "", 10);
|
|
6900
6999
|
const limit = Number.isNaN(parsedLimit) || parsedLimit <= 0 ? void 0 : parsedLimit;
|
|
6901
7000
|
const kind = parseListKind(request.query.kind);
|
|
6902
|
-
const where = kind ?
|
|
7001
|
+
const where = kind ? and3(eq7(runs.projectId, project.id), eq7(runs.kind, kind)) : eq7(runs.projectId, project.id);
|
|
6903
7002
|
const rows = limit == null ? app.db.select().from(runs).where(where).orderBy(asc(runs.createdAt)).all() : app.db.select().from(runs).where(where).orderBy(desc(runs.createdAt)).limit(limit).all().reverse();
|
|
6904
7003
|
return reply.send(rows.map(formatRun));
|
|
6905
7004
|
});
|
|
6906
7005
|
app.get("/projects/:name/runs/latest", async (request, reply) => {
|
|
6907
7006
|
const project = resolveProject(app.db, request.params.name);
|
|
6908
|
-
const countRow = app.db.select({ count: sql5`count(*)` }).from(runs).where(
|
|
7007
|
+
const countRow = app.db.select({ count: sql5`count(*)` }).from(runs).where(and3(eq7(runs.projectId, project.id), notProbeRun())).get();
|
|
6909
7008
|
const totalRuns = countRow?.count ?? 0;
|
|
6910
|
-
const latestRun = app.db.select().from(runs).where(
|
|
7009
|
+
const latestRun = app.db.select().from(runs).where(and3(eq7(runs.projectId, project.id), notProbeRun())).orderBy(desc(runs.createdAt), desc(runs.id)).limit(1).get();
|
|
6911
7010
|
if (!latestRun) {
|
|
6912
7011
|
return reply.send({ totalRuns: 0, run: null });
|
|
6913
7012
|
}
|
|
@@ -6926,7 +7025,7 @@ async function runRoutes(app, opts) {
|
|
|
6926
7025
|
if (kind) filters.push(eq7(runs.kind, kind));
|
|
6927
7026
|
const scopedProjectId = request.apiKey?.projectId;
|
|
6928
7027
|
if (scopedProjectId) filters.push(eq7(runs.projectId, scopedProjectId));
|
|
6929
|
-
const rows = app.db.select().from(runs).where(
|
|
7028
|
+
const rows = app.db.select().from(runs).where(and3(...filters)).orderBy(desc(runs.createdAt), desc(runs.id)).limit(limit).all();
|
|
6930
7029
|
return reply.send(rows.map(formatRun));
|
|
6931
7030
|
});
|
|
6932
7031
|
app.post("/runs", async (request, reply) => {
|
|
@@ -7137,7 +7236,7 @@ function loadRunDetail(app, run) {
|
|
|
7137
7236
|
|
|
7138
7237
|
// ../api-routes/src/apply.ts
|
|
7139
7238
|
import crypto10 from "crypto";
|
|
7140
|
-
import { and as
|
|
7239
|
+
import { and as and4, eq as eq8 } from "drizzle-orm";
|
|
7141
7240
|
|
|
7142
7241
|
// ../api-routes/src/schedule-utils.ts
|
|
7143
7242
|
import { CronExpressionParser } from "cron-parser";
|
|
@@ -7580,7 +7679,7 @@ async function applyRoutes(app, opts) {
|
|
|
7580
7679
|
});
|
|
7581
7680
|
const AV_KIND = SchedulableRunKinds["answer-visibility"];
|
|
7582
7681
|
if (resolvedSchedule) {
|
|
7583
|
-
const existingSched = tx.select().from(schedules).where(
|
|
7682
|
+
const existingSched = tx.select().from(schedules).where(and4(eq8(schedules.projectId, projectId), eq8(schedules.kind, AV_KIND))).get();
|
|
7584
7683
|
if (existingSched) {
|
|
7585
7684
|
tx.update(schedules).set({
|
|
7586
7685
|
cronExpr: resolvedSchedule.cronExpr,
|
|
@@ -7606,9 +7705,9 @@ async function applyRoutes(app, opts) {
|
|
|
7606
7705
|
}
|
|
7607
7706
|
scheduleAction = "upsert";
|
|
7608
7707
|
} else if (deleteSchedule) {
|
|
7609
|
-
const existingSched = tx.select().from(schedules).where(
|
|
7708
|
+
const existingSched = tx.select().from(schedules).where(and4(eq8(schedules.projectId, projectId), eq8(schedules.kind, AV_KIND))).get();
|
|
7610
7709
|
if (existingSched) {
|
|
7611
|
-
tx.delete(schedules).where(
|
|
7710
|
+
tx.delete(schedules).where(and4(eq8(schedules.projectId, projectId), eq8(schedules.kind, AV_KIND))).run();
|
|
7612
7711
|
scheduleAction = "delete";
|
|
7613
7712
|
}
|
|
7614
7713
|
}
|
|
@@ -7692,7 +7791,7 @@ function normalizeCompetitorList2(domains) {
|
|
|
7692
7791
|
}
|
|
7693
7792
|
|
|
7694
7793
|
// ../api-routes/src/history.ts
|
|
7695
|
-
import { and as
|
|
7794
|
+
import { and as and5, eq as eq9, desc as desc2, inArray as inArray2 } from "drizzle-orm";
|
|
7696
7795
|
|
|
7697
7796
|
// ../api-routes/src/notification-redaction.ts
|
|
7698
7797
|
var REDACTED_URL = {
|
|
@@ -7750,7 +7849,7 @@ async function historyRoutes(app) {
|
|
|
7750
7849
|
const project = resolveProject(app.db, request.params.name);
|
|
7751
7850
|
const limit = parseInt(request.query.limit ?? "50", 10);
|
|
7752
7851
|
const offset = parseInt(request.query.offset ?? "0", 10);
|
|
7753
|
-
const projectRuns = app.db.select({ id: runs.id }).from(runs).where(
|
|
7852
|
+
const projectRuns = app.db.select({ id: runs.id }).from(runs).where(and5(eq9(runs.projectId, project.id), notProbeRun())).all();
|
|
7754
7853
|
if (projectRuns.length === 0) {
|
|
7755
7854
|
return reply.send({ snapshots: [], total: 0 });
|
|
7756
7855
|
}
|
|
@@ -7799,7 +7898,7 @@ async function historyRoutes(app) {
|
|
|
7799
7898
|
app.get("/projects/:name/timeline", async (request, reply) => {
|
|
7800
7899
|
const project = resolveProject(app.db, request.params.name);
|
|
7801
7900
|
const projectQueries = app.db.select().from(queries).where(eq9(queries.projectId, project.id)).all();
|
|
7802
|
-
const projectRuns = app.db.select().from(runs).where(
|
|
7901
|
+
const projectRuns = app.db.select().from(runs).where(and5(eq9(runs.projectId, project.id), notProbeRun())).orderBy(runs.createdAt).all();
|
|
7803
7902
|
if (projectRuns.length === 0 || projectQueries.length === 0) {
|
|
7804
7903
|
return reply.send([]);
|
|
7805
7904
|
}
|
|
@@ -8010,13 +8109,13 @@ function formatAuditEntry(row) {
|
|
|
8010
8109
|
}
|
|
8011
8110
|
|
|
8012
8111
|
// ../api-routes/src/analytics.ts
|
|
8013
|
-
import { and as
|
|
8112
|
+
import { and as and6, eq as eq10, desc as desc3, inArray as inArray3 } from "drizzle-orm";
|
|
8014
8113
|
async function analyticsRoutes(app) {
|
|
8015
8114
|
app.get("/projects/:name/analytics/metrics", async (request, reply) => {
|
|
8016
8115
|
const project = resolveProject(app.db, request.params.name);
|
|
8017
8116
|
const window = parseWindow(request.query.window);
|
|
8018
8117
|
const cutoff = windowCutoff(window);
|
|
8019
|
-
const projectRuns = app.db.select().from(runs).where(
|
|
8118
|
+
const projectRuns = app.db.select().from(runs).where(and6(eq10(runs.projectId, project.id), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
|
|
8020
8119
|
if (projectRuns.length === 0) {
|
|
8021
8120
|
return reply.send({
|
|
8022
8121
|
window,
|
|
@@ -8068,14 +8167,14 @@ async function analyticsRoutes(app) {
|
|
|
8068
8167
|
const project = resolveProject(app.db, request.params.name);
|
|
8069
8168
|
const window = parseWindow(request.query.window);
|
|
8070
8169
|
const cutoff = windowCutoff(window);
|
|
8071
|
-
const completedRuns = app.db.select().from(runs).where(
|
|
8170
|
+
const completedRuns = app.db.select().from(runs).where(and6(eq10(runs.projectId, project.id), notProbeRun())).orderBy(desc3(runs.createdAt), desc3(runs.id)).all().filter((r) => r.status === "completed" || r.status === "partial");
|
|
8072
8171
|
const latestGroup = groupRunsByCreatedAt(completedRuns)[0] ?? [];
|
|
8073
8172
|
const latestGroupRunIds = latestGroup.map((r) => r.id);
|
|
8074
8173
|
const latestRun = pickGroupRepresentative(latestGroup);
|
|
8075
8174
|
if (!latestRun) {
|
|
8076
8175
|
return reply.send({ cited: [], gap: [], uncited: [], mentionedQueries: [], mentionGap: [], notMentioned: [], runId: "", window });
|
|
8077
8176
|
}
|
|
8078
|
-
const windowRuns = app.db.select().from(runs).where(
|
|
8177
|
+
const windowRuns = app.db.select().from(runs).where(and6(eq10(runs.projectId, project.id), notProbeRun())).orderBy(runs.createdAt).all().filter((r) => r.status === "completed" || r.status === "partial").filter((r) => !cutoff || r.createdAt >= cutoff);
|
|
8079
8178
|
const windowRunIds = windowRuns.map((r) => r.id);
|
|
8080
8179
|
const runIdToCreatedAt = new Map(windowRuns.map((r) => [r.id, r.createdAt]));
|
|
8081
8180
|
const consistencyMap = /* @__PURE__ */ new Map();
|
|
@@ -8206,7 +8305,7 @@ async function analyticsRoutes(app) {
|
|
|
8206
8305
|
const mapped = surfaceClassFromCompetitorType(row.competitorType);
|
|
8207
8306
|
if (mapped) storedSurfaceClasses.set(normalizeProjectDomain(row.domain), mapped);
|
|
8208
8307
|
}
|
|
8209
|
-
const windowRuns = app.db.select().from(runs).where(
|
|
8308
|
+
const windowRuns = app.db.select().from(runs).where(and6(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);
|
|
8210
8309
|
if (windowRuns.length === 0) {
|
|
8211
8310
|
return reply.send({
|
|
8212
8311
|
overall: [],
|
|
@@ -8471,7 +8570,7 @@ function buildCategoryCounts(counts2) {
|
|
|
8471
8570
|
}
|
|
8472
8571
|
|
|
8473
8572
|
// ../api-routes/src/intelligence.ts
|
|
8474
|
-
import { eq as eq11, desc as desc4, and as
|
|
8573
|
+
import { eq as eq11, desc as desc4, and as and7, inArray as inArray4, like } from "drizzle-orm";
|
|
8475
8574
|
var SEVERITY_RANK = { low: 0, medium: 1, high: 2, critical: 3 };
|
|
8476
8575
|
function severitiesAtOrAbove(min) {
|
|
8477
8576
|
const floor = SEVERITY_RANK[min];
|
|
@@ -8611,7 +8710,7 @@ async function intelligenceRoutes(app) {
|
|
|
8611
8710
|
}
|
|
8612
8711
|
limit = parsed;
|
|
8613
8712
|
}
|
|
8614
|
-
const rows = app.db.select().from(insights).where(conditions.length === 1 ? conditions[0] :
|
|
8713
|
+
const rows = app.db.select().from(insights).where(conditions.length === 1 ? conditions[0] : and7(...conditions)).orderBy(desc4(insights.createdAt)).all();
|
|
8615
8714
|
const showDismissed = request.query.dismissed === "true";
|
|
8616
8715
|
let result = rows.filter((r) => showDismissed || !r.dismissed).map(mapInsightRow);
|
|
8617
8716
|
if (limit !== void 0) result = result.slice(0, limit);
|
|
@@ -8636,7 +8735,7 @@ async function intelligenceRoutes(app) {
|
|
|
8636
8735
|
});
|
|
8637
8736
|
app.get("/projects/:name/health/latest", async (request, reply) => {
|
|
8638
8737
|
const project = resolveProject(app.db, request.params.name);
|
|
8639
|
-
const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
|
|
8738
|
+
const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and7(
|
|
8640
8739
|
eq11(runs.projectId, project.id),
|
|
8641
8740
|
eq11(runs.kind, RunKinds["answer-visibility"]),
|
|
8642
8741
|
inArray4(runs.status, [RunStatuses.completed, RunStatuses.partial]),
|
|
@@ -8647,7 +8746,7 @@ async function intelligenceRoutes(app) {
|
|
|
8647
8746
|
const latestGroup = groupRunsByCreatedAt(projectVisRuns)[0] ?? [];
|
|
8648
8747
|
const latestGroupRunIds = latestGroup.map((r) => r.id);
|
|
8649
8748
|
if (latestGroupRunIds.length > 0) {
|
|
8650
|
-
const groupRows = app.db.select().from(healthSnapshots).where(
|
|
8749
|
+
const groupRows = app.db.select().from(healthSnapshots).where(and7(
|
|
8651
8750
|
eq11(healthSnapshots.projectId, project.id),
|
|
8652
8751
|
inArray4(healthSnapshots.runId, latestGroupRunIds)
|
|
8653
8752
|
)).all();
|
|
@@ -8671,14 +8770,14 @@ async function intelligenceRoutes(app) {
|
|
|
8671
8770
|
}
|
|
8672
8771
|
|
|
8673
8772
|
// ../api-routes/src/report.ts
|
|
8674
|
-
import { and as
|
|
8773
|
+
import { and as and11, desc as desc7, eq as eq15, gte as gte2, inArray as inArray6, lt, lte, ne as ne2, or as or3, sql as sql7 } from "drizzle-orm";
|
|
8675
8774
|
|
|
8676
8775
|
// ../api-routes/src/content.ts
|
|
8677
8776
|
import crypto11 from "crypto";
|
|
8678
|
-
import { and as
|
|
8777
|
+
import { and as and9, desc as desc6, eq as eq13 } from "drizzle-orm";
|
|
8679
8778
|
|
|
8680
8779
|
// ../api-routes/src/content-data.ts
|
|
8681
|
-
import { and as
|
|
8780
|
+
import { and as and8, eq as eq12, desc as desc5, inArray as inArray5 } from "drizzle-orm";
|
|
8682
8781
|
var RECENT_RUNS_WINDOW = 5;
|
|
8683
8782
|
function loadOrchestratorInput(db, project, locationFilter = void 0) {
|
|
8684
8783
|
const projectId = project.id;
|
|
@@ -8808,7 +8907,7 @@ function listCompetitorDomains(db, projectId) {
|
|
|
8808
8907
|
}
|
|
8809
8908
|
function listRecentAnswerVisibilityRunIds(db, projectId, limit, locationFilter) {
|
|
8810
8909
|
const rows = db.select({ id: runs.id, location: runs.location }).from(runs).where(
|
|
8811
|
-
|
|
8910
|
+
and8(
|
|
8812
8911
|
eq12(runs.projectId, projectId),
|
|
8813
8912
|
eq12(runs.kind, RunKinds["answer-visibility"]),
|
|
8814
8913
|
// Queued/running/failed/cancelled runs may have partial or no
|
|
@@ -9179,7 +9278,7 @@ async function contentRoutes(app, opts = {}) {
|
|
|
9179
9278
|
dismissedAt: now
|
|
9180
9279
|
}
|
|
9181
9280
|
}).run();
|
|
9182
|
-
const row = app.db.select().from(contentTargetDismissals).where(
|
|
9281
|
+
const row = app.db.select().from(contentTargetDismissals).where(and9(
|
|
9183
9282
|
eq13(contentTargetDismissals.projectId, project.id),
|
|
9184
9283
|
eq13(contentTargetDismissals.targetRef, targetRef)
|
|
9185
9284
|
)).get();
|
|
@@ -9189,7 +9288,7 @@ async function contentRoutes(app, opts = {}) {
|
|
|
9189
9288
|
app.delete("/projects/:name/content/dismissals/:targetRef", async (request, reply) => {
|
|
9190
9289
|
const project = resolveProject(app.db, request.params.name);
|
|
9191
9290
|
const { targetRef } = request.params;
|
|
9192
|
-
const result = app.db.delete(contentTargetDismissals).where(
|
|
9291
|
+
const result = app.db.delete(contentTargetDismissals).where(and9(
|
|
9193
9292
|
eq13(contentTargetDismissals.projectId, project.id),
|
|
9194
9293
|
eq13(contentTargetDismissals.targetRef, targetRef)
|
|
9195
9294
|
)).run();
|
|
@@ -9200,7 +9299,7 @@ async function contentRoutes(app, opts = {}) {
|
|
|
9200
9299
|
});
|
|
9201
9300
|
app.get("/projects/:name/content/recommendations/:targetRef/analysis", async (request, reply) => {
|
|
9202
9301
|
const project = resolveProject(app.db, request.params.name);
|
|
9203
|
-
const row = app.db.select().from(recommendationExplanations).where(
|
|
9302
|
+
const row = app.db.select().from(recommendationExplanations).where(and9(
|
|
9204
9303
|
eq13(recommendationExplanations.projectId, project.id),
|
|
9205
9304
|
eq13(recommendationExplanations.targetRef, request.params.targetRef)
|
|
9206
9305
|
)).orderBy(desc6(recommendationExplanations.generatedAt)).limit(1).get();
|
|
@@ -9228,7 +9327,7 @@ async function contentRoutes(app, opts = {}) {
|
|
|
9228
9327
|
throw notFound("contentRecommendation", targetRef);
|
|
9229
9328
|
}
|
|
9230
9329
|
if (!body.forceRefresh) {
|
|
9231
|
-
const cached = app.db.select().from(recommendationExplanations).where(
|
|
9330
|
+
const cached = app.db.select().from(recommendationExplanations).where(and9(
|
|
9232
9331
|
eq13(recommendationExplanations.projectId, project.id),
|
|
9233
9332
|
eq13(recommendationExplanations.targetRef, targetRef)
|
|
9234
9333
|
)).orderBy(desc6(recommendationExplanations.generatedAt)).limit(1).get();
|
|
@@ -9267,7 +9366,7 @@ async function contentRoutes(app, opts = {}) {
|
|
|
9267
9366
|
generatedAt: now
|
|
9268
9367
|
}
|
|
9269
9368
|
}).run();
|
|
9270
|
-
const row = app.db.select().from(recommendationExplanations).where(
|
|
9369
|
+
const row = app.db.select().from(recommendationExplanations).where(and9(
|
|
9271
9370
|
eq13(recommendationExplanations.projectId, project.id),
|
|
9272
9371
|
eq13(recommendationExplanations.targetRef, targetRef),
|
|
9273
9372
|
eq13(recommendationExplanations.promptVersion, result.promptVersion)
|
|
@@ -9351,7 +9450,7 @@ async function contentRoutes(app, opts = {}) {
|
|
|
9351
9450
|
generatedAt: now
|
|
9352
9451
|
}
|
|
9353
9452
|
}).run();
|
|
9354
|
-
const row = app.db.select().from(recommendationBriefs).where(
|
|
9453
|
+
const row = app.db.select().from(recommendationBriefs).where(and9(
|
|
9355
9454
|
eq13(recommendationBriefs.projectId, project.id),
|
|
9356
9455
|
eq13(recommendationBriefs.targetRef, targetRef),
|
|
9357
9456
|
eq13(recommendationBriefs.promptVersion, result.promptVersion)
|
|
@@ -9381,7 +9480,7 @@ function lookupCachedBrief(db, projectId, targetRef, promptVersion) {
|
|
|
9381
9480
|
if (promptVersion !== void 0) {
|
|
9382
9481
|
conditions.push(eq13(recommendationBriefs.promptVersion, promptVersion));
|
|
9383
9482
|
}
|
|
9384
|
-
return db.select().from(recommendationBriefs).where(
|
|
9483
|
+
return db.select().from(recommendationBriefs).where(and9(...conditions)).orderBy(desc6(recommendationBriefs.generatedAt)).limit(1).get();
|
|
9385
9484
|
}
|
|
9386
9485
|
function parseLimitParam(raw) {
|
|
9387
9486
|
if (raw === void 0) return void 0;
|
|
@@ -9407,7 +9506,7 @@ function winnabilityClassRank(winnabilityClass) {
|
|
|
9407
9506
|
}
|
|
9408
9507
|
|
|
9409
9508
|
// ../api-routes/src/gsc-totals.ts
|
|
9410
|
-
import { and as
|
|
9509
|
+
import { and as and10, asc as asc2, eq as eq14, sql as sql6 } from "drizzle-orm";
|
|
9411
9510
|
function readGscDailyTotals(db, projectId, startDate, endDate) {
|
|
9412
9511
|
const rows = db.select({
|
|
9413
9512
|
date: gscDailyTotals.date,
|
|
@@ -9415,7 +9514,7 @@ function readGscDailyTotals(db, projectId, startDate, endDate) {
|
|
|
9415
9514
|
impressions: gscDailyTotals.impressions,
|
|
9416
9515
|
position: gscDailyTotals.position
|
|
9417
9516
|
}).from(gscDailyTotals).where(
|
|
9418
|
-
|
|
9517
|
+
and10(
|
|
9419
9518
|
eq14(gscDailyTotals.projectId, projectId),
|
|
9420
9519
|
sql6`${gscDailyTotals.date} >= ${startDate}`,
|
|
9421
9520
|
sql6`${gscDailyTotals.date} <= ${endDate}`
|
|
@@ -11846,7 +11945,7 @@ function buildGscSection(db, projectId, projectBrandNames, canonicalDomain, trac
|
|
|
11846
11945
|
function buildGaSection(db, projectId, windowDays) {
|
|
11847
11946
|
const gaWindowKey = GA_WINDOW_SUMMARY_KEYS[windowDays];
|
|
11848
11947
|
const windowSummary = gaWindowKey ? db.select().from(gaTrafficWindowSummaries).where(
|
|
11849
|
-
|
|
11948
|
+
and11(
|
|
11850
11949
|
eq15(gaTrafficWindowSummaries.projectId, projectId),
|
|
11851
11950
|
eq15(gaTrafficWindowSummaries.windowKey, gaWindowKey)
|
|
11852
11951
|
)
|
|
@@ -12024,7 +12123,7 @@ function nonSubresourceReferralPathCondition() {
|
|
|
12024
12123
|
}
|
|
12025
12124
|
function buildServerActivity(db, projectId, windowDays) {
|
|
12026
12125
|
const sourceRows = db.select({ id: trafficSources.id }).from(trafficSources).where(
|
|
12027
|
-
|
|
12126
|
+
and11(
|
|
12028
12127
|
eq15(trafficSources.projectId, projectId),
|
|
12029
12128
|
ne2(trafficSources.status, TrafficSourceStatuses.archived)
|
|
12030
12129
|
)
|
|
@@ -12040,7 +12139,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12040
12139
|
const trendStart = new Date(trendStartMs).toISOString();
|
|
12041
12140
|
const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
12042
12141
|
db.select({ total: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
12043
|
-
|
|
12142
|
+
and11(
|
|
12044
12143
|
eq15(crawlerEventsHourly.projectId, projectId),
|
|
12045
12144
|
eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
12046
12145
|
gte2(crawlerEventsHourly.tsHour, windowStartIso),
|
|
@@ -12050,7 +12149,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12050
12149
|
);
|
|
12051
12150
|
const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
12052
12151
|
db.select({ total: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
12053
|
-
|
|
12152
|
+
and11(
|
|
12054
12153
|
eq15(crawlerEventsHourly.projectId, projectId),
|
|
12055
12154
|
ne2(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
12056
12155
|
gte2(crawlerEventsHourly.tsHour, windowStartIso),
|
|
@@ -12060,7 +12159,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12060
12159
|
);
|
|
12061
12160
|
const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
12062
12161
|
db.select({ total: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
12063
|
-
|
|
12162
|
+
and11(
|
|
12064
12163
|
eq15(aiReferralEventsHourly.projectId, projectId),
|
|
12065
12164
|
nonSubresourceReferralPathCondition(),
|
|
12066
12165
|
gte2(aiReferralEventsHourly.tsHour, windowStartIso),
|
|
@@ -12070,7 +12169,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12070
12169
|
);
|
|
12071
12170
|
const sumUserFetches = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
12072
12171
|
db.select({ total: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
|
|
12073
|
-
|
|
12172
|
+
and11(
|
|
12074
12173
|
eq15(aiUserFetchEventsHourly.projectId, projectId),
|
|
12075
12174
|
gte2(aiUserFetchEventsHourly.tsHour, windowStartIso),
|
|
12076
12175
|
exclusiveEnd ? lt(aiUserFetchEventsHourly.tsHour, windowEndIso) : lte(aiUserFetchEventsHourly.tsHour, windowEndIso)
|
|
@@ -12090,7 +12189,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12090
12189
|
verificationStatus: crawlerEventsHourly.verificationStatus,
|
|
12091
12190
|
hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
12092
12191
|
}).from(crawlerEventsHourly).where(
|
|
12093
|
-
|
|
12192
|
+
and11(
|
|
12094
12193
|
eq15(crawlerEventsHourly.projectId, projectId),
|
|
12095
12194
|
gte2(crawlerEventsHourly.tsHour, headlineStart),
|
|
12096
12195
|
lte(crawlerEventsHourly.tsHour, headlineEnd)
|
|
@@ -12100,7 +12199,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12100
12199
|
operator: crawlerEventsHourly.operator,
|
|
12101
12200
|
hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
12102
12201
|
}).from(crawlerEventsHourly).where(
|
|
12103
|
-
|
|
12202
|
+
and11(
|
|
12104
12203
|
eq15(crawlerEventsHourly.projectId, projectId),
|
|
12105
12204
|
eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
12106
12205
|
gte2(crawlerEventsHourly.tsHour, priorStart),
|
|
@@ -12111,7 +12210,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12111
12210
|
operator: aiReferralEventsHourly.operator,
|
|
12112
12211
|
hits: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
|
|
12113
12212
|
}).from(aiReferralEventsHourly).where(
|
|
12114
|
-
|
|
12213
|
+
and11(
|
|
12115
12214
|
eq15(aiReferralEventsHourly.projectId, projectId),
|
|
12116
12215
|
nonSubresourceReferralPathCondition(),
|
|
12117
12216
|
gte2(aiReferralEventsHourly.tsHour, headlineStart),
|
|
@@ -12122,7 +12221,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12122
12221
|
operator: aiUserFetchEventsHourly.operator,
|
|
12123
12222
|
hits: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
|
|
12124
12223
|
}).from(aiUserFetchEventsHourly).where(
|
|
12125
|
-
|
|
12224
|
+
and11(
|
|
12126
12225
|
eq15(aiUserFetchEventsHourly.projectId, projectId),
|
|
12127
12226
|
gte2(aiUserFetchEventsHourly.tsHour, headlineStart),
|
|
12128
12227
|
lte(aiUserFetchEventsHourly.tsHour, headlineEnd)
|
|
@@ -12166,7 +12265,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12166
12265
|
hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
|
|
12167
12266
|
operators: sql7`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
|
|
12168
12267
|
}).from(crawlerEventsHourly).where(
|
|
12169
|
-
|
|
12268
|
+
and11(
|
|
12170
12269
|
eq15(crawlerEventsHourly.projectId, projectId),
|
|
12171
12270
|
eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
12172
12271
|
gte2(crawlerEventsHourly.tsHour, headlineStart),
|
|
@@ -12183,7 +12282,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12183
12282
|
arrivals: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
12184
12283
|
landingPaths: sql7`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
|
|
12185
12284
|
}).from(aiReferralEventsHourly).where(
|
|
12186
|
-
|
|
12285
|
+
and11(
|
|
12187
12286
|
eq15(aiReferralEventsHourly.projectId, projectId),
|
|
12188
12287
|
nonSubresourceReferralPathCondition(),
|
|
12189
12288
|
gte2(aiReferralEventsHourly.tsHour, headlineStart),
|
|
@@ -12200,7 +12299,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12200
12299
|
arrivals: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
12201
12300
|
products: sql7`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
|
|
12202
12301
|
}).from(aiReferralEventsHourly).where(
|
|
12203
|
-
|
|
12302
|
+
and11(
|
|
12204
12303
|
eq15(aiReferralEventsHourly.projectId, projectId),
|
|
12205
12304
|
nonSubresourceReferralPathCondition(),
|
|
12206
12305
|
gte2(aiReferralEventsHourly.tsHour, headlineStart),
|
|
@@ -12216,7 +12315,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12216
12315
|
date: sql7`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
|
|
12217
12316
|
hits: sql7`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
12218
12317
|
}).from(crawlerEventsHourly).where(
|
|
12219
|
-
|
|
12318
|
+
and11(
|
|
12220
12319
|
eq15(crawlerEventsHourly.projectId, projectId),
|
|
12221
12320
|
eq15(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
12222
12321
|
gte2(crawlerEventsHourly.tsHour, trendStart),
|
|
@@ -12227,7 +12326,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12227
12326
|
date: sql7`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
|
|
12228
12327
|
hits: sql7`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
|
|
12229
12328
|
}).from(aiReferralEventsHourly).where(
|
|
12230
|
-
|
|
12329
|
+
and11(
|
|
12231
12330
|
eq15(aiReferralEventsHourly.projectId, projectId),
|
|
12232
12331
|
nonSubresourceReferralPathCondition(),
|
|
12233
12332
|
gte2(aiReferralEventsHourly.tsHour, trendStart),
|
|
@@ -12238,7 +12337,7 @@ function buildServerActivity(db, projectId, windowDays) {
|
|
|
12238
12337
|
date: sql7`SUBSTR(${aiUserFetchEventsHourly.tsHour}, 1, 10)`,
|
|
12239
12338
|
hits: sql7`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)`
|
|
12240
12339
|
}).from(aiUserFetchEventsHourly).where(
|
|
12241
|
-
|
|
12340
|
+
and11(
|
|
12242
12341
|
eq15(aiUserFetchEventsHourly.projectId, projectId),
|
|
12243
12342
|
gte2(aiUserFetchEventsHourly.tsHour, trendStart),
|
|
12244
12343
|
lte(aiUserFetchEventsHourly.tsHour, headlineEnd)
|
|
@@ -12323,7 +12422,7 @@ function buildIndexingHealth(db, projectId) {
|
|
|
12323
12422
|
return null;
|
|
12324
12423
|
}
|
|
12325
12424
|
function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
|
|
12326
|
-
const visibilityRuns = db.select().from(runs).where(
|
|
12425
|
+
const visibilityRuns = db.select().from(runs).where(and11(eq15(runs.projectId, projectId), eq15(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter);
|
|
12327
12426
|
const totalQueries = queryLookup.byId.size;
|
|
12328
12427
|
const points = [];
|
|
12329
12428
|
for (const run of visibilityRuns) {
|
|
@@ -12371,7 +12470,7 @@ function buildCitationsTrend(db, projectId, queryLookup, locationFilter) {
|
|
|
12371
12470
|
}
|
|
12372
12471
|
function buildInsightList(db, projectId, locationFilter) {
|
|
12373
12472
|
const recentRunIds = db.select({ id: runs.id, location: runs.location }).from(runs).where(
|
|
12374
|
-
|
|
12473
|
+
and11(
|
|
12375
12474
|
eq15(runs.projectId, projectId),
|
|
12376
12475
|
eq15(runs.kind, RunKinds["answer-visibility"]),
|
|
12377
12476
|
or3(eq15(runs.status, RunStatuses.completed), eq15(runs.status, RunStatuses.partial)),
|
|
@@ -12379,7 +12478,7 @@ function buildInsightList(db, projectId, locationFilter) {
|
|
|
12379
12478
|
)
|
|
12380
12479
|
).orderBy(desc7(runs.createdAt)).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter).slice(0, INSIGHT_LOOKBACK_RUNS).map((r) => r.id);
|
|
12381
12480
|
if (recentRunIds.length === 0) return [];
|
|
12382
|
-
const rows = db.select().from(insights).where(
|
|
12481
|
+
const rows = db.select().from(insights).where(and11(eq15(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc7(insights.createdAt)).all();
|
|
12383
12482
|
const severityRank = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
12384
12483
|
const flat = rows.filter((r) => !r.dismissed).map((r) => {
|
|
12385
12484
|
const recommendation = r.recommendation;
|
|
@@ -12931,7 +13030,7 @@ function buildProjectReport(db, projectName, periodDays) {
|
|
|
12931
13030
|
const project = resolveProject(db, projectName);
|
|
12932
13031
|
const queryLookup = loadQueryLookup(db, project.id);
|
|
12933
13032
|
const comparisonWindowDays = reportComparisonWindowDays(periodDays);
|
|
12934
|
-
const allRuns = db.select().from(runs).where(
|
|
13033
|
+
const allRuns = db.select().from(runs).where(and11(eq15(runs.projectId, project.id), notProbeRun())).orderBy(desc7(runs.createdAt), desc7(runs.id)).all();
|
|
12935
13034
|
const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
|
|
12936
13035
|
const completedVisRunGroups = groupRunsByCreatedAt(
|
|
12937
13036
|
visibilityRuns.filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial)
|
|
@@ -13167,7 +13266,7 @@ async function reportRoutes(app) {
|
|
|
13167
13266
|
}
|
|
13168
13267
|
|
|
13169
13268
|
// ../api-routes/src/citations.ts
|
|
13170
|
-
import { and as
|
|
13269
|
+
import { and as and12, eq as eq16, inArray as inArray7 } from "drizzle-orm";
|
|
13171
13270
|
async function citationRoutes(app) {
|
|
13172
13271
|
app.get("/projects/:name/citations/visibility", async (request, reply) => {
|
|
13173
13272
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -13176,7 +13275,7 @@ async function citationRoutes(app) {
|
|
|
13176
13275
|
if (projectQueries.length === 0) {
|
|
13177
13276
|
return reply.send(emptyCitationVisibility("no-queries"));
|
|
13178
13277
|
}
|
|
13179
|
-
const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
|
|
13278
|
+
const projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and12(eq16(runs.projectId, project.id), notProbeRun())).all();
|
|
13180
13279
|
if (projectRuns.length === 0) {
|
|
13181
13280
|
return reply.send(emptyCitationVisibility("no-runs-yet"));
|
|
13182
13281
|
}
|
|
@@ -13332,7 +13431,7 @@ function normalizeDomain2(domain) {
|
|
|
13332
13431
|
}
|
|
13333
13432
|
|
|
13334
13433
|
// ../api-routes/src/visibility-stats.ts
|
|
13335
|
-
import { and as
|
|
13434
|
+
import { and as and13, desc as desc8, eq as eq17, inArray as inArray8 } from "drizzle-orm";
|
|
13336
13435
|
function round42(value) {
|
|
13337
13436
|
return Math.round(value * 1e4) / 1e4;
|
|
13338
13437
|
}
|
|
@@ -13484,7 +13583,7 @@ async function visibilityStatsRoutes(app) {
|
|
|
13484
13583
|
lastRuns = n;
|
|
13485
13584
|
}
|
|
13486
13585
|
const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq17(queries.projectId, project.id)).all();
|
|
13487
|
-
let projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(
|
|
13586
|
+
let projectRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and13(eq17(runs.projectId, project.id), eq17(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).orderBy(desc8(runs.createdAt)).all().filter((r) => r.status === RunStatuses.completed || r.status === RunStatuses.partial);
|
|
13488
13587
|
if (sinceMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) >= sinceMs);
|
|
13489
13588
|
if (untilMs !== null) projectRuns = projectRuns.filter((r) => Date.parse(r.createdAt) <= untilMs);
|
|
13490
13589
|
if (lastRuns !== null) projectRuns = projectRuns.slice(0, lastRuns);
|
|
@@ -13554,7 +13653,7 @@ async function visibilityStatsRoutes(app) {
|
|
|
13554
13653
|
}
|
|
13555
13654
|
|
|
13556
13655
|
// ../api-routes/src/composites.ts
|
|
13557
|
-
import { eq as eq18, and as
|
|
13656
|
+
import { eq as eq18, and as and14, desc as desc9, sql as sql8, like as like2, or as or4, inArray as inArray9 } from "drizzle-orm";
|
|
13558
13657
|
var TOP_INSIGHT_LIMIT = 5;
|
|
13559
13658
|
var SEARCH_HIT_HARD_LIMIT = 50;
|
|
13560
13659
|
var SEARCH_SNIPPET_RADIUS = 80;
|
|
@@ -13572,7 +13671,7 @@ async function compositeRoutes(app) {
|
|
|
13572
13671
|
const project = resolveProject(app.db, request.params.name);
|
|
13573
13672
|
const filterLocation = (request.query.location ?? "").trim() || null;
|
|
13574
13673
|
const sinceIso = parseSinceFilter(request.query.since);
|
|
13575
|
-
const allRunsRaw = app.db.select().from(runs).where(
|
|
13674
|
+
const allRunsRaw = app.db.select().from(runs).where(and14(eq18(runs.projectId, project.id), notProbeRun())).orderBy(desc9(runs.createdAt), desc9(runs.id)).all();
|
|
13576
13675
|
const allRuns = allRunsRaw.filter((r) => runMatchesFilters(r, filterLocation, sinceIso));
|
|
13577
13676
|
const totalRuns = allRuns.length;
|
|
13578
13677
|
const visibilityRuns = allRuns.filter((r) => r.kind === RunKinds["answer-visibility"]);
|
|
@@ -13728,7 +13827,7 @@ async function compositeRoutes(app) {
|
|
|
13728
13827
|
rawResponse: querySnapshots.rawResponse,
|
|
13729
13828
|
createdAt: querySnapshots.createdAt
|
|
13730
13829
|
}).from(querySnapshots).innerJoin(queries, eq18(querySnapshots.queryId, queries.id)).where(
|
|
13731
|
-
|
|
13830
|
+
and14(
|
|
13732
13831
|
eq18(queries.projectId, project.id),
|
|
13733
13832
|
or4(
|
|
13734
13833
|
sql8`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
|
|
@@ -13739,7 +13838,7 @@ async function compositeRoutes(app) {
|
|
|
13739
13838
|
)
|
|
13740
13839
|
).orderBy(desc9(querySnapshots.createdAt)).limit(limit + 1).all());
|
|
13741
13840
|
const insightMatches = app.db.select().from(insights).where(
|
|
13742
|
-
|
|
13841
|
+
and14(
|
|
13743
13842
|
eq18(insights.projectId, project.id),
|
|
13744
13843
|
or4(
|
|
13745
13844
|
like2(insights.title, pattern),
|
|
@@ -13956,7 +14055,7 @@ function buildSuggestedQueriesFromGsc(app, projectId, trackedQueries) {
|
|
|
13956
14055
|
// NULLIF guards the degenerate impressions=0 case (SQLite returns NULL,
|
|
13957
14056
|
// which the JS coerces to 0 — caught by the impression floor anyway).
|
|
13958
14057
|
avgPosition: sql8`COALESCE(SUM(${gscSearchData.position} * ${gscSearchData.impressions}) * 1.0 / NULLIF(SUM(${gscSearchData.impressions}), 0), 0)`
|
|
13959
|
-
}).from(gscSearchData).where(
|
|
14058
|
+
}).from(gscSearchData).where(and14(
|
|
13960
14059
|
eq18(gscSearchData.projectId, projectId),
|
|
13961
14060
|
sql8`${gscSearchData.date} >= ${cutoff}`,
|
|
13962
14061
|
sql8`${gscSearchData.impressions} > 0`
|
|
@@ -14464,6 +14563,13 @@ var runIdParameter = {
|
|
|
14464
14563
|
description: "Run ID.",
|
|
14465
14564
|
schema: stringSchema
|
|
14466
14565
|
};
|
|
14566
|
+
var queryIdParameter = {
|
|
14567
|
+
name: "id",
|
|
14568
|
+
in: "path",
|
|
14569
|
+
required: true,
|
|
14570
|
+
description: "Query ID.",
|
|
14571
|
+
schema: stringSchema
|
|
14572
|
+
};
|
|
14467
14573
|
var notificationIdParameter = {
|
|
14468
14574
|
name: "id",
|
|
14469
14575
|
in: "path",
|
|
@@ -14870,6 +14976,17 @@ var routeCatalog = [
|
|
|
14870
14976
|
400: errorResponse("Invalid query delete request.")
|
|
14871
14977
|
}
|
|
14872
14978
|
},
|
|
14979
|
+
{
|
|
14980
|
+
method: "delete",
|
|
14981
|
+
path: "/api/v1/projects/{name}/queries/{id}",
|
|
14982
|
+
summary: "Delete one query by ID",
|
|
14983
|
+
tags: ["queries"],
|
|
14984
|
+
parameters: [nameParameter, queryIdParameter],
|
|
14985
|
+
responses: {
|
|
14986
|
+
204: { description: "Query deleted." },
|
|
14987
|
+
404: errorResponse("Project or query not found.")
|
|
14988
|
+
}
|
|
14989
|
+
},
|
|
14873
14990
|
{
|
|
14874
14991
|
method: "post",
|
|
14875
14992
|
path: "/api/v1/projects/{name}/queries",
|
|
@@ -18862,7 +18979,7 @@ async function telemetryRoutes(app, opts) {
|
|
|
18862
18979
|
|
|
18863
18980
|
// ../api-routes/src/schedules.ts
|
|
18864
18981
|
import crypto13 from "crypto";
|
|
18865
|
-
import { and as
|
|
18982
|
+
import { and as and15, eq as eq20 } from "drizzle-orm";
|
|
18866
18983
|
function parseKindParam(raw) {
|
|
18867
18984
|
if (raw === void 0 || raw === null || raw === "") return SchedulableRunKinds["answer-visibility"];
|
|
18868
18985
|
const parsed = schedulableRunKindSchema.safeParse(raw);
|
|
@@ -18934,7 +19051,7 @@ async function scheduleRoutes(app, opts) {
|
|
|
18934
19051
|
}
|
|
18935
19052
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
18936
19053
|
const enabledBool = enabled !== false;
|
|
18937
|
-
const existing = app.db.select().from(schedules).where(
|
|
19054
|
+
const existing = app.db.select().from(schedules).where(and15(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
|
|
18938
19055
|
if (existing) {
|
|
18939
19056
|
app.db.update(schedules).set({
|
|
18940
19057
|
cronExpr,
|
|
@@ -18968,13 +19085,13 @@ async function scheduleRoutes(app, opts) {
|
|
|
18968
19085
|
diff: { kind, cronExpr, preset, timezone, providers, sourceId }
|
|
18969
19086
|
});
|
|
18970
19087
|
opts.onScheduleUpdated?.("upsert", project.id, kind);
|
|
18971
|
-
const schedule = app.db.select().from(schedules).where(
|
|
19088
|
+
const schedule = app.db.select().from(schedules).where(and15(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
|
|
18972
19089
|
return reply.status(existing ? 200 : 201).send(formatSchedule(schedule));
|
|
18973
19090
|
});
|
|
18974
19091
|
app.get("/projects/:name/schedule", async (request, reply) => {
|
|
18975
19092
|
const project = resolveProject(app.db, request.params.name);
|
|
18976
19093
|
const kind = parseKindParam(request.query?.kind);
|
|
18977
|
-
const schedule = app.db.select().from(schedules).where(
|
|
19094
|
+
const schedule = app.db.select().from(schedules).where(and15(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
|
|
18978
19095
|
if (!schedule) {
|
|
18979
19096
|
throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
|
|
18980
19097
|
}
|
|
@@ -18983,7 +19100,7 @@ async function scheduleRoutes(app, opts) {
|
|
|
18983
19100
|
app.delete("/projects/:name/schedule", async (request, reply) => {
|
|
18984
19101
|
const project = resolveProject(app.db, request.params.name);
|
|
18985
19102
|
const kind = parseKindParam(request.query?.kind);
|
|
18986
|
-
const schedule = app.db.select().from(schedules).where(
|
|
19103
|
+
const schedule = app.db.select().from(schedules).where(and15(eq20(schedules.projectId, project.id), eq20(schedules.kind, kind))).get();
|
|
18987
19104
|
if (!schedule) {
|
|
18988
19105
|
throw notFound("Schedule", `${request.params.name} (kind=${kind})`);
|
|
18989
19106
|
}
|
|
@@ -19140,7 +19257,7 @@ function formatNotification(row) {
|
|
|
19140
19257
|
|
|
19141
19258
|
// ../api-routes/src/google.ts
|
|
19142
19259
|
import crypto18 from "crypto";
|
|
19143
|
-
import { eq as eq22, and as
|
|
19260
|
+
import { eq as eq22, and as and16, desc as desc11, sql as sql9, inArray as inArray10 } from "drizzle-orm";
|
|
19144
19261
|
|
|
19145
19262
|
// ../api-routes/src/gbp-summary.ts
|
|
19146
19263
|
function computeMetricTotals(rows) {
|
|
@@ -21120,7 +21237,7 @@ async function googleRoutes(app, opts) {
|
|
|
21120
21237
|
if (page) conditions.push(sql9`${gscSearchData.page} LIKE ${"%" + escapeLikePattern(page) + "%"} ESCAPE '\\'`);
|
|
21121
21238
|
const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
|
|
21122
21239
|
const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
|
|
21123
|
-
const rows = app.db.select().from(gscSearchData).where(
|
|
21240
|
+
const rows = app.db.select().from(gscSearchData).where(and16(...conditions)).orderBy(desc11(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
|
|
21124
21241
|
return rows.map((r) => ({
|
|
21125
21242
|
date: r.date,
|
|
21126
21243
|
query: r.query,
|
|
@@ -21148,7 +21265,7 @@ async function googleRoutes(app, opts) {
|
|
|
21148
21265
|
date: gscSearchData.date,
|
|
21149
21266
|
clicks: sql9`COALESCE(SUM(${gscSearchData.clicks}), 0)`,
|
|
21150
21267
|
impressions: sql9`COALESCE(SUM(${gscSearchData.impressions}), 0)`
|
|
21151
|
-
}).from(gscSearchData).where(
|
|
21268
|
+
}).from(gscSearchData).where(and16(...conditions)).groupBy(gscSearchData.date).orderBy(gscSearchData.date).all();
|
|
21152
21269
|
const daily = mergeGscDailyTotalsWithFallback(
|
|
21153
21270
|
dailyTotals,
|
|
21154
21271
|
dimensionedRows.map((r) => ({
|
|
@@ -21241,7 +21358,7 @@ async function googleRoutes(app, opts) {
|
|
|
21241
21358
|
const { url, limit } = request.query;
|
|
21242
21359
|
const conditions = [eq22(gscUrlInspections.projectId, project.id)];
|
|
21243
21360
|
if (url) conditions.push(eq22(gscUrlInspections.url, url));
|
|
21244
|
-
const rows = app.db.select().from(gscUrlInspections).where(
|
|
21361
|
+
const rows = app.db.select().from(gscUrlInspections).where(and16(...conditions)).orderBy(desc11(gscUrlInspections.inspectedAt)).limit(parseInt(limit ?? "100", 10)).all();
|
|
21245
21362
|
return rows.map((r) => ({
|
|
21246
21363
|
id: r.id,
|
|
21247
21364
|
url: r.url,
|
|
@@ -21736,7 +21853,7 @@ async function googleRoutes(app, opts) {
|
|
|
21736
21853
|
app.db.transaction((tx) => {
|
|
21737
21854
|
if (switching) clearGbpProjectData(tx, project.id);
|
|
21738
21855
|
for (const remote of remoteLocations) {
|
|
21739
|
-
const existing = tx.select().from(gbpLocations).where(
|
|
21856
|
+
const existing = tx.select().from(gbpLocations).where(and16(eq22(gbpLocations.projectId, project.id), eq22(gbpLocations.locationName, remote.name))).get();
|
|
21740
21857
|
const profile = buildLocationProfileFields(remote);
|
|
21741
21858
|
if (existing) {
|
|
21742
21859
|
tx.update(gbpLocations).set({
|
|
@@ -21833,7 +21950,7 @@ async function googleRoutes(app, opts) {
|
|
|
21833
21950
|
throw validationError(parsed.error.issues[0]?.message ?? "Invalid selection request");
|
|
21834
21951
|
}
|
|
21835
21952
|
const { selected } = parsed.data;
|
|
21836
|
-
const existing = app.db.select().from(gbpLocations).where(
|
|
21953
|
+
const existing = app.db.select().from(gbpLocations).where(and16(eq22(gbpLocations.projectId, project.id), eq22(gbpLocations.locationName, locationName))).get();
|
|
21837
21954
|
if (!existing) throw notFound("GBP location", locationName);
|
|
21838
21955
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21839
21956
|
app.db.transaction((tx) => {
|
|
@@ -21893,7 +22010,7 @@ async function googleRoutes(app, opts) {
|
|
|
21893
22010
|
const conditions = [eq22(gbpDailyMetrics.projectId, project.id)];
|
|
21894
22011
|
if (request.query.locationName) conditions.push(eq22(gbpDailyMetrics.locationName, request.query.locationName));
|
|
21895
22012
|
if (request.query.metric) conditions.push(eq22(gbpDailyMetrics.metric, request.query.metric));
|
|
21896
|
-
const rows = app.db.select().from(gbpDailyMetrics).where(
|
|
22013
|
+
const rows = app.db.select().from(gbpDailyMetrics).where(and16(...conditions)).orderBy(desc11(gbpDailyMetrics.date)).all();
|
|
21897
22014
|
return {
|
|
21898
22015
|
metrics: rows.map((r) => ({ locationName: r.locationName, date: r.date, metric: r.metric, value: r.value })),
|
|
21899
22016
|
total: rows.length
|
|
@@ -21903,7 +22020,7 @@ async function googleRoutes(app, opts) {
|
|
|
21903
22020
|
const project = resolveProject(app.db, request.params.name);
|
|
21904
22021
|
const conditions = [eq22(gbpKeywordImpressions.projectId, project.id)];
|
|
21905
22022
|
if (request.query.locationName) conditions.push(eq22(gbpKeywordImpressions.locationName, request.query.locationName));
|
|
21906
|
-
const rows = app.db.select().from(gbpKeywordImpressions).where(
|
|
22023
|
+
const rows = app.db.select().from(gbpKeywordImpressions).where(and16(...conditions)).all();
|
|
21907
22024
|
rows.sort((a, b) => (b.valueCount ?? -1) - (a.valueCount ?? -1));
|
|
21908
22025
|
const thresholded = rows.filter((r) => r.valueThreshold !== null).length;
|
|
21909
22026
|
return {
|
|
@@ -21923,7 +22040,7 @@ async function googleRoutes(app, opts) {
|
|
|
21923
22040
|
const project = resolveProject(app.db, request.params.name);
|
|
21924
22041
|
const conditions = [eq22(gbpPlaceActions.projectId, project.id)];
|
|
21925
22042
|
if (request.query.locationName) conditions.push(eq22(gbpPlaceActions.locationName, request.query.locationName));
|
|
21926
|
-
const rows = app.db.select().from(gbpPlaceActions).where(
|
|
22043
|
+
const rows = app.db.select().from(gbpPlaceActions).where(and16(...conditions)).all();
|
|
21927
22044
|
return {
|
|
21928
22045
|
placeActions: rows.map((r) => ({
|
|
21929
22046
|
locationName: r.locationName,
|
|
@@ -21940,7 +22057,7 @@ async function googleRoutes(app, opts) {
|
|
|
21940
22057
|
const project = resolveProject(app.db, request.params.name);
|
|
21941
22058
|
const conditions = [eq22(gbpLodgingSnapshots.projectId, project.id)];
|
|
21942
22059
|
if (request.query.locationName) conditions.push(eq22(gbpLodgingSnapshots.locationName, request.query.locationName));
|
|
21943
|
-
const rows = app.db.select().from(gbpLodgingSnapshots).where(
|
|
22060
|
+
const rows = app.db.select().from(gbpLodgingSnapshots).where(and16(...conditions)).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
|
|
21944
22061
|
const latestByLocation = /* @__PURE__ */ new Map();
|
|
21945
22062
|
for (const row of rows) {
|
|
21946
22063
|
if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
|
|
@@ -21957,7 +22074,7 @@ async function googleRoutes(app, opts) {
|
|
|
21957
22074
|
const project = resolveProject(app.db, request.params.name);
|
|
21958
22075
|
const conditions = [eq22(gbpAttributesSnapshots.projectId, project.id)];
|
|
21959
22076
|
if (request.query.locationName) conditions.push(eq22(gbpAttributesSnapshots.locationName, request.query.locationName));
|
|
21960
|
-
const rows = app.db.select().from(gbpAttributesSnapshots).where(
|
|
22077
|
+
const rows = app.db.select().from(gbpAttributesSnapshots).where(and16(...conditions)).orderBy(desc11(gbpAttributesSnapshots.syncedAt)).all();
|
|
21961
22078
|
const latestByLocation = /* @__PURE__ */ new Map();
|
|
21962
22079
|
for (const row of rows) {
|
|
21963
22080
|
if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
|
|
@@ -21977,7 +22094,7 @@ async function googleRoutes(app, opts) {
|
|
|
21977
22094
|
const project = resolveProject(app.db, request.params.name);
|
|
21978
22095
|
const conditions = [eq22(gbpPlaceDetails.projectId, project.id)];
|
|
21979
22096
|
if (request.query.locationName) conditions.push(eq22(gbpPlaceDetails.locationName, request.query.locationName));
|
|
21980
|
-
const rows = app.db.select().from(gbpPlaceDetails).where(
|
|
22097
|
+
const rows = app.db.select().from(gbpPlaceDetails).where(and16(...conditions)).orderBy(desc11(gbpPlaceDetails.syncedAt)).all();
|
|
21981
22098
|
const latestByLocation = /* @__PURE__ */ new Map();
|
|
21982
22099
|
for (const row of rows) {
|
|
21983
22100
|
if (!latestByLocation.has(row.locationName)) latestByLocation.set(row.locationName, row);
|
|
@@ -21996,7 +22113,7 @@ async function googleRoutes(app, opts) {
|
|
|
21996
22113
|
app.get("/projects/:name/gbp/summary", async (request) => {
|
|
21997
22114
|
const project = resolveProject(app.db, request.params.name);
|
|
21998
22115
|
const locationName = request.query.locationName ?? null;
|
|
21999
|
-
const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(
|
|
22116
|
+
const locationNames = locationName ? [locationName] : app.db.select({ n: gbpLocations.locationName }).from(gbpLocations).where(and16(eq22(gbpLocations.projectId, project.id), eq22(gbpLocations.selected, true))).all().map((r) => r.n);
|
|
22000
22117
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
22001
22118
|
if (locationNames.length === 0) {
|
|
22002
22119
|
return buildGbpSummary({
|
|
@@ -22010,10 +22127,10 @@ async function googleRoutes(app, opts) {
|
|
|
22010
22127
|
locationProfiles: []
|
|
22011
22128
|
});
|
|
22012
22129
|
}
|
|
22013
|
-
const metricRows = app.db.select().from(gbpDailyMetrics).where(
|
|
22014
|
-
const keywordRows = app.db.select().from(gbpKeywordImpressions).where(
|
|
22015
|
-
const placeActionRows = app.db.select().from(gbpPlaceActions).where(
|
|
22016
|
-
const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(
|
|
22130
|
+
const metricRows = app.db.select().from(gbpDailyMetrics).where(and16(eq22(gbpDailyMetrics.projectId, project.id), inArray10(gbpDailyMetrics.locationName, locationNames))).all();
|
|
22131
|
+
const keywordRows = app.db.select().from(gbpKeywordImpressions).where(and16(eq22(gbpKeywordImpressions.projectId, project.id), inArray10(gbpKeywordImpressions.locationName, locationNames))).all();
|
|
22132
|
+
const placeActionRows = app.db.select().from(gbpPlaceActions).where(and16(eq22(gbpPlaceActions.projectId, project.id), inArray10(gbpPlaceActions.locationName, locationNames))).all();
|
|
22133
|
+
const lodgingRows = app.db.select().from(gbpLodgingSnapshots).where(and16(eq22(gbpLodgingSnapshots.projectId, project.id), inArray10(gbpLodgingSnapshots.locationName, locationNames))).orderBy(desc11(gbpLodgingSnapshots.syncedAt)).all();
|
|
22017
22134
|
const latestLodgingByLocation = /* @__PURE__ */ new Map();
|
|
22018
22135
|
for (const row of lodgingRows) {
|
|
22019
22136
|
if (!latestLodgingByLocation.has(row.locationName)) {
|
|
@@ -22027,7 +22144,7 @@ async function googleRoutes(app, opts) {
|
|
|
22027
22144
|
regularHours: gbpLocations.regularHours,
|
|
22028
22145
|
primaryPhone: gbpLocations.primaryPhone,
|
|
22029
22146
|
openStatus: gbpLocations.openStatus
|
|
22030
|
-
}).from(gbpLocations).where(
|
|
22147
|
+
}).from(gbpLocations).where(and16(eq22(gbpLocations.projectId, project.id), inArray10(gbpLocations.locationName, locationNames))).all();
|
|
22031
22148
|
return buildGbpSummary({
|
|
22032
22149
|
locationName,
|
|
22033
22150
|
locationCount: locationNames.length,
|
|
@@ -22043,7 +22160,7 @@ async function googleRoutes(app, opts) {
|
|
|
22043
22160
|
|
|
22044
22161
|
// ../api-routes/src/ads.ts
|
|
22045
22162
|
import crypto19 from "crypto";
|
|
22046
|
-
import { eq as eq23, and as
|
|
22163
|
+
import { eq as eq23, and as and17, asc as asc3, gte as gte3, lte as lte2, inArray as inArray11 } from "drizzle-orm";
|
|
22047
22164
|
function statusDto(row) {
|
|
22048
22165
|
if (!row) return { connected: false };
|
|
22049
22166
|
return {
|
|
@@ -22159,7 +22276,7 @@ async function adsRoutes(app, opts) {
|
|
|
22159
22276
|
if (!row) {
|
|
22160
22277
|
throw validationError('No ads connection for this project. Run "canonry ads connect" first.');
|
|
22161
22278
|
}
|
|
22162
|
-
const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(
|
|
22279
|
+
const inFlight = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and17(
|
|
22163
22280
|
eq23(runs.projectId, project.id),
|
|
22164
22281
|
eq23(runs.kind, RunKinds["ads-sync"]),
|
|
22165
22282
|
inArray11(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
@@ -22244,7 +22361,7 @@ async function adsRoutes(app, opts) {
|
|
|
22244
22361
|
if (entityId) conditions.push(eq23(adsInsightsDaily.entityId, entityId));
|
|
22245
22362
|
if (from) conditions.push(gte3(adsInsightsDaily.date, from));
|
|
22246
22363
|
if (to) conditions.push(lte2(adsInsightsDaily.date, to));
|
|
22247
|
-
const rows = app.db.select().from(adsInsightsDaily).where(
|
|
22364
|
+
const rows = app.db.select().from(adsInsightsDaily).where(and17(...conditions)).orderBy(asc3(adsInsightsDaily.date)).all();
|
|
22248
22365
|
const dtoRows = rows.map((row) => ({
|
|
22249
22366
|
level: row.level,
|
|
22250
22367
|
entityId: row.entityId,
|
|
@@ -22266,7 +22383,7 @@ async function adsRoutes(app, opts) {
|
|
|
22266
22383
|
const campaignCount = app.db.select().from(adsCampaigns).where(eq23(adsCampaigns.projectId, project.id)).all().length;
|
|
22267
22384
|
const adGroupCount = app.db.select().from(adsAdGroups).where(eq23(adsAdGroups.projectId, project.id)).all().length;
|
|
22268
22385
|
const adCount = app.db.select().from(adsAds).where(eq23(adsAds.projectId, project.id)).all().length;
|
|
22269
|
-
const campaignInsights = app.db.select().from(adsInsightsDaily).where(
|
|
22386
|
+
const campaignInsights = app.db.select().from(adsInsightsDaily).where(and17(
|
|
22270
22387
|
eq23(adsInsightsDaily.projectId, project.id),
|
|
22271
22388
|
eq23(adsInsightsDaily.level, "campaign")
|
|
22272
22389
|
)).all();
|
|
@@ -22308,7 +22425,7 @@ async function adsRoutes(app, opts) {
|
|
|
22308
22425
|
|
|
22309
22426
|
// ../api-routes/src/bing.ts
|
|
22310
22427
|
import crypto20 from "crypto";
|
|
22311
|
-
import { eq as eq24, and as
|
|
22428
|
+
import { eq as eq24, and as and18, desc as desc12 } from "drizzle-orm";
|
|
22312
22429
|
|
|
22313
22430
|
// ../integration-bing/src/constants.ts
|
|
22314
22431
|
var BING_WMT_API_BASE = "https://ssl.bing.com/webmaster/api.svc/json";
|
|
@@ -22783,7 +22900,7 @@ async function bingRoutes(app, opts) {
|
|
|
22783
22900
|
requireConnectionStore();
|
|
22784
22901
|
const project = resolveProject(app.db, request.params.name);
|
|
22785
22902
|
const { url, limit } = request.query;
|
|
22786
|
-
const whereClause = url ?
|
|
22903
|
+
const whereClause = url ? and18(eq24(bingUrlInspections.projectId, project.id), eq24(bingUrlInspections.url, url)) : eq24(bingUrlInspections.projectId, project.id);
|
|
22787
22904
|
const filtered = app.db.select().from(bingUrlInspections).where(whereClause).orderBy(desc12(bingUrlInspections.inspectedAt)).limit(Math.max(1, Math.min(parseInt(limit ?? "100", 10) || 100, 1e3))).all();
|
|
22788
22905
|
return filtered.map((r) => ({
|
|
22789
22906
|
id: r.id,
|
|
@@ -23015,7 +23132,7 @@ async function bingRoutes(app, opts) {
|
|
|
23015
23132
|
import fs from "fs";
|
|
23016
23133
|
import path from "path";
|
|
23017
23134
|
import os from "os";
|
|
23018
|
-
import { eq as eq25, and as
|
|
23135
|
+
import { eq as eq25, and as and19 } from "drizzle-orm";
|
|
23019
23136
|
function getScreenshotDir() {
|
|
23020
23137
|
return path.join(os.homedir(), ".canonry", "screenshots");
|
|
23021
23138
|
}
|
|
@@ -23090,7 +23207,7 @@ async function cdpRoutes(app, opts) {
|
|
|
23090
23207
|
async (request, reply) => {
|
|
23091
23208
|
const project = resolveProject(app.db, request.params.name);
|
|
23092
23209
|
const { runId } = request.params;
|
|
23093
|
-
const run = app.db.select().from(runs).where(
|
|
23210
|
+
const run = app.db.select().from(runs).where(and19(eq25(runs.id, runId), eq25(runs.projectId, project.id))).get();
|
|
23094
23211
|
if (!run) {
|
|
23095
23212
|
const err = notFound("Run", runId);
|
|
23096
23213
|
return reply.code(err.statusCode).send(err.toJSON());
|
|
@@ -23187,7 +23304,7 @@ async function cdpRoutes(app, opts) {
|
|
|
23187
23304
|
|
|
23188
23305
|
// ../api-routes/src/ga.ts
|
|
23189
23306
|
import crypto21 from "crypto";
|
|
23190
|
-
import { eq as eq26, desc as desc13, and as
|
|
23307
|
+
import { eq as eq26, desc as desc13, and as and20, sql as sql10 } from "drizzle-orm";
|
|
23191
23308
|
function gaLog(level, action, ctx) {
|
|
23192
23309
|
const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
|
|
23193
23310
|
const stream = level === "error" ? process.stderr : process.stdout;
|
|
@@ -23482,7 +23599,7 @@ async function ga4Routes(app, opts) {
|
|
|
23482
23599
|
app.db.transaction((tx) => {
|
|
23483
23600
|
if (syncTraffic) {
|
|
23484
23601
|
tx.delete(gaTrafficSnapshots).where(
|
|
23485
|
-
|
|
23602
|
+
and20(
|
|
23486
23603
|
eq26(gaTrafficSnapshots.projectId, project.id),
|
|
23487
23604
|
sql10`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
|
|
23488
23605
|
sql10`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
|
|
@@ -23506,7 +23623,7 @@ async function ga4Routes(app, opts) {
|
|
|
23506
23623
|
}
|
|
23507
23624
|
if (syncAi) {
|
|
23508
23625
|
tx.delete(gaAiReferrals).where(
|
|
23509
|
-
|
|
23626
|
+
and20(
|
|
23510
23627
|
eq26(gaAiReferrals.projectId, project.id),
|
|
23511
23628
|
sql10`${gaAiReferrals.date} >= ${summary.periodStart}`,
|
|
23512
23629
|
sql10`${gaAiReferrals.date} <= ${summary.periodEnd}`
|
|
@@ -23532,7 +23649,7 @@ async function ga4Routes(app, opts) {
|
|
|
23532
23649
|
}
|
|
23533
23650
|
if (syncSocial) {
|
|
23534
23651
|
tx.delete(gaSocialReferrals).where(
|
|
23535
|
-
|
|
23652
|
+
and20(
|
|
23536
23653
|
eq26(gaSocialReferrals.projectId, project.id),
|
|
23537
23654
|
sql10`${gaSocialReferrals.date} >= ${summary.periodStart}`,
|
|
23538
23655
|
sql10`${gaSocialReferrals.date} <= ${summary.periodEnd}`
|
|
@@ -23636,7 +23753,7 @@ async function ga4Routes(app, opts) {
|
|
|
23636
23753
|
totalDirectSessions: gaTrafficWindowSummaries.totalDirectSessions,
|
|
23637
23754
|
totalUsers: gaTrafficWindowSummaries.totalUsers
|
|
23638
23755
|
}).from(gaTrafficWindowSummaries).where(
|
|
23639
|
-
|
|
23756
|
+
and20(
|
|
23640
23757
|
eq26(gaTrafficWindowSummaries.projectId, project.id),
|
|
23641
23758
|
eq26(gaTrafficWindowSummaries.windowKey, window)
|
|
23642
23759
|
)
|
|
@@ -23645,7 +23762,7 @@ async function ga4Routes(app, opts) {
|
|
|
23645
23762
|
totalSessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
|
|
23646
23763
|
totalOrganicSessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
|
|
23647
23764
|
totalUsers: sql10`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
|
|
23648
|
-
}).from(gaTrafficSnapshots).where(
|
|
23765
|
+
}).from(gaTrafficSnapshots).where(and20(...snapshotConditions)).get() : null;
|
|
23649
23766
|
const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
|
|
23650
23767
|
totalSessions: gaTrafficSummaries.totalSessions,
|
|
23651
23768
|
totalOrganicSessions: gaTrafficSummaries.totalOrganicSessions,
|
|
@@ -23653,7 +23770,7 @@ async function ga4Routes(app, opts) {
|
|
|
23653
23770
|
}).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).get();
|
|
23654
23771
|
const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
|
|
23655
23772
|
totalDirectSessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
|
|
23656
|
-
}).from(gaTrafficSnapshots).where(
|
|
23773
|
+
}).from(gaTrafficSnapshots).where(and20(...snapshotConditions)).get();
|
|
23657
23774
|
const summaryMeta = app.db.select({
|
|
23658
23775
|
periodStart: gaTrafficSummaries.periodStart,
|
|
23659
23776
|
periodEnd: gaTrafficSummaries.periodEnd
|
|
@@ -23664,14 +23781,14 @@ async function ga4Routes(app, opts) {
|
|
|
23664
23781
|
organicSessions: sql10`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
23665
23782
|
directSessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
|
|
23666
23783
|
users: sql10`SUM(${gaTrafficSnapshots.users})`
|
|
23667
|
-
}).from(gaTrafficSnapshots).where(
|
|
23784
|
+
}).from(gaTrafficSnapshots).where(and20(...snapshotConditions)).groupBy(sql10`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql10`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
|
|
23668
23785
|
const aiReferralRows = app.db.select({
|
|
23669
23786
|
source: gaAiReferrals.source,
|
|
23670
23787
|
medium: gaAiReferrals.medium,
|
|
23671
23788
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
23672
23789
|
sessions: sql10`SUM(${gaAiReferrals.sessions})`,
|
|
23673
23790
|
users: sql10`SUM(${gaAiReferrals.users})`
|
|
23674
|
-
}).from(gaAiReferrals).where(
|
|
23791
|
+
}).from(gaAiReferrals).where(and20(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
|
|
23675
23792
|
const aiReferralLandingPageRows = app.db.select({
|
|
23676
23793
|
source: gaAiReferrals.source,
|
|
23677
23794
|
medium: gaAiReferrals.medium,
|
|
@@ -23679,7 +23796,7 @@ async function ga4Routes(app, opts) {
|
|
|
23679
23796
|
landingPage: sql10`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
|
|
23680
23797
|
sessions: sql10`SUM(${gaAiReferrals.sessions})`,
|
|
23681
23798
|
users: sql10`SUM(${gaAiReferrals.users})`
|
|
23682
|
-
}).from(gaAiReferrals).where(
|
|
23799
|
+
}).from(gaAiReferrals).where(and20(...aiConditions)).groupBy(
|
|
23683
23800
|
gaAiReferrals.source,
|
|
23684
23801
|
gaAiReferrals.medium,
|
|
23685
23802
|
gaAiReferrals.sourceDimension,
|
|
@@ -23716,7 +23833,7 @@ async function ga4Routes(app, opts) {
|
|
|
23716
23833
|
channelGroup: gaAiReferrals.channelGroup,
|
|
23717
23834
|
sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
|
|
23718
23835
|
users: sql10`COALESCE(SUM(${gaAiReferrals.users}), 0)`
|
|
23719
|
-
}).from(gaAiReferrals).where(
|
|
23836
|
+
}).from(gaAiReferrals).where(and20(...aiConditions, eq26(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
|
|
23720
23837
|
const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
|
|
23721
23838
|
let aiBySessionUsers = 0;
|
|
23722
23839
|
for (const row of aiBySessionRows) {
|
|
@@ -23730,11 +23847,11 @@ async function ga4Routes(app, opts) {
|
|
|
23730
23847
|
channelGroup: gaSocialReferrals.channelGroup,
|
|
23731
23848
|
sessions: sql10`SUM(${gaSocialReferrals.sessions})`,
|
|
23732
23849
|
users: sql10`SUM(${gaSocialReferrals.users})`
|
|
23733
|
-
}).from(gaSocialReferrals).where(
|
|
23850
|
+
}).from(gaSocialReferrals).where(and20(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql10`SUM(${gaSocialReferrals.sessions}) DESC`).all();
|
|
23734
23851
|
const socialTotals = app.db.select({
|
|
23735
23852
|
sessions: sql10`SUM(${gaSocialReferrals.sessions})`,
|
|
23736
23853
|
users: sql10`SUM(${gaSocialReferrals.users})`
|
|
23737
|
-
}).from(gaSocialReferrals).where(
|
|
23854
|
+
}).from(gaSocialReferrals).where(and20(...socialConditions)).get();
|
|
23738
23855
|
const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq26(gaTrafficSummaries.projectId, project.id)).orderBy(desc13(gaTrafficSummaries.syncedAt)).limit(1).get();
|
|
23739
23856
|
const total = summaryRow?.totalSessions ?? 0;
|
|
23740
23857
|
const totalDirectSessions = directTotalRow?.totalDirectSessions ?? 0;
|
|
@@ -23825,7 +23942,7 @@ async function ga4Routes(app, opts) {
|
|
|
23825
23942
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
23826
23943
|
sessions: sql10`SUM(${gaAiReferrals.sessions})`,
|
|
23827
23944
|
users: sql10`SUM(${gaAiReferrals.users})`
|
|
23828
|
-
}).from(gaAiReferrals).where(
|
|
23945
|
+
}).from(gaAiReferrals).where(and20(...conditions)).groupBy(
|
|
23829
23946
|
gaAiReferrals.date,
|
|
23830
23947
|
gaAiReferrals.source,
|
|
23831
23948
|
gaAiReferrals.medium,
|
|
@@ -23847,7 +23964,7 @@ async function ga4Routes(app, opts) {
|
|
|
23847
23964
|
channelGroup: gaSocialReferrals.channelGroup,
|
|
23848
23965
|
sessions: gaSocialReferrals.sessions,
|
|
23849
23966
|
users: gaSocialReferrals.users
|
|
23850
|
-
}).from(gaSocialReferrals).where(
|
|
23967
|
+
}).from(gaSocialReferrals).where(and20(...conditions)).orderBy(gaSocialReferrals.date).all();
|
|
23851
23968
|
return rows;
|
|
23852
23969
|
});
|
|
23853
23970
|
app.get("/projects/:name/ga/social-referral-trend", async (request, _reply) => {
|
|
@@ -23860,7 +23977,7 @@ async function ga4Routes(app, opts) {
|
|
|
23860
23977
|
d.setDate(d.getDate() - n);
|
|
23861
23978
|
return fmt(d);
|
|
23862
23979
|
};
|
|
23863
|
-
const sumSocial = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(
|
|
23980
|
+
const sumSocial = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and20(
|
|
23864
23981
|
eq26(gaSocialReferrals.projectId, project.id),
|
|
23865
23982
|
sql10`${gaSocialReferrals.date} >= ${from}`,
|
|
23866
23983
|
sql10`${gaSocialReferrals.date} < ${to}`
|
|
@@ -23873,7 +23990,7 @@ async function ga4Routes(app, opts) {
|
|
|
23873
23990
|
const sourceCurrent = app.db.select({
|
|
23874
23991
|
source: gaSocialReferrals.source,
|
|
23875
23992
|
sessions: sql10`SUM(${gaSocialReferrals.sessions})`
|
|
23876
|
-
}).from(gaSocialReferrals).where(
|
|
23993
|
+
}).from(gaSocialReferrals).where(and20(
|
|
23877
23994
|
eq26(gaSocialReferrals.projectId, project.id),
|
|
23878
23995
|
sql10`${gaSocialReferrals.date} >= ${daysAgo(7)}`,
|
|
23879
23996
|
sql10`${gaSocialReferrals.date} < ${fmt(today)}`
|
|
@@ -23881,7 +23998,7 @@ async function ga4Routes(app, opts) {
|
|
|
23881
23998
|
const sourcePrev = app.db.select({
|
|
23882
23999
|
source: gaSocialReferrals.source,
|
|
23883
24000
|
sessions: sql10`SUM(${gaSocialReferrals.sessions})`
|
|
23884
|
-
}).from(gaSocialReferrals).where(
|
|
24001
|
+
}).from(gaSocialReferrals).where(and20(
|
|
23885
24002
|
eq26(gaSocialReferrals.projectId, project.id),
|
|
23886
24003
|
sql10`${gaSocialReferrals.date} >= ${daysAgo(14)}`,
|
|
23887
24004
|
sql10`${gaSocialReferrals.date} < ${daysAgo(7)}`
|
|
@@ -23923,16 +24040,16 @@ async function ga4Routes(app, opts) {
|
|
|
23923
24040
|
return fmt(d);
|
|
23924
24041
|
};
|
|
23925
24042
|
const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
|
|
23926
|
-
const sumTotal = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(
|
|
23927
|
-
const sumOrganic = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(
|
|
23928
|
-
const sumDirect = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(
|
|
23929
|
-
const sumAi = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(
|
|
24043
|
+
const sumTotal = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and20(eq26(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
24044
|
+
const sumOrganic = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and20(eq26(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
24045
|
+
const sumDirect = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and20(eq26(gaTrafficSnapshots.projectId, project.id), sql10`${gaTrafficSnapshots.date} >= ${from}`, sql10`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
24046
|
+
const sumAi = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and20(
|
|
23930
24047
|
eq26(gaAiReferrals.projectId, project.id),
|
|
23931
24048
|
sql10`${gaAiReferrals.date} >= ${from}`,
|
|
23932
24049
|
sql10`${gaAiReferrals.date} < ${to}`,
|
|
23933
24050
|
eq26(gaAiReferrals.sourceDimension, "session")
|
|
23934
24051
|
)).get();
|
|
23935
|
-
const sumSocial = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(
|
|
24052
|
+
const sumSocial = (from, to) => app.db.select({ sessions: sql10`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and20(eq26(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${from}`, sql10`${gaSocialReferrals.date} < ${to}`)).get();
|
|
23936
24053
|
const todayStr = fmt(today);
|
|
23937
24054
|
const buildTrend = (sum) => {
|
|
23938
24055
|
const c7 = sum(daysAgo(7), todayStr)?.sessions ?? 0;
|
|
@@ -23941,13 +24058,13 @@ async function ga4Routes(app, opts) {
|
|
|
23941
24058
|
const p30 = sum(daysAgo(60), daysAgo(30))?.sessions ?? 0;
|
|
23942
24059
|
return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
|
|
23943
24060
|
};
|
|
23944
|
-
const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(
|
|
24061
|
+
const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and20(
|
|
23945
24062
|
eq26(gaAiReferrals.projectId, project.id),
|
|
23946
24063
|
sql10`${gaAiReferrals.date} >= ${daysAgo(7)}`,
|
|
23947
24064
|
sql10`${gaAiReferrals.date} < ${todayStr}`,
|
|
23948
24065
|
eq26(gaAiReferrals.sourceDimension, "session")
|
|
23949
24066
|
)).groupBy(gaAiReferrals.source).all();
|
|
23950
|
-
const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(
|
|
24067
|
+
const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql10`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and20(
|
|
23951
24068
|
eq26(gaAiReferrals.projectId, project.id),
|
|
23952
24069
|
sql10`${gaAiReferrals.date} >= ${daysAgo(14)}`,
|
|
23953
24070
|
sql10`${gaAiReferrals.date} < ${daysAgo(7)}`,
|
|
@@ -23967,8 +24084,8 @@ async function ga4Routes(app, opts) {
|
|
|
23967
24084
|
}
|
|
23968
24085
|
return mover;
|
|
23969
24086
|
};
|
|
23970
|
-
const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(
|
|
23971
|
-
const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(
|
|
24087
|
+
const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and20(eq26(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${daysAgo(7)}`, sql10`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
|
|
24088
|
+
const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql10`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and20(eq26(gaSocialReferrals.projectId, project.id), sql10`${gaSocialReferrals.date} >= ${daysAgo(14)}`, sql10`${gaSocialReferrals.date} < ${daysAgo(7)}`)).groupBy(gaSocialReferrals.source).all();
|
|
23972
24089
|
return {
|
|
23973
24090
|
total: buildTrend(sumTotal),
|
|
23974
24091
|
organic: buildTrend(sumOrganic),
|
|
@@ -23990,7 +24107,7 @@ async function ga4Routes(app, opts) {
|
|
|
23990
24107
|
sessions: sql10`SUM(${gaTrafficSnapshots.sessions})`,
|
|
23991
24108
|
organicSessions: sql10`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
23992
24109
|
users: sql10`SUM(${gaTrafficSnapshots.users})`
|
|
23993
|
-
}).from(gaTrafficSnapshots).where(
|
|
24110
|
+
}).from(gaTrafficSnapshots).where(and20(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
|
|
23994
24111
|
return rows.map((r) => ({
|
|
23995
24112
|
date: r.date,
|
|
23996
24113
|
sessions: r.sessions ?? 0,
|
|
@@ -25654,7 +25771,7 @@ async function wordpressRoutes(app, opts) {
|
|
|
25654
25771
|
|
|
25655
25772
|
// ../api-routes/src/backlinks.ts
|
|
25656
25773
|
import crypto23 from "crypto";
|
|
25657
|
-
import { and as
|
|
25774
|
+
import { and as and22, asc as asc4, desc as desc14, eq as eq27, sql as sql11 } from "drizzle-orm";
|
|
25658
25775
|
|
|
25659
25776
|
// ../integration-commoncrawl/src/constants.ts
|
|
25660
25777
|
import os2 from "os";
|
|
@@ -26057,7 +26174,7 @@ function pruneCachedRelease(release, opts = {}) {
|
|
|
26057
26174
|
}
|
|
26058
26175
|
|
|
26059
26176
|
// ../api-routes/src/backlinks-filter.ts
|
|
26060
|
-
import { and as
|
|
26177
|
+
import { and as and21, ne as ne3, notLike } from "drizzle-orm";
|
|
26061
26178
|
var BACKLINK_FILTER_PATTERNS = [
|
|
26062
26179
|
"*.google.com",
|
|
26063
26180
|
"*.googleusercontent.com",
|
|
@@ -26080,7 +26197,7 @@ function backlinkCrawlerExclusionClause() {
|
|
|
26080
26197
|
conditions.push(ne3(backlinkDomains.linkingDomain, pattern));
|
|
26081
26198
|
}
|
|
26082
26199
|
}
|
|
26083
|
-
const combined =
|
|
26200
|
+
const combined = and21(...conditions);
|
|
26084
26201
|
if (!combined) throw new Error("BACKLINK_FILTER_PATTERNS is unexpectedly empty");
|
|
26085
26202
|
return combined;
|
|
26086
26203
|
}
|
|
@@ -26155,7 +26272,7 @@ function latestSummaryForProject(db, projectId, source, release) {
|
|
|
26155
26272
|
eq27(backlinkSummaries.source, source)
|
|
26156
26273
|
];
|
|
26157
26274
|
if (release) conditions.push(eq27(backlinkSummaries.release, release));
|
|
26158
|
-
return db.select().from(backlinkSummaries).where(
|
|
26275
|
+
return db.select().from(backlinkSummaries).where(and22(...conditions)).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
|
|
26159
26276
|
}
|
|
26160
26277
|
function parseExcludeCrawlers(value) {
|
|
26161
26278
|
if (!value) return false;
|
|
@@ -26163,12 +26280,12 @@ function parseExcludeCrawlers(value) {
|
|
|
26163
26280
|
return lower === "1" || lower === "true" || lower === "yes";
|
|
26164
26281
|
}
|
|
26165
26282
|
function computeFilteredSummary(db, base) {
|
|
26166
|
-
const baseDomainCondition =
|
|
26283
|
+
const baseDomainCondition = and22(
|
|
26167
26284
|
eq27(backlinkDomains.projectId, base.projectId),
|
|
26168
26285
|
eq27(backlinkDomains.source, base.source),
|
|
26169
26286
|
eq27(backlinkDomains.release, base.release)
|
|
26170
26287
|
);
|
|
26171
|
-
const filteredCondition =
|
|
26288
|
+
const filteredCondition = and22(baseDomainCondition, backlinkCrawlerExclusionClause());
|
|
26172
26289
|
const unfilteredAgg = db.select({
|
|
26173
26290
|
count: sql11`count(*)`,
|
|
26174
26291
|
total: sql11`coalesce(sum(${backlinkDomains.numHosts}), 0)`
|
|
@@ -26198,10 +26315,10 @@ function computeFilteredSummary(db, base) {
|
|
|
26198
26315
|
};
|
|
26199
26316
|
}
|
|
26200
26317
|
function buildSourceAvailability(db, projectId, source, connected, excludeCrawlers) {
|
|
26201
|
-
const summary = db.select().from(backlinkSummaries).where(
|
|
26318
|
+
const summary = db.select().from(backlinkSummaries).where(and22(eq27(backlinkSummaries.projectId, projectId), eq27(backlinkSummaries.source, source))).orderBy(desc14(backlinkSummaries.queriedAt)).limit(1).get();
|
|
26202
26319
|
let totalLinkingDomains = summary?.totalLinkingDomains ?? 0;
|
|
26203
26320
|
if (summary && excludeCrawlers) {
|
|
26204
|
-
const filtered = db.select({ count: sql11`count(*)` }).from(backlinkDomains).where(
|
|
26321
|
+
const filtered = db.select({ count: sql11`count(*)` }).from(backlinkDomains).where(and22(
|
|
26205
26322
|
eq27(backlinkDomains.projectId, projectId),
|
|
26206
26323
|
eq27(backlinkDomains.source, source),
|
|
26207
26324
|
eq27(backlinkDomains.release, summary.release),
|
|
@@ -26384,12 +26501,12 @@ async function backlinksRoutes(app, opts) {
|
|
|
26384
26501
|
const limit = Math.min(Math.max(parseInt(request.query.limit ?? "50", 10) || 50, 1), 500);
|
|
26385
26502
|
const offset = Math.max(parseInt(request.query.offset ?? "0", 10) || 0, 0);
|
|
26386
26503
|
const excludeCrawlers = parseExcludeCrawlers(request.query.excludeCrawlers);
|
|
26387
|
-
const baseDomainCondition =
|
|
26504
|
+
const baseDomainCondition = and22(
|
|
26388
26505
|
eq27(backlinkDomains.projectId, project.id),
|
|
26389
26506
|
eq27(backlinkDomains.source, source),
|
|
26390
26507
|
eq27(backlinkDomains.release, targetRelease)
|
|
26391
26508
|
);
|
|
26392
|
-
const domainCondition = excludeCrawlers ?
|
|
26509
|
+
const domainCondition = excludeCrawlers ? and22(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
|
|
26393
26510
|
const totalRow = app.db.select({ count: sql11`count(*)` }).from(backlinkDomains).where(domainCondition).get();
|
|
26394
26511
|
const rows = app.db.select({
|
|
26395
26512
|
linkingDomain: backlinkDomains.linkingDomain,
|
|
@@ -26413,7 +26530,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
26413
26530
|
async (request, reply) => {
|
|
26414
26531
|
const project = resolveProject(app.db, request.params.name);
|
|
26415
26532
|
const source = parseSourceParam(request.query.source);
|
|
26416
|
-
const rows = app.db.select().from(backlinkSummaries).where(
|
|
26533
|
+
const rows = app.db.select().from(backlinkSummaries).where(and22(eq27(backlinkSummaries.projectId, project.id), eq27(backlinkSummaries.source, source))).orderBy(asc4(backlinkSummaries.queriedAt)).all();
|
|
26417
26534
|
const response = rows.map((r) => ({
|
|
26418
26535
|
release: r.release,
|
|
26419
26536
|
totalLinkingDomains: r.totalLinkingDomains,
|
|
@@ -26469,7 +26586,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
26469
26586
|
// ../api-routes/src/traffic.ts
|
|
26470
26587
|
import crypto25 from "crypto";
|
|
26471
26588
|
import { Agent as UndiciAgent } from "undici";
|
|
26472
|
-
import { and as
|
|
26589
|
+
import { and as and23, desc as desc15, eq as eq28, gte as gte4, lte as lte3, sql as sql12 } from "drizzle-orm";
|
|
26473
26590
|
|
|
26474
26591
|
// ../integration-cloud-run/src/auth.ts
|
|
26475
26592
|
import crypto24 from "crypto";
|
|
@@ -30328,28 +30445,28 @@ async function runBackfillTask(options) {
|
|
|
30328
30445
|
try {
|
|
30329
30446
|
app.db.transaction((tx) => {
|
|
30330
30447
|
tx.delete(crawlerEventsHourly).where(
|
|
30331
|
-
|
|
30448
|
+
and23(
|
|
30332
30449
|
eq28(crawlerEventsHourly.sourceId, sourceRow.id),
|
|
30333
30450
|
gte4(crawlerEventsHourly.tsHour, windowStartIso),
|
|
30334
30451
|
lte3(crawlerEventsHourly.tsHour, windowEndIso)
|
|
30335
30452
|
)
|
|
30336
30453
|
).run();
|
|
30337
30454
|
tx.delete(aiUserFetchEventsHourly).where(
|
|
30338
|
-
|
|
30455
|
+
and23(
|
|
30339
30456
|
eq28(aiUserFetchEventsHourly.sourceId, sourceRow.id),
|
|
30340
30457
|
gte4(aiUserFetchEventsHourly.tsHour, windowStartIso),
|
|
30341
30458
|
lte3(aiUserFetchEventsHourly.tsHour, windowEndIso)
|
|
30342
30459
|
)
|
|
30343
30460
|
).run();
|
|
30344
30461
|
tx.delete(aiReferralEventsHourly).where(
|
|
30345
|
-
|
|
30462
|
+
and23(
|
|
30346
30463
|
eq28(aiReferralEventsHourly.sourceId, sourceRow.id),
|
|
30347
30464
|
gte4(aiReferralEventsHourly.tsHour, windowStartIso),
|
|
30348
30465
|
lte3(aiReferralEventsHourly.tsHour, windowEndIso)
|
|
30349
30466
|
)
|
|
30350
30467
|
).run();
|
|
30351
30468
|
tx.delete(rawEventSamples).where(
|
|
30352
|
-
|
|
30469
|
+
and23(
|
|
30353
30470
|
eq28(rawEventSamples.sourceId, sourceRow.id),
|
|
30354
30471
|
gte4(rawEventSamples.ts, windowStartIso),
|
|
30355
30472
|
lte3(rawEventSamples.ts, windowEndIso)
|
|
@@ -30732,7 +30849,7 @@ async function trafficRoutes(app, opts) {
|
|
|
30732
30849
|
row = tx.select().from(trafficSources).where(eq28(trafficSources.id, newId)).get();
|
|
30733
30850
|
}
|
|
30734
30851
|
const existingSchedule = tx.select().from(schedules).where(
|
|
30735
|
-
|
|
30852
|
+
and23(
|
|
30736
30853
|
eq28(schedules.projectId, project.id),
|
|
30737
30854
|
eq28(schedules.kind, SchedulableRunKinds["traffic-sync"])
|
|
30738
30855
|
)
|
|
@@ -31454,7 +31571,7 @@ async function trafficRoutes(app, opts) {
|
|
|
31454
31571
|
pathNormalized: crawlerEventsHourly.pathNormalized,
|
|
31455
31572
|
hits: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
31456
31573
|
}).from(crawlerEventsHourly).where(
|
|
31457
|
-
|
|
31574
|
+
and23(
|
|
31458
31575
|
eq28(crawlerEventsHourly.sourceId, row.id),
|
|
31459
31576
|
gte4(crawlerEventsHourly.tsHour, since)
|
|
31460
31577
|
)
|
|
@@ -31464,25 +31581,25 @@ async function trafficRoutes(app, opts) {
|
|
|
31464
31581
|
);
|
|
31465
31582
|
const crawlerTotal = crawlerSegments.content + crawlerSegments.sitemap + crawlerSegments.robots + crawlerSegments.asset + crawlerSegments.other;
|
|
31466
31583
|
const aiUserFetchTotals = app.db.select({ total: sql12`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(
|
|
31467
|
-
|
|
31584
|
+
and23(
|
|
31468
31585
|
eq28(aiUserFetchEventsHourly.sourceId, row.id),
|
|
31469
31586
|
gte4(aiUserFetchEventsHourly.tsHour, since)
|
|
31470
31587
|
)
|
|
31471
31588
|
).get();
|
|
31472
31589
|
const aiTotals = app.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
31473
|
-
|
|
31590
|
+
and23(
|
|
31474
31591
|
eq28(aiReferralEventsHourly.sourceId, row.id),
|
|
31475
31592
|
gte4(aiReferralEventsHourly.tsHour, since)
|
|
31476
31593
|
)
|
|
31477
31594
|
).get();
|
|
31478
31595
|
const sampleTotals = app.db.select({ total: sql12`COUNT(*)` }).from(rawEventSamples).where(
|
|
31479
|
-
|
|
31596
|
+
and23(
|
|
31480
31597
|
eq28(rawEventSamples.sourceId, row.id),
|
|
31481
31598
|
gte4(rawEventSamples.ts, since)
|
|
31482
31599
|
)
|
|
31483
31600
|
).get();
|
|
31484
31601
|
const latestRun = app.db.select().from(runs).where(
|
|
31485
|
-
|
|
31602
|
+
and23(
|
|
31486
31603
|
eq28(runs.projectId, projectId),
|
|
31487
31604
|
eq28(runs.kind, RunKinds["traffic-sync"]),
|
|
31488
31605
|
eq28(runs.sourceId, row.id)
|
|
@@ -31516,7 +31633,7 @@ async function trafficRoutes(app, opts) {
|
|
|
31516
31633
|
"`advanceToNow` must be `true`. There is no implicit reset."
|
|
31517
31634
|
);
|
|
31518
31635
|
}
|
|
31519
|
-
const sourceRow = app.db.select().from(trafficSources).where(
|
|
31636
|
+
const sourceRow = app.db.select().from(trafficSources).where(and23(eq28(trafficSources.projectId, project.id), eq28(trafficSources.id, request.params.id))).get();
|
|
31520
31637
|
if (!sourceRow) {
|
|
31521
31638
|
throw notFound("traffic source", request.params.id);
|
|
31522
31639
|
}
|
|
@@ -31621,7 +31738,7 @@ async function trafficRoutes(app, opts) {
|
|
|
31621
31738
|
lte3(crawlerEventsHourly.tsHour, untilIso)
|
|
31622
31739
|
];
|
|
31623
31740
|
if (sourceIdParam) crawlerFilters.push(eq28(crawlerEventsHourly.sourceId, sourceIdParam));
|
|
31624
|
-
const crawlerWhere =
|
|
31741
|
+
const crawlerWhere = and23(...crawlerFilters);
|
|
31625
31742
|
const pathTotals = app.db.select({
|
|
31626
31743
|
pathNormalized: crawlerEventsHourly.pathNormalized,
|
|
31627
31744
|
hits: sql12`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
@@ -31653,7 +31770,7 @@ async function trafficRoutes(app, opts) {
|
|
|
31653
31770
|
lte3(aiUserFetchEventsHourly.tsHour, untilIso)
|
|
31654
31771
|
];
|
|
31655
31772
|
if (sourceIdParam) userFetchFilters.push(eq28(aiUserFetchEventsHourly.sourceId, sourceIdParam));
|
|
31656
|
-
const userFetchWhere =
|
|
31773
|
+
const userFetchWhere = and23(...userFetchFilters);
|
|
31657
31774
|
const total = app.db.select({ total: sql12`COALESCE(SUM(${aiUserFetchEventsHourly.hits}), 0)` }).from(aiUserFetchEventsHourly).where(userFetchWhere).get();
|
|
31658
31775
|
aiUserFetchTotal = Number(total?.total ?? 0);
|
|
31659
31776
|
const rows = app.db.select().from(aiUserFetchEventsHourly).where(userFetchWhere).orderBy(desc15(aiUserFetchEventsHourly.tsHour)).limit(limit).all();
|
|
@@ -31678,7 +31795,7 @@ async function trafficRoutes(app, opts) {
|
|
|
31678
31795
|
lte3(aiReferralEventsHourly.tsHour, untilIso)
|
|
31679
31796
|
];
|
|
31680
31797
|
if (sourceIdParam) aiFilters.push(eq28(aiReferralEventsHourly.sourceId, sourceIdParam));
|
|
31681
|
-
const aiWhere =
|
|
31798
|
+
const aiWhere = and23(...aiFilters);
|
|
31682
31799
|
const total = app.db.select({ total: sql12`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
|
|
31683
31800
|
aiReferralTotal = Number(total?.total ?? 0);
|
|
31684
31801
|
const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc15(aiReferralEventsHourly.tsHour)).limit(limit).all();
|
|
@@ -31885,7 +32002,7 @@ function readInstalledManifest(skillDir) {
|
|
|
31885
32002
|
var AGENT_CHECKS = [skillsInstalledCheck, skillsCurrentCheck];
|
|
31886
32003
|
|
|
31887
32004
|
// ../api-routes/src/doctor/checks/backlinks.ts
|
|
31888
|
-
import { and as
|
|
32005
|
+
import { and as and24, eq as eq29 } from "drizzle-orm";
|
|
31889
32006
|
function skippedNoProject() {
|
|
31890
32007
|
return {
|
|
31891
32008
|
status: CheckStatuses.skipped,
|
|
@@ -31917,7 +32034,7 @@ var BACKLINKS_CHECKS = [
|
|
|
31917
32034
|
details: { commoncrawl: ccConnected, bingWebmaster: bingConnected }
|
|
31918
32035
|
};
|
|
31919
32036
|
}
|
|
31920
|
-
const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(
|
|
32037
|
+
const ccHasData = ccConnected ? !!ctx.db.select({ id: backlinkSummaries.id }).from(backlinkSummaries).where(and24(
|
|
31921
32038
|
eq29(backlinkSummaries.projectId, ctx.project.id),
|
|
31922
32039
|
eq29(backlinkSummaries.source, BacklinkSources.commoncrawl)
|
|
31923
32040
|
)).limit(1).get() : false;
|
|
@@ -32442,7 +32559,7 @@ var ga4ConnectionCheck = {
|
|
|
32442
32559
|
var GA_AUTH_CHECKS = [ga4ConnectionCheck];
|
|
32443
32560
|
|
|
32444
32561
|
// ../api-routes/src/doctor/checks/gbp-auth.ts
|
|
32445
|
-
import { and as
|
|
32562
|
+
import { and as and25, eq as eq32 } from "drizzle-orm";
|
|
32446
32563
|
var RECENT_SYNC_WARN_DAYS2 = 7;
|
|
32447
32564
|
var RECENT_SYNC_FAIL_DAYS2 = 30;
|
|
32448
32565
|
function skippedNoProject3() {
|
|
@@ -32675,7 +32792,7 @@ var recentSyncCheck = {
|
|
|
32675
32792
|
title: "GBP recent sync",
|
|
32676
32793
|
run: (ctx) => {
|
|
32677
32794
|
if (!ctx.project) return skippedNoProject3();
|
|
32678
|
-
const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(
|
|
32795
|
+
const selected = ctx.db.select({ locationName: gbpLocations.locationName, syncedAt: gbpLocations.syncedAt }).from(gbpLocations).where(and25(eq32(gbpLocations.projectId, ctx.project.id), eq32(gbpLocations.selected, true))).all();
|
|
32679
32796
|
if (selected.length === 0) {
|
|
32680
32797
|
return {
|
|
32681
32798
|
status: CheckStatuses.skipped,
|
|
@@ -33277,7 +33394,7 @@ var RUNTIME_STATE_CHECKS = [
|
|
|
33277
33394
|
];
|
|
33278
33395
|
|
|
33279
33396
|
// ../api-routes/src/doctor/checks/traffic-source.ts
|
|
33280
|
-
import { and as
|
|
33397
|
+
import { and as and26, eq as eq34, gte as gte5, ne as ne4, sql as sql13 } from "drizzle-orm";
|
|
33281
33398
|
var RECENT_DATA_WARN_DAYS = 7;
|
|
33282
33399
|
var RECENT_DATA_FAIL_DAYS = 30;
|
|
33283
33400
|
function skippedNoProject5() {
|
|
@@ -33291,7 +33408,7 @@ function skippedNoProject5() {
|
|
|
33291
33408
|
function loadProbes(ctx) {
|
|
33292
33409
|
if (!ctx.project) return [];
|
|
33293
33410
|
const rows = ctx.db.select().from(trafficSources).where(
|
|
33294
|
-
|
|
33411
|
+
and26(
|
|
33295
33412
|
eq34(trafficSources.projectId, ctx.project.id),
|
|
33296
33413
|
ne4(trafficSources.status, TrafficSourceStatuses.archived)
|
|
33297
33414
|
)
|
|
@@ -33372,7 +33489,7 @@ var recentDataCheck = {
|
|
|
33372
33489
|
const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
|
|
33373
33490
|
const recentCrawlers = Number(
|
|
33374
33491
|
ctx.db.select({ total: sql13`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
33375
|
-
|
|
33492
|
+
and26(
|
|
33376
33493
|
eq34(crawlerEventsHourly.projectId, ctx.project.id),
|
|
33377
33494
|
gte5(crawlerEventsHourly.tsHour, warnCutoff)
|
|
33378
33495
|
)
|
|
@@ -33380,7 +33497,7 @@ var recentDataCheck = {
|
|
|
33380
33497
|
);
|
|
33381
33498
|
const recentReferrals = Number(
|
|
33382
33499
|
ctx.db.select({ total: sql13`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
33383
|
-
|
|
33500
|
+
and26(
|
|
33384
33501
|
eq34(aiReferralEventsHourly.projectId, ctx.project.id),
|
|
33385
33502
|
gte5(aiReferralEventsHourly.tsHour, warnCutoff)
|
|
33386
33503
|
)
|
|
@@ -33396,7 +33513,7 @@ var recentDataCheck = {
|
|
|
33396
33513
|
}
|
|
33397
33514
|
const olderCrawlers = Number(
|
|
33398
33515
|
ctx.db.select({ total: sql13`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
33399
|
-
|
|
33516
|
+
and26(
|
|
33400
33517
|
eq34(crawlerEventsHourly.projectId, ctx.project.id),
|
|
33401
33518
|
gte5(crawlerEventsHourly.tsHour, failCutoff)
|
|
33402
33519
|
)
|
|
@@ -33404,7 +33521,7 @@ var recentDataCheck = {
|
|
|
33404
33521
|
);
|
|
33405
33522
|
const olderReferrals = Number(
|
|
33406
33523
|
ctx.db.select({ total: sql13`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
33407
|
-
|
|
33524
|
+
and26(
|
|
33408
33525
|
eq34(aiReferralEventsHourly.projectId, ctx.project.id),
|
|
33409
33526
|
gte5(aiReferralEventsHourly.tsHour, failCutoff)
|
|
33410
33527
|
)
|
|
@@ -33821,7 +33938,7 @@ async function doctorRoutes(app, opts) {
|
|
|
33821
33938
|
|
|
33822
33939
|
// ../api-routes/src/discovery/routes.ts
|
|
33823
33940
|
import crypto27 from "crypto";
|
|
33824
|
-
import { and as
|
|
33941
|
+
import { and as and27, desc as desc16, eq as eq35, gte as gte6, inArray as inArray12, isNull, or as or5 } from "drizzle-orm";
|
|
33825
33942
|
var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
|
|
33826
33943
|
async function discoveryRoutes(app, opts) {
|
|
33827
33944
|
app.post("/projects/:name/discover/run", async (request, reply) => {
|
|
@@ -33855,7 +33972,7 @@ async function discoveryRoutes(app, opts) {
|
|
|
33855
33972
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
33856
33973
|
const ageFloorIso = new Date(Date.now() - MAX_INFLIGHT_DISCOVERY_AGE_MS).toISOString();
|
|
33857
33974
|
const decision = app.db.transaction((tx) => {
|
|
33858
|
-
const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(
|
|
33975
|
+
const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and27(
|
|
33859
33976
|
eq35(discoverySessions.projectId, project.id),
|
|
33860
33977
|
eq35(discoverySessions.icpDescription, icpDescription),
|
|
33861
33978
|
// Buyer is part of session identity: it changes the seed prompt's
|
|
@@ -34501,7 +34618,7 @@ function dedupeStrings(input) {
|
|
|
34501
34618
|
|
|
34502
34619
|
// ../api-routes/src/technical-aeo.ts
|
|
34503
34620
|
import crypto29 from "crypto";
|
|
34504
|
-
import { and as
|
|
34621
|
+
import { and as and28, asc as asc5, count, desc as desc17, eq as eq37, inArray as inArray13 } from "drizzle-orm";
|
|
34505
34622
|
var SURFACEABLE_STATUSES = [RunStatuses.completed, RunStatuses.partial];
|
|
34506
34623
|
function emptyScore(projectName) {
|
|
34507
34624
|
return {
|
|
@@ -34533,7 +34650,7 @@ function parsePositiveInt(value, fallback, max) {
|
|
|
34533
34650
|
async function technicalAeoRoutes(app, opts) {
|
|
34534
34651
|
app.get("/projects/:name/technical-aeo", async (request) => {
|
|
34535
34652
|
const project = resolveProject(app.db, request.params.name);
|
|
34536
|
-
const rows = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(
|
|
34653
|
+
const rows = app.db.select({ snap: siteAuditSnapshots, runStatus: runs.status }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(and28(
|
|
34537
34654
|
eq37(siteAuditSnapshots.projectId, project.id),
|
|
34538
34655
|
eq37(runs.kind, RunKinds["site-audit"]),
|
|
34539
34656
|
inArray13(runs.status, SURFACEABLE_STATUSES),
|
|
@@ -34568,7 +34685,7 @@ async function technicalAeoRoutes(app, opts) {
|
|
|
34568
34685
|
});
|
|
34569
34686
|
app.get("/projects/:name/technical-aeo/pages", async (request) => {
|
|
34570
34687
|
const project = resolveProject(app.db, request.params.name);
|
|
34571
|
-
const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(
|
|
34688
|
+
const latest = app.db.select({ runId: siteAuditSnapshots.runId, auditedAt: siteAuditSnapshots.auditedAt }).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(and28(
|
|
34572
34689
|
eq37(siteAuditSnapshots.projectId, project.id),
|
|
34573
34690
|
eq37(runs.kind, RunKinds["site-audit"]),
|
|
34574
34691
|
inArray13(runs.status, SURFACEABLE_STATUSES),
|
|
@@ -34580,7 +34697,7 @@ async function technicalAeoRoutes(app, opts) {
|
|
|
34580
34697
|
const statusFilter = request.query.status === "success" || request.query.status === "error" ? request.query.status : null;
|
|
34581
34698
|
const conds = [eq37(siteAuditPages.runId, latest.runId)];
|
|
34582
34699
|
if (statusFilter) conds.push(eq37(siteAuditPages.status, statusFilter));
|
|
34583
|
-
const where =
|
|
34700
|
+
const where = and28(...conds);
|
|
34584
34701
|
const totalRow = app.db.select({ value: count() }).from(siteAuditPages).where(where).get();
|
|
34585
34702
|
const total = totalRow?.value ?? 0;
|
|
34586
34703
|
const limit = parsePositiveInt(request.query.limit, 100, 500);
|
|
@@ -34604,7 +34721,7 @@ async function technicalAeoRoutes(app, opts) {
|
|
|
34604
34721
|
auditedAt: siteAuditSnapshots.auditedAt,
|
|
34605
34722
|
aggregateScore: siteAuditSnapshots.aggregateScore,
|
|
34606
34723
|
pagesAudited: siteAuditSnapshots.pagesAudited
|
|
34607
|
-
}).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(
|
|
34724
|
+
}).from(siteAuditSnapshots).innerJoin(runs, eq37(siteAuditSnapshots.runId, runs.id)).where(and28(
|
|
34608
34725
|
eq37(siteAuditSnapshots.projectId, project.id),
|
|
34609
34726
|
eq37(runs.kind, RunKinds["site-audit"]),
|
|
34610
34727
|
inArray13(runs.status, SURFACEABLE_STATUSES),
|
|
@@ -34618,7 +34735,7 @@ async function technicalAeoRoutes(app, opts) {
|
|
|
34618
34735
|
if (!parsed.success) {
|
|
34619
34736
|
throw validationError(parsed.error.issues[0]?.message ?? "Invalid site-audit request");
|
|
34620
34737
|
}
|
|
34621
|
-
const existing = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(
|
|
34738
|
+
const existing = app.db.select({ id: runs.id, status: runs.status }).from(runs).where(and28(
|
|
34622
34739
|
eq37(runs.projectId, project.id),
|
|
34623
34740
|
eq37(runs.kind, RunKinds["site-audit"]),
|
|
34624
34741
|
inArray13(runs.status, [RunStatuses.queued, RunStatuses.running])
|
|
@@ -34687,7 +34804,8 @@ async function apiRoutes(app, opts) {
|
|
|
34687
34804
|
if (!opts.skipAuth) {
|
|
34688
34805
|
await authPlugin(api, {
|
|
34689
34806
|
sessionCookieName: opts.sessionCookieName,
|
|
34690
|
-
resolveSessionApiKeyId: opts.resolveSessionApiKeyId
|
|
34807
|
+
resolveSessionApiKeyId: opts.resolveSessionApiKeyId,
|
|
34808
|
+
embedProjectTabs: opts.embedProjectTabs
|
|
34691
34809
|
});
|
|
34692
34810
|
}
|
|
34693
34811
|
await api.register(openApiRoutes, { ...opts.openApiInfo, routePrefix: opts.routePrefix });
|
|
@@ -35211,7 +35329,7 @@ var IntelligenceService = class {
|
|
|
35211
35329
|
*/
|
|
35212
35330
|
analyzeAndPersist(runId, projectId) {
|
|
35213
35331
|
const recentRuns = this.db.select().from(runs).where(
|
|
35214
|
-
|
|
35332
|
+
and29(
|
|
35215
35333
|
eq38(runs.projectId, projectId),
|
|
35216
35334
|
or6(eq38(runs.status, "completed"), eq38(runs.status, "partial")),
|
|
35217
35335
|
// Defensive: RunCoordinator already skips probes before this is
|
|
@@ -35303,7 +35421,7 @@ var IntelligenceService = class {
|
|
|
35303
35421
|
}
|
|
35304
35422
|
const windowStart = runRow.startedAt ?? runRow.createdAt;
|
|
35305
35423
|
const windowEnd = runRow.finishedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
35306
|
-
const selected = this.db.select().from(gbpLocations).where(
|
|
35424
|
+
const selected = this.db.select().from(gbpLocations).where(and29(
|
|
35307
35425
|
eq38(gbpLocations.projectId, projectId),
|
|
35308
35426
|
eq38(gbpLocations.selected, true),
|
|
35309
35427
|
gte7(gbpLocations.syncedAt, windowStart),
|
|
@@ -35340,12 +35458,12 @@ var IntelligenceService = class {
|
|
|
35340
35458
|
}
|
|
35341
35459
|
/** Build the per-location signal bundle the GBP analyzer consumes. */
|
|
35342
35460
|
buildGbpLocationSignals(projectId, locationName, displayName, fallbackDate) {
|
|
35343
|
-
const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(
|
|
35344
|
-
const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(
|
|
35345
|
-
const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(
|
|
35346
|
-
const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(
|
|
35461
|
+
const metricRows = this.db.select({ metric: gbpDailyMetrics.metric, date: gbpDailyMetrics.date, value: gbpDailyMetrics.value }).from(gbpDailyMetrics).where(and29(eq38(gbpDailyMetrics.projectId, projectId), eq38(gbpDailyMetrics.locationName, locationName))).all();
|
|
35462
|
+
const placeActionRows = this.db.select({ placeActionType: gbpPlaceActions.placeActionType, providerType: gbpPlaceActions.providerType }).from(gbpPlaceActions).where(and29(eq38(gbpPlaceActions.projectId, projectId), eq38(gbpPlaceActions.locationName, locationName))).all();
|
|
35463
|
+
const lodgingRow = this.db.select({ populatedGroupCount: gbpLodgingSnapshots.populatedGroupCount }).from(gbpLodgingSnapshots).where(and29(eq38(gbpLodgingSnapshots.projectId, projectId), eq38(gbpLodgingSnapshots.locationName, locationName))).orderBy(desc18(gbpLodgingSnapshots.syncedAt)).limit(1).get();
|
|
35464
|
+
const ownerRow = this.db.select({ description: gbpLocations.description }).from(gbpLocations).where(and29(eq38(gbpLocations.projectId, projectId), eq38(gbpLocations.locationName, locationName))).get();
|
|
35347
35465
|
const descriptionMissing = !(ownerRow?.description ?? "").trim();
|
|
35348
|
-
const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(
|
|
35466
|
+
const placeRow = this.db.select({ attributes: gbpPlaceDetails.attributes }).from(gbpPlaceDetails).where(and29(eq38(gbpPlaceDetails.projectId, projectId), eq38(gbpPlaceDetails.locationName, locationName))).orderBy(desc18(gbpPlaceDetails.syncedAt)).limit(1).get();
|
|
35349
35467
|
const placesAmenities = placeRow ? extractPlaceAmenities(placeRow.attributes) : [];
|
|
35350
35468
|
const summary = buildGbpSummary({
|
|
35351
35469
|
locationName,
|
|
@@ -35381,7 +35499,7 @@ var IntelligenceService = class {
|
|
|
35381
35499
|
/** Build the month-over-month keyword series for a location from the
|
|
35382
35500
|
* accumulating gbp_keyword_monthly table (latest complete month vs prior). */
|
|
35383
35501
|
buildGbpKeywordTrend(projectId, locationName) {
|
|
35384
|
-
const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(
|
|
35502
|
+
const rows = this.db.select({ month: gbpKeywordMonthly.month, keyword: gbpKeywordMonthly.keyword, valueCount: gbpKeywordMonthly.valueCount }).from(gbpKeywordMonthly).where(and29(eq38(gbpKeywordMonthly.projectId, projectId), eq38(gbpKeywordMonthly.locationName, locationName))).all();
|
|
35385
35503
|
if (rows.length === 0) return { recentMonth: null, priorMonth: null, points: [] };
|
|
35386
35504
|
const months = [...new Set(rows.map((r) => r.month))].sort().reverse();
|
|
35387
35505
|
const recentMonth = months[0] ?? null;
|
|
@@ -35412,7 +35530,7 @@ var IntelligenceService = class {
|
|
|
35412
35530
|
*/
|
|
35413
35531
|
persistGbpInsights(runId, projectId, gbpInsights, coveredLocationNames) {
|
|
35414
35532
|
const covered = new Set(coveredLocationNames);
|
|
35415
|
-
const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(
|
|
35533
|
+
const existing = this.db.select({ id: insights.id, dismissed: insights.dismissed }).from(insights).where(and29(eq38(insights.projectId, projectId), eq38(insights.provider, GBP_INSIGHT_PROVIDER))).all();
|
|
35416
35534
|
const staleIds = [];
|
|
35417
35535
|
const dismissedSlots = /* @__PURE__ */ new Set();
|
|
35418
35536
|
for (const row of existing) {
|
|
@@ -35514,7 +35632,7 @@ var IntelligenceService = class {
|
|
|
35514
35632
|
sinceTimestamp = parsed;
|
|
35515
35633
|
}
|
|
35516
35634
|
const allRuns = this.db.select().from(runs).where(
|
|
35517
|
-
|
|
35635
|
+
and29(
|
|
35518
35636
|
eq38(runs.projectId, project.id),
|
|
35519
35637
|
or6(eq38(runs.status, "completed"), eq38(runs.status, "partial")),
|
|
35520
35638
|
// Backfill must not replay probe runs as if they were real sweeps.
|
|
@@ -35691,7 +35809,7 @@ var IntelligenceService = class {
|
|
|
35691
35809
|
);
|
|
35692
35810
|
const ROWS_PER_GROUP_BUDGET = Math.max(2, locationCount);
|
|
35693
35811
|
const recentRunRows = this.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(
|
|
35694
|
-
|
|
35812
|
+
and29(
|
|
35695
35813
|
eq38(runs.projectId, projectId),
|
|
35696
35814
|
eq38(runs.kind, RunKinds["answer-visibility"]),
|
|
35697
35815
|
or6(eq38(runs.status, "completed"), eq38(runs.status, "partial")),
|
|
@@ -35714,7 +35832,7 @@ var IntelligenceService = class {
|
|
|
35714
35832
|
const haveHistory = recentRunIds.length > 0;
|
|
35715
35833
|
const priorRegressionsByPair = /* @__PURE__ */ new Map();
|
|
35716
35834
|
if (haveHistory) {
|
|
35717
|
-
const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(
|
|
35835
|
+
const priorRows = this.db.select({ query: insights.query, provider: insights.provider, runId: insights.runId }).from(insights).where(and29(eq38(insights.type, "regression"), inArray14(insights.runId, recentRunIds))).all();
|
|
35718
35836
|
const regressionGroups = /* @__PURE__ */ new Map();
|
|
35719
35837
|
for (const row of priorRows) {
|
|
35720
35838
|
if (!row.runId) continue;
|