@ainyc/canonry 4.36.0 → 4.40.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/{index-CAmKaZIt.js → index-BXkHB0ox.js} +110 -110
- package/assets/assets/index-DkoFqd1J.css +1 -0
- package/assets/index.html +2 -2
- package/dist/{chunk-JQQXMCQ7.js → chunk-EWYUJ2DG.js} +202 -11
- package/dist/{chunk-F2G67CIU.js → chunk-H6COOYA6.js} +329 -206
- package/dist/{chunk-O7S623DL.js → chunk-JS6KRBBZ.js} +18 -0
- package/dist/cli.js +68 -12
- package/dist/index.js +3 -3
- package/dist/{intelligence-service-7AWRUNI2.js → intelligence-service-ZZP3TVZZ.js} +1 -1
- package/dist/mcp.js +1 -1
- package/package.json +5 -5
- package/assets/assets/index-CTrHzgs-.css +0 -1
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
loadConfig,
|
|
6
6
|
loadConfigRaw,
|
|
7
7
|
saveConfigPatch
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JS6KRBBZ.js";
|
|
9
9
|
import {
|
|
10
10
|
DEFAULT_RUN_HISTORY_LIMIT,
|
|
11
11
|
IntelligenceService,
|
|
@@ -30,11 +30,14 @@ import {
|
|
|
30
30
|
buildGapQueryScore,
|
|
31
31
|
buildInventory,
|
|
32
32
|
buildMentionCoverage,
|
|
33
|
+
buildMentionGapScore,
|
|
33
34
|
buildMentionLandscape,
|
|
34
35
|
buildMovementSummary,
|
|
35
36
|
buildOverviewCompetitors,
|
|
36
37
|
buildProviderScores,
|
|
38
|
+
buildProviderTrends,
|
|
37
39
|
buildRunHistory,
|
|
40
|
+
buildShareOfVoice,
|
|
38
41
|
buildVisibilityScore,
|
|
39
42
|
categorizeQueryByIntent,
|
|
40
43
|
ccReleaseSyncs,
|
|
@@ -67,6 +70,7 @@ import {
|
|
|
67
70
|
parseJsonColumn,
|
|
68
71
|
pickGroupRepresentative,
|
|
69
72
|
projects,
|
|
73
|
+
providerKey,
|
|
70
74
|
queries,
|
|
71
75
|
querySnapshots,
|
|
72
76
|
rawEventSamples,
|
|
@@ -74,7 +78,7 @@ import {
|
|
|
74
78
|
schedules,
|
|
75
79
|
trafficSources,
|
|
76
80
|
usageCounters
|
|
77
|
-
} from "./chunk-
|
|
81
|
+
} from "./chunk-EWYUJ2DG.js";
|
|
78
82
|
import {
|
|
79
83
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
80
84
|
AGENT_PROVIDER_IDS,
|
|
@@ -926,7 +930,7 @@ function aliasArraysEqual(a, b) {
|
|
|
926
930
|
|
|
927
931
|
// ../api-routes/src/queries.ts
|
|
928
932
|
import crypto5 from "crypto";
|
|
929
|
-
import { eq as eq4 } from "drizzle-orm";
|
|
933
|
+
import { eq as eq4, inArray, sql as sql3 } from "drizzle-orm";
|
|
930
934
|
async function queryRoutes(app, opts) {
|
|
931
935
|
app.get("/projects/:name/queries", async (request, reply) => {
|
|
932
936
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -962,6 +966,36 @@ async function queryRoutes(app, opts) {
|
|
|
962
966
|
const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
|
|
963
967
|
return reply.send(rows.map((r) => ({ id: r.id, query: r.query, createdAt: r.createdAt })));
|
|
964
968
|
});
|
|
969
|
+
app.post("/projects/:name/queries/replace-preview", async (request, reply) => {
|
|
970
|
+
const project = resolveProject(app.db, request.params.name);
|
|
971
|
+
const body = request.body;
|
|
972
|
+
if (!body || !Array.isArray(body.queries)) {
|
|
973
|
+
throw validationError('Body must contain a "queries" array');
|
|
974
|
+
}
|
|
975
|
+
const currentRows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
|
|
976
|
+
const currentTexts = currentRows.map((r) => r.query);
|
|
977
|
+
const currentSet = new Set(currentTexts);
|
|
978
|
+
const proposedSet = new Set(body.queries);
|
|
979
|
+
const removed = currentTexts.filter((q) => !proposedSet.has(q));
|
|
980
|
+
const added = body.queries.filter((q) => !currentSet.has(q));
|
|
981
|
+
const unchanged = currentTexts.filter((q) => proposedSet.has(q));
|
|
982
|
+
const currentIds = currentRows.map((r) => r.id);
|
|
983
|
+
let snapshotsDetached = 0;
|
|
984
|
+
let affectedQueries = 0;
|
|
985
|
+
if (currentIds.length > 0) {
|
|
986
|
+
const snapshotCount = app.db.select({ n: sql3`count(*)` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
|
|
987
|
+
snapshotsDetached = snapshotCount?.n ?? 0;
|
|
988
|
+
const distinctAffected = app.db.select({ n: sql3`count(distinct ${querySnapshots.queryId})` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
|
|
989
|
+
affectedQueries = distinctAffected?.n ?? 0;
|
|
990
|
+
}
|
|
991
|
+
return reply.send({
|
|
992
|
+
project: { id: project.id, name: project.name },
|
|
993
|
+
current: currentTexts,
|
|
994
|
+
proposed: body.queries,
|
|
995
|
+
diff: { added, removed, unchanged },
|
|
996
|
+
snapshotImpact: { affectedQueries, snapshotsDetached }
|
|
997
|
+
});
|
|
998
|
+
});
|
|
965
999
|
app.delete("/projects/:name/queries", async (request, reply) => {
|
|
966
1000
|
const project = resolveProject(app.db, request.params.name);
|
|
967
1001
|
const body = request.body;
|
|
@@ -1325,7 +1359,7 @@ function parseCompetitorBatch(value) {
|
|
|
1325
1359
|
|
|
1326
1360
|
// ../api-routes/src/runs.ts
|
|
1327
1361
|
import crypto8 from "crypto";
|
|
1328
|
-
import { and as and2, eq as eq7, asc, desc, or as or2, sql as
|
|
1362
|
+
import { and as and2, eq as eq7, asc, desc, or as or2, sql as sql4 } from "drizzle-orm";
|
|
1329
1363
|
|
|
1330
1364
|
// ../api-routes/src/run-queue.ts
|
|
1331
1365
|
import crypto7 from "crypto";
|
|
@@ -1497,7 +1531,7 @@ async function runRoutes(app, opts) {
|
|
|
1497
1531
|
});
|
|
1498
1532
|
app.get("/projects/:name/runs/latest", async (request, reply) => {
|
|
1499
1533
|
const project = resolveProject(app.db, request.params.name);
|
|
1500
|
-
const countRow = app.db.select({ count:
|
|
1534
|
+
const countRow = app.db.select({ count: sql4`count(*)` }).from(runs).where(eq7(runs.projectId, project.id)).get();
|
|
1501
1535
|
const totalRuns = countRow?.count ?? 0;
|
|
1502
1536
|
const latestRun = app.db.select().from(runs).where(eq7(runs.projectId, project.id)).orderBy(desc(runs.createdAt), desc(runs.id)).limit(1).get();
|
|
1503
1537
|
if (!latestRun) {
|
|
@@ -2226,7 +2260,7 @@ function normalizeCompetitorList2(domains) {
|
|
|
2226
2260
|
}
|
|
2227
2261
|
|
|
2228
2262
|
// ../api-routes/src/history.ts
|
|
2229
|
-
import { eq as eq9, desc as desc2, inArray } from "drizzle-orm";
|
|
2263
|
+
import { eq as eq9, desc as desc2, inArray as inArray2 } from "drizzle-orm";
|
|
2230
2264
|
|
|
2231
2265
|
// ../api-routes/src/notification-redaction.ts
|
|
2232
2266
|
var REDACTED_URL = {
|
|
@@ -2302,7 +2336,7 @@ async function historyRoutes(app) {
|
|
|
2302
2336
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
2303
2337
|
location: querySnapshots.location,
|
|
2304
2338
|
createdAt: querySnapshots.createdAt
|
|
2305
|
-
}).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(
|
|
2339
|
+
}).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(inArray2(querySnapshots.runId, projectRuns.map((r) => r.id))).orderBy(desc2(querySnapshots.createdAt)).all();
|
|
2306
2340
|
const locationFilter = request.query.location;
|
|
2307
2341
|
const filtered = locationFilter !== void 0 ? allSnapshots.filter((s) => s.location === (locationFilter || null)) : allSnapshots;
|
|
2308
2342
|
const total = filtered.length;
|
|
@@ -2337,7 +2371,7 @@ async function historyRoutes(app) {
|
|
|
2337
2371
|
return reply.send([]);
|
|
2338
2372
|
}
|
|
2339
2373
|
const runIds = new Set(projectRuns.map((r) => r.id));
|
|
2340
|
-
const rawSnapshots = app.db.select().from(querySnapshots).where(
|
|
2374
|
+
const rawSnapshots = app.db.select().from(querySnapshots).where(inArray2(querySnapshots.runId, [...runIds])).all();
|
|
2341
2375
|
const timelineLocationFilter = request.query.location;
|
|
2342
2376
|
const filteredSnapshots = timelineLocationFilter !== void 0 ? rawSnapshots.filter((s) => s.location === (timelineLocationFilter || null)) : rawSnapshots;
|
|
2343
2377
|
const allSnapshots = filteredSnapshots.map((snapshot) => ({
|
|
@@ -2517,7 +2551,7 @@ function formatAuditEntry(row) {
|
|
|
2517
2551
|
}
|
|
2518
2552
|
|
|
2519
2553
|
// ../api-routes/src/analytics.ts
|
|
2520
|
-
import { eq as eq10, desc as desc3, inArray as
|
|
2554
|
+
import { eq as eq10, desc as desc3, inArray as inArray3 } from "drizzle-orm";
|
|
2521
2555
|
async function analyticsRoutes(app) {
|
|
2522
2556
|
app.get("/projects/:name/analytics/metrics", async (request, reply) => {
|
|
2523
2557
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -2544,7 +2578,7 @@ async function analyticsRoutes(app) {
|
|
|
2544
2578
|
answerMentioned: querySnapshots.answerMentioned,
|
|
2545
2579
|
answerText: querySnapshots.answerText,
|
|
2546
2580
|
createdAt: querySnapshots.createdAt
|
|
2547
|
-
}).from(querySnapshots).where(
|
|
2581
|
+
}).from(querySnapshots).where(inArray3(querySnapshots.runId, runIds)).all());
|
|
2548
2582
|
const allSnapshots = rawSnapshots.map((s) => ({
|
|
2549
2583
|
...s,
|
|
2550
2584
|
resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
|
|
@@ -2589,7 +2623,7 @@ async function analyticsRoutes(app) {
|
|
|
2589
2623
|
citationState: querySnapshots.citationState,
|
|
2590
2624
|
answerMentioned: querySnapshots.answerMentioned,
|
|
2591
2625
|
answerText: querySnapshots.answerText
|
|
2592
|
-
}).from(querySnapshots).where(
|
|
2626
|
+
}).from(querySnapshots).where(inArray3(querySnapshots.runId, windowRunIds)).all());
|
|
2593
2627
|
for (const s of allWindowSnaps) {
|
|
2594
2628
|
const timePoint = runIdToCreatedAt.get(s.runId) ?? s.runId;
|
|
2595
2629
|
let entry = consistencyMap.get(s.queryId);
|
|
@@ -2610,7 +2644,7 @@ async function analyticsRoutes(app) {
|
|
|
2610
2644
|
answerMentioned: querySnapshots.answerMentioned,
|
|
2611
2645
|
answerText: querySnapshots.answerText,
|
|
2612
2646
|
competitorOverlap: querySnapshots.competitorOverlap
|
|
2613
|
-
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(
|
|
2647
|
+
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, latestGroupRunIds)).all());
|
|
2614
2648
|
const snapshots = rawSnapshots.map((s) => ({
|
|
2615
2649
|
...s,
|
|
2616
2650
|
resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
|
|
@@ -2705,7 +2739,7 @@ async function analyticsRoutes(app) {
|
|
|
2705
2739
|
queryId: querySnapshots.queryId,
|
|
2706
2740
|
query: queries.query,
|
|
2707
2741
|
rawResponse: querySnapshots.rawResponse
|
|
2708
|
-
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(
|
|
2742
|
+
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, windowRunIds)).all();
|
|
2709
2743
|
const overallCounts = /* @__PURE__ */ new Map();
|
|
2710
2744
|
const byQuery = {};
|
|
2711
2745
|
for (const snap of snapshots) {
|
|
@@ -2864,7 +2898,7 @@ function buildCategoryCounts(counts) {
|
|
|
2864
2898
|
}
|
|
2865
2899
|
|
|
2866
2900
|
// ../api-routes/src/intelligence.ts
|
|
2867
|
-
import { eq as eq11, desc as desc4, and as and4, inArray as
|
|
2901
|
+
import { eq as eq11, desc as desc4, and as and4, inArray as inArray4 } from "drizzle-orm";
|
|
2868
2902
|
function emptyHealthSnapshot(projectId) {
|
|
2869
2903
|
return {
|
|
2870
2904
|
id: `no-data:${projectId}`,
|
|
@@ -2980,14 +3014,14 @@ async function intelligenceRoutes(app) {
|
|
|
2980
3014
|
const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and4(
|
|
2981
3015
|
eq11(runs.projectId, project.id),
|
|
2982
3016
|
eq11(runs.kind, RunKinds["answer-visibility"]),
|
|
2983
|
-
|
|
3017
|
+
inArray4(runs.status, [RunStatuses.completed, RunStatuses.partial])
|
|
2984
3018
|
)).orderBy(desc4(runs.createdAt), desc4(runs.id)).all();
|
|
2985
3019
|
const latestGroup = groupRunsByCreatedAt(projectVisRuns)[0] ?? [];
|
|
2986
3020
|
const latestGroupRunIds = latestGroup.map((r) => r.id);
|
|
2987
3021
|
if (latestGroupRunIds.length > 0) {
|
|
2988
3022
|
const groupRows = app.db.select().from(healthSnapshots).where(and4(
|
|
2989
3023
|
eq11(healthSnapshots.projectId, project.id),
|
|
2990
|
-
|
|
3024
|
+
inArray4(healthSnapshots.runId, latestGroupRunIds)
|
|
2991
3025
|
)).all();
|
|
2992
3026
|
if (groupRows.length > 0) {
|
|
2993
3027
|
return reply.send(aggregateHealthSnapshots(project.id, groupRows));
|
|
@@ -3009,7 +3043,7 @@ async function intelligenceRoutes(app) {
|
|
|
3009
3043
|
}
|
|
3010
3044
|
|
|
3011
3045
|
// ../api-routes/src/report.ts
|
|
3012
|
-
import { and as and6, desc as desc6, eq as eq13, gte, inArray as
|
|
3046
|
+
import { and as and6, desc as desc6, eq as eq13, gte, inArray as inArray6, lt, lte, ne, or as or3, sql as sql5 } from "drizzle-orm";
|
|
3013
3047
|
|
|
3014
3048
|
// ../api-routes/src/report-renderer.ts
|
|
3015
3049
|
var COLORS = {
|
|
@@ -5245,7 +5279,7 @@ function renderReportHtml(report, opts = {}) {
|
|
|
5245
5279
|
}
|
|
5246
5280
|
|
|
5247
5281
|
// ../api-routes/src/content-data.ts
|
|
5248
|
-
import { and as and5, eq as eq12, desc as desc5, inArray as
|
|
5282
|
+
import { and as and5, eq as eq12, desc as desc5, inArray as inArray5 } from "drizzle-orm";
|
|
5249
5283
|
var RECENT_RUNS_WINDOW = 5;
|
|
5250
5284
|
function loadOrchestratorInput(db, project, locationFilter = void 0) {
|
|
5251
5285
|
const projectId = project.id;
|
|
@@ -5375,7 +5409,7 @@ function listRecentAnswerVisibilityRunIds(db, projectId, limit, locationFilter)
|
|
|
5375
5409
|
// Queued/running/failed/cancelled runs may have partial or no
|
|
5376
5410
|
// snapshots; including them risks pointing latestRunId at a run with
|
|
5377
5411
|
// no usable evidence.
|
|
5378
|
-
|
|
5412
|
+
inArray5(runs.status, [RunStatuses.completed, RunStatuses.partial])
|
|
5379
5413
|
)
|
|
5380
5414
|
).orderBy(desc5(runs.createdAt)).all();
|
|
5381
5415
|
const filtered = locationFilter === void 0 ? rows : rows.filter((r) => (r.location ?? null) === locationFilter);
|
|
@@ -5417,7 +5451,7 @@ function buildCandidateQueries(opts) {
|
|
|
5417
5451
|
const queryRows = opts.db.select({ id: queries.id, text: queries.query }).from(queries).where(eq12(queries.projectId, opts.projectId)).all();
|
|
5418
5452
|
const queryIdByText = new Map(queryRows.map((r) => [r.text, r.id]));
|
|
5419
5453
|
const candidateQueryIds = opts.candidateQueryStrings.map((q) => queryIdByText.get(q)).filter((id) => Boolean(id));
|
|
5420
|
-
const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(
|
|
5454
|
+
const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(inArray5(querySnapshots.runId, opts.recentRunIds)).all()).filter((r) => candidateQueryIds.includes(r.queryId));
|
|
5421
5455
|
const snapshotsByQuery = /* @__PURE__ */ new Map();
|
|
5422
5456
|
for (const row of snapshotRows) {
|
|
5423
5457
|
const list = snapshotsByQuery.get(row.queryId) ?? [];
|
|
@@ -5635,7 +5669,7 @@ function loadSnapshotsForRun(db, runId) {
|
|
|
5635
5669
|
}
|
|
5636
5670
|
function loadSnapshotsForRunIds(db, runIds) {
|
|
5637
5671
|
if (runIds.length === 0) return [];
|
|
5638
|
-
const rows = db.select().from(querySnapshots).where(
|
|
5672
|
+
const rows = db.select().from(querySnapshots).where(inArray6(querySnapshots.runId, [...runIds])).all();
|
|
5639
5673
|
return rows.filter((r) => r.queryId !== null).map((r) => ({
|
|
5640
5674
|
id: r.id,
|
|
5641
5675
|
runId: r.runId,
|
|
@@ -5885,7 +5919,7 @@ function buildAiReferrals(db, projectId) {
|
|
|
5885
5919
|
return { totalSessions: total, totalUsers, bySource, trend, topLandingPages };
|
|
5886
5920
|
}
|
|
5887
5921
|
function nonSubresourceReferralPathCondition() {
|
|
5888
|
-
return
|
|
5922
|
+
return sql5`
|
|
5889
5923
|
LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/_next/static/%'
|
|
5890
5924
|
AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/assets/%'
|
|
5891
5925
|
AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/static/%'
|
|
@@ -5924,7 +5958,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5924
5958
|
const priorStart = new Date(priorStartMs).toISOString();
|
|
5925
5959
|
const trendStart = new Date(trendStartMs).toISOString();
|
|
5926
5960
|
const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
5927
|
-
db.select({ total:
|
|
5961
|
+
db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
5928
5962
|
and6(
|
|
5929
5963
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
5930
5964
|
eq13(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
@@ -5934,7 +5968,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5934
5968
|
).get()?.total ?? 0
|
|
5935
5969
|
);
|
|
5936
5970
|
const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
5937
|
-
db.select({ total:
|
|
5971
|
+
db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
5938
5972
|
and6(
|
|
5939
5973
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
5940
5974
|
ne(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
@@ -5944,7 +5978,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5944
5978
|
).get()?.total ?? 0
|
|
5945
5979
|
);
|
|
5946
5980
|
const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
5947
|
-
db.select({ total:
|
|
5981
|
+
db.select({ total: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
5948
5982
|
and6(
|
|
5949
5983
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
5950
5984
|
nonSubresourceReferralPathCondition(),
|
|
@@ -5962,7 +5996,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5962
5996
|
const crawlerByOperatorRows = db.select({
|
|
5963
5997
|
operator: crawlerEventsHourly.operator,
|
|
5964
5998
|
verificationStatus: crawlerEventsHourly.verificationStatus,
|
|
5965
|
-
hits:
|
|
5999
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
5966
6000
|
}).from(crawlerEventsHourly).where(
|
|
5967
6001
|
and6(
|
|
5968
6002
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -5972,7 +6006,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5972
6006
|
).groupBy(crawlerEventsHourly.operator, crawlerEventsHourly.verificationStatus).all();
|
|
5973
6007
|
const crawlerByOperatorPriorRows = db.select({
|
|
5974
6008
|
operator: crawlerEventsHourly.operator,
|
|
5975
|
-
hits:
|
|
6009
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
5976
6010
|
}).from(crawlerEventsHourly).where(
|
|
5977
6011
|
and6(
|
|
5978
6012
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -5983,7 +6017,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5983
6017
|
).groupBy(crawlerEventsHourly.operator).all();
|
|
5984
6018
|
const referralByOperatorRows = db.select({
|
|
5985
6019
|
operator: aiReferralEventsHourly.operator,
|
|
5986
|
-
hits:
|
|
6020
|
+
hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
|
|
5987
6021
|
}).from(aiReferralEventsHourly).where(
|
|
5988
6022
|
and6(
|
|
5989
6023
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6023,8 +6057,8 @@ function buildServerActivity(db, projectId) {
|
|
|
6023
6057
|
);
|
|
6024
6058
|
const topPathsRows = db.select({
|
|
6025
6059
|
path: crawlerEventsHourly.pathNormalized,
|
|
6026
|
-
hits:
|
|
6027
|
-
operators:
|
|
6060
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
|
|
6061
|
+
operators: sql5`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
|
|
6028
6062
|
}).from(crawlerEventsHourly).where(
|
|
6029
6063
|
and6(
|
|
6030
6064
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -6032,7 +6066,7 @@ function buildServerActivity(db, projectId) {
|
|
|
6032
6066
|
gte(crawlerEventsHourly.tsHour, headlineStart),
|
|
6033
6067
|
lte(crawlerEventsHourly.tsHour, headlineEnd)
|
|
6034
6068
|
)
|
|
6035
|
-
).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(
|
|
6069
|
+
).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(sql5`SUM(${crawlerEventsHourly.hits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
|
|
6036
6070
|
const topCrawledPaths = topPathsRows.map((r) => ({
|
|
6037
6071
|
path: r.path,
|
|
6038
6072
|
verifiedHits: Number(r.hits),
|
|
@@ -6040,8 +6074,8 @@ function buildServerActivity(db, projectId) {
|
|
|
6040
6074
|
}));
|
|
6041
6075
|
const referralProductsRows = db.select({
|
|
6042
6076
|
product: aiReferralEventsHourly.product,
|
|
6043
|
-
arrivals:
|
|
6044
|
-
landingPaths:
|
|
6077
|
+
arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
6078
|
+
landingPaths: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
|
|
6045
6079
|
}).from(aiReferralEventsHourly).where(
|
|
6046
6080
|
and6(
|
|
6047
6081
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6049,7 +6083,7 @@ function buildServerActivity(db, projectId) {
|
|
|
6049
6083
|
gte(aiReferralEventsHourly.tsHour, headlineStart),
|
|
6050
6084
|
lte(aiReferralEventsHourly.tsHour, headlineEnd)
|
|
6051
6085
|
)
|
|
6052
|
-
).groupBy(aiReferralEventsHourly.product).orderBy(desc6(
|
|
6086
|
+
).groupBy(aiReferralEventsHourly.product).orderBy(desc6(sql5`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).all();
|
|
6053
6087
|
const referralProducts = referralProductsRows.map((r) => ({
|
|
6054
6088
|
product: r.product,
|
|
6055
6089
|
arrivals: Number(r.arrivals),
|
|
@@ -6057,8 +6091,8 @@ function buildServerActivity(db, projectId) {
|
|
|
6057
6091
|
}));
|
|
6058
6092
|
const topReferralRows = db.select({
|
|
6059
6093
|
path: aiReferralEventsHourly.landingPathNormalized,
|
|
6060
|
-
arrivals:
|
|
6061
|
-
products:
|
|
6094
|
+
arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
6095
|
+
products: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
|
|
6062
6096
|
}).from(aiReferralEventsHourly).where(
|
|
6063
6097
|
and6(
|
|
6064
6098
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6066,15 +6100,15 @@ function buildServerActivity(db, projectId) {
|
|
|
6066
6100
|
gte(aiReferralEventsHourly.tsHour, headlineStart),
|
|
6067
6101
|
lte(aiReferralEventsHourly.tsHour, headlineEnd)
|
|
6068
6102
|
)
|
|
6069
|
-
).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(
|
|
6103
|
+
).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(sql5`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
|
|
6070
6104
|
const topReferralLandingPaths = topReferralRows.map((r) => ({
|
|
6071
6105
|
path: r.path,
|
|
6072
6106
|
arrivals: Number(r.arrivals),
|
|
6073
6107
|
distinctProducts: Number(r.products)
|
|
6074
6108
|
}));
|
|
6075
6109
|
const crawlerTrendRows = db.select({
|
|
6076
|
-
date:
|
|
6077
|
-
hits:
|
|
6110
|
+
date: sql5`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
|
|
6111
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
6078
6112
|
}).from(crawlerEventsHourly).where(
|
|
6079
6113
|
and6(
|
|
6080
6114
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -6082,10 +6116,10 @@ function buildServerActivity(db, projectId) {
|
|
|
6082
6116
|
gte(crawlerEventsHourly.tsHour, trendStart),
|
|
6083
6117
|
lte(crawlerEventsHourly.tsHour, headlineEnd)
|
|
6084
6118
|
)
|
|
6085
|
-
).groupBy(
|
|
6119
|
+
).groupBy(sql5`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`).all();
|
|
6086
6120
|
const referralTrendRows = db.select({
|
|
6087
|
-
date:
|
|
6088
|
-
hits:
|
|
6121
|
+
date: sql5`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
|
|
6122
|
+
hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
|
|
6089
6123
|
}).from(aiReferralEventsHourly).where(
|
|
6090
6124
|
and6(
|
|
6091
6125
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6093,7 +6127,7 @@ function buildServerActivity(db, projectId) {
|
|
|
6093
6127
|
gte(aiReferralEventsHourly.tsHour, trendStart),
|
|
6094
6128
|
lte(aiReferralEventsHourly.tsHour, headlineEnd)
|
|
6095
6129
|
)
|
|
6096
|
-
).groupBy(
|
|
6130
|
+
).groupBy(sql5`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`).all();
|
|
6097
6131
|
const dailyTrendMap = /* @__PURE__ */ new Map();
|
|
6098
6132
|
for (const r of crawlerTrendRows) {
|
|
6099
6133
|
const e = dailyTrendMap.get(r.date) ?? { verifiedCrawlerHits: 0, referralArrivals: 0 };
|
|
@@ -6215,7 +6249,7 @@ function buildInsightList(db, projectId, locationFilter) {
|
|
|
6215
6249
|
)
|
|
6216
6250
|
).orderBy(desc6(runs.createdAt)).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter).slice(0, INSIGHT_LOOKBACK_RUNS).map((r) => r.id);
|
|
6217
6251
|
if (recentRunIds.length === 0) return [];
|
|
6218
|
-
const rows = db.select().from(insights).where(and6(eq13(insights.projectId, projectId),
|
|
6252
|
+
const rows = db.select().from(insights).where(and6(eq13(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc6(insights.createdAt)).all();
|
|
6219
6253
|
const severityRank = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
6220
6254
|
const flat = rows.filter((r) => !r.dismissed).map((r) => {
|
|
6221
6255
|
const recommendation = parseJsonColumn(r.recommendation, null);
|
|
@@ -6988,7 +7022,7 @@ async function reportRoutes(app) {
|
|
|
6988
7022
|
}
|
|
6989
7023
|
|
|
6990
7024
|
// ../api-routes/src/citations.ts
|
|
6991
|
-
import { eq as eq14, inArray as
|
|
7025
|
+
import { eq as eq14, inArray as inArray7 } from "drizzle-orm";
|
|
6992
7026
|
async function citationRoutes(app) {
|
|
6993
7027
|
app.get("/projects/:name/citations/visibility", async (request, reply) => {
|
|
6994
7028
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -7012,7 +7046,7 @@ async function citationRoutes(app) {
|
|
|
7012
7046
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
7013
7047
|
answerMentioned: querySnapshots.answerMentioned,
|
|
7014
7048
|
createdAt: querySnapshots.createdAt
|
|
7015
|
-
}).from(querySnapshots).where(
|
|
7049
|
+
}).from(querySnapshots).where(inArray7(querySnapshots.runId, projectRuns.map((r) => r.id))).all();
|
|
7016
7050
|
if (rawSnapshots.length === 0) {
|
|
7017
7051
|
return reply.send(emptyCitationVisibility("no-runs-yet"));
|
|
7018
7052
|
}
|
|
@@ -7153,7 +7187,7 @@ function normalizeDomain2(domain) {
|
|
|
7153
7187
|
}
|
|
7154
7188
|
|
|
7155
7189
|
// ../api-routes/src/composites.ts
|
|
7156
|
-
import { eq as eq15, and as and7, desc as desc7, sql as
|
|
7190
|
+
import { eq as eq15, and as and7, desc as desc7, sql as sql6, like, or as or4, inArray as inArray8 } from "drizzle-orm";
|
|
7157
7191
|
var TOP_INSIGHT_LIMIT = 5;
|
|
7158
7192
|
var SEARCH_HIT_HARD_LIMIT = 50;
|
|
7159
7193
|
var SEARCH_SNIPPET_RADIUS = 80;
|
|
@@ -7205,10 +7239,19 @@ async function compositeRoutes(app) {
|
|
|
7205
7239
|
const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq15(queries.projectId, project.id)).all();
|
|
7206
7240
|
const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
|
|
7207
7241
|
const configuredApiProviders = parseJsonColumn(project.providers, []).filter((p) => !p.startsWith("cdp:"));
|
|
7242
|
+
const projectDomains = effectiveDomains({
|
|
7243
|
+
canonicalDomain: project.canonicalDomain,
|
|
7244
|
+
ownedDomains: parseJsonColumn(project.ownedDomains, [])
|
|
7245
|
+
});
|
|
7208
7246
|
const scores = {
|
|
7209
7247
|
mention: buildMentionCoverage(latestSnapshots, { configuredApiProviders }),
|
|
7210
7248
|
visibility: buildVisibilityScore(latestSnapshots, { configuredApiProviders }),
|
|
7249
|
+
shareOfVoice: buildShareOfVoice(latestSnapshots, {
|
|
7250
|
+
projectDomains,
|
|
7251
|
+
competitorDomains: competitorRows.map((c) => c.domain)
|
|
7252
|
+
}),
|
|
7211
7253
|
gapQueries: buildGapQueryScore(latestSnapshots),
|
|
7254
|
+
mentionGaps: buildMentionGapScore(latestSnapshots),
|
|
7212
7255
|
indexCoverage: buildIndexCoverageScore(app, project.id),
|
|
7213
7256
|
competitorPressure: buildCompetitorPressureScore(
|
|
7214
7257
|
latestSnapshots,
|
|
@@ -7217,8 +7260,19 @@ async function compositeRoutes(app) {
|
|
|
7217
7260
|
),
|
|
7218
7261
|
runStatus: buildRunStatusScore(allRuns)
|
|
7219
7262
|
};
|
|
7220
|
-
const movementSummary = buildMovementSummary(latestSnapshots, previousSnapshots
|
|
7221
|
-
|
|
7263
|
+
const movementSummary = buildMovementSummary(latestSnapshots, previousSnapshots, {
|
|
7264
|
+
queryLookup: queryLookup.byId
|
|
7265
|
+
});
|
|
7266
|
+
const providerScoresBase = buildProviderScores(latestSnapshots);
|
|
7267
|
+
const providerTrends = buildProviderTrends(
|
|
7268
|
+
visibilityRuns.slice(0, DEFAULT_RUN_HISTORY_LIMIT).map((r) => ({ id: r.id, createdAt: r.createdAt })),
|
|
7269
|
+
snapshotsByRun,
|
|
7270
|
+
DEFAULT_RUN_HISTORY_LIMIT
|
|
7271
|
+
);
|
|
7272
|
+
const providerScores = providerScoresBase.map((score) => {
|
|
7273
|
+
const trend = providerTrends.get(providerKey(score.provider, score.model)) ?? [];
|
|
7274
|
+
return trend.length > 1 ? { ...score, trend: trend.map((p) => p.rate) } : score;
|
|
7275
|
+
});
|
|
7222
7276
|
const overviewCompetitors = buildOverviewCompetitors(
|
|
7223
7277
|
latestSnapshots,
|
|
7224
7278
|
competitorRows.map((c) => ({ id: c.id, domain: c.domain })),
|
|
@@ -7271,9 +7325,9 @@ async function compositeRoutes(app) {
|
|
|
7271
7325
|
and7(
|
|
7272
7326
|
eq15(queries.projectId, project.id),
|
|
7273
7327
|
or4(
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7328
|
+
sql6`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
|
|
7329
|
+
sql6`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
|
|
7330
|
+
sql6`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
|
|
7277
7331
|
like(queries.query, pattern)
|
|
7278
7332
|
)
|
|
7279
7333
|
)
|
|
@@ -7284,8 +7338,8 @@ async function compositeRoutes(app) {
|
|
|
7284
7338
|
or4(
|
|
7285
7339
|
like(insights.title, pattern),
|
|
7286
7340
|
like(insights.query, pattern),
|
|
7287
|
-
|
|
7288
|
-
|
|
7341
|
+
sql6`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
|
|
7342
|
+
sql6`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
|
|
7289
7343
|
)
|
|
7290
7344
|
)
|
|
7291
7345
|
).orderBy(desc7(insights.createdAt)).limit(limit + 1).all();
|
|
@@ -7360,7 +7414,7 @@ function loadSnapshotsByRunIds(app, runIds) {
|
|
|
7360
7414
|
answerMentioned: querySnapshots.answerMentioned,
|
|
7361
7415
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
7362
7416
|
citedDomains: querySnapshots.citedDomains
|
|
7363
|
-
}).from(querySnapshots).where(
|
|
7417
|
+
}).from(querySnapshots).where(inArray8(querySnapshots.runId, [...runIds])).all());
|
|
7364
7418
|
for (const row of rows) {
|
|
7365
7419
|
const list = result.get(row.runId) ?? [];
|
|
7366
7420
|
list.push({
|
|
@@ -8139,6 +8193,32 @@ var routeCatalog = [
|
|
|
8139
8193
|
200: { description: "Queries appended." }
|
|
8140
8194
|
}
|
|
8141
8195
|
},
|
|
8196
|
+
{
|
|
8197
|
+
method: "post",
|
|
8198
|
+
path: "/api/v1/projects/{name}/queries/replace-preview",
|
|
8199
|
+
summary: "Preview the impact of replacing tracked queries",
|
|
8200
|
+
description: "Read-only impact summary backing `canonry query replace --dry-run`. Returns current vs proposed query sets, the added/removed/unchanged diff, and the count of snapshots that would detach (queryId \u2192 NULL; queryText preserved).",
|
|
8201
|
+
tags: ["queries"],
|
|
8202
|
+
parameters: [nameParameter],
|
|
8203
|
+
requestBody: {
|
|
8204
|
+
required: true,
|
|
8205
|
+
content: {
|
|
8206
|
+
"application/json": {
|
|
8207
|
+
schema: {
|
|
8208
|
+
type: "object",
|
|
8209
|
+
required: ["queries"],
|
|
8210
|
+
properties: {
|
|
8211
|
+
queries: stringArraySchema
|
|
8212
|
+
}
|
|
8213
|
+
}
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
},
|
|
8217
|
+
responses: {
|
|
8218
|
+
200: { description: "Replace preview returned." },
|
|
8219
|
+
404: { description: "Project not found." }
|
|
8220
|
+
}
|
|
8221
|
+
},
|
|
8142
8222
|
{
|
|
8143
8223
|
method: "post",
|
|
8144
8224
|
path: "/api/v1/projects/{name}/queries/generate",
|
|
@@ -11418,7 +11498,7 @@ function formatNotification(row) {
|
|
|
11418
11498
|
|
|
11419
11499
|
// ../api-routes/src/google.ts
|
|
11420
11500
|
import crypto14 from "crypto";
|
|
11421
|
-
import { eq as eq18, and as and9, desc as desc8, sql as
|
|
11501
|
+
import { eq as eq18, and as and9, desc as desc8, sql as sql7 } from "drizzle-orm";
|
|
11422
11502
|
|
|
11423
11503
|
// ../integration-google/src/constants.ts
|
|
11424
11504
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
@@ -12644,11 +12724,11 @@ async function googleRoutes(app, opts) {
|
|
|
12644
12724
|
const { startDate, endDate, query, page, limit, offset } = request.query;
|
|
12645
12725
|
const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
|
|
12646
12726
|
const conditions = [eq18(gscSearchData.projectId, project.id)];
|
|
12647
|
-
if (startDate) conditions.push(
|
|
12648
|
-
else if (cutoffDate) conditions.push(
|
|
12649
|
-
if (endDate) conditions.push(
|
|
12650
|
-
if (query) conditions.push(
|
|
12651
|
-
if (page) conditions.push(
|
|
12727
|
+
if (startDate) conditions.push(sql7`${gscSearchData.date} >= ${startDate}`);
|
|
12728
|
+
else if (cutoffDate) conditions.push(sql7`${gscSearchData.date} >= ${cutoffDate}`);
|
|
12729
|
+
if (endDate) conditions.push(sql7`${gscSearchData.date} <= ${endDate}`);
|
|
12730
|
+
if (query) conditions.push(sql7`${gscSearchData.query} LIKE ${"%" + query + "%"}`);
|
|
12731
|
+
if (page) conditions.push(sql7`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
|
|
12652
12732
|
const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
|
|
12653
12733
|
const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
|
|
12654
12734
|
const rows = app.db.select().from(gscSearchData).where(and9(...conditions)).orderBy(desc8(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
|
|
@@ -13893,7 +13973,7 @@ async function cdpRoutes(app, opts) {
|
|
|
13893
13973
|
|
|
13894
13974
|
// ../api-routes/src/ga.ts
|
|
13895
13975
|
import crypto16 from "crypto";
|
|
13896
|
-
import { eq as eq21, desc as desc10, and as and12, sql as
|
|
13976
|
+
import { eq as eq21, desc as desc10, and as and12, sql as sql8 } from "drizzle-orm";
|
|
13897
13977
|
function gaLog(level, action, ctx) {
|
|
13898
13978
|
const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
|
|
13899
13979
|
const stream = level === "error" ? process.stderr : process.stdout;
|
|
@@ -14190,8 +14270,8 @@ async function ga4Routes(app, opts) {
|
|
|
14190
14270
|
tx.delete(gaTrafficSnapshots).where(
|
|
14191
14271
|
and12(
|
|
14192
14272
|
eq21(gaTrafficSnapshots.projectId, project.id),
|
|
14193
|
-
|
|
14194
|
-
|
|
14273
|
+
sql8`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
|
|
14274
|
+
sql8`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
|
|
14195
14275
|
)
|
|
14196
14276
|
).run();
|
|
14197
14277
|
for (const row of rows) {
|
|
@@ -14214,8 +14294,8 @@ async function ga4Routes(app, opts) {
|
|
|
14214
14294
|
tx.delete(gaAiReferrals).where(
|
|
14215
14295
|
and12(
|
|
14216
14296
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14217
|
-
|
|
14218
|
-
|
|
14297
|
+
sql8`${gaAiReferrals.date} >= ${summary.periodStart}`,
|
|
14298
|
+
sql8`${gaAiReferrals.date} <= ${summary.periodEnd}`
|
|
14219
14299
|
)
|
|
14220
14300
|
).run();
|
|
14221
14301
|
for (const row of aiReferrals) {
|
|
@@ -14240,8 +14320,8 @@ async function ga4Routes(app, opts) {
|
|
|
14240
14320
|
tx.delete(gaSocialReferrals).where(
|
|
14241
14321
|
and12(
|
|
14242
14322
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14243
|
-
|
|
14244
|
-
|
|
14323
|
+
sql8`${gaSocialReferrals.date} >= ${summary.periodStart}`,
|
|
14324
|
+
sql8`${gaSocialReferrals.date} <= ${summary.periodEnd}`
|
|
14245
14325
|
)
|
|
14246
14326
|
).run();
|
|
14247
14327
|
for (const row of socialReferrals) {
|
|
@@ -14331,11 +14411,11 @@ async function ga4Routes(app, opts) {
|
|
|
14331
14411
|
const cutoff = windowCutoff(window);
|
|
14332
14412
|
const cutoffDate = cutoff?.slice(0, 10) ?? null;
|
|
14333
14413
|
const snapshotConditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
|
|
14334
|
-
if (cutoffDate) snapshotConditions.push(
|
|
14414
|
+
if (cutoffDate) snapshotConditions.push(sql8`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
|
|
14335
14415
|
const aiConditions = [eq21(gaAiReferrals.projectId, project.id)];
|
|
14336
|
-
if (cutoffDate) aiConditions.push(
|
|
14416
|
+
if (cutoffDate) aiConditions.push(sql8`${gaAiReferrals.date} >= ${cutoffDate}`);
|
|
14337
14417
|
const socialConditions = [eq21(gaSocialReferrals.projectId, project.id)];
|
|
14338
|
-
if (cutoffDate) socialConditions.push(
|
|
14418
|
+
if (cutoffDate) socialConditions.push(sql8`${gaSocialReferrals.date} >= ${cutoffDate}`);
|
|
14339
14419
|
const windowSummaryRow = cutoffDate ? app.db.select({
|
|
14340
14420
|
totalSessions: gaTrafficWindowSummaries.totalSessions,
|
|
14341
14421
|
totalOrganicSessions: gaTrafficWindowSummaries.totalOrganicSessions,
|
|
@@ -14348,9 +14428,9 @@ async function ga4Routes(app, opts) {
|
|
|
14348
14428
|
)
|
|
14349
14429
|
).get() : null;
|
|
14350
14430
|
const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
|
|
14351
|
-
totalSessions:
|
|
14352
|
-
totalOrganicSessions:
|
|
14353
|
-
totalUsers:
|
|
14431
|
+
totalSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
|
|
14432
|
+
totalOrganicSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
|
|
14433
|
+
totalUsers: sql8`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
|
|
14354
14434
|
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get() : null;
|
|
14355
14435
|
const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
|
|
14356
14436
|
totalSessions: gaTrafficSummaries.totalSessions,
|
|
@@ -14358,38 +14438,38 @@ async function ga4Routes(app, opts) {
|
|
|
14358
14438
|
totalUsers: gaTrafficSummaries.totalUsers
|
|
14359
14439
|
}).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
|
|
14360
14440
|
const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
|
|
14361
|
-
totalDirectSessions:
|
|
14441
|
+
totalDirectSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
|
|
14362
14442
|
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get();
|
|
14363
14443
|
const summaryMeta = app.db.select({
|
|
14364
14444
|
periodStart: gaTrafficSummaries.periodStart,
|
|
14365
14445
|
periodEnd: gaTrafficSummaries.periodEnd
|
|
14366
14446
|
}).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
|
|
14367
14447
|
const rows = app.db.select({
|
|
14368
|
-
landingPage:
|
|
14369
|
-
sessions:
|
|
14370
|
-
organicSessions:
|
|
14371
|
-
directSessions:
|
|
14372
|
-
users:
|
|
14373
|
-
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(
|
|
14448
|
+
landingPage: sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
|
|
14449
|
+
sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
|
|
14450
|
+
organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
14451
|
+
directSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
|
|
14452
|
+
users: sql8`SUM(${gaTrafficSnapshots.users})`
|
|
14453
|
+
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
|
|
14374
14454
|
const aiReferralRows = app.db.select({
|
|
14375
14455
|
source: gaAiReferrals.source,
|
|
14376
14456
|
medium: gaAiReferrals.medium,
|
|
14377
14457
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
14378
|
-
sessions:
|
|
14379
|
-
users:
|
|
14458
|
+
sessions: sql8`SUM(${gaAiReferrals.sessions})`,
|
|
14459
|
+
users: sql8`SUM(${gaAiReferrals.users})`
|
|
14380
14460
|
}).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
|
|
14381
14461
|
const aiReferralLandingPageRows = app.db.select({
|
|
14382
14462
|
source: gaAiReferrals.source,
|
|
14383
14463
|
medium: gaAiReferrals.medium,
|
|
14384
14464
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
14385
|
-
landingPage:
|
|
14386
|
-
sessions:
|
|
14387
|
-
users:
|
|
14465
|
+
landingPage: sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
|
|
14466
|
+
sessions: sql8`SUM(${gaAiReferrals.sessions})`,
|
|
14467
|
+
users: sql8`SUM(${gaAiReferrals.users})`
|
|
14388
14468
|
}).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(
|
|
14389
14469
|
gaAiReferrals.source,
|
|
14390
14470
|
gaAiReferrals.medium,
|
|
14391
14471
|
gaAiReferrals.sourceDimension,
|
|
14392
|
-
|
|
14472
|
+
sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
|
|
14393
14473
|
).all();
|
|
14394
14474
|
const aiReferrals = pickWinningDimension(
|
|
14395
14475
|
aiReferralRows,
|
|
@@ -14400,10 +14480,10 @@ async function ga4Routes(app, opts) {
|
|
|
14400
14480
|
(r) => `${r.source}\0${r.medium}\0${r.landingPage}`
|
|
14401
14481
|
);
|
|
14402
14482
|
const aiDeduped = app.db.select({
|
|
14403
|
-
sessions:
|
|
14404
|
-
users:
|
|
14483
|
+
sessions: sql8`COALESCE(SUM(max_sessions), 0)`,
|
|
14484
|
+
users: sql8`COALESCE(SUM(max_users), 0)`
|
|
14405
14485
|
}).from(
|
|
14406
|
-
|
|
14486
|
+
sql8`(
|
|
14407
14487
|
SELECT date, source, medium,
|
|
14408
14488
|
MAX(dimension_sessions) AS max_sessions,
|
|
14409
14489
|
MAX(dimension_users) AS max_users
|
|
@@ -14412,7 +14492,7 @@ async function ga4Routes(app, opts) {
|
|
|
14412
14492
|
SUM(sessions) AS dimension_sessions,
|
|
14413
14493
|
SUM(users) AS dimension_users
|
|
14414
14494
|
FROM ga_ai_referrals
|
|
14415
|
-
WHERE project_id = ${project.id}${cutoffDate ?
|
|
14495
|
+
WHERE project_id = ${project.id}${cutoffDate ? sql8` AND date >= ${cutoffDate}` : sql8``}
|
|
14416
14496
|
GROUP BY date, source, medium, source_dimension
|
|
14417
14497
|
)
|
|
14418
14498
|
GROUP BY date, source, medium
|
|
@@ -14420,8 +14500,8 @@ async function ga4Routes(app, opts) {
|
|
|
14420
14500
|
).get();
|
|
14421
14501
|
const aiBySessionRows = app.db.select({
|
|
14422
14502
|
channelGroup: gaAiReferrals.channelGroup,
|
|
14423
|
-
sessions:
|
|
14424
|
-
users:
|
|
14503
|
+
sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
|
|
14504
|
+
users: sql8`COALESCE(SUM(${gaAiReferrals.users}), 0)`
|
|
14425
14505
|
}).from(gaAiReferrals).where(and12(...aiConditions, eq21(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
|
|
14426
14506
|
const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
|
|
14427
14507
|
let aiBySessionUsers = 0;
|
|
@@ -14434,12 +14514,12 @@ async function ga4Routes(app, opts) {
|
|
|
14434
14514
|
source: gaSocialReferrals.source,
|
|
14435
14515
|
medium: gaSocialReferrals.medium,
|
|
14436
14516
|
channelGroup: gaSocialReferrals.channelGroup,
|
|
14437
|
-
sessions:
|
|
14438
|
-
users:
|
|
14439
|
-
}).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(
|
|
14517
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
|
|
14518
|
+
users: sql8`SUM(${gaSocialReferrals.users})`
|
|
14519
|
+
}).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql8`SUM(${gaSocialReferrals.sessions}) DESC`).all();
|
|
14440
14520
|
const socialTotals = app.db.select({
|
|
14441
|
-
sessions:
|
|
14442
|
-
users:
|
|
14521
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
|
|
14522
|
+
users: sql8`SUM(${gaSocialReferrals.users})`
|
|
14443
14523
|
}).from(gaSocialReferrals).where(and12(...socialConditions)).get();
|
|
14444
14524
|
const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).orderBy(desc10(gaTrafficSummaries.syncedAt)).limit(1).get();
|
|
14445
14525
|
const total = summaryRow?.totalSessions ?? 0;
|
|
@@ -14522,21 +14602,21 @@ async function ga4Routes(app, opts) {
|
|
|
14522
14602
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14523
14603
|
const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
|
|
14524
14604
|
const conditions = [eq21(gaAiReferrals.projectId, project.id)];
|
|
14525
|
-
if (cutoffDate) conditions.push(
|
|
14605
|
+
if (cutoffDate) conditions.push(sql8`${gaAiReferrals.date} >= ${cutoffDate}`);
|
|
14526
14606
|
const rows = app.db.select({
|
|
14527
14607
|
date: gaAiReferrals.date,
|
|
14528
14608
|
source: gaAiReferrals.source,
|
|
14529
14609
|
medium: gaAiReferrals.medium,
|
|
14530
|
-
landingPage:
|
|
14610
|
+
landingPage: sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
|
|
14531
14611
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
14532
|
-
sessions:
|
|
14533
|
-
users:
|
|
14612
|
+
sessions: sql8`SUM(${gaAiReferrals.sessions})`,
|
|
14613
|
+
users: sql8`SUM(${gaAiReferrals.users})`
|
|
14534
14614
|
}).from(gaAiReferrals).where(and12(...conditions)).groupBy(
|
|
14535
14615
|
gaAiReferrals.date,
|
|
14536
14616
|
gaAiReferrals.source,
|
|
14537
14617
|
gaAiReferrals.medium,
|
|
14538
14618
|
gaAiReferrals.sourceDimension,
|
|
14539
|
-
|
|
14619
|
+
sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
|
|
14540
14620
|
).orderBy(gaAiReferrals.date).all();
|
|
14541
14621
|
return rows;
|
|
14542
14622
|
});
|
|
@@ -14545,7 +14625,7 @@ async function ga4Routes(app, opts) {
|
|
|
14545
14625
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14546
14626
|
const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
|
|
14547
14627
|
const conditions = [eq21(gaSocialReferrals.projectId, project.id)];
|
|
14548
|
-
if (cutoffDate) conditions.push(
|
|
14628
|
+
if (cutoffDate) conditions.push(sql8`${gaSocialReferrals.date} >= ${cutoffDate}`);
|
|
14549
14629
|
const rows = app.db.select({
|
|
14550
14630
|
date: gaSocialReferrals.date,
|
|
14551
14631
|
source: gaSocialReferrals.source,
|
|
@@ -14566,10 +14646,10 @@ async function ga4Routes(app, opts) {
|
|
|
14566
14646
|
d.setDate(d.getDate() - n);
|
|
14567
14647
|
return fmt(d);
|
|
14568
14648
|
};
|
|
14569
|
-
const sumSocial = (from, to) => app.db.select({ sessions:
|
|
14649
|
+
const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(
|
|
14570
14650
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14571
|
-
|
|
14572
|
-
|
|
14651
|
+
sql8`${gaSocialReferrals.date} >= ${from}`,
|
|
14652
|
+
sql8`${gaSocialReferrals.date} < ${to}`
|
|
14573
14653
|
)).get();
|
|
14574
14654
|
const current7d = sumSocial(daysAgo2(7), fmt(today));
|
|
14575
14655
|
const prev7d = sumSocial(daysAgo2(14), daysAgo2(7));
|
|
@@ -14578,19 +14658,19 @@ async function ga4Routes(app, opts) {
|
|
|
14578
14658
|
const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
|
|
14579
14659
|
const sourceCurrent = app.db.select({
|
|
14580
14660
|
source: gaSocialReferrals.source,
|
|
14581
|
-
sessions:
|
|
14661
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`
|
|
14582
14662
|
}).from(gaSocialReferrals).where(and12(
|
|
14583
14663
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14584
|
-
|
|
14585
|
-
|
|
14664
|
+
sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
|
|
14665
|
+
sql8`${gaSocialReferrals.date} < ${fmt(today)}`
|
|
14586
14666
|
)).groupBy(gaSocialReferrals.source).all();
|
|
14587
14667
|
const sourcePrev = app.db.select({
|
|
14588
14668
|
source: gaSocialReferrals.source,
|
|
14589
|
-
sessions:
|
|
14669
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`
|
|
14590
14670
|
}).from(gaSocialReferrals).where(and12(
|
|
14591
14671
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14592
|
-
|
|
14593
|
-
|
|
14672
|
+
sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
|
|
14673
|
+
sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`
|
|
14594
14674
|
)).groupBy(gaSocialReferrals.source).all();
|
|
14595
14675
|
const prevMap = new Map(sourcePrev.map((r) => [r.source, r.sessions]));
|
|
14596
14676
|
let biggestMover = null;
|
|
@@ -14629,16 +14709,16 @@ async function ga4Routes(app, opts) {
|
|
|
14629
14709
|
return fmt(d);
|
|
14630
14710
|
};
|
|
14631
14711
|
const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
|
|
14632
|
-
const sumTotal = (from, to) => app.db.select({ sessions:
|
|
14633
|
-
const sumOrganic = (from, to) => app.db.select({ sessions:
|
|
14634
|
-
const sumDirect = (from, to) => app.db.select({ sessions:
|
|
14635
|
-
const sumAi = (from, to) => app.db.select({ sessions:
|
|
14712
|
+
const sumTotal = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
14713
|
+
const sumOrganic = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
14714
|
+
const sumDirect = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)` }).from(gaTrafficSnapshots).where(and12(eq21(gaTrafficSnapshots.projectId, project.id), sql8`${gaTrafficSnapshots.date} >= ${from}`, sql8`${gaTrafficSnapshots.date} < ${to}`)).get();
|
|
14715
|
+
const sumAi = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
|
|
14636
14716
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14637
|
-
|
|
14638
|
-
|
|
14717
|
+
sql8`${gaAiReferrals.date} >= ${from}`,
|
|
14718
|
+
sql8`${gaAiReferrals.date} < ${to}`,
|
|
14639
14719
|
eq21(gaAiReferrals.sourceDimension, "session")
|
|
14640
14720
|
)).get();
|
|
14641
|
-
const sumSocial = (from, to) => app.db.select({ sessions:
|
|
14721
|
+
const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${from}`, sql8`${gaSocialReferrals.date} < ${to}`)).get();
|
|
14642
14722
|
const todayStr = fmt(today);
|
|
14643
14723
|
const buildTrend = (sum) => {
|
|
14644
14724
|
const c7 = sum(daysAgo2(7), todayStr)?.sessions ?? 0;
|
|
@@ -14647,16 +14727,16 @@ async function ga4Routes(app, opts) {
|
|
|
14647
14727
|
const p30 = sum(daysAgo2(60), daysAgo2(30))?.sessions ?? 0;
|
|
14648
14728
|
return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
|
|
14649
14729
|
};
|
|
14650
|
-
const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions:
|
|
14730
|
+
const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
|
|
14651
14731
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14652
|
-
|
|
14653
|
-
|
|
14732
|
+
sql8`${gaAiReferrals.date} >= ${daysAgo2(7)}`,
|
|
14733
|
+
sql8`${gaAiReferrals.date} < ${todayStr}`,
|
|
14654
14734
|
eq21(gaAiReferrals.sourceDimension, "session")
|
|
14655
14735
|
)).groupBy(gaAiReferrals.source).all();
|
|
14656
|
-
const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions:
|
|
14736
|
+
const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
|
|
14657
14737
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14658
|
-
|
|
14659
|
-
|
|
14738
|
+
sql8`${gaAiReferrals.date} >= ${daysAgo2(14)}`,
|
|
14739
|
+
sql8`${gaAiReferrals.date} < ${daysAgo2(7)}`,
|
|
14660
14740
|
eq21(gaAiReferrals.sourceDimension, "session")
|
|
14661
14741
|
)).groupBy(gaAiReferrals.source).all();
|
|
14662
14742
|
const findBiggestMover = (current, prev) => {
|
|
@@ -14673,8 +14753,8 @@ async function ga4Routes(app, opts) {
|
|
|
14673
14753
|
}
|
|
14674
14754
|
return mover;
|
|
14675
14755
|
};
|
|
14676
|
-
const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions:
|
|
14677
|
-
const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions:
|
|
14756
|
+
const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`, sql8`${gaSocialReferrals.date} < ${todayStr}`)).groupBy(gaSocialReferrals.source).all();
|
|
14757
|
+
const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions: sql8`SUM(${gaSocialReferrals.sessions})` }).from(gaSocialReferrals).where(and12(eq21(gaSocialReferrals.projectId, project.id), sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`, sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`)).groupBy(gaSocialReferrals.source).all();
|
|
14678
14758
|
return {
|
|
14679
14759
|
total: buildTrend(sumTotal),
|
|
14680
14760
|
organic: buildTrend(sumOrganic),
|
|
@@ -14690,12 +14770,12 @@ async function ga4Routes(app, opts) {
|
|
|
14690
14770
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14691
14771
|
const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
|
|
14692
14772
|
const conditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
|
|
14693
|
-
if (cutoffDate) conditions.push(
|
|
14773
|
+
if (cutoffDate) conditions.push(sql8`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
|
|
14694
14774
|
const rows = app.db.select({
|
|
14695
14775
|
date: gaTrafficSnapshots.date,
|
|
14696
|
-
sessions:
|
|
14697
|
-
organicSessions:
|
|
14698
|
-
users:
|
|
14776
|
+
sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
|
|
14777
|
+
organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
14778
|
+
users: sql8`SUM(${gaTrafficSnapshots.users})`
|
|
14699
14779
|
}).from(gaTrafficSnapshots).where(and12(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
|
|
14700
14780
|
return rows.map((r) => ({
|
|
14701
14781
|
date: r.date,
|
|
@@ -14708,11 +14788,11 @@ async function ga4Routes(app, opts) {
|
|
|
14708
14788
|
const project = resolveProject(app.db, request.params.name);
|
|
14709
14789
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14710
14790
|
const trafficPages = app.db.select({
|
|
14711
|
-
landingPage:
|
|
14712
|
-
sessions:
|
|
14713
|
-
organicSessions:
|
|
14714
|
-
users:
|
|
14715
|
-
}).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(
|
|
14791
|
+
landingPage: sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
|
|
14792
|
+
sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
|
|
14793
|
+
organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
14794
|
+
users: sql8`SUM(${gaTrafficSnapshots.users})`
|
|
14795
|
+
}).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
|
|
14716
14796
|
return {
|
|
14717
14797
|
pages: trafficPages.map((r) => ({
|
|
14718
14798
|
landingPage: r.landingPage,
|
|
@@ -16349,7 +16429,7 @@ async function wordpressRoutes(app, opts) {
|
|
|
16349
16429
|
|
|
16350
16430
|
// ../api-routes/src/backlinks.ts
|
|
16351
16431
|
import crypto18 from "crypto";
|
|
16352
|
-
import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as
|
|
16432
|
+
import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as sql9 } from "drizzle-orm";
|
|
16353
16433
|
|
|
16354
16434
|
// ../integration-commoncrawl/src/constants.ts
|
|
16355
16435
|
import os3 from "os";
|
|
@@ -16633,7 +16713,7 @@ async function queryBacklinks(opts) {
|
|
|
16633
16713
|
const reversed = opts.targets.map(reverseDomain);
|
|
16634
16714
|
const targetList = reversed.map(quote).join(", ");
|
|
16635
16715
|
const limitClause = opts.limitPerTarget ? `QUALIFY row_number() OVER (PARTITION BY t.target_rev_domain ORDER BY v.num_hosts DESC) <= ${Math.floor(opts.limitPerTarget)}` : "";
|
|
16636
|
-
const
|
|
16716
|
+
const sql16 = `
|
|
16637
16717
|
WITH vertices AS (
|
|
16638
16718
|
SELECT * FROM read_csv(
|
|
16639
16719
|
${quote(opts.vertexPath)},
|
|
@@ -16669,7 +16749,7 @@ async function queryBacklinks(opts) {
|
|
|
16669
16749
|
const conn = await instance.connect();
|
|
16670
16750
|
let rows;
|
|
16671
16751
|
try {
|
|
16672
|
-
const reader = await conn.runAndReadAll(
|
|
16752
|
+
const reader = await conn.runAndReadAll(sql16);
|
|
16673
16753
|
rows = reader.getRowObjects();
|
|
16674
16754
|
} finally {
|
|
16675
16755
|
conn.disconnectSync?.();
|
|
@@ -16845,12 +16925,12 @@ function computeFilteredSummary(db, base) {
|
|
|
16845
16925
|
);
|
|
16846
16926
|
const filteredCondition = and14(baseDomainCondition, backlinkCrawlerExclusionClause());
|
|
16847
16927
|
const unfilteredAgg = db.select({
|
|
16848
|
-
count:
|
|
16849
|
-
total:
|
|
16928
|
+
count: sql9`count(*)`,
|
|
16929
|
+
total: sql9`coalesce(sum(${backlinkDomains.numHosts}), 0)`
|
|
16850
16930
|
}).from(backlinkDomains).where(baseDomainCondition).get();
|
|
16851
16931
|
const filteredAgg = db.select({
|
|
16852
|
-
count:
|
|
16853
|
-
total:
|
|
16932
|
+
count: sql9`count(*)`,
|
|
16933
|
+
total: sql9`coalesce(sum(${backlinkDomains.numHosts}), 0)`
|
|
16854
16934
|
}).from(backlinkDomains).where(filteredCondition).get();
|
|
16855
16935
|
const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc11(backlinkDomains.numHosts)).limit(10).all();
|
|
16856
16936
|
const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
|
|
@@ -17024,7 +17104,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
17024
17104
|
eq22(backlinkDomains.release, targetRelease)
|
|
17025
17105
|
);
|
|
17026
17106
|
const domainCondition = excludeCrawlers ? and14(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
|
|
17027
|
-
const totalRow = app.db.select({ count:
|
|
17107
|
+
const totalRow = app.db.select({ count: sql9`count(*)` }).from(backlinkDomains).where(domainCondition).get();
|
|
17028
17108
|
const rows = app.db.select({
|
|
17029
17109
|
linkingDomain: backlinkDomains.linkingDomain,
|
|
17030
17110
|
numHosts: backlinkDomains.numHosts
|
|
@@ -17059,7 +17139,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
17059
17139
|
|
|
17060
17140
|
// ../api-routes/src/traffic.ts
|
|
17061
17141
|
import crypto20 from "crypto";
|
|
17062
|
-
import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as
|
|
17142
|
+
import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as sql10 } from "drizzle-orm";
|
|
17063
17143
|
|
|
17064
17144
|
// ../integration-cloud-run/src/auth.ts
|
|
17065
17145
|
import crypto19 from "crypto";
|
|
@@ -18797,7 +18877,7 @@ async function trafficRoutes(app, opts) {
|
|
|
18797
18877
|
crawlerEventsHourly.status
|
|
18798
18878
|
],
|
|
18799
18879
|
set: {
|
|
18800
|
-
hits:
|
|
18880
|
+
hits: sql10`${crawlerEventsHourly.hits} + ${bucket.hits}`,
|
|
18801
18881
|
sampledUserAgent: bucket.sampledUserAgent,
|
|
18802
18882
|
updatedAt: finishedAt
|
|
18803
18883
|
}
|
|
@@ -18832,7 +18912,7 @@ async function trafficRoutes(app, opts) {
|
|
|
18832
18912
|
aiReferralEventsHourly.status
|
|
18833
18913
|
],
|
|
18834
18914
|
set: {
|
|
18835
|
-
sessionsOrHits:
|
|
18915
|
+
sessionsOrHits: sql10`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
|
|
18836
18916
|
updatedAt: finishedAt
|
|
18837
18917
|
}
|
|
18838
18918
|
}).run();
|
|
@@ -19084,19 +19164,19 @@ async function trafficRoutes(app, opts) {
|
|
|
19084
19164
|
return response;
|
|
19085
19165
|
});
|
|
19086
19166
|
function buildSourceDetail(projectId, row, since) {
|
|
19087
|
-
const crawlerTotals = app.db.select({ total:
|
|
19167
|
+
const crawlerTotals = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
19088
19168
|
and15(
|
|
19089
19169
|
eq23(crawlerEventsHourly.sourceId, row.id),
|
|
19090
19170
|
gte2(crawlerEventsHourly.tsHour, since)
|
|
19091
19171
|
)
|
|
19092
19172
|
).get();
|
|
19093
|
-
const aiTotals = app.db.select({ total:
|
|
19173
|
+
const aiTotals = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
19094
19174
|
and15(
|
|
19095
19175
|
eq23(aiReferralEventsHourly.sourceId, row.id),
|
|
19096
19176
|
gte2(aiReferralEventsHourly.tsHour, since)
|
|
19097
19177
|
)
|
|
19098
19178
|
).get();
|
|
19099
|
-
const sampleTotals = app.db.select({ total:
|
|
19179
|
+
const sampleTotals = app.db.select({ total: sql10`COUNT(*)` }).from(rawEventSamples).where(
|
|
19100
19180
|
and15(
|
|
19101
19181
|
eq23(rawEventSamples.sourceId, row.id),
|
|
19102
19182
|
gte2(rawEventSamples.ts, since)
|
|
@@ -19198,7 +19278,7 @@ async function trafficRoutes(app, opts) {
|
|
|
19198
19278
|
];
|
|
19199
19279
|
if (sourceIdParam) crawlerFilters.push(eq23(crawlerEventsHourly.sourceId, sourceIdParam));
|
|
19200
19280
|
const crawlerWhere = and15(...crawlerFilters);
|
|
19201
|
-
const total = app.db.select({ total:
|
|
19281
|
+
const total = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
|
|
19202
19282
|
crawlerTotal = Number(total?.total ?? 0);
|
|
19203
19283
|
const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc12(crawlerEventsHourly.tsHour)).limit(limit).all();
|
|
19204
19284
|
for (const r of rows) {
|
|
@@ -19223,7 +19303,7 @@ async function trafficRoutes(app, opts) {
|
|
|
19223
19303
|
];
|
|
19224
19304
|
if (sourceIdParam) aiFilters.push(eq23(aiReferralEventsHourly.sourceId, sourceIdParam));
|
|
19225
19305
|
const aiWhere = and15(...aiFilters);
|
|
19226
|
-
const total = app.db.select({ total:
|
|
19306
|
+
const total = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
|
|
19227
19307
|
aiReferralTotal = Number(total?.total ?? 0);
|
|
19228
19308
|
const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc12(aiReferralEventsHourly.tsHour)).limit(limit).all();
|
|
19229
19309
|
for (const r of rows) {
|
|
@@ -19946,7 +20026,7 @@ var providersConfiguredCheck = {
|
|
|
19946
20026
|
var PROVIDERS_CHECKS = [providersConfiguredCheck];
|
|
19947
20027
|
|
|
19948
20028
|
// ../api-routes/src/doctor/checks/traffic-source.ts
|
|
19949
|
-
import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as
|
|
20029
|
+
import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as sql11 } from "drizzle-orm";
|
|
19950
20030
|
var RECENT_DATA_WARN_DAYS = 7;
|
|
19951
20031
|
var RECENT_DATA_FAIL_DAYS = 30;
|
|
19952
20032
|
function skippedNoProject2() {
|
|
@@ -20040,7 +20120,7 @@ var recentDataCheck = {
|
|
|
20040
20120
|
const warnCutoff = new Date(now.getTime() - RECENT_DATA_WARN_DAYS * 24 * 60 * 6e4).toISOString();
|
|
20041
20121
|
const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
|
|
20042
20122
|
const recentCrawlers = Number(
|
|
20043
|
-
ctx.db.select({ total:
|
|
20123
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
20044
20124
|
and16(
|
|
20045
20125
|
eq24(crawlerEventsHourly.projectId, ctx.project.id),
|
|
20046
20126
|
gte3(crawlerEventsHourly.tsHour, warnCutoff)
|
|
@@ -20048,7 +20128,7 @@ var recentDataCheck = {
|
|
|
20048
20128
|
).get()?.total ?? 0
|
|
20049
20129
|
);
|
|
20050
20130
|
const recentReferrals = Number(
|
|
20051
|
-
ctx.db.select({ total:
|
|
20131
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
20052
20132
|
and16(
|
|
20053
20133
|
eq24(aiReferralEventsHourly.projectId, ctx.project.id),
|
|
20054
20134
|
gte3(aiReferralEventsHourly.tsHour, warnCutoff)
|
|
@@ -20064,7 +20144,7 @@ var recentDataCheck = {
|
|
|
20064
20144
|
};
|
|
20065
20145
|
}
|
|
20066
20146
|
const olderCrawlers = Number(
|
|
20067
|
-
ctx.db.select({ total:
|
|
20147
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
20068
20148
|
and16(
|
|
20069
20149
|
eq24(crawlerEventsHourly.projectId, ctx.project.id),
|
|
20070
20150
|
gte3(crawlerEventsHourly.tsHour, failCutoff)
|
|
@@ -20072,7 +20152,7 @@ var recentDataCheck = {
|
|
|
20072
20152
|
).get()?.total ?? 0
|
|
20073
20153
|
);
|
|
20074
20154
|
const olderReferrals = Number(
|
|
20075
|
-
ctx.db.select({ total:
|
|
20155
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
20076
20156
|
and16(
|
|
20077
20157
|
eq24(aiReferralEventsHourly.projectId, ctx.project.id),
|
|
20078
20158
|
gte3(aiReferralEventsHourly.tsHour, failCutoff)
|
|
@@ -20372,7 +20452,7 @@ async function doctorRoutes(app, opts) {
|
|
|
20372
20452
|
|
|
20373
20453
|
// ../api-routes/src/discovery/routes.ts
|
|
20374
20454
|
import crypto21 from "crypto";
|
|
20375
|
-
import { and as and17, desc as desc13, eq as eq25, gte as gte4, inArray as
|
|
20455
|
+
import { and as and17, desc as desc13, eq as eq25, gte as gte4, inArray as inArray9 } from "drizzle-orm";
|
|
20376
20456
|
var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
|
|
20377
20457
|
async function discoveryRoutes(app, opts) {
|
|
20378
20458
|
app.post("/projects/:name/discover/run", async (request, reply) => {
|
|
@@ -20407,7 +20487,7 @@ async function discoveryRoutes(app, opts) {
|
|
|
20407
20487
|
const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and17(
|
|
20408
20488
|
eq25(discoverySessions.projectId, project.id),
|
|
20409
20489
|
eq25(discoverySessions.icpDescription, icpDescription),
|
|
20410
|
-
|
|
20490
|
+
inArray9(discoverySessions.status, [
|
|
20411
20491
|
DiscoverySessionStatuses.queued,
|
|
20412
20492
|
DiscoverySessionStatuses.seeding,
|
|
20413
20493
|
DiscoverySessionStatuses.probing
|
|
@@ -23725,7 +23805,7 @@ import crypto24 from "crypto";
|
|
|
23725
23805
|
import fs8 from "fs";
|
|
23726
23806
|
import path10 from "path";
|
|
23727
23807
|
import os5 from "os";
|
|
23728
|
-
import { and as and18, eq as eq27, inArray as
|
|
23808
|
+
import { and as and18, eq as eq27, inArray as inArray10, sql as sql12 } from "drizzle-orm";
|
|
23729
23809
|
|
|
23730
23810
|
// src/run-telemetry.ts
|
|
23731
23811
|
import crypto23 from "crypto";
|
|
@@ -24070,7 +24150,7 @@ var JobRunner = class {
|
|
|
24070
24150
|
this.registry = registry;
|
|
24071
24151
|
}
|
|
24072
24152
|
recoverStaleRuns() {
|
|
24073
|
-
const stale = this.db.select({ id: runs.id, status: runs.status }).from(runs).where(
|
|
24153
|
+
const stale = this.db.select({ id: runs.id, status: runs.status }).from(runs).where(inArray10(runs.status, ["running", "queued"])).all();
|
|
24074
24154
|
if (stale.length === 0) return;
|
|
24075
24155
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24076
24156
|
for (const run of stale) {
|
|
@@ -24133,7 +24213,7 @@ var JobRunner = class {
|
|
|
24133
24213
|
}
|
|
24134
24214
|
log.info("run.dispatch", { runId, providerCount: activeProviders.length, providers: activeProviders.map((p) => p.adapter.name) });
|
|
24135
24215
|
const scopedQueryNames = parseJsonColumn(existingRun.queries, null);
|
|
24136
|
-
projectQueries = scopedQueryNames ? this.db.select().from(queries).where(and18(eq27(queries.projectId, projectId),
|
|
24216
|
+
projectQueries = scopedQueryNames ? this.db.select().from(queries).where(and18(eq27(queries.projectId, projectId), inArray10(queries.query, scopedQueryNames))).all() : this.db.select().from(queries).where(eq27(queries.projectId, projectId)).all();
|
|
24137
24217
|
const projectCompetitors = this.db.select().from(competitors).where(eq27(competitors.projectId, projectId)).all();
|
|
24138
24218
|
const competitorDomains = projectCompetitors.map((c) => c.domain);
|
|
24139
24219
|
const allDomains = effectiveDomains({
|
|
@@ -24406,7 +24486,7 @@ var JobRunner = class {
|
|
|
24406
24486
|
updatedAt: now
|
|
24407
24487
|
}).onConflictDoUpdate({
|
|
24408
24488
|
target: [usageCounters.scope, usageCounters.period, usageCounters.metric],
|
|
24409
|
-
set: { count:
|
|
24489
|
+
set: { count: sql12`${usageCounters.count} + ${count}`, updatedAt: now }
|
|
24410
24490
|
}).run();
|
|
24411
24491
|
}
|
|
24412
24492
|
flushProviderUsage(projectId, providerDispatchCounts) {
|
|
@@ -24476,7 +24556,7 @@ function buildPhases(input) {
|
|
|
24476
24556
|
|
|
24477
24557
|
// src/gsc-sync.ts
|
|
24478
24558
|
import crypto25 from "crypto";
|
|
24479
|
-
import { eq as eq28, and as and19, sql as
|
|
24559
|
+
import { eq as eq28, and as and19, sql as sql13 } from "drizzle-orm";
|
|
24480
24560
|
var log2 = createLogger("GscSync");
|
|
24481
24561
|
function formatDate3(d) {
|
|
24482
24562
|
return d.toISOString().split("T")[0];
|
|
@@ -24530,8 +24610,8 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
24530
24610
|
db.delete(gscSearchData).where(
|
|
24531
24611
|
and19(
|
|
24532
24612
|
eq28(gscSearchData.projectId, projectId),
|
|
24533
|
-
|
|
24534
|
-
|
|
24613
|
+
sql13`${gscSearchData.date} >= ${startDate}`,
|
|
24614
|
+
sql13`${gscSearchData.date} <= ${endDate}`
|
|
24535
24615
|
)
|
|
24536
24616
|
).run();
|
|
24537
24617
|
const batchSize = 500;
|
|
@@ -25067,7 +25147,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
25067
25147
|
// src/commoncrawl-sync.ts
|
|
25068
25148
|
import crypto28 from "crypto";
|
|
25069
25149
|
import path11 from "path";
|
|
25070
|
-
import { and as and21, eq as eq31, sql as
|
|
25150
|
+
import { and as and21, eq as eq31, sql as sql14 } from "drizzle-orm";
|
|
25071
25151
|
var log6 = createLogger("CommonCrawlSync");
|
|
25072
25152
|
var INSERT_CHUNK_SIZE = 1e4;
|
|
25073
25153
|
function defaultDeps() {
|
|
@@ -25679,23 +25759,25 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
25679
25759
|
}
|
|
25680
25760
|
|
|
25681
25761
|
// src/commands/backfill.ts
|
|
25682
|
-
import { and as and24, eq as eq34, inArray as
|
|
25762
|
+
import { and as and24, eq as eq34, inArray as inArray11 } from "drizzle-orm";
|
|
25683
25763
|
var SNAPSHOT_BATCH_SIZE = 500;
|
|
25684
25764
|
async function backfillAnswerVisibilityCommand(opts) {
|
|
25685
25765
|
const config = loadConfig();
|
|
25686
25766
|
const db = createClient(config.database);
|
|
25687
25767
|
migrate(db);
|
|
25688
25768
|
const projectFilter = opts?.project?.trim();
|
|
25769
|
+
const isDryRun = opts?.dryRun === true;
|
|
25689
25770
|
const scopedProjects = projectFilter ? db.select().from(projects).where(eq34(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
25690
25771
|
let examined = 0;
|
|
25691
25772
|
let updated = 0;
|
|
25773
|
+
let wouldUpdate = 0;
|
|
25692
25774
|
let mentioned = 0;
|
|
25693
25775
|
let reparsed = 0;
|
|
25694
25776
|
let providerErrors = 0;
|
|
25695
25777
|
if (scopedProjects.length > 0) {
|
|
25696
25778
|
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and24(
|
|
25697
25779
|
eq34(runs.kind, RunKinds["answer-visibility"]),
|
|
25698
|
-
|
|
25780
|
+
inArray11(runs.projectId, scopedProjects.map((project) => project.id))
|
|
25699
25781
|
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq34(runs.kind, RunKinds["answer-visibility"])).all();
|
|
25700
25782
|
const runIdsByProject = /* @__PURE__ */ new Map();
|
|
25701
25783
|
for (const run of runRows) {
|
|
@@ -25727,7 +25809,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
25727
25809
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
25728
25810
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
25729
25811
|
rawResponse: querySnapshots.rawResponse
|
|
25730
|
-
}).from(querySnapshots).where(
|
|
25812
|
+
}).from(querySnapshots).where(inArray11(querySnapshots.runId, batchRunIds)).all();
|
|
25731
25813
|
const pendingUpdates = [];
|
|
25732
25814
|
for (const snapshot of snapshotRows) {
|
|
25733
25815
|
examined++;
|
|
@@ -25791,12 +25873,16 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
25791
25873
|
}
|
|
25792
25874
|
}
|
|
25793
25875
|
if (pendingUpdates.length > 0) {
|
|
25794
|
-
|
|
25795
|
-
|
|
25796
|
-
|
|
25797
|
-
|
|
25798
|
-
|
|
25799
|
-
|
|
25876
|
+
if (isDryRun) {
|
|
25877
|
+
wouldUpdate += pendingUpdates.length;
|
|
25878
|
+
} else {
|
|
25879
|
+
db.transaction((tx) => {
|
|
25880
|
+
for (const update of pendingUpdates) {
|
|
25881
|
+
tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
|
|
25882
|
+
}
|
|
25883
|
+
});
|
|
25884
|
+
updated += pendingUpdates.length;
|
|
25885
|
+
}
|
|
25800
25886
|
}
|
|
25801
25887
|
}
|
|
25802
25888
|
}
|
|
@@ -25810,20 +25896,33 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
25810
25896
|
reparsed,
|
|
25811
25897
|
providerErrors
|
|
25812
25898
|
};
|
|
25899
|
+
if (isDryRun) {
|
|
25900
|
+
result.dryRun = true;
|
|
25901
|
+
result.wouldUpdate = wouldUpdate;
|
|
25902
|
+
}
|
|
25813
25903
|
if (opts?.format === "json") {
|
|
25814
25904
|
console.log(JSON.stringify(result, null, 2));
|
|
25815
25905
|
return;
|
|
25816
25906
|
}
|
|
25817
|
-
console.log(
|
|
25907
|
+
console.log(`Answer visibility backfill ${isDryRun ? "preview" : "complete"}.
|
|
25908
|
+
`);
|
|
25818
25909
|
if (projectFilter) {
|
|
25819
25910
|
console.log(` Project: ${projectFilter}`);
|
|
25820
25911
|
}
|
|
25821
25912
|
console.log(` Projects: ${scopedProjects.length}`);
|
|
25822
|
-
console.log(` Examined:
|
|
25823
|
-
|
|
25824
|
-
|
|
25825
|
-
|
|
25826
|
-
|
|
25913
|
+
console.log(` Examined: ${examined}`);
|
|
25914
|
+
if (isDryRun) {
|
|
25915
|
+
console.log(` Would update: ${wouldUpdate}`);
|
|
25916
|
+
} else {
|
|
25917
|
+
console.log(` Updated: ${updated}`);
|
|
25918
|
+
}
|
|
25919
|
+
console.log(` Mentioned: ${mentioned}`);
|
|
25920
|
+
console.log(` Reparsed: ${reparsed}`);
|
|
25921
|
+
console.log(` Errors: ${providerErrors}`);
|
|
25922
|
+
if (isDryRun) {
|
|
25923
|
+
console.log(`
|
|
25924
|
+
No DB writes performed. Re-run without --dry-run to apply.`);
|
|
25925
|
+
}
|
|
25827
25926
|
}
|
|
25828
25927
|
function backfillNormalizedPaths(db, opts) {
|
|
25829
25928
|
const baseConditions = [];
|
|
@@ -25969,7 +26068,8 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
25969
26068
|
console.log(` Updated: ${updated}`);
|
|
25970
26069
|
console.log(` Unchanged: ${unchanged}`);
|
|
25971
26070
|
}
|
|
25972
|
-
function backfillProjectAnswerMentions(db, projectId) {
|
|
26071
|
+
function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
26072
|
+
const isDryRun = opts?.dryRun === true;
|
|
25973
26073
|
const project = db.select().from(projects).where(eq34(projects.id, projectId)).get();
|
|
25974
26074
|
if (!project) return { examined: 0, updated: 0, mentioned: 0 };
|
|
25975
26075
|
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, projectId)).all().map((row) => row.domain);
|
|
@@ -25977,8 +26077,11 @@ function backfillProjectAnswerMentions(db, projectId) {
|
|
|
25977
26077
|
const runIds = runRows.map((r) => r.id);
|
|
25978
26078
|
let examined = 0;
|
|
25979
26079
|
let updated = 0;
|
|
26080
|
+
let wouldUpdate = 0;
|
|
25980
26081
|
let mentioned = 0;
|
|
25981
|
-
if (runIds.length === 0)
|
|
26082
|
+
if (runIds.length === 0) {
|
|
26083
|
+
return isDryRun ? { examined, updated, wouldUpdate, mentioned } : { examined, updated, mentioned };
|
|
26084
|
+
}
|
|
25982
26085
|
const projectDomains = effectiveDomains({
|
|
25983
26086
|
canonicalDomain: project.canonicalDomain,
|
|
25984
26087
|
ownedDomains: parseJsonColumn(project.ownedDomains, [])
|
|
@@ -25998,7 +26101,7 @@ function backfillProjectAnswerMentions(db, projectId) {
|
|
|
25998
26101
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
25999
26102
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
26000
26103
|
rawResponse: querySnapshots.rawResponse
|
|
26001
|
-
}).from(querySnapshots).where(
|
|
26104
|
+
}).from(querySnapshots).where(inArray11(querySnapshots.runId, batchRunIds)).all();
|
|
26002
26105
|
const pendingUpdates = [];
|
|
26003
26106
|
for (const snapshot of snapshotRows) {
|
|
26004
26107
|
examined++;
|
|
@@ -26041,29 +26144,36 @@ function backfillProjectAnswerMentions(db, projectId) {
|
|
|
26041
26144
|
}
|
|
26042
26145
|
}
|
|
26043
26146
|
if (pendingUpdates.length > 0) {
|
|
26044
|
-
|
|
26045
|
-
|
|
26046
|
-
|
|
26047
|
-
|
|
26048
|
-
|
|
26049
|
-
|
|
26147
|
+
if (isDryRun) {
|
|
26148
|
+
wouldUpdate += pendingUpdates.length;
|
|
26149
|
+
} else {
|
|
26150
|
+
db.transaction((tx) => {
|
|
26151
|
+
for (const update of pendingUpdates) {
|
|
26152
|
+
tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
|
|
26153
|
+
}
|
|
26154
|
+
});
|
|
26155
|
+
updated += pendingUpdates.length;
|
|
26156
|
+
}
|
|
26050
26157
|
}
|
|
26051
26158
|
}
|
|
26052
|
-
return { examined, updated, mentioned };
|
|
26159
|
+
return isDryRun ? { examined, updated, wouldUpdate, mentioned } : { examined, updated, mentioned };
|
|
26053
26160
|
}
|
|
26054
26161
|
async function backfillAnswerMentionsCommand(opts) {
|
|
26055
26162
|
const config = loadConfig();
|
|
26056
26163
|
const db = createClient(config.database);
|
|
26057
26164
|
migrate(db);
|
|
26058
26165
|
const projectFilter = opts?.project?.trim();
|
|
26166
|
+
const isDryRun = opts?.dryRun === true;
|
|
26059
26167
|
const scopedProjects = projectFilter ? db.select().from(projects).where(eq34(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
26060
26168
|
let examined = 0;
|
|
26061
26169
|
let updated = 0;
|
|
26170
|
+
let wouldUpdate = 0;
|
|
26062
26171
|
let mentioned = 0;
|
|
26063
26172
|
for (const project of scopedProjects) {
|
|
26064
|
-
const result2 = backfillProjectAnswerMentions(db, project.id);
|
|
26173
|
+
const result2 = backfillProjectAnswerMentions(db, project.id, { dryRun: isDryRun });
|
|
26065
26174
|
examined += result2.examined;
|
|
26066
26175
|
updated += result2.updated;
|
|
26176
|
+
wouldUpdate += result2.wouldUpdate ?? 0;
|
|
26067
26177
|
mentioned += result2.mentioned;
|
|
26068
26178
|
}
|
|
26069
26179
|
const result = {
|
|
@@ -26073,16 +26183,29 @@ async function backfillAnswerMentionsCommand(opts) {
|
|
|
26073
26183
|
updated,
|
|
26074
26184
|
mentioned
|
|
26075
26185
|
};
|
|
26186
|
+
if (isDryRun) {
|
|
26187
|
+
result.dryRun = true;
|
|
26188
|
+
result.wouldUpdate = wouldUpdate;
|
|
26189
|
+
}
|
|
26076
26190
|
if (opts?.format === "json") {
|
|
26077
26191
|
console.log(JSON.stringify(result, null, 2));
|
|
26078
26192
|
return;
|
|
26079
26193
|
}
|
|
26080
|
-
console.log(
|
|
26081
|
-
|
|
26082
|
-
console.log(`
|
|
26083
|
-
console.log(`
|
|
26084
|
-
console.log(`
|
|
26085
|
-
|
|
26194
|
+
console.log(`Answer mentions backfill ${isDryRun ? "preview" : "complete"}.
|
|
26195
|
+
`);
|
|
26196
|
+
if (projectFilter) console.log(` Project: ${projectFilter}`);
|
|
26197
|
+
console.log(` Projects: ${scopedProjects.length}`);
|
|
26198
|
+
console.log(` Examined: ${examined}`);
|
|
26199
|
+
if (isDryRun) {
|
|
26200
|
+
console.log(` Would update: ${wouldUpdate}`);
|
|
26201
|
+
} else {
|
|
26202
|
+
console.log(` Updated: ${updated}`);
|
|
26203
|
+
}
|
|
26204
|
+
console.log(` Mentioned: ${mentioned}`);
|
|
26205
|
+
if (isDryRun) {
|
|
26206
|
+
console.log(`
|
|
26207
|
+
No DB writes performed. Re-run without --dry-run to apply.`);
|
|
26208
|
+
}
|
|
26086
26209
|
}
|
|
26087
26210
|
function readStoredGroundingSources(rawResponse) {
|
|
26088
26211
|
const envelope = parseJsonColumn(rawResponse, {});
|
|
@@ -26101,7 +26224,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
26101
26224
|
return result;
|
|
26102
26225
|
}
|
|
26103
26226
|
async function backfillInsightsCommand(project, opts) {
|
|
26104
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
26227
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-ZZP3TVZZ.js");
|
|
26105
26228
|
const config = loadConfig();
|
|
26106
26229
|
const db = createClient(config.database);
|
|
26107
26230
|
migrate(db);
|
|
@@ -26419,7 +26542,7 @@ var Scheduler = class {
|
|
|
26419
26542
|
};
|
|
26420
26543
|
|
|
26421
26544
|
// src/notifier.ts
|
|
26422
|
-
import { eq as eq36, desc as desc16, and as and26, inArray as
|
|
26545
|
+
import { eq as eq36, desc as desc16, and as and26, inArray as inArray12, or as or5 } from "drizzle-orm";
|
|
26423
26546
|
import crypto31 from "crypto";
|
|
26424
26547
|
var log10 = createLogger("Notifier");
|
|
26425
26548
|
var Notifier = class {
|
|
@@ -26572,13 +26695,13 @@ var Notifier = class {
|
|
|
26572
26695
|
provider: querySnapshots.provider,
|
|
26573
26696
|
location: querySnapshots.location,
|
|
26574
26697
|
citationState: querySnapshots.citationState
|
|
26575
|
-
}).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(
|
|
26698
|
+
}).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(inArray12(querySnapshots.runId, currentRunIds)).all();
|
|
26576
26699
|
const previousSnapshots = this.db.select({
|
|
26577
26700
|
queryId: querySnapshots.queryId,
|
|
26578
26701
|
provider: querySnapshots.provider,
|
|
26579
26702
|
location: querySnapshots.location,
|
|
26580
26703
|
citationState: querySnapshots.citationState
|
|
26581
|
-
}).from(querySnapshots).where(
|
|
26704
|
+
}).from(querySnapshots).where(inArray12(querySnapshots.runId, previousRunIds)).all();
|
|
26582
26705
|
const prevMap = /* @__PURE__ */ new Map();
|
|
26583
26706
|
for (const s of previousSnapshots) {
|
|
26584
26707
|
if (s.queryId == null) continue;
|
|
@@ -27091,7 +27214,7 @@ function resolveSessionProviderAndModel(config, opts) {
|
|
|
27091
27214
|
|
|
27092
27215
|
// src/agent/memory-store.ts
|
|
27093
27216
|
import crypto32 from "crypto";
|
|
27094
|
-
import { and as and27, desc as desc17, eq as eq38, like as like2, sql as
|
|
27217
|
+
import { and as and27, desc as desc17, eq as eq38, like as like2, sql as sql15 } from "drizzle-orm";
|
|
27095
27218
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
27096
27219
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
27097
27220
|
function rowToDto2(row) {
|
|
@@ -27177,7 +27300,7 @@ function writeCompactionNote(db, args) {
|
|
|
27177
27300
|
).orderBy(desc17(agentMemory.updatedAt)).all();
|
|
27178
27301
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
27179
27302
|
if (stale.length > 0) {
|
|
27180
|
-
tx.delete(agentMemory).where(
|
|
27303
|
+
tx.delete(agentMemory).where(sql15`${agentMemory.id} IN (${sql15.join(stale.map((s) => sql15`${s}`), sql15`, `)})`).run();
|
|
27181
27304
|
}
|
|
27182
27305
|
const row = tx.select().from(agentMemory).where(and27(eq38(agentMemory.projectId, args.projectId), eq38(agentMemory.key, key))).get();
|
|
27183
27306
|
if (row) inserted = rowToDto2(row);
|