@canonry/canonry 5.20.4 → 5.21.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/agent-workspace/skills/aero/references/portfolio-analysis.md +1 -1
- package/assets/agent-workspace/skills/aero/references/reporting.md +9 -4
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +3 -0
- package/assets/assets/{AuditHistoryPanel-BSh3Oezj.js → AuditHistoryPanel-CjxWA3lx.js} +1 -1
- package/assets/assets/{BacklinksPage-DQ59dhCx.js → BacklinksPage-Bdd164Up.js} +1 -1
- package/assets/assets/{HistoryPage-BBvgid3o.js → HistoryPage-BvJmE-kP.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-Dmb18JbT.js → MeasurementPropertyPage-HuP6RMZ-.js} +1 -1
- package/assets/assets/{ProjectPage-CPg0q2Dy.js → ProjectPage-DOvXVbyy.js} +1 -1
- package/assets/assets/{RunRow-BK8sZCID.js → RunRow-Cnb3JOBw.js} +1 -1
- package/assets/assets/{RunsPage-CIcVBTdP.js → RunsPage-Dvz1JYIm.js} +1 -1
- package/assets/assets/{SettingsPage-Bc3aR-gz.js → SettingsPage-DYGvphkg.js} +1 -1
- package/assets/assets/{SiteHealthSection-FF14aIXT.js → SiteHealthSection-CUNiKaod.js} +3 -3
- package/assets/assets/{TrafficPage-C_qoeGDl.js → TrafficPage-Bf4Tq95q.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-B3i_B2MI.js → TrafficSourceDetailPage-CIuLWZgT.js} +1 -1
- package/assets/assets/{extract-error-message-BisUn6nN.js → extract-error-message-DZex1e4H.js} +1 -1
- package/assets/assets/{index-Dht7JNX5.js → index-CeXW-lzj.js} +21 -21
- package/assets/assets/{react-sigma_core.esm.min-tWhS09SS.js → react-sigma_core.esm.min-CxvtwEkv.js} +1 -1
- package/assets/assets/{v2-overview-adapter-CAoeWKQJ.js → v2-overview-adapter-Bh4qIzLp.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-TOSPZ7I5.js → chunk-2LO7FLVD.js} +48 -5
- package/dist/{chunk-NSKFW3NF.js → chunk-FAVYLZQV.js} +318 -80
- package/dist/{chunk-PXVJ3P5G.js → chunk-FRWZWJB3.js} +4 -4
- package/dist/{chunk-TWLJRAWA.js → chunk-HDJHQPW3.js} +2 -2
- package/dist/{chunk-CVUR3O5G.js → chunk-MEAKE5W7.js} +3 -3
- package/dist/{chunk-P73YBGZP.js → chunk-NFN5GSEJ.js} +7 -7
- package/dist/{chunk-JJPZ4OQ2.js → chunk-WUT3G2GM.js} +5 -3
- package/dist/cli.js +69 -12
- package/dist/{demo-server-FH2K6TCD.js → demo-server-7BVVVXPM.js} +4 -4
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-QHSR2H7F.js → intelligence-service-3UUKRAZ5.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +10 -10
|
@@ -417,6 +417,7 @@ import {
|
|
|
417
417
|
isTemplateLinkRatio,
|
|
418
418
|
isTrendBaseline,
|
|
419
419
|
isVertexGroundingRedirect,
|
|
420
|
+
isVisibilityCompareClassMetric,
|
|
420
421
|
isoDateDaysBeforeInTimeZone,
|
|
421
422
|
keywordDtoSchema,
|
|
422
423
|
keywordGenerateRequestSchema,
|
|
@@ -771,6 +772,7 @@ import {
|
|
|
771
772
|
userListDtoSchema,
|
|
772
773
|
validationError,
|
|
773
774
|
visibilityCompareDtoSchema,
|
|
775
|
+
visibilityCompareSelectionSchema,
|
|
774
776
|
visibilityReportQuerySchema,
|
|
775
777
|
visibilityReportResponseSchema,
|
|
776
778
|
visibilityStateFromAnswerMentioned,
|
|
@@ -792,7 +794,7 @@ import {
|
|
|
792
794
|
wordpressSchemaDeployResultDtoSchema,
|
|
793
795
|
wordpressSchemaStatusResultDtoSchema,
|
|
794
796
|
wordpressStatusDtoSchema
|
|
795
|
-
} from "./chunk-
|
|
797
|
+
} from "./chunk-2LO7FLVD.js";
|
|
796
798
|
|
|
797
799
|
// src/intelligence-service.ts
|
|
798
800
|
import { eq as eq71, desc as desc32, asc as asc12, and as and60, ne as ne11, or as or18, inArray as inArray27, gte as gte18, lte as lte15, isNull as isNull13, sql as sql28, exists } from "drizzle-orm";
|
|
@@ -16046,6 +16048,17 @@ function modelEvidenceStatesEqual(a, b) {
|
|
|
16046
16048
|
if (a.status !== "mixed" || b.status !== "mixed") return false;
|
|
16047
16049
|
return a.includesUnknown === b.includesUnknown && a.models.length === b.models.length && a.models.every((model, index2) => model === b.models[index2]);
|
|
16048
16050
|
}
|
|
16051
|
+
function compareModelContinuity(from, to) {
|
|
16052
|
+
const fromEvidence = classifyModelEvidence(from);
|
|
16053
|
+
const toEvidence = classifyModelEvidence(to);
|
|
16054
|
+
const ids = (evidence) => evidence.status === "known" ? [evidence.model] : evidence.status === "mixed" ? evidence.models : [];
|
|
16055
|
+
const unknown = (evidence) => evidence.status === "unknown" || evidence.status === "mixed" && evidence.includesUnknown;
|
|
16056
|
+
return {
|
|
16057
|
+
status: unknown(fromEvidence) || unknown(toEvidence) ? "model-unknown" : fromEvidence.status === "known" && toEvidence.status === "known" && fromEvidence.model === toEvidence.model ? "included" : "model-discontinuous",
|
|
16058
|
+
fromModels: ids(fromEvidence),
|
|
16059
|
+
toModels: ids(toEvidence)
|
|
16060
|
+
};
|
|
16061
|
+
}
|
|
16049
16062
|
|
|
16050
16063
|
// ../api-routes/src/competitor-landscape.ts
|
|
16051
16064
|
var COMPETITOR_LANDSCAPE_RANKED_ROW_LIMIT = 100;
|
|
@@ -24412,6 +24425,50 @@ function buildVisibilityReport(input) {
|
|
|
24412
24425
|
populations
|
|
24413
24426
|
});
|
|
24414
24427
|
}
|
|
24428
|
+
function visibilityComparisonPopulation(run, selection) {
|
|
24429
|
+
const targets = targetMap(run.definition);
|
|
24430
|
+
const byClass = ALL_CLASSES.map((queryClass) => ({ queryClass, candidates: candidatesFor(run, selection, queryClass).candidates }));
|
|
24431
|
+
const pooled = /* @__PURE__ */ new Map();
|
|
24432
|
+
for (const { candidates } of byClass) {
|
|
24433
|
+
for (const candidate of candidates) {
|
|
24434
|
+
const existing = pooled.get(candidate.slot.id);
|
|
24435
|
+
if (existing) existing.edges.push(...candidate.edges);
|
|
24436
|
+
else pooled.set(candidate.slot.id, { ...candidate, edges: [...candidate.edges] });
|
|
24437
|
+
}
|
|
24438
|
+
}
|
|
24439
|
+
const cohortKeys = new Map([...pooled.values()].map((candidate) => [candidate.slot.id, JSON.stringify({
|
|
24440
|
+
definition: run.comparableDefinitionIds.at(-1) ?? run.definitionId,
|
|
24441
|
+
query: candidate.slot.query,
|
|
24442
|
+
execution: candidate.slot.executionId,
|
|
24443
|
+
location: candidate.slot.location,
|
|
24444
|
+
edges: candidate.edges.map((edge) => [edge.targetKey, edge.queryClass, [...edge.marketKeys].sort()]).sort((a, b) => JSON.stringify(a).localeCompare(JSON.stringify(b)))
|
|
24445
|
+
})]));
|
|
24446
|
+
const row = (candidate, queryClass) => {
|
|
24447
|
+
const signals = targetValues(candidate, targets);
|
|
24448
|
+
return {
|
|
24449
|
+
queryId: candidate.slot.queryId ?? candidate.slot.queryKey,
|
|
24450
|
+
queryText: candidate.slot.query,
|
|
24451
|
+
provider: candidate.slot.provider,
|
|
24452
|
+
model: candidate.observation.model,
|
|
24453
|
+
answerMentioned: signals.mention,
|
|
24454
|
+
mentionApplicable: candidate.edges.some((edge) => targets.get(edge.targetKey)?.mentionEligible === true),
|
|
24455
|
+
citation: citationForCoverage(candidate, targets),
|
|
24456
|
+
answerText: candidate.observation.answerText,
|
|
24457
|
+
competitorDomains: [...new Set(candidate.edges.flatMap((edge) => edge.competitorDomains))],
|
|
24458
|
+
competitorMentions: candidate.observation.competitorMentionDomains.filter((domain) => candidate.edges.some((edge) => edge.competitorDomains.includes(domain))),
|
|
24459
|
+
competitorCitations: candidate.observation.competitorCitationDomains.filter((domain) => candidate.edges.some((edge) => edge.competitorDomains.includes(domain))),
|
|
24460
|
+
cohortKey: cohortKeys.get(candidate.slot.id),
|
|
24461
|
+
queryClass: queryClass === "unknown" ? null : queryClass
|
|
24462
|
+
};
|
|
24463
|
+
};
|
|
24464
|
+
return {
|
|
24465
|
+
snapshots: [...pooled.values()].filter((candidate) => candidate.observation !== null).map((candidate) => {
|
|
24466
|
+
const classes = new Set(candidate.edges.map((edge) => edge.queryClass));
|
|
24467
|
+
return row(candidate, classes.size === 1 ? candidate.edges[0].queryClass : null);
|
|
24468
|
+
}),
|
|
24469
|
+
classSnapshots: byClass.flatMap(({ queryClass, candidates }) => candidates.filter((candidate) => candidate.observation !== null).map((candidate) => row(candidate, queryClass)))
|
|
24470
|
+
};
|
|
24471
|
+
}
|
|
24415
24472
|
|
|
24416
24473
|
// ../api-routes/src/visibility-report.ts
|
|
24417
24474
|
function normalizedText3(value) {
|
|
@@ -24928,6 +24985,13 @@ function comparableVersionIds(rows, versionId) {
|
|
|
24928
24985
|
}
|
|
24929
24986
|
return ids;
|
|
24930
24987
|
}
|
|
24988
|
+
function comparableChainHeads(rows, rowsById) {
|
|
24989
|
+
const heads = /* @__PURE__ */ new Map();
|
|
24990
|
+
for (const row of [...rows].sort((left, right) => left.revision - right.revision)) {
|
|
24991
|
+
for (const id of comparableVersionIds(rowsById, row.id)) heads.set(id, row.id);
|
|
24992
|
+
}
|
|
24993
|
+
return heads;
|
|
24994
|
+
}
|
|
24931
24995
|
function assignmentSignature(plan, assignment) {
|
|
24932
24996
|
const node = plan.executionNodes.find((candidate) => candidate.stableKey === assignment.executionNodeKey);
|
|
24933
24997
|
if (!node) throw new Error(`Frozen assignment references missing execution node ${assignment.executionNodeKey}`);
|
|
@@ -25082,6 +25146,37 @@ async function visibilityReportRoutes(app) {
|
|
|
25082
25146
|
async (request) => readVisibilityReport(app.db, resolveProject(app.db, request.params.name), request.query)
|
|
25083
25147
|
);
|
|
25084
25148
|
}
|
|
25149
|
+
function readVisibilityComparisonRuns(db, projectId, since, until) {
|
|
25150
|
+
const sourceRuns = db.select().from(runs).where(and21(
|
|
25151
|
+
eq26(runs.projectId, projectId),
|
|
25152
|
+
eq26(runs.kind, RunKinds["answer-visibility"]),
|
|
25153
|
+
inArray10(runs.status, [RunStatuses.completed, RunStatuses.partial]),
|
|
25154
|
+
notProbeRun(),
|
|
25155
|
+
isNotNull(runs.measurementPlanVersionId),
|
|
25156
|
+
isNull5(runs.measurementScope),
|
|
25157
|
+
gte4(runs.createdAt, since),
|
|
25158
|
+
lte4(runs.createdAt, until)
|
|
25159
|
+
)).all();
|
|
25160
|
+
const versionRows = db.select().from(measurementPlanVersions).where(eq26(measurementPlanVersions.projectId, projectId)).all();
|
|
25161
|
+
const rowsById = new Map(versionRows.map((version) => [version.id, version]));
|
|
25162
|
+
const versions = parseV2Versions(versionRows);
|
|
25163
|
+
const heads = comparableChainHeads(versionRows, rowsById);
|
|
25164
|
+
const byRun = loadVisibilitySnapshots(db, sourceRuns.map((run) => run.id));
|
|
25165
|
+
let unavailable5 = false;
|
|
25166
|
+
const selected = sourceRuns.flatMap((run) => {
|
|
25167
|
+
const source = versions.get(run.measurementPlanVersionId);
|
|
25168
|
+
if (!source) {
|
|
25169
|
+
unavailable5 = true;
|
|
25170
|
+
return [];
|
|
25171
|
+
}
|
|
25172
|
+
const presentation = versions.get(heads.get(source.row.id) ?? source.row.id) ?? source;
|
|
25173
|
+
const snapshots = byRun.get(run.id) ?? [];
|
|
25174
|
+
const models = new Map(snapshots.map((snapshot) => [snapshot.id, snapshot.model]));
|
|
25175
|
+
const materialized = advancedRun(run, presentation.row, presentation.plan, snapshots, comparableVersionIds(rowsById, presentation.row.id));
|
|
25176
|
+
return [{ ...materialized, observations: materialized.observations.map((observation) => ({ ...observation, model: models.get(observation.answerId) ?? null })) }];
|
|
25177
|
+
});
|
|
25178
|
+
return { runs: selected, unavailable: unavailable5 };
|
|
25179
|
+
}
|
|
25085
25180
|
|
|
25086
25181
|
// ../api-routes/src/query-tracking.ts
|
|
25087
25182
|
import crypto21 from "crypto";
|
|
@@ -34822,15 +34917,15 @@ import { and as and35, desc as desc18, eq as eq39, inArray as inArray18 } from "
|
|
|
34822
34917
|
|
|
34823
34918
|
// ../api-routes/src/visibility-compare.ts
|
|
34824
34919
|
var VISIBILITY_COMPARE_MIN_RUNS = 5;
|
|
34825
|
-
function basketPairKey(queryId, provider) {
|
|
34826
|
-
return JSON.stringify([queryId, provider]);
|
|
34920
|
+
function basketPairKey(queryId, provider, cohortKey) {
|
|
34921
|
+
return JSON.stringify([queryId, provider, cohortKey ?? null]);
|
|
34827
34922
|
}
|
|
34828
34923
|
function restrict(snapshots, attribution, pairs) {
|
|
34829
34924
|
const out = [];
|
|
34830
34925
|
for (const snap of snapshots) {
|
|
34831
34926
|
const resolved = resolveCurrentQuery(attribution, snap);
|
|
34832
34927
|
if (!resolved) continue;
|
|
34833
|
-
if (!pairs.has(basketPairKey(resolved.id, snap.provider))) continue;
|
|
34928
|
+
if (!pairs.has(basketPairKey(resolved.id, snap.provider, snap.cohortKey))) continue;
|
|
34834
34929
|
out.push({ ...snap, queryId: resolved.id });
|
|
34835
34930
|
}
|
|
34836
34931
|
return out;
|
|
@@ -34851,8 +34946,8 @@ function observedPairs(snapshots, attribution, queryIds) {
|
|
|
34851
34946
|
for (const snap of snapshots) {
|
|
34852
34947
|
const resolved = resolveCurrentQuery(attribution, snap);
|
|
34853
34948
|
if (!resolved || !queryIds.has(resolved.id)) continue;
|
|
34854
|
-
const pair = { queryId: resolved.id, provider: snap.provider };
|
|
34855
|
-
pairs.set(basketPairKey(pair.queryId, pair.provider), pair);
|
|
34949
|
+
const pair = { queryId: resolved.id, provider: snap.provider, cohortKey: snap.cohortKey };
|
|
34950
|
+
pairs.set(basketPairKey(pair.queryId, pair.provider, pair.cohortKey), pair);
|
|
34856
34951
|
}
|
|
34857
34952
|
return pairs;
|
|
34858
34953
|
}
|
|
@@ -34899,7 +34994,9 @@ function modelEvidenceOf(snaps) {
|
|
|
34899
34994
|
function countPeriod(snaps, competitors2, queryClassOf, classificationAvailable, projectMatcher) {
|
|
34900
34995
|
let checked = 0;
|
|
34901
34996
|
let mentioned = 0;
|
|
34997
|
+
let total = 0;
|
|
34902
34998
|
let cited = 0;
|
|
34999
|
+
let citationUnknown = 0;
|
|
34903
35000
|
let competitorCited = 0;
|
|
34904
35001
|
const perProvider = /* @__PURE__ */ new Map();
|
|
34905
35002
|
const mentionedQueries = /* @__PURE__ */ new Set();
|
|
@@ -34911,21 +35008,26 @@ function countPeriod(snaps, competitors2, queryClassOf, classificationAvailable,
|
|
|
34911
35008
|
mentioned += 1;
|
|
34912
35009
|
mentionedQueries.add(snap.queryId);
|
|
34913
35010
|
}
|
|
34914
|
-
const
|
|
35011
|
+
const citationKnown = snap.citationChecked !== false;
|
|
35012
|
+
if (citationKnown) total += 1;
|
|
35013
|
+
else citationUnknown += 1;
|
|
35014
|
+
const isCited = citationKnown && snap.citationState === CitationStates.cited;
|
|
34915
35015
|
if (isCited) cited += 1;
|
|
34916
35016
|
const pp = perProvider.get(snap.provider) ?? { checked: 0, mentioned: 0, cited: 0 };
|
|
34917
35017
|
if (snap.answerMentioned === true || snap.answerMentioned === false) pp.checked += 1;
|
|
34918
35018
|
if (isMentioned) pp.mentioned += 1;
|
|
34919
35019
|
if (isCited) pp.cited += 1;
|
|
34920
35020
|
perProvider.set(snap.provider, pp);
|
|
34921
|
-
if (
|
|
35021
|
+
if (!citationKnown) continue;
|
|
35022
|
+
if (snap.competitorCitations !== void 0) competitorCited += snap.competitorCitations.length;
|
|
35023
|
+
else if (competitorHosts.length > 0 && snap.citedDomains.length > 0) {
|
|
34922
35024
|
const citedHosts = snap.citedDomains.map((d) => hostOf(d)).filter((h) => h !== null && h.length > 0);
|
|
34923
35025
|
for (const compHost of competitorHosts) {
|
|
34924
35026
|
if (citedHosts.some((ch) => hostMatchesDomain(ch, compHost))) competitorCited += 1;
|
|
34925
35027
|
}
|
|
34926
35028
|
}
|
|
34927
35029
|
}
|
|
34928
|
-
|
|
35030
|
+
let mentionShare = buildMentionShare(
|
|
34929
35031
|
snaps.map((s) => ({
|
|
34930
35032
|
// Share uses current identity; named-rate counts above deliberately keep
|
|
34931
35033
|
// their historical persisted-boolean semantics.
|
|
@@ -34935,11 +35037,26 @@ function countPeriod(snaps, competitors2, queryClassOf, classificationAvailable,
|
|
|
34935
35037
|
})),
|
|
34936
35038
|
{ competitors: competitors2, classificationAvailable }
|
|
34937
35039
|
);
|
|
35040
|
+
if (snaps.some((snapshot) => snapshot.competitorMentions !== void 0)) {
|
|
35041
|
+
const selected = snaps.filter((snapshot) => (!classificationAvailable || queryClassOf(snapshot) === "non-brand") && snapshot.answerMentioned !== null);
|
|
35042
|
+
const perCompetitor = competitors2.map((competitor) => ({
|
|
35043
|
+
domain: competitor.domain,
|
|
35044
|
+
mentionSnapshots: selected.filter((snapshot) => snapshot.competitorMentions?.includes(competitor.domain)).length
|
|
35045
|
+
})).filter((row) => row.mentionSnapshots > 0).sort((a, b) => b.mentionSnapshots - a.mentionSnapshots || (a.domain < b.domain ? -1 : 1));
|
|
35046
|
+
mentionShare = { ...mentionShare, breakdown: {
|
|
35047
|
+
...mentionShare.breakdown,
|
|
35048
|
+
projectMentionSnapshots: selected.filter((snapshot) => snapshot.answerMentioned === true).length,
|
|
35049
|
+
competitorMentionSnapshots: perCompetitor.reduce((total2, competitor) => total2 + competitor.mentionSnapshots, 0),
|
|
35050
|
+
perCompetitor
|
|
35051
|
+
} };
|
|
35052
|
+
}
|
|
34938
35053
|
return {
|
|
34939
35054
|
checked,
|
|
34940
35055
|
mentioned,
|
|
34941
|
-
total
|
|
35056
|
+
total,
|
|
34942
35057
|
cited,
|
|
35058
|
+
citationUnknown,
|
|
35059
|
+
citationEvidence: snaps.some((snapshot) => snapshot.citationChecked !== void 0),
|
|
34943
35060
|
projectCited: cited,
|
|
34944
35061
|
competitorCited,
|
|
34945
35062
|
queriesMentioned: mentionedQueries.size,
|
|
@@ -34953,15 +35070,12 @@ function modelIds(evidence) {
|
|
|
34953
35070
|
if (evidence.status === "mixed") return evidence.models;
|
|
34954
35071
|
return [];
|
|
34955
35072
|
}
|
|
34956
|
-
function isUnknownModelEvidence(evidence) {
|
|
34957
|
-
return evidence.status === "unknown" || evidence.status === "mixed" && evidence.includesUnknown;
|
|
34958
|
-
}
|
|
34959
35073
|
function computeVisibilityCompare(input) {
|
|
34960
35074
|
const attribution = buildQueryAttribution(input.queries);
|
|
34961
35075
|
const classifier = compileQueryClassifier(input.brandNames ?? []);
|
|
34962
|
-
const classificationAvailable = classifier !== null;
|
|
35076
|
+
const classificationAvailable = input.frozenClassification === true || classifier !== null;
|
|
34963
35077
|
const queryTextById = new Map(input.queries.map((q) => [q.id, q.query]));
|
|
34964
|
-
const queryClassOf = (snap) => classifier ? classifier.classify(queryTextById.get(snap.queryId) ?? snap.queryText) : null;
|
|
35078
|
+
const queryClassOf = (snap) => input.frozenClassification ? snap.queryClass ?? null : classifier ? classifier.classify(queryTextById.get(snap.queryId) ?? snap.queryText) : null;
|
|
34965
35079
|
const projectMatcher = compileBrandAliases(input.brandNames ?? []);
|
|
34966
35080
|
const fromObs = observed(input.from.snapshots, attribution);
|
|
34967
35081
|
const toObs = observed(input.to.snapshots, attribution);
|
|
@@ -34973,12 +35087,10 @@ function computeVisibilityCompare(input) {
|
|
|
34973
35087
|
const fromCandidateModels = modelEvidenceOf(restrict(input.from.snapshots, attribution, pairsBoth));
|
|
34974
35088
|
const toCandidateModels = modelEvidenceOf(restrict(input.to.snapshots, attribution, pairsBoth));
|
|
34975
35089
|
const continuityProviders = [...candidateProviders].sort((a, b) => a.localeCompare(b)).map((provider) => {
|
|
34976
|
-
|
|
34977
|
-
|
|
34978
|
-
|
|
34979
|
-
|
|
34980
|
-
const status = isUnknownModelEvidence(fromEvidence) || isUnknownModelEvidence(toEvidence) ? "model-unknown" : fromEvidence.status === "known" && toEvidence.status === "known" && fromEvidence.model === toEvidence.model ? "included" : "model-discontinuous";
|
|
34981
|
-
return { provider, status, fromModels, toModels };
|
|
35090
|
+
return { provider, ...compareModelContinuity(
|
|
35091
|
+
fromCandidateModels.get(provider) ?? [],
|
|
35092
|
+
toCandidateModels.get(provider) ?? []
|
|
35093
|
+
) };
|
|
34982
35094
|
});
|
|
34983
35095
|
const providersBoth = new Set(
|
|
34984
35096
|
continuityProviders.filter((provider) => provider.status === "included").map((provider) => provider.provider)
|
|
@@ -34992,12 +35104,18 @@ function computeVisibilityCompare(input) {
|
|
|
34992
35104
|
const toSnaps = restrict(input.to.snapshots, attribution, comparedPairs);
|
|
34993
35105
|
const fromCounts = countPeriod(fromSnaps, input.competitors, queryClassOf, classificationAvailable, projectMatcher);
|
|
34994
35106
|
const toCounts = countPeriod(toSnaps, input.competitors, queryClassOf, classificationAvailable, projectMatcher);
|
|
35107
|
+
const fromClassSnaps = input.from.classSnapshots === void 0 ? fromSnaps : restrict(input.from.classSnapshots, attribution, comparedPairs);
|
|
35108
|
+
const toClassSnaps = input.to.classSnapshots === void 0 ? toSnaps : restrict(input.to.classSnapshots, attribution, comparedPairs);
|
|
35109
|
+
const noProjectMatcher = compileBrandAliases([]);
|
|
35110
|
+
const fromShareCounts = input.frozenClassification ? countPeriod(fromClassSnaps, input.competitors, queryClassOf, true, noProjectMatcher) : fromCounts;
|
|
35111
|
+
const toShareCounts = input.frozenClassification ? countPeriod(toClassSnaps, input.competitors, queryClassOf, true, noProjectMatcher) : toCounts;
|
|
34995
35112
|
const shareCounts = (c) => ({
|
|
34996
35113
|
proj: c.mentionShare.breakdown.projectMentionSnapshots,
|
|
34997
35114
|
comp: c.mentionShare.breakdown.competitorMentionSnapshots
|
|
34998
35115
|
});
|
|
34999
|
-
const fromShare = shareCounts(
|
|
35000
|
-
const toShare = shareCounts(
|
|
35116
|
+
const fromShare = shareCounts(fromShareCounts);
|
|
35117
|
+
const toShare = shareCounts(toShareCounts);
|
|
35118
|
+
const citationPeriod = (c, value) => c.citationEvidence ? { ...value, excludedUnknown: c.citationUnknown } : value;
|
|
35001
35119
|
const metrics = [
|
|
35002
35120
|
metric(
|
|
35003
35121
|
"mention-share-of-voice",
|
|
@@ -35018,8 +35136,8 @@ function computeVisibilityCompare(input) {
|
|
|
35018
35136
|
"Cited share of voice",
|
|
35019
35137
|
"all",
|
|
35020
35138
|
true,
|
|
35021
|
-
input.competitors.length > 0 ? period(fromCounts.projectCited, fromCounts.projectCited + fromCounts.competitorCited) : unavailableCompetitivePeriod(fromCounts.projectCited),
|
|
35022
|
-
input.competitors.length > 0 ? period(toCounts.projectCited, toCounts.projectCited + toCounts.competitorCited) : unavailableCompetitivePeriod(toCounts.projectCited),
|
|
35139
|
+
citationPeriod(fromCounts, input.competitors.length > 0 ? period(fromCounts.projectCited, fromCounts.projectCited + fromCounts.competitorCited) : unavailableCompetitivePeriod(fromCounts.projectCited)),
|
|
35140
|
+
citationPeriod(toCounts, input.competitors.length > 0 ? period(toCounts.projectCited, toCounts.projectCited + toCounts.competitorCited) : unavailableCompetitivePeriod(toCounts.projectCited)),
|
|
35023
35141
|
continuityBlock
|
|
35024
35142
|
),
|
|
35025
35143
|
metric(
|
|
@@ -35036,11 +35154,31 @@ function computeVisibilityCompare(input) {
|
|
|
35036
35154
|
"Cited rate",
|
|
35037
35155
|
"all",
|
|
35038
35156
|
false,
|
|
35039
|
-
period(fromCounts.cited, fromCounts.total),
|
|
35040
|
-
period(toCounts.cited, toCounts.total),
|
|
35157
|
+
citationPeriod(fromCounts, period(fromCounts.cited, fromCounts.total)),
|
|
35158
|
+
citationPeriod(toCounts, period(toCounts.cited, toCounts.total)),
|
|
35041
35159
|
continuityBlock
|
|
35042
35160
|
)
|
|
35043
35161
|
];
|
|
35162
|
+
const classPeriod = (snapshots, queryClass, signal) => {
|
|
35163
|
+
if (!classificationAvailable) return { ...period(0, 0), availability: "classification-unavailable" };
|
|
35164
|
+
const selected = snapshots.filter((snapshot) => queryClassOf(snapshot) === queryClass && (signal === "cited" || snapshot.mentionApplicable !== false));
|
|
35165
|
+
const checked = selected.filter((snapshot) => signal === "mention" ? typeof snapshot.answerMentioned === "boolean" : snapshot.citationChecked !== false);
|
|
35166
|
+
const numerator = checked.filter((snapshot) => signal === "mention" ? snapshot.answerMentioned === true : snapshot.citationState === CitationStates.cited).length;
|
|
35167
|
+
return { ...period(numerator, checked.length), excludedUnknown: selected.length - checked.length };
|
|
35168
|
+
};
|
|
35169
|
+
for (const queryClass of ["branded", "non-brand"]) {
|
|
35170
|
+
for (const signal of ["mention", "cited"]) {
|
|
35171
|
+
metrics.push(metric(
|
|
35172
|
+
`${signal}-rate-${queryClass}`,
|
|
35173
|
+
signal === "mention" ? "Mention rate" : "Cited rate",
|
|
35174
|
+
queryClass,
|
|
35175
|
+
false,
|
|
35176
|
+
classPeriod(fromClassSnaps, queryClass, signal),
|
|
35177
|
+
classPeriod(toClassSnaps, queryClass, signal),
|
|
35178
|
+
continuityBlock
|
|
35179
|
+
));
|
|
35180
|
+
}
|
|
35181
|
+
}
|
|
35044
35182
|
const modelChanges = [...candidateProviders].sort((a, b) => a.localeCompare(b)).map((provider) => {
|
|
35045
35183
|
const fromModels = modelIds(classifyModelEvidence(fromCandidateModels.get(provider) ?? []));
|
|
35046
35184
|
const toModels = modelIds(classifyModelEvidence(toCandidateModels.get(provider) ?? []));
|
|
@@ -35088,7 +35226,7 @@ function computeVisibilityCompare(input) {
|
|
|
35088
35226
|
comparedProviders: [...providersBoth].sort((a, b) => a.localeCompare(b)),
|
|
35089
35227
|
providers: continuityProviders
|
|
35090
35228
|
},
|
|
35091
|
-
competitors: { from:
|
|
35229
|
+
competitors: { from: fromShareCounts.competitors, to: toShareCounts.competitors }
|
|
35092
35230
|
};
|
|
35093
35231
|
}
|
|
35094
35232
|
|
|
@@ -35306,59 +35444,150 @@ async function visibilityStatsRoutes(app) {
|
|
|
35306
35444
|
return reply.send(response);
|
|
35307
35445
|
});
|
|
35308
35446
|
app.get("/projects/:name/visibility-compare", async (request, reply) => {
|
|
35309
|
-
const
|
|
35310
|
-
|
|
35311
|
-
|
|
35312
|
-
|
|
35313
|
-
|
|
35314
|
-
|
|
35315
|
-
|
|
35316
|
-
|
|
35317
|
-
|
|
35318
|
-
|
|
35319
|
-
|
|
35320
|
-
|
|
35321
|
-
|
|
35322
|
-
|
|
35323
|
-
|
|
35324
|
-
|
|
35325
|
-
|
|
35326
|
-
|
|
35327
|
-
|
|
35328
|
-
|
|
35329
|
-
|
|
35330
|
-
|
|
35331
|
-
|
|
35332
|
-
|
|
35333
|
-
|
|
35334
|
-
|
|
35335
|
-
|
|
35336
|
-
|
|
35337
|
-
|
|
35338
|
-
|
|
35339
|
-
|
|
35340
|
-
|
|
35341
|
-
|
|
35342
|
-
|
|
35343
|
-
|
|
35344
|
-
|
|
35345
|
-
|
|
35346
|
-
|
|
35347
|
-
|
|
35348
|
-
|
|
35447
|
+
const dto = readVisibilityCompare(app.db, request.params.name, request.query);
|
|
35448
|
+
return reply.send(dto);
|
|
35449
|
+
});
|
|
35450
|
+
}
|
|
35451
|
+
function readVisibilityCompare(db, projectName, query) {
|
|
35452
|
+
const project = resolveProject(db, projectName);
|
|
35453
|
+
const selection = visibilityCompareSelectionSchema.safeParse(query);
|
|
35454
|
+
if (!selection.success) throw validationError("Invalid comparison selection", { issues: selection.error.issues });
|
|
35455
|
+
const filters = selection.data;
|
|
35456
|
+
const scope = filters.scope ?? "project";
|
|
35457
|
+
if (scope !== "project" && filters.scopeKey === void 0) throw validationError("A non-project comparison scope requires scopeKey.");
|
|
35458
|
+
if (scope === "project" && filters.scopeKey !== void 0) throw validationError("scopeKey is not valid for project scope.");
|
|
35459
|
+
if (scope === "market" && filters.marketKey !== void 0) throw validationError("marketKey is not valid for market scope.");
|
|
35460
|
+
const { from: fromRaw, to: toRaw } = query;
|
|
35461
|
+
if (fromRaw === void 0 || fromRaw === "") throw validationError('"from" (YYYY-MM) is required');
|
|
35462
|
+
if (toRaw === void 0 || toRaw === "") throw validationError('"to" (YYYY-MM) is required');
|
|
35463
|
+
let fromBounds;
|
|
35464
|
+
let toBounds;
|
|
35465
|
+
try {
|
|
35466
|
+
fromBounds = calendarMonthBounds(fromRaw);
|
|
35467
|
+
} catch (err) {
|
|
35468
|
+
throw validationError(err instanceof RangeError ? `"from": ${err.message}` : '"from" must be in YYYY-MM format');
|
|
35469
|
+
}
|
|
35470
|
+
try {
|
|
35471
|
+
toBounds = calendarMonthBounds(toRaw);
|
|
35472
|
+
} catch (err) {
|
|
35473
|
+
throw validationError(err instanceof RangeError ? `"to": ${err.message}` : '"to" must be in YYYY-MM format');
|
|
35474
|
+
}
|
|
35475
|
+
if (Date.parse(fromBounds.since) >= Date.parse(toBounds.since)) {
|
|
35476
|
+
throw validationError('"from" must be a month strictly before "to"');
|
|
35477
|
+
}
|
|
35478
|
+
const providerKey2 = filters.provider === void 0 ? void 0 : normalizeText3(filters.provider);
|
|
35479
|
+
const locationKey2 = filters.location === void 0 ? void 0 : normalizeText3(filters.location);
|
|
35480
|
+
const scoped = scope !== "project" || filters.marketKey !== void 0;
|
|
35481
|
+
let advancedComparison;
|
|
35482
|
+
if (activeMeasurementPlan(db, project.id) !== null) {
|
|
35483
|
+
let missingScope;
|
|
35484
|
+
let measuredScope = 0;
|
|
35485
|
+
const loadAdvancedMonth = (bounds) => {
|
|
35486
|
+
const selected = readVisibilityComparisonRuns(db, project.id, bounds.since, bounds.until);
|
|
35487
|
+
const snapshots = [];
|
|
35488
|
+
const classSnapshots = [];
|
|
35489
|
+
let runCount = 0;
|
|
35490
|
+
for (const run of selected.runs) {
|
|
35491
|
+
let population;
|
|
35492
|
+
try {
|
|
35493
|
+
population = visibilityComparisonPopulation(run, {
|
|
35494
|
+
...filters,
|
|
35495
|
+
scope,
|
|
35496
|
+
queryClass: "all",
|
|
35497
|
+
location: locationKey2 === void 0 ? { kind: "all" } : locationKey2 === "none" ? { kind: "none" } : { kind: "exact", value: filters.location },
|
|
35498
|
+
limit: 100
|
|
35499
|
+
});
|
|
35500
|
+
} catch (error) {
|
|
35501
|
+
if (error instanceof VisibilityReportScopeError && error.details !== void 0) {
|
|
35502
|
+
missingScope ??= error;
|
|
35503
|
+
continue;
|
|
35504
|
+
}
|
|
35505
|
+
if (error instanceof VisibilityReportScopeError) throw validationError(error.message, error.details);
|
|
35506
|
+
throw error;
|
|
35507
|
+
}
|
|
35508
|
+
runCount += 1;
|
|
35509
|
+
const adapt = (snapshot) => ({
|
|
35510
|
+
...snapshot,
|
|
35511
|
+
citationState: snapshot.citation === true ? CitationStates.cited : CitationStates["not-cited"],
|
|
35512
|
+
citationChecked: snapshot.citation !== null,
|
|
35513
|
+
citedDomains: []
|
|
35514
|
+
});
|
|
35515
|
+
snapshots.push(...population.snapshots.map(adapt));
|
|
35516
|
+
classSnapshots.push(...population.classSnapshots.map(adapt));
|
|
35517
|
+
}
|
|
35518
|
+
measuredScope += runCount;
|
|
35519
|
+
return { runCount, snapshots, classSnapshots, classificationAvailable: !selected.unavailable };
|
|
35349
35520
|
};
|
|
35350
|
-
const
|
|
35351
|
-
const
|
|
35352
|
-
|
|
35521
|
+
const fromMonth2 = loadAdvancedMonth(fromBounds);
|
|
35522
|
+
const toMonth2 = loadAdvancedMonth(toBounds);
|
|
35523
|
+
if (missingScope !== void 0 && measuredScope === 0) throw validationError(missingScope.message, missingScope.details);
|
|
35524
|
+
const queries2 = [...new Map([...fromMonth2.snapshots, ...toMonth2.snapshots].map((snapshot) => [snapshot.queryId, { id: snapshot.queryId, query: snapshot.queryText }])).values()];
|
|
35525
|
+
advancedComparison = { ...computeVisibilityCompare({
|
|
35353
35526
|
project: project.name,
|
|
35354
|
-
queries:
|
|
35355
|
-
competitors:
|
|
35356
|
-
|
|
35357
|
-
from: { month: fromRaw,
|
|
35358
|
-
to: { month: toRaw,
|
|
35359
|
-
});
|
|
35360
|
-
|
|
35527
|
+
queries: queries2,
|
|
35528
|
+
competitors: [...new Set([...fromMonth2.snapshots, ...toMonth2.snapshots].flatMap((snapshot) => snapshot.competitorDomains ?? []))].map((domain) => ({ domain, brandTokens: [] })),
|
|
35529
|
+
frozenClassification: fromMonth2.classificationAvailable && toMonth2.classificationAvailable,
|
|
35530
|
+
from: { ...fromMonth2, month: fromRaw, ...fromBounds },
|
|
35531
|
+
to: { ...toMonth2, month: toRaw, ...toBounds }
|
|
35532
|
+
}), selection: filters };
|
|
35533
|
+
if (scoped) {
|
|
35534
|
+
const unreadable = [fromMonth2.classificationAvailable ? null : fromRaw, toMonth2.classificationAvailable ? null : toRaw].filter((month) => month !== null);
|
|
35535
|
+
if (unreadable.length > 0) {
|
|
35536
|
+
throw validationError(`Property, group and market comparisons need frozen schema-v2 plan history; ${unreadable.join(" and ")} include runs whose plan cannot be reconstructed.`, { months: unreadable });
|
|
35537
|
+
}
|
|
35538
|
+
return advancedComparison;
|
|
35539
|
+
}
|
|
35540
|
+
}
|
|
35541
|
+
if (advancedComparison === void 0 && scoped) throw validationError("Property, group and market comparison scopes require an Advanced portfolio.");
|
|
35542
|
+
const projectQueries = db.select({ id: queries.id, query: queries.query }).from(queries).where(eq39(queries.projectId, project.id)).all();
|
|
35543
|
+
const competitorRows2 = db.select({ domain: competitors.domain }).from(competitors).where(eq39(competitors.projectId, project.id)).all();
|
|
35544
|
+
const competitorInputs = mentionShareCompetitorsFromDomains(competitorRows2.map((c) => c.domain));
|
|
35545
|
+
const loadMonth = (bounds) => {
|
|
35546
|
+
const sinceMs = Date.parse(bounds.since);
|
|
35547
|
+
const untilMs = Date.parse(bounds.until);
|
|
35548
|
+
const monthRuns = db.select({ id: runs.id, createdAt: runs.createdAt, status: runs.status }).from(runs).where(and35(eq39(runs.projectId, project.id), eq39(runs.kind, RunKinds["answer-visibility"]), notProbeRun())).all().filter(
|
|
35549
|
+
(r) => (r.status === RunStatuses.completed || r.status === RunStatuses.partial) && Date.parse(r.createdAt) >= sinceMs && Date.parse(r.createdAt) <= untilMs
|
|
35550
|
+
);
|
|
35551
|
+
const runIds = monthRuns.map((r) => r.id);
|
|
35552
|
+
const snapshots = runIds.length > 0 && projectQueries.length > 0 ? db.select({
|
|
35553
|
+
queryId: querySnapshots.queryId,
|
|
35554
|
+
queryText: querySnapshots.queryText,
|
|
35555
|
+
provider: querySnapshots.provider,
|
|
35556
|
+
model: querySnapshots.model,
|
|
35557
|
+
citationState: querySnapshots.citationState,
|
|
35558
|
+
answerMentioned: querySnapshots.answerMentioned,
|
|
35559
|
+
answerText: querySnapshots.answerText,
|
|
35560
|
+
citedDomains: querySnapshots.citedDomains,
|
|
35561
|
+
location: querySnapshots.location
|
|
35562
|
+
}).from(querySnapshots).where(inArray18(querySnapshots.runId, runIds)).all() : [];
|
|
35563
|
+
return {
|
|
35564
|
+
runCount: monthRuns.length,
|
|
35565
|
+
snapshots: snapshots.filter((snapshot) => (providerKey2 === void 0 || normalizeText3(snapshot.provider) === providerKey2) && (locationKey2 === void 0 || (locationKey2 === "none" ? snapshot.location === null : snapshot.location !== null && normalizeText3(snapshot.location) === locationKey2)))
|
|
35566
|
+
};
|
|
35567
|
+
};
|
|
35568
|
+
const fromMonth = loadMonth(fromBounds);
|
|
35569
|
+
const toMonth = loadMonth(toBounds);
|
|
35570
|
+
const dto = computeVisibilityCompare({
|
|
35571
|
+
project: project.name,
|
|
35572
|
+
queries: projectQueries,
|
|
35573
|
+
competitors: competitorInputs,
|
|
35574
|
+
brandNames: effectiveBrandNames(project),
|
|
35575
|
+
from: { month: fromRaw, since: fromBounds.since, until: fromBounds.until, runCount: fromMonth.runCount, snapshots: fromMonth.snapshots },
|
|
35576
|
+
to: { month: toRaw, since: toBounds.since, until: toBounds.until, runCount: toMonth.runCount, snapshots: toMonth.snapshots }
|
|
35361
35577
|
});
|
|
35578
|
+
if (advancedComparison !== void 0) {
|
|
35579
|
+
const { from, to, basket, continuity: continuity2, modelChanges } = advancedComparison;
|
|
35580
|
+
return {
|
|
35581
|
+
...dto,
|
|
35582
|
+
selection: filters,
|
|
35583
|
+
metrics: [
|
|
35584
|
+
...dto.metrics.filter((metric2) => !isVisibilityCompareClassMetric(metric2.key)),
|
|
35585
|
+
...advancedComparison.metrics.filter((metric2) => isVisibilityCompareClassMetric(metric2.key))
|
|
35586
|
+
],
|
|
35587
|
+
classComparison: { from, to, basket, continuity: continuity2, modelChanges }
|
|
35588
|
+
};
|
|
35589
|
+
}
|
|
35590
|
+
return { ...dto, selection: filters };
|
|
35362
35591
|
}
|
|
35363
35592
|
|
|
35364
35593
|
// ../api-routes/src/results-export.ts
|
|
@@ -39055,9 +39284,18 @@ var routeCatalog = [
|
|
|
39055
39284
|
method: "get",
|
|
39056
39285
|
path: "/api/v1/projects/{name}/visibility-compare",
|
|
39057
39286
|
summary: "Compare AEO visibility month over month",
|
|
39058
|
-
description: "Statistically honest month-over-month AEO comparison in one call. PRIMARY metric is share of voice (brand vs competitor mentions in the same answers), which is less exposed to broad model-wide naming propensity than an absolute rate but never bypasses model continuity. Rates are pooled per-snapshot over each month (invariant to sweep count), restricted to query/provider pairs present in BOTH months, and then restricted again to providers with exactly one known, identical configured model id in both months. `continuity` reports every provider and its model evidence; changed, mixed mid-month, or legacy-unknown models are excluded. When no provider remains, metrics return a continuity-blocked verdict rather than a directional call. `from` must be a month strictly before `to`. A silent upstream version bump under an unchanged configured id remains undetectable.",
|
|
39287
|
+
description: "Statistically honest month-over-month AEO comparison in one call. PRIMARY metric is share of voice (brand vs competitor mentions in the same answers), which is less exposed to broad model-wide naming propensity than an absolute rate but never bypasses model continuity. Rates are pooled per-snapshot over each month (invariant to sweep count), restricted to query/provider pairs present in BOTH months, and then restricted again to providers with exactly one known, identical configured model id in both months. `continuity` reports every provider and its model evidence; changed, mixed mid-month, or legacy-unknown models are excluded. When no provider remains, metrics return a continuity-blocked verdict rather than a directional call. `from` must be a month strictly before `to`. Adds separate branded/non-brand mention and cited rates with classification-unavailable rather than pooled fallback. Advanced class metrics read frozen Property/market/execution assignments and reuse each answer once per class. Without a scope or marketKey the four original metrics keep the project frame (provider/location narrow it the same way for Simple and Advanced) and Advanced class metrics carry their own `classComparison` cohort. A scope or marketKey answers entirely from the frozen frame and is refused when either month holds runs whose plan cannot be reconstructed (schema v1). On frozen frames, answers with incomplete source capture leave every citation figure and are counted in `excludedUnknown`. A silent upstream version bump under an unchanged configured id remains undetectable.",
|
|
39059
39288
|
tags: ["analytics"],
|
|
39060
|
-
parameters: [
|
|
39289
|
+
parameters: [
|
|
39290
|
+
nameParameter,
|
|
39291
|
+
compareFromQueryParameter,
|
|
39292
|
+
compareToQueryParameter,
|
|
39293
|
+
{ name: "scope", in: "query", description: "Advanced frozen population scope; default project.", schema: { type: "string", enum: ["project", "group", "market", "property"] } },
|
|
39294
|
+
{ name: "scopeKey", in: "query", description: "Stable Property Target, group, or market key; required for non-project scope.", schema: stringSchema },
|
|
39295
|
+
{ name: "marketKey", in: "query", description: "Intersect project/group/property selection with exact frozen market edges.", schema: stringSchema },
|
|
39296
|
+
{ name: "provider", in: "query", description: "Restrict both frames to one provider (case-insensitive).", schema: stringSchema },
|
|
39297
|
+
{ name: "location", in: "query", description: "Execution location label (case-insensitive), or none for no location.", schema: stringSchema }
|
|
39298
|
+
],
|
|
39061
39299
|
responses: {
|
|
39062
39300
|
200: jsonResponse("Month-over-month visibility comparison returned.", "VisibilityCompareDto"),
|
|
39063
39301
|
400: errorResponse("Invalid or missing from/to months."),
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
PACKAGE_VERSION,
|
|
6
6
|
canonryMcpTools,
|
|
7
7
|
isMachineFormat
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WUT3G2GM.js";
|
|
9
9
|
import {
|
|
10
10
|
AGENT_PROVIDER_IDS,
|
|
11
11
|
CANONRY_NPM_PACKAGE_URL,
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
skillsClientSchema,
|
|
38
38
|
upgradeCaveatFor,
|
|
39
39
|
upgradeCommandFor
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-2LO7FLVD.js";
|
|
41
41
|
|
|
42
42
|
// src/config.ts
|
|
43
43
|
import fs from "fs";
|
|
@@ -8891,12 +8891,12 @@ var ApiClient = class {
|
|
|
8891
8891
|
})
|
|
8892
8892
|
);
|
|
8893
8893
|
}
|
|
8894
|
-
async getVisibilityCompare(project, from, to) {
|
|
8894
|
+
async getVisibilityCompare(project, from, to, selection = {}) {
|
|
8895
8895
|
return this.invoke(
|
|
8896
8896
|
() => getApiV1ProjectsByNameVisibilityCompare({
|
|
8897
8897
|
client: this.heyClient,
|
|
8898
8898
|
path: { name: project },
|
|
8899
|
-
query: { from, to }
|
|
8899
|
+
query: { from, to, ...selection }
|
|
8900
8900
|
})
|
|
8901
8901
|
);
|
|
8902
8902
|
}
|
|
@@ -5,12 +5,12 @@ import {
|
|
|
5
5
|
siteCrawlGraphNodes,
|
|
6
6
|
siteCrawlPages,
|
|
7
7
|
siteCrawlSnapshots
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-FAVYLZQV.js";
|
|
9
9
|
import {
|
|
10
10
|
SITE_CRAWL_GRAPH_MAX_EDGES,
|
|
11
11
|
SITE_CRAWL_GRAPH_MAX_NODES,
|
|
12
12
|
TEMPLATE_LINK_RATIO_THRESHOLD
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-2LO7FLVD.js";
|
|
14
14
|
|
|
15
15
|
// src/site-crawl-graph-layout.ts
|
|
16
16
|
import crypto from "crypto";
|
|
@@ -4,14 +4,14 @@ import {
|
|
|
4
4
|
installSkills,
|
|
5
5
|
loadConfigRaw,
|
|
6
6
|
saveConfigPatch
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-FRWZWJB3.js";
|
|
8
8
|
import {
|
|
9
9
|
PACKAGE_VERSION
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-WUT3G2GM.js";
|
|
11
11
|
import {
|
|
12
12
|
SKILL_MANIFEST_FILENAME,
|
|
13
13
|
SkillsClients
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-2LO7FLVD.js";
|
|
15
15
|
|
|
16
16
|
// src/skills-autosync.ts
|
|
17
17
|
import fs from "fs";
|