@ainyc/canonry 4.36.0 → 4.39.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-BNTDaIuT.js} +105 -105
- package/assets/assets/index-J4ya3gFy.css +1 -0
- package/assets/index.html +2 -2
- package/dist/{chunk-JQQXMCQ7.js → chunk-DOUV45KI.js} +163 -11
- package/dist/{chunk-O7S623DL.js → chunk-JS6KRBBZ.js} +18 -0
- package/dist/{chunk-F2G67CIU.js → chunk-Q57CMOL6.js} +317 -205
- package/dist/cli.js +68 -12
- package/dist/index.js +3 -3
- package/dist/{intelligence-service-7AWRUNI2.js → intelligence-service-CIGYPPMR.js} +1 -1
- package/dist/mcp.js +1 -1
- package/package.json +8 -8
- 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,13 @@ import {
|
|
|
30
30
|
buildGapQueryScore,
|
|
31
31
|
buildInventory,
|
|
32
32
|
buildMentionCoverage,
|
|
33
|
+
buildMentionGapScore,
|
|
33
34
|
buildMentionLandscape,
|
|
34
35
|
buildMovementSummary,
|
|
35
36
|
buildOverviewCompetitors,
|
|
36
37
|
buildProviderScores,
|
|
37
38
|
buildRunHistory,
|
|
39
|
+
buildShareOfVoice,
|
|
38
40
|
buildVisibilityScore,
|
|
39
41
|
categorizeQueryByIntent,
|
|
40
42
|
ccReleaseSyncs,
|
|
@@ -74,7 +76,7 @@ import {
|
|
|
74
76
|
schedules,
|
|
75
77
|
trafficSources,
|
|
76
78
|
usageCounters
|
|
77
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-DOUV45KI.js";
|
|
78
80
|
import {
|
|
79
81
|
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
80
82
|
AGENT_PROVIDER_IDS,
|
|
@@ -926,7 +928,7 @@ function aliasArraysEqual(a, b) {
|
|
|
926
928
|
|
|
927
929
|
// ../api-routes/src/queries.ts
|
|
928
930
|
import crypto5 from "crypto";
|
|
929
|
-
import { eq as eq4 } from "drizzle-orm";
|
|
931
|
+
import { eq as eq4, inArray, sql as sql3 } from "drizzle-orm";
|
|
930
932
|
async function queryRoutes(app, opts) {
|
|
931
933
|
app.get("/projects/:name/queries", async (request, reply) => {
|
|
932
934
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -962,6 +964,36 @@ async function queryRoutes(app, opts) {
|
|
|
962
964
|
const rows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
|
|
963
965
|
return reply.send(rows.map((r) => ({ id: r.id, query: r.query, createdAt: r.createdAt })));
|
|
964
966
|
});
|
|
967
|
+
app.post("/projects/:name/queries/replace-preview", async (request, reply) => {
|
|
968
|
+
const project = resolveProject(app.db, request.params.name);
|
|
969
|
+
const body = request.body;
|
|
970
|
+
if (!body || !Array.isArray(body.queries)) {
|
|
971
|
+
throw validationError('Body must contain a "queries" array');
|
|
972
|
+
}
|
|
973
|
+
const currentRows = app.db.select().from(queries).where(eq4(queries.projectId, project.id)).all();
|
|
974
|
+
const currentTexts = currentRows.map((r) => r.query);
|
|
975
|
+
const currentSet = new Set(currentTexts);
|
|
976
|
+
const proposedSet = new Set(body.queries);
|
|
977
|
+
const removed = currentTexts.filter((q) => !proposedSet.has(q));
|
|
978
|
+
const added = body.queries.filter((q) => !currentSet.has(q));
|
|
979
|
+
const unchanged = currentTexts.filter((q) => proposedSet.has(q));
|
|
980
|
+
const currentIds = currentRows.map((r) => r.id);
|
|
981
|
+
let snapshotsDetached = 0;
|
|
982
|
+
let affectedQueries = 0;
|
|
983
|
+
if (currentIds.length > 0) {
|
|
984
|
+
const snapshotCount = app.db.select({ n: sql3`count(*)` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
|
|
985
|
+
snapshotsDetached = snapshotCount?.n ?? 0;
|
|
986
|
+
const distinctAffected = app.db.select({ n: sql3`count(distinct ${querySnapshots.queryId})` }).from(querySnapshots).where(inArray(querySnapshots.queryId, currentIds)).get();
|
|
987
|
+
affectedQueries = distinctAffected?.n ?? 0;
|
|
988
|
+
}
|
|
989
|
+
return reply.send({
|
|
990
|
+
project: { id: project.id, name: project.name },
|
|
991
|
+
current: currentTexts,
|
|
992
|
+
proposed: body.queries,
|
|
993
|
+
diff: { added, removed, unchanged },
|
|
994
|
+
snapshotImpact: { affectedQueries, snapshotsDetached }
|
|
995
|
+
});
|
|
996
|
+
});
|
|
965
997
|
app.delete("/projects/:name/queries", async (request, reply) => {
|
|
966
998
|
const project = resolveProject(app.db, request.params.name);
|
|
967
999
|
const body = request.body;
|
|
@@ -1325,7 +1357,7 @@ function parseCompetitorBatch(value) {
|
|
|
1325
1357
|
|
|
1326
1358
|
// ../api-routes/src/runs.ts
|
|
1327
1359
|
import crypto8 from "crypto";
|
|
1328
|
-
import { and as and2, eq as eq7, asc, desc, or as or2, sql as
|
|
1360
|
+
import { and as and2, eq as eq7, asc, desc, or as or2, sql as sql4 } from "drizzle-orm";
|
|
1329
1361
|
|
|
1330
1362
|
// ../api-routes/src/run-queue.ts
|
|
1331
1363
|
import crypto7 from "crypto";
|
|
@@ -1497,7 +1529,7 @@ async function runRoutes(app, opts) {
|
|
|
1497
1529
|
});
|
|
1498
1530
|
app.get("/projects/:name/runs/latest", async (request, reply) => {
|
|
1499
1531
|
const project = resolveProject(app.db, request.params.name);
|
|
1500
|
-
const countRow = app.db.select({ count:
|
|
1532
|
+
const countRow = app.db.select({ count: sql4`count(*)` }).from(runs).where(eq7(runs.projectId, project.id)).get();
|
|
1501
1533
|
const totalRuns = countRow?.count ?? 0;
|
|
1502
1534
|
const latestRun = app.db.select().from(runs).where(eq7(runs.projectId, project.id)).orderBy(desc(runs.createdAt), desc(runs.id)).limit(1).get();
|
|
1503
1535
|
if (!latestRun) {
|
|
@@ -2226,7 +2258,7 @@ function normalizeCompetitorList2(domains) {
|
|
|
2226
2258
|
}
|
|
2227
2259
|
|
|
2228
2260
|
// ../api-routes/src/history.ts
|
|
2229
|
-
import { eq as eq9, desc as desc2, inArray } from "drizzle-orm";
|
|
2261
|
+
import { eq as eq9, desc as desc2, inArray as inArray2 } from "drizzle-orm";
|
|
2230
2262
|
|
|
2231
2263
|
// ../api-routes/src/notification-redaction.ts
|
|
2232
2264
|
var REDACTED_URL = {
|
|
@@ -2302,7 +2334,7 @@ async function historyRoutes(app) {
|
|
|
2302
2334
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
2303
2335
|
location: querySnapshots.location,
|
|
2304
2336
|
createdAt: querySnapshots.createdAt
|
|
2305
|
-
}).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(
|
|
2337
|
+
}).from(querySnapshots).leftJoin(queries, eq9(querySnapshots.queryId, queries.id)).where(inArray2(querySnapshots.runId, projectRuns.map((r) => r.id))).orderBy(desc2(querySnapshots.createdAt)).all();
|
|
2306
2338
|
const locationFilter = request.query.location;
|
|
2307
2339
|
const filtered = locationFilter !== void 0 ? allSnapshots.filter((s) => s.location === (locationFilter || null)) : allSnapshots;
|
|
2308
2340
|
const total = filtered.length;
|
|
@@ -2337,7 +2369,7 @@ async function historyRoutes(app) {
|
|
|
2337
2369
|
return reply.send([]);
|
|
2338
2370
|
}
|
|
2339
2371
|
const runIds = new Set(projectRuns.map((r) => r.id));
|
|
2340
|
-
const rawSnapshots = app.db.select().from(querySnapshots).where(
|
|
2372
|
+
const rawSnapshots = app.db.select().from(querySnapshots).where(inArray2(querySnapshots.runId, [...runIds])).all();
|
|
2341
2373
|
const timelineLocationFilter = request.query.location;
|
|
2342
2374
|
const filteredSnapshots = timelineLocationFilter !== void 0 ? rawSnapshots.filter((s) => s.location === (timelineLocationFilter || null)) : rawSnapshots;
|
|
2343
2375
|
const allSnapshots = filteredSnapshots.map((snapshot) => ({
|
|
@@ -2517,7 +2549,7 @@ function formatAuditEntry(row) {
|
|
|
2517
2549
|
}
|
|
2518
2550
|
|
|
2519
2551
|
// ../api-routes/src/analytics.ts
|
|
2520
|
-
import { eq as eq10, desc as desc3, inArray as
|
|
2552
|
+
import { eq as eq10, desc as desc3, inArray as inArray3 } from "drizzle-orm";
|
|
2521
2553
|
async function analyticsRoutes(app) {
|
|
2522
2554
|
app.get("/projects/:name/analytics/metrics", async (request, reply) => {
|
|
2523
2555
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -2544,7 +2576,7 @@ async function analyticsRoutes(app) {
|
|
|
2544
2576
|
answerMentioned: querySnapshots.answerMentioned,
|
|
2545
2577
|
answerText: querySnapshots.answerText,
|
|
2546
2578
|
createdAt: querySnapshots.createdAt
|
|
2547
|
-
}).from(querySnapshots).where(
|
|
2579
|
+
}).from(querySnapshots).where(inArray3(querySnapshots.runId, runIds)).all());
|
|
2548
2580
|
const allSnapshots = rawSnapshots.map((s) => ({
|
|
2549
2581
|
...s,
|
|
2550
2582
|
resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
|
|
@@ -2589,7 +2621,7 @@ async function analyticsRoutes(app) {
|
|
|
2589
2621
|
citationState: querySnapshots.citationState,
|
|
2590
2622
|
answerMentioned: querySnapshots.answerMentioned,
|
|
2591
2623
|
answerText: querySnapshots.answerText
|
|
2592
|
-
}).from(querySnapshots).where(
|
|
2624
|
+
}).from(querySnapshots).where(inArray3(querySnapshots.runId, windowRunIds)).all());
|
|
2593
2625
|
for (const s of allWindowSnaps) {
|
|
2594
2626
|
const timePoint = runIdToCreatedAt.get(s.runId) ?? s.runId;
|
|
2595
2627
|
let entry = consistencyMap.get(s.queryId);
|
|
@@ -2610,7 +2642,7 @@ async function analyticsRoutes(app) {
|
|
|
2610
2642
|
answerMentioned: querySnapshots.answerMentioned,
|
|
2611
2643
|
answerText: querySnapshots.answerText,
|
|
2612
2644
|
competitorOverlap: querySnapshots.competitorOverlap
|
|
2613
|
-
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(
|
|
2645
|
+
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, latestGroupRunIds)).all());
|
|
2614
2646
|
const snapshots = rawSnapshots.map((s) => ({
|
|
2615
2647
|
...s,
|
|
2616
2648
|
resolvedMentioned: resolveSnapshotAnswerMentioned(s, project)
|
|
@@ -2705,7 +2737,7 @@ async function analyticsRoutes(app) {
|
|
|
2705
2737
|
queryId: querySnapshots.queryId,
|
|
2706
2738
|
query: queries.query,
|
|
2707
2739
|
rawResponse: querySnapshots.rawResponse
|
|
2708
|
-
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(
|
|
2740
|
+
}).from(querySnapshots).leftJoin(queries, eq10(querySnapshots.queryId, queries.id)).where(inArray3(querySnapshots.runId, windowRunIds)).all();
|
|
2709
2741
|
const overallCounts = /* @__PURE__ */ new Map();
|
|
2710
2742
|
const byQuery = {};
|
|
2711
2743
|
for (const snap of snapshots) {
|
|
@@ -2864,7 +2896,7 @@ function buildCategoryCounts(counts) {
|
|
|
2864
2896
|
}
|
|
2865
2897
|
|
|
2866
2898
|
// ../api-routes/src/intelligence.ts
|
|
2867
|
-
import { eq as eq11, desc as desc4, and as and4, inArray as
|
|
2899
|
+
import { eq as eq11, desc as desc4, and as and4, inArray as inArray4 } from "drizzle-orm";
|
|
2868
2900
|
function emptyHealthSnapshot(projectId) {
|
|
2869
2901
|
return {
|
|
2870
2902
|
id: `no-data:${projectId}`,
|
|
@@ -2980,14 +3012,14 @@ async function intelligenceRoutes(app) {
|
|
|
2980
3012
|
const projectVisRuns = app.db.select({ id: runs.id, createdAt: runs.createdAt }).from(runs).where(and4(
|
|
2981
3013
|
eq11(runs.projectId, project.id),
|
|
2982
3014
|
eq11(runs.kind, RunKinds["answer-visibility"]),
|
|
2983
|
-
|
|
3015
|
+
inArray4(runs.status, [RunStatuses.completed, RunStatuses.partial])
|
|
2984
3016
|
)).orderBy(desc4(runs.createdAt), desc4(runs.id)).all();
|
|
2985
3017
|
const latestGroup = groupRunsByCreatedAt(projectVisRuns)[0] ?? [];
|
|
2986
3018
|
const latestGroupRunIds = latestGroup.map((r) => r.id);
|
|
2987
3019
|
if (latestGroupRunIds.length > 0) {
|
|
2988
3020
|
const groupRows = app.db.select().from(healthSnapshots).where(and4(
|
|
2989
3021
|
eq11(healthSnapshots.projectId, project.id),
|
|
2990
|
-
|
|
3022
|
+
inArray4(healthSnapshots.runId, latestGroupRunIds)
|
|
2991
3023
|
)).all();
|
|
2992
3024
|
if (groupRows.length > 0) {
|
|
2993
3025
|
return reply.send(aggregateHealthSnapshots(project.id, groupRows));
|
|
@@ -3009,7 +3041,7 @@ async function intelligenceRoutes(app) {
|
|
|
3009
3041
|
}
|
|
3010
3042
|
|
|
3011
3043
|
// ../api-routes/src/report.ts
|
|
3012
|
-
import { and as and6, desc as desc6, eq as eq13, gte, inArray as
|
|
3044
|
+
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
3045
|
|
|
3014
3046
|
// ../api-routes/src/report-renderer.ts
|
|
3015
3047
|
var COLORS = {
|
|
@@ -5245,7 +5277,7 @@ function renderReportHtml(report, opts = {}) {
|
|
|
5245
5277
|
}
|
|
5246
5278
|
|
|
5247
5279
|
// ../api-routes/src/content-data.ts
|
|
5248
|
-
import { and as and5, eq as eq12, desc as desc5, inArray as
|
|
5280
|
+
import { and as and5, eq as eq12, desc as desc5, inArray as inArray5 } from "drizzle-orm";
|
|
5249
5281
|
var RECENT_RUNS_WINDOW = 5;
|
|
5250
5282
|
function loadOrchestratorInput(db, project, locationFilter = void 0) {
|
|
5251
5283
|
const projectId = project.id;
|
|
@@ -5375,7 +5407,7 @@ function listRecentAnswerVisibilityRunIds(db, projectId, limit, locationFilter)
|
|
|
5375
5407
|
// Queued/running/failed/cancelled runs may have partial or no
|
|
5376
5408
|
// snapshots; including them risks pointing latestRunId at a run with
|
|
5377
5409
|
// no usable evidence.
|
|
5378
|
-
|
|
5410
|
+
inArray5(runs.status, [RunStatuses.completed, RunStatuses.partial])
|
|
5379
5411
|
)
|
|
5380
5412
|
).orderBy(desc5(runs.createdAt)).all();
|
|
5381
5413
|
const filtered = locationFilter === void 0 ? rows : rows.filter((r) => (r.location ?? null) === locationFilter);
|
|
@@ -5417,7 +5449,7 @@ function buildCandidateQueries(opts) {
|
|
|
5417
5449
|
const queryRows = opts.db.select({ id: queries.id, text: queries.query }).from(queries).where(eq12(queries.projectId, opts.projectId)).all();
|
|
5418
5450
|
const queryIdByText = new Map(queryRows.map((r) => [r.text, r.id]));
|
|
5419
5451
|
const candidateQueryIds = opts.candidateQueryStrings.map((q) => queryIdByText.get(q)).filter((id) => Boolean(id));
|
|
5420
|
-
const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(
|
|
5452
|
+
const snapshotRows = filterTrackedSnapshots(opts.db.select().from(querySnapshots).where(inArray5(querySnapshots.runId, opts.recentRunIds)).all()).filter((r) => candidateQueryIds.includes(r.queryId));
|
|
5421
5453
|
const snapshotsByQuery = /* @__PURE__ */ new Map();
|
|
5422
5454
|
for (const row of snapshotRows) {
|
|
5423
5455
|
const list = snapshotsByQuery.get(row.queryId) ?? [];
|
|
@@ -5635,7 +5667,7 @@ function loadSnapshotsForRun(db, runId) {
|
|
|
5635
5667
|
}
|
|
5636
5668
|
function loadSnapshotsForRunIds(db, runIds) {
|
|
5637
5669
|
if (runIds.length === 0) return [];
|
|
5638
|
-
const rows = db.select().from(querySnapshots).where(
|
|
5670
|
+
const rows = db.select().from(querySnapshots).where(inArray6(querySnapshots.runId, [...runIds])).all();
|
|
5639
5671
|
return rows.filter((r) => r.queryId !== null).map((r) => ({
|
|
5640
5672
|
id: r.id,
|
|
5641
5673
|
runId: r.runId,
|
|
@@ -5885,7 +5917,7 @@ function buildAiReferrals(db, projectId) {
|
|
|
5885
5917
|
return { totalSessions: total, totalUsers, bySource, trend, topLandingPages };
|
|
5886
5918
|
}
|
|
5887
5919
|
function nonSubresourceReferralPathCondition() {
|
|
5888
|
-
return
|
|
5920
|
+
return sql5`
|
|
5889
5921
|
LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/_next/static/%'
|
|
5890
5922
|
AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/assets/%'
|
|
5891
5923
|
AND LOWER(${aiReferralEventsHourly.landingPathNormalized}) NOT LIKE '/static/%'
|
|
@@ -5924,7 +5956,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5924
5956
|
const priorStart = new Date(priorStartMs).toISOString();
|
|
5925
5957
|
const trendStart = new Date(trendStartMs).toISOString();
|
|
5926
5958
|
const sumVerifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
5927
|
-
db.select({ total:
|
|
5959
|
+
db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
5928
5960
|
and6(
|
|
5929
5961
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
5930
5962
|
eq13(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
@@ -5934,7 +5966,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5934
5966
|
).get()?.total ?? 0
|
|
5935
5967
|
);
|
|
5936
5968
|
const sumUnverifiedCrawlers = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
5937
|
-
db.select({ total:
|
|
5969
|
+
db.select({ total: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
5938
5970
|
and6(
|
|
5939
5971
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
5940
5972
|
ne(crawlerEventsHourly.verificationStatus, VerificationStatuses.verified),
|
|
@@ -5944,7 +5976,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5944
5976
|
).get()?.total ?? 0
|
|
5945
5977
|
);
|
|
5946
5978
|
const sumReferrals = (windowStartIso, windowEndIso, exclusiveEnd = false) => Number(
|
|
5947
|
-
db.select({ total:
|
|
5979
|
+
db.select({ total: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
5948
5980
|
and6(
|
|
5949
5981
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
5950
5982
|
nonSubresourceReferralPathCondition(),
|
|
@@ -5962,7 +5994,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5962
5994
|
const crawlerByOperatorRows = db.select({
|
|
5963
5995
|
operator: crawlerEventsHourly.operator,
|
|
5964
5996
|
verificationStatus: crawlerEventsHourly.verificationStatus,
|
|
5965
|
-
hits:
|
|
5997
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
5966
5998
|
}).from(crawlerEventsHourly).where(
|
|
5967
5999
|
and6(
|
|
5968
6000
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -5972,7 +6004,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5972
6004
|
).groupBy(crawlerEventsHourly.operator, crawlerEventsHourly.verificationStatus).all();
|
|
5973
6005
|
const crawlerByOperatorPriorRows = db.select({
|
|
5974
6006
|
operator: crawlerEventsHourly.operator,
|
|
5975
|
-
hits:
|
|
6007
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
5976
6008
|
}).from(crawlerEventsHourly).where(
|
|
5977
6009
|
and6(
|
|
5978
6010
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -5983,7 +6015,7 @@ function buildServerActivity(db, projectId) {
|
|
|
5983
6015
|
).groupBy(crawlerEventsHourly.operator).all();
|
|
5984
6016
|
const referralByOperatorRows = db.select({
|
|
5985
6017
|
operator: aiReferralEventsHourly.operator,
|
|
5986
|
-
hits:
|
|
6018
|
+
hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
|
|
5987
6019
|
}).from(aiReferralEventsHourly).where(
|
|
5988
6020
|
and6(
|
|
5989
6021
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6023,8 +6055,8 @@ function buildServerActivity(db, projectId) {
|
|
|
6023
6055
|
);
|
|
6024
6056
|
const topPathsRows = db.select({
|
|
6025
6057
|
path: crawlerEventsHourly.pathNormalized,
|
|
6026
|
-
hits:
|
|
6027
|
-
operators:
|
|
6058
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`,
|
|
6059
|
+
operators: sql5`COUNT(DISTINCT ${crawlerEventsHourly.operator})`
|
|
6028
6060
|
}).from(crawlerEventsHourly).where(
|
|
6029
6061
|
and6(
|
|
6030
6062
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -6032,7 +6064,7 @@ function buildServerActivity(db, projectId) {
|
|
|
6032
6064
|
gte(crawlerEventsHourly.tsHour, headlineStart),
|
|
6033
6065
|
lte(crawlerEventsHourly.tsHour, headlineEnd)
|
|
6034
6066
|
)
|
|
6035
|
-
).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(
|
|
6067
|
+
).groupBy(crawlerEventsHourly.pathNormalized).orderBy(desc6(sql5`SUM(${crawlerEventsHourly.hits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
|
|
6036
6068
|
const topCrawledPaths = topPathsRows.map((r) => ({
|
|
6037
6069
|
path: r.path,
|
|
6038
6070
|
verifiedHits: Number(r.hits),
|
|
@@ -6040,8 +6072,8 @@ function buildServerActivity(db, projectId) {
|
|
|
6040
6072
|
}));
|
|
6041
6073
|
const referralProductsRows = db.select({
|
|
6042
6074
|
product: aiReferralEventsHourly.product,
|
|
6043
|
-
arrivals:
|
|
6044
|
-
landingPaths:
|
|
6075
|
+
arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
6076
|
+
landingPaths: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.landingPathNormalized})`
|
|
6045
6077
|
}).from(aiReferralEventsHourly).where(
|
|
6046
6078
|
and6(
|
|
6047
6079
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6049,7 +6081,7 @@ function buildServerActivity(db, projectId) {
|
|
|
6049
6081
|
gte(aiReferralEventsHourly.tsHour, headlineStart),
|
|
6050
6082
|
lte(aiReferralEventsHourly.tsHour, headlineEnd)
|
|
6051
6083
|
)
|
|
6052
|
-
).groupBy(aiReferralEventsHourly.product).orderBy(desc6(
|
|
6084
|
+
).groupBy(aiReferralEventsHourly.product).orderBy(desc6(sql5`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).all();
|
|
6053
6085
|
const referralProducts = referralProductsRows.map((r) => ({
|
|
6054
6086
|
product: r.product,
|
|
6055
6087
|
arrivals: Number(r.arrivals),
|
|
@@ -6057,8 +6089,8 @@ function buildServerActivity(db, projectId) {
|
|
|
6057
6089
|
}));
|
|
6058
6090
|
const topReferralRows = db.select({
|
|
6059
6091
|
path: aiReferralEventsHourly.landingPathNormalized,
|
|
6060
|
-
arrivals:
|
|
6061
|
-
products:
|
|
6092
|
+
arrivals: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`,
|
|
6093
|
+
products: sql5`COUNT(DISTINCT ${aiReferralEventsHourly.product})`
|
|
6062
6094
|
}).from(aiReferralEventsHourly).where(
|
|
6063
6095
|
and6(
|
|
6064
6096
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6066,15 +6098,15 @@ function buildServerActivity(db, projectId) {
|
|
|
6066
6098
|
gte(aiReferralEventsHourly.tsHour, headlineStart),
|
|
6067
6099
|
lte(aiReferralEventsHourly.tsHour, headlineEnd)
|
|
6068
6100
|
)
|
|
6069
|
-
).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(
|
|
6101
|
+
).groupBy(aiReferralEventsHourly.landingPathNormalized).orderBy(desc6(sql5`SUM(${aiReferralEventsHourly.sessionsOrHits})`)).limit(SERVER_ACTIVITY_TOP_PATHS_LIMIT).all();
|
|
6070
6102
|
const topReferralLandingPaths = topReferralRows.map((r) => ({
|
|
6071
6103
|
path: r.path,
|
|
6072
6104
|
arrivals: Number(r.arrivals),
|
|
6073
6105
|
distinctProducts: Number(r.products)
|
|
6074
6106
|
}));
|
|
6075
6107
|
const crawlerTrendRows = db.select({
|
|
6076
|
-
date:
|
|
6077
|
-
hits:
|
|
6108
|
+
date: sql5`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`,
|
|
6109
|
+
hits: sql5`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)`
|
|
6078
6110
|
}).from(crawlerEventsHourly).where(
|
|
6079
6111
|
and6(
|
|
6080
6112
|
eq13(crawlerEventsHourly.projectId, projectId),
|
|
@@ -6082,10 +6114,10 @@ function buildServerActivity(db, projectId) {
|
|
|
6082
6114
|
gte(crawlerEventsHourly.tsHour, trendStart),
|
|
6083
6115
|
lte(crawlerEventsHourly.tsHour, headlineEnd)
|
|
6084
6116
|
)
|
|
6085
|
-
).groupBy(
|
|
6117
|
+
).groupBy(sql5`SUBSTR(${crawlerEventsHourly.tsHour}, 1, 10)`).all();
|
|
6086
6118
|
const referralTrendRows = db.select({
|
|
6087
|
-
date:
|
|
6088
|
-
hits:
|
|
6119
|
+
date: sql5`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`,
|
|
6120
|
+
hits: sql5`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)`
|
|
6089
6121
|
}).from(aiReferralEventsHourly).where(
|
|
6090
6122
|
and6(
|
|
6091
6123
|
eq13(aiReferralEventsHourly.projectId, projectId),
|
|
@@ -6093,7 +6125,7 @@ function buildServerActivity(db, projectId) {
|
|
|
6093
6125
|
gte(aiReferralEventsHourly.tsHour, trendStart),
|
|
6094
6126
|
lte(aiReferralEventsHourly.tsHour, headlineEnd)
|
|
6095
6127
|
)
|
|
6096
|
-
).groupBy(
|
|
6128
|
+
).groupBy(sql5`SUBSTR(${aiReferralEventsHourly.tsHour}, 1, 10)`).all();
|
|
6097
6129
|
const dailyTrendMap = /* @__PURE__ */ new Map();
|
|
6098
6130
|
for (const r of crawlerTrendRows) {
|
|
6099
6131
|
const e = dailyTrendMap.get(r.date) ?? { verifiedCrawlerHits: 0, referralArrivals: 0 };
|
|
@@ -6215,7 +6247,7 @@ function buildInsightList(db, projectId, locationFilter) {
|
|
|
6215
6247
|
)
|
|
6216
6248
|
).orderBy(desc6(runs.createdAt)).all().filter((r) => locationFilter === void 0 || (r.location ?? null) === locationFilter).slice(0, INSIGHT_LOOKBACK_RUNS).map((r) => r.id);
|
|
6217
6249
|
if (recentRunIds.length === 0) return [];
|
|
6218
|
-
const rows = db.select().from(insights).where(and6(eq13(insights.projectId, projectId),
|
|
6250
|
+
const rows = db.select().from(insights).where(and6(eq13(insights.projectId, projectId), inArray6(insights.runId, recentRunIds))).orderBy(desc6(insights.createdAt)).all();
|
|
6219
6251
|
const severityRank = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
6220
6252
|
const flat = rows.filter((r) => !r.dismissed).map((r) => {
|
|
6221
6253
|
const recommendation = parseJsonColumn(r.recommendation, null);
|
|
@@ -6988,7 +7020,7 @@ async function reportRoutes(app) {
|
|
|
6988
7020
|
}
|
|
6989
7021
|
|
|
6990
7022
|
// ../api-routes/src/citations.ts
|
|
6991
|
-
import { eq as eq14, inArray as
|
|
7023
|
+
import { eq as eq14, inArray as inArray7 } from "drizzle-orm";
|
|
6992
7024
|
async function citationRoutes(app) {
|
|
6993
7025
|
app.get("/projects/:name/citations/visibility", async (request, reply) => {
|
|
6994
7026
|
const project = resolveProject(app.db, request.params.name);
|
|
@@ -7012,7 +7044,7 @@ async function citationRoutes(app) {
|
|
|
7012
7044
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
7013
7045
|
answerMentioned: querySnapshots.answerMentioned,
|
|
7014
7046
|
createdAt: querySnapshots.createdAt
|
|
7015
|
-
}).from(querySnapshots).where(
|
|
7047
|
+
}).from(querySnapshots).where(inArray7(querySnapshots.runId, projectRuns.map((r) => r.id))).all();
|
|
7016
7048
|
if (rawSnapshots.length === 0) {
|
|
7017
7049
|
return reply.send(emptyCitationVisibility("no-runs-yet"));
|
|
7018
7050
|
}
|
|
@@ -7153,7 +7185,7 @@ function normalizeDomain2(domain) {
|
|
|
7153
7185
|
}
|
|
7154
7186
|
|
|
7155
7187
|
// ../api-routes/src/composites.ts
|
|
7156
|
-
import { eq as eq15, and as and7, desc as desc7, sql as
|
|
7188
|
+
import { eq as eq15, and as and7, desc as desc7, sql as sql6, like, or as or4, inArray as inArray8 } from "drizzle-orm";
|
|
7157
7189
|
var TOP_INSIGHT_LIMIT = 5;
|
|
7158
7190
|
var SEARCH_HIT_HARD_LIMIT = 50;
|
|
7159
7191
|
var SEARCH_SNIPPET_RADIUS = 80;
|
|
@@ -7205,10 +7237,19 @@ async function compositeRoutes(app) {
|
|
|
7205
7237
|
const projectQueries = app.db.select({ id: queries.id, query: queries.query }).from(queries).where(eq15(queries.projectId, project.id)).all();
|
|
7206
7238
|
const queryLookup = { byId: new Map(projectQueries.map((q) => [q.id, q.query])) };
|
|
7207
7239
|
const configuredApiProviders = parseJsonColumn(project.providers, []).filter((p) => !p.startsWith("cdp:"));
|
|
7240
|
+
const projectDomains = effectiveDomains({
|
|
7241
|
+
canonicalDomain: project.canonicalDomain,
|
|
7242
|
+
ownedDomains: parseJsonColumn(project.ownedDomains, [])
|
|
7243
|
+
});
|
|
7208
7244
|
const scores = {
|
|
7209
7245
|
mention: buildMentionCoverage(latestSnapshots, { configuredApiProviders }),
|
|
7210
7246
|
visibility: buildVisibilityScore(latestSnapshots, { configuredApiProviders }),
|
|
7247
|
+
shareOfVoice: buildShareOfVoice(latestSnapshots, {
|
|
7248
|
+
projectDomains,
|
|
7249
|
+
competitorDomains: competitorRows.map((c) => c.domain)
|
|
7250
|
+
}),
|
|
7211
7251
|
gapQueries: buildGapQueryScore(latestSnapshots),
|
|
7252
|
+
mentionGaps: buildMentionGapScore(latestSnapshots),
|
|
7212
7253
|
indexCoverage: buildIndexCoverageScore(app, project.id),
|
|
7213
7254
|
competitorPressure: buildCompetitorPressureScore(
|
|
7214
7255
|
latestSnapshots,
|
|
@@ -7217,7 +7258,9 @@ async function compositeRoutes(app) {
|
|
|
7217
7258
|
),
|
|
7218
7259
|
runStatus: buildRunStatusScore(allRuns)
|
|
7219
7260
|
};
|
|
7220
|
-
const movementSummary = buildMovementSummary(latestSnapshots, previousSnapshots
|
|
7261
|
+
const movementSummary = buildMovementSummary(latestSnapshots, previousSnapshots, {
|
|
7262
|
+
queryLookup: queryLookup.byId
|
|
7263
|
+
});
|
|
7221
7264
|
const providerScores = buildProviderScores(latestSnapshots);
|
|
7222
7265
|
const overviewCompetitors = buildOverviewCompetitors(
|
|
7223
7266
|
latestSnapshots,
|
|
@@ -7271,9 +7314,9 @@ async function compositeRoutes(app) {
|
|
|
7271
7314
|
and7(
|
|
7272
7315
|
eq15(queries.projectId, project.id),
|
|
7273
7316
|
or4(
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7317
|
+
sql6`${querySnapshots.answerText} LIKE ${pattern} ESCAPE '\\'`,
|
|
7318
|
+
sql6`${querySnapshots.citedDomains} LIKE ${pattern} ESCAPE '\\'`,
|
|
7319
|
+
sql6`${querySnapshots.rawResponse} LIKE ${pattern} ESCAPE '\\'`,
|
|
7277
7320
|
like(queries.query, pattern)
|
|
7278
7321
|
)
|
|
7279
7322
|
)
|
|
@@ -7284,8 +7327,8 @@ async function compositeRoutes(app) {
|
|
|
7284
7327
|
or4(
|
|
7285
7328
|
like(insights.title, pattern),
|
|
7286
7329
|
like(insights.query, pattern),
|
|
7287
|
-
|
|
7288
|
-
|
|
7330
|
+
sql6`${insights.recommendation} LIKE ${pattern} ESCAPE '\\'`,
|
|
7331
|
+
sql6`${insights.cause} LIKE ${pattern} ESCAPE '\\'`
|
|
7289
7332
|
)
|
|
7290
7333
|
)
|
|
7291
7334
|
).orderBy(desc7(insights.createdAt)).limit(limit + 1).all();
|
|
@@ -7360,7 +7403,7 @@ function loadSnapshotsByRunIds(app, runIds) {
|
|
|
7360
7403
|
answerMentioned: querySnapshots.answerMentioned,
|
|
7361
7404
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
7362
7405
|
citedDomains: querySnapshots.citedDomains
|
|
7363
|
-
}).from(querySnapshots).where(
|
|
7406
|
+
}).from(querySnapshots).where(inArray8(querySnapshots.runId, [...runIds])).all());
|
|
7364
7407
|
for (const row of rows) {
|
|
7365
7408
|
const list = result.get(row.runId) ?? [];
|
|
7366
7409
|
list.push({
|
|
@@ -8139,6 +8182,32 @@ var routeCatalog = [
|
|
|
8139
8182
|
200: { description: "Queries appended." }
|
|
8140
8183
|
}
|
|
8141
8184
|
},
|
|
8185
|
+
{
|
|
8186
|
+
method: "post",
|
|
8187
|
+
path: "/api/v1/projects/{name}/queries/replace-preview",
|
|
8188
|
+
summary: "Preview the impact of replacing tracked queries",
|
|
8189
|
+
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).",
|
|
8190
|
+
tags: ["queries"],
|
|
8191
|
+
parameters: [nameParameter],
|
|
8192
|
+
requestBody: {
|
|
8193
|
+
required: true,
|
|
8194
|
+
content: {
|
|
8195
|
+
"application/json": {
|
|
8196
|
+
schema: {
|
|
8197
|
+
type: "object",
|
|
8198
|
+
required: ["queries"],
|
|
8199
|
+
properties: {
|
|
8200
|
+
queries: stringArraySchema
|
|
8201
|
+
}
|
|
8202
|
+
}
|
|
8203
|
+
}
|
|
8204
|
+
}
|
|
8205
|
+
},
|
|
8206
|
+
responses: {
|
|
8207
|
+
200: { description: "Replace preview returned." },
|
|
8208
|
+
404: { description: "Project not found." }
|
|
8209
|
+
}
|
|
8210
|
+
},
|
|
8142
8211
|
{
|
|
8143
8212
|
method: "post",
|
|
8144
8213
|
path: "/api/v1/projects/{name}/queries/generate",
|
|
@@ -11418,7 +11487,7 @@ function formatNotification(row) {
|
|
|
11418
11487
|
|
|
11419
11488
|
// ../api-routes/src/google.ts
|
|
11420
11489
|
import crypto14 from "crypto";
|
|
11421
|
-
import { eq as eq18, and as and9, desc as desc8, sql as
|
|
11490
|
+
import { eq as eq18, and as and9, desc as desc8, sql as sql7 } from "drizzle-orm";
|
|
11422
11491
|
|
|
11423
11492
|
// ../integration-google/src/constants.ts
|
|
11424
11493
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
@@ -12644,11 +12713,11 @@ async function googleRoutes(app, opts) {
|
|
|
12644
12713
|
const { startDate, endDate, query, page, limit, offset } = request.query;
|
|
12645
12714
|
const cutoffDate = !startDate ? windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null : null;
|
|
12646
12715
|
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(
|
|
12716
|
+
if (startDate) conditions.push(sql7`${gscSearchData.date} >= ${startDate}`);
|
|
12717
|
+
else if (cutoffDate) conditions.push(sql7`${gscSearchData.date} >= ${cutoffDate}`);
|
|
12718
|
+
if (endDate) conditions.push(sql7`${gscSearchData.date} <= ${endDate}`);
|
|
12719
|
+
if (query) conditions.push(sql7`${gscSearchData.query} LIKE ${"%" + query + "%"}`);
|
|
12720
|
+
if (page) conditions.push(sql7`${gscSearchData.page} LIKE ${"%" + page + "%"}`);
|
|
12652
12721
|
const limitVal = Math.max(parseInt(limit ?? "500", 10) || 0, 1);
|
|
12653
12722
|
const offsetVal = Math.max(parseInt(offset ?? "0", 10) || 0, 0);
|
|
12654
12723
|
const rows = app.db.select().from(gscSearchData).where(and9(...conditions)).orderBy(desc8(gscSearchData.date)).limit(limitVal).offset(offsetVal).all();
|
|
@@ -13893,7 +13962,7 @@ async function cdpRoutes(app, opts) {
|
|
|
13893
13962
|
|
|
13894
13963
|
// ../api-routes/src/ga.ts
|
|
13895
13964
|
import crypto16 from "crypto";
|
|
13896
|
-
import { eq as eq21, desc as desc10, and as and12, sql as
|
|
13965
|
+
import { eq as eq21, desc as desc10, and as and12, sql as sql8 } from "drizzle-orm";
|
|
13897
13966
|
function gaLog(level, action, ctx) {
|
|
13898
13967
|
const entry = { ts: (/* @__PURE__ */ new Date()).toISOString(), level, module: "GA4Routes", action, ...ctx };
|
|
13899
13968
|
const stream = level === "error" ? process.stderr : process.stdout;
|
|
@@ -14190,8 +14259,8 @@ async function ga4Routes(app, opts) {
|
|
|
14190
14259
|
tx.delete(gaTrafficSnapshots).where(
|
|
14191
14260
|
and12(
|
|
14192
14261
|
eq21(gaTrafficSnapshots.projectId, project.id),
|
|
14193
|
-
|
|
14194
|
-
|
|
14262
|
+
sql8`${gaTrafficSnapshots.date} >= ${summary.periodStart}`,
|
|
14263
|
+
sql8`${gaTrafficSnapshots.date} <= ${summary.periodEnd}`
|
|
14195
14264
|
)
|
|
14196
14265
|
).run();
|
|
14197
14266
|
for (const row of rows) {
|
|
@@ -14214,8 +14283,8 @@ async function ga4Routes(app, opts) {
|
|
|
14214
14283
|
tx.delete(gaAiReferrals).where(
|
|
14215
14284
|
and12(
|
|
14216
14285
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14217
|
-
|
|
14218
|
-
|
|
14286
|
+
sql8`${gaAiReferrals.date} >= ${summary.periodStart}`,
|
|
14287
|
+
sql8`${gaAiReferrals.date} <= ${summary.periodEnd}`
|
|
14219
14288
|
)
|
|
14220
14289
|
).run();
|
|
14221
14290
|
for (const row of aiReferrals) {
|
|
@@ -14240,8 +14309,8 @@ async function ga4Routes(app, opts) {
|
|
|
14240
14309
|
tx.delete(gaSocialReferrals).where(
|
|
14241
14310
|
and12(
|
|
14242
14311
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14243
|
-
|
|
14244
|
-
|
|
14312
|
+
sql8`${gaSocialReferrals.date} >= ${summary.periodStart}`,
|
|
14313
|
+
sql8`${gaSocialReferrals.date} <= ${summary.periodEnd}`
|
|
14245
14314
|
)
|
|
14246
14315
|
).run();
|
|
14247
14316
|
for (const row of socialReferrals) {
|
|
@@ -14331,11 +14400,11 @@ async function ga4Routes(app, opts) {
|
|
|
14331
14400
|
const cutoff = windowCutoff(window);
|
|
14332
14401
|
const cutoffDate = cutoff?.slice(0, 10) ?? null;
|
|
14333
14402
|
const snapshotConditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
|
|
14334
|
-
if (cutoffDate) snapshotConditions.push(
|
|
14403
|
+
if (cutoffDate) snapshotConditions.push(sql8`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
|
|
14335
14404
|
const aiConditions = [eq21(gaAiReferrals.projectId, project.id)];
|
|
14336
|
-
if (cutoffDate) aiConditions.push(
|
|
14405
|
+
if (cutoffDate) aiConditions.push(sql8`${gaAiReferrals.date} >= ${cutoffDate}`);
|
|
14337
14406
|
const socialConditions = [eq21(gaSocialReferrals.projectId, project.id)];
|
|
14338
|
-
if (cutoffDate) socialConditions.push(
|
|
14407
|
+
if (cutoffDate) socialConditions.push(sql8`${gaSocialReferrals.date} >= ${cutoffDate}`);
|
|
14339
14408
|
const windowSummaryRow = cutoffDate ? app.db.select({
|
|
14340
14409
|
totalSessions: gaTrafficWindowSummaries.totalSessions,
|
|
14341
14410
|
totalOrganicSessions: gaTrafficWindowSummaries.totalOrganicSessions,
|
|
@@ -14348,9 +14417,9 @@ async function ga4Routes(app, opts) {
|
|
|
14348
14417
|
)
|
|
14349
14418
|
).get() : null;
|
|
14350
14419
|
const snapshotTotalsRow = cutoffDate && !windowSummaryRow ? app.db.select({
|
|
14351
|
-
totalSessions:
|
|
14352
|
-
totalOrganicSessions:
|
|
14353
|
-
totalUsers:
|
|
14420
|
+
totalSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.sessions}), 0)`,
|
|
14421
|
+
totalOrganicSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.organicSessions}), 0)`,
|
|
14422
|
+
totalUsers: sql8`COALESCE(SUM(${gaTrafficSnapshots.users}), 0)`
|
|
14354
14423
|
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get() : null;
|
|
14355
14424
|
const summaryRow = cutoffDate ? windowSummaryRow ?? snapshotTotalsRow : app.db.select({
|
|
14356
14425
|
totalSessions: gaTrafficSummaries.totalSessions,
|
|
@@ -14358,38 +14427,38 @@ async function ga4Routes(app, opts) {
|
|
|
14358
14427
|
totalUsers: gaTrafficSummaries.totalUsers
|
|
14359
14428
|
}).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
|
|
14360
14429
|
const directTotalRow = windowSummaryRow ? { totalDirectSessions: windowSummaryRow.totalDirectSessions } : app.db.select({
|
|
14361
|
-
totalDirectSessions:
|
|
14430
|
+
totalDirectSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`
|
|
14362
14431
|
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).get();
|
|
14363
14432
|
const summaryMeta = app.db.select({
|
|
14364
14433
|
periodStart: gaTrafficSummaries.periodStart,
|
|
14365
14434
|
periodEnd: gaTrafficSummaries.periodEnd
|
|
14366
14435
|
}).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).get();
|
|
14367
14436
|
const rows = app.db.select({
|
|
14368
|
-
landingPage:
|
|
14369
|
-
sessions:
|
|
14370
|
-
organicSessions:
|
|
14371
|
-
directSessions:
|
|
14372
|
-
users:
|
|
14373
|
-
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(
|
|
14437
|
+
landingPage: sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
|
|
14438
|
+
sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
|
|
14439
|
+
organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
14440
|
+
directSessions: sql8`COALESCE(SUM(${gaTrafficSnapshots.directSessions}), 0)`,
|
|
14441
|
+
users: sql8`SUM(${gaTrafficSnapshots.users})`
|
|
14442
|
+
}).from(gaTrafficSnapshots).where(and12(...snapshotConditions)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).limit(limit).all();
|
|
14374
14443
|
const aiReferralRows = app.db.select({
|
|
14375
14444
|
source: gaAiReferrals.source,
|
|
14376
14445
|
medium: gaAiReferrals.medium,
|
|
14377
14446
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
14378
|
-
sessions:
|
|
14379
|
-
users:
|
|
14447
|
+
sessions: sql8`SUM(${gaAiReferrals.sessions})`,
|
|
14448
|
+
users: sql8`SUM(${gaAiReferrals.users})`
|
|
14380
14449
|
}).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(gaAiReferrals.source, gaAiReferrals.medium, gaAiReferrals.sourceDimension).all();
|
|
14381
14450
|
const aiReferralLandingPageRows = app.db.select({
|
|
14382
14451
|
source: gaAiReferrals.source,
|
|
14383
14452
|
medium: gaAiReferrals.medium,
|
|
14384
14453
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
14385
|
-
landingPage:
|
|
14386
|
-
sessions:
|
|
14387
|
-
users:
|
|
14454
|
+
landingPage: sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
|
|
14455
|
+
sessions: sql8`SUM(${gaAiReferrals.sessions})`,
|
|
14456
|
+
users: sql8`SUM(${gaAiReferrals.users})`
|
|
14388
14457
|
}).from(gaAiReferrals).where(and12(...aiConditions)).groupBy(
|
|
14389
14458
|
gaAiReferrals.source,
|
|
14390
14459
|
gaAiReferrals.medium,
|
|
14391
14460
|
gaAiReferrals.sourceDimension,
|
|
14392
|
-
|
|
14461
|
+
sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
|
|
14393
14462
|
).all();
|
|
14394
14463
|
const aiReferrals = pickWinningDimension(
|
|
14395
14464
|
aiReferralRows,
|
|
@@ -14400,10 +14469,10 @@ async function ga4Routes(app, opts) {
|
|
|
14400
14469
|
(r) => `${r.source}\0${r.medium}\0${r.landingPage}`
|
|
14401
14470
|
);
|
|
14402
14471
|
const aiDeduped = app.db.select({
|
|
14403
|
-
sessions:
|
|
14404
|
-
users:
|
|
14472
|
+
sessions: sql8`COALESCE(SUM(max_sessions), 0)`,
|
|
14473
|
+
users: sql8`COALESCE(SUM(max_users), 0)`
|
|
14405
14474
|
}).from(
|
|
14406
|
-
|
|
14475
|
+
sql8`(
|
|
14407
14476
|
SELECT date, source, medium,
|
|
14408
14477
|
MAX(dimension_sessions) AS max_sessions,
|
|
14409
14478
|
MAX(dimension_users) AS max_users
|
|
@@ -14412,7 +14481,7 @@ async function ga4Routes(app, opts) {
|
|
|
14412
14481
|
SUM(sessions) AS dimension_sessions,
|
|
14413
14482
|
SUM(users) AS dimension_users
|
|
14414
14483
|
FROM ga_ai_referrals
|
|
14415
|
-
WHERE project_id = ${project.id}${cutoffDate ?
|
|
14484
|
+
WHERE project_id = ${project.id}${cutoffDate ? sql8` AND date >= ${cutoffDate}` : sql8``}
|
|
14416
14485
|
GROUP BY date, source, medium, source_dimension
|
|
14417
14486
|
)
|
|
14418
14487
|
GROUP BY date, source, medium
|
|
@@ -14420,8 +14489,8 @@ async function ga4Routes(app, opts) {
|
|
|
14420
14489
|
).get();
|
|
14421
14490
|
const aiBySessionRows = app.db.select({
|
|
14422
14491
|
channelGroup: gaAiReferrals.channelGroup,
|
|
14423
|
-
sessions:
|
|
14424
|
-
users:
|
|
14492
|
+
sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)`,
|
|
14493
|
+
users: sql8`COALESCE(SUM(${gaAiReferrals.users}), 0)`
|
|
14425
14494
|
}).from(gaAiReferrals).where(and12(...aiConditions, eq21(gaAiReferrals.sourceDimension, "session"))).groupBy(gaAiReferrals.channelGroup).all();
|
|
14426
14495
|
const aiSessionsByChannelGroup = /* @__PURE__ */ new Map();
|
|
14427
14496
|
let aiBySessionUsers = 0;
|
|
@@ -14434,12 +14503,12 @@ async function ga4Routes(app, opts) {
|
|
|
14434
14503
|
source: gaSocialReferrals.source,
|
|
14435
14504
|
medium: gaSocialReferrals.medium,
|
|
14436
14505
|
channelGroup: gaSocialReferrals.channelGroup,
|
|
14437
|
-
sessions:
|
|
14438
|
-
users:
|
|
14439
|
-
}).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(
|
|
14506
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
|
|
14507
|
+
users: sql8`SUM(${gaSocialReferrals.users})`
|
|
14508
|
+
}).from(gaSocialReferrals).where(and12(...socialConditions)).groupBy(gaSocialReferrals.source, gaSocialReferrals.medium, gaSocialReferrals.channelGroup).orderBy(sql8`SUM(${gaSocialReferrals.sessions}) DESC`).all();
|
|
14440
14509
|
const socialTotals = app.db.select({
|
|
14441
|
-
sessions:
|
|
14442
|
-
users:
|
|
14510
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`,
|
|
14511
|
+
users: sql8`SUM(${gaSocialReferrals.users})`
|
|
14443
14512
|
}).from(gaSocialReferrals).where(and12(...socialConditions)).get();
|
|
14444
14513
|
const latestSync = app.db.select({ syncedAt: gaTrafficSummaries.syncedAt }).from(gaTrafficSummaries).where(eq21(gaTrafficSummaries.projectId, project.id)).orderBy(desc10(gaTrafficSummaries.syncedAt)).limit(1).get();
|
|
14445
14514
|
const total = summaryRow?.totalSessions ?? 0;
|
|
@@ -14522,21 +14591,21 @@ async function ga4Routes(app, opts) {
|
|
|
14522
14591
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14523
14592
|
const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
|
|
14524
14593
|
const conditions = [eq21(gaAiReferrals.projectId, project.id)];
|
|
14525
|
-
if (cutoffDate) conditions.push(
|
|
14594
|
+
if (cutoffDate) conditions.push(sql8`${gaAiReferrals.date} >= ${cutoffDate}`);
|
|
14526
14595
|
const rows = app.db.select({
|
|
14527
14596
|
date: gaAiReferrals.date,
|
|
14528
14597
|
source: gaAiReferrals.source,
|
|
14529
14598
|
medium: gaAiReferrals.medium,
|
|
14530
|
-
landingPage:
|
|
14599
|
+
landingPage: sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`,
|
|
14531
14600
|
sourceDimension: gaAiReferrals.sourceDimension,
|
|
14532
|
-
sessions:
|
|
14533
|
-
users:
|
|
14601
|
+
sessions: sql8`SUM(${gaAiReferrals.sessions})`,
|
|
14602
|
+
users: sql8`SUM(${gaAiReferrals.users})`
|
|
14534
14603
|
}).from(gaAiReferrals).where(and12(...conditions)).groupBy(
|
|
14535
14604
|
gaAiReferrals.date,
|
|
14536
14605
|
gaAiReferrals.source,
|
|
14537
14606
|
gaAiReferrals.medium,
|
|
14538
14607
|
gaAiReferrals.sourceDimension,
|
|
14539
|
-
|
|
14608
|
+
sql8`COALESCE(${gaAiReferrals.landingPageNormalized}, ${gaAiReferrals.landingPage})`
|
|
14540
14609
|
).orderBy(gaAiReferrals.date).all();
|
|
14541
14610
|
return rows;
|
|
14542
14611
|
});
|
|
@@ -14545,7 +14614,7 @@ async function ga4Routes(app, opts) {
|
|
|
14545
14614
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14546
14615
|
const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
|
|
14547
14616
|
const conditions = [eq21(gaSocialReferrals.projectId, project.id)];
|
|
14548
|
-
if (cutoffDate) conditions.push(
|
|
14617
|
+
if (cutoffDate) conditions.push(sql8`${gaSocialReferrals.date} >= ${cutoffDate}`);
|
|
14549
14618
|
const rows = app.db.select({
|
|
14550
14619
|
date: gaSocialReferrals.date,
|
|
14551
14620
|
source: gaSocialReferrals.source,
|
|
@@ -14566,10 +14635,10 @@ async function ga4Routes(app, opts) {
|
|
|
14566
14635
|
d.setDate(d.getDate() - n);
|
|
14567
14636
|
return fmt(d);
|
|
14568
14637
|
};
|
|
14569
|
-
const sumSocial = (from, to) => app.db.select({ sessions:
|
|
14638
|
+
const sumSocial = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaSocialReferrals.sessions}), 0)` }).from(gaSocialReferrals).where(and12(
|
|
14570
14639
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14571
|
-
|
|
14572
|
-
|
|
14640
|
+
sql8`${gaSocialReferrals.date} >= ${from}`,
|
|
14641
|
+
sql8`${gaSocialReferrals.date} < ${to}`
|
|
14573
14642
|
)).get();
|
|
14574
14643
|
const current7d = sumSocial(daysAgo2(7), fmt(today));
|
|
14575
14644
|
const prev7d = sumSocial(daysAgo2(14), daysAgo2(7));
|
|
@@ -14578,19 +14647,19 @@ async function ga4Routes(app, opts) {
|
|
|
14578
14647
|
const pct = (cur, prev) => prev === 0 ? null : Math.round((cur - prev) / prev * 100);
|
|
14579
14648
|
const sourceCurrent = app.db.select({
|
|
14580
14649
|
source: gaSocialReferrals.source,
|
|
14581
|
-
sessions:
|
|
14650
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`
|
|
14582
14651
|
}).from(gaSocialReferrals).where(and12(
|
|
14583
14652
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14584
|
-
|
|
14585
|
-
|
|
14653
|
+
sql8`${gaSocialReferrals.date} >= ${daysAgo2(7)}`,
|
|
14654
|
+
sql8`${gaSocialReferrals.date} < ${fmt(today)}`
|
|
14586
14655
|
)).groupBy(gaSocialReferrals.source).all();
|
|
14587
14656
|
const sourcePrev = app.db.select({
|
|
14588
14657
|
source: gaSocialReferrals.source,
|
|
14589
|
-
sessions:
|
|
14658
|
+
sessions: sql8`SUM(${gaSocialReferrals.sessions})`
|
|
14590
14659
|
}).from(gaSocialReferrals).where(and12(
|
|
14591
14660
|
eq21(gaSocialReferrals.projectId, project.id),
|
|
14592
|
-
|
|
14593
|
-
|
|
14661
|
+
sql8`${gaSocialReferrals.date} >= ${daysAgo2(14)}`,
|
|
14662
|
+
sql8`${gaSocialReferrals.date} < ${daysAgo2(7)}`
|
|
14594
14663
|
)).groupBy(gaSocialReferrals.source).all();
|
|
14595
14664
|
const prevMap = new Map(sourcePrev.map((r) => [r.source, r.sessions]));
|
|
14596
14665
|
let biggestMover = null;
|
|
@@ -14629,16 +14698,16 @@ async function ga4Routes(app, opts) {
|
|
|
14629
14698
|
return fmt(d);
|
|
14630
14699
|
};
|
|
14631
14700
|
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:
|
|
14701
|
+
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();
|
|
14702
|
+
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();
|
|
14703
|
+
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();
|
|
14704
|
+
const sumAi = (from, to) => app.db.select({ sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
|
|
14636
14705
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14637
|
-
|
|
14638
|
-
|
|
14706
|
+
sql8`${gaAiReferrals.date} >= ${from}`,
|
|
14707
|
+
sql8`${gaAiReferrals.date} < ${to}`,
|
|
14639
14708
|
eq21(gaAiReferrals.sourceDimension, "session")
|
|
14640
14709
|
)).get();
|
|
14641
|
-
const sumSocial = (from, to) => app.db.select({ sessions:
|
|
14710
|
+
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
14711
|
const todayStr = fmt(today);
|
|
14643
14712
|
const buildTrend = (sum) => {
|
|
14644
14713
|
const c7 = sum(daysAgo2(7), todayStr)?.sessions ?? 0;
|
|
@@ -14647,16 +14716,16 @@ async function ga4Routes(app, opts) {
|
|
|
14647
14716
|
const p30 = sum(daysAgo2(60), daysAgo2(30))?.sessions ?? 0;
|
|
14648
14717
|
return { sessions7d: c7, sessionsPrev7d: p7, trend7dPct: pct(c7, p7), sessions30d: c30, sessionsPrev30d: p30, trend30dPct: pct(c30, p30) };
|
|
14649
14718
|
};
|
|
14650
|
-
const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions:
|
|
14719
|
+
const aiSourceCurrent = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
|
|
14651
14720
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14652
|
-
|
|
14653
|
-
|
|
14721
|
+
sql8`${gaAiReferrals.date} >= ${daysAgo2(7)}`,
|
|
14722
|
+
sql8`${gaAiReferrals.date} < ${todayStr}`,
|
|
14654
14723
|
eq21(gaAiReferrals.sourceDimension, "session")
|
|
14655
14724
|
)).groupBy(gaAiReferrals.source).all();
|
|
14656
|
-
const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions:
|
|
14725
|
+
const aiSourcePrev = app.db.select({ source: gaAiReferrals.source, sessions: sql8`COALESCE(SUM(${gaAiReferrals.sessions}), 0)` }).from(gaAiReferrals).where(and12(
|
|
14657
14726
|
eq21(gaAiReferrals.projectId, project.id),
|
|
14658
|
-
|
|
14659
|
-
|
|
14727
|
+
sql8`${gaAiReferrals.date} >= ${daysAgo2(14)}`,
|
|
14728
|
+
sql8`${gaAiReferrals.date} < ${daysAgo2(7)}`,
|
|
14660
14729
|
eq21(gaAiReferrals.sourceDimension, "session")
|
|
14661
14730
|
)).groupBy(gaAiReferrals.source).all();
|
|
14662
14731
|
const findBiggestMover = (current, prev) => {
|
|
@@ -14673,8 +14742,8 @@ async function ga4Routes(app, opts) {
|
|
|
14673
14742
|
}
|
|
14674
14743
|
return mover;
|
|
14675
14744
|
};
|
|
14676
|
-
const socialSourceCurrent = app.db.select({ source: gaSocialReferrals.source, sessions:
|
|
14677
|
-
const socialSourcePrev = app.db.select({ source: gaSocialReferrals.source, sessions:
|
|
14745
|
+
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();
|
|
14746
|
+
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
14747
|
return {
|
|
14679
14748
|
total: buildTrend(sumTotal),
|
|
14680
14749
|
organic: buildTrend(sumOrganic),
|
|
@@ -14690,12 +14759,12 @@ async function ga4Routes(app, opts) {
|
|
|
14690
14759
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14691
14760
|
const cutoffDate = windowCutoff(parseWindow(request.query.window))?.slice(0, 10) ?? null;
|
|
14692
14761
|
const conditions = [eq21(gaTrafficSnapshots.projectId, project.id)];
|
|
14693
|
-
if (cutoffDate) conditions.push(
|
|
14762
|
+
if (cutoffDate) conditions.push(sql8`${gaTrafficSnapshots.date} >= ${cutoffDate}`);
|
|
14694
14763
|
const rows = app.db.select({
|
|
14695
14764
|
date: gaTrafficSnapshots.date,
|
|
14696
|
-
sessions:
|
|
14697
|
-
organicSessions:
|
|
14698
|
-
users:
|
|
14765
|
+
sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
|
|
14766
|
+
organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
14767
|
+
users: sql8`SUM(${gaTrafficSnapshots.users})`
|
|
14699
14768
|
}).from(gaTrafficSnapshots).where(and12(...conditions)).groupBy(gaTrafficSnapshots.date).orderBy(gaTrafficSnapshots.date).all();
|
|
14700
14769
|
return rows.map((r) => ({
|
|
14701
14770
|
date: r.date,
|
|
@@ -14708,11 +14777,11 @@ async function ga4Routes(app, opts) {
|
|
|
14708
14777
|
const project = resolveProject(app.db, request.params.name);
|
|
14709
14778
|
requireGa4Connection(opts, project.name, project.canonicalDomain);
|
|
14710
14779
|
const trafficPages = app.db.select({
|
|
14711
|
-
landingPage:
|
|
14712
|
-
sessions:
|
|
14713
|
-
organicSessions:
|
|
14714
|
-
users:
|
|
14715
|
-
}).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(
|
|
14780
|
+
landingPage: sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`,
|
|
14781
|
+
sessions: sql8`SUM(${gaTrafficSnapshots.sessions})`,
|
|
14782
|
+
organicSessions: sql8`SUM(${gaTrafficSnapshots.organicSessions})`,
|
|
14783
|
+
users: sql8`SUM(${gaTrafficSnapshots.users})`
|
|
14784
|
+
}).from(gaTrafficSnapshots).where(eq21(gaTrafficSnapshots.projectId, project.id)).groupBy(sql8`COALESCE(${gaTrafficSnapshots.landingPageNormalized}, ${gaTrafficSnapshots.landingPage})`).orderBy(sql8`SUM(${gaTrafficSnapshots.sessions}) DESC`).all();
|
|
14716
14785
|
return {
|
|
14717
14786
|
pages: trafficPages.map((r) => ({
|
|
14718
14787
|
landingPage: r.landingPage,
|
|
@@ -16349,7 +16418,7 @@ async function wordpressRoutes(app, opts) {
|
|
|
16349
16418
|
|
|
16350
16419
|
// ../api-routes/src/backlinks.ts
|
|
16351
16420
|
import crypto18 from "crypto";
|
|
16352
|
-
import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as
|
|
16421
|
+
import { and as and14, asc as asc2, desc as desc11, eq as eq22, sql as sql9 } from "drizzle-orm";
|
|
16353
16422
|
|
|
16354
16423
|
// ../integration-commoncrawl/src/constants.ts
|
|
16355
16424
|
import os3 from "os";
|
|
@@ -16633,7 +16702,7 @@ async function queryBacklinks(opts) {
|
|
|
16633
16702
|
const reversed = opts.targets.map(reverseDomain);
|
|
16634
16703
|
const targetList = reversed.map(quote).join(", ");
|
|
16635
16704
|
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
|
|
16705
|
+
const sql16 = `
|
|
16637
16706
|
WITH vertices AS (
|
|
16638
16707
|
SELECT * FROM read_csv(
|
|
16639
16708
|
${quote(opts.vertexPath)},
|
|
@@ -16669,7 +16738,7 @@ async function queryBacklinks(opts) {
|
|
|
16669
16738
|
const conn = await instance.connect();
|
|
16670
16739
|
let rows;
|
|
16671
16740
|
try {
|
|
16672
|
-
const reader = await conn.runAndReadAll(
|
|
16741
|
+
const reader = await conn.runAndReadAll(sql16);
|
|
16673
16742
|
rows = reader.getRowObjects();
|
|
16674
16743
|
} finally {
|
|
16675
16744
|
conn.disconnectSync?.();
|
|
@@ -16845,12 +16914,12 @@ function computeFilteredSummary(db, base) {
|
|
|
16845
16914
|
);
|
|
16846
16915
|
const filteredCondition = and14(baseDomainCondition, backlinkCrawlerExclusionClause());
|
|
16847
16916
|
const unfilteredAgg = db.select({
|
|
16848
|
-
count:
|
|
16849
|
-
total:
|
|
16917
|
+
count: sql9`count(*)`,
|
|
16918
|
+
total: sql9`coalesce(sum(${backlinkDomains.numHosts}), 0)`
|
|
16850
16919
|
}).from(backlinkDomains).where(baseDomainCondition).get();
|
|
16851
16920
|
const filteredAgg = db.select({
|
|
16852
|
-
count:
|
|
16853
|
-
total:
|
|
16921
|
+
count: sql9`count(*)`,
|
|
16922
|
+
total: sql9`coalesce(sum(${backlinkDomains.numHosts}), 0)`
|
|
16854
16923
|
}).from(backlinkDomains).where(filteredCondition).get();
|
|
16855
16924
|
const top10Rows = db.select({ numHosts: backlinkDomains.numHosts }).from(backlinkDomains).where(filteredCondition).orderBy(desc11(backlinkDomains.numHosts)).limit(10).all();
|
|
16856
16925
|
const totalLinkingDomains = Number(filteredAgg?.count ?? 0);
|
|
@@ -17024,7 +17093,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
17024
17093
|
eq22(backlinkDomains.release, targetRelease)
|
|
17025
17094
|
);
|
|
17026
17095
|
const domainCondition = excludeCrawlers ? and14(baseDomainCondition, backlinkCrawlerExclusionClause()) : baseDomainCondition;
|
|
17027
|
-
const totalRow = app.db.select({ count:
|
|
17096
|
+
const totalRow = app.db.select({ count: sql9`count(*)` }).from(backlinkDomains).where(domainCondition).get();
|
|
17028
17097
|
const rows = app.db.select({
|
|
17029
17098
|
linkingDomain: backlinkDomains.linkingDomain,
|
|
17030
17099
|
numHosts: backlinkDomains.numHosts
|
|
@@ -17059,7 +17128,7 @@ async function backlinksRoutes(app, opts) {
|
|
|
17059
17128
|
|
|
17060
17129
|
// ../api-routes/src/traffic.ts
|
|
17061
17130
|
import crypto20 from "crypto";
|
|
17062
|
-
import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as
|
|
17131
|
+
import { and as and15, desc as desc12, eq as eq23, gte as gte2, lte as lte2, sql as sql10 } from "drizzle-orm";
|
|
17063
17132
|
|
|
17064
17133
|
// ../integration-cloud-run/src/auth.ts
|
|
17065
17134
|
import crypto19 from "crypto";
|
|
@@ -18797,7 +18866,7 @@ async function trafficRoutes(app, opts) {
|
|
|
18797
18866
|
crawlerEventsHourly.status
|
|
18798
18867
|
],
|
|
18799
18868
|
set: {
|
|
18800
|
-
hits:
|
|
18869
|
+
hits: sql10`${crawlerEventsHourly.hits} + ${bucket.hits}`,
|
|
18801
18870
|
sampledUserAgent: bucket.sampledUserAgent,
|
|
18802
18871
|
updatedAt: finishedAt
|
|
18803
18872
|
}
|
|
@@ -18832,7 +18901,7 @@ async function trafficRoutes(app, opts) {
|
|
|
18832
18901
|
aiReferralEventsHourly.status
|
|
18833
18902
|
],
|
|
18834
18903
|
set: {
|
|
18835
|
-
sessionsOrHits:
|
|
18904
|
+
sessionsOrHits: sql10`${aiReferralEventsHourly.sessionsOrHits} + ${bucket.hits}`,
|
|
18836
18905
|
updatedAt: finishedAt
|
|
18837
18906
|
}
|
|
18838
18907
|
}).run();
|
|
@@ -19084,19 +19153,19 @@ async function trafficRoutes(app, opts) {
|
|
|
19084
19153
|
return response;
|
|
19085
19154
|
});
|
|
19086
19155
|
function buildSourceDetail(projectId, row, since) {
|
|
19087
|
-
const crawlerTotals = app.db.select({ total:
|
|
19156
|
+
const crawlerTotals = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
19088
19157
|
and15(
|
|
19089
19158
|
eq23(crawlerEventsHourly.sourceId, row.id),
|
|
19090
19159
|
gte2(crawlerEventsHourly.tsHour, since)
|
|
19091
19160
|
)
|
|
19092
19161
|
).get();
|
|
19093
|
-
const aiTotals = app.db.select({ total:
|
|
19162
|
+
const aiTotals = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
19094
19163
|
and15(
|
|
19095
19164
|
eq23(aiReferralEventsHourly.sourceId, row.id),
|
|
19096
19165
|
gte2(aiReferralEventsHourly.tsHour, since)
|
|
19097
19166
|
)
|
|
19098
19167
|
).get();
|
|
19099
|
-
const sampleTotals = app.db.select({ total:
|
|
19168
|
+
const sampleTotals = app.db.select({ total: sql10`COUNT(*)` }).from(rawEventSamples).where(
|
|
19100
19169
|
and15(
|
|
19101
19170
|
eq23(rawEventSamples.sourceId, row.id),
|
|
19102
19171
|
gte2(rawEventSamples.ts, since)
|
|
@@ -19198,7 +19267,7 @@ async function trafficRoutes(app, opts) {
|
|
|
19198
19267
|
];
|
|
19199
19268
|
if (sourceIdParam) crawlerFilters.push(eq23(crawlerEventsHourly.sourceId, sourceIdParam));
|
|
19200
19269
|
const crawlerWhere = and15(...crawlerFilters);
|
|
19201
|
-
const total = app.db.select({ total:
|
|
19270
|
+
const total = app.db.select({ total: sql10`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(crawlerWhere).get();
|
|
19202
19271
|
crawlerTotal = Number(total?.total ?? 0);
|
|
19203
19272
|
const rows = app.db.select().from(crawlerEventsHourly).where(crawlerWhere).orderBy(desc12(crawlerEventsHourly.tsHour)).limit(limit).all();
|
|
19204
19273
|
for (const r of rows) {
|
|
@@ -19223,7 +19292,7 @@ async function trafficRoutes(app, opts) {
|
|
|
19223
19292
|
];
|
|
19224
19293
|
if (sourceIdParam) aiFilters.push(eq23(aiReferralEventsHourly.sourceId, sourceIdParam));
|
|
19225
19294
|
const aiWhere = and15(...aiFilters);
|
|
19226
|
-
const total = app.db.select({ total:
|
|
19295
|
+
const total = app.db.select({ total: sql10`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(aiWhere).get();
|
|
19227
19296
|
aiReferralTotal = Number(total?.total ?? 0);
|
|
19228
19297
|
const rows = app.db.select().from(aiReferralEventsHourly).where(aiWhere).orderBy(desc12(aiReferralEventsHourly.tsHour)).limit(limit).all();
|
|
19229
19298
|
for (const r of rows) {
|
|
@@ -19946,7 +20015,7 @@ var providersConfiguredCheck = {
|
|
|
19946
20015
|
var PROVIDERS_CHECKS = [providersConfiguredCheck];
|
|
19947
20016
|
|
|
19948
20017
|
// ../api-routes/src/doctor/checks/traffic-source.ts
|
|
19949
|
-
import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as
|
|
20018
|
+
import { and as and16, eq as eq24, gte as gte3, ne as ne3, sql as sql11 } from "drizzle-orm";
|
|
19950
20019
|
var RECENT_DATA_WARN_DAYS = 7;
|
|
19951
20020
|
var RECENT_DATA_FAIL_DAYS = 30;
|
|
19952
20021
|
function skippedNoProject2() {
|
|
@@ -20040,7 +20109,7 @@ var recentDataCheck = {
|
|
|
20040
20109
|
const warnCutoff = new Date(now.getTime() - RECENT_DATA_WARN_DAYS * 24 * 60 * 6e4).toISOString();
|
|
20041
20110
|
const failCutoff = new Date(now.getTime() - RECENT_DATA_FAIL_DAYS * 24 * 60 * 6e4).toISOString();
|
|
20042
20111
|
const recentCrawlers = Number(
|
|
20043
|
-
ctx.db.select({ total:
|
|
20112
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
20044
20113
|
and16(
|
|
20045
20114
|
eq24(crawlerEventsHourly.projectId, ctx.project.id),
|
|
20046
20115
|
gte3(crawlerEventsHourly.tsHour, warnCutoff)
|
|
@@ -20048,7 +20117,7 @@ var recentDataCheck = {
|
|
|
20048
20117
|
).get()?.total ?? 0
|
|
20049
20118
|
);
|
|
20050
20119
|
const recentReferrals = Number(
|
|
20051
|
-
ctx.db.select({ total:
|
|
20120
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
20052
20121
|
and16(
|
|
20053
20122
|
eq24(aiReferralEventsHourly.projectId, ctx.project.id),
|
|
20054
20123
|
gte3(aiReferralEventsHourly.tsHour, warnCutoff)
|
|
@@ -20064,7 +20133,7 @@ var recentDataCheck = {
|
|
|
20064
20133
|
};
|
|
20065
20134
|
}
|
|
20066
20135
|
const olderCrawlers = Number(
|
|
20067
|
-
ctx.db.select({ total:
|
|
20136
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${crawlerEventsHourly.hits}), 0)` }).from(crawlerEventsHourly).where(
|
|
20068
20137
|
and16(
|
|
20069
20138
|
eq24(crawlerEventsHourly.projectId, ctx.project.id),
|
|
20070
20139
|
gte3(crawlerEventsHourly.tsHour, failCutoff)
|
|
@@ -20072,7 +20141,7 @@ var recentDataCheck = {
|
|
|
20072
20141
|
).get()?.total ?? 0
|
|
20073
20142
|
);
|
|
20074
20143
|
const olderReferrals = Number(
|
|
20075
|
-
ctx.db.select({ total:
|
|
20144
|
+
ctx.db.select({ total: sql11`COALESCE(SUM(${aiReferralEventsHourly.sessionsOrHits}), 0)` }).from(aiReferralEventsHourly).where(
|
|
20076
20145
|
and16(
|
|
20077
20146
|
eq24(aiReferralEventsHourly.projectId, ctx.project.id),
|
|
20078
20147
|
gte3(aiReferralEventsHourly.tsHour, failCutoff)
|
|
@@ -20372,7 +20441,7 @@ async function doctorRoutes(app, opts) {
|
|
|
20372
20441
|
|
|
20373
20442
|
// ../api-routes/src/discovery/routes.ts
|
|
20374
20443
|
import crypto21 from "crypto";
|
|
20375
|
-
import { and as and17, desc as desc13, eq as eq25, gte as gte4, inArray as
|
|
20444
|
+
import { and as and17, desc as desc13, eq as eq25, gte as gte4, inArray as inArray9 } from "drizzle-orm";
|
|
20376
20445
|
var MAX_INFLIGHT_DISCOVERY_AGE_MS = 2 * 60 * 60 * 1e3;
|
|
20377
20446
|
async function discoveryRoutes(app, opts) {
|
|
20378
20447
|
app.post("/projects/:name/discover/run", async (request, reply) => {
|
|
@@ -20407,7 +20476,7 @@ async function discoveryRoutes(app, opts) {
|
|
|
20407
20476
|
const existing = tx.select({ id: discoverySessions.id, runId: discoverySessions.runId }).from(discoverySessions).where(and17(
|
|
20408
20477
|
eq25(discoverySessions.projectId, project.id),
|
|
20409
20478
|
eq25(discoverySessions.icpDescription, icpDescription),
|
|
20410
|
-
|
|
20479
|
+
inArray9(discoverySessions.status, [
|
|
20411
20480
|
DiscoverySessionStatuses.queued,
|
|
20412
20481
|
DiscoverySessionStatuses.seeding,
|
|
20413
20482
|
DiscoverySessionStatuses.probing
|
|
@@ -23725,7 +23794,7 @@ import crypto24 from "crypto";
|
|
|
23725
23794
|
import fs8 from "fs";
|
|
23726
23795
|
import path10 from "path";
|
|
23727
23796
|
import os5 from "os";
|
|
23728
|
-
import { and as and18, eq as eq27, inArray as
|
|
23797
|
+
import { and as and18, eq as eq27, inArray as inArray10, sql as sql12 } from "drizzle-orm";
|
|
23729
23798
|
|
|
23730
23799
|
// src/run-telemetry.ts
|
|
23731
23800
|
import crypto23 from "crypto";
|
|
@@ -24070,7 +24139,7 @@ var JobRunner = class {
|
|
|
24070
24139
|
this.registry = registry;
|
|
24071
24140
|
}
|
|
24072
24141
|
recoverStaleRuns() {
|
|
24073
|
-
const stale = this.db.select({ id: runs.id, status: runs.status }).from(runs).where(
|
|
24142
|
+
const stale = this.db.select({ id: runs.id, status: runs.status }).from(runs).where(inArray10(runs.status, ["running", "queued"])).all();
|
|
24074
24143
|
if (stale.length === 0) return;
|
|
24075
24144
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24076
24145
|
for (const run of stale) {
|
|
@@ -24133,7 +24202,7 @@ var JobRunner = class {
|
|
|
24133
24202
|
}
|
|
24134
24203
|
log.info("run.dispatch", { runId, providerCount: activeProviders.length, providers: activeProviders.map((p) => p.adapter.name) });
|
|
24135
24204
|
const scopedQueryNames = parseJsonColumn(existingRun.queries, null);
|
|
24136
|
-
projectQueries = scopedQueryNames ? this.db.select().from(queries).where(and18(eq27(queries.projectId, projectId),
|
|
24205
|
+
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
24206
|
const projectCompetitors = this.db.select().from(competitors).where(eq27(competitors.projectId, projectId)).all();
|
|
24138
24207
|
const competitorDomains = projectCompetitors.map((c) => c.domain);
|
|
24139
24208
|
const allDomains = effectiveDomains({
|
|
@@ -24406,7 +24475,7 @@ var JobRunner = class {
|
|
|
24406
24475
|
updatedAt: now
|
|
24407
24476
|
}).onConflictDoUpdate({
|
|
24408
24477
|
target: [usageCounters.scope, usageCounters.period, usageCounters.metric],
|
|
24409
|
-
set: { count:
|
|
24478
|
+
set: { count: sql12`${usageCounters.count} + ${count}`, updatedAt: now }
|
|
24410
24479
|
}).run();
|
|
24411
24480
|
}
|
|
24412
24481
|
flushProviderUsage(projectId, providerDispatchCounts) {
|
|
@@ -24476,7 +24545,7 @@ function buildPhases(input) {
|
|
|
24476
24545
|
|
|
24477
24546
|
// src/gsc-sync.ts
|
|
24478
24547
|
import crypto25 from "crypto";
|
|
24479
|
-
import { eq as eq28, and as and19, sql as
|
|
24548
|
+
import { eq as eq28, and as and19, sql as sql13 } from "drizzle-orm";
|
|
24480
24549
|
var log2 = createLogger("GscSync");
|
|
24481
24550
|
function formatDate3(d) {
|
|
24482
24551
|
return d.toISOString().split("T")[0];
|
|
@@ -24530,8 +24599,8 @@ async function executeGscSync(db, runId, projectId, opts) {
|
|
|
24530
24599
|
db.delete(gscSearchData).where(
|
|
24531
24600
|
and19(
|
|
24532
24601
|
eq28(gscSearchData.projectId, projectId),
|
|
24533
|
-
|
|
24534
|
-
|
|
24602
|
+
sql13`${gscSearchData.date} >= ${startDate}`,
|
|
24603
|
+
sql13`${gscSearchData.date} <= ${endDate}`
|
|
24535
24604
|
)
|
|
24536
24605
|
).run();
|
|
24537
24606
|
const batchSize = 500;
|
|
@@ -25067,7 +25136,7 @@ async function executeBingInspectSitemap(db, runId, projectId, opts) {
|
|
|
25067
25136
|
// src/commoncrawl-sync.ts
|
|
25068
25137
|
import crypto28 from "crypto";
|
|
25069
25138
|
import path11 from "path";
|
|
25070
|
-
import { and as and21, eq as eq31, sql as
|
|
25139
|
+
import { and as and21, eq as eq31, sql as sql14 } from "drizzle-orm";
|
|
25071
25140
|
var log6 = createLogger("CommonCrawlSync");
|
|
25072
25141
|
var INSERT_CHUNK_SIZE = 1e4;
|
|
25073
25142
|
function defaultDeps() {
|
|
@@ -25679,23 +25748,25 @@ function buildDiscoveryInsightTitle(input) {
|
|
|
25679
25748
|
}
|
|
25680
25749
|
|
|
25681
25750
|
// src/commands/backfill.ts
|
|
25682
|
-
import { and as and24, eq as eq34, inArray as
|
|
25751
|
+
import { and as and24, eq as eq34, inArray as inArray11 } from "drizzle-orm";
|
|
25683
25752
|
var SNAPSHOT_BATCH_SIZE = 500;
|
|
25684
25753
|
async function backfillAnswerVisibilityCommand(opts) {
|
|
25685
25754
|
const config = loadConfig();
|
|
25686
25755
|
const db = createClient(config.database);
|
|
25687
25756
|
migrate(db);
|
|
25688
25757
|
const projectFilter = opts?.project?.trim();
|
|
25758
|
+
const isDryRun = opts?.dryRun === true;
|
|
25689
25759
|
const scopedProjects = projectFilter ? db.select().from(projects).where(eq34(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
25690
25760
|
let examined = 0;
|
|
25691
25761
|
let updated = 0;
|
|
25762
|
+
let wouldUpdate = 0;
|
|
25692
25763
|
let mentioned = 0;
|
|
25693
25764
|
let reparsed = 0;
|
|
25694
25765
|
let providerErrors = 0;
|
|
25695
25766
|
if (scopedProjects.length > 0) {
|
|
25696
25767
|
const runRows = projectFilter ? db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(and24(
|
|
25697
25768
|
eq34(runs.kind, RunKinds["answer-visibility"]),
|
|
25698
|
-
|
|
25769
|
+
inArray11(runs.projectId, scopedProjects.map((project) => project.id))
|
|
25699
25770
|
)).all() : db.select({ id: runs.id, projectId: runs.projectId }).from(runs).where(eq34(runs.kind, RunKinds["answer-visibility"])).all();
|
|
25700
25771
|
const runIdsByProject = /* @__PURE__ */ new Map();
|
|
25701
25772
|
for (const run of runRows) {
|
|
@@ -25727,7 +25798,7 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
25727
25798
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
25728
25799
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
25729
25800
|
rawResponse: querySnapshots.rawResponse
|
|
25730
|
-
}).from(querySnapshots).where(
|
|
25801
|
+
}).from(querySnapshots).where(inArray11(querySnapshots.runId, batchRunIds)).all();
|
|
25731
25802
|
const pendingUpdates = [];
|
|
25732
25803
|
for (const snapshot of snapshotRows) {
|
|
25733
25804
|
examined++;
|
|
@@ -25791,12 +25862,16 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
25791
25862
|
}
|
|
25792
25863
|
}
|
|
25793
25864
|
if (pendingUpdates.length > 0) {
|
|
25794
|
-
|
|
25795
|
-
|
|
25796
|
-
|
|
25797
|
-
|
|
25798
|
-
|
|
25799
|
-
|
|
25865
|
+
if (isDryRun) {
|
|
25866
|
+
wouldUpdate += pendingUpdates.length;
|
|
25867
|
+
} else {
|
|
25868
|
+
db.transaction((tx) => {
|
|
25869
|
+
for (const update of pendingUpdates) {
|
|
25870
|
+
tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
|
|
25871
|
+
}
|
|
25872
|
+
});
|
|
25873
|
+
updated += pendingUpdates.length;
|
|
25874
|
+
}
|
|
25800
25875
|
}
|
|
25801
25876
|
}
|
|
25802
25877
|
}
|
|
@@ -25810,20 +25885,33 @@ async function backfillAnswerVisibilityCommand(opts) {
|
|
|
25810
25885
|
reparsed,
|
|
25811
25886
|
providerErrors
|
|
25812
25887
|
};
|
|
25888
|
+
if (isDryRun) {
|
|
25889
|
+
result.dryRun = true;
|
|
25890
|
+
result.wouldUpdate = wouldUpdate;
|
|
25891
|
+
}
|
|
25813
25892
|
if (opts?.format === "json") {
|
|
25814
25893
|
console.log(JSON.stringify(result, null, 2));
|
|
25815
25894
|
return;
|
|
25816
25895
|
}
|
|
25817
|
-
console.log(
|
|
25896
|
+
console.log(`Answer visibility backfill ${isDryRun ? "preview" : "complete"}.
|
|
25897
|
+
`);
|
|
25818
25898
|
if (projectFilter) {
|
|
25819
25899
|
console.log(` Project: ${projectFilter}`);
|
|
25820
25900
|
}
|
|
25821
25901
|
console.log(` Projects: ${scopedProjects.length}`);
|
|
25822
|
-
console.log(` Examined:
|
|
25823
|
-
|
|
25824
|
-
|
|
25825
|
-
|
|
25826
|
-
|
|
25902
|
+
console.log(` Examined: ${examined}`);
|
|
25903
|
+
if (isDryRun) {
|
|
25904
|
+
console.log(` Would update: ${wouldUpdate}`);
|
|
25905
|
+
} else {
|
|
25906
|
+
console.log(` Updated: ${updated}`);
|
|
25907
|
+
}
|
|
25908
|
+
console.log(` Mentioned: ${mentioned}`);
|
|
25909
|
+
console.log(` Reparsed: ${reparsed}`);
|
|
25910
|
+
console.log(` Errors: ${providerErrors}`);
|
|
25911
|
+
if (isDryRun) {
|
|
25912
|
+
console.log(`
|
|
25913
|
+
No DB writes performed. Re-run without --dry-run to apply.`);
|
|
25914
|
+
}
|
|
25827
25915
|
}
|
|
25828
25916
|
function backfillNormalizedPaths(db, opts) {
|
|
25829
25917
|
const baseConditions = [];
|
|
@@ -25969,7 +26057,8 @@ async function backfillAiReferralPathsCommand(opts) {
|
|
|
25969
26057
|
console.log(` Updated: ${updated}`);
|
|
25970
26058
|
console.log(` Unchanged: ${unchanged}`);
|
|
25971
26059
|
}
|
|
25972
|
-
function backfillProjectAnswerMentions(db, projectId) {
|
|
26060
|
+
function backfillProjectAnswerMentions(db, projectId, opts) {
|
|
26061
|
+
const isDryRun = opts?.dryRun === true;
|
|
25973
26062
|
const project = db.select().from(projects).where(eq34(projects.id, projectId)).get();
|
|
25974
26063
|
if (!project) return { examined: 0, updated: 0, mentioned: 0 };
|
|
25975
26064
|
const competitorDomains = db.select({ domain: competitors.domain }).from(competitors).where(eq34(competitors.projectId, projectId)).all().map((row) => row.domain);
|
|
@@ -25977,8 +26066,11 @@ function backfillProjectAnswerMentions(db, projectId) {
|
|
|
25977
26066
|
const runIds = runRows.map((r) => r.id);
|
|
25978
26067
|
let examined = 0;
|
|
25979
26068
|
let updated = 0;
|
|
26069
|
+
let wouldUpdate = 0;
|
|
25980
26070
|
let mentioned = 0;
|
|
25981
|
-
if (runIds.length === 0)
|
|
26071
|
+
if (runIds.length === 0) {
|
|
26072
|
+
return isDryRun ? { examined, updated, wouldUpdate, mentioned } : { examined, updated, mentioned };
|
|
26073
|
+
}
|
|
25982
26074
|
const projectDomains = effectiveDomains({
|
|
25983
26075
|
canonicalDomain: project.canonicalDomain,
|
|
25984
26076
|
ownedDomains: parseJsonColumn(project.ownedDomains, [])
|
|
@@ -25998,7 +26090,7 @@ function backfillProjectAnswerMentions(db, projectId) {
|
|
|
25998
26090
|
competitorOverlap: querySnapshots.competitorOverlap,
|
|
25999
26091
|
recommendedCompetitors: querySnapshots.recommendedCompetitors,
|
|
26000
26092
|
rawResponse: querySnapshots.rawResponse
|
|
26001
|
-
}).from(querySnapshots).where(
|
|
26093
|
+
}).from(querySnapshots).where(inArray11(querySnapshots.runId, batchRunIds)).all();
|
|
26002
26094
|
const pendingUpdates = [];
|
|
26003
26095
|
for (const snapshot of snapshotRows) {
|
|
26004
26096
|
examined++;
|
|
@@ -26041,29 +26133,36 @@ function backfillProjectAnswerMentions(db, projectId) {
|
|
|
26041
26133
|
}
|
|
26042
26134
|
}
|
|
26043
26135
|
if (pendingUpdates.length > 0) {
|
|
26044
|
-
|
|
26045
|
-
|
|
26046
|
-
|
|
26047
|
-
|
|
26048
|
-
|
|
26049
|
-
|
|
26136
|
+
if (isDryRun) {
|
|
26137
|
+
wouldUpdate += pendingUpdates.length;
|
|
26138
|
+
} else {
|
|
26139
|
+
db.transaction((tx) => {
|
|
26140
|
+
for (const update of pendingUpdates) {
|
|
26141
|
+
tx.update(querySnapshots).set(update.patch).where(eq34(querySnapshots.id, update.id)).run();
|
|
26142
|
+
}
|
|
26143
|
+
});
|
|
26144
|
+
updated += pendingUpdates.length;
|
|
26145
|
+
}
|
|
26050
26146
|
}
|
|
26051
26147
|
}
|
|
26052
|
-
return { examined, updated, mentioned };
|
|
26148
|
+
return isDryRun ? { examined, updated, wouldUpdate, mentioned } : { examined, updated, mentioned };
|
|
26053
26149
|
}
|
|
26054
26150
|
async function backfillAnswerMentionsCommand(opts) {
|
|
26055
26151
|
const config = loadConfig();
|
|
26056
26152
|
const db = createClient(config.database);
|
|
26057
26153
|
migrate(db);
|
|
26058
26154
|
const projectFilter = opts?.project?.trim();
|
|
26155
|
+
const isDryRun = opts?.dryRun === true;
|
|
26059
26156
|
const scopedProjects = projectFilter ? db.select().from(projects).where(eq34(projects.name, projectFilter)).all() : db.select().from(projects).all();
|
|
26060
26157
|
let examined = 0;
|
|
26061
26158
|
let updated = 0;
|
|
26159
|
+
let wouldUpdate = 0;
|
|
26062
26160
|
let mentioned = 0;
|
|
26063
26161
|
for (const project of scopedProjects) {
|
|
26064
|
-
const result2 = backfillProjectAnswerMentions(db, project.id);
|
|
26162
|
+
const result2 = backfillProjectAnswerMentions(db, project.id, { dryRun: isDryRun });
|
|
26065
26163
|
examined += result2.examined;
|
|
26066
26164
|
updated += result2.updated;
|
|
26165
|
+
wouldUpdate += result2.wouldUpdate ?? 0;
|
|
26067
26166
|
mentioned += result2.mentioned;
|
|
26068
26167
|
}
|
|
26069
26168
|
const result = {
|
|
@@ -26073,16 +26172,29 @@ async function backfillAnswerMentionsCommand(opts) {
|
|
|
26073
26172
|
updated,
|
|
26074
26173
|
mentioned
|
|
26075
26174
|
};
|
|
26175
|
+
if (isDryRun) {
|
|
26176
|
+
result.dryRun = true;
|
|
26177
|
+
result.wouldUpdate = wouldUpdate;
|
|
26178
|
+
}
|
|
26076
26179
|
if (opts?.format === "json") {
|
|
26077
26180
|
console.log(JSON.stringify(result, null, 2));
|
|
26078
26181
|
return;
|
|
26079
26182
|
}
|
|
26080
|
-
console.log(
|
|
26081
|
-
|
|
26082
|
-
console.log(`
|
|
26083
|
-
console.log(`
|
|
26084
|
-
console.log(`
|
|
26085
|
-
|
|
26183
|
+
console.log(`Answer mentions backfill ${isDryRun ? "preview" : "complete"}.
|
|
26184
|
+
`);
|
|
26185
|
+
if (projectFilter) console.log(` Project: ${projectFilter}`);
|
|
26186
|
+
console.log(` Projects: ${scopedProjects.length}`);
|
|
26187
|
+
console.log(` Examined: ${examined}`);
|
|
26188
|
+
if (isDryRun) {
|
|
26189
|
+
console.log(` Would update: ${wouldUpdate}`);
|
|
26190
|
+
} else {
|
|
26191
|
+
console.log(` Updated: ${updated}`);
|
|
26192
|
+
}
|
|
26193
|
+
console.log(` Mentioned: ${mentioned}`);
|
|
26194
|
+
if (isDryRun) {
|
|
26195
|
+
console.log(`
|
|
26196
|
+
No DB writes performed. Re-run without --dry-run to apply.`);
|
|
26197
|
+
}
|
|
26086
26198
|
}
|
|
26087
26199
|
function readStoredGroundingSources(rawResponse) {
|
|
26088
26200
|
const envelope = parseJsonColumn(rawResponse, {});
|
|
@@ -26101,7 +26213,7 @@ function readStoredGroundingSources(rawResponse) {
|
|
|
26101
26213
|
return result;
|
|
26102
26214
|
}
|
|
26103
26215
|
async function backfillInsightsCommand(project, opts) {
|
|
26104
|
-
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-
|
|
26216
|
+
const { IntelligenceService: IntelligenceService2 } = await import("./intelligence-service-CIGYPPMR.js");
|
|
26105
26217
|
const config = loadConfig();
|
|
26106
26218
|
const db = createClient(config.database);
|
|
26107
26219
|
migrate(db);
|
|
@@ -26419,7 +26531,7 @@ var Scheduler = class {
|
|
|
26419
26531
|
};
|
|
26420
26532
|
|
|
26421
26533
|
// src/notifier.ts
|
|
26422
|
-
import { eq as eq36, desc as desc16, and as and26, inArray as
|
|
26534
|
+
import { eq as eq36, desc as desc16, and as and26, inArray as inArray12, or as or5 } from "drizzle-orm";
|
|
26423
26535
|
import crypto31 from "crypto";
|
|
26424
26536
|
var log10 = createLogger("Notifier");
|
|
26425
26537
|
var Notifier = class {
|
|
@@ -26572,13 +26684,13 @@ var Notifier = class {
|
|
|
26572
26684
|
provider: querySnapshots.provider,
|
|
26573
26685
|
location: querySnapshots.location,
|
|
26574
26686
|
citationState: querySnapshots.citationState
|
|
26575
|
-
}).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(
|
|
26687
|
+
}).from(querySnapshots).leftJoin(queries, eq36(querySnapshots.queryId, queries.id)).where(inArray12(querySnapshots.runId, currentRunIds)).all();
|
|
26576
26688
|
const previousSnapshots = this.db.select({
|
|
26577
26689
|
queryId: querySnapshots.queryId,
|
|
26578
26690
|
provider: querySnapshots.provider,
|
|
26579
26691
|
location: querySnapshots.location,
|
|
26580
26692
|
citationState: querySnapshots.citationState
|
|
26581
|
-
}).from(querySnapshots).where(
|
|
26693
|
+
}).from(querySnapshots).where(inArray12(querySnapshots.runId, previousRunIds)).all();
|
|
26582
26694
|
const prevMap = /* @__PURE__ */ new Map();
|
|
26583
26695
|
for (const s of previousSnapshots) {
|
|
26584
26696
|
if (s.queryId == null) continue;
|
|
@@ -27091,7 +27203,7 @@ function resolveSessionProviderAndModel(config, opts) {
|
|
|
27091
27203
|
|
|
27092
27204
|
// src/agent/memory-store.ts
|
|
27093
27205
|
import crypto32 from "crypto";
|
|
27094
|
-
import { and as and27, desc as desc17, eq as eq38, like as like2, sql as
|
|
27206
|
+
import { and as and27, desc as desc17, eq as eq38, like as like2, sql as sql15 } from "drizzle-orm";
|
|
27095
27207
|
var COMPACTION_KEY_PREFIX = "compaction:";
|
|
27096
27208
|
var COMPACTION_NOTES_PER_SESSION = 3;
|
|
27097
27209
|
function rowToDto2(row) {
|
|
@@ -27177,7 +27289,7 @@ function writeCompactionNote(db, args) {
|
|
|
27177
27289
|
).orderBy(desc17(agentMemory.updatedAt)).all();
|
|
27178
27290
|
const stale = existing.slice(COMPACTION_NOTES_PER_SESSION).map((r) => r.id);
|
|
27179
27291
|
if (stale.length > 0) {
|
|
27180
|
-
tx.delete(agentMemory).where(
|
|
27292
|
+
tx.delete(agentMemory).where(sql15`${agentMemory.id} IN (${sql15.join(stale.map((s) => sql15`${s}`), sql15`, `)})`).run();
|
|
27181
27293
|
}
|
|
27182
27294
|
const row = tx.select().from(agentMemory).where(and27(eq38(agentMemory.projectId, args.projectId), eq38(agentMemory.key, key))).get();
|
|
27183
27295
|
if (row) inserted = rowToDto2(row);
|