@canonry/canonry 7.17.1 → 7.17.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-CzjskfMX.js → AuditHistoryPanel-Dga03Niw.js} +1 -1
- package/assets/assets/BacklinksPage-CfodWuIt.js +1 -0
- package/assets/assets/Hint-DICI5Jq9.js +1 -0
- package/assets/assets/{HistoryPage-BZgVoYGm.js → HistoryPage-BN_vTU5L.js} +1 -1
- package/assets/assets/{MeasurementPropertyPage-EBI5sn-D.js → MeasurementPropertyPage-CjiyzOmE.js} +1 -1
- package/assets/assets/{ProjectPage-lGGjM_qj.js → ProjectPage-AxclA3YC.js} +6 -6
- package/assets/assets/{RunsPage-CuHi8241.js → RunsPage-Ca_ziCGC.js} +1 -1
- package/assets/assets/{SettingsPage-D0fgAoVu.js → SettingsPage-SO1xl0d3.js} +1 -1
- package/assets/assets/SiteHealthSection-C9JgRzzs.js +4 -0
- package/assets/assets/{TrafficPage-DD65RqeR.js → TrafficPage-DrcbXNQB.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CFVCj13J.js → TrafficSourceDetailPage-HDZGKgyd.js} +1 -1
- package/assets/assets/{extract-error-message-D118msoE.js → extract-error-message-D48vwe7I.js} +1 -1
- package/assets/assets/index-BaBHBReI.js +86 -0
- package/assets/assets/{react-sigma_core.esm.min-DdlHLMEz.js → react-sigma_core.esm.min-DVHs4npB.js} +1 -1
- package/assets/assets/{v2-overview-adapter-CoCb2aB0.js → v2-overview-adapter-CTEXuiRt.js} +1 -1
- package/assets/assets/{vendor-lucide-N6vOpZmh.js → vendor-lucide-Y6jANGfG.js} +1 -1
- package/assets/index.html +2 -2
- package/dist/{chunk-ORA5R7QM.js → chunk-3LPQ4JV5.js} +130 -103
- package/dist/{chunk-OPJCFDNR.js → chunk-4EDH2ORA.js} +3 -3
- package/dist/{chunk-MHB23E3N.js → chunk-MIRAKDRW.js} +106 -79
- package/dist/{chunk-C6GWKYGE.js → chunk-NPNU6UHD.js} +5 -4
- package/dist/{chunk-PLS4EM2Y.js → chunk-P2XY5YX7.js} +2 -2
- package/dist/{chunk-FK7AWV57.js → chunk-QAIJH74T.js} +1 -1
- package/dist/{chunk-ZQ7C6IFA.js → chunk-YJS4OE6I.js} +614 -578
- package/dist/cli.js +13 -15
- package/dist/{demo-server-WQDHQG6P.js → demo-server-54SLPSJF.js} +6 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +6 -6
- package/dist/{intelligence-service-JWZRZ6JZ.js → intelligence-service-62CEP6M2.js} +2 -2
- package/dist/mcp.js +4 -4
- package/package.json +14 -14
- package/assets/assets/BacklinksPage-ToENGBdp.js +0 -1
- package/assets/assets/SiteHealthSection-DhLxldgs.js +0 -4
- package/assets/assets/index-DxFd1t4G.js +0 -86
|
@@ -303,6 +303,7 @@ import {
|
|
|
303
303
|
coerceSkillManifest,
|
|
304
304
|
compactDateToIso,
|
|
305
305
|
compareSemver,
|
|
306
|
+
compareText,
|
|
306
307
|
competitorAliasBlockRequestSchema,
|
|
307
308
|
competitorAliasProjectIdentity,
|
|
308
309
|
competitorAliasesRequestSchema,
|
|
@@ -376,6 +377,7 @@ import {
|
|
|
376
377
|
emptyCitationVisibility,
|
|
377
378
|
emptySentimentCounts,
|
|
378
379
|
escapeLikePattern,
|
|
380
|
+
escapeRegExp,
|
|
379
381
|
evaluateModelPointerExposure,
|
|
380
382
|
expandQueryTemplate,
|
|
381
383
|
expandResearchTemplate,
|
|
@@ -493,6 +495,7 @@ import {
|
|
|
493
495
|
isoDateDaysBeforeInTimeZone,
|
|
494
496
|
keywordDtoSchema,
|
|
495
497
|
keywordGenerateRequestSchema,
|
|
498
|
+
keywordGenerateResponseSchema,
|
|
496
499
|
latestProjectRunDtoSchema,
|
|
497
500
|
linearTrend,
|
|
498
501
|
locationContextSchema,
|
|
@@ -604,6 +607,7 @@ import {
|
|
|
604
607
|
normalizeCompetitorAliases,
|
|
605
608
|
normalizeCompetitorDomain,
|
|
606
609
|
normalizeIdTokens,
|
|
610
|
+
normalizeIdentityText,
|
|
607
611
|
normalizeMeasurementExactUrl,
|
|
608
612
|
normalizeMeasurementExecutionQueryText,
|
|
609
613
|
normalizeMeasurementHost,
|
|
@@ -643,8 +647,10 @@ import {
|
|
|
643
647
|
planCompetitorAliases,
|
|
644
648
|
planCompetitorAutoAliases,
|
|
645
649
|
prepareBrandMatchText,
|
|
650
|
+
projectConfigExportSchema,
|
|
646
651
|
projectConfigSchema,
|
|
647
652
|
projectCreateRequestSchema,
|
|
653
|
+
projectDefaultLocationResponseSchema,
|
|
648
654
|
projectDtoSchema,
|
|
649
655
|
projectOverviewDtoSchema,
|
|
650
656
|
projectSearchResponseSchema,
|
|
@@ -656,6 +662,7 @@ import {
|
|
|
656
662
|
providersFailing,
|
|
657
663
|
queryDtoSchema,
|
|
658
664
|
queryGenerateRequestSchema,
|
|
665
|
+
queryGenerateResponseSchema,
|
|
659
666
|
queryTrackingCommitRequestSchema,
|
|
660
667
|
queryTrackingCommitResponseSchema,
|
|
661
668
|
queryTrackingPreviewRequestSchema,
|
|
@@ -777,6 +784,7 @@ import {
|
|
|
777
784
|
snapshotReportSchema,
|
|
778
785
|
snapshotRequestInputSchema,
|
|
779
786
|
snapshotRequestSchema,
|
|
787
|
+
sortedUnique,
|
|
780
788
|
sourceBreakdownDtoSchema,
|
|
781
789
|
sourceBreakdownQuerySchema,
|
|
782
790
|
splitList,
|
|
@@ -833,12 +841,12 @@ import {
|
|
|
833
841
|
wordpressManualAssistDtoSchema,
|
|
834
842
|
wordpressOnboardResultDtoSchema,
|
|
835
843
|
wordpressPageDetailDtoSchema,
|
|
836
|
-
|
|
844
|
+
wordpressPageListDtoSchema,
|
|
837
845
|
wordpressSchemaBlockDtoSchema,
|
|
838
846
|
wordpressSchemaDeployResultDtoSchema,
|
|
839
847
|
wordpressSchemaStatusResultDtoSchema,
|
|
840
848
|
wordpressStatusDtoSchema
|
|
841
|
-
} from "./chunk-
|
|
849
|
+
} from "./chunk-MIRAKDRW.js";
|
|
842
850
|
|
|
843
851
|
// src/intelligence-service.ts
|
|
844
852
|
import { eq as eq89, desc as desc41, asc as asc18, and as and75, ne as ne16, or as or22, inArray as inArray36, gte as gte24, lte as lte20, isNull as isNull17, sql as sql32, exists as exists2 } from "drizzle-orm";
|
|
@@ -1040,7 +1048,7 @@ var projects = sqliteTable("projects", {
|
|
|
1040
1048
|
locations: text("locations", { mode: "json" }).$type().notNull().default([]),
|
|
1041
1049
|
defaultLocation: text("default_location"),
|
|
1042
1050
|
autoExtractBacklinks: integer("auto_extract_backlinks", { mode: "boolean" }).notNull().default(false),
|
|
1043
|
-
configSource: text("config_source").notNull().default("cli"),
|
|
1051
|
+
configSource: text("config_source").$type().notNull().default("cli"),
|
|
1044
1052
|
configRevision: integer("config_revision").notNull().default(1),
|
|
1045
1053
|
icpDescription: text("icp_description"),
|
|
1046
1054
|
createdAt: text("created_at").notNull(),
|
|
@@ -9682,11 +9690,13 @@ var SentimentRepository = class {
|
|
|
9682
9690
|
}
|
|
9683
9691
|
};
|
|
9684
9692
|
|
|
9685
|
-
// ../intelligence/src/
|
|
9693
|
+
// ../intelligence/src/snapshot-key.ts
|
|
9686
9694
|
function snapshotKey(snap) {
|
|
9687
9695
|
const loc = snap.location ?? "__none__";
|
|
9688
9696
|
return JSON.stringify([snap.query, snap.provider, loc]);
|
|
9689
9697
|
}
|
|
9698
|
+
|
|
9699
|
+
// ../intelligence/src/regressions.ts
|
|
9690
9700
|
function detectRegressions(currentRun, previousRun) {
|
|
9691
9701
|
if ((currentRun.location ?? null) !== (previousRun.location ?? null)) {
|
|
9692
9702
|
return [];
|
|
@@ -9726,24 +9736,20 @@ function observedKeys(run, keyOf) {
|
|
|
9726
9736
|
}
|
|
9727
9737
|
|
|
9728
9738
|
// ../intelligence/src/gains.ts
|
|
9729
|
-
function snapshotKey2(snap) {
|
|
9730
|
-
const loc = snap.location ?? "__none__";
|
|
9731
|
-
return JSON.stringify([snap.query, snap.provider, loc]);
|
|
9732
|
-
}
|
|
9733
9739
|
function detectGains(currentRun, previousRun) {
|
|
9734
9740
|
if ((currentRun.location ?? null) !== (previousRun.location ?? null)) {
|
|
9735
9741
|
return [];
|
|
9736
9742
|
}
|
|
9737
9743
|
const gains = [];
|
|
9738
|
-
const previousObserved = observedKeys(previousRun,
|
|
9744
|
+
const previousObserved = observedKeys(previousRun, snapshotKey);
|
|
9739
9745
|
const previousCited = /* @__PURE__ */ new Set();
|
|
9740
9746
|
for (const snap of previousRun.snapshots) {
|
|
9741
9747
|
if (snap.cited) {
|
|
9742
|
-
previousCited.add(
|
|
9748
|
+
previousCited.add(snapshotKey(snap));
|
|
9743
9749
|
}
|
|
9744
9750
|
}
|
|
9745
9751
|
for (const snap of currentRun.snapshots) {
|
|
9746
|
-
const key =
|
|
9752
|
+
const key = snapshotKey(snap);
|
|
9747
9753
|
if (snap.cited && previousObserved.has(key) && !previousCited.has(key)) {
|
|
9748
9754
|
gains.push({
|
|
9749
9755
|
query: snap.query,
|
|
@@ -12451,6 +12457,11 @@ var FORWARDED_HEADERS = ["x-forwarded-for", "forwarded", "x-real-ip"];
|
|
|
12451
12457
|
function hasForwardedHeaders(request) {
|
|
12452
12458
|
return FORWARDED_HEADERS.some((header2) => request.headers[header2] !== void 0);
|
|
12453
12459
|
}
|
|
12460
|
+
var PROXY_PATH_HEADERS = ["via", "cf-connecting-ip", "true-client-ip"];
|
|
12461
|
+
var FORWARDED_HEADER_PREFIX = "x-forwarded-";
|
|
12462
|
+
function hasProxyHeaders(request) {
|
|
12463
|
+
return hasForwardedHeaders(request) || PROXY_PATH_HEADERS.some((header2) => request.headers[header2] !== void 0) || Object.keys(request.headers).some((header2) => header2.startsWith(FORWARDED_HEADER_PREFIX));
|
|
12464
|
+
}
|
|
12454
12465
|
function resolveCallerKey(request, trustProxyConfigured) {
|
|
12455
12466
|
const socketAddress = request.socket.remoteAddress ?? request.ip;
|
|
12456
12467
|
if (!trustProxyConfigured) return socketAddress;
|
|
@@ -14418,8 +14429,7 @@ function rowKey(row) {
|
|
|
14418
14429
|
return { queryText: row.queryText, queryId: row.queryId, executionNodeKey: row.executionNodeKey };
|
|
14419
14430
|
}
|
|
14420
14431
|
function compareRows(left, right) {
|
|
14421
|
-
|
|
14422
|
-
return left.queryText.localeCompare(right.queryText, "en") || order(left.queryText, right.queryText) || order(left.queryId, right.queryId) || order(left.executionNodeKey ?? "", right.executionNodeKey ?? "");
|
|
14432
|
+
return left.queryText.localeCompare(right.queryText, "en") || compareText(left.queryText, right.queryText) || compareText(left.queryId, right.queryId) || compareText(left.executionNodeKey ?? "", right.executionNodeKey ?? "");
|
|
14423
14433
|
}
|
|
14424
14434
|
function withheldCounts(counts2) {
|
|
14425
14435
|
return Object.fromEntries(Object.entries(counts2).map(([key, value]) => [key, OPERATIONAL_OUTCOMES.has(key) ? value : 0]));
|
|
@@ -14987,9 +14997,6 @@ import { and as and7, desc as desc4, eq as eq12, exists, inArray as inArray4, is
|
|
|
14987
14997
|
import crypto7 from "crypto";
|
|
14988
14998
|
import { and as and6, eq as eq11, gt as gt2, lte as lte4 } from "drizzle-orm";
|
|
14989
14999
|
var MEASUREMENT_RECEIPT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
14990
|
-
function compareText(left, right) {
|
|
14991
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
14992
|
-
}
|
|
14993
15000
|
function canonicalJsonValue(value) {
|
|
14994
15001
|
if (Array.isArray(value)) return value.map(canonicalJsonValue);
|
|
14995
15002
|
if (value && typeof value === "object") {
|
|
@@ -15957,10 +15964,6 @@ import { and as and11, desc as desc7, eq as eq17, gt as gt3, inArray as inArray6
|
|
|
15957
15964
|
import { and as and9, desc as desc5, eq as eq15, gte as gte2, inArray as inArray5, isNull as isNull4, lt as lt4, lte as lte5, ne as ne4 } from "drizzle-orm";
|
|
15958
15965
|
|
|
15959
15966
|
// ../api-routes/src/measurement-report.ts
|
|
15960
|
-
var compareText2 = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
15961
|
-
function sortedUnique(values) {
|
|
15962
|
-
return [...new Set(values)].sort(compareText2);
|
|
15963
|
-
}
|
|
15964
15967
|
function normalizedHost(value) {
|
|
15965
15968
|
try {
|
|
15966
15969
|
return normalizeMeasurementHost(value);
|
|
@@ -16042,7 +16045,7 @@ function classifySourceAttribution(value, routes, normalizedOwnedHosts) {
|
|
|
16042
16045
|
const claim = routeClaim(source, route);
|
|
16043
16046
|
if (claim) claims.push(claim);
|
|
16044
16047
|
}
|
|
16045
|
-
claims.sort((left, right) => right.modeRank - left.modeRank || right.pathLength - left.pathLength ||
|
|
16048
|
+
claims.sort((left, right) => right.modeRank - left.modeRank || right.pathLength - left.pathLength || compareText(left.targetId, right.targetId) || compareText(left.urlId, right.urlId));
|
|
16046
16049
|
const best = claims.at(0);
|
|
16047
16050
|
if (!best) {
|
|
16048
16051
|
return {
|
|
@@ -16083,8 +16086,7 @@ function classifySourceForUsageEdge(source, usageEdge) {
|
|
|
16083
16086
|
}
|
|
16084
16087
|
function normalizeMeasurementLocation(value) {
|
|
16085
16088
|
if (value === null) return null;
|
|
16086
|
-
|
|
16087
|
-
return normalized || null;
|
|
16089
|
+
return normalizeIdentityText(value) || null;
|
|
16088
16090
|
}
|
|
16089
16091
|
function words(value) {
|
|
16090
16092
|
return value.normalize("NFKC").toLowerCase().match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
@@ -16142,7 +16144,7 @@ function mentionedTargetsForAliases(answerText, aliases, preparedWords) {
|
|
|
16142
16144
|
}
|
|
16143
16145
|
function identityAmbiguityPatterns(aliases) {
|
|
16144
16146
|
const patterns = aliases.flatMap((alias2) => {
|
|
16145
|
-
const name = words(alias2).join(" ")
|
|
16147
|
+
const name = escapeRegExp(words(alias2).join(" "));
|
|
16146
16148
|
if (!name) return [];
|
|
16147
16149
|
return [{
|
|
16148
16150
|
clarification: new RegExp(`(?:^| )which (?:specific )?${name} (?:(?:(?:do|did) )?you mean|(?:(?:are|were) you|you(?: re| are| were)?) (?:asking about|referring to|talking about))(?= |$)`, "u"),
|
|
@@ -16245,7 +16247,7 @@ function prepareReport(input, options = {}, detailedEvidence = true) {
|
|
|
16245
16247
|
const observationsBySlot = /* @__PURE__ */ new Map();
|
|
16246
16248
|
const historical = /* @__PURE__ */ new Set();
|
|
16247
16249
|
const evidenceIncomplete = /* @__PURE__ */ new Set();
|
|
16248
|
-
for (const slot of [...input.expectedSlots].sort((left, right) =>
|
|
16250
|
+
for (const slot of [...input.expectedSlots].sort((left, right) => compareText(left.id, right.id))) {
|
|
16249
16251
|
const rows = candidates.get(slot.id) ?? [];
|
|
16250
16252
|
if (rows.length > 1) {
|
|
16251
16253
|
for (const row of rows) ambiguous.add(row.id);
|
|
@@ -16301,7 +16303,7 @@ function prepareReport(input, options = {}, detailedEvidence = true) {
|
|
|
16301
16303
|
observation.unknownMentionTargetIds = mentions.unknown;
|
|
16302
16304
|
observation.citedTargetIds = citedTargetIds;
|
|
16303
16305
|
if (!detailedEvidence) continue;
|
|
16304
|
-
const edges = [...edgesByExecution.get(observation.slot.executionId) ?? []].sort((left, right) =>
|
|
16306
|
+
const edges = [...edgesByExecution.get(observation.slot.executionId) ?? []].sort((left, right) => compareText(left.id, right.id));
|
|
16305
16307
|
for (const edge of edges) {
|
|
16306
16308
|
const sources = observation.sourceUrls.map((sourceUrl) => {
|
|
16307
16309
|
const { classification, normalizedUrl, matchedTargetIds, matchedUrlIds } = classifySourceForUsageEdge(sourceAttribution(sourceUrl), edge);
|
|
@@ -16336,7 +16338,7 @@ function prepareReport(input, options = {}, detailedEvidence = true) {
|
|
|
16336
16338
|
});
|
|
16337
16339
|
}
|
|
16338
16340
|
}
|
|
16339
|
-
answers.sort((left, right) =>
|
|
16341
|
+
answers.sort((left, right) => compareText(left.expectedSlotId, right.expectedSlotId) || compareText(left.usageEdgeId, right.usageEdgeId));
|
|
16340
16342
|
const evidence = answers.flatMap((answer) => answer.sources.map((source) => ({
|
|
16341
16343
|
observationId: answer.observationId,
|
|
16342
16344
|
expectedSlotId: answer.expectedSlotId,
|
|
@@ -16355,7 +16357,7 @@ function prepareReport(input, options = {}, detailedEvidence = true) {
|
|
|
16355
16357
|
matchedTargetIds: source.matchedTargetIds,
|
|
16356
16358
|
matchedUrlIds: source.matchedUrlIds
|
|
16357
16359
|
})));
|
|
16358
|
-
evidence.sort((left, right) =>
|
|
16360
|
+
evidence.sort((left, right) => compareText(left.expectedSlotId, right.expectedSlotId) || compareText(left.usageEdgeId, right.usageEdgeId) || compareText(left.sourceUrl, right.sourceUrl) || compareText(left.classification, right.classification));
|
|
16359
16361
|
options.onSourceAttributionComputed?.(sourceAttributions.size);
|
|
16360
16362
|
return {
|
|
16361
16363
|
observationsBySlot,
|
|
@@ -16387,7 +16389,7 @@ function slotsForEdges(expectedSlots, edges) {
|
|
|
16387
16389
|
for (const slot of expectedSlots) {
|
|
16388
16390
|
if (executionIds.has(slot.executionId)) slots.set(slot.id, slot);
|
|
16389
16391
|
}
|
|
16390
|
-
return [...slots.values()].sort((left, right) =>
|
|
16392
|
+
return [...slots.values()].sort((left, right) => compareText(left.id, right.id));
|
|
16391
16393
|
}
|
|
16392
16394
|
function completeness(slots, prepared) {
|
|
16393
16395
|
const observations = slots.flatMap((slot) => {
|
|
@@ -16450,7 +16452,7 @@ function providersFor(slots) {
|
|
|
16450
16452
|
return sortedUnique(slots.map((slot) => slot.provider));
|
|
16451
16453
|
}
|
|
16452
16454
|
function buildSovForSlots(slots, competitors2, projectBrandNames, projectDomain, prepared) {
|
|
16453
|
-
const sortedCompetitors = [...competitors2].sort((left, right) =>
|
|
16455
|
+
const sortedCompetitors = [...competitors2].sort((left, right) => compareText(left.domain, right.domain));
|
|
16454
16456
|
const rows = [
|
|
16455
16457
|
{ domain: projectDomain, own: true, aliases: projectBrandNames },
|
|
16456
16458
|
...sortedCompetitors.map((competitor) => ({
|
|
@@ -16614,7 +16616,7 @@ function indexedSlotsForEdges(edges, indexes) {
|
|
|
16614
16616
|
for (const edge of edges) {
|
|
16615
16617
|
for (const slot of indexes.slotsByExecutionId.get(edge.executionId) ?? []) slots.set(slot.id, slot);
|
|
16616
16618
|
}
|
|
16617
|
-
return [...slots.values()].sort((left, right) =>
|
|
16619
|
+
return [...slots.values()].sort((left, right) => compareText(left.id, right.id));
|
|
16618
16620
|
}
|
|
16619
16621
|
function indexedAnsweredSlots(slots, indexes) {
|
|
16620
16622
|
return slots.filter((slot) => indexes.answeredSlotIds.has(slot.id));
|
|
@@ -16778,22 +16780,19 @@ function buildMeasurementReport(input, options = {}) {
|
|
|
16778
16780
|
const prepared = prepareReport(input, options);
|
|
16779
16781
|
return {
|
|
16780
16782
|
revision: input.revision,
|
|
16781
|
-
groups: [...input.groups].sort((left, right) =>
|
|
16782
|
-
targets: [...input.targets].sort((left, right) =>
|
|
16783
|
+
groups: [...input.groups].sort((left, right) => compareText(left.id, right.id)).map((group) => buildGroupReport(group, input, prepared)),
|
|
16784
|
+
targets: [...input.targets].sort((left, right) => compareText(left.id, right.id)).map((target) => buildTargetReport(target, input, prepared)),
|
|
16783
16785
|
evidence: prepared.evidence,
|
|
16784
16786
|
diagnostics: prepared.diagnostics
|
|
16785
16787
|
};
|
|
16786
16788
|
}
|
|
16787
16789
|
|
|
16788
16790
|
// ../api-routes/src/measurement-report-adapter.ts
|
|
16789
|
-
function compareText3(left, right) {
|
|
16790
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
16791
|
-
}
|
|
16792
16791
|
function normalizedProviders(values) {
|
|
16793
16792
|
const providers = values.map((value) => value.trim().toLocaleLowerCase("en"));
|
|
16794
16793
|
if (providers.some((value) => !value)) throw new Error("measurement manifest provider must be non-empty");
|
|
16795
16794
|
if (new Set(providers).size !== providers.length) throw new Error("measurement manifest providers must be unique");
|
|
16796
|
-
return providers.sort(
|
|
16795
|
+
return providers.sort(compareText);
|
|
16797
16796
|
}
|
|
16798
16797
|
function canonicalContext(value) {
|
|
16799
16798
|
if (value === null) return "null";
|
|
@@ -16826,7 +16825,7 @@ function frozenExecutionNodes(plan) {
|
|
|
16826
16825
|
}
|
|
16827
16826
|
function manifestFromNodes(nodes, providersFor2) {
|
|
16828
16827
|
const expectedSlots = [];
|
|
16829
|
-
for (const node of [...nodes].sort((left, right) =>
|
|
16828
|
+
for (const node of [...nodes].sort((left, right) => compareText(left.stableKey, right.stableKey))) {
|
|
16830
16829
|
const providers = providersFor2(node);
|
|
16831
16830
|
if (node.expectedSnapshots !== providers.length) {
|
|
16832
16831
|
throw new Error(`measurement manifest provider roster does not satisfy execution ${node.stableKey}`);
|
|
@@ -17324,9 +17323,6 @@ function expectedSlotsFor(nodes, providers, models) {
|
|
|
17324
17323
|
...models[provider] ? { requestedModel: models[provider] } : {}
|
|
17325
17324
|
})));
|
|
17326
17325
|
}
|
|
17327
|
-
function compareText4(left, right) {
|
|
17328
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
17329
|
-
}
|
|
17330
17326
|
function normalizedLocationIdentity(value) {
|
|
17331
17327
|
if (!value) return null;
|
|
17332
17328
|
return [value.country, value.region, value.city, value.label, value.timezone ?? ""].map((part) => part.trim().toLocaleLowerCase("en")).join("\0");
|
|
@@ -17353,7 +17349,7 @@ function materializeV2ExecutionNodes(nodes, instanceModels) {
|
|
|
17353
17349
|
const answering = /* @__PURE__ */ new Map();
|
|
17354
17350
|
const retired = /* @__PURE__ */ new Set();
|
|
17355
17351
|
const claimed = /* @__PURE__ */ new Set();
|
|
17356
|
-
for (const node of [...nodes].sort((left, right) =>
|
|
17352
|
+
for (const node of [...nodes].sort((left, right) => compareText(left.stableKey, right.stableKey))) {
|
|
17357
17353
|
const nodeProviders = normalizeProviders(node.context.providers);
|
|
17358
17354
|
const declared = nodeProviderModels(node);
|
|
17359
17355
|
const resolved = /* @__PURE__ */ new Map();
|
|
@@ -17385,11 +17381,11 @@ function materializeV2ExecutionNodes(nodes, instanceModels) {
|
|
|
17385
17381
|
}
|
|
17386
17382
|
return {
|
|
17387
17383
|
expectedSlots,
|
|
17388
|
-
providers: [...providers].sort(
|
|
17384
|
+
providers: [...providers].sort(compareText),
|
|
17389
17385
|
models: Object.fromEntries([...models].flatMap(([provider, model]) => {
|
|
17390
17386
|
if (model) return [[provider, model]];
|
|
17391
17387
|
const current = answering.get(provider);
|
|
17392
|
-
return retired.has(provider) && current ? [[provider, [...current].sort(
|
|
17388
|
+
return retired.has(provider) && current ? [[provider, [...current].sort(compareText).join(" + ")]] : [];
|
|
17393
17389
|
}))
|
|
17394
17390
|
};
|
|
17395
17391
|
}
|
|
@@ -17405,12 +17401,9 @@ function sliceForV2(plan, params) {
|
|
|
17405
17401
|
function quotedList(values) {
|
|
17406
17402
|
return values.map((value) => `"${value}"`).join(", ");
|
|
17407
17403
|
}
|
|
17408
|
-
function sortedUnique2(values) {
|
|
17409
|
-
return [...new Set(values)].sort(compareText4);
|
|
17410
|
-
}
|
|
17411
17404
|
function resolveV2RunScope(plan, scope) {
|
|
17412
|
-
const requestedGroups =
|
|
17413
|
-
const requestedTargets =
|
|
17405
|
+
const requestedGroups = sortedUnique(scope.groups ?? []);
|
|
17406
|
+
const requestedTargets = sortedUnique(scope.targets ?? []);
|
|
17414
17407
|
const groupsByKey = new Map(plan.groups.map((group) => [group.stableKey, group]));
|
|
17415
17408
|
const targetKeys = new Set(plan.targets.map((target) => target.stableKey));
|
|
17416
17409
|
const unknownGroups = requestedGroups.filter((key) => !groupsByKey.has(key));
|
|
@@ -17429,7 +17422,7 @@ function resolveV2RunScope(plan, scope) {
|
|
|
17429
17422
|
for (const key of requestedGroups) {
|
|
17430
17423
|
for (const targetKey of groupsByKey.get(key).targetKeys) selected.add(targetKey);
|
|
17431
17424
|
}
|
|
17432
|
-
const resolvedTargets = [...selected].sort(
|
|
17425
|
+
const resolvedTargets = [...selected].sort(compareText);
|
|
17433
17426
|
const usedNodeKeys = new Set(plan.usageEdges.filter((edge) => selected.has(edge.targetKey)).map((edge) => edge.executionNodeKey));
|
|
17434
17427
|
const executionNodes = plan.executionNodes.filter((node) => usedNodeKeys.has(node.stableKey));
|
|
17435
17428
|
if (executionNodes.length === 0) {
|
|
@@ -17444,7 +17437,7 @@ function resolveV2RunScope(plan, scope) {
|
|
|
17444
17437
|
};
|
|
17445
17438
|
}
|
|
17446
17439
|
function resolveV2QueryScope(plan, queryTexts) {
|
|
17447
|
-
const requested =
|
|
17440
|
+
const requested = sortedUnique(queryTexts.map(normalizeMeasurementExecutionQueryText).filter(Boolean));
|
|
17448
17441
|
const measured = new Set(plan.executionNodes.map((node) => normalizeMeasurementExecutionQueryText(node.queryText)));
|
|
17449
17442
|
const unknown = requested.filter((text2) => !measured.has(text2));
|
|
17450
17443
|
if (unknown.length) {
|
|
@@ -19804,20 +19797,17 @@ function countMetric(rate2) {
|
|
|
19804
19797
|
if (rate2.numerator === null) return unavailable2(metricReason(rate2.reason));
|
|
19805
19798
|
return { state: "available", value: rate2.numerator, numerator: rate2.numerator, denominator: rate2.denominator };
|
|
19806
19799
|
}
|
|
19807
|
-
function normalizedText(value) {
|
|
19808
|
-
return value.normalize("NFKC").trim().replace(/\s+/g, " ").toLocaleLowerCase("en");
|
|
19809
|
-
}
|
|
19810
19800
|
function overviewFilterFingerprint(query) {
|
|
19811
19801
|
const filters = {
|
|
19812
19802
|
scope: query.scope,
|
|
19813
19803
|
groupKey: query.groupKey ?? null,
|
|
19814
19804
|
targetKey: query.targetKey ?? null,
|
|
19815
19805
|
queryClass: query.queryClass ?? "all",
|
|
19816
|
-
provider: query.provider === void 0 ? null :
|
|
19817
|
-
location: query.location === void 0 ? null :
|
|
19806
|
+
provider: query.provider === void 0 ? null : normalizeIdentityText(query.provider),
|
|
19807
|
+
location: query.location === void 0 ? null : normalizeIdentityText(query.location),
|
|
19818
19808
|
from: query.from ?? null,
|
|
19819
19809
|
to: query.to ?? null,
|
|
19820
|
-
search: query.search === void 0 ? null :
|
|
19810
|
+
search: query.search === void 0 ? null : normalizeIdentityText(query.search),
|
|
19821
19811
|
compact: query.compact ?? false
|
|
19822
19812
|
};
|
|
19823
19813
|
return createHash4("sha256").update(JSON.stringify(filters)).digest("base64url");
|
|
@@ -19828,27 +19818,27 @@ function overviewAggregateFingerprint(query) {
|
|
|
19828
19818
|
groupKey: query.groupKey ?? null,
|
|
19829
19819
|
targetKey: query.targetKey ?? null,
|
|
19830
19820
|
queryClass: query.queryClass ?? "all",
|
|
19831
|
-
provider: query.provider === void 0 ? null :
|
|
19832
|
-
location: query.location === void 0 ? null :
|
|
19821
|
+
provider: query.provider === void 0 ? null : normalizeIdentityText(query.provider),
|
|
19822
|
+
location: query.location === void 0 ? null : normalizeIdentityText(query.location),
|
|
19833
19823
|
from: query.from ?? null,
|
|
19834
19824
|
to: query.to ?? null
|
|
19835
19825
|
};
|
|
19836
19826
|
return createHash4("sha256").update(JSON.stringify(filters)).digest("base64url");
|
|
19837
19827
|
}
|
|
19838
19828
|
function compareLabels(left, right) {
|
|
19839
|
-
const leftLabel =
|
|
19840
|
-
const rightLabel =
|
|
19829
|
+
const leftLabel = normalizeIdentityText(left.label);
|
|
19830
|
+
const rightLabel = normalizeIdentityText(right.label);
|
|
19841
19831
|
if (leftLabel !== rightLabel) return leftLabel < rightLabel ? -1 : 1;
|
|
19842
|
-
return left.targetKey
|
|
19832
|
+
return compareText(left.targetKey, right.targetKey);
|
|
19843
19833
|
}
|
|
19844
19834
|
function compareLabelSort(left, right, descending) {
|
|
19845
|
-
const leftLabel =
|
|
19846
|
-
const rightLabel =
|
|
19835
|
+
const leftLabel = normalizeIdentityText(left.label);
|
|
19836
|
+
const rightLabel = normalizeIdentityText(right.label);
|
|
19847
19837
|
if (leftLabel !== rightLabel) {
|
|
19848
19838
|
const compared = leftLabel < rightLabel ? -1 : 1;
|
|
19849
19839
|
return descending ? -compared : compared;
|
|
19850
19840
|
}
|
|
19851
|
-
return left.targetKey
|
|
19841
|
+
return compareText(left.targetKey, right.targetKey);
|
|
19852
19842
|
}
|
|
19853
19843
|
function metricForSort(row, sort) {
|
|
19854
19844
|
switch (sort) {
|
|
@@ -19877,7 +19867,7 @@ function compareRows2(left, right, sort) {
|
|
|
19877
19867
|
return compareLabels(left, right);
|
|
19878
19868
|
}
|
|
19879
19869
|
function legacyCursorOf(row) {
|
|
19880
|
-
return Buffer.from(`${
|
|
19870
|
+
return Buffer.from(`${normalizeIdentityText(row.label)}:${row.targetKey}`, "utf8").toString("base64url");
|
|
19881
19871
|
}
|
|
19882
19872
|
function cursorOf(row, sort, displayedRunId, filterFingerprint2, planVersionId, evidenceFingerprint) {
|
|
19883
19873
|
const cursor = {
|
|
@@ -20116,12 +20106,9 @@ function planExpectedSlots(plan) {
|
|
|
20116
20106
|
}
|
|
20117
20107
|
function matchesSearch(row, search) {
|
|
20118
20108
|
if (search === void 0) return true;
|
|
20119
|
-
const needle =
|
|
20109
|
+
const needle = normalizeIdentityText(search);
|
|
20120
20110
|
if (needle === "") return true;
|
|
20121
|
-
return
|
|
20122
|
-
}
|
|
20123
|
-
function compareGroupText(left, right) {
|
|
20124
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
20111
|
+
return normalizeIdentityText(row.label).includes(needle) || normalizeIdentityText(row.targetKey).includes(needle);
|
|
20125
20112
|
}
|
|
20126
20113
|
function propertyLocations(plan) {
|
|
20127
20114
|
const groupsByKey = new Map(plan.groups.map((group) => [group.stableKey, group]));
|
|
@@ -20138,7 +20125,7 @@ function propertyLocations(plan) {
|
|
|
20138
20125
|
}
|
|
20139
20126
|
return levels;
|
|
20140
20127
|
};
|
|
20141
|
-
const byLabel = (left, right) =>
|
|
20128
|
+
const byLabel = (left, right) => compareText(left.label, right.label) || compareText(left.stableKey, right.stableKey);
|
|
20142
20129
|
const metros = /* @__PURE__ */ new Map();
|
|
20143
20130
|
const submarkets = /* @__PURE__ */ new Map();
|
|
20144
20131
|
const push = (map, key, value) => {
|
|
@@ -20275,7 +20262,7 @@ function planV2Overview(db, projectId, active, plan, query, scope, cache) {
|
|
|
20275
20262
|
}, () => {
|
|
20276
20263
|
const manifest = measurementRunExpectedSlots(displayed, plan);
|
|
20277
20264
|
const { input, edgeQueryClass } = buildMeasurementPlanV2ReportInput(active.version.revision, plan, manifest, snapshots);
|
|
20278
|
-
const expectedSlots = input.expectedSlots.filter((slot) => (query.provider === void 0 || slot.provider ===
|
|
20265
|
+
const expectedSlots = input.expectedSlots.filter((slot) => (query.provider === void 0 || slot.provider === normalizeIdentityText(query.provider)) && (query.location === void 0 || normalizeIdentityText(slot.location ?? "") === normalizeIdentityText(query.location)));
|
|
20279
20266
|
const usageEdges = queryClass === "all" ? input.usageEdges : input.usageEdges.filter((edge) => edgeQueryClass.get(edge.id) === queryClass);
|
|
20280
20267
|
return buildMeasurementOverview({
|
|
20281
20268
|
...input,
|
|
@@ -20514,7 +20501,7 @@ function readCompetitorIdentityChangedAt(db, projectId) {
|
|
|
20514
20501
|
return renamed !== null && renamed > audited ? renamed : audited;
|
|
20515
20502
|
}
|
|
20516
20503
|
function pooledRunIds(pooled) {
|
|
20517
|
-
return [...pooled].sort((left, right) => left.createdAt === right.createdAt ?
|
|
20504
|
+
return [...pooled].sort((left, right) => left.createdAt === right.createdAt ? compareText(right.id, left.id) : compareText(right.createdAt, left.createdAt)).slice(0, POOLED_RUN_ID_LIMIT).map((run) => run.id);
|
|
20518
20505
|
}
|
|
20519
20506
|
function readCompetitorLandscape(app, projectName, query, selection) {
|
|
20520
20507
|
const project = resolveProject(app.db, projectName);
|
|
@@ -20666,7 +20653,7 @@ function readCompetitorLandscape(app, projectName, query, selection) {
|
|
|
20666
20653
|
else excluded.nonCompleted++;
|
|
20667
20654
|
excludedByModel.set(key, excluded);
|
|
20668
20655
|
}
|
|
20669
|
-
const sortedGroups = [...modelGroups.values()].sort((left, right) =>
|
|
20656
|
+
const sortedGroups = [...modelGroups.values()].sort((left, right) => compareText(left.provider, right.provider) || (left.model === null ? right.model === null ? 0 : -1 : right.model === null ? 1 : compareText(left.model, right.model)));
|
|
20670
20657
|
const groups = sortedGroups.slice(0, COMPETITOR_LANDSCAPE_MODEL_GROUP_LIMIT).map((group) => {
|
|
20671
20658
|
const groupHistory = buildHistory(group.snapshots);
|
|
20672
20659
|
const groupObserved = groupHistory.observed.slice(0, COMPETITOR_LANDSCAPE_RANKED_ROW_LIMIT);
|
|
@@ -20878,9 +20865,6 @@ function snapshotMatchesFilters(input) {
|
|
|
20878
20865
|
function requestedModel(model) {
|
|
20879
20866
|
return model?.trim() || null;
|
|
20880
20867
|
}
|
|
20881
|
-
function compareStoredIds(left, right) {
|
|
20882
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
20883
|
-
}
|
|
20884
20868
|
function normalizedDomain(value) {
|
|
20885
20869
|
return registrableDomain(value) || hostOf(value);
|
|
20886
20870
|
}
|
|
@@ -21853,8 +21837,8 @@ function hash(value) {
|
|
|
21853
21837
|
function answerIdentityFrom(project, competitorRows2, marketNames = /* @__PURE__ */ new Map()) {
|
|
21854
21838
|
const projectDomains = effectiveDomains({ canonicalDomain: project.canonicalDomain, ownedDomains: project.ownedDomains });
|
|
21855
21839
|
const projectBrandNames = effectiveBrandNames({ displayName: project.displayName, aliases: project.aliases });
|
|
21856
|
-
const names = competitorRows2.map((row) => [row.domain, competitorIdentityAliases(row)]).sort(([left], [right]) => left
|
|
21857
|
-
const market = [...marketNames].map(([domain, learned]) => [domain, [...learned]]).sort(([left], [right]) => left
|
|
21840
|
+
const names = competitorRows2.map((row) => [row.domain, competitorIdentityAliases(row)]).sort(([left], [right]) => compareText(left, right));
|
|
21841
|
+
const market = [...marketNames].map(([domain, learned]) => [domain, [...learned]]).sort(([left], [right]) => compareText(left, right));
|
|
21858
21842
|
return {
|
|
21859
21843
|
projectDomains,
|
|
21860
21844
|
projectBrandNames,
|
|
@@ -22304,12 +22288,6 @@ var MeasurementDiscoveryConfigurationError = class extends Error {
|
|
|
22304
22288
|
this.name = "MeasurementDiscoveryConfigurationError";
|
|
22305
22289
|
}
|
|
22306
22290
|
};
|
|
22307
|
-
function compareText5(a, b) {
|
|
22308
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
22309
|
-
}
|
|
22310
|
-
function sortedUnique3(values) {
|
|
22311
|
-
return [...new Set(values)].sort(compareText5);
|
|
22312
|
-
}
|
|
22313
22291
|
function normalizeDeclaredHost(value, label) {
|
|
22314
22292
|
const normalized = value.trim().toLowerCase().replace(/\.$/, "");
|
|
22315
22293
|
if (!normalized || normalized.includes("://") || normalized.includes("/") || normalized.includes("@")) {
|
|
@@ -22374,7 +22352,7 @@ function normalizePatternValue(value, label) {
|
|
|
22374
22352
|
return normalized;
|
|
22375
22353
|
}
|
|
22376
22354
|
function compileRules(input) {
|
|
22377
|
-
const ownedHosts =
|
|
22355
|
+
const ownedHosts = sortedUnique(input.ownedHosts.map((host, index2) => normalizeDeclaredHost(host, `ownedHosts[${index2}]`)));
|
|
22378
22356
|
if (ownedHosts.length === 0) {
|
|
22379
22357
|
throw new MeasurementDiscoveryConfigurationError("ownedHosts must contain at least one hostname");
|
|
22380
22358
|
}
|
|
@@ -22385,7 +22363,7 @@ function compileRules(input) {
|
|
|
22385
22363
|
if (aliases.some((alias2) => alias2.host === primary.host)) {
|
|
22386
22364
|
throw new MeasurementDiscoveryConfigurationError("alias rules must use another owned host");
|
|
22387
22365
|
}
|
|
22388
|
-
const excludedSlugSuffixes =
|
|
22366
|
+
const excludedSlugSuffixes = sortedUnique(
|
|
22389
22367
|
(input.rules.excludedSlugSuffixes ?? []).map(
|
|
22390
22368
|
(value, index2) => normalizePatternValue(value, `rules.excludedSlugSuffixes[${index2}]`)
|
|
22391
22369
|
)
|
|
@@ -22393,7 +22371,7 @@ function compileRules(input) {
|
|
|
22393
22371
|
const excludedSlugPatterns = (input.rules.excludedSlugPatterns ?? []).map((pattern, index2) => ({
|
|
22394
22372
|
kind: pattern.kind,
|
|
22395
22373
|
value: normalizePatternValue(pattern.value, `rules.excludedSlugPatterns[${index2}].value`)
|
|
22396
|
-
})).sort((a, b) =>
|
|
22374
|
+
})).sort((a, b) => compareText(`${a.kind}:${a.value}`, `${b.kind}:${b.value}`));
|
|
22397
22375
|
return { ownedHosts, rules: { primary, aliases, excludedSlugSuffixes, excludedSlugPatterns } };
|
|
22398
22376
|
}
|
|
22399
22377
|
function parseSitemapUrl(value) {
|
|
@@ -22473,10 +22451,10 @@ function classifyMeasurementSitemapUrls(input) {
|
|
|
22473
22451
|
const shared = [];
|
|
22474
22452
|
const unmatched = [];
|
|
22475
22453
|
const excluded = [];
|
|
22476
|
-
const canonicalEntries = [...canonicalGroups.entries()].sort(([a], [b]) =>
|
|
22454
|
+
const canonicalEntries = [...canonicalGroups.entries()].sort(([a], [b]) => compareText(a, b));
|
|
22477
22455
|
let classifiedUrlCount = 0;
|
|
22478
22456
|
for (const [canonicalUrl, group] of canonicalEntries) {
|
|
22479
|
-
const orderedGroup = [...group].sort((a, b) =>
|
|
22457
|
+
const orderedGroup = [...group].sort((a, b) => compareText(a.sourceUrl, b.sourceUrl));
|
|
22480
22458
|
const representative = orderedGroup[0];
|
|
22481
22459
|
for (let index2 = 1; index2 < orderedGroup.length; index2 += 1) {
|
|
22482
22460
|
diagnostics.push({
|
|
@@ -22534,7 +22512,7 @@ function classifyMeasurementSitemapUrls(input) {
|
|
|
22534
22512
|
else unmatched.push(item(canonicalUrl, "unmatched", "unmatched-path"));
|
|
22535
22513
|
}
|
|
22536
22514
|
const proposed = [...candidatesBySlug.values()].map(({ slug, stableKey, url }) => {
|
|
22537
|
-
const aliasCoverageUrls =
|
|
22515
|
+
const aliasCoverageUrls = sortedUnique((aliasesBySlug.get(slug) ?? []).map((match) => match.url));
|
|
22538
22516
|
return {
|
|
22539
22517
|
classification: "proposed",
|
|
22540
22518
|
reason: "primary-match",
|
|
@@ -22544,7 +22522,7 @@ function classifyMeasurementSitemapUrls(input) {
|
|
|
22544
22522
|
primaryUrl: url,
|
|
22545
22523
|
aliasCoverageUrls
|
|
22546
22524
|
};
|
|
22547
|
-
}).sort((a, b) =>
|
|
22525
|
+
}).sort((a, b) => compareText(a.stableKey, b.stableKey));
|
|
22548
22526
|
const aliases = [];
|
|
22549
22527
|
for (const [slug, matches] of aliasesBySlug) {
|
|
22550
22528
|
const target = candidatesBySlug.get(slug);
|
|
@@ -22562,14 +22540,14 @@ function classifyMeasurementSitemapUrls(input) {
|
|
|
22562
22540
|
}
|
|
22563
22541
|
for (const match of matches) unmatched.push(item(match.url, "unmatched", "alias-without-primary"));
|
|
22564
22542
|
}
|
|
22565
|
-
const sortItems = (items) => items.sort((a, b) =>
|
|
22543
|
+
const sortItems = (items) => items.sort((a, b) => compareText(a.url, b.url));
|
|
22566
22544
|
return {
|
|
22567
22545
|
proposed,
|
|
22568
|
-
aliases: aliases.sort((a, b) =>
|
|
22546
|
+
aliases: aliases.sort((a, b) => compareText(a.url, b.url)),
|
|
22569
22547
|
shared: sortItems(shared),
|
|
22570
22548
|
unmatched: sortItems(unmatched),
|
|
22571
22549
|
excluded: sortItems(excluded),
|
|
22572
|
-
diagnostics: diagnostics.sort((a, b) =>
|
|
22550
|
+
diagnostics: diagnostics.sort((a, b) => compareText(`${a.kind}:${a.url}`, `${b.kind}:${b.url}`))
|
|
22573
22551
|
};
|
|
22574
22552
|
}
|
|
22575
22553
|
|
|
@@ -22580,7 +22558,117 @@ import { gunzip } from "zlib";
|
|
|
22580
22558
|
import { promisify as promisify2 } from "util";
|
|
22581
22559
|
import http from "http";
|
|
22582
22560
|
import https from "https";
|
|
22561
|
+
import net2 from "net";
|
|
22562
|
+
|
|
22563
|
+
// ../api-routes/src/egress-policy.ts
|
|
22583
22564
|
import net from "net";
|
|
22565
|
+
function blockedAddressReason(value, options = {}) {
|
|
22566
|
+
const address = stripZoneId(stripIpv6Brackets(value));
|
|
22567
|
+
if (options.allowLoopback && isLoopbackAddress(address)) return null;
|
|
22568
|
+
const family = net.isIP(address);
|
|
22569
|
+
if (family === 4) return blockedIpv4Reason(ipv4Octets(address));
|
|
22570
|
+
if (family === 6) {
|
|
22571
|
+
const bytes = ipv6Bytes(address);
|
|
22572
|
+
return bytes ? blockedIpv6Reason(bytes) : "which could not be read as an address";
|
|
22573
|
+
}
|
|
22574
|
+
return "which is not an IP address";
|
|
22575
|
+
}
|
|
22576
|
+
function isLoopbackAddress(value) {
|
|
22577
|
+
const address = stripZoneId(stripIpv6Brackets(value));
|
|
22578
|
+
const family = net.isIP(address);
|
|
22579
|
+
if (family === 4) return ipv4Octets(address)[0] === 127;
|
|
22580
|
+
if (family !== 6) return false;
|
|
22581
|
+
const bytes = ipv6Bytes(address);
|
|
22582
|
+
if (!bytes) return false;
|
|
22583
|
+
const mapped = mappedIpv4(bytes);
|
|
22584
|
+
if (mapped) return mapped[0] === 127;
|
|
22585
|
+
return bytes.slice(0, 15).every((byte) => byte === 0) && bytes[15] === 1;
|
|
22586
|
+
}
|
|
22587
|
+
function stripIpv6Brackets(value) {
|
|
22588
|
+
return value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
22589
|
+
}
|
|
22590
|
+
function stripZoneId(address) {
|
|
22591
|
+
return address.split("%")[0];
|
|
22592
|
+
}
|
|
22593
|
+
function ipv4Octets(address) {
|
|
22594
|
+
return address.split(".").map((part) => Number.parseInt(part, 10));
|
|
22595
|
+
}
|
|
22596
|
+
function blockedIpv4Reason(octets) {
|
|
22597
|
+
const [first, second] = octets;
|
|
22598
|
+
if (first === 0) return "which is unspecified space";
|
|
22599
|
+
if (first === 127) return "which is loopback";
|
|
22600
|
+
if (first === 10 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168) {
|
|
22601
|
+
return "which is private space";
|
|
22602
|
+
}
|
|
22603
|
+
if (first === 100 && second >= 64 && second <= 127) return "which is carrier-grade NAT space";
|
|
22604
|
+
if (first === 169 && second === 254) return "which is link-local";
|
|
22605
|
+
if (first >= 224 && first <= 239) return "which is multicast";
|
|
22606
|
+
if (first === 192 && second === 0 && octets[2] === 0 || first === 198 && (second === 18 || second === 19) || first >= 240) {
|
|
22607
|
+
return "which is reserved";
|
|
22608
|
+
}
|
|
22609
|
+
return null;
|
|
22610
|
+
}
|
|
22611
|
+
function blockedIpv6Reason(bytes) {
|
|
22612
|
+
const first = bytes[0];
|
|
22613
|
+
if (first === 0) {
|
|
22614
|
+
const nat64 = nat64Ipv4(bytes);
|
|
22615
|
+
if (nat64) return blockedIpv4Reason(nat64) === null ? null : `which is the NAT64 form of ${nat64.join(".")}`;
|
|
22616
|
+
const mapped = mappedIpv4(bytes);
|
|
22617
|
+
return mapped ? `which is the IPv6 form of ${mapped.join(".")}` : "which is reserved IPv6 space";
|
|
22618
|
+
}
|
|
22619
|
+
if (first === 1 && bytes.slice(1, 8).every((byte) => byte === 0)) return "which is reserved IPv6 space";
|
|
22620
|
+
if (first === 255) return "which is multicast";
|
|
22621
|
+
if ((first & 254) === 252) return "which is unique-local";
|
|
22622
|
+
if (first === 254 && (bytes[1] & 192) === 128) return "which is link-local";
|
|
22623
|
+
if (first === 254 && (bytes[1] & 192) === 192) return "which is reserved site-local space";
|
|
22624
|
+
if (first === 32 && bytes[1] === 1 && bytes[2] === 0 && bytes[3] === 0) return "which is a Teredo tunnel";
|
|
22625
|
+
if (first === 32 && bytes[1] === 2) return "which is a 6to4 tunnel";
|
|
22626
|
+
if (first === 32 && bytes[1] === 1 && bytes[2] === 0 && bytes[3] === 2 && bytes[4] === 0 && bytes[5] === 0) {
|
|
22627
|
+
return "which is reserved IPv6 space";
|
|
22628
|
+
}
|
|
22629
|
+
return null;
|
|
22630
|
+
}
|
|
22631
|
+
function mappedIpv4(bytes) {
|
|
22632
|
+
const isMapped = bytes.slice(0, 10).every((byte) => byte === 0) && bytes[10] === 255 && bytes[11] === 255;
|
|
22633
|
+
return isMapped ? [bytes[12], bytes[13], bytes[14], bytes[15]] : null;
|
|
22634
|
+
}
|
|
22635
|
+
var NAT64_WELL_KNOWN_PREFIX = [0, 100, 255, 155, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
22636
|
+
function nat64Ipv4(bytes) {
|
|
22637
|
+
const isNat64 = NAT64_WELL_KNOWN_PREFIX.every((byte, index2) => bytes[index2] === byte);
|
|
22638
|
+
return isNat64 ? [bytes[12], bytes[13], bytes[14], bytes[15]] : null;
|
|
22639
|
+
}
|
|
22640
|
+
function ipv6Bytes(address) {
|
|
22641
|
+
if (!net.isIPv6(address)) return null;
|
|
22642
|
+
const halves = address.split("::");
|
|
22643
|
+
if (halves.length > 2) return null;
|
|
22644
|
+
const expand = (text2) => {
|
|
22645
|
+
if (text2 === "") return [];
|
|
22646
|
+
const groups2 = [];
|
|
22647
|
+
const chunks = text2.split(":");
|
|
22648
|
+
for (const [index2, chunk] of chunks.entries()) {
|
|
22649
|
+
if (index2 === chunks.length - 1 && chunk.includes(".")) {
|
|
22650
|
+
const octets = ipv4Octets(chunk);
|
|
22651
|
+
groups2.push(octets[0] << 8 | octets[1], octets[2] << 8 | octets[3]);
|
|
22652
|
+
continue;
|
|
22653
|
+
}
|
|
22654
|
+
groups2.push(Number.parseInt(chunk, 16));
|
|
22655
|
+
}
|
|
22656
|
+
return groups2;
|
|
22657
|
+
};
|
|
22658
|
+
const head = expand(halves[0]);
|
|
22659
|
+
const tail = halves.length === 2 ? expand(halves[1]) : [];
|
|
22660
|
+
const missing = 8 - head.length - tail.length;
|
|
22661
|
+
if (halves.length === 1 ? missing !== 0 : missing < 0) return null;
|
|
22662
|
+
const groups = [...head, ...new Array(halves.length === 2 ? missing : 0).fill(0), ...tail];
|
|
22663
|
+
const bytes = new Uint8Array(16);
|
|
22664
|
+
for (const [index2, group] of groups.entries()) {
|
|
22665
|
+
bytes[index2 * 2] = group >> 8;
|
|
22666
|
+
bytes[index2 * 2 + 1] = group & 255;
|
|
22667
|
+
}
|
|
22668
|
+
return bytes;
|
|
22669
|
+
}
|
|
22670
|
+
|
|
22671
|
+
// ../api-routes/src/measurement-sitemap-fetch.ts
|
|
22584
22672
|
var gunzipAsync = promisify2(gunzip);
|
|
22585
22673
|
var DEFAULT_MEASUREMENT_SITEMAP_LIMITS = {
|
|
22586
22674
|
timeoutMs: 1e4,
|
|
@@ -22630,7 +22718,7 @@ async function fetchMeasurementSitemap(sitemapUrl, options = {}) {
|
|
|
22630
22718
|
}
|
|
22631
22719
|
}
|
|
22632
22720
|
await visit(sitemapUrl, 0);
|
|
22633
|
-
return { urls: [...urls].sort(
|
|
22721
|
+
return { urls: [...urls].sort(compareText), fetchedSitemaps: visited.size, bytesChecksum: documents.digest("hex") };
|
|
22634
22722
|
}
|
|
22635
22723
|
async function resolveMeasurementSitemapTarget(rawUrl, options = {}) {
|
|
22636
22724
|
let parsed;
|
|
@@ -22647,7 +22735,7 @@ async function resolveMeasurementSitemapTarget(rawUrl, options = {}) {
|
|
|
22647
22735
|
}
|
|
22648
22736
|
const hostname = stripIpv6Brackets(parsed.hostname);
|
|
22649
22737
|
if (!hostname) return { ok: false, message: "the URL has no hostname" };
|
|
22650
|
-
const literalFamily =
|
|
22738
|
+
const literalFamily = net2.isIPv6(hostname) ? 6 : net2.isIPv4(hostname) ? 4 : null;
|
|
22651
22739
|
const addresses = literalFamily ? [{ address: hostname, family: literalFamily }] : await (options.resolveAddresses ?? resolveSitemapAddresses)(hostname);
|
|
22652
22740
|
if (addresses.length === 0) return { ok: false, message: `${hostname} does not resolve` };
|
|
22653
22741
|
for (const entry of addresses) {
|
|
@@ -22663,85 +22751,6 @@ async function resolveSitemapAddresses(hostname) {
|
|
|
22663
22751
|
if (ipv6.status === "fulfilled") for (const address of ipv6.value) found.set(`6:${address}`, { address, family: 6 });
|
|
22664
22752
|
return [...found.values()];
|
|
22665
22753
|
}
|
|
22666
|
-
function blockedAddressReason(value) {
|
|
22667
|
-
const address = stripIpv6Brackets(value).split("%")[0];
|
|
22668
|
-
const family = net.isIP(address);
|
|
22669
|
-
if (family === 4) return blockedIpv4Reason(ipv4Octets(address));
|
|
22670
|
-
if (family === 6) {
|
|
22671
|
-
const bytes = ipv6Bytes(address);
|
|
22672
|
-
return bytes ? blockedIpv6Reason(bytes) : "which could not be read as an address";
|
|
22673
|
-
}
|
|
22674
|
-
return "which is not an IP address";
|
|
22675
|
-
}
|
|
22676
|
-
function ipv4Octets(address) {
|
|
22677
|
-
return address.split(".").map((part) => Number.parseInt(part, 10));
|
|
22678
|
-
}
|
|
22679
|
-
function blockedIpv4Reason(octets) {
|
|
22680
|
-
const [first, second] = octets;
|
|
22681
|
-
if (first === 0) return "which is unspecified space";
|
|
22682
|
-
if (first === 127) return "which is loopback";
|
|
22683
|
-
if (first === 10 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168) {
|
|
22684
|
-
return "which is private space";
|
|
22685
|
-
}
|
|
22686
|
-
if (first === 100 && second >= 64 && second <= 127) return "which is carrier-grade NAT space";
|
|
22687
|
-
if (first === 169 && second === 254) return "which is link-local";
|
|
22688
|
-
if (first >= 224 && first <= 239) return "which is multicast";
|
|
22689
|
-
if (first === 192 && second === 0 && octets[2] === 0 || first === 198 && (second === 18 || second === 19) || first >= 240) {
|
|
22690
|
-
return "which is reserved";
|
|
22691
|
-
}
|
|
22692
|
-
return null;
|
|
22693
|
-
}
|
|
22694
|
-
function blockedIpv6Reason(bytes) {
|
|
22695
|
-
const first = bytes[0];
|
|
22696
|
-
if (first === 0) {
|
|
22697
|
-
const mapped = mappedIpv4(bytes);
|
|
22698
|
-
return mapped ? `which is the IPv6 form of ${mapped.join(".")}` : "which is reserved IPv6 space";
|
|
22699
|
-
}
|
|
22700
|
-
if (first === 255) return "which is multicast";
|
|
22701
|
-
if ((first & 254) === 252) return "which is unique-local";
|
|
22702
|
-
if (first === 254 && (bytes[1] & 192) === 128) return "which is link-local";
|
|
22703
|
-
if (first === 254 && (bytes[1] & 192) === 192) return "which is reserved site-local space";
|
|
22704
|
-
if (first === 32 && bytes[1] === 1 && bytes[2] === 0 && bytes[3] === 0) return "which is a Teredo tunnel";
|
|
22705
|
-
if (first === 32 && bytes[1] === 2) return "which is a 6to4 tunnel";
|
|
22706
|
-
return null;
|
|
22707
|
-
}
|
|
22708
|
-
function mappedIpv4(bytes) {
|
|
22709
|
-
const isMapped = bytes.slice(0, 10).every((byte) => byte === 0) && bytes[10] === 255 && bytes[11] === 255;
|
|
22710
|
-
return isMapped ? [bytes[12], bytes[13], bytes[14], bytes[15]] : null;
|
|
22711
|
-
}
|
|
22712
|
-
function ipv6Bytes(address) {
|
|
22713
|
-
if (!net.isIPv6(address)) return null;
|
|
22714
|
-
const halves = address.split("::");
|
|
22715
|
-
if (halves.length > 2) return null;
|
|
22716
|
-
const expand = (text2) => {
|
|
22717
|
-
if (text2 === "") return [];
|
|
22718
|
-
const groups2 = [];
|
|
22719
|
-
const chunks = text2.split(":");
|
|
22720
|
-
for (const [index2, chunk] of chunks.entries()) {
|
|
22721
|
-
if (index2 === chunks.length - 1 && chunk.includes(".")) {
|
|
22722
|
-
const octets = ipv4Octets(chunk);
|
|
22723
|
-
groups2.push(octets[0] << 8 | octets[1], octets[2] << 8 | octets[3]);
|
|
22724
|
-
continue;
|
|
22725
|
-
}
|
|
22726
|
-
groups2.push(Number.parseInt(chunk, 16));
|
|
22727
|
-
}
|
|
22728
|
-
return groups2;
|
|
22729
|
-
};
|
|
22730
|
-
const head = expand(halves[0]);
|
|
22731
|
-
const tail = halves.length === 2 ? expand(halves[1]) : [];
|
|
22732
|
-
const missing = 8 - head.length - tail.length;
|
|
22733
|
-
if (halves.length === 1 ? missing !== 0 : missing < 0) return null;
|
|
22734
|
-
const groups = [...head, ...new Array(halves.length === 2 ? missing : 0).fill(0), ...tail];
|
|
22735
|
-
const bytes = new Uint8Array(16);
|
|
22736
|
-
for (const [index2, group] of groups.entries()) {
|
|
22737
|
-
bytes[index2 * 2] = group >> 8;
|
|
22738
|
-
bytes[index2 * 2 + 1] = group & 255;
|
|
22739
|
-
}
|
|
22740
|
-
return bytes;
|
|
22741
|
-
}
|
|
22742
|
-
function stripIpv6Brackets(value) {
|
|
22743
|
-
return value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
22744
|
-
}
|
|
22745
22754
|
async function requestFollowingValidatedRedirects(startUrl, resolveTarget, transport, limits, deadlineAt) {
|
|
22746
22755
|
let current = startUrl;
|
|
22747
22756
|
for (let redirects = 0; ; redirects += 1) {
|
|
@@ -22798,7 +22807,7 @@ async function requestPinnedSitemap(target, limits) {
|
|
|
22798
22807
|
path: `${target.url.pathname}${target.url.search}`,
|
|
22799
22808
|
headers: { Host: target.url.host, Accept: "application/xml,text/xml,*/*;q=0.1", "Accept-Encoding": "gzip" }
|
|
22800
22809
|
};
|
|
22801
|
-
if (secure) requestOptions.servername = target.url.hostname
|
|
22810
|
+
if (secure) requestOptions.servername = stripIpv6Brackets(target.url.hostname);
|
|
22802
22811
|
return await new Promise((resolve, reject) => {
|
|
22803
22812
|
let settled = false;
|
|
22804
22813
|
const succeed = (response) => {
|
|
@@ -22854,11 +22863,11 @@ async function decodeSitemapBody(body, headers, maxBodyBytes) {
|
|
|
22854
22863
|
}
|
|
22855
22864
|
function sitemapIndexLocations(xml) {
|
|
22856
22865
|
if (!hasXmlElement(xml, "sitemapindex")) return [];
|
|
22857
|
-
return
|
|
22866
|
+
return sortedUnique(xmlElementContents(xml, "sitemap").flatMap(extractLocation));
|
|
22858
22867
|
}
|
|
22859
22868
|
function urlSetLocations(xml) {
|
|
22860
22869
|
if (!hasXmlElement(xml, "urlset")) return [];
|
|
22861
|
-
return
|
|
22870
|
+
return sortedUnique(xmlElementContents(xml, "url").flatMap(extractLocation));
|
|
22862
22871
|
}
|
|
22863
22872
|
function extractLocation(xml) {
|
|
22864
22873
|
const value = xmlElementContents(xml, "loc")[0]?.trim();
|
|
@@ -22949,12 +22958,6 @@ function decodeXmlEntities(value) {
|
|
|
22949
22958
|
function firstHeader(value) {
|
|
22950
22959
|
return Array.isArray(value) ? value[0] : value;
|
|
22951
22960
|
}
|
|
22952
|
-
function compareText6(a, b) {
|
|
22953
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
22954
|
-
}
|
|
22955
|
-
function sortedUniqueUrls(values) {
|
|
22956
|
-
return [...new Set(values)].sort(compareText6);
|
|
22957
|
-
}
|
|
22958
22961
|
function assertLimits(limits) {
|
|
22959
22962
|
for (const [name, value] of Object.entries(limits)) {
|
|
22960
22963
|
if (!Number.isSafeInteger(value) || value < 1) throw new Error(`Sitemap ${name} must be a positive safe integer`);
|
|
@@ -23025,12 +23028,6 @@ var MEASUREMENT_DRAFT_MAX_QUERIES = 1e3;
|
|
|
23025
23028
|
var MEASUREMENT_PLAN_V2_MAX_BYTES = 4 * 1024 * 1024;
|
|
23026
23029
|
var CHECKSUM_PLACEHOLDER = "0".repeat(64);
|
|
23027
23030
|
var MEASUREMENT_V2_PROVENANCE_EPOCH = "1970-01-01T00:00:00.000Z";
|
|
23028
|
-
function compareText7(left, right) {
|
|
23029
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
23030
|
-
}
|
|
23031
|
-
function canonicalStrings(values) {
|
|
23032
|
-
return [...new Set(values)].sort(compareText7);
|
|
23033
|
-
}
|
|
23034
23031
|
function proposeQueryClass(queryText, brandNames) {
|
|
23035
23032
|
const matcher = compileBrandAliases(brandNames.filter((name) => brandKeyFromText(name).length > 0));
|
|
23036
23033
|
return matcherMatchesText(matcher, queryText) ? "branded" : "non-brand";
|
|
@@ -23142,7 +23139,7 @@ function createChecks() {
|
|
|
23142
23139
|
};
|
|
23143
23140
|
}
|
|
23144
23141
|
function normalizeProviders2(values) {
|
|
23145
|
-
return
|
|
23142
|
+
return sortedUnique(values.map((value) => value.trim().toLowerCase()).filter(Boolean));
|
|
23146
23143
|
}
|
|
23147
23144
|
function compileMeasurementDraft(authoring, context) {
|
|
23148
23145
|
const { checks, sink, failed } = createChecks();
|
|
@@ -23152,7 +23149,7 @@ function compileMeasurementDraft(authoring, context) {
|
|
|
23152
23149
|
} catch {
|
|
23153
23150
|
sink.fail("invalid-project-identity", "The project canonical domain is not a valid hostname.", ["identities", "projectBrand", "canonicalHost"]);
|
|
23154
23151
|
}
|
|
23155
|
-
const ownedHosts =
|
|
23152
|
+
const ownedHosts = sortedUnique([context.canonicalDomain, ...context.ownedDomains].flatMap((value) => {
|
|
23156
23153
|
try {
|
|
23157
23154
|
return [normalizeMeasurementHost(value)];
|
|
23158
23155
|
} catch {
|
|
@@ -23216,7 +23213,7 @@ function compileMeasurementDraft(authoring, context) {
|
|
|
23216
23213
|
}
|
|
23217
23214
|
aliasClaims.set(identity, target.stableKey);
|
|
23218
23215
|
});
|
|
23219
|
-
const aliases =
|
|
23216
|
+
const aliases = sortedUnique(target.aliases);
|
|
23220
23217
|
const aliasKeys = aliases.map(measurementMentionAliasKey).filter(Boolean);
|
|
23221
23218
|
target.identityAliases?.forEach((identityAlias, index2) => {
|
|
23222
23219
|
const identityKey = measurementMentionAliasKey(identityAlias);
|
|
@@ -23233,7 +23230,7 @@ function compileMeasurementDraft(authoring, context) {
|
|
|
23233
23230
|
stableKey: target.stableKey,
|
|
23234
23231
|
label: target.label,
|
|
23235
23232
|
aliases,
|
|
23236
|
-
...target.identityAliases === void 0 ? {} : { identityAliases:
|
|
23233
|
+
...target.identityAliases === void 0 ? {} : { identityAliases: sortedUnique(target.identityAliases) },
|
|
23237
23234
|
urlMatchers: matchers,
|
|
23238
23235
|
mentionNotApplicable: aliases.length === 0,
|
|
23239
23236
|
discoveryIdentity: target.discoveryIdentity ?? null
|
|
@@ -23281,8 +23278,8 @@ function compileMeasurementDraft(authoring, context) {
|
|
|
23281
23278
|
stableKey: group.stableKey,
|
|
23282
23279
|
label: group.label,
|
|
23283
23280
|
...group.parentGroupKey === void 0 ? {} : { parentGroupKey: group.parentGroupKey },
|
|
23284
|
-
targetKeys:
|
|
23285
|
-
competitors: [...group.competitors].map((competitor) => ({ ...competitor, aliases:
|
|
23281
|
+
targetKeys: sortedUnique(group.targetKeys),
|
|
23282
|
+
competitors: [...group.competitors].map((competitor) => ({ ...competitor, aliases: sortedUnique(competitor.aliases) })).sort((left, right) => compareText(left.stableKey, right.stableKey))
|
|
23286
23283
|
});
|
|
23287
23284
|
});
|
|
23288
23285
|
const resolveContext = (override, path8) => {
|
|
@@ -23300,7 +23297,7 @@ function compileMeasurementDraft(authoring, context) {
|
|
|
23300
23297
|
}
|
|
23301
23298
|
models[normalized] = model;
|
|
23302
23299
|
}
|
|
23303
|
-
const labels =
|
|
23300
|
+
const labels = sortedUnique(override?.locations ?? authoring.defaultContext.locations);
|
|
23304
23301
|
const locations = [];
|
|
23305
23302
|
labels.forEach((label, labelIndex) => {
|
|
23306
23303
|
const location = locationsByLabel.get(label);
|
|
@@ -23461,7 +23458,7 @@ function compileMeasurementDraft(authoring, context) {
|
|
|
23461
23458
|
}
|
|
23462
23459
|
}
|
|
23463
23460
|
if (failed()) return { ok: false, checks };
|
|
23464
|
-
const querySnapshots3 = [...usedQueryIds].sort(
|
|
23461
|
+
const querySnapshots3 = [...usedQueryIds].sort(compareText).map((queryId) => ({
|
|
23465
23462
|
queryId,
|
|
23466
23463
|
queryText: queriesById.get(queryId),
|
|
23467
23464
|
// Provenance is frozen at publish time so a later reader can explain the
|
|
@@ -23478,7 +23475,7 @@ function compileMeasurementDraft(authoring, context) {
|
|
|
23478
23475
|
projectBrand: {
|
|
23479
23476
|
canonicalHost,
|
|
23480
23477
|
ownedHosts,
|
|
23481
|
-
names:
|
|
23478
|
+
names: sortedUnique(context.brandNames)
|
|
23482
23479
|
}
|
|
23483
23480
|
},
|
|
23484
23481
|
targets: compiledTargets.map(({ target }) => target),
|
|
@@ -23527,13 +23524,13 @@ function keyedDiff2(before, after) {
|
|
|
23527
23524
|
const added = [];
|
|
23528
23525
|
const changed = [];
|
|
23529
23526
|
const unchanged = [];
|
|
23530
|
-
for (const stableKey of [...afterByKey.keys()].sort(
|
|
23527
|
+
for (const stableKey of [...afterByKey.keys()].sort(compareText)) {
|
|
23531
23528
|
const beforeValue = beforeByKey.get(stableKey);
|
|
23532
23529
|
if (!beforeValue) added.push(stableKey);
|
|
23533
23530
|
else if (JSON.stringify(canonicalJsonValue(beforeValue)) === JSON.stringify(canonicalJsonValue(afterByKey.get(stableKey)))) unchanged.push(stableKey);
|
|
23534
23531
|
else changed.push(stableKey);
|
|
23535
23532
|
}
|
|
23536
|
-
const removed = [...beforeByKey.keys()].filter((key) => !afterByKey.has(key)).sort(
|
|
23533
|
+
const removed = [...beforeByKey.keys()].filter((key) => !afterByKey.has(key)).sort(compareText);
|
|
23537
23534
|
return { added, removed, changed, unchanged };
|
|
23538
23535
|
}
|
|
23539
23536
|
function assignmentClasses(plan) {
|
|
@@ -23562,8 +23559,8 @@ function diffCompiledPlans(active, candidate, activeRevision) {
|
|
|
23562
23559
|
groups: keyedDiff2(active?.groups ?? [], candidate.groups),
|
|
23563
23560
|
assignments: { added: addedAssignments, removed: removedAssignments, reclassified },
|
|
23564
23561
|
execution: {
|
|
23565
|
-
addedNodeKeys: [...afterNodes].filter((key) => !beforeNodes.has(key)).sort(
|
|
23566
|
-
removedNodeKeys: [...beforeNodes].filter((key) => !afterNodes.has(key)).sort(
|
|
23562
|
+
addedNodeKeys: [...afterNodes].filter((key) => !beforeNodes.has(key)).sort(compareText),
|
|
23563
|
+
removedNodeKeys: [...beforeNodes].filter((key) => !afterNodes.has(key)).sort(compareText)
|
|
23567
23564
|
}
|
|
23568
23565
|
};
|
|
23569
23566
|
}
|
|
@@ -23636,9 +23633,6 @@ function requireGroup(authoring, groupKey) {
|
|
|
23636
23633
|
function unique(values) {
|
|
23637
23634
|
return [...new Set(values)];
|
|
23638
23635
|
}
|
|
23639
|
-
function compareText8(left, right) {
|
|
23640
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
23641
|
-
}
|
|
23642
23636
|
function knownQueryIds(queryIds, context) {
|
|
23643
23637
|
const uniqueQueryIds = unique(queryIds);
|
|
23644
23638
|
const unknown = uniqueQueryIds.filter((queryId) => !context.queriesById.has(queryId));
|
|
@@ -23692,7 +23686,7 @@ function resolveDraftAudience(authoring, audience) {
|
|
|
23692
23686
|
groups.push({ groupKey: group.stableKey, label: group.label, memberCount: memberKeys.length });
|
|
23693
23687
|
selected.push(...memberKeys);
|
|
23694
23688
|
}
|
|
23695
|
-
const targetKeys = unique(selected).sort(
|
|
23689
|
+
const targetKeys = unique(selected).sort(compareText);
|
|
23696
23690
|
return {
|
|
23697
23691
|
targetKeys,
|
|
23698
23692
|
groups,
|
|
@@ -24122,9 +24116,6 @@ var MeasurementGroupMembershipImportError = class extends Error {
|
|
|
24122
24116
|
statusCode;
|
|
24123
24117
|
details;
|
|
24124
24118
|
};
|
|
24125
|
-
function compareText9(left, right) {
|
|
24126
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
24127
|
-
}
|
|
24128
24119
|
function sha256Hex2(value) {
|
|
24129
24120
|
return crypto20.createHash("sha256").update(value).digest("hex");
|
|
24130
24121
|
}
|
|
@@ -24132,7 +24123,7 @@ function canonicalJsonValue2(value) {
|
|
|
24132
24123
|
if (Array.isArray(value)) return value.map(canonicalJsonValue2);
|
|
24133
24124
|
if (value && typeof value === "object") {
|
|
24134
24125
|
return Object.fromEntries(
|
|
24135
|
-
Object.entries(value).filter(([, child]) => child !== void 0).sort(([left], [right]) =>
|
|
24126
|
+
Object.entries(value).filter(([, child]) => child !== void 0).sort(([left], [right]) => compareText(left, right)).map(([key, child]) => [key, canonicalJsonValue2(child)])
|
|
24136
24127
|
);
|
|
24137
24128
|
}
|
|
24138
24129
|
return value;
|
|
@@ -24429,8 +24420,8 @@ function groupIdentities(rows, authoring, segments) {
|
|
|
24429
24420
|
groupsByLabel.set(label, matches);
|
|
24430
24421
|
}
|
|
24431
24422
|
const identities = [];
|
|
24432
|
-
for (const normalizedGroupLabel of [...labels.keys()].sort(
|
|
24433
|
-
const existing = [...groupsByLabel.get(normalizedGroupLabel) ?? []].sort((left, right) =>
|
|
24423
|
+
for (const normalizedGroupLabel of [...labels.keys()].sort(compareText)) {
|
|
24424
|
+
const existing = [...groupsByLabel.get(normalizedGroupLabel) ?? []].sort((left, right) => compareText(left.stableKey, right.stableKey));
|
|
24434
24425
|
if (existing.length > 1) {
|
|
24435
24426
|
identities.push({
|
|
24436
24427
|
kind: "ambiguous",
|
|
@@ -24508,8 +24499,8 @@ function groupChangesFromRows(rows, identities) {
|
|
|
24508
24499
|
bucket.rows.push(row);
|
|
24509
24500
|
byGroupKey.set(row.groupKey, bucket);
|
|
24510
24501
|
}
|
|
24511
|
-
return [...byGroupKey.values()].sort((left, right) =>
|
|
24512
|
-
const targetKeys =
|
|
24502
|
+
return [...byGroupKey.values()].sort((left, right) => compareText(left.identity.normalizedGroupLabel, right.identity.normalizedGroupLabel)).map(({ identity, rows: matchedRows }) => {
|
|
24503
|
+
const targetKeys = sortedUnique(matchedRows.map((row) => row.targetKey));
|
|
24513
24504
|
const addedTargetKeys = targetKeys.filter((targetKey) => !identity.existingTargetKeys.has(targetKey));
|
|
24514
24505
|
const unchangedTargetKeys = targetKeys.filter((targetKey) => identity.existingTargetKeys.has(targetKey));
|
|
24515
24506
|
return {
|
|
@@ -24573,7 +24564,7 @@ function resolveRows(parsedRows, authoring, identities) {
|
|
|
24573
24564
|
resolved.push(rowFailure(row, "invalid", "missing-group"));
|
|
24574
24565
|
continue;
|
|
24575
24566
|
}
|
|
24576
|
-
const labelCandidates = [...targetsByLabel.get(row.normalizedProperty) ?? []].sort((left, right) =>
|
|
24567
|
+
const labelCandidates = [...targetsByLabel.get(row.normalizedProperty) ?? []].sort((left, right) => compareText(left.stableKey, right.stableKey));
|
|
24577
24568
|
if (!labelCandidates.length) {
|
|
24578
24569
|
resolved.push(rowFailure(row, "unmatched", "property-not-found"));
|
|
24579
24570
|
continue;
|
|
@@ -24654,7 +24645,7 @@ function resolveArtifacts(input) {
|
|
|
24654
24645
|
return { rows, groupChanges, counts: countsFromRows(rows, groupChanges), identities };
|
|
24655
24646
|
}
|
|
24656
24647
|
function checksumIdentities(identities) {
|
|
24657
|
-
return [...identities].sort((left, right) =>
|
|
24648
|
+
return [...identities].sort((left, right) => compareText(left.normalizedGroupLabel, right.normalizedGroupLabel)).map((identity) => {
|
|
24658
24649
|
if (identity.kind === "ready") {
|
|
24659
24650
|
return {
|
|
24660
24651
|
normalizedGroupLabel: identity.normalizedGroupLabel,
|
|
@@ -24668,7 +24659,7 @@ function checksumIdentities(identities) {
|
|
|
24668
24659
|
return {
|
|
24669
24660
|
normalizedGroupLabel: identity.normalizedGroupLabel,
|
|
24670
24661
|
outcome: identity.kind,
|
|
24671
|
-
groupKeys: [...identity.groupKeys].sort(
|
|
24662
|
+
groupKeys: [...identity.groupKeys].sort(compareText)
|
|
24672
24663
|
};
|
|
24673
24664
|
}
|
|
24674
24665
|
return {
|
|
@@ -24676,8 +24667,8 @@ function checksumIdentities(identities) {
|
|
|
24676
24667
|
outcome: identity.kind,
|
|
24677
24668
|
groupKey: identity.groupKey,
|
|
24678
24669
|
evidence: [...identity.evidence].sort((left, right) => {
|
|
24679
|
-
const bySource =
|
|
24680
|
-
return bySource ||
|
|
24670
|
+
const bySource = compareText(left.source, right.source);
|
|
24671
|
+
return bySource || compareText(left.normalizedGroupLabel ?? "", right.normalizedGroupLabel ?? "");
|
|
24681
24672
|
})
|
|
24682
24673
|
};
|
|
24683
24674
|
});
|
|
@@ -24697,7 +24688,7 @@ function resolveGroupMembershipPreview(input) {
|
|
|
24697
24688
|
})),
|
|
24698
24689
|
rows: artifacts.rows,
|
|
24699
24690
|
proposedGroups: checksumIdentities(artifacts.identities),
|
|
24700
|
-
persistedSegments: [...input.segments].sort((left, right) =>
|
|
24691
|
+
persistedSegments: [...input.segments].sort((left, right) => compareText(left.stableKey, right.stableKey)).map((segment) => ({ stableKey: segment.stableKey, kind: segment.kind, retiredAt: segment.retiredAt }))
|
|
24701
24692
|
});
|
|
24702
24693
|
return {
|
|
24703
24694
|
draftEtag: input.draftEtag,
|
|
@@ -24825,9 +24816,6 @@ function applyReviewedGroupMembership(authoring, preview, review) {
|
|
|
24825
24816
|
|
|
24826
24817
|
// ../api-routes/src/measurement-draft.ts
|
|
24827
24818
|
var MEASUREMENT_GROUP_MEMBERSHIP_BODY_LIMIT = MEASUREMENT_GROUP_MEMBERSHIP_CSV_MAX_BYTES * 7 + 4096;
|
|
24828
|
-
function compareText10(left, right) {
|
|
24829
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
24830
|
-
}
|
|
24831
24819
|
function authoringIdentity(authoring) {
|
|
24832
24820
|
return canonicalJson(authoring);
|
|
24833
24821
|
}
|
|
@@ -24896,7 +24884,7 @@ function emptyAuthoring(project, opts) {
|
|
|
24896
24884
|
defaultContext: {
|
|
24897
24885
|
providers,
|
|
24898
24886
|
...Object.keys(models).length ? { models } : {},
|
|
24899
|
-
locations: project.locations.map((location) => location.label).sort(
|
|
24887
|
+
locations: project.locations.map((location) => location.label).sort(compareText)
|
|
24900
24888
|
},
|
|
24901
24889
|
targets: [],
|
|
24902
24890
|
assignments: [],
|
|
@@ -25106,7 +25094,7 @@ function pageLimit(value) {
|
|
|
25106
25094
|
}
|
|
25107
25095
|
function paginate(rows, query, sortKey, searchable) {
|
|
25108
25096
|
const needle = query.search?.trim().toLowerCase();
|
|
25109
|
-
const filtered = (needle ? rows.filter((row) => searchable(row).toLowerCase().includes(needle)) : [...rows]).sort((left, right) =>
|
|
25097
|
+
const filtered = (needle ? rows.filter((row) => searchable(row).toLowerCase().includes(needle)) : [...rows]).sort((left, right) => compareText(sortKey(left), sortKey(right)));
|
|
25110
25098
|
const after = query.cursor ? Buffer.from(query.cursor, "base64url").toString("utf8") : null;
|
|
25111
25099
|
const remaining = after === null ? filtered : filtered.filter((row) => sortKey(row) > after);
|
|
25112
25100
|
const limit = pageLimit(query.limit);
|
|
@@ -26112,22 +26100,22 @@ function proposeMeasurementDiscoveryBindings(input) {
|
|
|
26112
26100
|
if (excluded.has(candidate.slug) || excluded.has(identity.toLowerCase())) continue;
|
|
26113
26101
|
const parts = parseMeasurementDiscoveryIdentity(identity);
|
|
26114
26102
|
if (!parts) continue;
|
|
26115
|
-
const candidates = missing.map((entry) => ({ targetKey: entry.target.stableKey, score: rebindScore(parts, entry.parts) })).filter((entry) => entry.score > MEASUREMENT_REBIND_THRESHOLD).sort((left, right) => right.score - left.score ||
|
|
26103
|
+
const candidates = missing.map((entry) => ({ targetKey: entry.target.stableKey, score: rebindScore(parts, entry.parts) })).filter((entry) => entry.score > MEASUREMENT_REBIND_THRESHOLD).sort((left, right) => right.score - left.score || compareText(left.targetKey, right.targetKey));
|
|
26116
26104
|
proposals.push({
|
|
26117
26105
|
discoveryIdentity: identity,
|
|
26118
26106
|
stableKey: candidate.stableKey,
|
|
26119
26107
|
slug: candidate.slug,
|
|
26120
26108
|
label: candidate.label,
|
|
26121
26109
|
discoveredUrl: candidate.primaryUrl,
|
|
26122
|
-
urlMatchers:
|
|
26110
|
+
urlMatchers: sortedUnique([candidate.primaryUrl, ...candidate.aliasCoverageUrls]),
|
|
26123
26111
|
kind: candidates.length === 1 ? "rebind" : candidates.length === 0 ? "new-target" : "ambiguous",
|
|
26124
26112
|
...candidates.length === 1 ? { rebindTargetKey: candidates[0].targetKey } : {},
|
|
26125
26113
|
candidates
|
|
26126
26114
|
});
|
|
26127
26115
|
}
|
|
26128
26116
|
return {
|
|
26129
|
-
proposals: proposals.sort((left, right) =>
|
|
26130
|
-
unchanged: input.targets.filter((target) => target.discoveryIdentity !== void 0 && present.has(target.discoveryIdentity)).map((target) => target.stableKey).sort(
|
|
26117
|
+
proposals: proposals.sort((left, right) => compareText(left.discoveryIdentity, right.discoveryIdentity)),
|
|
26118
|
+
unchanged: input.targets.filter((target) => target.discoveryIdentity !== void 0 && present.has(target.discoveryIdentity)).map((target) => target.stableKey).sort(compareText)
|
|
26131
26119
|
};
|
|
26132
26120
|
}
|
|
26133
26121
|
function rebindScore(left, right) {
|
|
@@ -26150,15 +26138,12 @@ function normalizePathTemplate(value) {
|
|
|
26150
26138
|
function hostOfUrl(value) {
|
|
26151
26139
|
return normalizeMeasurementHost(new URL(value).hostname);
|
|
26152
26140
|
}
|
|
26153
|
-
function compareText11(left, right) {
|
|
26154
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
26155
|
-
}
|
|
26156
26141
|
function canonicalJson2(value) {
|
|
26157
26142
|
const canonical = (input) => {
|
|
26158
26143
|
if (Array.isArray(input)) return input.map(canonical);
|
|
26159
26144
|
if (input && typeof input === "object") {
|
|
26160
26145
|
return Object.fromEntries(
|
|
26161
|
-
Object.entries(input).filter(([, child]) => child !== void 0).sort(([left], [right]) =>
|
|
26146
|
+
Object.entries(input).filter(([, child]) => child !== void 0).sort(([left], [right]) => compareText(left, right)).map(([key, child]) => [key, canonical(child)])
|
|
26162
26147
|
);
|
|
26163
26148
|
}
|
|
26164
26149
|
return input;
|
|
@@ -26173,20 +26158,10 @@ function measurementDiscoveryInputChecksum(input) {
|
|
|
26173
26158
|
compilerVersion: MEASUREMENT_DISCOVERY_COMPILER_VERSION,
|
|
26174
26159
|
sitemapUrl: input.sitemapUrl,
|
|
26175
26160
|
rule: input.rule,
|
|
26176
|
-
exclusions:
|
|
26161
|
+
exclusions: sortedUnique(input.exclusions.map((value) => value.trim().toLowerCase())),
|
|
26177
26162
|
bytesChecksum: input.bytesChecksum
|
|
26178
26163
|
}));
|
|
26179
26164
|
}
|
|
26180
|
-
function draftCounts2(authoring) {
|
|
26181
|
-
return {
|
|
26182
|
-
targets: authoring.targets.length,
|
|
26183
|
-
includedTargets: authoring.targets.filter((target) => target.status === "included").length,
|
|
26184
|
-
assignments: authoring.assignments.length,
|
|
26185
|
-
unclassifiedAssignments: authoring.assignments.filter((assignment) => assignment.queryClass === "unclassified").length,
|
|
26186
|
-
groups: authoring.groups.length,
|
|
26187
|
-
competitors: authoring.groups.reduce((total, group) => total + group.competitors.length, 0)
|
|
26188
|
-
};
|
|
26189
|
-
}
|
|
26190
26165
|
function loadDraft(app, projectId, projectName) {
|
|
26191
26166
|
const row = app.db.select().from(measurementPlanDrafts).where(eq32(measurementPlanDrafts.projectId, projectId)).get();
|
|
26192
26167
|
if (!row) throw notFound("Measurement plan draft", projectName);
|
|
@@ -26243,7 +26218,7 @@ function commitDraft(app, input) {
|
|
|
26243
26218
|
etag: measurementDraftEtag(etagVersion),
|
|
26244
26219
|
changed: input.changed,
|
|
26245
26220
|
warnings: input.warnings,
|
|
26246
|
-
counts:
|
|
26221
|
+
counts: draftCounts(authoring.data)
|
|
26247
26222
|
});
|
|
26248
26223
|
const actor = actorReference(input.request);
|
|
26249
26224
|
app.db.transaction((tx) => {
|
|
@@ -26346,7 +26321,7 @@ async function measurementDiscoveryV2Routes(app) {
|
|
|
26346
26321
|
if (!parsed.success) {
|
|
26347
26322
|
throw validationError("Invalid sitemap import request", { issues: parsed.error.issues });
|
|
26348
26323
|
}
|
|
26349
|
-
const exclusions =
|
|
26324
|
+
const exclusions = sortedUnique((parsed.data.exclusions ?? []).map((value) => value.trim().toLowerCase()));
|
|
26350
26325
|
const idempotencyKey = requireIdempotencyKey2(request, "import-sitemap");
|
|
26351
26326
|
const requestChecksum2 = sha2562(canonicalJson2({ ...parsed.data, exclusions }));
|
|
26352
26327
|
const replay = replayedResponse(app, project.id, "import-sitemap", idempotencyKey, requestChecksum2);
|
|
@@ -26491,7 +26466,7 @@ async function measurementDiscoveryV2Routes(app) {
|
|
|
26491
26466
|
const rebound = {
|
|
26492
26467
|
...existing,
|
|
26493
26468
|
...relabelled,
|
|
26494
|
-
urlMatchers:
|
|
26469
|
+
urlMatchers: sortedUnique([...existing.urlMatchers, ...proposal.urlMatchers]),
|
|
26495
26470
|
discoveredUrl: proposal.discoveredUrl,
|
|
26496
26471
|
discoveryIdentity: proposal.discoveryIdentity
|
|
26497
26472
|
};
|
|
@@ -26549,15 +26524,12 @@ function parseEvidenceQuery(raw) {
|
|
|
26549
26524
|
}
|
|
26550
26525
|
return parsed.data;
|
|
26551
26526
|
}
|
|
26552
|
-
function normalizedText2(value) {
|
|
26553
|
-
return value.normalize("NFKC").trim().replace(/\s+/g, " ").toLocaleLowerCase("en");
|
|
26554
|
-
}
|
|
26555
26527
|
function filterFingerprint(query) {
|
|
26556
26528
|
const filters = {
|
|
26557
26529
|
targetKey: query.targetKey,
|
|
26558
26530
|
queryClass: query.queryClass ?? "all",
|
|
26559
|
-
provider: query.provider === void 0 ? null :
|
|
26560
|
-
location: query.location === void 0 ? null :
|
|
26531
|
+
provider: query.provider === void 0 ? null : normalizeIdentityText(query.provider),
|
|
26532
|
+
location: query.location === void 0 ? null : normalizeIdentityText(query.location)
|
|
26561
26533
|
};
|
|
26562
26534
|
return createHash5("sha256").update(JSON.stringify(filters)).digest("base64url");
|
|
26563
26535
|
}
|
|
@@ -26619,7 +26591,7 @@ function propertyEvidenceRows(db, plan, active, run, query, queryClass) {
|
|
|
26619
26591
|
const manifest = measurementRunExpectedSlots(run, plan);
|
|
26620
26592
|
const { input, edgeQueryClass } = buildMeasurementPlanV2ReportInput(active.version.revision, plan, manifest, snapshots);
|
|
26621
26593
|
const ownEdgeIds = new Set(input.usageEdges.filter((edge) => edge.type === "target" && edge.targetId === query.targetKey).filter((edge) => queryClass === "all" || edgeQueryClass.get(edge.id) === queryClass).map((edge) => edge.id));
|
|
26622
|
-
const provider = query.provider === void 0 ? void 0 :
|
|
26594
|
+
const provider = query.provider === void 0 ? void 0 : normalizeIdentityText(query.provider);
|
|
26623
26595
|
const location = query.location === void 0 ? void 0 : normalizeMeasurementLocation(query.location);
|
|
26624
26596
|
const owned = (row) => ownEdgeIds.has(row.usageEdgeId) && (provider === void 0 || row.provider === provider) && (location === void 0 || normalizeMeasurementLocation(row.location) === location);
|
|
26625
26597
|
const built = buildMeasurementEvidence(input);
|
|
@@ -26734,12 +26706,6 @@ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
|
26734
26706
|
RunStatuses.partial
|
|
26735
26707
|
]);
|
|
26736
26708
|
var { rawResponse: omittedRawResponse, ...measurementQuestionSnapshotColumns } = getTableColumns(querySnapshots);
|
|
26737
|
-
function compareText12(left, right) {
|
|
26738
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
26739
|
-
}
|
|
26740
|
-
function normalizeText(value) {
|
|
26741
|
-
return value.normalize("NFKC").trim().replace(/\s+/g, " ").toLocaleLowerCase("en");
|
|
26742
|
-
}
|
|
26743
26709
|
function excerptOf(answer) {
|
|
26744
26710
|
const normalized = answer.normalize("NFKC").trim().replace(/\s+/g, " ");
|
|
26745
26711
|
return normalized.length <= 240 ? normalized : normalized.slice(0, 240);
|
|
@@ -26826,16 +26792,16 @@ function selectMeasurementQuestionRun(db, projectId, active, runId) {
|
|
|
26826
26792
|
return run;
|
|
26827
26793
|
}
|
|
26828
26794
|
function slotKey(executionId, provider) {
|
|
26829
|
-
return `${executionId}\0${
|
|
26795
|
+
return `${executionId}\0${normalizeIdentityText(provider)}`;
|
|
26830
26796
|
}
|
|
26831
26797
|
function scopedManifest(manifest, scope) {
|
|
26832
26798
|
if (scope === void 0) return manifest;
|
|
26833
26799
|
const executionIds = new Set(scope.executionIds);
|
|
26834
|
-
const provider = scope.provider === void 0 ? void 0 :
|
|
26800
|
+
const provider = scope.provider === void 0 ? void 0 : normalizeIdentityText(scope.provider);
|
|
26835
26801
|
const location = scope.location === void 0 ? void 0 : normalizeMeasurementLocation(scope.location);
|
|
26836
26802
|
return {
|
|
26837
26803
|
...manifest,
|
|
26838
|
-
expectedSlots: manifest.expectedSlots.filter((slot) => executionIds.has(slot.executionId) && (provider === void 0 ||
|
|
26804
|
+
expectedSlots: manifest.expectedSlots.filter((slot) => executionIds.has(slot.executionId) && (provider === void 0 || normalizeIdentityText(slot.provider) === provider) && (location === void 0 || normalizeMeasurementLocation(slot.context?.label ?? null) === location))
|
|
26839
26805
|
};
|
|
26840
26806
|
}
|
|
26841
26807
|
function selectedQuestionSnapshots(db, runId, manifest, scope) {
|
|
@@ -26846,7 +26812,7 @@ function selectedQuestionSnapshots(db, runId, manifest, scope) {
|
|
|
26846
26812
|
conditions.push(inArray15(querySnapshots.measurementExecutionId, executionIds));
|
|
26847
26813
|
}
|
|
26848
26814
|
if (scope?.provider !== void 0) {
|
|
26849
|
-
conditions.push(sql12`lower(trim(${querySnapshots.provider})) = ${
|
|
26815
|
+
conditions.push(sql12`lower(trim(${querySnapshots.provider})) = ${normalizeIdentityText(scope.provider)}`);
|
|
26850
26816
|
}
|
|
26851
26817
|
return db.select(measurementQuestionSnapshotColumns).from(querySnapshots).where(and26(...conditions)).all();
|
|
26852
26818
|
}
|
|
@@ -26921,7 +26887,7 @@ function questionRows(plan, target, materialized, filters = {}) {
|
|
|
26921
26887
|
for (const slot of materialized.input.expectedSlots) {
|
|
26922
26888
|
const assignment = assignmentsByExecution.get(slot.executionId);
|
|
26923
26889
|
if (!assignment) continue;
|
|
26924
|
-
if (filters.provider !== void 0 &&
|
|
26890
|
+
if (filters.provider !== void 0 && normalizeIdentityText(slot.provider) !== normalizeIdentityText(filters.provider)) continue;
|
|
26925
26891
|
if (filters.location !== void 0 && normalizeMeasurementLocation(slot.location) !== normalizeMeasurementLocation(filters.location)) continue;
|
|
26926
26892
|
const question = queryById.get(assignment.queryId);
|
|
26927
26893
|
const manifestSlot = materialized.manifestSlotsByIdentity.get(slotKey(slot.executionId, slot.provider));
|
|
@@ -26958,7 +26924,7 @@ function questionRows(plan, target, materialized, filters = {}) {
|
|
|
26958
26924
|
const key = `${row.queryId}\0${row.provider}\0${row.location ?? ""}`;
|
|
26959
26925
|
if (!deduped.has(key)) deduped.set(key, row);
|
|
26960
26926
|
}
|
|
26961
|
-
return [...deduped.values()].sort((left, right) =>
|
|
26927
|
+
return [...deduped.values()].sort((left, right) => compareText(left.queryId, right.queryId) || compareText(left.provider, right.provider) || compareText(left.location ?? "", right.location ?? "") || compareText(left.text, right.text));
|
|
26962
26928
|
}
|
|
26963
26929
|
function recommendedInstead(candidates, target) {
|
|
26964
26930
|
const ownNames = new Set([target.label, ...target.aliases].map(brandKeyFromText).filter((key) => key.length > 0));
|
|
@@ -27095,12 +27061,6 @@ async function measurementQuestionReadRoutes(app) {
|
|
|
27095
27061
|
|
|
27096
27062
|
// ../api-routes/src/measurement-portfolio-reads.ts
|
|
27097
27063
|
var DEFAULT_LIMIT3 = 10;
|
|
27098
|
-
function compareText13(left, right) {
|
|
27099
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
27100
|
-
}
|
|
27101
|
-
function normalizeText2(value) {
|
|
27102
|
-
return value.normalize("NFKC").trim().replace(/\s+/g, " ").toLocaleLowerCase("en");
|
|
27103
|
-
}
|
|
27104
27064
|
function parseLimitQuery(raw, schema, message) {
|
|
27105
27065
|
const candidate = { ...raw, ...raw.limit === void 0 ? {} : { limit: Number(raw.limit) } };
|
|
27106
27066
|
const parsed = schema.safeParse(candidate);
|
|
@@ -27173,9 +27133,9 @@ function materializeWithDb(db, active, plan, run) {
|
|
|
27173
27133
|
return materializeMeasurementQuestionRun(db, active, plan, run);
|
|
27174
27134
|
}
|
|
27175
27135
|
function filteredEvaluator(materialized, filters, targetKeys) {
|
|
27176
|
-
const provider = filters.provider === void 0 ? void 0 :
|
|
27136
|
+
const provider = filters.provider === void 0 ? void 0 : normalizeIdentityText(filters.provider);
|
|
27177
27137
|
const location = filters.location === void 0 ? void 0 : normalizeMeasurementLocation(filters.location);
|
|
27178
|
-
const expectedSlots = materialized.input.expectedSlots.filter((slot) => (provider === void 0 ||
|
|
27138
|
+
const expectedSlots = materialized.input.expectedSlots.filter((slot) => (provider === void 0 || normalizeIdentityText(slot.provider) === provider) && (location === void 0 || normalizeMeasurementLocation(slot.location) === location));
|
|
27179
27139
|
const usageEdges = materialized.input.usageEdges.filter((edge) => filters.queryClass === "all" || materialized.edgeQueryClass.get(edge.id) === filters.queryClass);
|
|
27180
27140
|
return createMeasurementOverviewEvaluator({
|
|
27181
27141
|
...materialized.input,
|
|
@@ -27230,12 +27190,12 @@ function createTargetAnswerReader(plan, materialized, filters) {
|
|
|
27230
27190
|
}
|
|
27231
27191
|
const incompleteObservationIds = new Set(materialized.evidence.diagnostics.evidenceIncompleteObservationIds);
|
|
27232
27192
|
const questionsById = new Map(plan.querySnapshots.map((question) => [question.queryId, question.queryText]));
|
|
27233
|
-
const provider = filters.provider === void 0 ? void 0 :
|
|
27193
|
+
const provider = filters.provider === void 0 ? void 0 : normalizeIdentityText(filters.provider);
|
|
27234
27194
|
const location = filters.location === void 0 ? void 0 : normalizeMeasurementLocation(filters.location);
|
|
27235
27195
|
const slotsByExecution = /* @__PURE__ */ new Map();
|
|
27236
27196
|
const slotOrder = /* @__PURE__ */ new Map();
|
|
27237
27197
|
materialized.input.expectedSlots.forEach((slot, index2) => {
|
|
27238
|
-
if (provider !== void 0 &&
|
|
27198
|
+
if (provider !== void 0 && normalizeIdentityText(slot.provider) !== provider) return;
|
|
27239
27199
|
if (location !== void 0 && normalizeMeasurementLocation(slot.location) !== location) return;
|
|
27240
27200
|
pushTo(slotsByExecution, slot.executionId, slot);
|
|
27241
27201
|
slotOrder.set(slot.id, index2);
|
|
@@ -27316,7 +27276,7 @@ function domainRows(answers, limit, readHost = createCitedHostReader()) {
|
|
|
27316
27276
|
const domains = new Set(answerDomains(answer, readHost).values());
|
|
27317
27277
|
for (const domain of domains) counts2.set(domain, (counts2.get(domain) ?? 0) + 1);
|
|
27318
27278
|
}
|
|
27319
|
-
const rows = [...counts2].map(([domain, count6]) => ({ domain, answers: count6 })).sort((left, right) => right.answers - left.answers ||
|
|
27279
|
+
const rows = [...counts2].map(([domain, count6]) => ({ domain, answers: count6 })).sort((left, right) => right.answers - left.answers || compareText(left.domain, right.domain));
|
|
27320
27280
|
return { rows: rows.slice(0, limit), total: rows.length };
|
|
27321
27281
|
}
|
|
27322
27282
|
function targetAliasKeys(target) {
|
|
@@ -27341,7 +27301,7 @@ function recommendationRows(target, population) {
|
|
|
27341
27301
|
forEachNameWrittenInstead(target, population.answers, (key, name, answer, firstInAnswer) => {
|
|
27342
27302
|
const existing = grouped.get(key);
|
|
27343
27303
|
if (existing) {
|
|
27344
|
-
if (
|
|
27304
|
+
if (compareText(name, existing.name) < 0) existing.name = name;
|
|
27345
27305
|
if (!firstInAnswer) return;
|
|
27346
27306
|
existing.occurrences++;
|
|
27347
27307
|
existing.providers.add(answer.provider);
|
|
@@ -27356,8 +27316,8 @@ function recommendationRows(target, population) {
|
|
|
27356
27316
|
}
|
|
27357
27317
|
});
|
|
27358
27318
|
return [...grouped.values()].map((row) => {
|
|
27359
|
-
const providers = [...row.providers].sort(
|
|
27360
|
-
const questions = [...row.questions].sort(
|
|
27319
|
+
const providers = [...row.providers].sort(compareText);
|
|
27320
|
+
const questions = [...row.questions].sort(compareText);
|
|
27361
27321
|
return {
|
|
27362
27322
|
name: row.name,
|
|
27363
27323
|
occurrences: row.occurrences,
|
|
@@ -27368,7 +27328,7 @@ function recommendationRows(target, population) {
|
|
|
27368
27328
|
questionTotal: questions.length,
|
|
27369
27329
|
questionsTruncated: questions.length > 5
|
|
27370
27330
|
};
|
|
27371
|
-
}).sort((left, right) => right.occurrences - left.occurrences ||
|
|
27331
|
+
}).sort((left, right) => right.occurrences - left.occurrences || compareText(left.name, right.name));
|
|
27372
27332
|
}
|
|
27373
27333
|
function namedInsteadFields(names, limit = MEASUREMENT_PORTFOLIO_ROW_EVIDENCE_LIMIT) {
|
|
27374
27334
|
const returned = names.slice(0, limit);
|
|
@@ -27388,7 +27348,7 @@ function compareWeakest(left, right) {
|
|
|
27388
27348
|
const rightUnavailable = right.mentionCoverage.state === "unavailable" || right.citationCoverage.state === "unavailable";
|
|
27389
27349
|
if (leftUnavailable !== rightUnavailable) return leftUnavailable ? 1 : -1;
|
|
27390
27350
|
if (leftUnavailable || rightUnavailable) {
|
|
27391
|
-
return
|
|
27351
|
+
return compareText(left.label, right.label) || compareText(left.targetKey, right.targetKey);
|
|
27392
27352
|
}
|
|
27393
27353
|
if (left.mentionCoverage.state === "available" && right.mentionCoverage.state === "available" && left.mentionCoverage.value !== right.mentionCoverage.value) {
|
|
27394
27354
|
return left.mentionCoverage.value - right.mentionCoverage.value;
|
|
@@ -27396,7 +27356,7 @@ function compareWeakest(left, right) {
|
|
|
27396
27356
|
if (left.citationCoverage.state === "available" && right.citationCoverage.state === "available" && left.citationCoverage.value !== right.citationCoverage.value) {
|
|
27397
27357
|
return left.citationCoverage.value - right.citationCoverage.value;
|
|
27398
27358
|
}
|
|
27399
|
-
return
|
|
27359
|
+
return compareText(left.label, right.label) || compareText(left.targetKey, right.targetKey);
|
|
27400
27360
|
}
|
|
27401
27361
|
function requireGroup2(plan, groupKey) {
|
|
27402
27362
|
const group = plan.groups.find((candidate) => candidate.stableKey === groupKey);
|
|
@@ -27498,14 +27458,14 @@ function compareWeakestMarket(a, b) {
|
|
|
27498
27458
|
const aUnavailable = a.mentionCoverage.state === "unavailable" || a.citationCoverage.state === "unavailable";
|
|
27499
27459
|
const bUnavailable = b.mentionCoverage.state === "unavailable" || b.citationCoverage.state === "unavailable";
|
|
27500
27460
|
if (aUnavailable !== bUnavailable) return aUnavailable ? 1 : -1;
|
|
27501
|
-
if (aUnavailable || bUnavailable) return
|
|
27461
|
+
if (aUnavailable || bUnavailable) return compareText(a.label, b.label) || compareText(a.groupKey, b.groupKey);
|
|
27502
27462
|
if (a.mentionCoverage.state === "available" && b.mentionCoverage.state === "available" && a.mentionCoverage.value !== b.mentionCoverage.value) {
|
|
27503
27463
|
return a.mentionCoverage.value - b.mentionCoverage.value;
|
|
27504
27464
|
}
|
|
27505
27465
|
if (a.citationCoverage.state === "available" && b.citationCoverage.state === "available" && a.citationCoverage.value !== b.citationCoverage.value) {
|
|
27506
27466
|
return a.citationCoverage.value - b.citationCoverage.value;
|
|
27507
27467
|
}
|
|
27508
|
-
return
|
|
27468
|
+
return compareText(a.label, b.label) || compareText(a.groupKey, b.groupKey);
|
|
27509
27469
|
}
|
|
27510
27470
|
function mentionRanking(rows, limit) {
|
|
27511
27471
|
const eligible = [];
|
|
@@ -27526,7 +27486,7 @@ function mentionRanking(rows, limit) {
|
|
|
27526
27486
|
});
|
|
27527
27487
|
}
|
|
27528
27488
|
}
|
|
27529
|
-
const byLabel = (a, b) =>
|
|
27489
|
+
const byLabel = (a, b) => compareText(a.label, b.label) || compareText(a.targetKey, b.targetKey);
|
|
27530
27490
|
const byRate = (direction) => (a, b) => direction * (a.mentionCoverage.value - b.mentionCoverage.value) || byLabel(a, b);
|
|
27531
27491
|
return {
|
|
27532
27492
|
eligiblePropertyCount: eligible.length,
|
|
@@ -27676,7 +27636,7 @@ function compactPortfolioPage(response, query, versionId, evidence, cursor) {
|
|
|
27676
27636
|
function portfolioAnswerEvidence(plan, materialized, filters, targetKeys) {
|
|
27677
27637
|
const targets = new Set(targetKeys);
|
|
27678
27638
|
const executions = new Set(plan.assignments.filter((assignment) => targets.has(assignment.targetKey) && (filters.queryClass === "all" || assignment.queryClass === filters.queryClass)).map((assignment) => assignment.executionNodeKey));
|
|
27679
|
-
const slots = materialized.input.expectedSlots.filter((slot) => executions.has(slot.executionId) && (filters.provider === void 0 ||
|
|
27639
|
+
const slots = materialized.input.expectedSlots.filter((slot) => executions.has(slot.executionId) && (filters.provider === void 0 || normalizeIdentityText(slot.provider) === normalizeIdentityText(filters.provider)) && (filters.location === void 0 || normalizeMeasurementLocation(slot.location) === normalizeMeasurementLocation(filters.location)));
|
|
27680
27640
|
const mentionsBySlot = /* @__PURE__ */ new Map();
|
|
27681
27641
|
for (const answer of materialized.evidence.answers) pushTo(mentionsBySlot, answer.expectedSlotId, answer.mentioned);
|
|
27682
27642
|
const selected = [];
|
|
@@ -27818,7 +27778,7 @@ function portfolioResponse(db, active, plan, query, ownDomains) {
|
|
|
27818
27778
|
},
|
|
27819
27779
|
measurement,
|
|
27820
27780
|
queryClass: query.queryClass,
|
|
27821
|
-
engines: [...engines].sort(
|
|
27781
|
+
engines: [...engines].sort(compareText),
|
|
27822
27782
|
metrics: {
|
|
27823
27783
|
propertiesMentioned: countMetric2(overview.propertiesMentioned),
|
|
27824
27784
|
propertiesNeverMentioned: neverMentionedMetric(overview.propertiesMentioned, targetKeys.length),
|
|
@@ -27885,7 +27845,7 @@ function tieByMetro(rows) {
|
|
|
27885
27845
|
add(row.metro);
|
|
27886
27846
|
for (const other of row.otherMetros ?? []) add(other);
|
|
27887
27847
|
}
|
|
27888
|
-
return [...counts2.values()].sort((left, right) => right.count - left.count || (left.metro === null ? 1 : 0) - (right.metro === null ? 1 : 0) ||
|
|
27848
|
+
return [...counts2.values()].sort((left, right) => right.count - left.count || (left.metro === null ? 1 : 0) - (right.metro === null ? 1 : 0) || compareText(left.metro?.label ?? "", right.metro?.label ?? "") || compareText(left.metro?.groupKey ?? "", right.metro?.groupKey ?? "")).map(({ metro, count: count6 }) => ({ metro: metro?.label ?? null, count: count6 }));
|
|
27889
27849
|
}
|
|
27890
27850
|
function tieNamedInstead(rows) {
|
|
27891
27851
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -27893,14 +27853,14 @@ function tieNamedInstead(rows) {
|
|
|
27893
27853
|
forEachNameWrittenInstead(target, population.answers, (key, name, answer) => {
|
|
27894
27854
|
const existing = grouped.get(key);
|
|
27895
27855
|
if (existing) {
|
|
27896
|
-
if (
|
|
27856
|
+
if (compareText(name, existing.name) < 0) existing.name = name;
|
|
27897
27857
|
existing.answers.add(answer.snapshot.id);
|
|
27898
27858
|
} else {
|
|
27899
27859
|
grouped.set(key, { name, answers: /* @__PURE__ */ new Set([answer.snapshot.id]) });
|
|
27900
27860
|
}
|
|
27901
27861
|
});
|
|
27902
27862
|
}
|
|
27903
|
-
const counted = [...grouped.values()].map((row) => ({ name: row.name, answers: row.answers.size })).sort((left, right) => right.answers - left.answers ||
|
|
27863
|
+
const counted = [...grouped.values()].map((row) => ({ name: row.name, answers: row.answers.size })).sort((left, right) => right.answers - left.answers || compareText(left.name, right.name));
|
|
27904
27864
|
return { rows: counted.slice(0, MEASUREMENT_PORTFOLIO_TIE_NAMED_INSTEAD_LIMIT), total: counted.length };
|
|
27905
27865
|
}
|
|
27906
27866
|
function weakestAnswerSources(rows, ownDomains) {
|
|
@@ -28007,10 +27967,10 @@ function scopeKey(run) {
|
|
|
28007
27967
|
if (run.measurementScope === null) return "full";
|
|
28008
27968
|
const scope = run.measurementScope;
|
|
28009
27969
|
return JSON.stringify({
|
|
28010
|
-
groups: [...scope.groups].sort(
|
|
28011
|
-
targets: [...scope.targets].sort(
|
|
28012
|
-
queries: [...scope.queries].sort(
|
|
28013
|
-
resolvedTargets: [...scope.resolvedTargets].sort(
|
|
27970
|
+
groups: [...scope.groups].sort(compareText),
|
|
27971
|
+
targets: [...scope.targets].sort(compareText),
|
|
27972
|
+
queries: [...scope.queries].sort(compareText),
|
|
27973
|
+
resolvedTargets: [...scope.resolvedTargets].sort(compareText)
|
|
28014
27974
|
});
|
|
28015
27975
|
}
|
|
28016
27976
|
function previousComparableRun(db, active, current) {
|
|
@@ -28142,7 +28102,7 @@ function classifyPropertyMove(mention, citation) {
|
|
|
28142
28102
|
};
|
|
28143
28103
|
}
|
|
28144
28104
|
function compareChangeLabel(left, right) {
|
|
28145
|
-
return
|
|
28105
|
+
return compareText(left.row.label, right.row.label) || compareText(left.row.targetKey, right.row.targetKey);
|
|
28146
28106
|
}
|
|
28147
28107
|
function compareChangeMagnitude(left, right) {
|
|
28148
28108
|
const larger = (move) => Math.max(move.mentionSize, move.citationSize);
|
|
@@ -28320,11 +28280,11 @@ function qualityResponse(db, active, plan, query) {
|
|
|
28320
28280
|
const materialized = materializeWithDb(db, active, plan, run);
|
|
28321
28281
|
const expected = materialized.manifest.expectedSlots.length;
|
|
28322
28282
|
const snapshots = materialized.snapshots;
|
|
28323
|
-
const expectedSlotKeys = new Set(materialized.manifest.expectedSlots.map((slot) => `${slot.executionId}\0${
|
|
28283
|
+
const expectedSlotKeys = new Set(materialized.manifest.expectedSlots.map((slot) => `${slot.executionId}\0${normalizeIdentityText(slot.provider)}`));
|
|
28324
28284
|
const observedSlotKeys = /* @__PURE__ */ new Set();
|
|
28325
28285
|
for (const snapshot of snapshots) {
|
|
28326
28286
|
if (snapshot.measurementExecutionId === null) throw new Error("stored result has no frozen execution identity");
|
|
28327
|
-
const key = `${snapshot.measurementExecutionId}\0${
|
|
28287
|
+
const key = `${snapshot.measurementExecutionId}\0${normalizeIdentityText(snapshot.provider)}`;
|
|
28328
28288
|
if (!expectedSlotKeys.has(key) || observedSlotKeys.has(key)) {
|
|
28329
28289
|
throw new Error("stored results do not map one-to-one to the frozen manifest");
|
|
28330
28290
|
}
|
|
@@ -28515,16 +28475,10 @@ var VisibilityReportScopeError = class extends Error {
|
|
|
28515
28475
|
}
|
|
28516
28476
|
};
|
|
28517
28477
|
var ALL_CLASSES = ["branded", "non-brand", "unknown"];
|
|
28518
|
-
function compareText14(left, right) {
|
|
28519
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
28520
|
-
}
|
|
28521
|
-
function normalizeText3(value) {
|
|
28522
|
-
return value.normalize("NFKC").trim().replace(/\s+/g, " ").toLocaleLowerCase("en");
|
|
28523
|
-
}
|
|
28524
28478
|
function locationMatches(location, selected) {
|
|
28525
28479
|
if (selected.kind === "all") return true;
|
|
28526
28480
|
if (selected.kind === "none") return location === null;
|
|
28527
|
-
return location !== null &&
|
|
28481
|
+
return location !== null && normalizeIdentityText(location) === normalizeIdentityText(selected.value);
|
|
28528
28482
|
}
|
|
28529
28483
|
function selectedClasses(value) {
|
|
28530
28484
|
return value === "all" ? ALL_CLASSES : [value];
|
|
@@ -28580,15 +28534,15 @@ function scopeResolution(definition, selection) {
|
|
|
28580
28534
|
}
|
|
28581
28535
|
function scopeTargetKeys(definition, selection) {
|
|
28582
28536
|
const resolution = scopeResolution(definition, selection);
|
|
28583
|
-
if (resolution.market !== void 0) return
|
|
28584
|
-
if (selection.scope === "project") return definition.targets.map((target) => target.id).sort(
|
|
28537
|
+
if (resolution.market !== void 0) return sortedUnique(definition.edges.filter((edge) => resolution.edgeIds.has(edge.id)).map((edge) => edge.targetKey));
|
|
28538
|
+
if (selection.scope === "project") return definition.targets.map((target) => target.id).sort(compareText);
|
|
28585
28539
|
if (selection.scope === "property") return [resolution.option.id];
|
|
28586
28540
|
if (selection.scope === "group") {
|
|
28587
28541
|
const group = definition.groups.find((candidate) => candidate.id === resolution.option.id);
|
|
28588
28542
|
if (!group) throw new VisibilityReportScopeError(`Group "${resolution.option.id}" is not in this frozen definition.`, { reason: VisibilityReportScopeErrorReasons["retired-scope"], kind: "group", key: resolution.option.id });
|
|
28589
|
-
return [...group.targetKeys].sort(
|
|
28543
|
+
return [...group.targetKeys].sort(compareText);
|
|
28590
28544
|
}
|
|
28591
|
-
return
|
|
28545
|
+
return sortedUnique(definition.edges.filter((edge) => resolution.edgeIds.has(edge.id)).map((edge) => edge.targetKey));
|
|
28592
28546
|
}
|
|
28593
28547
|
function observationIndex(run) {
|
|
28594
28548
|
const index2 = /* @__PURE__ */ new Map();
|
|
@@ -28624,16 +28578,16 @@ function candidatesFor(run, selection, queryClass) {
|
|
|
28624
28578
|
bySlot.set(slot.id, list);
|
|
28625
28579
|
}
|
|
28626
28580
|
}
|
|
28627
|
-
const provider = selection.provider === void 0 ? void 0 :
|
|
28628
|
-
const model = selection.model === void 0 ? void 0 :
|
|
28581
|
+
const provider = selection.provider === void 0 ? void 0 : normalizeIdentityText(selection.provider);
|
|
28582
|
+
const model = selection.model === void 0 ? void 0 : normalizeIdentityText(selection.model);
|
|
28629
28583
|
const candidates = [];
|
|
28630
28584
|
for (const [slotId, edges] of bySlot) {
|
|
28631
28585
|
const slot = slotsById.get(slotId);
|
|
28632
28586
|
if (!slot) throw new Error(`Frozen slot ${slotId} disappeared during reconstruction`);
|
|
28633
28587
|
const observation = observations.get(slot.id) ?? null;
|
|
28634
|
-
if (provider !== void 0 &&
|
|
28588
|
+
if (provider !== void 0 && normalizeIdentityText(slot.provider) !== provider) continue;
|
|
28635
28589
|
if (!locationMatches(slot.location, selection.location)) continue;
|
|
28636
|
-
if (model !== void 0 && (observation === null || observation.model === null ||
|
|
28590
|
+
if (model !== void 0 && (observation === null || observation.model === null || normalizeIdentityText(observation.model) !== model)) continue;
|
|
28637
28591
|
candidates.push({ run, slot, edges, observation });
|
|
28638
28592
|
}
|
|
28639
28593
|
return { resolution, candidates };
|
|
@@ -28702,7 +28656,7 @@ function candidateKey(candidate) {
|
|
|
28702
28656
|
return `${candidate.run.id}\0${candidate.slot.id}`;
|
|
28703
28657
|
}
|
|
28704
28658
|
function selectedTargetKeys(candidates) {
|
|
28705
|
-
return
|
|
28659
|
+
return sortedUnique(candidates.flatMap((candidate) => candidate.edges.map((edge) => edge.targetKey)));
|
|
28706
28660
|
}
|
|
28707
28661
|
function targetMetrics(candidates, targets, targetKey) {
|
|
28708
28662
|
const own = candidates.filter((candidate) => candidate.edges.some((edge) => edge.targetKey === targetKey));
|
|
@@ -28773,7 +28727,7 @@ function summary2(candidates, definition, populationTargetKeys = selectedTargetK
|
|
|
28773
28727
|
}
|
|
28774
28728
|
function queryRows(candidates, definition, selection) {
|
|
28775
28729
|
const targets = targetMap(definition);
|
|
28776
|
-
const narrowed = candidates.filter((candidate) => (selection.queryKey === void 0 || candidate.slot.queryKey === selection.queryKey) && (selection.queryId === void 0 || candidate.slot.queryId === selection.queryId) && (selection.search === void 0 ||
|
|
28730
|
+
const narrowed = candidates.filter((candidate) => (selection.queryKey === void 0 || candidate.slot.queryKey === selection.queryKey) && (selection.queryId === void 0 || candidate.slot.queryId === selection.queryId) && (selection.search === void 0 || normalizeIdentityText(candidate.slot.query).includes(normalizeIdentityText(selection.search))));
|
|
28777
28731
|
const groups = /* @__PURE__ */ new Map();
|
|
28778
28732
|
for (const candidate of narrowed) {
|
|
28779
28733
|
const model = candidate.observation?.model ?? null;
|
|
@@ -28786,7 +28740,7 @@ function queryRows(candidates, definition, selection) {
|
|
|
28786
28740
|
const first = rows[0];
|
|
28787
28741
|
const value = coverageSummary(rows, definition, targets);
|
|
28788
28742
|
return {
|
|
28789
|
-
sourceSnapshotIds:
|
|
28743
|
+
sourceSnapshotIds: sortedUnique(rows.flatMap((row) => row.observation ? [row.observation.answerId] : [])),
|
|
28790
28744
|
queryKey: first.slot.queryKey,
|
|
28791
28745
|
queryId: first.slot.queryId,
|
|
28792
28746
|
query: first.slot.query,
|
|
@@ -28795,14 +28749,14 @@ function queryRows(candidates, definition, selection) {
|
|
|
28795
28749
|
location: first.slot.location,
|
|
28796
28750
|
targetKeys: selectedTargetKeys(rows),
|
|
28797
28751
|
...(() => {
|
|
28798
|
-
const marketKeys =
|
|
28752
|
+
const marketKeys = sortedUnique(rows.flatMap((row) => row.edges.flatMap((edge) => edge.marketKeys)));
|
|
28799
28753
|
return marketKeys.length === 0 ? {} : { marketKeys };
|
|
28800
28754
|
})(),
|
|
28801
28755
|
answerCount: value.answerCount,
|
|
28802
28756
|
mentionCoverage: value.mentionCoverage,
|
|
28803
28757
|
citationCoverage: value.citationCoverage
|
|
28804
28758
|
};
|
|
28805
|
-
}).sort((left, right) => (selection.scope === "property" && selection.marketKey === void 0 ?
|
|
28759
|
+
}).sort((left, right) => (selection.scope === "property" && selection.marketKey === void 0 ? compareText((left.marketKeys ?? []).join("\0"), (right.marketKeys ?? []).join("\0")) : 0) || compareText(normalizeIdentityText(left.query), normalizeIdentityText(right.query)) || compareText(left.provider, right.provider) || compareText(left.model ?? "", right.model ?? "") || compareText(left.location ?? "", right.location ?? "") || compareText(left.queryKey, right.queryKey));
|
|
28806
28760
|
}
|
|
28807
28761
|
function evidenceRows(candidates, definition, selection) {
|
|
28808
28762
|
if (selection.queryKey === void 0 && selection.queryId === void 0) return [];
|
|
@@ -28830,10 +28784,10 @@ function evidenceRows(candidates, definition, selection) {
|
|
|
28830
28784
|
sources: [...observation.sources],
|
|
28831
28785
|
observedCompetitors: [...observation.observedCompetitorNames]
|
|
28832
28786
|
};
|
|
28833
|
-
}).sort((left, right) =>
|
|
28787
|
+
}).sort((left, right) => compareText(left.createdAt, right.createdAt) || compareText(left.answerId, right.answerId));
|
|
28834
28788
|
}
|
|
28835
28789
|
function competitorRows(candidates) {
|
|
28836
|
-
const domains =
|
|
28790
|
+
const domains = sortedUnique(candidates.flatMap((candidate) => candidate.edges.flatMap((edge) => edge.competitorDomains)));
|
|
28837
28791
|
return domains.map((domain) => {
|
|
28838
28792
|
const eligible = candidates.map((candidate) => {
|
|
28839
28793
|
const edges = candidate.edges.filter((edge) => edge.competitorDomains.includes(domain));
|
|
@@ -28864,7 +28818,7 @@ function observedCompetitorRows(candidates) {
|
|
|
28864
28818
|
answersByName.set(name, answers);
|
|
28865
28819
|
}
|
|
28866
28820
|
}
|
|
28867
|
-
return [...answersByName].map(([name, answers]) => ({ name, answerCount: answers.size })).sort((left, right) =>
|
|
28821
|
+
return [...answersByName].map(([name, answers]) => ({ name, answerCount: answers.size })).sort((left, right) => compareText(left.name, right.name));
|
|
28868
28822
|
}
|
|
28869
28823
|
function narrowedToTargetKeys(candidate, targetKeys) {
|
|
28870
28824
|
const edges = candidate.edges.filter((edge) => targetKeys.has(edge.targetKey));
|
|
@@ -28883,7 +28837,7 @@ function breakdown(candidates, definition, populationTargetKeys = selectedTarget
|
|
|
28883
28837
|
queryCount: new Set(own.map((candidate) => candidate.slot.queryKey)).size,
|
|
28884
28838
|
...metrics
|
|
28885
28839
|
};
|
|
28886
|
-
}).sort((left, right) =>
|
|
28840
|
+
}).sort((left, right) => compareText(left.label, right.label) || compareText(left.id, right.id));
|
|
28887
28841
|
const populationTargetSet = new Set(populationTargetKeys);
|
|
28888
28842
|
const groups = definition.groups.map((group) => ({ group, targetKeys: group.targetKeys.filter((targetKey) => populationTargetSet.has(targetKey)) })).filter(({ targetKeys }) => targetKeys.length > 0).map(({ group, targetKeys }) => {
|
|
28889
28843
|
const groupTargetKeys = new Set(targetKeys);
|
|
@@ -28897,7 +28851,7 @@ function breakdown(candidates, definition, populationTargetKeys = selectedTarget
|
|
|
28897
28851
|
mentionCoverage: metrics,
|
|
28898
28852
|
citationCoverage: citations
|
|
28899
28853
|
};
|
|
28900
|
-
}).sort((left, right) =>
|
|
28854
|
+
}).sort((left, right) => compareText(left.label, right.label) || compareText(left.id, right.id));
|
|
28901
28855
|
return { properties, groups };
|
|
28902
28856
|
}
|
|
28903
28857
|
function fingerprint(value) {
|
|
@@ -28974,16 +28928,16 @@ function filterOptions(definition, selection, run) {
|
|
|
28974
28928
|
const resolution = scopeResolution(definition, selection);
|
|
28975
28929
|
const slots = definition.slots.filter((slot) => definition.edges.some((edge) => edge.executionId === slot.executionId && resolution.edgeIds.has(edge.id)));
|
|
28976
28930
|
const observations = run ? observationIndex(run) : /* @__PURE__ */ new Map();
|
|
28977
|
-
const providers =
|
|
28931
|
+
const providers = sortedUnique(slots.map((slot) => slot.provider));
|
|
28978
28932
|
const models = [...new Map(slots.flatMap((slot) => {
|
|
28979
28933
|
const observation = observations.get(slot.id);
|
|
28980
28934
|
return observation?.model === null || observation === void 0 ? [] : [[`${slot.provider}\0${observation.model}`, { provider: slot.provider, model: observation.model }]];
|
|
28981
|
-
})).values()].sort((left, right) =>
|
|
28935
|
+
})).values()].sort((left, right) => compareText(left.provider, right.provider) || compareText(left.model, right.model));
|
|
28982
28936
|
const locations = [{ kind: "all" }];
|
|
28983
28937
|
const seen2 = /* @__PURE__ */ new Set(["all"]);
|
|
28984
28938
|
for (const slot of slots) {
|
|
28985
28939
|
const value = slot.location === null ? { kind: "none" } : { kind: "exact", value: slot.location };
|
|
28986
|
-
const key = value.kind === "exact" ? `exact:${
|
|
28940
|
+
const key = value.kind === "exact" ? `exact:${normalizeIdentityText(value.value)}` : value.kind;
|
|
28987
28941
|
if (!seen2.has(key)) {
|
|
28988
28942
|
seen2.add(key);
|
|
28989
28943
|
locations.push(value);
|
|
@@ -29074,7 +29028,7 @@ function emptyPopulation(queryClass, definition, input) {
|
|
|
29074
29028
|
};
|
|
29075
29029
|
}
|
|
29076
29030
|
function buildVisibilityReport(input) {
|
|
29077
|
-
const runs2 = input.runs.filter((run) => !run.probe && inTime(run, input.selection)).sort((left, right) =>
|
|
29031
|
+
const runs2 = input.runs.filter((run) => !run.probe && inTime(run, input.selection)).sort((left, right) => compareText(left.createdAt, right.createdAt) || compareText(left.id, right.id));
|
|
29078
29032
|
const selectedRun = input.selection.runId === void 0 ? input.preferredRunId === void 0 ? runs2.at(-1) : runs2.find((run) => run.id === input.preferredRunId) : runs2.find((run) => run.id === input.selection.runId);
|
|
29079
29033
|
const definition = selectedRun?.definition ?? input.activeDefinition;
|
|
29080
29034
|
const selectedResolution = scopeResolution(definition, input.selection);
|
|
@@ -29208,9 +29162,6 @@ function visibilityComparisonPopulation(run, selection) {
|
|
|
29208
29162
|
}
|
|
29209
29163
|
|
|
29210
29164
|
// ../api-routes/src/visibility-report.ts
|
|
29211
|
-
function normalizedText3(value) {
|
|
29212
|
-
return value.normalize("NFKC").trim().replace(/\s+/g, " ").toLocaleLowerCase("en");
|
|
29213
|
-
}
|
|
29214
29165
|
function observedCompetitorNames2(values) {
|
|
29215
29166
|
const names = [];
|
|
29216
29167
|
const seen2 = /* @__PURE__ */ new Set();
|
|
@@ -29294,7 +29245,7 @@ function competitorSignals(snapshot, matchers, preparedAnswerText = preparedAnsw
|
|
|
29294
29245
|
try {
|
|
29295
29246
|
return normalizeMeasurementHost(value);
|
|
29296
29247
|
} catch {
|
|
29297
|
-
return
|
|
29248
|
+
return normalizeIdentityText(value);
|
|
29298
29249
|
}
|
|
29299
29250
|
}));
|
|
29300
29251
|
const cited = [...matchers.keys()].filter((domain) => citedDomains.has(normalizeMeasurementHost(domain)));
|
|
@@ -29462,7 +29413,7 @@ function stableSimpleDefinitionId(definition) {
|
|
|
29462
29413
|
return `simple:${createHash8("sha256").update(JSON.stringify(source)).digest("hex")}`;
|
|
29463
29414
|
}
|
|
29464
29415
|
function simpleModelFingerprint(definition) {
|
|
29465
|
-
const engines = [...definition.engines].map((engine) => ({ provider:
|
|
29416
|
+
const engines = [...definition.engines].map((engine) => ({ provider: normalizeIdentityText(engine.provider), requestedModel: engine.requestedModel })).sort((left, right) => left.provider.localeCompare(right.provider, "en"));
|
|
29466
29417
|
return `simple-model:${createHash8("sha256").update(JSON.stringify(engines)).digest("hex")}`;
|
|
29467
29418
|
}
|
|
29468
29419
|
function frozenSimpleRun(run, definition, snapshots, includeEvidence = true) {
|
|
@@ -29479,21 +29430,21 @@ function frozenSimpleRun(run, definition, snapshots, includeEvidence = true) {
|
|
|
29479
29430
|
matches.push(query);
|
|
29480
29431
|
queriesByText.set(query.queryText, matches);
|
|
29481
29432
|
}
|
|
29482
|
-
const enginesByProvider = new Map(definition.engines.map((engine) => [
|
|
29433
|
+
const enginesByProvider = new Map(definition.engines.map((engine) => [normalizeIdentityText(engine.provider), engine]));
|
|
29483
29434
|
const competitors2 = definition.competitors === void 0 ? null : frozenCompetitorMatchers(definition.competitors);
|
|
29484
29435
|
const slots = definition.queries.flatMap((query) => definition.engines.map((engine) => ({
|
|
29485
|
-
id: `slot:simple:${query.queryId}:${
|
|
29436
|
+
id: `slot:simple:${query.queryId}:${normalizeIdentityText(engine.provider)}`,
|
|
29486
29437
|
executionId: `simple:${query.queryId}`,
|
|
29487
29438
|
queryKey: query.queryId,
|
|
29488
29439
|
queryId: query.queryId,
|
|
29489
29440
|
query: query.queryText,
|
|
29490
|
-
provider:
|
|
29441
|
+
provider: normalizeIdentityText(engine.provider),
|
|
29491
29442
|
location: definition.location?.label ?? null
|
|
29492
29443
|
})));
|
|
29493
29444
|
const slotKeys = new Set(slots.map((slot) => slot.id));
|
|
29494
29445
|
const observations = [];
|
|
29495
29446
|
for (const snapshot of snapshots) {
|
|
29496
|
-
const provider =
|
|
29447
|
+
const provider = normalizeIdentityText(snapshot.provider);
|
|
29497
29448
|
const engine = enginesByProvider.get(provider);
|
|
29498
29449
|
if (!engine) throw new Error(`Frozen simple definition has no provider ${snapshot.provider} for stored snapshot ${snapshot.id}`);
|
|
29499
29450
|
const query = snapshot.queryId === null ? (() => {
|
|
@@ -29568,7 +29519,7 @@ function legacySimpleRun(project, run, snapshots, includeEvidence = true) {
|
|
|
29568
29519
|
queryKey: snapshot.queryId ?? `legacy:${snapshot.queryText ?? snapshot.id}`,
|
|
29569
29520
|
queryId: snapshot.queryId,
|
|
29570
29521
|
query: snapshot.queryText ?? "",
|
|
29571
|
-
provider:
|
|
29522
|
+
provider: normalizeIdentityText(snapshot.provider),
|
|
29572
29523
|
location: snapshot.location
|
|
29573
29524
|
}));
|
|
29574
29525
|
return {
|
|
@@ -29937,17 +29888,11 @@ import { and as and29, desc as desc16, eq as eq37, inArray as inArray18, isNotNu
|
|
|
29937
29888
|
var SOURCE_LIMIT = 100;
|
|
29938
29889
|
var MAX_REVIEW_AGE_MS = 15 * 60 * 1e3;
|
|
29939
29890
|
var MAX_REVIEW_FUTURE_SKEW_MS = 60 * 1e3;
|
|
29940
|
-
function compareText15(left, right) {
|
|
29941
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
29942
|
-
}
|
|
29943
|
-
function normalizeText4(value) {
|
|
29944
|
-
return value.normalize("NFKC").trim().replace(/\s+/g, " ").toLocaleLowerCase("en");
|
|
29945
|
-
}
|
|
29946
29891
|
function unique2(values) {
|
|
29947
29892
|
return [...new Set(values)];
|
|
29948
29893
|
}
|
|
29949
|
-
function stableNewQueryId(projectId,
|
|
29950
|
-
return `qtc_${sha256Hex(`${projectId}\0${
|
|
29894
|
+
function stableNewQueryId(projectId, normalizedText) {
|
|
29895
|
+
return `qtc_${sha256Hex(`${projectId}\0${normalizedText}`).slice(0, 32)}`;
|
|
29951
29896
|
}
|
|
29952
29897
|
function providerNames(project, opts) {
|
|
29953
29898
|
return resolveRunProviderSelection({
|
|
@@ -29981,7 +29926,7 @@ function simpleDispatchEngines(project, opts) {
|
|
|
29981
29926
|
// override/configuration, just as it is in JobRunner, which also resolves
|
|
29982
29927
|
// a retired override to the id that answers now.
|
|
29983
29928
|
requestedModel: resolvedOverride(provider, overrides[provider]) ?? summaries.get(provider)?.model ?? null
|
|
29984
|
-
})).sort((left, right) =>
|
|
29929
|
+
})).sort((left, right) => compareText(left.provider, right.provider));
|
|
29985
29930
|
}
|
|
29986
29931
|
function resolvedOverride(provider, model) {
|
|
29987
29932
|
return model === void 0 ? void 0 : resolveProviderModel(provider, model);
|
|
@@ -29992,12 +29937,12 @@ function simpleDefaultLocation(project) {
|
|
|
29992
29937
|
}
|
|
29993
29938
|
function contextKey(context) {
|
|
29994
29939
|
const location = context.location ? [context.location.label, context.location.city, context.location.region, context.location.country, context.location.timezone ?? ""].join("\0") : "";
|
|
29995
|
-
return canonicalJson({ providers: [...context.providers].sort(
|
|
29940
|
+
return canonicalJson({ providers: [...context.providers].sort(compareText), models: context.models, location });
|
|
29996
29941
|
}
|
|
29997
29942
|
function uniqueContexts(contexts) {
|
|
29998
29943
|
const byKey = /* @__PURE__ */ new Map();
|
|
29999
29944
|
for (const context of contexts) byKey.set(contextKey(context), context);
|
|
30000
|
-
return [...byKey.values()].sort((left, right) =>
|
|
29945
|
+
return [...byKey.values()].sort((left, right) => compareText(contextKey(left), contextKey(right)));
|
|
30001
29946
|
}
|
|
30002
29947
|
function defaultContexts(project, opts, mode) {
|
|
30003
29948
|
const providers = providerNames(project, opts);
|
|
@@ -30028,7 +29973,7 @@ function workspaceFingerprint(project, active, queryRows2) {
|
|
|
30028
29973
|
defaultLocation: project.defaultLocation
|
|
30029
29974
|
},
|
|
30030
29975
|
active: active ? { id: active.version.id, revision: active.version.revision, compiledChecksum: active.version.compiledChecksum } : null,
|
|
30031
|
-
queries: queryRows2.map((row) => ({ id: row.id, query: row.query, provenance: row.provenance })).sort((left, right) =>
|
|
29976
|
+
queries: queryRows2.map((row) => ({ id: row.id, query: row.query, provenance: row.provenance })).sort((left, right) => compareText(left.id, right.id))
|
|
30032
29977
|
}))}`;
|
|
30033
29978
|
}
|
|
30034
29979
|
function readWorkspace(db, project) {
|
|
@@ -30065,9 +30010,9 @@ function simpleExecutionSignature(input) {
|
|
|
30065
30010
|
return canonicalJson({
|
|
30066
30011
|
identity: {
|
|
30067
30012
|
displayName: input.identity.displayName,
|
|
30068
|
-
aliases: [...input.identity.aliases].sort(
|
|
30013
|
+
aliases: [...input.identity.aliases].sort(compareText),
|
|
30069
30014
|
canonicalDomain: input.identity.canonicalDomain,
|
|
30070
|
-
ownedDomains: [...input.identity.ownedDomains].sort(
|
|
30015
|
+
ownedDomains: [...input.identity.ownedDomains].sort(compareText)
|
|
30071
30016
|
},
|
|
30072
30017
|
country: input.country,
|
|
30073
30018
|
language: input.language,
|
|
@@ -30075,7 +30020,7 @@ function simpleExecutionSignature(input) {
|
|
|
30075
30020
|
engines: input.engines.map((engine) => ({
|
|
30076
30021
|
provider: engine.provider.trim().toLocaleLowerCase("en"),
|
|
30077
30022
|
requestedModel: engine.requestedModel
|
|
30078
|
-
})).sort((left, right) =>
|
|
30023
|
+
})).sort((left, right) => compareText(left.provider, right.provider))
|
|
30079
30024
|
});
|
|
30080
30025
|
}
|
|
30081
30026
|
function currentSimpleExecutionSignature(project, opts) {
|
|
@@ -30338,7 +30283,7 @@ function measuredAtByQuery(db, state, opts) {
|
|
|
30338
30283
|
for (const [queryId, signatures] of activeSignatures) {
|
|
30339
30284
|
const measured = measurements.get(queryId);
|
|
30340
30285
|
if (!measured || [...signatures].some((signature) => !measured.has(signature))) continue;
|
|
30341
|
-
const latest = [...measured.values()].sort(
|
|
30286
|
+
const latest = [...measured.values()].sort(compareText).at(-1);
|
|
30342
30287
|
if (latest) result.set(queryId, latest);
|
|
30343
30288
|
}
|
|
30344
30289
|
return result;
|
|
@@ -30353,7 +30298,7 @@ function marketKeysByEdge(plan) {
|
|
|
30353
30298
|
else result.set(key, [scope.stableKey]);
|
|
30354
30299
|
}
|
|
30355
30300
|
}
|
|
30356
|
-
for (const keys of result.values()) keys.sort(
|
|
30301
|
+
for (const keys of result.values()) keys.sort(compareText);
|
|
30357
30302
|
return result;
|
|
30358
30303
|
}
|
|
30359
30304
|
function trackedRows(db, state, queryRows2, plan, opts) {
|
|
@@ -30362,12 +30307,12 @@ function trackedRows(db, state, queryRows2, plan, opts) {
|
|
|
30362
30307
|
return [...queryRows2.values()].map((row) => ({
|
|
30363
30308
|
queryId: row.id,
|
|
30364
30309
|
queryText: row.query,
|
|
30365
|
-
normalizedText:
|
|
30310
|
+
normalizedText: normalizeIdentityText(row.query),
|
|
30366
30311
|
provenance: parseQueryProvenance(row.provenance),
|
|
30367
30312
|
state: measured.has(row.id) ? "tracked" : "awaiting-sweep",
|
|
30368
30313
|
lastMeasuredAt: measured.get(row.id) ?? null,
|
|
30369
30314
|
assignments: []
|
|
30370
|
-
})).sort((left, right) =>
|
|
30315
|
+
})).sort((left, right) => compareText(left.normalizedText, right.normalizedText) || compareText(left.queryId, right.queryId));
|
|
30371
30316
|
}
|
|
30372
30317
|
const snapshots = new Map(plan.querySnapshots.map((snapshot) => [snapshot.queryId, snapshot]));
|
|
30373
30318
|
const allIds = unique2([...queryRows2.keys(), ...snapshots.keys()]);
|
|
@@ -30405,7 +30350,7 @@ function trackedRows(db, state, queryRows2, plan, opts) {
|
|
|
30405
30350
|
} else {
|
|
30406
30351
|
assignments.set(assignment.targetKey, {
|
|
30407
30352
|
targetKey: assignment.targetKey,
|
|
30408
|
-
groupKeys: [...groupsByTarget.get(assignment.targetKey) ?? []].sort(
|
|
30353
|
+
groupKeys: [...groupsByTarget.get(assignment.targetKey) ?? []].sort(compareText),
|
|
30409
30354
|
marketKeys: [...marketsByEdge.get(edgeKey2) ?? []],
|
|
30410
30355
|
queryClass: assignment.queryClass,
|
|
30411
30356
|
classificationSource: source,
|
|
@@ -30416,17 +30361,17 @@ function trackedRows(db, state, queryRows2, plan, opts) {
|
|
|
30416
30361
|
return {
|
|
30417
30362
|
queryId,
|
|
30418
30363
|
queryText,
|
|
30419
|
-
normalizedText:
|
|
30364
|
+
normalizedText: normalizeIdentityText(queryText),
|
|
30420
30365
|
provenance: snapshot ? provenanceFromPlan(snapshot.provenance) : parseQueryProvenance(row?.provenance ?? null),
|
|
30421
30366
|
state: measured.has(queryId) ? "tracked" : "awaiting-sweep",
|
|
30422
30367
|
lastMeasuredAt: measured.get(queryId) ?? null,
|
|
30423
30368
|
assignments: [...assignments.values()].map((assignment) => ({
|
|
30424
30369
|
...assignment,
|
|
30425
|
-
marketKeys: unique2(assignment.marketKeys).sort(
|
|
30370
|
+
marketKeys: unique2(assignment.marketKeys).sort(compareText),
|
|
30426
30371
|
contexts: uniqueContexts(assignment.contexts)
|
|
30427
|
-
})).sort((left, right) =>
|
|
30372
|
+
})).sort((left, right) => compareText(left.targetKey, right.targetKey))
|
|
30428
30373
|
};
|
|
30429
|
-
}).sort((left, right) =>
|
|
30374
|
+
}).sort((left, right) => compareText(left.normalizedText, right.normalizedText) || compareText(left.queryId, right.queryId));
|
|
30430
30375
|
}
|
|
30431
30376
|
function savedSources(db, projectId) {
|
|
30432
30377
|
const research = db.select({
|
|
@@ -30509,9 +30454,9 @@ function resolveAudience(plan, input) {
|
|
|
30509
30454
|
throw validationError("Select at least one Property, group, or market for a portfolio query.");
|
|
30510
30455
|
}
|
|
30511
30456
|
return {
|
|
30512
|
-
targetKeys: [...targetKeys].sort(
|
|
30513
|
-
groupKeys: groupKeys.sort(
|
|
30514
|
-
marketKeys: marketKeys.sort(
|
|
30457
|
+
targetKeys: [...targetKeys].sort(compareText),
|
|
30458
|
+
groupKeys: groupKeys.sort(compareText),
|
|
30459
|
+
marketKeys: marketKeys.sort(compareText)
|
|
30515
30460
|
};
|
|
30516
30461
|
}
|
|
30517
30462
|
function contextsFromInput(project, contexts) {
|
|
@@ -30522,7 +30467,7 @@ function contextsFromInput(project, contexts) {
|
|
|
30522
30467
|
if (context.location !== null && !location) {
|
|
30523
30468
|
throw validationError(`Execution context names a location the project does not configure: ${context.location}`);
|
|
30524
30469
|
}
|
|
30525
|
-
const providers = unique2(context.providers.map((provider) => provider.trim().toLocaleLowerCase("en")).filter(Boolean)).sort(
|
|
30470
|
+
const providers = unique2(context.providers.map((provider) => provider.trim().toLocaleLowerCase("en")).filter(Boolean)).sort(compareText);
|
|
30526
30471
|
for (const provider of Object.keys(context.models)) {
|
|
30527
30472
|
if (!providers.includes(provider.trim().toLocaleLowerCase("en"))) {
|
|
30528
30473
|
throw validationError(`Model "${provider}" is not selected by this execution context.`);
|
|
@@ -30747,23 +30692,23 @@ function edgeKey(edge) {
|
|
|
30747
30692
|
function queryIdsByNormalized(state) {
|
|
30748
30693
|
const result = /* @__PURE__ */ new Map();
|
|
30749
30694
|
for (const row of state.queryRows) {
|
|
30750
|
-
const key =
|
|
30695
|
+
const key = normalizeIdentityText(row.query);
|
|
30751
30696
|
const ids = result.get(key);
|
|
30752
30697
|
if (ids) ids.push(row.id);
|
|
30753
30698
|
else result.set(key, [row.id]);
|
|
30754
30699
|
}
|
|
30755
30700
|
for (const snapshot of state.active?.plan.querySnapshots ?? []) {
|
|
30756
|
-
const key =
|
|
30701
|
+
const key = normalizeIdentityText(snapshot.queryText);
|
|
30757
30702
|
const ids = result.get(key);
|
|
30758
30703
|
if (ids) ids.push(snapshot.queryId);
|
|
30759
30704
|
else result.set(key, [snapshot.queryId]);
|
|
30760
30705
|
}
|
|
30761
|
-
for (const ids of result.values()) ids.sort(
|
|
30706
|
+
for (const ids of result.values()) ids.sort(compareText);
|
|
30762
30707
|
return result;
|
|
30763
30708
|
}
|
|
30764
|
-
function preferredQueryId(state,
|
|
30765
|
-
const candidates = queryIdsByNormalized(state).get(
|
|
30766
|
-
const bound = new Set((state.active?.plan.querySnapshots ?? []).filter((snapshot) =>
|
|
30709
|
+
function preferredQueryId(state, normalizedText) {
|
|
30710
|
+
const candidates = queryIdsByNormalized(state).get(normalizedText) ?? [];
|
|
30711
|
+
const bound = new Set((state.active?.plan.querySnapshots ?? []).filter((snapshot) => normalizeIdentityText(snapshot.queryText) === normalizedText).map((snapshot) => snapshot.queryId));
|
|
30767
30712
|
return candidates.find((candidate) => bound.has(candidate)) ?? candidates.at(0) ?? null;
|
|
30768
30713
|
}
|
|
30769
30714
|
function rowsEqual(before, after) {
|
|
@@ -30785,7 +30730,7 @@ function changeRows(ids, rows, plan, fallbackPlan = null) {
|
|
|
30785
30730
|
queryText,
|
|
30786
30731
|
assignmentCount: (plan ?? fallbackPlan)?.assignments.filter((assignment) => assignment.queryId === queryId).length ?? 0
|
|
30787
30732
|
}];
|
|
30788
|
-
}).sort((left, right) =>
|
|
30733
|
+
}).sort((left, right) => compareText(left.queryText, right.queryText) || compareText(left.queryId, right.queryId));
|
|
30789
30734
|
}
|
|
30790
30735
|
function planSnapshot(plan, queryId) {
|
|
30791
30736
|
return plan?.querySnapshots.find((snapshot) => snapshot.queryId === queryId) ?? null;
|
|
@@ -30793,26 +30738,26 @@ function planSnapshot(plan, queryId) {
|
|
|
30793
30738
|
function displayIds(rows, plan) {
|
|
30794
30739
|
return /* @__PURE__ */ new Set([...rows.keys(), ...plan?.querySnapshots.map((snapshot) => snapshot.queryId) ?? []]);
|
|
30795
30740
|
}
|
|
30796
|
-
function candidateIdsForText(state, plan, rows,
|
|
30741
|
+
function candidateIdsForText(state, plan, rows, normalizedText) {
|
|
30797
30742
|
const ids = /* @__PURE__ */ new Set();
|
|
30798
30743
|
for (const row of rows.values()) {
|
|
30799
|
-
if (
|
|
30744
|
+
if (normalizeIdentityText(row.query) === normalizedText) ids.add(row.id);
|
|
30800
30745
|
}
|
|
30801
30746
|
for (const snapshot of plan?.querySnapshots ?? []) {
|
|
30802
|
-
if (
|
|
30747
|
+
if (normalizeIdentityText(snapshot.queryText) === normalizedText) ids.add(snapshot.queryId);
|
|
30803
30748
|
}
|
|
30804
|
-
const activeId = preferredQueryId(state,
|
|
30749
|
+
const activeId = preferredQueryId(state, normalizedText);
|
|
30805
30750
|
return [...ids].sort((left, right) => {
|
|
30806
30751
|
if (left === activeId) return -1;
|
|
30807
30752
|
if (right === activeId) return 1;
|
|
30808
|
-
return
|
|
30753
|
+
return compareText(left, right);
|
|
30809
30754
|
});
|
|
30810
30755
|
}
|
|
30811
30756
|
function resolveMutationQueryId(state, plan, rows, removal) {
|
|
30812
30757
|
if (removal.queryId !== void 0) {
|
|
30813
30758
|
return rows.has(removal.queryId) || planSnapshot(plan, removal.queryId) !== null ? removal.queryId : null;
|
|
30814
30759
|
}
|
|
30815
|
-
const ids = candidateIdsForText(state, plan, rows,
|
|
30760
|
+
const ids = candidateIdsForText(state, plan, rows, normalizeIdentityText(removal.queryText));
|
|
30816
30761
|
return ids[0] ?? null;
|
|
30817
30762
|
}
|
|
30818
30763
|
function queryTextForId(state, plan, rows, queryId, fallback) {
|
|
@@ -30909,7 +30854,7 @@ function simpleWorkload(project, rows, opts) {
|
|
|
30909
30854
|
const result = /* @__PURE__ */ new Map();
|
|
30910
30855
|
for (const context of defaultContexts(project, opts, "simple")) {
|
|
30911
30856
|
for (const row of rows.values()) {
|
|
30912
|
-
result.set(`${
|
|
30857
|
+
result.set(`${normalizeIdentityText(row.query)}\0${contextKey(context)}`, context.providers.length);
|
|
30913
30858
|
}
|
|
30914
30859
|
}
|
|
30915
30860
|
return result;
|
|
@@ -30979,7 +30924,7 @@ function mutationPreviewToken(candidate, mutation, reviewedAt) {
|
|
|
30979
30924
|
mutation: { additions: mutation.additions, removals: mutation.removals, ...mutation.edits ? { edits: mutation.edits } : {} },
|
|
30980
30925
|
reviewedAt,
|
|
30981
30926
|
plan: document,
|
|
30982
|
-
queries: [...candidate.queryRows.values()].map((row) => ({ id: row.id, query: row.query, provenance: row.provenance })).sort((left, right) =>
|
|
30927
|
+
queries: [...candidate.queryRows.values()].map((row) => ({ id: row.id, query: row.query, provenance: row.provenance })).sort((left, right) => compareText(left.id, right.id))
|
|
30983
30928
|
}))}`;
|
|
30984
30929
|
}
|
|
30985
30930
|
function assertReviewedAt(reviewedAt) {
|
|
@@ -30996,7 +30941,7 @@ function assertSimpleAddition(addition) {
|
|
|
30996
30941
|
}
|
|
30997
30942
|
}
|
|
30998
30943
|
function addSimpleSource(state, rows, source, reusedQueryIds) {
|
|
30999
|
-
const normalized =
|
|
30944
|
+
const normalized = normalizeIdentityText(source.queryText);
|
|
31000
30945
|
const existingId = candidateIdsForText(state, null, rows, normalized).at(0) ?? preferredQueryId(state, normalized) ?? stableNewQueryId(state.project.id, normalized);
|
|
31001
30946
|
const existing = rows.get(existingId);
|
|
31002
30947
|
if (existing) {
|
|
@@ -31026,7 +30971,7 @@ function buildSimpleCandidate(db, state, mutation, opts, manualCapturedAt) {
|
|
|
31026
30971
|
}
|
|
31027
30972
|
const existing = rows.get(edit.queryId);
|
|
31028
30973
|
if (!existing) throw notFound("Tracked query", edit.queryId);
|
|
31029
|
-
if (edit.text === void 0 ||
|
|
30974
|
+
if (edit.text === void 0 || normalizeIdentityText(edit.text) === normalizeIdentityText(existing.query)) continue;
|
|
31030
30975
|
rows.delete(edit.queryId);
|
|
31031
30976
|
mutatedQueryIds.add(edit.queryId);
|
|
31032
30977
|
addSimpleSource(state, rows, {
|
|
@@ -31070,7 +31015,7 @@ function buildSimpleCandidate(db, state, mutation, opts, manualCapturedAt) {
|
|
|
31070
31015
|
};
|
|
31071
31016
|
}
|
|
31072
31017
|
function addAdvancedSource(state, plan, rows, addition, source, reusedQueryIds, mutatedQueryIds) {
|
|
31073
|
-
const normalized =
|
|
31018
|
+
const normalized = normalizeIdentityText(source.queryText);
|
|
31074
31019
|
const queryId = candidateIdsForText(state, plan, rows, normalized).at(0) ?? preferredQueryId(state, normalized) ?? stableNewQueryId(state.project.id, normalized);
|
|
31075
31020
|
const knownBefore = state.queryRows.some((row) => row.id === queryId) || planSnapshot(state.active?.plan ?? null, queryId) !== null;
|
|
31076
31021
|
const queryText = queryTextForId(state, plan, rows, queryId, source.queryText.trim());
|
|
@@ -31146,7 +31091,7 @@ function applyAdvancedEdit(state, plan, rows, edit, capturedAt, reusedQueryIds,
|
|
|
31146
31091
|
if (!snapshot && !row) throw notFound("Tracked query", edit.queryId);
|
|
31147
31092
|
const oldText = snapshot?.queryText ?? row.query;
|
|
31148
31093
|
const text2 = edit.text ?? oldText;
|
|
31149
|
-
const textChanged =
|
|
31094
|
+
const textChanged = normalizeIdentityText(text2) !== normalizeIdentityText(oldText);
|
|
31150
31095
|
const audience = edit.audience === void 0 ? null : resolveAudience(requireAdvanced(state).plan, edit.audience);
|
|
31151
31096
|
const targetKeys = audience ? new Set(audience.targetKeys) : null;
|
|
31152
31097
|
const marketKeys = new Set(audience?.marketKeys ?? []);
|
|
@@ -31180,7 +31125,7 @@ function applyAdvancedEdit(state, plan, rows, edit, capturedAt, reusedQueryIds,
|
|
|
31180
31125
|
}
|
|
31181
31126
|
return plan;
|
|
31182
31127
|
}
|
|
31183
|
-
const newQueryId = candidateIdsForText(state, plan, rows,
|
|
31128
|
+
const newQueryId = candidateIdsForText(state, plan, rows, normalizeIdentityText(text2)).at(0) ?? preferredQueryId(state, normalizeIdentityText(text2)) ?? stableNewQueryId(state.project.id, normalizeIdentityText(text2));
|
|
31184
31129
|
const queryText = queryTextForId(state, plan, rows, newQueryId, text2.trim());
|
|
31185
31130
|
const provenance = provenanceForId(state, plan, rows, newQueryId, { source: "manual", sourceId: null, capturedAt });
|
|
31186
31131
|
if (!rows.has(newQueryId)) rows.set(newQueryId, { id: newQueryId, query: queryText, provenance: serializeQueryProvenance(provenance) });
|
|
@@ -31281,7 +31226,7 @@ function writeSimpleCandidate(tx, candidate, now) {
|
|
|
31281
31226
|
for (const queryId of candidate.createdQueryIds) {
|
|
31282
31227
|
const planned = candidate.queryRows.get(queryId);
|
|
31283
31228
|
if (!planned) continue;
|
|
31284
|
-
const row = persisted.find((candidateRow) =>
|
|
31229
|
+
const row = persisted.find((candidateRow) => normalizeIdentityText(candidateRow.query) === normalizeIdentityText(planned.query));
|
|
31285
31230
|
if (!row) continue;
|
|
31286
31231
|
if (row.id !== planned.id) tx.update(queries).set({ id: planned.id }).where(eq37(queries.id, row.id)).run();
|
|
31287
31232
|
if (planned.provenance) tx.update(queries).set({ provenance: planned.provenance }).where(eq37(queries.id, planned.id)).run();
|
|
@@ -31616,7 +31561,7 @@ import crypto24 from "crypto";
|
|
|
31616
31561
|
import dns2 from "dns/promises";
|
|
31617
31562
|
import http2 from "http";
|
|
31618
31563
|
import https2 from "https";
|
|
31619
|
-
import
|
|
31564
|
+
import net3 from "net";
|
|
31620
31565
|
var REQUEST_TIMEOUT_MS = 1e4;
|
|
31621
31566
|
async function resolveWebhookTarget(raw, options = {}) {
|
|
31622
31567
|
let parsed;
|
|
@@ -31631,13 +31576,13 @@ async function resolveWebhookTarget(raw, options = {}) {
|
|
|
31631
31576
|
if (parsed.username || parsed.password) {
|
|
31632
31577
|
return { ok: false, message: '"url" must not include credentials' };
|
|
31633
31578
|
}
|
|
31634
|
-
const lookupHost =
|
|
31579
|
+
const lookupHost = stripIpv6Brackets(parsed.hostname);
|
|
31635
31580
|
if (!lookupHost) {
|
|
31636
31581
|
return { ok: false, message: '"url" must include a hostname' };
|
|
31637
31582
|
}
|
|
31638
|
-
const addresses = await resolveHostAddresses(lookupHost);
|
|
31583
|
+
const addresses = await resolveHostAddresses(lookupHost, options.resolveAddresses);
|
|
31639
31584
|
if (addresses.length === 0) {
|
|
31640
|
-
return { ok: false, message: '"url" hostname could not be resolved' };
|
|
31585
|
+
return { ok: false, message: '"url" hostname could not be resolved', unresolved: true };
|
|
31641
31586
|
}
|
|
31642
31587
|
const blocked = addresses.find((entry) => isBlockedAddress(entry.address, options));
|
|
31643
31588
|
if (blocked) {
|
|
@@ -31648,7 +31593,8 @@ async function resolveWebhookTarget(raw, options = {}) {
|
|
|
31648
31593
|
target: {
|
|
31649
31594
|
url: parsed,
|
|
31650
31595
|
address: addresses[0].address,
|
|
31651
|
-
family: addresses[0].family
|
|
31596
|
+
family: addresses[0].family,
|
|
31597
|
+
addresses
|
|
31652
31598
|
}
|
|
31653
31599
|
};
|
|
31654
31600
|
}
|
|
@@ -31668,16 +31614,23 @@ async function deliverWebhook(target, payload, webhookSecret) {
|
|
|
31668
31614
|
}
|
|
31669
31615
|
return await new Promise((resolve) => {
|
|
31670
31616
|
const requestOptions = {
|
|
31671
|
-
|
|
31617
|
+
// A connection of its own: a shared agent pools by hostname, so it could
|
|
31618
|
+
// hand this request a socket some other caller dialed unchecked.
|
|
31619
|
+
agent: false,
|
|
31620
|
+
// Dial the checked addresses, falling back from one to the next, so a
|
|
31621
|
+
// receiver that listens on only one of them (`localhost` checks as
|
|
31622
|
+
// 127.0.0.1 and ::1) still gets the delivery.
|
|
31623
|
+
autoSelectFamily: true,
|
|
31624
|
+
lookup: pinnedLookup(checkedAddresses(target)),
|
|
31672
31625
|
headers,
|
|
31673
|
-
hostname: target.
|
|
31626
|
+
hostname: stripIpv6Brackets(target.url.hostname),
|
|
31674
31627
|
method: "POST",
|
|
31675
31628
|
path: path8,
|
|
31676
31629
|
port,
|
|
31677
31630
|
timeout: REQUEST_TIMEOUT_MS
|
|
31678
31631
|
};
|
|
31679
31632
|
if (isHttps) {
|
|
31680
|
-
requestOptions.servername =
|
|
31633
|
+
requestOptions.servername = stripIpv6Brackets(target.url.hostname);
|
|
31681
31634
|
}
|
|
31682
31635
|
const request = (isHttps ? https2.request : http2.request)(requestOptions, (response) => {
|
|
31683
31636
|
response.resume();
|
|
@@ -31694,11 +31647,27 @@ async function deliverWebhook(target, payload, webhookSecret) {
|
|
|
31694
31647
|
request.end(body);
|
|
31695
31648
|
});
|
|
31696
31649
|
}
|
|
31697
|
-
|
|
31698
|
-
|
|
31650
|
+
function checkedAddresses(target) {
|
|
31651
|
+
return target.addresses ?? [{ address: target.address, family: target.family }];
|
|
31652
|
+
}
|
|
31653
|
+
function pinnedLookup(addresses) {
|
|
31654
|
+
return (_hostname, options, callback) => {
|
|
31655
|
+
if (options.all) callback(null, addresses.map(({ address, family }) => ({ address, family })));
|
|
31656
|
+
else callback(null, addresses[0].address, addresses[0].family);
|
|
31657
|
+
};
|
|
31658
|
+
}
|
|
31659
|
+
var LOCALHOST_ADDRESSES = [{ address: "127.0.0.1", family: 4 }, { address: "::1", family: 6 }];
|
|
31660
|
+
function isLocalhostName(hostname) {
|
|
31661
|
+
const name = hostname.toLowerCase().replace(/\.$/, "");
|
|
31662
|
+
return name === "localhost" || name.endsWith(".localhost");
|
|
31663
|
+
}
|
|
31664
|
+
async function resolveHostAddresses(hostname, resolveAddresses) {
|
|
31665
|
+
const family = net3.isIP(hostname);
|
|
31699
31666
|
if (family === 4 || family === 6) {
|
|
31700
31667
|
return [{ address: hostname, family }];
|
|
31701
31668
|
}
|
|
31669
|
+
if (isLocalhostName(hostname)) return LOCALHOST_ADDRESSES;
|
|
31670
|
+
if (resolveAddresses) return await resolveAddresses(hostname);
|
|
31702
31671
|
try {
|
|
31703
31672
|
const [ipv4, ipv6] = await Promise.allSettled([dns2.resolve4(hostname), dns2.resolve6(hostname)]);
|
|
31704
31673
|
const unique3 = /* @__PURE__ */ new Map();
|
|
@@ -31718,65 +31687,7 @@ async function resolveHostAddresses(hostname) {
|
|
|
31718
31687
|
}
|
|
31719
31688
|
}
|
|
31720
31689
|
function isBlockedAddress(address, options) {
|
|
31721
|
-
|
|
31722
|
-
const family = net2.isIP(normalized);
|
|
31723
|
-
if (family === 4) {
|
|
31724
|
-
return isBlockedIpv4(normalized, options);
|
|
31725
|
-
}
|
|
31726
|
-
if (family === 6) {
|
|
31727
|
-
const mappedIpv42 = extractMappedIpv4(normalized);
|
|
31728
|
-
if (mappedIpv42) {
|
|
31729
|
-
return isBlockedIpv4(mappedIpv42, options);
|
|
31730
|
-
}
|
|
31731
|
-
return isBlockedIpv6(normalized, options);
|
|
31732
|
-
}
|
|
31733
|
-
return true;
|
|
31734
|
-
}
|
|
31735
|
-
function isBlockedIpv4(address, options) {
|
|
31736
|
-
const octets = address.split(".").map((part) => Number.parseInt(part, 10));
|
|
31737
|
-
if (octets.length !== 4 || octets.some(Number.isNaN)) {
|
|
31738
|
-
return true;
|
|
31739
|
-
}
|
|
31740
|
-
const [first, second] = octets;
|
|
31741
|
-
if (first === 127 && !options.allowLoopback) {
|
|
31742
|
-
return true;
|
|
31743
|
-
}
|
|
31744
|
-
return first === 0 || first === 10 || first === 100 && second >= 64 && second <= 127 || first === 169 && second === 254 || first === 172 && second >= 16 && second <= 31 || first === 192 && second === 168 || first === 198 && (second === 18 || second === 19);
|
|
31745
|
-
}
|
|
31746
|
-
function isBlockedIpv6(address, options) {
|
|
31747
|
-
const normalized = address.split("%")[0].toLowerCase();
|
|
31748
|
-
if (normalized === "::") {
|
|
31749
|
-
return true;
|
|
31750
|
-
}
|
|
31751
|
-
if (normalized === "::1") {
|
|
31752
|
-
return !options.allowLoopback;
|
|
31753
|
-
}
|
|
31754
|
-
const firstHextetText = normalized.split(":")[0] ?? "";
|
|
31755
|
-
const firstHextet = firstHextetText === "" ? 0 : Number.parseInt(firstHextetText, 16);
|
|
31756
|
-
if (Number.isNaN(firstHextet)) {
|
|
31757
|
-
return true;
|
|
31758
|
-
}
|
|
31759
|
-
return firstHextet >= 64512 && firstHextet <= 65023 || firstHextet >= 65152 && firstHextet <= 65215;
|
|
31760
|
-
}
|
|
31761
|
-
function extractMappedIpv4(address) {
|
|
31762
|
-
const normalized = address.toLowerCase();
|
|
31763
|
-
if (!normalized.startsWith("::ffff:")) {
|
|
31764
|
-
return null;
|
|
31765
|
-
}
|
|
31766
|
-
const remainder = normalized.slice("::ffff:".length);
|
|
31767
|
-
if (net2.isIP(remainder) === 4) {
|
|
31768
|
-
return remainder;
|
|
31769
|
-
}
|
|
31770
|
-
const parts = remainder.split(":");
|
|
31771
|
-
if (parts.length !== 2 || parts.some((part) => !/^[0-9a-f]{1,4}$/.test(part))) {
|
|
31772
|
-
return null;
|
|
31773
|
-
}
|
|
31774
|
-
const high = Number.parseInt(parts[0], 16);
|
|
31775
|
-
const low = Number.parseInt(parts[1], 16);
|
|
31776
|
-
return [high >> 8, high & 255, low >> 8, low & 255].join(".");
|
|
31777
|
-
}
|
|
31778
|
-
function stripIpv6Brackets2(value) {
|
|
31779
|
-
return value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
|
|
31690
|
+
return blockedAddressReason(address, { allowLoopback: options.allowLoopback }) !== null;
|
|
31780
31691
|
}
|
|
31781
31692
|
|
|
31782
31693
|
// ../api-routes/src/apply.ts
|
|
@@ -31895,7 +31806,7 @@ async function applyRoutes(app, opts) {
|
|
|
31895
31806
|
const nextAliases = normalizeProjectAliases(config.spec.displayName, config.spec.aliases ?? []);
|
|
31896
31807
|
if (existing) {
|
|
31897
31808
|
const prevAliases = existing.aliases;
|
|
31898
|
-
aliasesChanged = !
|
|
31809
|
+
aliasesChanged = !aliasArraysEqual(prevAliases, nextAliases);
|
|
31899
31810
|
}
|
|
31900
31811
|
if (existing) {
|
|
31901
31812
|
projectId = existing.id;
|
|
@@ -32126,13 +32037,6 @@ async function applyRoutes(app, opts) {
|
|
|
32126
32037
|
});
|
|
32127
32038
|
});
|
|
32128
32039
|
}
|
|
32129
|
-
function aliasArraysEqual2(a, b) {
|
|
32130
|
-
if (a.length !== b.length) return false;
|
|
32131
|
-
for (let i = 0; i < a.length; i++) {
|
|
32132
|
-
if (a[i].toLowerCase() !== b[i].toLowerCase()) return false;
|
|
32133
|
-
}
|
|
32134
|
-
return true;
|
|
32135
|
-
}
|
|
32136
32040
|
|
|
32137
32041
|
// ../api-routes/src/history.ts
|
|
32138
32042
|
import { and as and31, asc as asc6, count, desc as desc17, eq as eq39, gte as gte6, inArray as inArray19, like as like2, ne as ne8, or as or10 } from "drizzle-orm";
|
|
@@ -36134,8 +36038,8 @@ function readVisibilityCompare(db, projectName, query) {
|
|
|
36134
36038
|
if (scope === "project" && filters.scopeKey !== void 0) throw validationError("scopeKey is not valid for project scope.");
|
|
36135
36039
|
if (scope === "market" && filters.marketKey !== void 0) throw validationError("marketKey is not valid for market scope.");
|
|
36136
36040
|
const { fromRaw, toRaw, fromBounds, toBounds } = compareMonths(query.from, query.to);
|
|
36137
|
-
const providerKey2 = filters.provider === void 0 ? void 0 :
|
|
36138
|
-
const locationKey2 = filters.location === void 0 ? void 0 :
|
|
36041
|
+
const providerKey2 = filters.provider === void 0 ? void 0 : normalizeIdentityText(filters.provider);
|
|
36042
|
+
const locationKey2 = filters.location === void 0 ? void 0 : normalizeIdentityText(filters.location);
|
|
36139
36043
|
const scoped = scope !== "project" || filters.marketKey !== void 0;
|
|
36140
36044
|
let advancedComparison;
|
|
36141
36045
|
if (activeMeasurementPlan(db, project.id) !== null) {
|
|
@@ -36186,7 +36090,7 @@ function readVisibilityCompare(db, projectName, query) {
|
|
|
36186
36090
|
}).from(querySnapshots).where(inArray23(querySnapshots.runId, runIds)).all() : [];
|
|
36187
36091
|
return {
|
|
36188
36092
|
runCount: runIds.length,
|
|
36189
|
-
snapshots: snapshots.filter((snapshot) => (providerKey2 === void 0 ||
|
|
36093
|
+
snapshots: snapshots.filter((snapshot) => (providerKey2 === void 0 || normalizeIdentityText(snapshot.provider) === providerKey2) && (locationKey2 === void 0 || (locationKey2 === "none" ? snapshot.location === null : snapshot.location !== null && normalizeIdentityText(snapshot.location) === locationKey2)))
|
|
36190
36094
|
};
|
|
36191
36095
|
};
|
|
36192
36096
|
const fromMonth = loadMonth(fromBounds);
|
|
@@ -38513,6 +38417,7 @@ var SCHEMA_TABLE = {
|
|
|
38513
38417
|
IndexingRequestResponseDto: indexingRequestResponseDtoSchema,
|
|
38514
38418
|
IntegrationSettingsSummaryDto: integrationSettingsSummaryDtoSchema,
|
|
38515
38419
|
KeywordDto: keywordDtoSchema,
|
|
38420
|
+
KeywordGenerateResponse: keywordGenerateResponseSchema,
|
|
38516
38421
|
LatestProjectRunDto: latestProjectRunDtoSchema,
|
|
38517
38422
|
RunAdmissionDto: runAdmissionDtoSchema,
|
|
38518
38423
|
LocationContext: locationContextSchema,
|
|
@@ -38589,13 +38494,16 @@ var SCHEMA_TABLE = {
|
|
|
38589
38494
|
OrganicEvidenceDto: organicEvidenceDtoSchema,
|
|
38590
38495
|
ProjectCreateRequest: projectCreateRequestSchema,
|
|
38591
38496
|
ProjectDto: projectDtoSchema,
|
|
38497
|
+
ProjectDefaultLocationResponse: projectDefaultLocationResponseSchema,
|
|
38592
38498
|
ProjectUpsertRequest: projectUpsertRequestSchema,
|
|
38593
38499
|
ProjectConfig: projectConfigSchema,
|
|
38500
|
+
ProjectConfigExport: projectConfigExportSchema,
|
|
38594
38501
|
ProjectOverviewDto: projectOverviewDtoSchema,
|
|
38595
38502
|
TrafficAnalyticsResponse: trafficAnalyticsResponseSchema,
|
|
38596
38503
|
ProjectSearchResponseDto: projectSearchResponseSchema,
|
|
38597
38504
|
ProviderSummaryEntryDto: providerSummaryEntryDtoSchema,
|
|
38598
38505
|
QueryDto: queryDtoSchema,
|
|
38506
|
+
QueryGenerateResponse: queryGenerateResponseSchema,
|
|
38599
38507
|
ReferralAssessment: referralAssessmentSchema,
|
|
38600
38508
|
ResultsExportDto: resultsExportDtoSchema,
|
|
38601
38509
|
RunCompletenessDto: runCompletenessDtoSchema,
|
|
@@ -38658,7 +38566,7 @@ var SCHEMA_TABLE = {
|
|
|
38658
38566
|
WordpressManualAssistDto: wordpressManualAssistDtoSchema,
|
|
38659
38567
|
WordpressOnboardResultDto: wordpressOnboardResultDtoSchema,
|
|
38660
38568
|
WordpressPageDetailDto: wordpressPageDetailDtoSchema,
|
|
38661
|
-
|
|
38569
|
+
WordpressPageListDto: wordpressPageListDtoSchema,
|
|
38662
38570
|
WordpressSchemaBlockDto: wordpressSchemaBlockDtoSchema,
|
|
38663
38571
|
WordpressSchemaDeployResultDto: wordpressSchemaDeployResultDtoSchema,
|
|
38664
38572
|
WordpressSchemaStatusResultDto: wordpressSchemaStatusResultDtoSchema,
|
|
@@ -38678,10 +38586,27 @@ var SCHEMA_TABLE = {
|
|
|
38678
38586
|
var SHARED_ENUM_REFS = [
|
|
38679
38587
|
{ schema: "ScheduleDto", property: "kind", component: "SchedulableRunKind" }
|
|
38680
38588
|
];
|
|
38589
|
+
function addNullToNullableEnums(node) {
|
|
38590
|
+
if (Array.isArray(node)) {
|
|
38591
|
+
for (const item2 of node) addNullToNullableEnums(item2);
|
|
38592
|
+
return;
|
|
38593
|
+
}
|
|
38594
|
+
if (node === null || typeof node !== "object") return;
|
|
38595
|
+
const record = node;
|
|
38596
|
+
if (record.nullable === true && Array.isArray(record.enum)) {
|
|
38597
|
+
const values = record.enum;
|
|
38598
|
+
if (!values.includes(null)) record.enum = [...values, null];
|
|
38599
|
+
}
|
|
38600
|
+
for (const value of Object.values(record)) addNullToNullableEnums(value);
|
|
38601
|
+
}
|
|
38602
|
+
var INPUT_FORM_SCHEMAS = /* @__PURE__ */ new Set(["ProjectConfig"]);
|
|
38681
38603
|
function buildComponentSchemas() {
|
|
38682
38604
|
const out = {};
|
|
38683
38605
|
for (const [name, schema] of Object.entries(SCHEMA_TABLE)) {
|
|
38684
|
-
|
|
38606
|
+
const io = INPUT_FORM_SCHEMAS.has(name) ? "input" : "output";
|
|
38607
|
+
const json = z.toJSONSchema(schema, { target: "openapi-3.0", io });
|
|
38608
|
+
addNullToNullableEnums(json);
|
|
38609
|
+
out[name] = json;
|
|
38685
38610
|
}
|
|
38686
38611
|
for (const { schema, property, component } of SHARED_ENUM_REFS) {
|
|
38687
38612
|
const properties = out[schema]?.properties;
|
|
@@ -40428,7 +40353,7 @@ var routeCatalog = [
|
|
|
40428
40353
|
}
|
|
40429
40354
|
},
|
|
40430
40355
|
responses: {
|
|
40431
|
-
200: jsonResponse("Default location updated.", "
|
|
40356
|
+
200: jsonResponse("Default location updated.", "ProjectDefaultLocationResponse"),
|
|
40432
40357
|
400: errorResponse("Invalid location."),
|
|
40433
40358
|
404: errorResponse("Project not found.")
|
|
40434
40359
|
}
|
|
@@ -40440,7 +40365,7 @@ var routeCatalog = [
|
|
|
40440
40365
|
tags: ["projects"],
|
|
40441
40366
|
parameters: [nameParameter],
|
|
40442
40367
|
responses: {
|
|
40443
|
-
200: jsonResponse("Project configuration returned.", "
|
|
40368
|
+
200: jsonResponse("Project configuration returned.", "ProjectConfigExport"),
|
|
40444
40369
|
404: errorResponse("Project not found.")
|
|
40445
40370
|
}
|
|
40446
40371
|
},
|
|
@@ -40643,7 +40568,7 @@ var routeCatalog = [
|
|
|
40643
40568
|
}
|
|
40644
40569
|
},
|
|
40645
40570
|
responses: {
|
|
40646
|
-
200:
|
|
40571
|
+
200: jsonResponse("Query suggestions returned.", "QueryGenerateResponse"),
|
|
40647
40572
|
501: errorResponse("Query generation is not available.")
|
|
40648
40573
|
}
|
|
40649
40574
|
},
|
|
@@ -40752,8 +40677,7 @@ var routeCatalog = [
|
|
|
40752
40677
|
}
|
|
40753
40678
|
},
|
|
40754
40679
|
responses: {
|
|
40755
|
-
|
|
40756
|
-
200: rawJsonResponse("Legacy keyword suggestions returned.", looseObjectSchema),
|
|
40680
|
+
200: jsonResponse("Legacy keyword suggestions returned.", "KeywordGenerateResponse"),
|
|
40757
40681
|
501: errorResponse("Legacy keyword generation is not available.")
|
|
40758
40682
|
}
|
|
40759
40683
|
},
|
|
@@ -44089,7 +44013,7 @@ var routeCatalog = [
|
|
|
44089
44013
|
tags: ["wordpress"],
|
|
44090
44014
|
parameters: [nameParameter, wordpressEnvQueryParameter],
|
|
44091
44015
|
responses: {
|
|
44092
|
-
200:
|
|
44016
|
+
200: jsonResponse("WordPress pages returned.", "WordpressPageListDto"),
|
|
44093
44017
|
400: errorResponse("Invalid environment or missing connection."),
|
|
44094
44018
|
404: errorResponse("Project not found.")
|
|
44095
44019
|
}
|
|
@@ -47812,18 +47736,18 @@ async function exchangeCode(clientId, clientSecret, code, redirectUri) {
|
|
|
47812
47736
|
const parsed = JSON.parse(body);
|
|
47813
47737
|
if (parsed.error) detail = parsed.error;
|
|
47814
47738
|
if (parsed.error_description) {
|
|
47815
|
-
const sanitized = parsed.error_description.replace(new RegExp(
|
|
47739
|
+
const sanitized = parsed.error_description.replace(new RegExp(escapeRegExp2(clientId), "g"), "***").replace(new RegExp(escapeRegExp2(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp2(code), "g"), "***");
|
|
47816
47740
|
detail += detail ? `: ${sanitized}` : sanitized;
|
|
47817
47741
|
}
|
|
47818
47742
|
} catch {
|
|
47819
47743
|
const truncated = body.length <= 120 ? body : `${body.slice(0, 120)}...`;
|
|
47820
|
-
detail = truncated.replace(new RegExp(
|
|
47744
|
+
detail = truncated.replace(new RegExp(escapeRegExp2(clientId), "g"), "***").replace(new RegExp(escapeRegExp2(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp2(code), "g"), "***");
|
|
47821
47745
|
}
|
|
47822
47746
|
throw new GoogleAuthError(`Token exchange failed (${res.status}): ${detail}`);
|
|
47823
47747
|
}
|
|
47824
47748
|
return await res.json();
|
|
47825
47749
|
}
|
|
47826
|
-
function
|
|
47750
|
+
function escapeRegExp2(str) {
|
|
47827
47751
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
47828
47752
|
}
|
|
47829
47753
|
async function refreshAccessToken(clientId, clientSecret, currentRefreshToken) {
|
|
@@ -47851,12 +47775,12 @@ async function refreshAccessToken(clientId, clientSecret, currentRefreshToken) {
|
|
|
47851
47775
|
const parsed = JSON.parse(body);
|
|
47852
47776
|
if (parsed.error) detail = parsed.error;
|
|
47853
47777
|
if (parsed.error_description) {
|
|
47854
|
-
const sanitized = parsed.error_description.replace(new RegExp(
|
|
47778
|
+
const sanitized = parsed.error_description.replace(new RegExp(escapeRegExp2(clientId), "g"), "***").replace(new RegExp(escapeRegExp2(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp2(currentRefreshToken), "g"), "***");
|
|
47855
47779
|
detail += detail ? `: ${sanitized}` : sanitized;
|
|
47856
47780
|
}
|
|
47857
47781
|
} catch {
|
|
47858
47782
|
const truncated = body.length <= 120 ? body : `${body.slice(0, 120)}...`;
|
|
47859
|
-
detail = truncated.replace(new RegExp(
|
|
47783
|
+
detail = truncated.replace(new RegExp(escapeRegExp2(clientId), "g"), "***").replace(new RegExp(escapeRegExp2(clientSecret), "g"), "***").replace(new RegExp(escapeRegExp2(currentRefreshToken), "g"), "***");
|
|
47860
47784
|
}
|
|
47861
47785
|
throw new GoogleAuthError(`Token refresh failed (${res.status}): ${detail}`);
|
|
47862
47786
|
}
|
|
@@ -47946,7 +47870,7 @@ function gscClientLog(level, action, ctx) {
|
|
|
47946
47870
|
const stream = level === "error" ? process.stderr : process.stdout;
|
|
47947
47871
|
stream.write(JSON.stringify(entry) + "\n");
|
|
47948
47872
|
}
|
|
47949
|
-
function
|
|
47873
|
+
function escapeRegExp3(str) {
|
|
47950
47874
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
47951
47875
|
}
|
|
47952
47876
|
async function gscFetchResponse(accessToken, url, opts) {
|
|
@@ -47975,7 +47899,7 @@ async function gscFetchResponse(accessToken, url, opts) {
|
|
|
47975
47899
|
const detail = body.length <= 500 ? body : `${body.slice(0, 500)}... [truncated]`;
|
|
47976
47900
|
let sanitizedDetail = detail;
|
|
47977
47901
|
if (accessToken) {
|
|
47978
|
-
sanitizedDetail = sanitizedDetail.replace(new RegExp(
|
|
47902
|
+
sanitizedDetail = sanitizedDetail.replace(new RegExp(escapeRegExp3(accessToken), "g"), "***");
|
|
47979
47903
|
}
|
|
47980
47904
|
throw new GoogleApiError(`GSC API error (${res.status}): ${sanitizedDetail}`, res.status);
|
|
47981
47905
|
}
|
|
@@ -48263,15 +48187,12 @@ async function getAccessToken(clientEmail, privateKey) {
|
|
|
48263
48187
|
const body = await res.text().catch(() => "");
|
|
48264
48188
|
ga4Log("error", "token.failed", { httpStatus: res.status });
|
|
48265
48189
|
const detail = body.length <= 200 ? body : `${body.slice(0, 200)}... [truncated]`;
|
|
48266
|
-
const sanitizedDetail = detail.replace(new RegExp(
|
|
48190
|
+
const sanitizedDetail = detail.replace(new RegExp(escapeRegExp(clientEmail), "g"), "***").replace(new RegExp(escapeRegExp(privateKey.slice(0, 32)), "g"), "***");
|
|
48267
48191
|
throw new GA4ApiError(`Failed to get access token: ${sanitizedDetail}`, res.status);
|
|
48268
48192
|
}
|
|
48269
48193
|
const data = await res.json();
|
|
48270
48194
|
return data.access_token;
|
|
48271
48195
|
}
|
|
48272
|
-
function escapeRegExp3(str) {
|
|
48273
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
48274
|
-
}
|
|
48275
48196
|
var gaInFlight = 0;
|
|
48276
48197
|
var gaWaitQueue = [];
|
|
48277
48198
|
async function acquireGa4Slot() {
|
|
@@ -48377,7 +48298,7 @@ async function runReport(accessToken, propertyId, request) {
|
|
|
48377
48298
|
}
|
|
48378
48299
|
let sanitizedDetail = detail;
|
|
48379
48300
|
if (accessToken) {
|
|
48380
|
-
sanitizedDetail = sanitizedDetail.replace(new RegExp(
|
|
48301
|
+
sanitizedDetail = sanitizedDetail.replace(new RegExp(escapeRegExp(accessToken), "g"), "***");
|
|
48381
48302
|
}
|
|
48382
48303
|
ga4Log("error", "report.auth-failed", { propertyId, httpStatus: res.status });
|
|
48383
48304
|
throw new GA4ApiError(
|
|
@@ -48397,7 +48318,7 @@ async function runReport(accessToken, propertyId, request) {
|
|
|
48397
48318
|
const detail = body.length <= 500 ? body : `${body.slice(0, 500)}... [truncated]`;
|
|
48398
48319
|
let sanitizedDetail = detail;
|
|
48399
48320
|
if (accessToken) {
|
|
48400
|
-
sanitizedDetail = sanitizedDetail.replace(new RegExp(
|
|
48321
|
+
sanitizedDetail = sanitizedDetail.replace(new RegExp(escapeRegExp(accessToken), "g"), "***");
|
|
48401
48322
|
}
|
|
48402
48323
|
throw new GA4ApiError(`GA4 API error (${res.status}): ${sanitizedDetail}`, res.status, retryAfterSeconds);
|
|
48403
48324
|
}
|
|
@@ -48421,7 +48342,7 @@ async function batchRunReports(accessToken, propertyId, requests) {
|
|
|
48421
48342
|
ga4Log("error", "batch-report.auth-failed", { propertyId, httpStatus: res.status });
|
|
48422
48343
|
let sanitizedDetail = body;
|
|
48423
48344
|
if (accessToken) {
|
|
48424
|
-
sanitizedDetail = sanitizedDetail.replace(new RegExp(
|
|
48345
|
+
sanitizedDetail = sanitizedDetail.replace(new RegExp(escapeRegExp(accessToken), "g"), "***");
|
|
48425
48346
|
}
|
|
48426
48347
|
throw new GA4ApiError(
|
|
48427
48348
|
`GA4 API authentication failed \u2014 check service account permissions. ${sanitizedDetail}`,
|
|
@@ -48440,7 +48361,7 @@ async function batchRunReports(accessToken, propertyId, requests) {
|
|
|
48440
48361
|
const detail = body.length <= 500 ? body : `${body.slice(0, 500)}... [truncated]`;
|
|
48441
48362
|
let sanitizedDetail = detail;
|
|
48442
48363
|
if (accessToken) {
|
|
48443
|
-
sanitizedDetail = sanitizedDetail.replace(new RegExp(
|
|
48364
|
+
sanitizedDetail = sanitizedDetail.replace(new RegExp(escapeRegExp(accessToken), "g"), "***");
|
|
48444
48365
|
}
|
|
48445
48366
|
throw new GA4ApiError(`GA4 API error (${res.status}): ${sanitizedDetail}`, res.status, retryAfterSeconds);
|
|
48446
48367
|
}
|
|
@@ -48742,7 +48663,7 @@ async function listProperties(accessToken) {
|
|
|
48742
48663
|
} catch {
|
|
48743
48664
|
if (raw.length < 200) detail = ` ${raw}`;
|
|
48744
48665
|
}
|
|
48745
|
-
const sanitized = detail.replace(new RegExp(
|
|
48666
|
+
const sanitized = detail.replace(new RegExp(escapeRegExp(accessToken), "g"), "***");
|
|
48746
48667
|
const retryAfterSeconds = res.status === 429 || res.status >= 500 || res.status === 403 && quotaReason ? parseRetryAfter(res.headers.get("retry-after")) : void 0;
|
|
48747
48668
|
ga4Log("error", "admin.account-summaries-failed", {
|
|
48748
48669
|
httpStatus: res.status,
|
|
@@ -58074,9 +57995,6 @@ function bingClientLog(level, action, ctx) {
|
|
|
58074
57995
|
const stream = level === "error" ? process.stderr : process.stdout;
|
|
58075
57996
|
stream.write(JSON.stringify(entry) + "\n");
|
|
58076
57997
|
}
|
|
58077
|
-
function escapeRegExp4(str) {
|
|
58078
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
58079
|
-
}
|
|
58080
57998
|
function parseBingErrorCode(body) {
|
|
58081
57999
|
try {
|
|
58082
58000
|
const parsed = JSON.parse(body);
|
|
@@ -58113,7 +58031,7 @@ async function bingFetchOnce(apiKey, endpoint, opts) {
|
|
|
58113
58031
|
if (!res.ok) {
|
|
58114
58032
|
const body = await res.text();
|
|
58115
58033
|
let detail = body.length <= 500 ? body : `${body.slice(0, 500)}... [truncated]`;
|
|
58116
|
-
detail = detail.replace(new RegExp(
|
|
58034
|
+
detail = detail.replace(new RegExp(escapeRegExp(apiKey), "g"), "***");
|
|
58117
58035
|
const bingErrorCode = parseBingErrorCode(body);
|
|
58118
58036
|
const err = new BingApiError(`Bing API error (${res.status}): ${detail}`, res.status, bingErrorCode);
|
|
58119
58037
|
bingClientLog("error", err.isThrottle ? "http.throttled" : "http.error", {
|
|
@@ -58956,7 +58874,7 @@ function findBiggestMover(current, prior) {
|
|
|
58956
58874
|
for (const row of current) entry(row.source).current += row.sessions ?? 0;
|
|
58957
58875
|
for (const row of prior) entry(row.source).prior += row.sessions ?? 0;
|
|
58958
58876
|
let mover = null;
|
|
58959
|
-
const sources = [...bySource.keys()].sort(
|
|
58877
|
+
const sources = [...bySource.keys()].sort(compareText);
|
|
58960
58878
|
for (const source of sources) {
|
|
58961
58879
|
const sessions = bySource.get(source);
|
|
58962
58880
|
const size = Math.abs(sessions.current - sessions.prior);
|
|
@@ -60572,7 +60490,7 @@ function buildAuthErrorMessage(res, responseText) {
|
|
|
60572
60490
|
async function fetchJson(connection, siteUrl, path8, init) {
|
|
60573
60491
|
if (siteUrl.startsWith("http:")) {
|
|
60574
60492
|
}
|
|
60575
|
-
const res = await fetch(`${normalizeSiteUrl(siteUrl)}${path8}`, {
|
|
60493
|
+
const res = await (connection.fetchImpl ?? fetch)(`${normalizeSiteUrl(siteUrl)}${path8}`, {
|
|
60576
60494
|
...init,
|
|
60577
60495
|
headers: {
|
|
60578
60496
|
"Authorization": `Basic ${encodeBasicAuth(connection.username, connection.appPassword)}`,
|
|
@@ -60596,10 +60514,10 @@ async function fetchJson(connection, siteUrl, path8, init) {
|
|
|
60596
60514
|
const text2 = await res.text().catch(() => "");
|
|
60597
60515
|
let detail = text2 || res.statusText;
|
|
60598
60516
|
if (connection.appPassword) {
|
|
60599
|
-
detail = detail.replace(new RegExp(
|
|
60517
|
+
detail = detail.replace(new RegExp(escapeRegExp(connection.appPassword), "g"), "***");
|
|
60600
60518
|
}
|
|
60601
60519
|
if (connection.username) {
|
|
60602
|
-
detail = detail.replace(new RegExp(
|
|
60520
|
+
detail = detail.replace(new RegExp(escapeRegExp(connection.username), "g"), "***");
|
|
60603
60521
|
}
|
|
60604
60522
|
throw new WordpressApiError("UPSTREAM_ERROR", `WordPress API error (${res.status}): ${detail}`, res.status);
|
|
60605
60523
|
}
|
|
@@ -60631,9 +60549,9 @@ async function fetchPageCollectionSummary(connection, siteUrl, options) {
|
|
|
60631
60549
|
);
|
|
60632
60550
|
return response;
|
|
60633
60551
|
}
|
|
60634
|
-
async function fetchText(url) {
|
|
60552
|
+
async function fetchText(connection, url) {
|
|
60635
60553
|
try {
|
|
60636
|
-
const res = await fetch(url, { signal: AbortSignal.timeout(WP_FETCH_TEXT_TIMEOUT_MS) });
|
|
60554
|
+
const res = await (connection.fetchImpl ?? fetch)(url, { signal: AbortSignal.timeout(WP_FETCH_TEXT_TIMEOUT_MS) });
|
|
60637
60555
|
if (!res.ok) return null;
|
|
60638
60556
|
return await res.text();
|
|
60639
60557
|
} catch {
|
|
@@ -60775,7 +60693,7 @@ async function verifyWordpressConnection(connection) {
|
|
|
60775
60693
|
validateConnection(connection, site.siteUrl);
|
|
60776
60694
|
const userInfo = await verifyAuthenticatedRestAccess(connection, site.siteUrl);
|
|
60777
60695
|
const response = await fetchPageCollectionSummary(connection, site.siteUrl, { context: "view" });
|
|
60778
|
-
const homeHtml = await fetchText(site.siteUrl);
|
|
60696
|
+
const homeHtml = await fetchText(connection, site.siteUrl);
|
|
60779
60697
|
return {
|
|
60780
60698
|
url: site.siteUrl,
|
|
60781
60699
|
reachable: true,
|
|
@@ -60791,7 +60709,7 @@ async function getSiteStatus(connection, env) {
|
|
|
60791
60709
|
try {
|
|
60792
60710
|
const userInfo = await verifyAuthenticatedRestAccess(connection, site.siteUrl);
|
|
60793
60711
|
const response = await fetchPageCollectionSummary(connection, site.siteUrl, { context: "view" });
|
|
60794
|
-
const homeHtml = await fetchText(site.siteUrl);
|
|
60712
|
+
const homeHtml = await fetchText(connection, site.siteUrl);
|
|
60795
60713
|
const plugins = await listActivePlugins(connection, env);
|
|
60796
60714
|
return {
|
|
60797
60715
|
url: site.siteUrl,
|
|
@@ -60881,15 +60799,15 @@ async function getPageBySlug(connection, slug, env) {
|
|
|
60881
60799
|
}
|
|
60882
60800
|
return body[0];
|
|
60883
60801
|
}
|
|
60884
|
-
async function fetchRenderedPage(link) {
|
|
60802
|
+
async function fetchRenderedPage(connection, link) {
|
|
60885
60803
|
if (!link) return null;
|
|
60886
|
-
return fetchText(link);
|
|
60804
|
+
return fetchText(connection, link);
|
|
60887
60805
|
}
|
|
60888
60806
|
async function getPageDetail(connection, slug, env, plugins) {
|
|
60889
60807
|
const site = resolveEnvironment(connection, env);
|
|
60890
60808
|
const resolvedPlugins = plugins === void 0 ? await listActivePlugins(connection, site.env) : plugins;
|
|
60891
60809
|
const page3 = await getPageBySlug(connection, slug, site.env);
|
|
60892
|
-
const html = await fetchRenderedPage(page3.link);
|
|
60810
|
+
const html = await fetchRenderedPage(connection, page3.link);
|
|
60893
60811
|
const schemaBlocks = html ? extractSchemaBlocks(html) : [];
|
|
60894
60812
|
const seo = buildSeoState(page3, html, resolvedPlugins);
|
|
60895
60813
|
return {
|
|
@@ -61094,14 +61012,11 @@ var CANONRY_SCHEMA_START = "<!-- canonry:schema:start -->";
|
|
|
61094
61012
|
var CANONRY_SCHEMA_END = "<!-- canonry:schema:end -->";
|
|
61095
61013
|
function stripCanonrySchema(content) {
|
|
61096
61014
|
const regex = new RegExp(
|
|
61097
|
-
`${
|
|
61015
|
+
`${escapeRegExp(CANONRY_SCHEMA_START)}[\\s\\S]*?${escapeRegExp(CANONRY_SCHEMA_END)}`,
|
|
61098
61016
|
"g"
|
|
61099
61017
|
);
|
|
61100
61018
|
return content.replace(regex, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
61101
61019
|
}
|
|
61102
|
-
function escapeRegExp5(str) {
|
|
61103
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
61104
|
-
}
|
|
61105
61020
|
function injectCanonrySchema(content, schemas) {
|
|
61106
61021
|
if (schemas.length === 0) return content;
|
|
61107
61022
|
const blocks = schemas.map((schema) => `<script type="application/ld+json">${JSON.stringify(schema).replace(/<\//g, "<\\/")}</script>`).join("\n");
|
|
@@ -61206,7 +61121,7 @@ async function getSchemaStatus(connection, env) {
|
|
|
61206
61121
|
const thirdPartySchemas = [];
|
|
61207
61122
|
if (hasCanonryMarker) {
|
|
61208
61123
|
const markerRegex = new RegExp(
|
|
61209
|
-
`${
|
|
61124
|
+
`${escapeRegExp(CANONRY_SCHEMA_START)}([\\s\\S]*?)${escapeRegExp(CANONRY_SCHEMA_END)}`
|
|
61210
61125
|
);
|
|
61211
61126
|
const match = markerRegex.exec(rawContent);
|
|
61212
61127
|
if (match?.[1]) {
|
|
@@ -61249,7 +61164,7 @@ async function getLlmsTxt(connection, env) {
|
|
|
61249
61164
|
return {
|
|
61250
61165
|
env: site.env,
|
|
61251
61166
|
url,
|
|
61252
|
-
content: await fetchText(url)
|
|
61167
|
+
content: await fetchText(connection, url)
|
|
61253
61168
|
};
|
|
61254
61169
|
}
|
|
61255
61170
|
async function buildManualLlmsTxtUpdate(connection, content, env) {
|
|
@@ -61431,6 +61346,132 @@ function parseEnv(value) {
|
|
|
61431
61346
|
return parsed.success ? parsed.data : void 0;
|
|
61432
61347
|
}
|
|
61433
61348
|
|
|
61349
|
+
// ../api-routes/src/guarded-fetch.ts
|
|
61350
|
+
import { Agent, fetch as undiciFetch } from "undici";
|
|
61351
|
+
var GUARDED_FETCH_MAX_REDIRECTS = 5;
|
|
61352
|
+
var MAX_POOLED_AGENTS = 16;
|
|
61353
|
+
var EgressRefusedError = class extends Error {
|
|
61354
|
+
/**
|
|
61355
|
+
* True when the refused target is a redirect the site chose rather than the
|
|
61356
|
+
* URL the caller asked for. Refusing the caller's URL is a verdict on the
|
|
61357
|
+
* caller's input; refusing a redirect is the site's failure.
|
|
61358
|
+
*/
|
|
61359
|
+
redirected;
|
|
61360
|
+
constructor(message, redirected) {
|
|
61361
|
+
super(message);
|
|
61362
|
+
this.name = "EgressRefusedError";
|
|
61363
|
+
this.redirected = redirected;
|
|
61364
|
+
}
|
|
61365
|
+
};
|
|
61366
|
+
var EgressFailedError = class extends Error {
|
|
61367
|
+
constructor(message) {
|
|
61368
|
+
super(message);
|
|
61369
|
+
this.name = "EgressFailedError";
|
|
61370
|
+
}
|
|
61371
|
+
};
|
|
61372
|
+
var CROSS_ORIGIN_DROPPED_HEADERS = ["authorization", "cookie", "proxy-authorization"];
|
|
61373
|
+
var BODY_HEADERS = ["content-encoding", "content-language", "content-length", "content-location", "content-type"];
|
|
61374
|
+
function createGuardedFetch(options = {}) {
|
|
61375
|
+
const resolveTarget = options.resolveTarget ?? ((url) => resolveWebhookTarget(url, { allowLoopback: options.allowLoopback }));
|
|
61376
|
+
const agents = /* @__PURE__ */ new Map();
|
|
61377
|
+
function agentFor(target) {
|
|
61378
|
+
const addresses = checkedAddresses(target);
|
|
61379
|
+
const key = `${target.url.origin} ${addresses.map(({ address }) => address).sort().join(" ")}`;
|
|
61380
|
+
const agent = agents.get(key) ?? pinnedAgent(addresses);
|
|
61381
|
+
agents.delete(key);
|
|
61382
|
+
agents.set(key, agent);
|
|
61383
|
+
if (agents.size > MAX_POOLED_AGENTS) {
|
|
61384
|
+
const [oldestKey, oldest] = agents.entries().next().value;
|
|
61385
|
+
agents.delete(oldestKey);
|
|
61386
|
+
oldest.close().catch(() => {
|
|
61387
|
+
});
|
|
61388
|
+
}
|
|
61389
|
+
return agent;
|
|
61390
|
+
}
|
|
61391
|
+
return async (input, init = {}) => {
|
|
61392
|
+
let href = String(input);
|
|
61393
|
+
let method = (init.method ?? "GET").toUpperCase();
|
|
61394
|
+
let body = init.body ?? null;
|
|
61395
|
+
const headers = new Headers(init.headers);
|
|
61396
|
+
const signal = init.signal ?? void 0;
|
|
61397
|
+
let mayRedirectToLoopback = true;
|
|
61398
|
+
for (let redirects = 0; ; redirects += 1) {
|
|
61399
|
+
signal?.throwIfAborted();
|
|
61400
|
+
const check2 = await untilAborted(resolveTarget(href), signal);
|
|
61401
|
+
if (!check2.ok) {
|
|
61402
|
+
const parsed = URL.canParse(href) ? new URL(href) : null;
|
|
61403
|
+
if (check2.unresolved) throw new EgressFailedError(`Could not resolve ${parsed?.hostname || href}`);
|
|
61404
|
+
throw new EgressRefusedError(`Refused to connect to ${parsed?.host || href}: ${check2.message.replace(/^"url" /, "")}`, redirects > 0);
|
|
61405
|
+
}
|
|
61406
|
+
const url = check2.target.url;
|
|
61407
|
+
const onLoopback = checkedAddresses(check2.target).map(({ address }) => isLoopbackAddress(address));
|
|
61408
|
+
if (!mayRedirectToLoopback && onLoopback.includes(true)) {
|
|
61409
|
+
throw new EgressRefusedError(`Refused to connect to ${url.host}: a redirect from a site that is not on loopback must not lead to loopback`, true);
|
|
61410
|
+
}
|
|
61411
|
+
mayRedirectToLoopback = !onLoopback.includes(false);
|
|
61412
|
+
const response = await requestPinned(check2.target, agentFor(check2.target), { method, headers, body, signal });
|
|
61413
|
+
const location = isLocationRedirectStatus(response.status) ? response.headers.get("location") : null;
|
|
61414
|
+
if (location === null) return response;
|
|
61415
|
+
await response.body?.cancel();
|
|
61416
|
+
if (redirects >= GUARDED_FETCH_MAX_REDIRECTS) {
|
|
61417
|
+
throw new EgressFailedError(`${url.host} redirected more than ${GUARDED_FETCH_MAX_REDIRECTS} times`);
|
|
61418
|
+
}
|
|
61419
|
+
let next;
|
|
61420
|
+
try {
|
|
61421
|
+
next = new URL(location, url);
|
|
61422
|
+
} catch {
|
|
61423
|
+
throw new EgressFailedError(`${url.host} redirected to an invalid Location`);
|
|
61424
|
+
}
|
|
61425
|
+
const status = response.status;
|
|
61426
|
+
if (status === 303 && method !== "GET" && method !== "HEAD" || (status === 301 || status === 302) && method === "POST") {
|
|
61427
|
+
method = "GET";
|
|
61428
|
+
body = null;
|
|
61429
|
+
for (const name of BODY_HEADERS) headers.delete(name);
|
|
61430
|
+
}
|
|
61431
|
+
if (next.origin !== url.origin) {
|
|
61432
|
+
for (const name of CROSS_ORIGIN_DROPPED_HEADERS) headers.delete(name);
|
|
61433
|
+
}
|
|
61434
|
+
href = next.href;
|
|
61435
|
+
}
|
|
61436
|
+
};
|
|
61437
|
+
}
|
|
61438
|
+
async function untilAborted(operation, signal) {
|
|
61439
|
+
if (!signal) return await operation;
|
|
61440
|
+
let onAbort;
|
|
61441
|
+
try {
|
|
61442
|
+
return await Promise.race([
|
|
61443
|
+
operation,
|
|
61444
|
+
new Promise((_resolve, reject) => {
|
|
61445
|
+
onAbort = () => reject(signal.reason instanceof Error ? signal.reason : new DOMException("This operation was aborted", "AbortError"));
|
|
61446
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
61447
|
+
})
|
|
61448
|
+
]);
|
|
61449
|
+
} finally {
|
|
61450
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
61451
|
+
}
|
|
61452
|
+
}
|
|
61453
|
+
async function requestPinned(target, dispatcher, init) {
|
|
61454
|
+
const response = await undiciFetch(target.url, {
|
|
61455
|
+
method: init.method,
|
|
61456
|
+
headers: [...init.headers],
|
|
61457
|
+
body: init.body,
|
|
61458
|
+
signal: init.signal,
|
|
61459
|
+
redirect: "manual",
|
|
61460
|
+
dispatcher
|
|
61461
|
+
});
|
|
61462
|
+
return response;
|
|
61463
|
+
}
|
|
61464
|
+
function pinnedAgent(addresses) {
|
|
61465
|
+
return new Agent({
|
|
61466
|
+
// Try the next checked address when one does not answer, so a dual-stack
|
|
61467
|
+
// site whose first address is unreachable (an IPv6-only network, a dead A
|
|
61468
|
+
// record) still loads, as it does through global `fetch`. Every address
|
|
61469
|
+
// passed the policy, so falling back reaches nothing it refused.
|
|
61470
|
+
autoSelectFamily: true,
|
|
61471
|
+
connect: { lookup: pinnedLookup(addresses) }
|
|
61472
|
+
});
|
|
61473
|
+
}
|
|
61474
|
+
|
|
61434
61475
|
// ../api-routes/src/wordpress.ts
|
|
61435
61476
|
function parseEnvInput(value, fieldName = "env") {
|
|
61436
61477
|
const env = parseEnv(value);
|
|
@@ -61458,6 +61499,8 @@ async function withWordpressErrorHandling(handler) {
|
|
|
61458
61499
|
return await handler();
|
|
61459
61500
|
} catch (error) {
|
|
61460
61501
|
if (error instanceof WordpressApiError) throw toAppError(error);
|
|
61502
|
+
if (error instanceof EgressRefusedError && !error.redirected) throw validationError(error.message);
|
|
61503
|
+
if (error instanceof EgressRefusedError || error instanceof EgressFailedError) throw providerError(error.message);
|
|
61461
61504
|
throw error;
|
|
61462
61505
|
}
|
|
61463
61506
|
}
|
|
@@ -61467,6 +61510,10 @@ async function wordpressRoutes(app, opts) {
|
|
|
61467
61510
|
if (opts.wordpressConnectionStore) return opts.wordpressConnectionStore;
|
|
61468
61511
|
throw validationError("WordPress connection storage is not configured for this deployment");
|
|
61469
61512
|
}
|
|
61513
|
+
const egressFetch = createGuardedFetch({ allowLoopback });
|
|
61514
|
+
function guarded(connection) {
|
|
61515
|
+
return { ...connection, fetchImpl: egressFetch };
|
|
61516
|
+
}
|
|
61470
61517
|
async function assertWordpressUrlAllowed(rawUrl, field) {
|
|
61471
61518
|
const check2 = await resolveWebhookTarget(rawUrl, { allowLoopback });
|
|
61472
61519
|
if (!check2.ok) {
|
|
@@ -61478,7 +61525,7 @@ async function wordpressRoutes(app, opts) {
|
|
|
61478
61525
|
if (!connection) {
|
|
61479
61526
|
throw validationError(`No WordPress connection found for project "${projectName}". Run "canonry wordpress connect ${projectName}" first.`);
|
|
61480
61527
|
}
|
|
61481
|
-
return connection;
|
|
61528
|
+
return guarded(connection);
|
|
61482
61529
|
}
|
|
61483
61530
|
app.post("/projects/:name/wordpress/connect", async (request) => {
|
|
61484
61531
|
return withWordpressErrorHandling(async () => {
|
|
@@ -61506,8 +61553,8 @@ async function wordpressRoutes(app, opts) {
|
|
|
61506
61553
|
createdAt: existing?.createdAt ?? now,
|
|
61507
61554
|
updatedAt: now
|
|
61508
61555
|
};
|
|
61509
|
-
await verifyWordpressConnection(nextConnection);
|
|
61510
|
-
const connection = store.upsertConnection(nextConnection);
|
|
61556
|
+
await verifyWordpressConnection(guarded(nextConnection));
|
|
61557
|
+
const connection = guarded(store.upsertConnection(nextConnection));
|
|
61511
61558
|
const live = await getSiteStatus(connection, "live");
|
|
61512
61559
|
const staging = connection.stagingUrl ? await getSiteStatus(connection, "staging") : null;
|
|
61513
61560
|
writeAuditLog(app.db, {
|
|
@@ -61545,8 +61592,8 @@ async function wordpressRoutes(app, opts) {
|
|
|
61545
61592
|
});
|
|
61546
61593
|
app.get("/projects/:name/wordpress/status", async (request) => {
|
|
61547
61594
|
const project = resolveProject(app.db, request.params.name);
|
|
61548
|
-
const
|
|
61549
|
-
if (!
|
|
61595
|
+
const stored = opts.wordpressConnectionStore?.getConnection(project.name);
|
|
61596
|
+
if (!stored) {
|
|
61550
61597
|
return {
|
|
61551
61598
|
connected: false,
|
|
61552
61599
|
projectName: project.name,
|
|
@@ -61556,6 +61603,7 @@ async function wordpressRoutes(app, opts) {
|
|
|
61556
61603
|
adminUrl: null
|
|
61557
61604
|
};
|
|
61558
61605
|
}
|
|
61606
|
+
const connection = guarded(stored);
|
|
61559
61607
|
const live = await getSiteStatus(connection, "live");
|
|
61560
61608
|
const staging = connection.stagingUrl ? await getSiteStatus(connection, "staging") : null;
|
|
61561
61609
|
return {
|
|
@@ -61840,8 +61888,8 @@ async function wordpressRoutes(app, opts) {
|
|
|
61840
61888
|
createdAt: existing?.createdAt ?? now,
|
|
61841
61889
|
updatedAt: now
|
|
61842
61890
|
};
|
|
61843
|
-
await verifyWordpressConnection(nextConnection);
|
|
61844
|
-
connection = store.upsertConnection(nextConnection);
|
|
61891
|
+
await verifyWordpressConnection(guarded(nextConnection));
|
|
61892
|
+
connection = guarded(store.upsertConnection(nextConnection));
|
|
61845
61893
|
writeAuditLog(app.db, {
|
|
61846
61894
|
projectId: project.id,
|
|
61847
61895
|
actor: "api",
|
|
@@ -62793,7 +62841,6 @@ async function backlinksRoutes(app, opts) {
|
|
|
62793
62841
|
import crypto48 from "crypto";
|
|
62794
62842
|
import { isIP } from "net";
|
|
62795
62843
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
62796
|
-
import { Agent as UndiciAgent } from "undici";
|
|
62797
62844
|
import { and as and60, desc as desc35, eq as eq69, gte as gte16, inArray as inArray28, lte as lte17, sql as sql28 } from "drizzle-orm";
|
|
62798
62845
|
|
|
62799
62846
|
// ../api-routes/src/cloudflare-worker-version.ts
|
|
@@ -78093,7 +78140,7 @@ async function listWordpressTrafficEvents(options) {
|
|
|
78093
78140
|
let skippedEntryCount = 0;
|
|
78094
78141
|
let hasMore = false;
|
|
78095
78142
|
const events = [];
|
|
78096
|
-
const
|
|
78143
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
78097
78144
|
for (let page3 = 0; page3 < maxPages; page3 += 1) {
|
|
78098
78145
|
const url = new URL(endpoint);
|
|
78099
78146
|
url.searchParams.set("limit", String(pageSize));
|
|
@@ -78107,7 +78154,7 @@ async function listWordpressTrafficEvents(options) {
|
|
|
78107
78154
|
url.searchParams.set("until", options.until);
|
|
78108
78155
|
}
|
|
78109
78156
|
url.searchParams.set("_cb", randomUUID4());
|
|
78110
|
-
const
|
|
78157
|
+
const response = await fetchImpl(url, {
|
|
78111
78158
|
method: "GET",
|
|
78112
78159
|
headers: {
|
|
78113
78160
|
Authorization: authHeader,
|
|
@@ -78115,11 +78162,7 @@ async function listWordpressTrafficEvents(options) {
|
|
|
78115
78162
|
"Cache-Control": "no-cache"
|
|
78116
78163
|
},
|
|
78117
78164
|
signal: AbortSignal.timeout(timeoutMs)
|
|
78118
|
-
};
|
|
78119
|
-
if (dispatcher !== void 0) {
|
|
78120
|
-
fetchInit.dispatcher = dispatcher;
|
|
78121
|
-
}
|
|
78122
|
-
const response = await fetch(url, fetchInit);
|
|
78165
|
+
});
|
|
78123
78166
|
if (!response.ok) {
|
|
78124
78167
|
const body2 = await readErrorBody2(response);
|
|
78125
78168
|
throw new WordpressTrafficApiError(
|
|
@@ -80570,28 +80613,8 @@ async function trafficRoutes(app, opts) {
|
|
|
80570
80613
|
if (!check2.ok) {
|
|
80571
80614
|
throw validationError(`WordPress baseUrl rejected: ${check2.message}`);
|
|
80572
80615
|
}
|
|
80573
|
-
const { address, family } = check2.target;
|
|
80574
|
-
return new UndiciAgent({
|
|
80575
|
-
connect: {
|
|
80576
|
-
lookup: (_hostname, options, cb) => {
|
|
80577
|
-
if (options?.all) {
|
|
80578
|
-
cb(null, [{ address, family: family === 6 ? 6 : 4 }]);
|
|
80579
|
-
} else {
|
|
80580
|
-
cb(null, address, family === 6 ? 6 : 4);
|
|
80581
|
-
}
|
|
80582
|
-
}
|
|
80583
|
-
}
|
|
80584
|
-
});
|
|
80585
|
-
}
|
|
80586
|
-
async function withPinnedWordpressDispatcher(baseUrl, fn) {
|
|
80587
|
-
const dispatcher = await assertWordpressTargetAllowed(baseUrl);
|
|
80588
|
-
try {
|
|
80589
|
-
return await fn(dispatcher);
|
|
80590
|
-
} finally {
|
|
80591
|
-
await dispatcher.close().catch(() => {
|
|
80592
|
-
});
|
|
80593
|
-
}
|
|
80594
80616
|
}
|
|
80617
|
+
const wordpressTrafficFetch = createGuardedFetch({ allowLoopback });
|
|
80595
80618
|
const vercelMaxPages = opts.defaultVercelMaxPages ?? DEFAULT_VERCEL_MAX_PAGES;
|
|
80596
80619
|
const vercelSyncDeadlineMs = opts.vercelSyncDeadlineMs ?? DEFAULT_VERCEL_SYNC_DEADLINE_MS;
|
|
80597
80620
|
const syncWindowMinutes = opts.defaultSyncWindowMinutes ?? DEFAULT_SYNC_WINDOW_MINUTES;
|
|
@@ -80743,26 +80766,26 @@ async function trafficRoutes(app, opts) {
|
|
|
80743
80766
|
throw validationError(parsed.error.issues.map((i) => i.message).join("; "));
|
|
80744
80767
|
}
|
|
80745
80768
|
const { baseUrl, username, applicationPassword, displayName } = parsed.data;
|
|
80746
|
-
await
|
|
80747
|
-
|
|
80748
|
-
|
|
80749
|
-
|
|
80750
|
-
|
|
80751
|
-
|
|
80752
|
-
|
|
80753
|
-
|
|
80754
|
-
|
|
80755
|
-
|
|
80756
|
-
|
|
80757
|
-
|
|
80758
|
-
|
|
80759
|
-
|
|
80760
|
-
|
|
80761
|
-
}
|
|
80762
|
-
const msg = describeError(e);
|
|
80763
|
-
throw providerError(`WordPress traffic probe failed: ${msg}`);
|
|
80769
|
+
await assertWordpressTargetAllowed(baseUrl);
|
|
80770
|
+
try {
|
|
80771
|
+
await pullWordpressEvents({
|
|
80772
|
+
baseUrl,
|
|
80773
|
+
username,
|
|
80774
|
+
applicationPassword,
|
|
80775
|
+
pageSize: 1,
|
|
80776
|
+
maxPages: 1,
|
|
80777
|
+
fetchImpl: wordpressTrafficFetch
|
|
80778
|
+
});
|
|
80779
|
+
} catch (e) {
|
|
80780
|
+
if (e instanceof WordpressTrafficApiError) {
|
|
80781
|
+
throw providerError(
|
|
80782
|
+
`WordPress traffic probe failed (HTTP ${e.status}): ${e.message}${e.body ? ` \u2014 ${e.body}` : ""}`
|
|
80783
|
+
);
|
|
80764
80784
|
}
|
|
80765
|
-
|
|
80785
|
+
if (e instanceof EgressRefusedError && !e.redirected) throw validationError(`WordPress baseUrl rejected: ${e.message}`);
|
|
80786
|
+
const msg = describeError(e);
|
|
80787
|
+
throw providerError(`WordPress traffic probe failed: ${msg}`);
|
|
80788
|
+
}
|
|
80766
80789
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
80767
80790
|
const existing = credentialStore.getConnection(project.name);
|
|
80768
80791
|
credentialStore.upsertConnection({
|
|
@@ -81835,10 +81858,9 @@ async function trafficRoutes(app, opts) {
|
|
|
81835
81858
|
const windowEndIso = windowEnd.toISOString();
|
|
81836
81859
|
const wpPageSize = opts.defaultWordpressPageSize ?? DEFAULT_WP_PAGE_SIZE;
|
|
81837
81860
|
const wpMaxPages = opts.defaultWordpressMaxPages ?? DEFAULT_WP_MAX_PAGES;
|
|
81838
|
-
let pinnedDispatcher;
|
|
81839
81861
|
try {
|
|
81840
81862
|
renewWordpressLease();
|
|
81841
|
-
|
|
81863
|
+
await assertWordpressTargetAllowed(credential.baseUrl);
|
|
81842
81864
|
} catch (e) {
|
|
81843
81865
|
const msg = describeError(e);
|
|
81844
81866
|
markFailed(msg, "PROVIDER_PULL");
|
|
@@ -81858,7 +81880,7 @@ async function trafficRoutes(app, opts) {
|
|
|
81858
81880
|
maxPages: 1,
|
|
81859
81881
|
since: windowStartIso,
|
|
81860
81882
|
until: windowEndIso,
|
|
81861
|
-
|
|
81883
|
+
fetchImpl: wordpressTrafficFetch
|
|
81862
81884
|
});
|
|
81863
81885
|
collected.push(...pageResult.events);
|
|
81864
81886
|
const previousCursor = cursor;
|
|
@@ -81876,10 +81898,8 @@ async function trafficRoutes(app, opts) {
|
|
|
81876
81898
|
} catch (e) {
|
|
81877
81899
|
const msg = describeError(e);
|
|
81878
81900
|
markFailed(msg, "PROVIDER_PULL");
|
|
81901
|
+
if (e instanceof EgressRefusedError && !e.redirected) throw validationError(`WordPress baseUrl rejected: ${msg}`);
|
|
81879
81902
|
throw providerError(`WordPress pull failed: ${msg}`);
|
|
81880
|
-
} finally {
|
|
81881
|
-
await pinnedDispatcher.close().catch(() => {
|
|
81882
|
-
});
|
|
81883
81903
|
}
|
|
81884
81904
|
} else {
|
|
81885
81905
|
auditAction = "traffic.vercel.synced";
|
|
@@ -84383,7 +84403,7 @@ var DATA_FRESHNESS_CHECKS = [gaRecentDataCheck, gscRecentDataCheck];
|
|
|
84383
84403
|
import dns3 from "dns/promises";
|
|
84384
84404
|
import http3 from "http";
|
|
84385
84405
|
import https3 from "https";
|
|
84386
|
-
import
|
|
84406
|
+
import net4 from "net";
|
|
84387
84407
|
var SITE_REACHABILITY_USER_AGENT = "Canonry site-liveness (+https://canonry.ai)";
|
|
84388
84408
|
var DEFAULT_SITE_MAX_REDIRECTS = 10;
|
|
84389
84409
|
var DEFAULT_TIMEOUT_MS5 = 1e4;
|
|
@@ -84408,7 +84428,7 @@ function errorCode(err) {
|
|
|
84408
84428
|
return typeof code === "string" ? code : "";
|
|
84409
84429
|
}
|
|
84410
84430
|
async function lookupSiteAddresses(hostname, timeoutMs = DNS_TIMEOUT_MS) {
|
|
84411
|
-
const literalFamily =
|
|
84431
|
+
const literalFamily = net4.isIPv6(hostname) ? 6 : net4.isIPv4(hostname) ? 4 : null;
|
|
84412
84432
|
if (literalFamily) return { kind: "addresses", addresses: [{ address: hostname, family: literalFamily }] };
|
|
84413
84433
|
let timer;
|
|
84414
84434
|
const deadline = new Promise((resolve) => {
|
|
@@ -86374,7 +86394,10 @@ var WORDPRESS_PUBLISH_CHECKS = [
|
|
|
86374
86394
|
};
|
|
86375
86395
|
}
|
|
86376
86396
|
try {
|
|
86377
|
-
const status = await verifyWordpressConnection(
|
|
86397
|
+
const status = await verifyWordpressConnection({
|
|
86398
|
+
...connection,
|
|
86399
|
+
fetchImpl: createGuardedFetch({ allowLoopback: ctx.allowLoopbackWebhooks === true })
|
|
86400
|
+
});
|
|
86378
86401
|
return {
|
|
86379
86402
|
status: CheckStatuses.ok,
|
|
86380
86403
|
code: "wordpress.publish.connected",
|
|
@@ -87023,6 +87046,7 @@ async function doctorRoutes(app, opts) {
|
|
|
87023
87046
|
googleConnectionStore: opts.googleConnectionStore,
|
|
87024
87047
|
bingConnectionStore: opts.bingConnectionStore,
|
|
87025
87048
|
wordpressConnectionStore: opts.wordpressConnectionStore,
|
|
87049
|
+
allowLoopbackWebhooks: opts.allowLoopbackWebhooks,
|
|
87026
87050
|
ga4CredentialStore: opts.ga4CredentialStore,
|
|
87027
87051
|
adsCredentialStore: opts.adsCredentialStore,
|
|
87028
87052
|
getGoogleAuthConfig: opts.getGoogleAuthConfig,
|
|
@@ -87056,6 +87080,7 @@ async function doctorRoutes(app, opts) {
|
|
|
87056
87080
|
googleConnectionStore: opts.googleConnectionStore,
|
|
87057
87081
|
bingConnectionStore: opts.bingConnectionStore,
|
|
87058
87082
|
wordpressConnectionStore: opts.wordpressConnectionStore,
|
|
87083
|
+
allowLoopbackWebhooks: opts.allowLoopbackWebhooks,
|
|
87059
87084
|
ga4CredentialStore: opts.ga4CredentialStore,
|
|
87060
87085
|
adsCredentialStore: opts.adsCredentialStore,
|
|
87061
87086
|
getGoogleAuthConfig: opts.getGoogleAuthConfig,
|
|
@@ -90964,6 +90989,7 @@ async function apiRoutes(app, opts) {
|
|
|
90964
90989
|
googleConnectionStore: opts.googleConnectionStore,
|
|
90965
90990
|
bingConnectionStore: opts.bingConnectionStore,
|
|
90966
90991
|
wordpressConnectionStore: opts.wordpressConnectionStore,
|
|
90992
|
+
allowLoopbackWebhooks: opts.allowLoopbackWebhooks,
|
|
90967
90993
|
ga4CredentialStore: opts.ga4CredentialStore,
|
|
90968
90994
|
adsCredentialStore: opts.adsCredentialStore,
|
|
90969
90995
|
getGoogleAuthConfig: opts.getGoogleAuthConfig,
|
|
@@ -91028,6 +91054,7 @@ function buildTrafficSourceValidators(opts) {
|
|
|
91028
91054
|
if (opts.wordpressTrafficCredentialStore) {
|
|
91029
91055
|
const store = opts.wordpressTrafficCredentialStore;
|
|
91030
91056
|
const pullEvents = opts.pullWordpressTrafficEvents ?? listWordpressTrafficEvents;
|
|
91057
|
+
const wordpressTrafficFetch = createGuardedFetch({ allowLoopback: opts.allowLoopbackWebhooks === true });
|
|
91031
91058
|
validators[TrafficSourceTypes.wordpress] = {
|
|
91032
91059
|
validateCredentials: async (source) => {
|
|
91033
91060
|
const record = store.getConnection(source.projectName);
|
|
@@ -91045,7 +91072,10 @@ function buildTrafficSourceValidators(opts) {
|
|
|
91045
91072
|
username: record.username,
|
|
91046
91073
|
applicationPassword: record.applicationPassword,
|
|
91047
91074
|
pageSize: 1,
|
|
91048
|
-
maxPages: 1
|
|
91075
|
+
maxPages: 1,
|
|
91076
|
+
// The probe carries Basic-auth credentials: check the stored URL
|
|
91077
|
+
// and every redirect hop as the sync route does.
|
|
91078
|
+
fetchImpl: wordpressTrafficFetch
|
|
91049
91079
|
});
|
|
91050
91080
|
return {
|
|
91051
91081
|
status: CheckStatuses.ok,
|
|
@@ -92319,6 +92349,7 @@ export {
|
|
|
92319
92349
|
determineCitationState,
|
|
92320
92350
|
computeCitedCompetitorDomains,
|
|
92321
92351
|
resolveTrustProxy,
|
|
92352
|
+
hasProxyHeaders,
|
|
92322
92353
|
USER_SESSION_COOKIE_NAME,
|
|
92323
92354
|
cookieIsSecure,
|
|
92324
92355
|
anyUsersExist,
|
|
@@ -92331,6 +92362,8 @@ export {
|
|
|
92331
92362
|
requireInstanceAdministrator,
|
|
92332
92363
|
hashApiKey,
|
|
92333
92364
|
notProbeRun,
|
|
92365
|
+
writeAuditLog,
|
|
92366
|
+
auditFromRequest,
|
|
92334
92367
|
registerAgentConversationRoutes,
|
|
92335
92368
|
measurementSlotKey,
|
|
92336
92369
|
measurementRunSlotState,
|
|
@@ -92392,6 +92425,9 @@ export {
|
|
|
92392
92425
|
classifyReviewsError,
|
|
92393
92426
|
getUrlInfo,
|
|
92394
92427
|
getCrawlIssues,
|
|
92428
|
+
EgressRefusedError,
|
|
92429
|
+
EgressFailedError,
|
|
92430
|
+
createGuardedFetch,
|
|
92395
92431
|
PLUGIN_DIR,
|
|
92396
92432
|
DUCKDB_SPEC,
|
|
92397
92433
|
CC_CACHE_DIR,
|