@canonry/canonry 4.136.1 → 4.136.2
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/{AuditHistoryPanel-B73VrWNb.js → AuditHistoryPanel-DtUGiN21.js} +1 -1
- package/assets/assets/{BacklinksPage-CLVMg_qx.js → BacklinksPage-BGwtmwfz.js} +1 -1
- package/assets/assets/{ChartPrimitives-P81CbvWc.js → ChartPrimitives-Df2UC1g3.js} +1 -1
- package/assets/assets/{HistoryPage-IcDqWBYW.js → HistoryPage-AtaMVEUx.js} +1 -1
- package/assets/assets/{ProjectPage-DTivvVIl.js → ProjectPage-OlBjxXNx.js} +1 -1
- package/assets/assets/{RunRow-BTH3-Tml.js → RunRow-Cmf-5xVo.js} +1 -1
- package/assets/assets/{RunsPage-B0k5wjFN.js → RunsPage-BAPy9Yri.js} +1 -1
- package/assets/assets/{SettingsPage-DGnT3Vdr.js → SettingsPage-BXO7WpTo.js} +1 -1
- package/assets/assets/{TrafficPage-D79PC0bB.js → TrafficPage-IH-wiPu2.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-BYEVjfFm.js → TrafficSourceDetailPage-CTTN-P52.js} +1 -1
- package/assets/assets/{arrow-left-DCdFYJK-.js → arrow-left-BwzAIeJ_.js} +1 -1
- package/assets/assets/{extract-error-message-bM0aUBUI.js → extract-error-message-BIcIoJLN.js} +1 -1
- package/assets/assets/index-BPpSrJY0.js +210 -0
- package/assets/assets/{trash-2-CEz2iXBv.js → trash-2-C50OeBAV.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-PIMWEIJ3.js → chunk-GGIRLRAY.js} +3042 -397
- package/dist/{chunk-YMF35LN5.js → chunk-MXR3GI5L.js} +54 -122
- package/dist/{chunk-OESGFFUO.js → chunk-SMFUCXHI.js} +1 -1
- package/dist/{chunk-YQ2RXMU2.js → chunk-YVMQWVNY.js} +91 -184
- package/dist/cli.js +4 -4
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-BKLR57RQ.js → intelligence-service-BD6BHTEJ.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +10 -10
- package/assets/assets/index-CMuA-uEN.js +0 -210
|
@@ -248,6 +248,8 @@ import {
|
|
|
248
248
|
gscUrlInspectionDtoSchema,
|
|
249
249
|
hasLocationLabel,
|
|
250
250
|
healthSnapshotDtoSchema,
|
|
251
|
+
hostMatchesAnyDomain,
|
|
252
|
+
hostMatchesDomain,
|
|
251
253
|
hostOf,
|
|
252
254
|
indexingRequestResponseDtoSchema,
|
|
253
255
|
internalError,
|
|
@@ -336,6 +338,9 @@ import {
|
|
|
336
338
|
summarizeCheckResults,
|
|
337
339
|
surfaceClassFromCompetitorType,
|
|
338
340
|
surfaceClassLabel,
|
|
341
|
+
textContainsAnyBrandAlias,
|
|
342
|
+
textContainsBrandAlias,
|
|
343
|
+
textContainsDomain,
|
|
339
344
|
trafficBackfillResponseSchema,
|
|
340
345
|
trafficConnectVercelRequestSchema,
|
|
341
346
|
trafficConnectWordpressRequestSchema,
|
|
@@ -368,7 +373,7 @@ import {
|
|
|
368
373
|
wordpressSchemaDeployResultDtoSchema,
|
|
369
374
|
wordpressSchemaStatusResultDtoSchema,
|
|
370
375
|
wordpressStatusDtoSchema
|
|
371
|
-
} from "./chunk-
|
|
376
|
+
} from "./chunk-GGIRLRAY.js";
|
|
372
377
|
|
|
373
378
|
// src/intelligence-service.ts
|
|
374
379
|
import { eq as eq44, desc as desc21, asc as asc9, and as and35, ne as ne6, or as or10, inArray as inArray15, gte as gte12, lte as lte8 } from "drizzle-orm";
|
|
@@ -5466,24 +5471,19 @@ function groupInsights(insights2, keyFn = (i) => JSON.stringify([i.query, i.prov
|
|
|
5466
5471
|
var TRANSACTIONAL_RE = /\b(?:buy|price|pricing|cost|hire|near me|services?|agency|consultant|company)\b/i;
|
|
5467
5472
|
var INFORMATIONAL_RE = /\b(?:what|how|why|when|guide|tutorial|vs|versus|alternatives?|examples?|definition)\b/i;
|
|
5468
5473
|
var MIN_BRAND_TOKEN_LENGTH = 3;
|
|
5469
|
-
function compact(value) {
|
|
5470
|
-
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
5471
|
-
}
|
|
5472
5474
|
function buildBrandTokens(canonicalDomain, brandNames = []) {
|
|
5473
5475
|
const seen = /* @__PURE__ */ new Set();
|
|
5474
|
-
const
|
|
5475
|
-
const stemCompact = compact(stem);
|
|
5476
|
+
const stemCompact = brandKeyFromText(brandLabelFromDomain(canonicalDomain));
|
|
5476
5477
|
if (stemCompact.length >= MIN_BRAND_TOKEN_LENGTH) seen.add(stemCompact);
|
|
5477
5478
|
for (const name of brandNames) {
|
|
5478
5479
|
if (!name) continue;
|
|
5479
|
-
const nameCompact =
|
|
5480
|
+
const nameCompact = brandKeyFromText(name);
|
|
5480
5481
|
if (nameCompact.length >= MIN_BRAND_TOKEN_LENGTH) seen.add(nameCompact);
|
|
5481
5482
|
}
|
|
5482
5483
|
return [...seen];
|
|
5483
5484
|
}
|
|
5484
5485
|
function categorizeQueryByIntent(query, brandTokens) {
|
|
5485
|
-
|
|
5486
|
-
if (brandTokens.length > 0 && brandTokens.some((t) => compactQuery.includes(t))) {
|
|
5486
|
+
if (textContainsAnyBrandAlias(query, brandTokens)) {
|
|
5487
5487
|
return "brand";
|
|
5488
5488
|
}
|
|
5489
5489
|
if (TRANSACTIONAL_RE.test(query)) return "lead-gen";
|
|
@@ -5551,12 +5551,7 @@ function buildCitationScorecard(snapshots, queryLookup) {
|
|
|
5551
5551
|
|
|
5552
5552
|
// ../intelligence/src/domain-matching.ts
|
|
5553
5553
|
function citedDomainBelongsToProject(citedDomain, projectDomains) {
|
|
5554
|
-
|
|
5555
|
-
for (const domain of projectDomains) {
|
|
5556
|
-
const normalized = normalizeProjectDomain(domain);
|
|
5557
|
-
if (candidate === normalized || candidate.endsWith(`.${normalized}`)) return true;
|
|
5558
|
-
}
|
|
5559
|
-
return false;
|
|
5554
|
+
return hostMatchesAnyDomain(citedDomain, projectDomains);
|
|
5560
5555
|
}
|
|
5561
5556
|
|
|
5562
5557
|
// ../intelligence/src/competitor-landscape.ts
|
|
@@ -5578,11 +5573,8 @@ function buildCompetitorLandscape(snapshots, competitorDomains, projectDomains,
|
|
|
5578
5573
|
entry.count++;
|
|
5579
5574
|
if (q) entry.queries.add(q);
|
|
5580
5575
|
}
|
|
5581
|
-
const competitorNorm = normalizeUrlDomain(competitor);
|
|
5582
5576
|
for (const gs of snap.groundingSources) {
|
|
5583
|
-
|
|
5584
|
-
if (!host) continue;
|
|
5585
|
-
if (host === competitorNorm || host.endsWith(`.${competitorNorm}`)) {
|
|
5577
|
+
if (hostMatchesDomain(gs.uri, competitor)) {
|
|
5586
5578
|
const entry = competitorMap.get(competitor);
|
|
5587
5579
|
const pageQueries = entry.pages.get(gs.uri) ?? /* @__PURE__ */ new Set();
|
|
5588
5580
|
if (q) pageQueries.add(q);
|
|
@@ -5616,16 +5608,6 @@ function buildCompetitorLandscape(snapshots, competitorDomains, projectDomains,
|
|
|
5616
5608
|
competitorRows.sort((a, b) => b.citationCount - a.citationCount);
|
|
5617
5609
|
return { projectCitationCount, competitors: competitorRows };
|
|
5618
5610
|
}
|
|
5619
|
-
function normalizeUrlDomain(domain) {
|
|
5620
|
-
return domain.toLowerCase().replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/$/, "");
|
|
5621
|
-
}
|
|
5622
|
-
function extractHostFromUri(uri) {
|
|
5623
|
-
try {
|
|
5624
|
-
return new URL(uri).hostname;
|
|
5625
|
-
} catch {
|
|
5626
|
-
return "";
|
|
5627
|
-
}
|
|
5628
|
-
}
|
|
5629
5611
|
|
|
5630
5612
|
// ../intelligence/src/mention-landscape.ts
|
|
5631
5613
|
function buildMentionLandscape(snapshots, competitorDomains, projectBrandNames, projectDomains, queryLookup) {
|
|
@@ -6043,10 +6025,9 @@ function buildCompetitorPressureScore(snapshots, competitorDomains, totalTracked
|
|
|
6043
6025
|
trend: []
|
|
6044
6026
|
};
|
|
6045
6027
|
}
|
|
6046
|
-
const competitorSet = new Set(competitorDomains);
|
|
6047
6028
|
let overlapCount = 0;
|
|
6048
6029
|
for (const snap of snapshots) {
|
|
6049
|
-
if (snap.competitorOverlap.some((
|
|
6030
|
+
if (snap.competitorOverlap.some((domain) => hostMatchesAnyDomain(domain, competitorDomains))) {
|
|
6050
6031
|
overlapCount++;
|
|
6051
6032
|
}
|
|
6052
6033
|
}
|
|
@@ -6071,7 +6052,7 @@ function buildOverviewCompetitors(snapshots, competitors2, queryLookup) {
|
|
|
6071
6052
|
return competitors2.map((competitor, index2) => {
|
|
6072
6053
|
const citedQuerySet = /* @__PURE__ */ new Set();
|
|
6073
6054
|
for (const snap of snapshots) {
|
|
6074
|
-
if (snap.competitorOverlap.
|
|
6055
|
+
if (snap.competitorOverlap.some((domain) => hostMatchesDomain(domain, competitor.domain)) || snap.citedDomains.some((domain) => hostMatchesDomain(domain, competitor.domain))) {
|
|
6075
6056
|
if (snap.queryId) citedQuerySet.add(snap.queryId);
|
|
6076
6057
|
}
|
|
6077
6058
|
}
|
|
@@ -6218,15 +6199,22 @@ function buildMentionShare(snapshots, options) {
|
|
|
6218
6199
|
let projectMentionSnapshots = 0;
|
|
6219
6200
|
let snapshotsWithAnswerText = 0;
|
|
6220
6201
|
const competitorCounts = /* @__PURE__ */ new Map();
|
|
6221
|
-
|
|
6202
|
+
const competitorAliases = /* @__PURE__ */ new Map();
|
|
6203
|
+
for (const competitor of options.competitors) {
|
|
6204
|
+
competitorCounts.set(competitor.domain, 0);
|
|
6205
|
+
competitorAliases.set(
|
|
6206
|
+
competitor.domain,
|
|
6207
|
+
competitor.brandTokens.filter((alias) => brandKeyFromText(alias).length >= 3)
|
|
6208
|
+
);
|
|
6209
|
+
}
|
|
6222
6210
|
for (const snap of snapshots) {
|
|
6223
6211
|
const text2 = snap.answerText ?? "";
|
|
6224
6212
|
if (text2.length === 0) continue;
|
|
6225
6213
|
snapshotsWithAnswerText++;
|
|
6226
6214
|
if (snap.projectMentioned) projectMentionSnapshots++;
|
|
6227
|
-
const answerBrandKey = brandKeyFromText(text2);
|
|
6228
6215
|
for (const competitor of options.competitors) {
|
|
6229
|
-
|
|
6216
|
+
const aliases = competitorAliases.get(competitor.domain) ?? [];
|
|
6217
|
+
if (textContainsAnyBrandAlias(text2, aliases)) {
|
|
6230
6218
|
competitorCounts.set(competitor.domain, (competitorCounts.get(competitor.domain) ?? 0) + 1);
|
|
6231
6219
|
}
|
|
6232
6220
|
}
|
|
@@ -6269,16 +6257,6 @@ function mentionShareTone(score) {
|
|
|
6269
6257
|
if (score >= 25) return "caution";
|
|
6270
6258
|
return "negative";
|
|
6271
6259
|
}
|
|
6272
|
-
function competitorMentioned(text2, answerBrandKey, brandTokens) {
|
|
6273
|
-
for (const token of brandTokens) {
|
|
6274
|
-
if (token.length < 3) continue;
|
|
6275
|
-
const escaped = token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6276
|
-
if (new RegExp(`\\b${escaped}\\b`, "i").test(text2)) return true;
|
|
6277
|
-
const tokenBrandKey = brandKeyFromText(token);
|
|
6278
|
-
if (tokenBrandKey.length >= 3 && answerBrandKey.includes(tokenBrandKey)) return true;
|
|
6279
|
-
}
|
|
6280
|
-
return false;
|
|
6281
|
-
}
|
|
6282
6260
|
function describe(parts) {
|
|
6283
6261
|
const { score, projectMentionSnapshots, competitorMentionSnapshots, perCompetitor } = parts;
|
|
6284
6262
|
if (projectMentionSnapshots === 0 && competitorMentionSnapshots === 0) {
|
|
@@ -9779,14 +9757,7 @@ async function analyticsRoutes(app) {
|
|
|
9779
9757
|
}
|
|
9780
9758
|
var ANCHOR_SWEEP_SCAN_LIMIT = 60;
|
|
9781
9759
|
function isProviderInfraDomain(uri) {
|
|
9782
|
-
|
|
9783
|
-
const host = new URL(uri).hostname.toLowerCase();
|
|
9784
|
-
for (const blocked of AI_PROVIDER_INFRA_DOMAINS) {
|
|
9785
|
-
if (host === blocked || host.endsWith(`.${blocked}`)) return true;
|
|
9786
|
-
}
|
|
9787
|
-
} catch {
|
|
9788
|
-
}
|
|
9789
|
-
return false;
|
|
9760
|
+
return hostMatchesAnyDomain(uri, AI_PROVIDER_INFRA_DOMAINS);
|
|
9790
9761
|
}
|
|
9791
9762
|
function parseGroundingSources(rawResponse) {
|
|
9792
9763
|
const parsed = parseJsonColumn(rawResponse, {});
|
|
@@ -10218,12 +10189,12 @@ import { and as and9, eq as eq13, desc as desc5, inArray as inArray6 } from "dri
|
|
|
10218
10189
|
var RECENT_RUNS_WINDOW = 5;
|
|
10219
10190
|
function loadOrchestratorInput(db, project, locationFilter = void 0) {
|
|
10220
10191
|
const projectId = project.id;
|
|
10221
|
-
const ownDomain =
|
|
10192
|
+
const ownDomain = hostOf(project.canonicalDomain) ?? "";
|
|
10222
10193
|
const ownedDomains = project.ownedDomains ?? [];
|
|
10223
|
-
const ourDomains = /* @__PURE__ */ new Set([ownDomain, ...ownedDomains.map(
|
|
10194
|
+
const ourDomains = /* @__PURE__ */ new Set([ownDomain, ...ownedDomains.map((domain) => hostOf(domain) ?? "")]);
|
|
10224
10195
|
const trackedQueries = listQueries(db, projectId);
|
|
10225
10196
|
const candidateQueryStrings = trackedQueries.filter(isBlogShapedQuery);
|
|
10226
|
-
const trackedCompetitors = listCompetitorDomains(db, projectId).map(
|
|
10197
|
+
const trackedCompetitors = listCompetitorDomains(db, projectId).map((domain) => hostOf(domain) ?? "");
|
|
10227
10198
|
const competitorSet = new Set(trackedCompetitors);
|
|
10228
10199
|
const recentRunIds = listRecentAnswerVisibilityRunIds(db, projectId, RECENT_RUNS_WINDOW, locationFilter);
|
|
10229
10200
|
const latestRunId = recentRunIds[0] ?? "";
|
|
@@ -10264,7 +10235,7 @@ function loadOrchestratorInput(db, project, locationFilter = void 0) {
|
|
|
10264
10235
|
}
|
|
10265
10236
|
function loadDomainClasses(db, projectId) {
|
|
10266
10237
|
const rows = db.select({ domain: domainClassifications.domain, competitorType: domainClassifications.competitorType }).from(domainClassifications).where(eq13(domainClassifications.projectId, projectId)).all();
|
|
10267
|
-
return new Map(rows.map((r) => [
|
|
10238
|
+
return new Map(rows.map((r) => [hostOf(r.domain) ?? "", r.competitorType]));
|
|
10268
10239
|
}
|
|
10269
10240
|
function buildQueryIntentModifiers(project, locationFilter) {
|
|
10270
10241
|
if (locationFilter === void 0 || locationFilter === null) return [];
|
|
@@ -10483,21 +10454,22 @@ function aggregateCandidate(opts) {
|
|
|
10483
10454
|
const isLatestRun = snap.runId === opts.latestRunId;
|
|
10484
10455
|
const competitorOverlap = snap.competitorOverlap;
|
|
10485
10456
|
for (const domain of competitorOverlap) {
|
|
10486
|
-
const normalized =
|
|
10487
|
-
|
|
10488
|
-
|
|
10457
|
+
const normalized = hostOf(domain) ?? "";
|
|
10458
|
+
const competitor = findMatchingDomain(normalized, opts.competitorSet);
|
|
10459
|
+
if (!competitor) continue;
|
|
10460
|
+
competitorTally.set(competitor, (competitorTally.get(competitor) ?? 0) + 1);
|
|
10489
10461
|
}
|
|
10490
10462
|
const grounding = extractGroundingSources(snap.rawResponse);
|
|
10491
10463
|
for (const g of grounding) {
|
|
10492
|
-
const domain =
|
|
10464
|
+
const domain = hostOf(g.uri) ?? "";
|
|
10493
10465
|
if (!domain) continue;
|
|
10494
|
-
if (opts.ourDomains
|
|
10466
|
+
if (findMatchingDomain(domain, opts.ourDomains)) {
|
|
10495
10467
|
if (isLatestRun) ourCitedInLatestRun = true;
|
|
10496
10468
|
recordGroundingHit(ourGroundingTally, g, domain, snap.provider);
|
|
10497
10469
|
continue;
|
|
10498
10470
|
}
|
|
10499
10471
|
citedSurfaceTally.set(domain, (citedSurfaceTally.get(domain) ?? 0) + 1);
|
|
10500
|
-
if (!opts.competitorSet
|
|
10472
|
+
if (!findMatchingDomain(domain, opts.competitorSet)) continue;
|
|
10501
10473
|
recordGroundingHit(competitorGroundingTally, g, domain, snap.provider);
|
|
10502
10474
|
}
|
|
10503
10475
|
}
|
|
@@ -10519,6 +10491,9 @@ function aggregateCandidate(opts) {
|
|
|
10519
10491
|
runsOfHistory: new Set(opts.snapshots.map((s) => s.runId)).size
|
|
10520
10492
|
};
|
|
10521
10493
|
}
|
|
10494
|
+
function findMatchingDomain(candidate, domains) {
|
|
10495
|
+
return [...domains].find((domain) => hostMatchesDomain(candidate, domain));
|
|
10496
|
+
}
|
|
10522
10497
|
function recordGroundingHit(tally, g, domain, provider) {
|
|
10523
10498
|
const existing = tally.get(g.uri);
|
|
10524
10499
|
if (existing) {
|
|
@@ -10571,16 +10546,6 @@ function extractGroundingSources(rawResponse) {
|
|
|
10571
10546
|
}
|
|
10572
10547
|
return [];
|
|
10573
10548
|
}
|
|
10574
|
-
function extractHostFromUri2(uri) {
|
|
10575
|
-
try {
|
|
10576
|
-
return new URL(uri).hostname;
|
|
10577
|
-
} catch {
|
|
10578
|
-
return "";
|
|
10579
|
-
}
|
|
10580
|
-
}
|
|
10581
|
-
function normalizeDomain(domain) {
|
|
10582
|
-
return domain.toLowerCase().replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/$/, "");
|
|
10583
|
-
}
|
|
10584
10549
|
function extractPath2(url) {
|
|
10585
10550
|
if (!url) return "";
|
|
10586
10551
|
const trimmed = url.trim();
|
|
@@ -14943,10 +14908,7 @@ function stableUnique(values, normalize) {
|
|
|
14943
14908
|
return result;
|
|
14944
14909
|
}
|
|
14945
14910
|
function matchesHost(value, marketingHosts) {
|
|
14946
|
-
|
|
14947
|
-
return marketingHosts.some(
|
|
14948
|
-
(candidate) => normalized === candidate || normalized.endsWith(`.${candidate}`)
|
|
14949
|
-
);
|
|
14911
|
+
return hostMatchesAnyDomain(value, marketingHosts);
|
|
14950
14912
|
}
|
|
14951
14913
|
function normalizeLandingPage(value) {
|
|
14952
14914
|
return normalizeUrlPath(value) ?? UNKNOWN_LANDING_PAGE;
|
|
@@ -15016,10 +14978,10 @@ function buildGaMeasurementAnalysis(db, projectName, options = {}) {
|
|
|
15016
14978
|
const conditions = [];
|
|
15017
14979
|
if (parsedHostScope.data === "marketing") {
|
|
15018
14980
|
const lowerHost = sql9`lower(${hostColumn})`;
|
|
15019
|
-
const
|
|
14981
|
+
const normalizedHost = sql9`case when ${lowerHost} like 'www.%' then substr(${lowerHost}, 5) else ${lowerHost} end`;
|
|
15020
14982
|
conditions.push(or4(...marketingHosts.flatMap((host) => [
|
|
15021
|
-
sql9`${
|
|
15022
|
-
sql9`${
|
|
14983
|
+
sql9`${normalizedHost} = ${host}`,
|
|
14984
|
+
sql9`${normalizedHost} like ${`%.${host}`}`
|
|
15023
14985
|
])));
|
|
15024
14986
|
}
|
|
15025
14987
|
if (pathPrefix && pathPrefix !== "/") {
|
|
@@ -15276,11 +15238,7 @@ var normalizedPath = (value) => {
|
|
|
15276
15238
|
return normalized.split("?")[0] || "/";
|
|
15277
15239
|
};
|
|
15278
15240
|
var inRange = (date, startDate, endDate) => date >= startDate && date <= endDate;
|
|
15279
|
-
var
|
|
15280
|
-
var matchesMarketingHost = (value, marketingHosts) => {
|
|
15281
|
-
const hostname = normalizedHost(value);
|
|
15282
|
-
return marketingHosts.some((candidate) => hostname === candidate || hostname.endsWith(`.${candidate}`));
|
|
15283
|
-
};
|
|
15241
|
+
var matchesMarketingHost = (value, marketingHosts) => hostMatchesAnyDomain(value, marketingHosts);
|
|
15284
15242
|
var pageMatchesMarketingHost = (value, marketingHosts) => {
|
|
15285
15243
|
return matchesMarketingHost(hostOf(value) ?? "", marketingHosts);
|
|
15286
15244
|
};
|
|
@@ -15360,7 +15318,9 @@ function buildOrganicEvidence(db, projectName, periodDays) {
|
|
|
15360
15318
|
const nativeAcquisition = measurement.acquisition;
|
|
15361
15319
|
const nativeAcquisitionActive = nativeAcquisition.status !== "never-synced";
|
|
15362
15320
|
const marketingHosts = measurement.filters.marketingHosts;
|
|
15363
|
-
const normalizedMarketingHosts = [...new Set(
|
|
15321
|
+
const normalizedMarketingHosts = [...new Set(
|
|
15322
|
+
marketingHosts.map((host) => hostOf(host)).filter((host) => host !== null)
|
|
15323
|
+
)];
|
|
15364
15324
|
const nativeStartDate = nativeAcquisition.periods[0]?.startDate;
|
|
15365
15325
|
const nativeEndDate = nativeAcquisition.periods.at(-1)?.endDate;
|
|
15366
15326
|
const gscCoverage = aggregateCoverage(db.select({
|
|
@@ -15813,7 +15773,7 @@ async function citationRoutes(app) {
|
|
|
15813
15773
|
queryId: s.queryId,
|
|
15814
15774
|
runCreatedAt: runCreatedAt.get(s.runId) ?? s.createdAt
|
|
15815
15775
|
}));
|
|
15816
|
-
const projectCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq19(competitors.projectId, project.id)).all().map((c) =>
|
|
15776
|
+
const projectCompetitors = app.db.select({ domain: competitors.domain }).from(competitors).where(eq19(competitors.projectId, project.id)).all().map((c) => hostOf(c.domain) ?? "").filter((d) => d.length > 0);
|
|
15817
15777
|
const response = computeCitationVisibility({
|
|
15818
15778
|
queries: projectQueries.map((q) => ({ id: q.id, query: q.query })),
|
|
15819
15779
|
snapshots,
|
|
@@ -15887,18 +15847,19 @@ function computeCitationVisibility(input) {
|
|
|
15887
15847
|
totalProviders: providers.length
|
|
15888
15848
|
});
|
|
15889
15849
|
}
|
|
15890
|
-
const competitorSet = new Set(competitorDomains);
|
|
15891
15850
|
const competitorGaps = [];
|
|
15892
15851
|
const queryById = new Map(qs.map((q) => [q.id, q.query]));
|
|
15893
15852
|
for (const snap of latestByPair.values()) {
|
|
15894
15853
|
if (citationStateToCited(snap.citationState)) continue;
|
|
15895
|
-
if (
|
|
15854
|
+
if (competitorDomains.length === 0) continue;
|
|
15896
15855
|
const cited = snap.citedDomains;
|
|
15897
15856
|
const overlap = snap.competitorOverlap;
|
|
15898
15857
|
const candidates = new Set(
|
|
15899
|
-
[...cited, ...overlap].map((d) =>
|
|
15858
|
+
[...cited, ...overlap].map((d) => hostOf(d) ?? "").filter((d) => d.length > 0)
|
|
15859
|
+
);
|
|
15860
|
+
const citingCompetitors = competitorDomains.filter(
|
|
15861
|
+
(competitor) => [...candidates].some((candidate) => hostMatchesDomain(candidate, competitor))
|
|
15900
15862
|
);
|
|
15901
|
-
const citingCompetitors = Array.from(candidates).filter((d) => competitorSet.has(d));
|
|
15902
15863
|
if (citingCompetitors.length === 0) continue;
|
|
15903
15864
|
competitorGaps.push({
|
|
15904
15865
|
queryId: snap.queryId,
|
|
@@ -15940,9 +15901,6 @@ function computeCitationVisibility(input) {
|
|
|
15940
15901
|
status: "ready"
|
|
15941
15902
|
};
|
|
15942
15903
|
}
|
|
15943
|
-
function normalizeDomain2(domain) {
|
|
15944
|
-
return domain.toLowerCase().trim().replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/$/, "");
|
|
15945
|
-
}
|
|
15946
15904
|
|
|
15947
15905
|
// ../api-routes/src/visibility-stats.ts
|
|
15948
15906
|
import { and as and16, desc as desc9, eq as eq20, inArray as inArray9 } from "drizzle-orm";
|
|
@@ -15952,9 +15910,6 @@ var VISIBILITY_COMPARE_MIN_RUNS = 5;
|
|
|
15952
15910
|
function basketPairKey(queryId, provider) {
|
|
15953
15911
|
return JSON.stringify([queryId, provider]);
|
|
15954
15912
|
}
|
|
15955
|
-
function citedHostMatches(citedHost, competitorHost) {
|
|
15956
|
-
return citedHost === competitorHost || citedHost.endsWith(`.${competitorHost}`);
|
|
15957
|
-
}
|
|
15958
15913
|
function restrict(snapshots, attribution, pairs) {
|
|
15959
15914
|
const out = [];
|
|
15960
15915
|
for (const snap of snapshots) {
|
|
@@ -16035,7 +15990,7 @@ function countPeriod(snaps, competitors2) {
|
|
|
16035
15990
|
if (competitorHosts.length > 0 && snap.citedDomains.length > 0) {
|
|
16036
15991
|
const citedHosts = snap.citedDomains.map((d) => hostOf(d)).filter((h) => h !== null && h.length > 0);
|
|
16037
15992
|
for (const compHost of competitorHosts) {
|
|
16038
|
-
if (citedHosts.some((ch) =>
|
|
15993
|
+
if (citedHosts.some((ch) => hostMatchesDomain(ch, compHost))) competitorCited += 1;
|
|
16039
15994
|
}
|
|
16040
15995
|
}
|
|
16041
15996
|
}
|
|
@@ -24844,11 +24799,9 @@ function stableStringify3(value) {
|
|
|
24844
24799
|
|
|
24845
24800
|
// ../api-routes/src/google.ts
|
|
24846
24801
|
function isOnProjectDomain(url, projectDomain) {
|
|
24847
|
-
|
|
24848
|
-
|
|
24849
|
-
|
|
24850
|
-
return false;
|
|
24851
|
-
}
|
|
24802
|
+
const urlHost = hostOf(url);
|
|
24803
|
+
const projectHost = hostOf(projectDomain);
|
|
24804
|
+
return urlHost !== null && projectHost !== null && urlHost === projectHost;
|
|
24852
24805
|
}
|
|
24853
24806
|
function scopesForConnectionType(type) {
|
|
24854
24807
|
switch (type) {
|
|
@@ -24870,12 +24823,10 @@ function isSitemapOwnedByProperty(sitemapUrl, propertyId, canonicalDomain) {
|
|
|
24870
24823
|
if (sitemap.protocol !== "http:" && sitemap.protocol !== "https:") return false;
|
|
24871
24824
|
if (/^sc-domain:/i.test(propertyId)) {
|
|
24872
24825
|
const domain = propertyId.slice("sc-domain:".length).toLowerCase();
|
|
24873
|
-
|
|
24874
|
-
return host === domain || host.endsWith(`.${domain}`);
|
|
24826
|
+
return hostMatchesDomain(sitemap.hostname, domain);
|
|
24875
24827
|
}
|
|
24876
24828
|
if (/^\d+$/.test(propertyId)) {
|
|
24877
|
-
|
|
24878
|
-
return sitemap.hostname.toLowerCase().replace(/^www\./, "") === domain;
|
|
24829
|
+
return hostOf(sitemap.hostname) === hostOf(canonicalDomain);
|
|
24879
24830
|
}
|
|
24880
24831
|
try {
|
|
24881
24832
|
const property = new URL(propertyId);
|
|
@@ -38436,9 +38387,7 @@ function normalizeHost2(host) {
|
|
|
38436
38387
|
return host.trim().toLowerCase().replace(/^www\./, "");
|
|
38437
38388
|
}
|
|
38438
38389
|
function hostMatches(host, domain) {
|
|
38439
|
-
|
|
38440
|
-
const normalizedDomain = normalizeHost2(domain);
|
|
38441
|
-
return normalizedHost2 === normalizedDomain || normalizedHost2.endsWith(`.${normalizedDomain}`);
|
|
38390
|
+
return hostMatchesDomain(host, domain);
|
|
38442
38391
|
}
|
|
38443
38392
|
function utmTokenMatchesDomain(utmSource, domain) {
|
|
38444
38393
|
if (hostMatches(utmSource, domain)) return true;
|
|
@@ -38447,12 +38396,7 @@ function utmTokenMatchesDomain(utmSource, domain) {
|
|
|
38447
38396
|
return Boolean(firstLabel) && normalizedUtm === firstLabel;
|
|
38448
38397
|
}
|
|
38449
38398
|
function hostFromUrl(value) {
|
|
38450
|
-
|
|
38451
|
-
try {
|
|
38452
|
-
return normalizeHost2(new URL(value).hostname);
|
|
38453
|
-
} catch {
|
|
38454
|
-
return null;
|
|
38455
|
-
}
|
|
38399
|
+
return hostOf(value);
|
|
38456
38400
|
}
|
|
38457
38401
|
function utmSourceFromQuery(queryString) {
|
|
38458
38402
|
if (!queryString) return null;
|
|
@@ -38622,13 +38566,9 @@ function sessionWindowBucket(value, windowMs) {
|
|
|
38622
38566
|
if (Number.isNaN(date.getTime())) return value;
|
|
38623
38567
|
return new Date(Math.floor(date.getTime() / windowMs) * windowMs).toISOString();
|
|
38624
38568
|
}
|
|
38625
|
-
function normalizeHost3(host) {
|
|
38626
|
-
if (!host) return null;
|
|
38627
|
-
return host.trim().toLowerCase().replace(/^www\./, "") || null;
|
|
38628
|
-
}
|
|
38629
38569
|
function sameHost(a, b) {
|
|
38630
|
-
const normalizedA =
|
|
38631
|
-
const normalizedB =
|
|
38570
|
+
const normalizedA = hostOf(a);
|
|
38571
|
+
const normalizedB = hostOf(b);
|
|
38632
38572
|
return !!normalizedA && !!normalizedB && normalizedA === normalizedB;
|
|
38633
38573
|
}
|
|
38634
38574
|
function pathFromSameOriginReferer(event) {
|
|
@@ -43888,7 +43828,7 @@ function upsertDomainClassifications(db, projectId, sessionId, competitorMap) {
|
|
|
43888
43828
|
if (competitorMap.length === 0) return;
|
|
43889
43829
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
43890
43830
|
for (const entry of competitorMap) {
|
|
43891
|
-
const domain =
|
|
43831
|
+
const domain = hostOf(entry.domain) ?? "";
|
|
43892
43832
|
if (!domain) continue;
|
|
43893
43833
|
db.insert(domainClassifications).values({
|
|
43894
43834
|
id: crypto31.randomUUID(),
|
|
@@ -44555,38 +44495,20 @@ function createLogger(module) {
|
|
|
44555
44495
|
}
|
|
44556
44496
|
|
|
44557
44497
|
// src/citation-utils.ts
|
|
44558
|
-
function domainMatches(domain, canonicalDomain) {
|
|
44559
|
-
const normalized = normalizeProjectDomain(canonicalDomain);
|
|
44560
|
-
const d = normalizeProjectDomain(domain);
|
|
44561
|
-
return d === normalized || d.endsWith(`.${normalized}`);
|
|
44562
|
-
}
|
|
44563
44498
|
function pickProjectCitedDomain(citedDomains, projectDomains) {
|
|
44564
44499
|
for (const cited of citedDomains) {
|
|
44565
|
-
if (projectDomains.some((pd) =>
|
|
44500
|
+
if (projectDomains.some((pd) => hostMatchesDomain(cited, pd))) return cited;
|
|
44566
44501
|
}
|
|
44567
44502
|
return void 0;
|
|
44568
44503
|
}
|
|
44569
44504
|
function determineCitationState(normalized, domains) {
|
|
44570
44505
|
for (const canonicalDomain of domains) {
|
|
44571
|
-
|
|
44572
|
-
if (normalized.citedDomains.some((d) => domainMatches(d, bareDomain))) {
|
|
44506
|
+
if (normalized.citedDomains.some((d) => hostMatchesDomain(d, canonicalDomain))) {
|
|
44573
44507
|
return "cited";
|
|
44574
44508
|
}
|
|
44575
|
-
const lowerDomain = bareDomain.toLowerCase();
|
|
44576
44509
|
for (const source of normalized.groundingSources) {
|
|
44577
|
-
|
|
44578
|
-
|
|
44579
|
-
if (lowerDomain.includes(".") && uri.includes(lowerDomain)) {
|
|
44580
|
-
return "cited";
|
|
44581
|
-
}
|
|
44582
|
-
} catch {
|
|
44583
|
-
}
|
|
44584
|
-
if (source.title) {
|
|
44585
|
-
const titleLower = source.title.toLowerCase().replace(/^www\./, "");
|
|
44586
|
-
if (titleLower === lowerDomain || titleLower.endsWith(`.${lowerDomain}`)) {
|
|
44587
|
-
return "cited";
|
|
44588
|
-
}
|
|
44589
|
-
}
|
|
44510
|
+
if (hostMatchesDomain(source.uri, canonicalDomain)) return "cited";
|
|
44511
|
+
if (source.title && hostMatchesDomain(source.title, canonicalDomain)) return "cited";
|
|
44590
44512
|
}
|
|
44591
44513
|
}
|
|
44592
44514
|
return "not-cited";
|
|
@@ -44595,27 +44517,25 @@ function computeCompetitorOverlap(normalized, competitorDomains) {
|
|
|
44595
44517
|
const overlapSet = /* @__PURE__ */ new Set();
|
|
44596
44518
|
for (const d of normalized.citedDomains) {
|
|
44597
44519
|
for (const cd of competitorDomains) {
|
|
44598
|
-
if (
|
|
44520
|
+
if (hostMatchesDomain(d, cd)) {
|
|
44599
44521
|
overlapSet.add(cd);
|
|
44600
44522
|
}
|
|
44601
44523
|
}
|
|
44602
44524
|
}
|
|
44603
44525
|
for (const source of normalized.groundingSources) {
|
|
44604
|
-
const uri = source.uri.toLowerCase();
|
|
44605
44526
|
for (const cd of competitorDomains) {
|
|
44606
|
-
if (uri
|
|
44527
|
+
if (hostMatchesDomain(source.uri, cd)) {
|
|
44607
44528
|
overlapSet.add(cd);
|
|
44608
44529
|
}
|
|
44609
44530
|
}
|
|
44610
44531
|
}
|
|
44611
44532
|
if (normalized.answerText) {
|
|
44612
|
-
const lowerAnswer = normalized.answerText.toLowerCase();
|
|
44613
44533
|
for (const cd of competitorDomains) {
|
|
44614
|
-
if (
|
|
44534
|
+
if (textContainsDomain(normalized.answerText, cd)) {
|
|
44615
44535
|
overlapSet.add(cd);
|
|
44616
44536
|
}
|
|
44617
44537
|
const brand = brandLabelFromDomain(cd);
|
|
44618
|
-
if (brand.length >= 4 &&
|
|
44538
|
+
if (brandKeyFromText(brand).length >= 4 && textContainsBrandAlias(normalized.answerText, brand)) {
|
|
44619
44539
|
overlapSet.add(cd);
|
|
44620
44540
|
}
|
|
44621
44541
|
}
|
|
@@ -44626,27 +44546,24 @@ function computeCitedCompetitorDomains(citedDomains, competitorDomains) {
|
|
|
44626
44546
|
const citedCompetitors = /* @__PURE__ */ new Set();
|
|
44627
44547
|
for (const citedDomain of citedDomains) {
|
|
44628
44548
|
for (const competitorDomain of competitorDomains) {
|
|
44629
|
-
if (
|
|
44549
|
+
if (hostMatchesDomain(citedDomain, competitorDomain)) citedCompetitors.add(competitorDomain);
|
|
44630
44550
|
}
|
|
44631
44551
|
}
|
|
44632
44552
|
return [...citedCompetitors];
|
|
44633
44553
|
}
|
|
44634
|
-
function escapeRegExp5(value) {
|
|
44635
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
44636
|
-
}
|
|
44637
44554
|
function extractRecommendedCompetitors(answerText, ownDomains, citedDomains, competitorDomains, ownBrandNames = []) {
|
|
44638
44555
|
if (!answerText || answerText.length < 20) return [];
|
|
44639
|
-
const
|
|
44640
|
-
ownDomains.flatMap((domain) =>
|
|
44556
|
+
const ownBrandAliases = new Set(
|
|
44557
|
+
ownDomains.flatMap((domain) => collectBrandAliasesFromDomain(domain))
|
|
44641
44558
|
);
|
|
44642
44559
|
for (const name of ownBrandNames) {
|
|
44643
|
-
|
|
44644
|
-
if (key.length >= 4) ownBrandKeys.add(key);
|
|
44560
|
+
if (brandKeyFromText(name).length >= 4) ownBrandAliases.add(name);
|
|
44645
44561
|
}
|
|
44646
|
-
const
|
|
44647
|
-
|
|
44562
|
+
const ownKeys = new Set([...ownBrandAliases].map(brandKeyFromText));
|
|
44563
|
+
const knownCompetitorAliases = new Set(
|
|
44564
|
+
[...citedDomains, ...competitorDomains].flatMap((domain) => collectBrandAliasesFromDomain(domain)).filter((alias) => !ownKeys.has(brandKeyFromText(alias)))
|
|
44648
44565
|
);
|
|
44649
|
-
if (
|
|
44566
|
+
if (knownCompetitorAliases.size === 0) return [];
|
|
44650
44567
|
const candidatePatterns = [
|
|
44651
44568
|
/^\s*(?:[-*]|\d+\.)\s+(?:\*\*)?([A-Z0-9][A-Za-z0-9][\w\s.&',/()-]{1,50}?)(?:\*\*)?\s*[:\u2014\u2013-]/gm,
|
|
44652
44569
|
/\*\*([A-Z0-9][A-Za-z0-9][\w\s.&',/()-]{1,50})\*\*/g,
|
|
@@ -44696,8 +44613,8 @@ function extractRecommendedCompetitors(answerText, ownDomains, citedDomains, com
|
|
|
44696
44613
|
if (!candidateKey) continue;
|
|
44697
44614
|
if (genericKeys.has(candidateKey)) continue;
|
|
44698
44615
|
if (candidate.split(/\s+/).length > 6) continue;
|
|
44699
|
-
if (
|
|
44700
|
-
if (!
|
|
44616
|
+
if (matchesBrandAlias(candidate, ownBrandAliases)) continue;
|
|
44617
|
+
if (!matchesBrandAlias(candidate, knownCompetitorAliases)) continue;
|
|
44701
44618
|
if (!seen.has(candidateKey)) seen.set(candidateKey, candidate);
|
|
44702
44619
|
}
|
|
44703
44620
|
}
|
|
@@ -44706,27 +44623,17 @@ function extractRecommendedCompetitors(answerText, ownDomains, citedDomains, com
|
|
|
44706
44623
|
function cleanCandidateName(candidate) {
|
|
44707
44624
|
return candidate.replace(/^[\s"'`]+|[\s"'`.,:;!?]+$/g, "").replace(/\s+/g, " ").trim();
|
|
44708
44625
|
}
|
|
44709
|
-
function
|
|
44626
|
+
function collectBrandAliasesFromDomain(domain) {
|
|
44710
44627
|
const reg = registrableDomain(domain);
|
|
44711
|
-
if (!reg)
|
|
44712
|
-
|
|
44713
|
-
|
|
44714
|
-
|
|
44715
|
-
|
|
44716
|
-
|
|
44717
|
-
|
|
44718
|
-
|
|
44719
|
-
|
|
44720
|
-
if (brand.length >= 4) keys.add(brand);
|
|
44721
|
-
return [...keys];
|
|
44722
|
-
}
|
|
44723
|
-
function matchesBrandKey(candidateKey, brandKeys) {
|
|
44724
|
-
for (const brandKey of brandKeys) {
|
|
44725
|
-
if (candidateKey === brandKey) return true;
|
|
44726
|
-
if (candidateKey.startsWith(brandKey) || candidateKey.endsWith(brandKey)) return true;
|
|
44727
|
-
if (brandKey.startsWith(candidateKey) || brandKey.endsWith(candidateKey)) return true;
|
|
44728
|
-
}
|
|
44729
|
-
return false;
|
|
44628
|
+
if (!reg) return [];
|
|
44629
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
44630
|
+
if (brandKeyFromText(reg).length >= 4) aliases.add(reg);
|
|
44631
|
+
const brand = brandLabelFromDomain(reg);
|
|
44632
|
+
if (brandKeyFromText(brand).length >= 4) aliases.add(brand);
|
|
44633
|
+
return [...aliases];
|
|
44634
|
+
}
|
|
44635
|
+
function matchesBrandAlias(candidate, aliases) {
|
|
44636
|
+
return [...aliases].some((alias) => textContainsBrandAlias(candidate, alias));
|
|
44730
44637
|
}
|
|
44731
44638
|
|
|
44732
44639
|
// src/intelligence-service.ts
|
package/dist/cli.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
setTelemetrySource,
|
|
30
30
|
showFirstRunNotice,
|
|
31
31
|
trackEvent
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-MXR3GI5L.js";
|
|
33
33
|
import {
|
|
34
34
|
CliError,
|
|
35
35
|
EXIT_SYSTEM_ERROR,
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
saveConfig,
|
|
47
47
|
saveConfigPatch,
|
|
48
48
|
usageError
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-SMFUCXHI.js";
|
|
50
50
|
import {
|
|
51
51
|
apiKeys,
|
|
52
52
|
createClient,
|
|
@@ -54,7 +54,7 @@ import {
|
|
|
54
54
|
projects,
|
|
55
55
|
queries,
|
|
56
56
|
renderReportHtml
|
|
57
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-YVMQWVNY.js";
|
|
58
58
|
import {
|
|
59
59
|
AdsDeliverySnapshotStatuses,
|
|
60
60
|
AdsHistoricalCampaignRollupStatuses,
|
|
@@ -100,7 +100,7 @@ import {
|
|
|
100
100
|
providerQuotaPolicySchema,
|
|
101
101
|
resolveProviderInput,
|
|
102
102
|
winnabilityClassSchema
|
|
103
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-GGIRLRAY.js";
|
|
104
104
|
|
|
105
105
|
// src/cli.ts
|
|
106
106
|
import { pathToFileURL } from "url";
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-MXR3GI5L.js";
|
|
4
4
|
import {
|
|
5
5
|
loadConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-SMFUCXHI.js";
|
|
7
|
+
import "./chunk-YVMQWVNY.js";
|
|
8
|
+
import "./chunk-GGIRLRAY.js";
|
|
9
9
|
export {
|
|
10
10
|
createServer,
|
|
11
11
|
loadConfig
|