@ainyc/canonry 4.115.1 → 4.117.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/assets/agent-workspace/skills/aero/references/reporting.md +10 -0
- package/assets/agent-workspace/skills/canonry/references/canonry-cli.md +6 -0
- package/assets/assets/{BacklinksPage-yUb8oJuq.js → BacklinksPage-Bkafm10m.js} +1 -1
- package/assets/assets/{ChartPrimitives-CdQM3YDG.js → ChartPrimitives-NV_GgqFn.js} +1 -1
- package/assets/assets/ProjectPage-0-NnRxys.js +7 -0
- package/assets/assets/{RunRow-BU_x7EcP.js → RunRow-df0d24S6.js} +1 -1
- package/assets/assets/{RunsPage-CFIBWaO7.js → RunsPage-CU6-4YTM.js} +1 -1
- package/assets/assets/{SettingsPage-PyMryUad.js → SettingsPage-BKw6B6cd.js} +1 -1
- package/assets/assets/{TrafficPage-471ypAdQ.js → TrafficPage-CzpiYHKj.js} +1 -1
- package/assets/assets/{TrafficSourceDetailPage-CI7TEQge.js → TrafficSourceDetailPage-CC5nuqM1.js} +1 -1
- package/assets/assets/{arrow-left-D2N9u-0i.js → arrow-left-CbQcW2pL.js} +1 -1
- package/assets/assets/{extract-error-message-CAdsqKp3.js → extract-error-message-CvqIZ-le.js} +1 -1
- package/assets/assets/index-LjcWHaNZ.js +210 -0
- package/assets/assets/{trash-2-UgQrN5Np.js → trash-2-3J2u0woR.js} +1 -1
- package/assets/index.html +1 -1
- package/dist/{chunk-Z3OGDQHB.js → chunk-FVVKNMZW.js} +514 -52
- package/dist/{chunk-6LOKMEL3.js → chunk-IAJ5TQ4S.js} +1438 -1234
- package/dist/{chunk-CW4JISBL.js → chunk-PJATAPZW.js} +37 -1
- package/dist/{chunk-B2SNXXAJ.js → chunk-ZZEA774R.js} +4 -4
- package/dist/cli.js +98 -5
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-4AWB2V7U.js → intelligence-service-J7G4CMI5.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +6 -6
- package/assets/assets/ProjectPage-CHj9fvTs.js +0 -7
- package/assets/assets/index-CoL5HENE.js +0 -210
|
@@ -1998,7 +1998,7 @@ var DEDUP_SIMILARITY_BAND_HIGH = 0.97;
|
|
|
1998
1998
|
function computeDedupSimilarityStats(vectors, indexClusters) {
|
|
1999
1999
|
const n = vectors.length;
|
|
2000
2000
|
if (n < 2) return { perClusterMinSimilarity: [], bandPairFraction: null, pairsTotal: 0 };
|
|
2001
|
-
const
|
|
2001
|
+
const round2 = (v) => Math.round(v * 1e4) / 1e4;
|
|
2002
2002
|
const perClusterMinSimilarity = [];
|
|
2003
2003
|
for (const cluster of indexClusters) {
|
|
2004
2004
|
if (cluster.length < 2) continue;
|
|
@@ -2008,7 +2008,7 @@ function computeDedupSimilarityStats(vectors, indexClusters) {
|
|
|
2008
2008
|
min = Math.min(min, cosineSimilarity(vectors[cluster[a]], vectors[cluster[b]]));
|
|
2009
2009
|
}
|
|
2010
2010
|
}
|
|
2011
|
-
perClusterMinSimilarity.push(
|
|
2011
|
+
perClusterMinSimilarity.push(round2(min));
|
|
2012
2012
|
}
|
|
2013
2013
|
let inBand = 0;
|
|
2014
2014
|
let pairsTotal = 0;
|
|
@@ -2019,7 +2019,7 @@ function computeDedupSimilarityStats(vectors, indexClusters) {
|
|
|
2019
2019
|
if (sim >= DEDUP_SIMILARITY_BAND_LOW && sim < DEDUP_SIMILARITY_BAND_HIGH) inBand++;
|
|
2020
2020
|
}
|
|
2021
2021
|
}
|
|
2022
|
-
return { perClusterMinSimilarity, bandPairFraction:
|
|
2022
|
+
return { perClusterMinSimilarity, bandPairFraction: round2(inBand / pairsTotal), pairsTotal };
|
|
2023
2023
|
}
|
|
2024
2024
|
|
|
2025
2025
|
// ../contracts/src/url-normalize.ts
|
|
@@ -2894,6 +2894,137 @@ var visibilityStatsDtoSchema = z20.object({
|
|
|
2894
2894
|
/** Pooled share of voice vs tracked competitors — present only when `shareOfVoice=1` was requested. */
|
|
2895
2895
|
shareOfVoice: visibilityStatsShareOfVoiceSchema.optional()
|
|
2896
2896
|
});
|
|
2897
|
+
var visibilityCompareVerdictSchema = z20.enum([
|
|
2898
|
+
"within-noise",
|
|
2899
|
+
"moved",
|
|
2900
|
+
"insufficient-data",
|
|
2901
|
+
"model-discontinuous",
|
|
2902
|
+
"model-unknown"
|
|
2903
|
+
]);
|
|
2904
|
+
var visibilityCompareDirectionSchema = z20.enum(["up", "down", "flat"]);
|
|
2905
|
+
var visibilityCompareMetricPeriodSchema = z20.object({
|
|
2906
|
+
/** The proportion in `[0,1]`, rounded to 4 dp; `null` when `denominator === 0` (undefined over no data). */
|
|
2907
|
+
point: z20.number().nullable(),
|
|
2908
|
+
/** Wilson 95% lower bound `[0,1]`; `null` when `denominator === 0`. */
|
|
2909
|
+
ciLow: z20.number().nullable(),
|
|
2910
|
+
/** Wilson 95% upper bound `[0,1]`; `null` when `denominator === 0`. */
|
|
2911
|
+
ciHigh: z20.number().nullable(),
|
|
2912
|
+
/** Successes (mentions / citations / project-brand mentions). */
|
|
2913
|
+
numerator: z20.number().int(),
|
|
2914
|
+
/** Sample size the proportion is over (checked snapshots / total / project+competitor brand mentions). */
|
|
2915
|
+
denominator: z20.number().int()
|
|
2916
|
+
});
|
|
2917
|
+
var visibilityCompareMetricKeySchema = z20.enum([
|
|
2918
|
+
"mention-share-of-voice",
|
|
2919
|
+
"cited-share-of-voice",
|
|
2920
|
+
"mention-rate",
|
|
2921
|
+
"cited-rate"
|
|
2922
|
+
]);
|
|
2923
|
+
var visibilityCompareMetricSchema = z20.object({
|
|
2924
|
+
key: visibilityCompareMetricKeySchema,
|
|
2925
|
+
/** Human label ("Named share of voice", "Cited rate", …). */
|
|
2926
|
+
label: z20.string(),
|
|
2927
|
+
/**
|
|
2928
|
+
* `true` for share of voice, which is less exposed to broad model-wide naming
|
|
2929
|
+
* propensity than an absolute rate. It does not bypass model continuity: no
|
|
2930
|
+
* metric can make a directional call when every provider is excluded.
|
|
2931
|
+
*/
|
|
2932
|
+
driftRobust: z20.boolean(),
|
|
2933
|
+
from: visibilityCompareMetricPeriodSchema,
|
|
2934
|
+
to: visibilityCompareMetricPeriodSchema,
|
|
2935
|
+
/** `to.point / from.point`; `null` when `from.point` is `0` or `null` (ratio undefined). */
|
|
2936
|
+
rateRatio: z20.number().nullable(),
|
|
2937
|
+
/** Sign of `to.point - from.point`; `null` when either point is `null`. */
|
|
2938
|
+
direction: z20.union([visibilityCompareDirectionSchema, z20.null()]),
|
|
2939
|
+
verdict: visibilityCompareVerdictSchema
|
|
2940
|
+
});
|
|
2941
|
+
var visibilityComparePeriodWindowSchema = z20.object({
|
|
2942
|
+
/** The `YYYY-MM` requested. */
|
|
2943
|
+
month: z20.string(),
|
|
2944
|
+
/** Resolved inclusive ISO bounds the month expanded to. */
|
|
2945
|
+
since: z20.string(),
|
|
2946
|
+
until: z20.string(),
|
|
2947
|
+
/** Completed/partial, non-probe answer-visibility runs in the month. */
|
|
2948
|
+
runCount: z20.number().int(),
|
|
2949
|
+
/**
|
|
2950
|
+
* `true` when `runCount` is below the reliability floor (`< 5` sweeps): the
|
|
2951
|
+
* intervals are wide and a `moved` verdict is unlikely to be reachable. A
|
|
2952
|
+
* signal to raise the sweep schedule, surfaced so the report can caveat.
|
|
2953
|
+
*/
|
|
2954
|
+
lowRunCount: z20.boolean()
|
|
2955
|
+
});
|
|
2956
|
+
var visibilityCompareBasketSchema = z20.object({
|
|
2957
|
+
/** Distinct tracked queries with at least one common provider pair — the compared universe. */
|
|
2958
|
+
queryCount: z20.number().int(),
|
|
2959
|
+
/** Queries observed only in `from` (dropped from the comparison). */
|
|
2960
|
+
excludedFromOnly: z20.number().int(),
|
|
2961
|
+
/** Queries observed only in `to`. */
|
|
2962
|
+
excludedToOnly: z20.number().int(),
|
|
2963
|
+
/**
|
|
2964
|
+
* The compared engine set: providers with ≥1 common query/provider pair.
|
|
2965
|
+
*/
|
|
2966
|
+
providers: z20.array(z20.string()),
|
|
2967
|
+
/** Providers observed in either period that did not make the basket (dropped from the comparison). */
|
|
2968
|
+
excludedProviders: z20.array(z20.string())
|
|
2969
|
+
});
|
|
2970
|
+
var visibilityCompareModelChangeSchema = z20.object({
|
|
2971
|
+
provider: z20.string(),
|
|
2972
|
+
fromModels: z20.array(z20.string()),
|
|
2973
|
+
toModels: z20.array(z20.string())
|
|
2974
|
+
});
|
|
2975
|
+
var visibilityCompareProviderContinuityStatusSchema = z20.enum([
|
|
2976
|
+
"included",
|
|
2977
|
+
"model-discontinuous",
|
|
2978
|
+
"model-unknown"
|
|
2979
|
+
]);
|
|
2980
|
+
var visibilityCompareProviderContinuitySchema = z20.object({
|
|
2981
|
+
provider: z20.string(),
|
|
2982
|
+
/** `included` only when both months carry exactly one known model id, identical across the two, and NO snapshot with an unrecorded (null) model — a null mixed alongside a known id still marks the period `model-unknown`. */
|
|
2983
|
+
status: visibilityCompareProviderContinuityStatusSchema,
|
|
2984
|
+
fromModels: z20.array(z20.string()),
|
|
2985
|
+
toModels: z20.array(z20.string())
|
|
2986
|
+
});
|
|
2987
|
+
var visibilityCompareContinuityStatusSchema = z20.enum([
|
|
2988
|
+
"comparable",
|
|
2989
|
+
"model-discontinuous",
|
|
2990
|
+
"model-unknown",
|
|
2991
|
+
"insufficient-data"
|
|
2992
|
+
]);
|
|
2993
|
+
var visibilityCompareContinuitySchema = z20.object({
|
|
2994
|
+
status: visibilityCompareContinuityStatusSchema,
|
|
2995
|
+
/** Providers retained in the directional comparison after the continuity gate. */
|
|
2996
|
+
comparedProviders: z20.array(z20.string()),
|
|
2997
|
+
/** Every provider with common query/provider pairs, including excluded ones and their model evidence. */
|
|
2998
|
+
providers: z20.array(visibilityCompareProviderContinuitySchema)
|
|
2999
|
+
});
|
|
3000
|
+
var visibilityCompareProviderRowSchema = z20.object({
|
|
3001
|
+
provider: z20.string(),
|
|
3002
|
+
from: z20.object({ checked: z20.number().int(), mentioned: z20.number().int(), cited: z20.number().int() }),
|
|
3003
|
+
to: z20.object({ checked: z20.number().int(), mentioned: z20.number().int(), cited: z20.number().int() })
|
|
3004
|
+
});
|
|
3005
|
+
var visibilityCompareQueriesMentionedSchema = z20.object({
|
|
3006
|
+
from: z20.object({ count: z20.number().int(), of: z20.number().int() }),
|
|
3007
|
+
to: z20.object({ count: z20.number().int(), of: z20.number().int() })
|
|
3008
|
+
});
|
|
3009
|
+
var visibilityCompareDtoSchema = z20.object({
|
|
3010
|
+
project: z20.string(),
|
|
3011
|
+
from: visibilityComparePeriodWindowSchema,
|
|
3012
|
+
to: visibilityComparePeriodWindowSchema,
|
|
3013
|
+
basket: visibilityCompareBasketSchema,
|
|
3014
|
+
/** Ordered: mention SoV (primary), cited SoV, mention rate (level), cited rate. */
|
|
3015
|
+
metrics: z20.array(visibilityCompareMetricSchema),
|
|
3016
|
+
queriesMentioned: visibilityCompareQueriesMentionedSchema,
|
|
3017
|
+
byProvider: z20.array(visibilityCompareProviderRowSchema),
|
|
3018
|
+
/** Providers whose configured model id set changed between the periods (empty = none detected). */
|
|
3019
|
+
modelChanges: z20.array(visibilityCompareModelChangeSchema),
|
|
3020
|
+
/** Strict model-continuity evidence and the providers that remain comparable. */
|
|
3021
|
+
continuity: visibilityCompareContinuitySchema,
|
|
3022
|
+
/** Per-competitor mention counts within each period's basket (for the SoV detail). */
|
|
3023
|
+
competitors: z20.object({
|
|
3024
|
+
from: z20.array(visibilityStatsShareCompetitorSchema),
|
|
3025
|
+
to: z20.array(visibilityStatsShareCompetitorSchema)
|
|
3026
|
+
})
|
|
3027
|
+
});
|
|
2897
3028
|
function calendarMonthBounds(month) {
|
|
2898
3029
|
const match = /^(\d{4})-(\d{2})$/.exec(month);
|
|
2899
3030
|
if (!match) throw new RangeError("month must be in YYYY-MM format");
|
|
@@ -2907,9 +3038,98 @@ function calendarMonthBounds(month) {
|
|
|
2907
3038
|
}
|
|
2908
3039
|
|
|
2909
3040
|
// ../contracts/src/ga.ts
|
|
3041
|
+
import { z as z22 } from "zod";
|
|
3042
|
+
|
|
3043
|
+
// ../contracts/src/traffic-class.ts
|
|
2910
3044
|
import { z as z21 } from "zod";
|
|
2911
|
-
|
|
2912
|
-
|
|
3045
|
+
|
|
3046
|
+
// ../contracts/src/formatting.ts
|
|
3047
|
+
function formatRatio(value) {
|
|
3048
|
+
if (!Number.isFinite(value) || value === 0) return "0%";
|
|
3049
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
3050
|
+
}
|
|
3051
|
+
function formatNumber(value) {
|
|
3052
|
+
if (!Number.isFinite(value)) return "\u2014";
|
|
3053
|
+
if (Math.abs(value) >= 1e6) return `${(value / 1e6).toFixed(1)}M`;
|
|
3054
|
+
if (Math.abs(value) >= 1e3) return `${(value / 1e3).toFixed(1)}K`;
|
|
3055
|
+
return value.toLocaleString("en-US");
|
|
3056
|
+
}
|
|
3057
|
+
function formatDate(iso) {
|
|
3058
|
+
if (!iso) return "\u2014";
|
|
3059
|
+
try {
|
|
3060
|
+
const dateOnly = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
|
|
3061
|
+
const options = { month: "short", day: "numeric", year: "numeric" };
|
|
3062
|
+
const d = dateOnly && dateOnly[1] && dateOnly[2] && dateOnly[3] ? new Date(Date.UTC(Number(dateOnly[1]), Number(dateOnly[2]) - 1, Number(dateOnly[3]))) : new Date(iso);
|
|
3063
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
3064
|
+
return d.toLocaleDateString("en-US", dateOnly ? { ...options, timeZone: "UTC" } : options);
|
|
3065
|
+
} catch {
|
|
3066
|
+
return iso;
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
function formatIsoDate(iso) {
|
|
3070
|
+
if (!iso) return "\u2014";
|
|
3071
|
+
try {
|
|
3072
|
+
const d = new Date(iso);
|
|
3073
|
+
if (Number.isNaN(d.getTime())) return iso;
|
|
3074
|
+
const yyyy = d.getUTCFullYear();
|
|
3075
|
+
const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
3076
|
+
const dd = String(d.getUTCDate()).padStart(2, "0");
|
|
3077
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
3078
|
+
} catch {
|
|
3079
|
+
return iso;
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
function formatDateRange(start, end) {
|
|
3083
|
+
if (!start && !end) return "";
|
|
3084
|
+
if (start && end) return `${formatDate(start)} \u2192 ${formatDate(end)}`;
|
|
3085
|
+
return formatDate(start || end);
|
|
3086
|
+
}
|
|
3087
|
+
var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
3088
|
+
function parseInclusiveEndMs(iso) {
|
|
3089
|
+
const ms = Date.parse(iso);
|
|
3090
|
+
if (Number.isNaN(ms)) return null;
|
|
3091
|
+
return DATE_ONLY_PATTERN.test(iso) ? ms + 864e5 - 1 : ms;
|
|
3092
|
+
}
|
|
3093
|
+
function deltaPercent(current, prior) {
|
|
3094
|
+
if (prior <= 0) return null;
|
|
3095
|
+
return Math.round((current - prior) / prior * 100);
|
|
3096
|
+
}
|
|
3097
|
+
function deltaTone(deltaPct) {
|
|
3098
|
+
if (deltaPct === null || deltaPct === 0) return "neutral";
|
|
3099
|
+
return deltaPct > 0 ? "positive" : "negative";
|
|
3100
|
+
}
|
|
3101
|
+
function formatDeltaCopy(d, suffix, windowLabel = "vs prior 7 days") {
|
|
3102
|
+
if (d.deltaPct === null) {
|
|
3103
|
+
return d.prior === 0 ? "First baseline week" : "";
|
|
3104
|
+
}
|
|
3105
|
+
if (d.deltaPct > 0) return `Up ${d.deltaPct}% ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
|
|
3106
|
+
if (d.deltaPct < 0) return `Down ${Math.abs(d.deltaPct)}% ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
|
|
3107
|
+
return `Flat ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
|
|
3108
|
+
}
|
|
3109
|
+
var MIN_PCT_BASE = 30;
|
|
3110
|
+
function round1(value) {
|
|
3111
|
+
return Math.round(value * 10) / 10;
|
|
3112
|
+
}
|
|
3113
|
+
function formatAverageDelta(d) {
|
|
3114
|
+
if (d.prior >= MIN_PCT_BASE && d.deltaPct !== null) {
|
|
3115
|
+
const sign2 = d.deltaPct > 0 ? "+" : "";
|
|
3116
|
+
return `${sign2}${d.deltaPct}% vs prior`;
|
|
3117
|
+
}
|
|
3118
|
+
const sign = d.deltaAbs > 0 ? "+" : "";
|
|
3119
|
+
return `${sign}${round1(d.deltaAbs)} vs ${round1(d.prior)}`;
|
|
3120
|
+
}
|
|
3121
|
+
function formatWindowCountDelta(d, countLabel, windowLabel) {
|
|
3122
|
+
if (d.prior >= MIN_PCT_BASE && d.deltaPct !== null) {
|
|
3123
|
+
const sign2 = d.deltaPct > 0 ? "+" : "";
|
|
3124
|
+
return `${sign2}${d.deltaPct}% ${windowLabel}`;
|
|
3125
|
+
}
|
|
3126
|
+
const sign = d.deltaAbs > 0 ? "+" : "";
|
|
3127
|
+
return `${sign}${formatNumber(Math.round(d.deltaAbs))} ${countLabel} ${windowLabel}`;
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
// ../contracts/src/traffic-class.ts
|
|
3131
|
+
var aiReferralTrafficClassSchema = z21.enum(["organic", "paid"]);
|
|
3132
|
+
var AiReferralTrafficClasses = aiReferralTrafficClassSchema.enum;
|
|
2913
3133
|
var PAID_CHANNEL_GROUPS = /* @__PURE__ */ new Set([
|
|
2914
3134
|
"paid search",
|
|
2915
3135
|
"paid social",
|
|
@@ -2956,39 +3176,51 @@ function hasPaidLandingPageParam(landingPage) {
|
|
|
2956
3176
|
return false;
|
|
2957
3177
|
}
|
|
2958
3178
|
}
|
|
2959
|
-
function
|
|
3179
|
+
function classifyAiReferralTrafficClass(input) {
|
|
2960
3180
|
const channelGroup = (input.channelGroup ?? "").trim().toLowerCase();
|
|
2961
3181
|
if (PAID_CHANNEL_GROUPS.has(channelGroup) || channelGroup.startsWith("paid ")) {
|
|
2962
|
-
return
|
|
3182
|
+
return AiReferralTrafficClasses.paid;
|
|
2963
3183
|
}
|
|
2964
3184
|
if (hasPaidToken(input.medium) || hasPaidToken(input.source) || hasPaidLandingPageParam(input.landingPage)) {
|
|
2965
|
-
return
|
|
3185
|
+
return AiReferralTrafficClasses.paid;
|
|
2966
3186
|
}
|
|
2967
|
-
return
|
|
2968
|
-
}
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
3187
|
+
return AiReferralTrafficClasses.organic;
|
|
3188
|
+
}
|
|
3189
|
+
function aiReferralClassCounts(total, paid, organic) {
|
|
3190
|
+
return { total, paid, organic, unknown: Math.max(0, total - paid - organic) };
|
|
3191
|
+
}
|
|
3192
|
+
function formatAiReferralClassSummary(counts) {
|
|
3193
|
+
const parts = [];
|
|
3194
|
+
if (counts.paid > 0) parts.push(`Paid ${formatNumber(counts.paid)}`);
|
|
3195
|
+
if (counts.organic > 0) parts.push(`Organic ${formatNumber(counts.organic)}`);
|
|
3196
|
+
if (counts.unknown > 0) parts.push(`Unclassified ${formatNumber(counts.unknown)}`);
|
|
3197
|
+
return parts.join(" \xB7 ");
|
|
3198
|
+
}
|
|
3199
|
+
|
|
3200
|
+
// ../contracts/src/ga.ts
|
|
3201
|
+
var ga4ConnectionDtoSchema = z22.object({
|
|
3202
|
+
id: z22.string(),
|
|
3203
|
+
projectId: z22.string(),
|
|
3204
|
+
propertyId: z22.string(),
|
|
3205
|
+
clientEmail: z22.string(),
|
|
3206
|
+
connected: z22.boolean(),
|
|
3207
|
+
createdAt: z22.string(),
|
|
3208
|
+
updatedAt: z22.string()
|
|
3209
|
+
});
|
|
3210
|
+
var ga4TrafficSnapshotDtoSchema = z22.object({
|
|
3211
|
+
date: z22.string(),
|
|
3212
|
+
landingPage: z22.string(),
|
|
3213
|
+
sessions: z22.number(),
|
|
3214
|
+
organicSessions: z22.number(),
|
|
3215
|
+
users: z22.number()
|
|
3216
|
+
});
|
|
3217
|
+
var ga4SourceDimensionSchema = z22.enum(["session", "first_user", "manual_utm"]);
|
|
3218
|
+
var ga4AiReferralDtoSchema = z22.object({
|
|
3219
|
+
source: z22.string(),
|
|
3220
|
+
medium: z22.string(),
|
|
3221
|
+
trafficClass: aiReferralTrafficClassSchema,
|
|
3222
|
+
sessions: z22.number(),
|
|
3223
|
+
users: z22.number(),
|
|
2992
3224
|
/**
|
|
2993
3225
|
* The winning attribution dimension for this (source, medium) tuple — the
|
|
2994
3226
|
* one with the highest session count. GA4 emits one row per dimension
|
|
@@ -2998,178 +3230,178 @@ var ga4AiReferralDtoSchema = z21.object({
|
|
|
2998
3230
|
*/
|
|
2999
3231
|
sourceDimension: ga4SourceDimensionSchema
|
|
3000
3232
|
});
|
|
3001
|
-
var ga4AiReferralLandingPageDtoSchema =
|
|
3002
|
-
source:
|
|
3003
|
-
medium:
|
|
3004
|
-
trafficClass:
|
|
3233
|
+
var ga4AiReferralLandingPageDtoSchema = z22.object({
|
|
3234
|
+
source: z22.string(),
|
|
3235
|
+
medium: z22.string(),
|
|
3236
|
+
trafficClass: aiReferralTrafficClassSchema,
|
|
3005
3237
|
/**
|
|
3006
3238
|
* The winning attribution dimension for this (source, medium, landingPage)
|
|
3007
3239
|
* tuple — the one with the highest session count.
|
|
3008
3240
|
*/
|
|
3009
3241
|
sourceDimension: ga4SourceDimensionSchema,
|
|
3010
|
-
landingPage:
|
|
3011
|
-
sessions:
|
|
3012
|
-
users:
|
|
3013
|
-
});
|
|
3014
|
-
var ga4SocialReferralDtoSchema =
|
|
3015
|
-
source:
|
|
3016
|
-
medium:
|
|
3017
|
-
sessions:
|
|
3018
|
-
users:
|
|
3242
|
+
landingPage: z22.string(),
|
|
3243
|
+
sessions: z22.number(),
|
|
3244
|
+
users: z22.number()
|
|
3245
|
+
});
|
|
3246
|
+
var ga4SocialReferralDtoSchema = z22.object({
|
|
3247
|
+
source: z22.string(),
|
|
3248
|
+
medium: z22.string(),
|
|
3249
|
+
sessions: z22.number(),
|
|
3250
|
+
users: z22.number(),
|
|
3019
3251
|
/** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
|
|
3020
|
-
channelGroup:
|
|
3252
|
+
channelGroup: z22.string()
|
|
3021
3253
|
});
|
|
3022
|
-
var ga4ChannelBucketDtoSchema =
|
|
3023
|
-
sessions:
|
|
3024
|
-
sharePct:
|
|
3025
|
-
sharePctDisplay:
|
|
3254
|
+
var ga4ChannelBucketDtoSchema = z22.object({
|
|
3255
|
+
sessions: z22.number(),
|
|
3256
|
+
sharePct: z22.number(),
|
|
3257
|
+
sharePctDisplay: z22.string()
|
|
3026
3258
|
});
|
|
3027
|
-
var ga4ChannelBreakdownDtoSchema =
|
|
3259
|
+
var ga4ChannelBreakdownDtoSchema = z22.object({
|
|
3028
3260
|
organic: ga4ChannelBucketDtoSchema,
|
|
3029
3261
|
social: ga4ChannelBucketDtoSchema,
|
|
3030
3262
|
direct: ga4ChannelBucketDtoSchema,
|
|
3031
3263
|
ai: ga4ChannelBucketDtoSchema,
|
|
3032
3264
|
other: ga4ChannelBucketDtoSchema
|
|
3033
3265
|
});
|
|
3034
|
-
var ga4TrafficSummaryDtoSchema =
|
|
3035
|
-
totalSessions:
|
|
3036
|
-
totalOrganicSessions:
|
|
3266
|
+
var ga4TrafficSummaryDtoSchema = z22.object({
|
|
3267
|
+
totalSessions: z22.number(),
|
|
3268
|
+
totalOrganicSessions: z22.number(),
|
|
3037
3269
|
/** Direct-channel sessions (sessions with no source — bookmarks, typed URLs, AI-driven traffic with stripped referrer). 0 for legacy rows from before the column was added. */
|
|
3038
|
-
totalDirectSessions:
|
|
3039
|
-
totalUsers:
|
|
3040
|
-
topPages:
|
|
3041
|
-
landingPage:
|
|
3042
|
-
sessions:
|
|
3043
|
-
organicSessions:
|
|
3270
|
+
totalDirectSessions: z22.number(),
|
|
3271
|
+
totalUsers: z22.number(),
|
|
3272
|
+
topPages: z22.array(z22.object({
|
|
3273
|
+
landingPage: z22.string(),
|
|
3274
|
+
sessions: z22.number(),
|
|
3275
|
+
organicSessions: z22.number(),
|
|
3044
3276
|
/** Per-page Direct-channel sessions. 0 for legacy rows. */
|
|
3045
|
-
directSessions:
|
|
3046
|
-
users:
|
|
3277
|
+
directSessions: z22.number(),
|
|
3278
|
+
users: z22.number()
|
|
3047
3279
|
})),
|
|
3048
|
-
aiReferrals:
|
|
3049
|
-
aiReferralLandingPages:
|
|
3280
|
+
aiReferrals: z22.array(ga4AiReferralDtoSchema),
|
|
3281
|
+
aiReferralLandingPages: z22.array(ga4AiReferralLandingPageDtoSchema),
|
|
3050
3282
|
/** Deduped AI session total: MAX(sessions) per date+source+medium across attribution dimensions, then summed. Cross-cutting: can overlap with Direct/Organic/Social via firstUserSource. */
|
|
3051
|
-
aiSessionsDeduped:
|
|
3283
|
+
aiSessionsDeduped: z22.number(),
|
|
3052
3284
|
/** Deduped AI user total: MAX(users) per date+source+medium across attribution dimensions, then summed. */
|
|
3053
|
-
aiUsersDeduped:
|
|
3285
|
+
aiUsersDeduped: z22.number(),
|
|
3054
3286
|
/** Deduped AI sessions whose attribution carries paid intent. */
|
|
3055
|
-
paidAiSessionsDeduped:
|
|
3287
|
+
paidAiSessionsDeduped: z22.number(),
|
|
3056
3288
|
/** Deduped users for paid AI sessions. */
|
|
3057
|
-
paidAiUsersDeduped:
|
|
3289
|
+
paidAiUsersDeduped: z22.number(),
|
|
3058
3290
|
/** Deduped AI sessions without paid intent evidence. */
|
|
3059
|
-
organicAiSessionsDeduped:
|
|
3291
|
+
organicAiSessionsDeduped: z22.number(),
|
|
3060
3292
|
/** Deduped users for organic/non-paid AI sessions. */
|
|
3061
|
-
organicAiUsersDeduped:
|
|
3293
|
+
organicAiUsersDeduped: z22.number(),
|
|
3062
3294
|
/** AI sessions whose CURRENT sessionSource matched an AI engine. Can overlap with raw Organic/Social/Direct totals; `channelBreakdown` removes those overlaps for display. */
|
|
3063
|
-
aiSessionsBySession:
|
|
3295
|
+
aiSessionsBySession: z22.number(),
|
|
3064
3296
|
/** AI users whose CURRENT sessionSource matched an AI engine. Can overlap with raw Organic/Social/Direct totals. */
|
|
3065
|
-
aiUsersBySession:
|
|
3297
|
+
aiUsersBySession: z22.number(),
|
|
3066
3298
|
/** Session-source-only paid AI sessions. */
|
|
3067
|
-
paidAiSessionsBySession:
|
|
3299
|
+
paidAiSessionsBySession: z22.number(),
|
|
3068
3300
|
/** Session-source-only paid AI users. */
|
|
3069
|
-
paidAiUsersBySession:
|
|
3301
|
+
paidAiUsersBySession: z22.number(),
|
|
3070
3302
|
/** Session-source-only organic/non-paid AI sessions. */
|
|
3071
|
-
organicAiSessionsBySession:
|
|
3303
|
+
organicAiSessionsBySession: z22.number(),
|
|
3072
3304
|
/** Session-source-only organic/non-paid AI users. */
|
|
3073
|
-
organicAiUsersBySession:
|
|
3074
|
-
socialReferrals:
|
|
3305
|
+
organicAiUsersBySession: z22.number(),
|
|
3306
|
+
socialReferrals: z22.array(ga4SocialReferralDtoSchema),
|
|
3075
3307
|
/** Total social sessions (session-scoped, no cross-dimension dedup needed). */
|
|
3076
|
-
socialSessions:
|
|
3308
|
+
socialSessions: z22.number(),
|
|
3077
3309
|
/** Total social users (session-scoped, no cross-dimension dedup needed). */
|
|
3078
|
-
socialUsers:
|
|
3310
|
+
socialUsers: z22.number(),
|
|
3079
3311
|
/** Five disjoint buckets used for the channel breakdown. Known AI session-source matches are removed from their native GA4 bucket before shares are computed. */
|
|
3080
3312
|
channelBreakdown: ga4ChannelBreakdownDtoSchema,
|
|
3081
3313
|
/** Organic sessions as a percentage of total sessions (0–100, rounded). */
|
|
3082
|
-
organicSharePct:
|
|
3314
|
+
organicSharePct: z22.number(),
|
|
3083
3315
|
/** Deduped AI sessions as a percentage of total sessions (0–100, rounded). Cross-cutting: can overlap with Direct/Organic/Social. */
|
|
3084
|
-
aiSharePct:
|
|
3316
|
+
aiSharePct: z22.number(),
|
|
3085
3317
|
/** Session-source-only AI sessions as a percentage of total sessions (0–100, rounded). Can overlap with raw Organic/Social/Direct totals. */
|
|
3086
|
-
aiSharePctBySession:
|
|
3318
|
+
aiSharePctBySession: z22.number(),
|
|
3087
3319
|
/** Paid AI sessions as a percentage of total sessions (0–100, rounded). */
|
|
3088
|
-
paidAiSharePct:
|
|
3320
|
+
paidAiSharePct: z22.number(),
|
|
3089
3321
|
/** Session-source paid AI sessions as a percentage of total sessions (0–100, rounded). */
|
|
3090
|
-
paidAiSharePctBySession:
|
|
3322
|
+
paidAiSharePctBySession: z22.number(),
|
|
3091
3323
|
/** Organic/non-paid AI sessions as a percentage of total sessions (0–100, rounded). */
|
|
3092
|
-
organicAiSharePct:
|
|
3324
|
+
organicAiSharePct: z22.number(),
|
|
3093
3325
|
/** Session-source organic/non-paid AI sessions as a percentage of total sessions (0–100, rounded). */
|
|
3094
|
-
organicAiSharePctBySession:
|
|
3326
|
+
organicAiSharePctBySession: z22.number(),
|
|
3095
3327
|
/** Direct-channel sessions as a percentage of total sessions (0–100, rounded). */
|
|
3096
|
-
directSharePct:
|
|
3328
|
+
directSharePct: z22.number(),
|
|
3097
3329
|
/** Social sessions as a percentage of total sessions (0–100, rounded). */
|
|
3098
|
-
socialSharePct:
|
|
3330
|
+
socialSharePct: z22.number(),
|
|
3099
3331
|
/** Display string for organicSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
3100
|
-
organicSharePctDisplay:
|
|
3332
|
+
organicSharePctDisplay: z22.string(),
|
|
3101
3333
|
/** Display string for aiSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
3102
|
-
aiSharePctDisplay:
|
|
3334
|
+
aiSharePctDisplay: z22.string(),
|
|
3103
3335
|
/** Display string for aiSharePctBySession: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
3104
|
-
aiSharePctBySessionDisplay:
|
|
3336
|
+
aiSharePctBySessionDisplay: z22.string(),
|
|
3105
3337
|
/** Display string for paidAiSharePct. */
|
|
3106
|
-
paidAiSharePctDisplay:
|
|
3338
|
+
paidAiSharePctDisplay: z22.string(),
|
|
3107
3339
|
/** Display string for paidAiSharePctBySession. */
|
|
3108
|
-
paidAiSharePctBySessionDisplay:
|
|
3340
|
+
paidAiSharePctBySessionDisplay: z22.string(),
|
|
3109
3341
|
/** Display string for organicAiSharePct. */
|
|
3110
|
-
organicAiSharePctDisplay:
|
|
3342
|
+
organicAiSharePctDisplay: z22.string(),
|
|
3111
3343
|
/** Display string for organicAiSharePctBySession. */
|
|
3112
|
-
organicAiSharePctBySessionDisplay:
|
|
3344
|
+
organicAiSharePctBySessionDisplay: z22.string(),
|
|
3113
3345
|
/** Display string for directSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
3114
|
-
directSharePctDisplay:
|
|
3346
|
+
directSharePctDisplay: z22.string(),
|
|
3115
3347
|
/** Display string for socialSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
3116
|
-
socialSharePctDisplay:
|
|
3348
|
+
socialSharePctDisplay: z22.string(),
|
|
3117
3349
|
/** Sessions not covered by Organic, Social, Direct, or AI (session) channels — e.g. Referral, Email, Paid Search, Display. Always non-negative; clamped to 0 when the four disjoint channels sum above total (rounding edge). */
|
|
3118
|
-
otherSessions:
|
|
3350
|
+
otherSessions: z22.number(),
|
|
3119
3351
|
/** Other sessions as a percentage of total sessions (0–100, rounded). */
|
|
3120
|
-
otherSharePct:
|
|
3352
|
+
otherSharePct: z22.number(),
|
|
3121
3353
|
/** Display string for otherSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
3122
|
-
otherSharePctDisplay:
|
|
3123
|
-
lastSyncedAt:
|
|
3124
|
-
});
|
|
3125
|
-
var ga4StatusDtoSchema =
|
|
3126
|
-
connected:
|
|
3127
|
-
propertyId:
|
|
3128
|
-
clientEmail:
|
|
3129
|
-
authMethod:
|
|
3130
|
-
lastSyncedAt:
|
|
3131
|
-
createdAt:
|
|
3132
|
-
updatedAt:
|
|
3133
|
-
});
|
|
3134
|
-
var ga4SyncResponseDtoSchema =
|
|
3135
|
-
synced:
|
|
3136
|
-
rowCount:
|
|
3137
|
-
aiReferralCount:
|
|
3138
|
-
socialReferralCount:
|
|
3139
|
-
days:
|
|
3140
|
-
syncedAt:
|
|
3354
|
+
otherSharePctDisplay: z22.string(),
|
|
3355
|
+
lastSyncedAt: z22.string().nullable()
|
|
3356
|
+
});
|
|
3357
|
+
var ga4StatusDtoSchema = z22.object({
|
|
3358
|
+
connected: z22.boolean(),
|
|
3359
|
+
propertyId: z22.string().nullable(),
|
|
3360
|
+
clientEmail: z22.string().nullable(),
|
|
3361
|
+
authMethod: z22.enum(["service-account", "oauth"]).nullable(),
|
|
3362
|
+
lastSyncedAt: z22.string().nullable(),
|
|
3363
|
+
createdAt: z22.string().nullable().optional(),
|
|
3364
|
+
updatedAt: z22.string().nullable().optional()
|
|
3365
|
+
});
|
|
3366
|
+
var ga4SyncResponseDtoSchema = z22.object({
|
|
3367
|
+
synced: z22.boolean(),
|
|
3368
|
+
rowCount: z22.number().int().nonnegative(),
|
|
3369
|
+
aiReferralCount: z22.number().int().nonnegative(),
|
|
3370
|
+
socialReferralCount: z22.number().int().nonnegative(),
|
|
3371
|
+
days: z22.number().int().nonnegative(),
|
|
3372
|
+
syncedAt: z22.string(),
|
|
3141
3373
|
/**
|
|
3142
3374
|
* Components that were written this run. Present when `only` is set.
|
|
3143
3375
|
* Always includes `traffic` and `summary` (the share denominator) plus
|
|
3144
3376
|
* the requested channel breakdown — `ai` and/or `social`.
|
|
3145
3377
|
*/
|
|
3146
|
-
syncedComponents:
|
|
3147
|
-
});
|
|
3148
|
-
var ga4AiReferralHistoryEntrySchema =
|
|
3149
|
-
date:
|
|
3150
|
-
source:
|
|
3151
|
-
medium:
|
|
3152
|
-
trafficClass:
|
|
3153
|
-
landingPage:
|
|
3154
|
-
sessions:
|
|
3155
|
-
users:
|
|
3378
|
+
syncedComponents: z22.array(z22.string()).optional()
|
|
3379
|
+
});
|
|
3380
|
+
var ga4AiReferralHistoryEntrySchema = z22.object({
|
|
3381
|
+
date: z22.string(),
|
|
3382
|
+
source: z22.string(),
|
|
3383
|
+
medium: z22.string(),
|
|
3384
|
+
trafficClass: aiReferralTrafficClassSchema,
|
|
3385
|
+
landingPage: z22.string(),
|
|
3386
|
+
sessions: z22.number(),
|
|
3387
|
+
users: z22.number(),
|
|
3156
3388
|
/** Which GA4 dimension this row came from: session (sessionSource), first_user (firstUserSource), or manual_utm (utm_source parameter) */
|
|
3157
3389
|
sourceDimension: ga4SourceDimensionSchema
|
|
3158
3390
|
});
|
|
3159
|
-
var ga4SocialReferralHistoryEntrySchema =
|
|
3160
|
-
date:
|
|
3161
|
-
source:
|
|
3162
|
-
medium:
|
|
3163
|
-
sessions:
|
|
3164
|
-
users:
|
|
3391
|
+
var ga4SocialReferralHistoryEntrySchema = z22.object({
|
|
3392
|
+
date: z22.string(),
|
|
3393
|
+
source: z22.string(),
|
|
3394
|
+
medium: z22.string(),
|
|
3395
|
+
sessions: z22.number(),
|
|
3396
|
+
users: z22.number(),
|
|
3165
3397
|
/** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
|
|
3166
|
-
channelGroup:
|
|
3398
|
+
channelGroup: z22.string()
|
|
3167
3399
|
});
|
|
3168
|
-
var ga4SessionHistoryEntrySchema =
|
|
3169
|
-
date:
|
|
3170
|
-
sessions:
|
|
3171
|
-
organicSessions:
|
|
3172
|
-
users:
|
|
3400
|
+
var ga4SessionHistoryEntrySchema = z22.object({
|
|
3401
|
+
date: z22.string(),
|
|
3402
|
+
sessions: z22.number(),
|
|
3403
|
+
organicSessions: z22.number(),
|
|
3404
|
+
users: z22.number()
|
|
3173
3405
|
});
|
|
3174
3406
|
|
|
3175
3407
|
// ../contracts/src/answer-visibility.ts
|
|
@@ -3337,50 +3569,50 @@ function escapeRegExp(value) {
|
|
|
3337
3569
|
}
|
|
3338
3570
|
|
|
3339
3571
|
// ../contracts/src/agent.ts
|
|
3340
|
-
import { z as
|
|
3341
|
-
var agentProviderIdSchema =
|
|
3342
|
-
var agentProviderOptionDtoSchema =
|
|
3572
|
+
import { z as z23 } from "zod";
|
|
3573
|
+
var agentProviderIdSchema = z23.enum(["claude", "openai", "gemini", "zai", "deepinfra"]);
|
|
3574
|
+
var agentProviderOptionDtoSchema = z23.object({
|
|
3343
3575
|
/** Stable identifier — what clients pass back as `provider` on the prompt endpoint. */
|
|
3344
3576
|
id: agentProviderIdSchema,
|
|
3345
3577
|
/** Human-readable label for UI pickers, e.g. "Anthropic (Claude)". */
|
|
3346
|
-
label:
|
|
3578
|
+
label: z23.string(),
|
|
3347
3579
|
/** Default model if the caller doesn't pick one. */
|
|
3348
|
-
defaultModel:
|
|
3580
|
+
defaultModel: z23.string(),
|
|
3349
3581
|
/** Whether a usable API key was found (config.yaml or provider env var). */
|
|
3350
|
-
configured:
|
|
3582
|
+
configured: z23.boolean(),
|
|
3351
3583
|
/**
|
|
3352
3584
|
* Where the key resolved from, if any. `null` when `configured === false`.
|
|
3353
3585
|
* Surfaced so the UI can nudge users toward their preferred source of truth.
|
|
3354
3586
|
*/
|
|
3355
|
-
keySource:
|
|
3587
|
+
keySource: z23.enum(["config", "env"]).nullable()
|
|
3356
3588
|
});
|
|
3357
|
-
var agentProvidersResponseDtoSchema =
|
|
3589
|
+
var agentProvidersResponseDtoSchema = z23.object({
|
|
3358
3590
|
/**
|
|
3359
3591
|
* Every provider Aero knows about. `configured === false` entries are
|
|
3360
3592
|
* included so the UI can render them disabled with an onboarding hint.
|
|
3361
3593
|
*/
|
|
3362
|
-
providers:
|
|
3594
|
+
providers: z23.array(agentProviderOptionDtoSchema).default([]),
|
|
3363
3595
|
/**
|
|
3364
3596
|
* Provider Aero auto-picks when no explicit override is passed. Null if
|
|
3365
3597
|
* nothing is configured (install never exchanged a key).
|
|
3366
3598
|
*/
|
|
3367
3599
|
defaultProvider: agentProviderIdSchema.nullable()
|
|
3368
3600
|
});
|
|
3369
|
-
var memorySourceSchema =
|
|
3601
|
+
var memorySourceSchema = z23.enum(["aero", "user", "compaction"]);
|
|
3370
3602
|
var MemorySources = memorySourceSchema.enum;
|
|
3371
3603
|
var AGENT_MEMORY_VALUE_MAX_BYTES = 2 * 1024;
|
|
3372
3604
|
var AGENT_MEMORY_KEY_MAX_LENGTH = 128;
|
|
3373
|
-
var agentMemoryUpsertRequestSchema =
|
|
3374
|
-
key:
|
|
3375
|
-
value:
|
|
3605
|
+
var agentMemoryUpsertRequestSchema = z23.object({
|
|
3606
|
+
key: z23.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH),
|
|
3607
|
+
value: z23.string().min(1)
|
|
3376
3608
|
});
|
|
3377
|
-
var agentMemoryDeleteRequestSchema =
|
|
3378
|
-
key:
|
|
3609
|
+
var agentMemoryDeleteRequestSchema = z23.object({
|
|
3610
|
+
key: z23.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH)
|
|
3379
3611
|
});
|
|
3380
3612
|
|
|
3381
3613
|
// ../contracts/src/backlinks.ts
|
|
3382
|
-
import { z as
|
|
3383
|
-
var backlinkSourceSchema =
|
|
3614
|
+
import { z as z24 } from "zod";
|
|
3615
|
+
var backlinkSourceSchema = z24.enum(["commoncrawl", "bing-webmaster"]);
|
|
3384
3616
|
var BacklinkSources = backlinkSourceSchema.enum;
|
|
3385
3617
|
function computeBacklinkSummaryMetrics(rows) {
|
|
3386
3618
|
if (rows.length === 0) {
|
|
@@ -3396,181 +3628,181 @@ function computeBacklinkSummaryMetrics(rows) {
|
|
|
3396
3628
|
top10HostsShare: share.toFixed(6)
|
|
3397
3629
|
};
|
|
3398
3630
|
}
|
|
3399
|
-
var ccReleaseSyncStatusSchema =
|
|
3631
|
+
var ccReleaseSyncStatusSchema = z24.enum(["queued", "downloading", "querying", "ready", "failed"]);
|
|
3400
3632
|
var CcReleaseSyncStatuses = ccReleaseSyncStatusSchema.enum;
|
|
3401
|
-
var ccReleaseSyncDtoSchema =
|
|
3402
|
-
id:
|
|
3403
|
-
release:
|
|
3633
|
+
var ccReleaseSyncDtoSchema = z24.object({
|
|
3634
|
+
id: z24.string(),
|
|
3635
|
+
release: z24.string(),
|
|
3404
3636
|
status: ccReleaseSyncStatusSchema,
|
|
3405
|
-
phaseDetail:
|
|
3406
|
-
vertexPath:
|
|
3407
|
-
edgesPath:
|
|
3408
|
-
vertexSha256:
|
|
3409
|
-
edgesSha256:
|
|
3410
|
-
vertexBytes:
|
|
3411
|
-
edgesBytes:
|
|
3412
|
-
projectsProcessed:
|
|
3413
|
-
domainsDiscovered:
|
|
3414
|
-
downloadStartedAt:
|
|
3415
|
-
downloadFinishedAt:
|
|
3416
|
-
queryStartedAt:
|
|
3417
|
-
queryFinishedAt:
|
|
3418
|
-
error:
|
|
3419
|
-
createdAt:
|
|
3420
|
-
updatedAt:
|
|
3421
|
-
});
|
|
3422
|
-
var backlinkDomainDtoSchema =
|
|
3423
|
-
linkingDomain:
|
|
3637
|
+
phaseDetail: z24.string().nullable().optional(),
|
|
3638
|
+
vertexPath: z24.string().nullable().optional(),
|
|
3639
|
+
edgesPath: z24.string().nullable().optional(),
|
|
3640
|
+
vertexSha256: z24.string().nullable().optional(),
|
|
3641
|
+
edgesSha256: z24.string().nullable().optional(),
|
|
3642
|
+
vertexBytes: z24.number().int().nullable().optional(),
|
|
3643
|
+
edgesBytes: z24.number().int().nullable().optional(),
|
|
3644
|
+
projectsProcessed: z24.number().int().nullable().optional(),
|
|
3645
|
+
domainsDiscovered: z24.number().int().nullable().optional(),
|
|
3646
|
+
downloadStartedAt: z24.string().nullable().optional(),
|
|
3647
|
+
downloadFinishedAt: z24.string().nullable().optional(),
|
|
3648
|
+
queryStartedAt: z24.string().nullable().optional(),
|
|
3649
|
+
queryFinishedAt: z24.string().nullable().optional(),
|
|
3650
|
+
error: z24.string().nullable().optional(),
|
|
3651
|
+
createdAt: z24.string(),
|
|
3652
|
+
updatedAt: z24.string()
|
|
3653
|
+
});
|
|
3654
|
+
var backlinkDomainDtoSchema = z24.object({
|
|
3655
|
+
linkingDomain: z24.string(),
|
|
3424
3656
|
// For Common Crawl this is the count of distinct hosts within the linking
|
|
3425
3657
|
// domain; for Bing Webmaster it is the count of distinct linking pages (URLs)
|
|
3426
3658
|
// from that linking host. Read alongside `source` — the unit differs per source.
|
|
3427
|
-
numHosts:
|
|
3659
|
+
numHosts: z24.number().int(),
|
|
3428
3660
|
source: backlinkSourceSchema
|
|
3429
3661
|
});
|
|
3430
|
-
var backlinkSummaryDtoSchema =
|
|
3431
|
-
projectId:
|
|
3662
|
+
var backlinkSummaryDtoSchema = z24.object({
|
|
3663
|
+
projectId: z24.string(),
|
|
3432
3664
|
// Window identifier. Common Crawl uses the release slug
|
|
3433
3665
|
// (`cc-main-YYYY-<mon>-<mon>-<mon>`); Bing Webmaster uses a synthetic
|
|
3434
3666
|
// per-sync-day window (`bing-YYYY-MM-DD`).
|
|
3435
|
-
release:
|
|
3436
|
-
targetDomain:
|
|
3437
|
-
totalLinkingDomains:
|
|
3438
|
-
totalHosts:
|
|
3439
|
-
top10HostsShare:
|
|
3440
|
-
queriedAt:
|
|
3667
|
+
release: z24.string(),
|
|
3668
|
+
targetDomain: z24.string(),
|
|
3669
|
+
totalLinkingDomains: z24.number().int(),
|
|
3670
|
+
totalHosts: z24.number().int(),
|
|
3671
|
+
top10HostsShare: z24.string(),
|
|
3672
|
+
queriedAt: z24.string(),
|
|
3441
3673
|
source: backlinkSourceSchema,
|
|
3442
3674
|
// Populated when the response is filtered (e.g. ?excludeCrawlers=1).
|
|
3443
3675
|
// Counts the rows omitted from totalLinkingDomains/totalHosts so callers
|
|
3444
3676
|
// can show "N hidden" hints without re-deriving them.
|
|
3445
|
-
excludedLinkingDomains:
|
|
3446
|
-
excludedHosts:
|
|
3677
|
+
excludedLinkingDomains: z24.number().int().optional(),
|
|
3678
|
+
excludedHosts: z24.number().int().optional()
|
|
3447
3679
|
});
|
|
3448
|
-
var backlinkListResponseSchema =
|
|
3680
|
+
var backlinkListResponseSchema = z24.object({
|
|
3449
3681
|
// The source this response was filtered to (defaults to commoncrawl when the
|
|
3450
3682
|
// caller omits `?source`).
|
|
3451
3683
|
source: backlinkSourceSchema,
|
|
3452
3684
|
summary: backlinkSummaryDtoSchema.nullable(),
|
|
3453
|
-
total:
|
|
3454
|
-
rows:
|
|
3455
|
-
});
|
|
3456
|
-
var backlinkHistoryEntrySchema =
|
|
3457
|
-
release:
|
|
3458
|
-
totalLinkingDomains:
|
|
3459
|
-
totalHosts:
|
|
3460
|
-
top10HostsShare:
|
|
3461
|
-
queriedAt:
|
|
3685
|
+
total: z24.number().int(),
|
|
3686
|
+
rows: z24.array(backlinkDomainDtoSchema)
|
|
3687
|
+
});
|
|
3688
|
+
var backlinkHistoryEntrySchema = z24.object({
|
|
3689
|
+
release: z24.string(),
|
|
3690
|
+
totalLinkingDomains: z24.number().int(),
|
|
3691
|
+
totalHosts: z24.number().int(),
|
|
3692
|
+
top10HostsShare: z24.string(),
|
|
3693
|
+
queriedAt: z24.string(),
|
|
3462
3694
|
source: backlinkSourceSchema
|
|
3463
3695
|
});
|
|
3464
|
-
var backlinkSourceAvailabilityDtoSchema =
|
|
3696
|
+
var backlinkSourceAvailabilityDtoSchema = z24.object({
|
|
3465
3697
|
source: backlinkSourceSchema,
|
|
3466
3698
|
/**
|
|
3467
3699
|
* The source is set up for this project:
|
|
3468
3700
|
* - commoncrawl: `autoExtractBacklinks` enabled AND a `ready` release sync exists.
|
|
3469
3701
|
* - bing-webmaster: a Bing Webmaster connection exists for the project domain.
|
|
3470
3702
|
*/
|
|
3471
|
-
connected:
|
|
3703
|
+
connected: z24.boolean(),
|
|
3472
3704
|
/** Backlink rows exist for this project + source. */
|
|
3473
|
-
hasData:
|
|
3705
|
+
hasData: z24.boolean(),
|
|
3474
3706
|
/** Latest window id with data for this source, null when none. */
|
|
3475
|
-
latestRelease:
|
|
3707
|
+
latestRelease: z24.string().nullable(),
|
|
3476
3708
|
/**
|
|
3477
3709
|
* Linking-domain count in the latest window. Excludes crawler/proxy hosts only
|
|
3478
3710
|
* when the request sets `?excludeCrawlers=1` (default off, matching the summary
|
|
3479
3711
|
* and domains endpoints); the dashboard passes it so the switcher pill matches
|
|
3480
3712
|
* the metric card.
|
|
3481
3713
|
*/
|
|
3482
|
-
totalLinkingDomains:
|
|
3714
|
+
totalLinkingDomains: z24.number().int(),
|
|
3483
3715
|
/** Freshness: `queriedAt` of the latest summary for this source, null when none. */
|
|
3484
|
-
lastSyncedAt:
|
|
3716
|
+
lastSyncedAt: z24.string().nullable()
|
|
3485
3717
|
});
|
|
3486
|
-
var backlinkSourcesResponseSchema =
|
|
3487
|
-
projectId:
|
|
3488
|
-
targetDomain:
|
|
3718
|
+
var backlinkSourcesResponseSchema = z24.object({
|
|
3719
|
+
projectId: z24.string(),
|
|
3720
|
+
targetDomain: z24.string(),
|
|
3489
3721
|
/** Availability for every known source, in a stable order. */
|
|
3490
|
-
sources:
|
|
3491
|
-
anyConnected:
|
|
3492
|
-
anyData:
|
|
3493
|
-
});
|
|
3494
|
-
var backlinksInstallStatusDtoSchema =
|
|
3495
|
-
duckdbInstalled:
|
|
3496
|
-
duckdbVersion:
|
|
3497
|
-
duckdbSpec:
|
|
3498
|
-
pluginDir:
|
|
3499
|
-
});
|
|
3500
|
-
var backlinksInstallResultDtoSchema =
|
|
3501
|
-
installed:
|
|
3502
|
-
version:
|
|
3503
|
-
path:
|
|
3504
|
-
alreadyPresent:
|
|
3505
|
-
});
|
|
3506
|
-
var ccAvailableReleaseSchema =
|
|
3507
|
-
release:
|
|
3508
|
-
vertexUrl:
|
|
3509
|
-
edgesUrl:
|
|
3510
|
-
vertexBytes:
|
|
3511
|
-
edgesBytes:
|
|
3512
|
-
lastModified:
|
|
3513
|
-
});
|
|
3514
|
-
var ccCachedReleaseSchema =
|
|
3515
|
-
release:
|
|
3722
|
+
sources: z24.array(backlinkSourceAvailabilityDtoSchema),
|
|
3723
|
+
anyConnected: z24.boolean(),
|
|
3724
|
+
anyData: z24.boolean()
|
|
3725
|
+
});
|
|
3726
|
+
var backlinksInstallStatusDtoSchema = z24.object({
|
|
3727
|
+
duckdbInstalled: z24.boolean(),
|
|
3728
|
+
duckdbVersion: z24.string().nullable().optional(),
|
|
3729
|
+
duckdbSpec: z24.string(),
|
|
3730
|
+
pluginDir: z24.string()
|
|
3731
|
+
});
|
|
3732
|
+
var backlinksInstallResultDtoSchema = z24.object({
|
|
3733
|
+
installed: z24.boolean(),
|
|
3734
|
+
version: z24.string(),
|
|
3735
|
+
path: z24.string(),
|
|
3736
|
+
alreadyPresent: z24.boolean()
|
|
3737
|
+
});
|
|
3738
|
+
var ccAvailableReleaseSchema = z24.object({
|
|
3739
|
+
release: z24.string(),
|
|
3740
|
+
vertexUrl: z24.string(),
|
|
3741
|
+
edgesUrl: z24.string(),
|
|
3742
|
+
vertexBytes: z24.number().int().nullable(),
|
|
3743
|
+
edgesBytes: z24.number().int().nullable(),
|
|
3744
|
+
lastModified: z24.string().nullable()
|
|
3745
|
+
});
|
|
3746
|
+
var ccCachedReleaseSchema = z24.object({
|
|
3747
|
+
release: z24.string(),
|
|
3516
3748
|
syncStatus: ccReleaseSyncStatusSchema.nullable(),
|
|
3517
|
-
bytes:
|
|
3518
|
-
lastUsedAt:
|
|
3749
|
+
bytes: z24.number().int(),
|
|
3750
|
+
lastUsedAt: z24.string().nullable()
|
|
3519
3751
|
});
|
|
3520
3752
|
|
|
3521
3753
|
// ../contracts/src/composites.ts
|
|
3522
|
-
import { z as
|
|
3523
|
-
var metricToneSchema =
|
|
3524
|
-
var scoreSummarySchema =
|
|
3525
|
-
label:
|
|
3526
|
-
value:
|
|
3527
|
-
delta:
|
|
3754
|
+
import { z as z25 } from "zod";
|
|
3755
|
+
var metricToneSchema = z25.enum(["positive", "caution", "negative", "neutral"]);
|
|
3756
|
+
var scoreSummarySchema = z25.object({
|
|
3757
|
+
label: z25.string(),
|
|
3758
|
+
value: z25.string(),
|
|
3759
|
+
delta: z25.string(),
|
|
3528
3760
|
tone: metricToneSchema,
|
|
3529
|
-
description:
|
|
3530
|
-
tooltip:
|
|
3531
|
-
trend:
|
|
3532
|
-
progress:
|
|
3533
|
-
providerCoverage:
|
|
3761
|
+
description: z25.string(),
|
|
3762
|
+
tooltip: z25.string().optional(),
|
|
3763
|
+
trend: z25.array(z25.number()),
|
|
3764
|
+
progress: z25.number().optional(),
|
|
3765
|
+
providerCoverage: z25.string().optional()
|
|
3534
3766
|
});
|
|
3535
3767
|
var mentionShareSchema = scoreSummarySchema.extend({
|
|
3536
|
-
breakdown:
|
|
3537
|
-
projectMentionSnapshots:
|
|
3538
|
-
competitorMentionSnapshots:
|
|
3539
|
-
perCompetitor:
|
|
3540
|
-
domain:
|
|
3541
|
-
mentionSnapshots:
|
|
3542
|
-
shareOfCompetitiveTotal:
|
|
3768
|
+
breakdown: z25.object({
|
|
3769
|
+
projectMentionSnapshots: z25.number().int().nonnegative(),
|
|
3770
|
+
competitorMentionSnapshots: z25.number().int().nonnegative(),
|
|
3771
|
+
perCompetitor: z25.array(z25.object({
|
|
3772
|
+
domain: z25.string(),
|
|
3773
|
+
mentionSnapshots: z25.number().int().nonnegative(),
|
|
3774
|
+
shareOfCompetitiveTotal: z25.number()
|
|
3543
3775
|
})),
|
|
3544
|
-
snapshotsWithAnswerText:
|
|
3545
|
-
snapshotsTotal:
|
|
3776
|
+
snapshotsWithAnswerText: z25.number().int().nonnegative(),
|
|
3777
|
+
snapshotsTotal: z25.number().int().nonnegative()
|
|
3546
3778
|
})
|
|
3547
3779
|
});
|
|
3548
|
-
var movementSummarySchema =
|
|
3549
|
-
gained:
|
|
3550
|
-
lost:
|
|
3780
|
+
var movementSummarySchema = z25.object({
|
|
3781
|
+
gained: z25.number().int().nonnegative(),
|
|
3782
|
+
lost: z25.number().int().nonnegative(),
|
|
3551
3783
|
tone: metricToneSchema,
|
|
3552
|
-
hasPreviousRun:
|
|
3553
|
-
gainedQueries:
|
|
3554
|
-
lostQueries:
|
|
3555
|
-
});
|
|
3556
|
-
var movementComparisonSchema =
|
|
3557
|
-
hasPreviousRun:
|
|
3558
|
-
comparable:
|
|
3559
|
-
querySetChanged:
|
|
3560
|
-
previousRunAt:
|
|
3561
|
-
currentQueryCount:
|
|
3562
|
-
previousQueryCount:
|
|
3563
|
-
comparableQueryCount:
|
|
3564
|
-
addedQueryCount:
|
|
3565
|
-
removedQueryCount:
|
|
3566
|
-
addedQueries:
|
|
3567
|
-
removedQueries:
|
|
3568
|
-
});
|
|
3569
|
-
var projectOverviewInsightSchema =
|
|
3570
|
-
id:
|
|
3571
|
-
projectId:
|
|
3572
|
-
runId:
|
|
3573
|
-
type:
|
|
3784
|
+
hasPreviousRun: z25.boolean(),
|
|
3785
|
+
gainedQueries: z25.array(z25.string()).optional(),
|
|
3786
|
+
lostQueries: z25.array(z25.string()).optional()
|
|
3787
|
+
});
|
|
3788
|
+
var movementComparisonSchema = z25.object({
|
|
3789
|
+
hasPreviousRun: z25.boolean(),
|
|
3790
|
+
comparable: z25.boolean(),
|
|
3791
|
+
querySetChanged: z25.boolean(),
|
|
3792
|
+
previousRunAt: z25.string().nullable(),
|
|
3793
|
+
currentQueryCount: z25.number().int().nonnegative(),
|
|
3794
|
+
previousQueryCount: z25.number().int().nonnegative(),
|
|
3795
|
+
comparableQueryCount: z25.number().int().nonnegative(),
|
|
3796
|
+
addedQueryCount: z25.number().int().nonnegative(),
|
|
3797
|
+
removedQueryCount: z25.number().int().nonnegative(),
|
|
3798
|
+
addedQueries: z25.array(z25.string()),
|
|
3799
|
+
removedQueries: z25.array(z25.string())
|
|
3800
|
+
});
|
|
3801
|
+
var projectOverviewInsightSchema = z25.object({
|
|
3802
|
+
id: z25.string(),
|
|
3803
|
+
projectId: z25.string(),
|
|
3804
|
+
runId: z25.string().nullable(),
|
|
3805
|
+
type: z25.enum([
|
|
3574
3806
|
"regression",
|
|
3575
3807
|
"gain",
|
|
3576
3808
|
"opportunity",
|
|
@@ -3586,70 +3818,70 @@ var projectOverviewInsightSchema = z24.object({
|
|
|
3586
3818
|
"gbp-metric-drop",
|
|
3587
3819
|
"gbp-keyword-drop"
|
|
3588
3820
|
]),
|
|
3589
|
-
severity:
|
|
3590
|
-
title:
|
|
3591
|
-
query:
|
|
3592
|
-
provider:
|
|
3593
|
-
recommendation:
|
|
3594
|
-
action:
|
|
3595
|
-
target:
|
|
3596
|
-
reason:
|
|
3821
|
+
severity: z25.enum(["critical", "high", "medium", "low"]),
|
|
3822
|
+
title: z25.string(),
|
|
3823
|
+
query: z25.string(),
|
|
3824
|
+
provider: z25.string(),
|
|
3825
|
+
recommendation: z25.object({
|
|
3826
|
+
action: z25.string(),
|
|
3827
|
+
target: z25.string().optional(),
|
|
3828
|
+
reason: z25.string()
|
|
3597
3829
|
}).optional(),
|
|
3598
|
-
cause:
|
|
3599
|
-
cause:
|
|
3600
|
-
competitorDomain:
|
|
3601
|
-
details:
|
|
3830
|
+
cause: z25.object({
|
|
3831
|
+
cause: z25.string(),
|
|
3832
|
+
competitorDomain: z25.string().optional(),
|
|
3833
|
+
details: z25.string().optional()
|
|
3602
3834
|
}).optional(),
|
|
3603
|
-
dismissed:
|
|
3604
|
-
createdAt:
|
|
3605
|
-
});
|
|
3606
|
-
var projectOverviewHealthSchema =
|
|
3607
|
-
id:
|
|
3608
|
-
projectId:
|
|
3609
|
-
runId:
|
|
3610
|
-
overallCitedRate:
|
|
3611
|
-
overallMentionRate:
|
|
3612
|
-
totalPairs:
|
|
3613
|
-
citedPairs:
|
|
3614
|
-
mentionedPairs:
|
|
3615
|
-
providerBreakdown:
|
|
3616
|
-
citedRate:
|
|
3617
|
-
mentionRate:
|
|
3618
|
-
cited:
|
|
3619
|
-
mentioned:
|
|
3620
|
-
total:
|
|
3835
|
+
dismissed: z25.boolean(),
|
|
3836
|
+
createdAt: z25.string()
|
|
3837
|
+
});
|
|
3838
|
+
var projectOverviewHealthSchema = z25.object({
|
|
3839
|
+
id: z25.string(),
|
|
3840
|
+
projectId: z25.string(),
|
|
3841
|
+
runId: z25.string().nullable(),
|
|
3842
|
+
overallCitedRate: z25.number(),
|
|
3843
|
+
overallMentionRate: z25.number(),
|
|
3844
|
+
totalPairs: z25.number().int().nonnegative(),
|
|
3845
|
+
citedPairs: z25.number().int().nonnegative(),
|
|
3846
|
+
mentionedPairs: z25.number().int().nonnegative(),
|
|
3847
|
+
providerBreakdown: z25.record(z25.string(), z25.object({
|
|
3848
|
+
citedRate: z25.number(),
|
|
3849
|
+
mentionRate: z25.number(),
|
|
3850
|
+
cited: z25.number().int().nonnegative(),
|
|
3851
|
+
mentioned: z25.number().int().nonnegative(),
|
|
3852
|
+
total: z25.number().int().nonnegative()
|
|
3621
3853
|
})),
|
|
3622
|
-
createdAt:
|
|
3623
|
-
status:
|
|
3624
|
-
reason:
|
|
3854
|
+
createdAt: z25.string(),
|
|
3855
|
+
status: z25.enum(["ready", "no-data"]),
|
|
3856
|
+
reason: z25.literal("no-runs-yet").optional()
|
|
3625
3857
|
});
|
|
3626
|
-
var projectOverviewDtoSchema =
|
|
3858
|
+
var projectOverviewDtoSchema = z25.object({
|
|
3627
3859
|
project: projectDtoSchema,
|
|
3628
3860
|
latestRun: latestProjectRunDtoSchema,
|
|
3629
3861
|
health: projectOverviewHealthSchema.nullable(),
|
|
3630
|
-
topInsights:
|
|
3631
|
-
queryCounts:
|
|
3632
|
-
totalQueries:
|
|
3633
|
-
citedQueries:
|
|
3634
|
-
notCitedQueries:
|
|
3635
|
-
citedRate:
|
|
3636
|
-
mentionedQueries:
|
|
3637
|
-
notMentionedQueries:
|
|
3638
|
-
mentionRate:
|
|
3862
|
+
topInsights: z25.array(projectOverviewInsightSchema),
|
|
3863
|
+
queryCounts: z25.object({
|
|
3864
|
+
totalQueries: z25.number().int().nonnegative(),
|
|
3865
|
+
citedQueries: z25.number().int().nonnegative(),
|
|
3866
|
+
notCitedQueries: z25.number().int().nonnegative(),
|
|
3867
|
+
citedRate: z25.number(),
|
|
3868
|
+
mentionedQueries: z25.number().int().nonnegative(),
|
|
3869
|
+
notMentionedQueries: z25.number().int().nonnegative(),
|
|
3870
|
+
mentionRate: z25.number()
|
|
3639
3871
|
}),
|
|
3640
|
-
providers:
|
|
3641
|
-
provider:
|
|
3642
|
-
citedRate:
|
|
3643
|
-
cited:
|
|
3644
|
-
total:
|
|
3872
|
+
providers: z25.array(z25.object({
|
|
3873
|
+
provider: z25.string(),
|
|
3874
|
+
citedRate: z25.number(),
|
|
3875
|
+
cited: z25.number().int().nonnegative(),
|
|
3876
|
+
total: z25.number().int().nonnegative()
|
|
3645
3877
|
})),
|
|
3646
|
-
transitions:
|
|
3647
|
-
since:
|
|
3648
|
-
gained:
|
|
3649
|
-
lost:
|
|
3650
|
-
emerging:
|
|
3878
|
+
transitions: z25.object({
|
|
3879
|
+
since: z25.string().nullable(),
|
|
3880
|
+
gained: z25.number().int().nonnegative(),
|
|
3881
|
+
lost: z25.number().int().nonnegative(),
|
|
3882
|
+
emerging: z25.number().int().nonnegative()
|
|
3651
3883
|
}),
|
|
3652
|
-
scores:
|
|
3884
|
+
scores: z25.object({
|
|
3653
3885
|
mention: scoreSummarySchema,
|
|
3654
3886
|
visibility: scoreSummarySchema,
|
|
3655
3887
|
mentionShare: mentionShareSchema,
|
|
@@ -3663,72 +3895,72 @@ var projectOverviewDtoSchema = z24.object({
|
|
|
3663
3895
|
citationMovement: movementSummarySchema,
|
|
3664
3896
|
mentionMovement: movementSummarySchema,
|
|
3665
3897
|
movementComparison: movementComparisonSchema,
|
|
3666
|
-
competitors:
|
|
3667
|
-
id:
|
|
3668
|
-
domain:
|
|
3669
|
-
citationCount:
|
|
3670
|
-
totalQueries:
|
|
3671
|
-
pressureLabel:
|
|
3672
|
-
citedQueries:
|
|
3898
|
+
competitors: z25.array(z25.object({
|
|
3899
|
+
id: z25.string(),
|
|
3900
|
+
domain: z25.string(),
|
|
3901
|
+
citationCount: z25.number().int().nonnegative(),
|
|
3902
|
+
totalQueries: z25.number().int().nonnegative(),
|
|
3903
|
+
pressureLabel: z25.enum(["None", "Low", "Moderate", "High"]),
|
|
3904
|
+
citedQueries: z25.array(z25.string())
|
|
3673
3905
|
})),
|
|
3674
|
-
providerScores:
|
|
3675
|
-
provider:
|
|
3676
|
-
model:
|
|
3677
|
-
score:
|
|
3678
|
-
cited:
|
|
3679
|
-
total:
|
|
3680
|
-
trend:
|
|
3906
|
+
providerScores: z25.array(z25.object({
|
|
3907
|
+
provider: z25.string(),
|
|
3908
|
+
model: z25.string().nullable(),
|
|
3909
|
+
score: z25.number(),
|
|
3910
|
+
cited: z25.number().int().nonnegative(),
|
|
3911
|
+
total: z25.number().int().nonnegative(),
|
|
3912
|
+
trend: z25.array(z25.number()).optional()
|
|
3681
3913
|
})),
|
|
3682
|
-
attentionItems:
|
|
3683
|
-
id:
|
|
3914
|
+
attentionItems: z25.array(z25.object({
|
|
3915
|
+
id: z25.string(),
|
|
3684
3916
|
tone: metricToneSchema,
|
|
3685
|
-
title:
|
|
3686
|
-
detail:
|
|
3687
|
-
actionLabel:
|
|
3688
|
-
href:
|
|
3917
|
+
title: z25.string(),
|
|
3918
|
+
detail: z25.string(),
|
|
3919
|
+
actionLabel: z25.string(),
|
|
3920
|
+
href: z25.string()
|
|
3689
3921
|
})),
|
|
3690
|
-
runHistory:
|
|
3691
|
-
runId:
|
|
3692
|
-
createdAt:
|
|
3693
|
-
citedCount:
|
|
3694
|
-
totalCount:
|
|
3695
|
-
citationRate:
|
|
3696
|
-
mentionedCount:
|
|
3697
|
-
mentionRate:
|
|
3698
|
-
status:
|
|
3922
|
+
runHistory: z25.array(z25.object({
|
|
3923
|
+
runId: z25.string(),
|
|
3924
|
+
createdAt: z25.string(),
|
|
3925
|
+
citedCount: z25.number().int().nonnegative(),
|
|
3926
|
+
totalCount: z25.number().int().nonnegative(),
|
|
3927
|
+
citationRate: z25.number(),
|
|
3928
|
+
mentionedCount: z25.number().int().nonnegative(),
|
|
3929
|
+
mentionRate: z25.number(),
|
|
3930
|
+
status: z25.string()
|
|
3699
3931
|
})),
|
|
3700
|
-
suggestedQueries:
|
|
3701
|
-
rows:
|
|
3702
|
-
query:
|
|
3703
|
-
impressions:
|
|
3704
|
-
clicks:
|
|
3705
|
-
avgPosition:
|
|
3706
|
-
reason:
|
|
3932
|
+
suggestedQueries: z25.object({
|
|
3933
|
+
rows: z25.array(z25.object({
|
|
3934
|
+
query: z25.string(),
|
|
3935
|
+
impressions: z25.number(),
|
|
3936
|
+
clicks: z25.number(),
|
|
3937
|
+
avgPosition: z25.number(),
|
|
3938
|
+
reason: z25.string()
|
|
3707
3939
|
})),
|
|
3708
|
-
totalCandidates:
|
|
3709
|
-
skippedAlreadyTracked:
|
|
3940
|
+
totalCandidates: z25.number().int().nonnegative(),
|
|
3941
|
+
skippedAlreadyTracked: z25.number().int().nonnegative()
|
|
3710
3942
|
}),
|
|
3711
|
-
dateRangeLabel:
|
|
3712
|
-
contextLabel:
|
|
3713
|
-
});
|
|
3714
|
-
var searchHitKindSchema =
|
|
3715
|
-
var projectSearchSnapshotHitSchema =
|
|
3716
|
-
kind:
|
|
3717
|
-
id:
|
|
3718
|
-
runId:
|
|
3719
|
-
query:
|
|
3720
|
-
provider:
|
|
3721
|
-
model:
|
|
3943
|
+
dateRangeLabel: z25.string(),
|
|
3944
|
+
contextLabel: z25.string()
|
|
3945
|
+
});
|
|
3946
|
+
var searchHitKindSchema = z25.enum(["snapshot", "insight"]);
|
|
3947
|
+
var projectSearchSnapshotHitSchema = z25.object({
|
|
3948
|
+
kind: z25.literal("snapshot"),
|
|
3949
|
+
id: z25.string(),
|
|
3950
|
+
runId: z25.string(),
|
|
3951
|
+
query: z25.string(),
|
|
3952
|
+
provider: z25.string(),
|
|
3953
|
+
model: z25.string().nullable(),
|
|
3722
3954
|
citationState: citationStateSchema,
|
|
3723
|
-
matchedField:
|
|
3724
|
-
snippet:
|
|
3725
|
-
createdAt:
|
|
3726
|
-
});
|
|
3727
|
-
var projectSearchInsightHitSchema =
|
|
3728
|
-
kind:
|
|
3729
|
-
id:
|
|
3730
|
-
runId:
|
|
3731
|
-
type:
|
|
3955
|
+
matchedField: z25.enum(["answerText", "citedDomains", "searchQueries", "query"]),
|
|
3956
|
+
snippet: z25.string(),
|
|
3957
|
+
createdAt: z25.string()
|
|
3958
|
+
});
|
|
3959
|
+
var projectSearchInsightHitSchema = z25.object({
|
|
3960
|
+
kind: z25.literal("insight"),
|
|
3961
|
+
id: z25.string(),
|
|
3962
|
+
runId: z25.string().nullable(),
|
|
3963
|
+
type: z25.enum([
|
|
3732
3964
|
"regression",
|
|
3733
3965
|
"gain",
|
|
3734
3966
|
"opportunity",
|
|
@@ -3745,29 +3977,29 @@ var projectSearchInsightHitSchema = z24.object({
|
|
|
3745
3977
|
"gbp-metric-drop",
|
|
3746
3978
|
"gbp-keyword-drop"
|
|
3747
3979
|
]),
|
|
3748
|
-
severity:
|
|
3749
|
-
title:
|
|
3750
|
-
query:
|
|
3751
|
-
provider:
|
|
3752
|
-
matchedField:
|
|
3753
|
-
snippet:
|
|
3754
|
-
dismissed:
|
|
3755
|
-
createdAt:
|
|
3756
|
-
});
|
|
3757
|
-
var projectSearchHitSchema =
|
|
3980
|
+
severity: z25.enum(["critical", "high", "medium", "low"]),
|
|
3981
|
+
title: z25.string(),
|
|
3982
|
+
query: z25.string(),
|
|
3983
|
+
provider: z25.string(),
|
|
3984
|
+
matchedField: z25.enum(["title", "query", "recommendation", "cause"]),
|
|
3985
|
+
snippet: z25.string(),
|
|
3986
|
+
dismissed: z25.boolean(),
|
|
3987
|
+
createdAt: z25.string()
|
|
3988
|
+
});
|
|
3989
|
+
var projectSearchHitSchema = z25.discriminatedUnion("kind", [
|
|
3758
3990
|
projectSearchSnapshotHitSchema,
|
|
3759
3991
|
projectSearchInsightHitSchema
|
|
3760
3992
|
]);
|
|
3761
|
-
var projectSearchResponseSchema =
|
|
3762
|
-
query:
|
|
3763
|
-
totalHits:
|
|
3764
|
-
truncated:
|
|
3765
|
-
hits:
|
|
3993
|
+
var projectSearchResponseSchema = z25.object({
|
|
3994
|
+
query: z25.string(),
|
|
3995
|
+
totalHits: z25.number().int().nonnegative(),
|
|
3996
|
+
truncated: z25.boolean(),
|
|
3997
|
+
hits: z25.array(projectSearchHitSchema)
|
|
3766
3998
|
});
|
|
3767
3999
|
|
|
3768
4000
|
// ../contracts/src/content.ts
|
|
3769
|
-
import { z as
|
|
3770
|
-
var contentActionSchema =
|
|
4001
|
+
import { z as z26 } from "zod";
|
|
4002
|
+
var contentActionSchema = z26.enum(["create", "expand", "refresh", "add-schema"]);
|
|
3771
4003
|
var ContentActions = contentActionSchema.enum;
|
|
3772
4004
|
function contentActionLabel(action) {
|
|
3773
4005
|
switch (action) {
|
|
@@ -3781,9 +4013,9 @@ function contentActionLabel(action) {
|
|
|
3781
4013
|
return "Add schema";
|
|
3782
4014
|
}
|
|
3783
4015
|
}
|
|
3784
|
-
var demandSourceSchema =
|
|
4016
|
+
var demandSourceSchema = z26.enum(["gsc", "competitor-evidence", "both"]);
|
|
3785
4017
|
var DemandSources = demandSourceSchema.enum;
|
|
3786
|
-
var actionConfidenceSchema =
|
|
4018
|
+
var actionConfidenceSchema = z26.enum(["high", "medium", "low"]);
|
|
3787
4019
|
var ActionConfidences = actionConfidenceSchema.enum;
|
|
3788
4020
|
function actionConfidenceLabel(confidence) {
|
|
3789
4021
|
switch (confidence) {
|
|
@@ -3795,7 +4027,7 @@ function actionConfidenceLabel(confidence) {
|
|
|
3795
4027
|
return "Low";
|
|
3796
4028
|
}
|
|
3797
4029
|
}
|
|
3798
|
-
var pageTypeSchema =
|
|
4030
|
+
var pageTypeSchema = z26.enum([
|
|
3799
4031
|
"blog-post",
|
|
3800
4032
|
"comparison",
|
|
3801
4033
|
"listicle",
|
|
@@ -3804,7 +4036,7 @@ var pageTypeSchema = z25.enum([
|
|
|
3804
4036
|
"glossary"
|
|
3805
4037
|
]);
|
|
3806
4038
|
var PageTypes = pageTypeSchema.enum;
|
|
3807
|
-
var contentActionStateSchema =
|
|
4039
|
+
var contentActionStateSchema = z26.enum([
|
|
3808
4040
|
"proposed",
|
|
3809
4041
|
"briefed",
|
|
3810
4042
|
"payload-generated",
|
|
@@ -3814,7 +4046,7 @@ var contentActionStateSchema = z25.enum([
|
|
|
3814
4046
|
"dismissed"
|
|
3815
4047
|
]);
|
|
3816
4048
|
var ContentActionStates = contentActionStateSchema.enum;
|
|
3817
|
-
var winnabilityClassSchema =
|
|
4049
|
+
var winnabilityClassSchema = z26.enum(["ownable", "ceded"]);
|
|
3818
4050
|
var WinnabilityClasses = winnabilityClassSchema.enum;
|
|
3819
4051
|
function winnabilityClassLabel(winnabilityClass) {
|
|
3820
4052
|
switch (winnabilityClass) {
|
|
@@ -3849,40 +4081,40 @@ function deriveWinnabilityClass(citedSurfaceDomains, surfaceClasses, threshold =
|
|
|
3849
4081
|
winnability
|
|
3850
4082
|
};
|
|
3851
4083
|
}
|
|
3852
|
-
var ourBestPageSchema =
|
|
3853
|
-
url:
|
|
3854
|
-
gscImpressions:
|
|
3855
|
-
gscClicks:
|
|
4084
|
+
var ourBestPageSchema = z26.object({
|
|
4085
|
+
url: z26.string(),
|
|
4086
|
+
gscImpressions: z26.number().nonnegative(),
|
|
4087
|
+
gscClicks: z26.number().nonnegative(),
|
|
3856
4088
|
// Null when the page came from the inventory fallback (no GSC ranking data).
|
|
3857
|
-
gscAvgPosition:
|
|
3858
|
-
organicSessions:
|
|
4089
|
+
gscAvgPosition: z26.number().nonnegative().nullable(),
|
|
4090
|
+
organicSessions: z26.number().nonnegative()
|
|
3859
4091
|
});
|
|
3860
|
-
var winningCompetitorSchema =
|
|
3861
|
-
domain:
|
|
3862
|
-
url:
|
|
3863
|
-
title:
|
|
3864
|
-
citationCount:
|
|
4092
|
+
var winningCompetitorSchema = z26.object({
|
|
4093
|
+
domain: z26.string(),
|
|
4094
|
+
url: z26.string(),
|
|
4095
|
+
title: z26.string(),
|
|
4096
|
+
citationCount: z26.number().int().nonnegative()
|
|
3865
4097
|
});
|
|
3866
|
-
var scoreBreakdownSchema =
|
|
3867
|
-
demand:
|
|
3868
|
-
competitor:
|
|
3869
|
-
absence:
|
|
3870
|
-
gapSeverity:
|
|
4098
|
+
var scoreBreakdownSchema = z26.object({
|
|
4099
|
+
demand: z26.number(),
|
|
4100
|
+
competitor: z26.number(),
|
|
4101
|
+
absence: z26.number(),
|
|
4102
|
+
gapSeverity: z26.number()
|
|
3871
4103
|
});
|
|
3872
|
-
var existingActionRefSchema =
|
|
3873
|
-
actionId:
|
|
4104
|
+
var existingActionRefSchema = z26.object({
|
|
4105
|
+
actionId: z26.string(),
|
|
3874
4106
|
state: contentActionStateSchema,
|
|
3875
|
-
lastUpdated:
|
|
4107
|
+
lastUpdated: z26.string()
|
|
3876
4108
|
});
|
|
3877
|
-
var contentTargetRowDtoSchema =
|
|
3878
|
-
targetRef:
|
|
3879
|
-
query:
|
|
4109
|
+
var contentTargetRowDtoSchema = z26.object({
|
|
4110
|
+
targetRef: z26.string(),
|
|
4111
|
+
query: z26.string(),
|
|
3880
4112
|
action: contentActionSchema,
|
|
3881
4113
|
ourBestPage: ourBestPageSchema.nullable(),
|
|
3882
4114
|
winningCompetitor: winningCompetitorSchema.nullable(),
|
|
3883
|
-
score:
|
|
4115
|
+
score: z26.number(),
|
|
3884
4116
|
scoreBreakdown: scoreBreakdownSchema,
|
|
3885
|
-
drivers:
|
|
4117
|
+
drivers: z26.array(z26.string()),
|
|
3886
4118
|
demandSource: demandSourceSchema,
|
|
3887
4119
|
actionConfidence: actionConfidenceSchema,
|
|
3888
4120
|
existingAction: existingActionRefSchema.nullable(),
|
|
@@ -3897,134 +4129,134 @@ var contentTargetRowDtoSchema = z25.object({
|
|
|
3897
4129
|
* `[0, 1]`. `null` when the gate failed open (no classification coverage for
|
|
3898
4130
|
* the cited surface) — distinct from a computed `1.0`.
|
|
3899
4131
|
*/
|
|
3900
|
-
winnability:
|
|
3901
|
-
});
|
|
3902
|
-
var contentTargetsResponseDtoSchema =
|
|
3903
|
-
targets:
|
|
3904
|
-
contextMetrics:
|
|
3905
|
-
totalAiReferralSessions:
|
|
3906
|
-
latestRunId:
|
|
3907
|
-
runTimestamp:
|
|
4132
|
+
winnability: z26.number().min(0).max(1).nullable()
|
|
4133
|
+
});
|
|
4134
|
+
var contentTargetsResponseDtoSchema = z26.object({
|
|
4135
|
+
targets: z26.array(contentTargetRowDtoSchema),
|
|
4136
|
+
contextMetrics: z26.object({
|
|
4137
|
+
totalAiReferralSessions: z26.number().int().nonnegative(),
|
|
4138
|
+
latestRunId: z26.string(),
|
|
4139
|
+
runTimestamp: z26.string()
|
|
3908
4140
|
})
|
|
3909
4141
|
});
|
|
3910
|
-
var contentTargetDismissalDtoSchema =
|
|
3911
|
-
targetRef:
|
|
3912
|
-
addressedUrl:
|
|
3913
|
-
note:
|
|
3914
|
-
dismissedAt:
|
|
4142
|
+
var contentTargetDismissalDtoSchema = z26.object({
|
|
4143
|
+
targetRef: z26.string(),
|
|
4144
|
+
addressedUrl: z26.string().nullable(),
|
|
4145
|
+
note: z26.string().nullable(),
|
|
4146
|
+
dismissedAt: z26.string()
|
|
3915
4147
|
});
|
|
3916
|
-
var contentTargetDismissalsResponseDtoSchema =
|
|
3917
|
-
dismissals:
|
|
4148
|
+
var contentTargetDismissalsResponseDtoSchema = z26.object({
|
|
4149
|
+
dismissals: z26.array(contentTargetDismissalDtoSchema)
|
|
3918
4150
|
});
|
|
3919
|
-
var contentTargetDismissRequestSchema =
|
|
3920
|
-
targetRef:
|
|
4151
|
+
var contentTargetDismissRequestSchema = z26.object({
|
|
4152
|
+
targetRef: z26.string().min(1),
|
|
3921
4153
|
/** URL of the page the user wrote that addresses this recommendation. Stored verbatim for the audit trail; not currently used to suppress the slug-token matcher. */
|
|
3922
|
-
addressedUrl:
|
|
4154
|
+
addressedUrl: z26.string().url().optional(),
|
|
3923
4155
|
/** Free-form note (e.g. "covered in our Q1 content sprint"). 500 char cap is the API surface limit; the DB column is unbounded. */
|
|
3924
|
-
note:
|
|
4156
|
+
note: z26.string().max(500).optional()
|
|
3925
4157
|
});
|
|
3926
|
-
var recommendationExplanationDtoSchema =
|
|
3927
|
-
targetRef:
|
|
4158
|
+
var recommendationExplanationDtoSchema = z26.object({
|
|
4159
|
+
targetRef: z26.string(),
|
|
3928
4160
|
/** Version of the prompt template used. Bumping the version invalidates the cache forward without touching the table. */
|
|
3929
|
-
promptVersion:
|
|
4161
|
+
promptVersion: z26.string(),
|
|
3930
4162
|
/** Provider that produced the explanation (e.g. "claude", "gemini"). */
|
|
3931
|
-
provider:
|
|
4163
|
+
provider: z26.string(),
|
|
3932
4164
|
/** Model id within that provider (e.g. "claude-sonnet-4-6"). */
|
|
3933
|
-
model:
|
|
4165
|
+
model: z26.string(),
|
|
3934
4166
|
/** Markdown-formatted rationale + recommended next steps. */
|
|
3935
|
-
responseText:
|
|
4167
|
+
responseText: z26.string(),
|
|
3936
4168
|
/** Estimated cost in millicents (1/100 of a cent). 0 when unknown. */
|
|
3937
|
-
costMillicents:
|
|
3938
|
-
generatedAt:
|
|
4169
|
+
costMillicents: z26.number().int().nonnegative(),
|
|
4170
|
+
generatedAt: z26.string()
|
|
3939
4171
|
});
|
|
3940
|
-
var recommendationExplainRequestSchema =
|
|
4172
|
+
var recommendationExplainRequestSchema = z26.object({
|
|
3941
4173
|
/**
|
|
3942
4174
|
* Optional provider override (e.g. "claude" to force Claude even if
|
|
3943
4175
|
* the project's default is Gemini). Falls through to project default
|
|
3944
4176
|
* → auto-detect when omitted.
|
|
3945
4177
|
*/
|
|
3946
|
-
provider:
|
|
4178
|
+
provider: z26.string().optional(),
|
|
3947
4179
|
/**
|
|
3948
4180
|
* Optional model override within the chosen provider. Falls through to
|
|
3949
4181
|
* the `analyze`-tier default model when omitted.
|
|
3950
4182
|
*/
|
|
3951
|
-
model:
|
|
4183
|
+
model: z26.string().optional(),
|
|
3952
4184
|
/**
|
|
3953
4185
|
* Force a fresh LLM call even if a cached explanation exists for the
|
|
3954
4186
|
* current prompt version. Use sparingly — defeats the cache.
|
|
3955
4187
|
*/
|
|
3956
|
-
forceRefresh:
|
|
4188
|
+
forceRefresh: z26.boolean().optional()
|
|
3957
4189
|
});
|
|
3958
|
-
var contentBriefDtoSchema =
|
|
4190
|
+
var contentBriefDtoSchema = z26.object({
|
|
3959
4191
|
/** The query the brief is for (echoed from the recommendation). */
|
|
3960
|
-
targetQuery:
|
|
4192
|
+
targetQuery: z26.string().trim().min(1),
|
|
3961
4193
|
/** Always `ownable` in practice — the gate rejects `ceded` before synthesis. */
|
|
3962
4194
|
winnabilityClass: winnabilityClassSchema,
|
|
3963
4195
|
/** The differentiated content angle to take. */
|
|
3964
|
-
angle:
|
|
4196
|
+
angle: z26.string().trim().min(1),
|
|
3965
4197
|
/** Why this query is winnable, citing the cited-surface signal. */
|
|
3966
|
-
whyWinnable:
|
|
4198
|
+
whyWinnable: z26.string().trim().min(1),
|
|
3967
4199
|
/** The schema.org type or markup to add or extend. */
|
|
3968
|
-
schemaHookup:
|
|
4200
|
+
schemaHookup: z26.string().trim().min(1),
|
|
3969
4201
|
/** Why the cited surface is controllable (the ownable-vs-ceded reasoning). */
|
|
3970
|
-
controllableSurfaceRationale:
|
|
4202
|
+
controllableSurfaceRationale: z26.string().trim().min(1)
|
|
3971
4203
|
});
|
|
3972
|
-
var recommendationBriefDtoSchema =
|
|
3973
|
-
targetRef:
|
|
4204
|
+
var recommendationBriefDtoSchema = z26.object({
|
|
4205
|
+
targetRef: z26.string(),
|
|
3974
4206
|
/** Version of the brief prompt template; bumping invalidates the cache forward. */
|
|
3975
|
-
promptVersion:
|
|
3976
|
-
provider:
|
|
3977
|
-
model:
|
|
4207
|
+
promptVersion: z26.string(),
|
|
4208
|
+
provider: z26.string(),
|
|
4209
|
+
model: z26.string(),
|
|
3978
4210
|
brief: contentBriefDtoSchema,
|
|
3979
4211
|
/** Estimated cost in millicents (1/100 of a cent). 0 when unknown. */
|
|
3980
|
-
costMillicents:
|
|
3981
|
-
generatedAt:
|
|
4212
|
+
costMillicents: z26.number().int().nonnegative(),
|
|
4213
|
+
generatedAt: z26.string()
|
|
3982
4214
|
});
|
|
3983
|
-
var domainClassificationDtoSchema =
|
|
3984
|
-
domain:
|
|
4215
|
+
var domainClassificationDtoSchema = z26.object({
|
|
4216
|
+
domain: z26.string(),
|
|
3985
4217
|
competitorType: discoveryCompetitorTypeSchema,
|
|
3986
|
-
hits:
|
|
3987
|
-
updatedAt:
|
|
4218
|
+
hits: z26.number().int().nonnegative(),
|
|
4219
|
+
updatedAt: z26.string()
|
|
3988
4220
|
});
|
|
3989
|
-
var domainClassificationsResponseDtoSchema =
|
|
3990
|
-
classifications:
|
|
3991
|
-
});
|
|
3992
|
-
var contentGroundingSourceSchema = z25.object({
|
|
3993
|
-
uri: z25.string(),
|
|
3994
|
-
title: z25.string(),
|
|
3995
|
-
domain: z25.string(),
|
|
3996
|
-
isOurDomain: z25.boolean(),
|
|
3997
|
-
isCompetitor: z25.boolean(),
|
|
3998
|
-
citationCount: z25.number().int().nonnegative(),
|
|
3999
|
-
providers: z25.array(providerNameSchema)
|
|
4221
|
+
var domainClassificationsResponseDtoSchema = z26.object({
|
|
4222
|
+
classifications: z26.array(domainClassificationDtoSchema)
|
|
4000
4223
|
});
|
|
4001
|
-
var
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4224
|
+
var contentGroundingSourceSchema = z26.object({
|
|
4225
|
+
uri: z26.string(),
|
|
4226
|
+
title: z26.string(),
|
|
4227
|
+
domain: z26.string(),
|
|
4228
|
+
isOurDomain: z26.boolean(),
|
|
4229
|
+
isCompetitor: z26.boolean(),
|
|
4230
|
+
citationCount: z26.number().int().nonnegative(),
|
|
4231
|
+
providers: z26.array(providerNameSchema)
|
|
4232
|
+
});
|
|
4233
|
+
var contentSourceRowDtoSchema = z26.object({
|
|
4234
|
+
query: z26.string(),
|
|
4235
|
+
groundingSources: z26.array(contentGroundingSourceSchema)
|
|
4236
|
+
});
|
|
4237
|
+
var contentSourcesResponseDtoSchema = z26.object({
|
|
4238
|
+
sources: z26.array(contentSourceRowDtoSchema),
|
|
4239
|
+
latestRunId: z26.string()
|
|
4240
|
+
});
|
|
4241
|
+
var contentGapRowDtoSchema = z26.object({
|
|
4242
|
+
query: z26.string(),
|
|
4243
|
+
competitorDomains: z26.array(z26.string()),
|
|
4244
|
+
competitorCount: z26.number().int().nonnegative(),
|
|
4245
|
+
missRate: z26.number().min(0).max(1),
|
|
4246
|
+
lastSeenInRunId: z26.string()
|
|
4247
|
+
});
|
|
4248
|
+
var contentGapsResponseDtoSchema = z26.object({
|
|
4249
|
+
gaps: z26.array(contentGapRowDtoSchema),
|
|
4250
|
+
latestRunId: z26.string()
|
|
4019
4251
|
});
|
|
4020
4252
|
|
|
4021
4253
|
// ../contracts/src/doctor.ts
|
|
4022
|
-
import { z as
|
|
4023
|
-
var checkStatusSchema =
|
|
4254
|
+
import { z as z27 } from "zod";
|
|
4255
|
+
var checkStatusSchema = z27.enum(["ok", "warn", "fail", "skipped"]);
|
|
4024
4256
|
var CheckStatuses = checkStatusSchema.enum;
|
|
4025
|
-
var checkScopeSchema =
|
|
4257
|
+
var checkScopeSchema = z27.enum(["global", "project"]);
|
|
4026
4258
|
var CheckScopes = checkScopeSchema.enum;
|
|
4027
|
-
var checkCategorySchema =
|
|
4259
|
+
var checkCategorySchema = z27.enum([
|
|
4028
4260
|
"auth",
|
|
4029
4261
|
"config",
|
|
4030
4262
|
"providers",
|
|
@@ -4035,31 +4267,31 @@ var checkCategorySchema = z26.enum([
|
|
|
4035
4267
|
"agent"
|
|
4036
4268
|
]);
|
|
4037
4269
|
var CheckCategories = checkCategorySchema.enum;
|
|
4038
|
-
var checkResultSchema =
|
|
4039
|
-
id:
|
|
4270
|
+
var checkResultSchema = z27.object({
|
|
4271
|
+
id: z27.string(),
|
|
4040
4272
|
category: checkCategorySchema,
|
|
4041
4273
|
scope: checkScopeSchema,
|
|
4042
|
-
title:
|
|
4274
|
+
title: z27.string(),
|
|
4043
4275
|
status: checkStatusSchema,
|
|
4044
|
-
code:
|
|
4045
|
-
summary:
|
|
4046
|
-
remediation:
|
|
4047
|
-
details:
|
|
4048
|
-
durationMs:
|
|
4276
|
+
code: z27.string().describe('Stable machine-readable code (e.g. "google.token.refresh-failed"). Use this for filtering and remediation logic.'),
|
|
4277
|
+
summary: z27.string(),
|
|
4278
|
+
remediation: z27.string().nullable().optional().describe('Operator-facing next step. Null when status is "ok" or no specific remediation applies.'),
|
|
4279
|
+
details: z27.record(z27.string(), z27.unknown()).optional().describe("Structured context \u2014 principal email, redirect URI, missing scopes, etc. Stable per check id."),
|
|
4280
|
+
durationMs: z27.number().int().nonnegative().describe("How long the check took to execute.")
|
|
4049
4281
|
});
|
|
4050
|
-
var doctorReportSchema =
|
|
4282
|
+
var doctorReportSchema = z27.object({
|
|
4051
4283
|
scope: checkScopeSchema,
|
|
4052
|
-
project:
|
|
4053
|
-
generatedAt:
|
|
4054
|
-
durationMs:
|
|
4055
|
-
summary:
|
|
4056
|
-
total:
|
|
4057
|
-
ok:
|
|
4058
|
-
warn:
|
|
4059
|
-
fail:
|
|
4060
|
-
skipped:
|
|
4284
|
+
project: z27.string().nullable().describe('Project name when scope is "project", null otherwise.'),
|
|
4285
|
+
generatedAt: z27.string().describe("ISO-8601 timestamp when this doctor run started."),
|
|
4286
|
+
durationMs: z27.number().int().nonnegative(),
|
|
4287
|
+
summary: z27.object({
|
|
4288
|
+
total: z27.number().int().nonnegative(),
|
|
4289
|
+
ok: z27.number().int().nonnegative(),
|
|
4290
|
+
warn: z27.number().int().nonnegative(),
|
|
4291
|
+
fail: z27.number().int().nonnegative(),
|
|
4292
|
+
skipped: z27.number().int().nonnegative()
|
|
4061
4293
|
}),
|
|
4062
|
-
checks:
|
|
4294
|
+
checks: z27.array(checkResultSchema)
|
|
4063
4295
|
});
|
|
4064
4296
|
function summarizeCheckResults(results) {
|
|
4065
4297
|
const summary = { total: results.length, ok: 0, warn: 0, fail: 0, skipped: 0 };
|
|
@@ -4088,52 +4320,52 @@ function normalizeQueryText(value) {
|
|
|
4088
4320
|
}
|
|
4089
4321
|
|
|
4090
4322
|
// ../contracts/src/citations.ts
|
|
4091
|
-
import { z as
|
|
4092
|
-
var citationCoverageProviderSchema =
|
|
4093
|
-
provider:
|
|
4323
|
+
import { z as z28 } from "zod";
|
|
4324
|
+
var citationCoverageProviderSchema = z28.object({
|
|
4325
|
+
provider: z28.string(),
|
|
4094
4326
|
citationState: citationStateSchema,
|
|
4095
|
-
cited:
|
|
4096
|
-
mentioned:
|
|
4097
|
-
runId:
|
|
4098
|
-
runCreatedAt:
|
|
4099
|
-
});
|
|
4100
|
-
var citationCoverageRowSchema =
|
|
4101
|
-
queryId:
|
|
4102
|
-
query:
|
|
4103
|
-
providers:
|
|
4104
|
-
citedCount:
|
|
4105
|
-
mentionedCount:
|
|
4106
|
-
totalProviders:
|
|
4107
|
-
});
|
|
4108
|
-
var competitorGapRowSchema =
|
|
4109
|
-
queryId:
|
|
4110
|
-
query:
|
|
4111
|
-
provider:
|
|
4112
|
-
citingCompetitors:
|
|
4113
|
-
runId:
|
|
4114
|
-
runCreatedAt:
|
|
4115
|
-
});
|
|
4116
|
-
var citationVisibilitySummarySchema =
|
|
4117
|
-
providersConfigured:
|
|
4118
|
-
providersCiting:
|
|
4119
|
-
providersMentioning:
|
|
4120
|
-
totalQueries:
|
|
4327
|
+
cited: z28.boolean(),
|
|
4328
|
+
mentioned: z28.boolean(),
|
|
4329
|
+
runId: z28.string(),
|
|
4330
|
+
runCreatedAt: z28.string()
|
|
4331
|
+
});
|
|
4332
|
+
var citationCoverageRowSchema = z28.object({
|
|
4333
|
+
queryId: z28.string(),
|
|
4334
|
+
query: z28.string(),
|
|
4335
|
+
providers: z28.array(citationCoverageProviderSchema),
|
|
4336
|
+
citedCount: z28.number().int().nonnegative(),
|
|
4337
|
+
mentionedCount: z28.number().int().nonnegative(),
|
|
4338
|
+
totalProviders: z28.number().int().nonnegative()
|
|
4339
|
+
});
|
|
4340
|
+
var competitorGapRowSchema = z28.object({
|
|
4341
|
+
queryId: z28.string(),
|
|
4342
|
+
query: z28.string(),
|
|
4343
|
+
provider: z28.string(),
|
|
4344
|
+
citingCompetitors: z28.array(z28.string()),
|
|
4345
|
+
runId: z28.string(),
|
|
4346
|
+
runCreatedAt: z28.string()
|
|
4347
|
+
});
|
|
4348
|
+
var citationVisibilitySummarySchema = z28.object({
|
|
4349
|
+
providersConfigured: z28.number().int().nonnegative(),
|
|
4350
|
+
providersCiting: z28.number().int().nonnegative(),
|
|
4351
|
+
providersMentioning: z28.number().int().nonnegative(),
|
|
4352
|
+
totalQueries: z28.number().int().nonnegative(),
|
|
4121
4353
|
// Cross-tab buckets — each tracked query with at least one snapshot lands
|
|
4122
4354
|
// in exactly one of these. Queries with zero snapshots are not counted in
|
|
4123
4355
|
// any bucket; (sum of buckets) ≤ totalQueries.
|
|
4124
|
-
queriesCitedAndMentioned:
|
|
4125
|
-
queriesCitedOnly:
|
|
4126
|
-
queriesMentionedOnly:
|
|
4127
|
-
queriesInvisible:
|
|
4128
|
-
latestRunId:
|
|
4129
|
-
latestRunAt:
|
|
4130
|
-
});
|
|
4131
|
-
var citationVisibilityResponseSchema =
|
|
4356
|
+
queriesCitedAndMentioned: z28.number().int().nonnegative(),
|
|
4357
|
+
queriesCitedOnly: z28.number().int().nonnegative(),
|
|
4358
|
+
queriesMentionedOnly: z28.number().int().nonnegative(),
|
|
4359
|
+
queriesInvisible: z28.number().int().nonnegative(),
|
|
4360
|
+
latestRunId: z28.string().nullable(),
|
|
4361
|
+
latestRunAt: z28.string().nullable()
|
|
4362
|
+
});
|
|
4363
|
+
var citationVisibilityResponseSchema = z28.object({
|
|
4132
4364
|
summary: citationVisibilitySummarySchema,
|
|
4133
|
-
byQuery:
|
|
4134
|
-
competitorGaps:
|
|
4135
|
-
status:
|
|
4136
|
-
reason:
|
|
4365
|
+
byQuery: z28.array(citationCoverageRowSchema),
|
|
4366
|
+
competitorGaps: z28.array(competitorGapRowSchema),
|
|
4367
|
+
status: z28.enum(["ready", "no-data"]),
|
|
4368
|
+
reason: z28.enum(["no-runs-yet", "no-queries"]).optional()
|
|
4137
4369
|
});
|
|
4138
4370
|
function emptyCitationVisibility(reason) {
|
|
4139
4371
|
return {
|
|
@@ -4160,10 +4392,10 @@ function citationStateToCited(state) {
|
|
|
4160
4392
|
}
|
|
4161
4393
|
|
|
4162
4394
|
// ../contracts/src/report.ts
|
|
4163
|
-
import { z as
|
|
4395
|
+
import { z as z29 } from "zod";
|
|
4164
4396
|
var REPORT_PERIOD_OPTIONS = [7, 14, 30, 90];
|
|
4165
4397
|
var REPORT_DEFAULT_PERIOD_DAYS = 30;
|
|
4166
|
-
var reportPeriodSchema =
|
|
4398
|
+
var reportPeriodSchema = z29.union([z29.literal(7), z29.literal(14), z29.literal(30), z29.literal(90)]).describe("Report window in days (7, 14, 30, or 90). Defaults to 30 when omitted.");
|
|
4167
4399
|
function isReportPeriodDays(n) {
|
|
4168
4400
|
return REPORT_PERIOD_OPTIONS.includes(n);
|
|
4169
4401
|
}
|
|
@@ -4178,45 +4410,45 @@ function parseReportPeriodDays(value) {
|
|
|
4178
4410
|
function reportComparisonWindowDays(periodDays) {
|
|
4179
4411
|
return Math.max(1, Math.floor(periodDays / 2));
|
|
4180
4412
|
}
|
|
4181
|
-
var providerLocationTreatmentSchema =
|
|
4413
|
+
var providerLocationTreatmentSchema = z29.enum([
|
|
4182
4414
|
"prompt",
|
|
4183
4415
|
"request-param",
|
|
4184
4416
|
"browser-geo",
|
|
4185
4417
|
"ignored"
|
|
4186
4418
|
]);
|
|
4187
|
-
var reportMetaLocationSchema =
|
|
4419
|
+
var reportMetaLocationSchema = z29.object({
|
|
4188
4420
|
/** Human-readable label as configured on the project (e.g. "michigan"). */
|
|
4189
|
-
label:
|
|
4421
|
+
label: z29.string(),
|
|
4190
4422
|
/** Resolved city/region/country from the project's `LocationContext`. */
|
|
4191
|
-
city:
|
|
4192
|
-
region:
|
|
4193
|
-
country:
|
|
4423
|
+
city: z29.string(),
|
|
4424
|
+
region: z29.string(),
|
|
4425
|
+
country: z29.string(),
|
|
4194
4426
|
/**
|
|
4195
4427
|
* Other locations configured on the project that did NOT power this report.
|
|
4196
4428
|
* When non-empty, callers should make clear that the report is location-scoped:
|
|
4197
4429
|
* a separate sweep is needed to see how AI engines respond from each one.
|
|
4198
4430
|
*/
|
|
4199
|
-
otherConfiguredLabels:
|
|
4431
|
+
otherConfiguredLabels: z29.array(z29.string())
|
|
4200
4432
|
});
|
|
4201
|
-
var reportProviderLocationHandlingSchema =
|
|
4433
|
+
var reportProviderLocationHandlingSchema = z29.object({
|
|
4202
4434
|
/** Provider name (matches `query_snapshots.provider`). */
|
|
4203
|
-
provider:
|
|
4435
|
+
provider: z29.string(),
|
|
4204
4436
|
/** How this provider applied the configured location during this run. */
|
|
4205
4437
|
treatment: providerLocationTreatmentSchema,
|
|
4206
4438
|
/** One-sentence explanation suitable for the report. */
|
|
4207
|
-
description:
|
|
4439
|
+
description: z29.string()
|
|
4208
4440
|
});
|
|
4209
|
-
var reportMetaSchema =
|
|
4441
|
+
var reportMetaSchema = z29.object({
|
|
4210
4442
|
/** ISO timestamp the report was generated (server clock). */
|
|
4211
|
-
generatedAt:
|
|
4443
|
+
generatedAt: z29.string(),
|
|
4212
4444
|
/** Project the report covers. */
|
|
4213
|
-
project:
|
|
4214
|
-
id:
|
|
4215
|
-
name:
|
|
4216
|
-
displayName:
|
|
4217
|
-
canonicalDomain:
|
|
4218
|
-
country:
|
|
4219
|
-
language:
|
|
4445
|
+
project: z29.object({
|
|
4446
|
+
id: z29.string(),
|
|
4447
|
+
name: z29.string(),
|
|
4448
|
+
displayName: z29.string(),
|
|
4449
|
+
canonicalDomain: z29.string(),
|
|
4450
|
+
country: z29.string(),
|
|
4451
|
+
language: z29.string()
|
|
4220
4452
|
}),
|
|
4221
4453
|
/**
|
|
4222
4454
|
* The location that powered the latest visibility run, when one was set.
|
|
@@ -4231,31 +4463,31 @@ var reportMetaSchema = z28.object({
|
|
|
4231
4463
|
* each provider's answer — some providers append it to the prompt, some
|
|
4232
4464
|
* pass it as a structured request field, and some (CDP) ignore it.
|
|
4233
4465
|
*/
|
|
4234
|
-
providerLocationHandling:
|
|
4466
|
+
providerLocationHandling: z29.array(reportProviderLocationHandlingSchema),
|
|
4235
4467
|
/** Earliest data point referenced by the report (ISO date). */
|
|
4236
|
-
periodStart:
|
|
4468
|
+
periodStart: z29.string().nullable(),
|
|
4237
4469
|
/** Latest data point referenced by the report (ISO date). */
|
|
4238
|
-
periodEnd:
|
|
4470
|
+
periodEnd: z29.string().nullable(),
|
|
4239
4471
|
/**
|
|
4240
4472
|
* The selected report window, in days (one of `REPORT_PERIOD_OPTIONS`).
|
|
4241
4473
|
* Every time-windowed section scopes to this many days; renderers read it to
|
|
4242
4474
|
* label the window ("Last 30 days", "(30d)"). Defaults to
|
|
4243
4475
|
* `REPORT_DEFAULT_PERIOD_DAYS` when no `period` is requested.
|
|
4244
4476
|
*/
|
|
4245
|
-
periodDays:
|
|
4477
|
+
periodDays: z29.number().int().positive()
|
|
4246
4478
|
});
|
|
4247
|
-
var reportExecutiveSummarySchema =
|
|
4479
|
+
var reportExecutiveSummarySchema = z29.object({
|
|
4248
4480
|
/**
|
|
4249
4481
|
* 0..100 — share of tracked queries that were cited by at least one
|
|
4250
4482
|
* provider in the latest run. "Cited" means the project's domain appeared
|
|
4251
4483
|
* in the source list / grounding the AI used to answer. Computed per-query
|
|
4252
4484
|
* (not per-(query × provider)) so the rate is invariant to provider count.
|
|
4253
4485
|
*/
|
|
4254
|
-
citationRate:
|
|
4486
|
+
citationRate: z29.number(),
|
|
4255
4487
|
/** Numerator of `citationRate` — distinct tracked queries cited by ≥1 provider in the latest run. */
|
|
4256
|
-
citedQueryCount:
|
|
4488
|
+
citedQueryCount: z29.number(),
|
|
4257
4489
|
/** Denominator of `citationRate` — total tracked queries. */
|
|
4258
|
-
totalQueryCount:
|
|
4490
|
+
totalQueryCount: z29.number(),
|
|
4259
4491
|
/**
|
|
4260
4492
|
* 0..100 — share of tracked queries where the project's brand or domain
|
|
4261
4493
|
* appeared in at least one provider's answer text in the latest run.
|
|
@@ -4263,71 +4495,71 @@ var reportExecutiveSummarySchema = z28.object({
|
|
|
4263
4495
|
* the prose without citing your domain in its sources, and vice versa.
|
|
4264
4496
|
* Same per-query denominator as `citationRate` for consistency.
|
|
4265
4497
|
*/
|
|
4266
|
-
mentionRate:
|
|
4498
|
+
mentionRate: z29.number(),
|
|
4267
4499
|
/** Numerator of `mentionRate` — distinct tracked queries mentioned in ≥1 provider's answer text. */
|
|
4268
|
-
mentionedQueryCount:
|
|
4500
|
+
mentionedQueryCount: z29.number(),
|
|
4269
4501
|
/** Compared to the previous run: 'up' | 'down' | 'flat' | 'unknown' (no prior run). */
|
|
4270
|
-
trend:
|
|
4502
|
+
trend: z29.enum(["up", "down", "flat", "unknown"]),
|
|
4271
4503
|
/** Total tracked queries. */
|
|
4272
|
-
queryCount:
|
|
4504
|
+
queryCount: z29.number(),
|
|
4273
4505
|
/** Total tracked competitors. */
|
|
4274
|
-
competitorCount:
|
|
4506
|
+
competitorCount: z29.number(),
|
|
4275
4507
|
/** Number of providers in the latest run. */
|
|
4276
|
-
providerCount:
|
|
4508
|
+
providerCount: z29.number(),
|
|
4277
4509
|
/** GSC totals across the most-recent sync window. Null when GSC is not connected. */
|
|
4278
|
-
gsc:
|
|
4279
|
-
clicks:
|
|
4280
|
-
impressions:
|
|
4281
|
-
ctr:
|
|
4282
|
-
avgPosition:
|
|
4283
|
-
periodStart:
|
|
4284
|
-
periodEnd:
|
|
4510
|
+
gsc: z29.object({
|
|
4511
|
+
clicks: z29.number(),
|
|
4512
|
+
impressions: z29.number(),
|
|
4513
|
+
ctr: z29.number(),
|
|
4514
|
+
avgPosition: z29.number(),
|
|
4515
|
+
periodStart: z29.string(),
|
|
4516
|
+
periodEnd: z29.string()
|
|
4285
4517
|
}).nullable(),
|
|
4286
4518
|
/** GA4 totals across the most-recent sync period. Null when GA4 is not connected. */
|
|
4287
|
-
ga:
|
|
4288
|
-
sessions:
|
|
4289
|
-
users:
|
|
4290
|
-
periodStart:
|
|
4291
|
-
periodEnd:
|
|
4519
|
+
ga: z29.object({
|
|
4520
|
+
sessions: z29.number(),
|
|
4521
|
+
users: z29.number(),
|
|
4522
|
+
periodStart: z29.string(),
|
|
4523
|
+
periodEnd: z29.string()
|
|
4292
4524
|
}).nullable(),
|
|
4293
4525
|
/** Top 3-5 findings, each rendered as a single-sentence narrative. */
|
|
4294
|
-
findings:
|
|
4295
|
-
title:
|
|
4296
|
-
detail:
|
|
4297
|
-
tone:
|
|
4526
|
+
findings: z29.array(z29.object({
|
|
4527
|
+
title: z29.string(),
|
|
4528
|
+
detail: z29.string(),
|
|
4529
|
+
tone: z29.enum(["positive", "caution", "negative", "neutral"])
|
|
4298
4530
|
}))
|
|
4299
4531
|
});
|
|
4300
|
-
var citationCellSchema =
|
|
4301
|
-
citationState:
|
|
4302
|
-
answerMentioned:
|
|
4303
|
-
model:
|
|
4532
|
+
var citationCellSchema = z29.object({
|
|
4533
|
+
citationState: z29.enum(["cited", "not-cited", "pending"]),
|
|
4534
|
+
answerMentioned: z29.boolean().nullable(),
|
|
4535
|
+
model: z29.string().nullable()
|
|
4304
4536
|
});
|
|
4305
|
-
var citationScorecardSchema =
|
|
4306
|
-
queries:
|
|
4307
|
-
providers:
|
|
4537
|
+
var citationScorecardSchema = z29.object({
|
|
4538
|
+
queries: z29.array(z29.string()),
|
|
4539
|
+
providers: z29.array(z29.string()),
|
|
4308
4540
|
/** matrix[queryIndex][providerIndex] — null when no snapshot exists for the pair. */
|
|
4309
|
-
matrix:
|
|
4541
|
+
matrix: z29.array(z29.array(citationCellSchema.nullable())),
|
|
4310
4542
|
/** Per-provider citation + mention rates (0..100). */
|
|
4311
|
-
providerRates:
|
|
4312
|
-
provider:
|
|
4313
|
-
citedCount:
|
|
4543
|
+
providerRates: z29.array(z29.object({
|
|
4544
|
+
provider: z29.string(),
|
|
4545
|
+
citedCount: z29.number(),
|
|
4314
4546
|
/** Number of snapshots for this provider where the answer text mentioned the project. */
|
|
4315
|
-
mentionedCount:
|
|
4316
|
-
totalCount:
|
|
4317
|
-
citationRate:
|
|
4318
|
-
mentionRate:
|
|
4547
|
+
mentionedCount: z29.number(),
|
|
4548
|
+
totalCount: z29.number(),
|
|
4549
|
+
citationRate: z29.number(),
|
|
4550
|
+
mentionRate: z29.number()
|
|
4319
4551
|
}))
|
|
4320
4552
|
});
|
|
4321
|
-
var competitorRowSchema =
|
|
4322
|
-
domain:
|
|
4553
|
+
var competitorRowSchema = z29.object({
|
|
4554
|
+
domain: z29.string(),
|
|
4323
4555
|
/** Number of (query × provider) pairs that cited this competitor. */
|
|
4324
|
-
citationCount:
|
|
4556
|
+
citationCount: z29.number(),
|
|
4325
4557
|
/** Out-of count for the same denominator. */
|
|
4326
|
-
totalCount:
|
|
4558
|
+
totalCount: z29.number(),
|
|
4327
4559
|
/** 'High' | 'Moderate' | 'Low' | 'None' — from buildPortfolioProject pressure logic. */
|
|
4328
|
-
pressureLabel:
|
|
4560
|
+
pressureLabel: z29.enum(["High", "Moderate", "Low", "None"]),
|
|
4329
4561
|
/** Distinct queries on which this competitor was cited. */
|
|
4330
|
-
citedQueries:
|
|
4562
|
+
citedQueries: z29.array(z29.string()),
|
|
4331
4563
|
/**
|
|
4332
4564
|
* Citation share 0..100. Numerator = this competitor's `citationCount`.
|
|
4333
4565
|
* Denominator = sum of `citationCount` across all competitors plus the
|
|
@@ -4335,30 +4567,30 @@ var competitorRowSchema = z28.object({
|
|
|
4335
4567
|
* slots in the snapshot. Distinct from the project-level Mention Share
|
|
4336
4568
|
* gauge — that one is brand-in-answer-text, this one is domain-in-source-list.
|
|
4337
4569
|
*/
|
|
4338
|
-
sharePct:
|
|
4570
|
+
sharePct: z29.number(),
|
|
4339
4571
|
/**
|
|
4340
4572
|
* URLs from the latest run's grounding sources whose host matches this
|
|
4341
4573
|
* competitor's domain, with the queries each URL was cited for. Empty
|
|
4342
4574
|
* when no grounding-source data is available (e.g. no `rawResponse` JSON
|
|
4343
4575
|
* stored for the snapshots).
|
|
4344
4576
|
*/
|
|
4345
|
-
theirCitedPages:
|
|
4577
|
+
theirCitedPages: z29.array(z29.object({ url: z29.string(), citedFor: z29.array(z29.string()) }))
|
|
4346
4578
|
});
|
|
4347
|
-
var competitorLandscapeSchema =
|
|
4579
|
+
var competitorLandscapeSchema = z29.object({
|
|
4348
4580
|
/** Project's own citation count (for the bar chart comparing project vs competitors). */
|
|
4349
|
-
projectCitationCount:
|
|
4350
|
-
competitors:
|
|
4581
|
+
projectCitationCount: z29.number(),
|
|
4582
|
+
competitors: z29.array(competitorRowSchema)
|
|
4351
4583
|
});
|
|
4352
|
-
var mentionRowSchema =
|
|
4353
|
-
domain:
|
|
4584
|
+
var mentionRowSchema = z29.object({
|
|
4585
|
+
domain: z29.string(),
|
|
4354
4586
|
/** Number of (query × provider) pairs whose answer text mentioned this competitor's brand or domain. */
|
|
4355
|
-
mentionCount:
|
|
4587
|
+
mentionCount: z29.number(),
|
|
4356
4588
|
/** Out-of count for the same denominator (snapshots that had answer text). */
|
|
4357
|
-
totalCount:
|
|
4589
|
+
totalCount: z29.number(),
|
|
4358
4590
|
/** 'High' | 'Moderate' | 'Low' | 'None' — mention frequency tier (mirrors CompetitorRow.pressureLabel). */
|
|
4359
|
-
pressureLabel:
|
|
4591
|
+
pressureLabel: z29.enum(["High", "Moderate", "Low", "None"]),
|
|
4360
4592
|
/** Distinct queries on which this competitor was mentioned. */
|
|
4361
|
-
mentionedQueries:
|
|
4593
|
+
mentionedQueries: z29.array(z29.string()),
|
|
4362
4594
|
/**
|
|
4363
4595
|
* Mention share 0..100. Numerator = this competitor's `mentionCount`.
|
|
4364
4596
|
* Denominator = sum of `mentionCount` across all competitors plus the
|
|
@@ -4366,135 +4598,135 @@ var mentionRowSchema = z28.object({
|
|
|
4366
4598
|
* mention. Per-competitor split of the same head-to-head measure the
|
|
4367
4599
|
* project's hero `MentionShareDto` gauge headlines.
|
|
4368
4600
|
*/
|
|
4369
|
-
sharePct:
|
|
4601
|
+
sharePct: z29.number()
|
|
4370
4602
|
});
|
|
4371
|
-
var mentionLandscapeSchema =
|
|
4603
|
+
var mentionLandscapeSchema = z29.object({
|
|
4372
4604
|
/** Project's own mention count (for the bar chart comparing project vs competitors). */
|
|
4373
|
-
projectMentionCount:
|
|
4605
|
+
projectMentionCount: z29.number(),
|
|
4374
4606
|
/** Snapshots considered — those with non-empty answerText. Drives the totalCount denominator. */
|
|
4375
|
-
totalAnswerSnapshots:
|
|
4376
|
-
competitors:
|
|
4607
|
+
totalAnswerSnapshots: z29.number(),
|
|
4608
|
+
competitors: z29.array(mentionRowSchema)
|
|
4377
4609
|
});
|
|
4378
|
-
var aiSourceCategoryBucketSchema =
|
|
4610
|
+
var aiSourceCategoryBucketSchema = z29.object({
|
|
4379
4611
|
/** Category slug from packages/contracts/src/source-categories. */
|
|
4380
|
-
category:
|
|
4612
|
+
category: z29.string(),
|
|
4381
4613
|
/** Display label. */
|
|
4382
|
-
label:
|
|
4614
|
+
label: z29.string(),
|
|
4383
4615
|
/** Number of citations falling in this category. */
|
|
4384
|
-
count:
|
|
4616
|
+
count: z29.number(),
|
|
4385
4617
|
/** 0..100 share of total citations. */
|
|
4386
|
-
sharePct:
|
|
4618
|
+
sharePct: z29.number()
|
|
4387
4619
|
});
|
|
4388
|
-
var aiSourceOriginSchema =
|
|
4389
|
-
categories:
|
|
4620
|
+
var aiSourceOriginSchema = z29.object({
|
|
4621
|
+
categories: z29.array(aiSourceCategoryBucketSchema),
|
|
4390
4622
|
/** Top 20 source domains by citation count (excluding the project's own domain). */
|
|
4391
|
-
topDomains:
|
|
4392
|
-
domain:
|
|
4393
|
-
count:
|
|
4623
|
+
topDomains: z29.array(z29.object({
|
|
4624
|
+
domain: z29.string(),
|
|
4625
|
+
count: z29.number(),
|
|
4394
4626
|
/** True when the domain is one of the project's tracked competitors. */
|
|
4395
|
-
isCompetitor:
|
|
4627
|
+
isCompetitor: z29.boolean()
|
|
4396
4628
|
}))
|
|
4397
4629
|
});
|
|
4398
|
-
var gscQueryRowSchema =
|
|
4399
|
-
query:
|
|
4400
|
-
clicks:
|
|
4401
|
-
impressions:
|
|
4402
|
-
ctr:
|
|
4403
|
-
avgPosition:
|
|
4630
|
+
var gscQueryRowSchema = z29.object({
|
|
4631
|
+
query: z29.string(),
|
|
4632
|
+
clicks: z29.number(),
|
|
4633
|
+
impressions: z29.number(),
|
|
4634
|
+
ctr: z29.number(),
|
|
4635
|
+
avgPosition: z29.number(),
|
|
4404
4636
|
/** Heuristic categorization: 'brand' | 'lead-gen' | 'industry' | 'other'. */
|
|
4405
|
-
category:
|
|
4406
|
-
});
|
|
4407
|
-
var gscSectionSchema =
|
|
4408
|
-
periodStart:
|
|
4409
|
-
periodEnd:
|
|
4410
|
-
totalClicks:
|
|
4411
|
-
totalImpressions:
|
|
4412
|
-
ctr:
|
|
4413
|
-
avgPosition:
|
|
4414
|
-
topQueries:
|
|
4415
|
-
categoryBreakdown:
|
|
4416
|
-
category:
|
|
4417
|
-
clicks:
|
|
4418
|
-
impressions:
|
|
4419
|
-
sharePct:
|
|
4637
|
+
category: z29.enum(["brand", "lead-gen", "industry", "other"])
|
|
4638
|
+
});
|
|
4639
|
+
var gscSectionSchema = z29.object({
|
|
4640
|
+
periodStart: z29.string(),
|
|
4641
|
+
periodEnd: z29.string(),
|
|
4642
|
+
totalClicks: z29.number(),
|
|
4643
|
+
totalImpressions: z29.number(),
|
|
4644
|
+
ctr: z29.number(),
|
|
4645
|
+
avgPosition: z29.number(),
|
|
4646
|
+
topQueries: z29.array(gscQueryRowSchema),
|
|
4647
|
+
categoryBreakdown: z29.array(z29.object({
|
|
4648
|
+
category: z29.enum(["brand", "lead-gen", "industry", "other"]),
|
|
4649
|
+
clicks: z29.number(),
|
|
4650
|
+
impressions: z29.number(),
|
|
4651
|
+
sharePct: z29.number()
|
|
4420
4652
|
})),
|
|
4421
|
-
trend:
|
|
4653
|
+
trend: z29.array(z29.object({ date: z29.string(), clicks: z29.number(), impressions: z29.number() })),
|
|
4422
4654
|
/**
|
|
4423
4655
|
* Tracked AEO queries that have no GSC impressions in the report window.
|
|
4424
4656
|
* Surfaces queries that may not represent real search demand.
|
|
4425
4657
|
*/
|
|
4426
|
-
trackedButNoGsc:
|
|
4658
|
+
trackedButNoGsc: z29.array(z29.string()),
|
|
4427
4659
|
/**
|
|
4428
4660
|
* GSC top queries (sorted by impressions desc) that are not tracked as
|
|
4429
4661
|
* AEO queries — the candidate set for adding to the AEO project.
|
|
4430
4662
|
*/
|
|
4431
|
-
gscButNotTracked:
|
|
4432
|
-
});
|
|
4433
|
-
var gaTrafficSectionSchema =
|
|
4434
|
-
totalSessions:
|
|
4435
|
-
totalUsers:
|
|
4436
|
-
totalOrganicSessions:
|
|
4437
|
-
periodStart:
|
|
4438
|
-
periodEnd:
|
|
4439
|
-
topLandingPages:
|
|
4440
|
-
page:
|
|
4441
|
-
sessions:
|
|
4442
|
-
users:
|
|
4443
|
-
organicSessions:
|
|
4663
|
+
gscButNotTracked: z29.array(z29.string())
|
|
4664
|
+
});
|
|
4665
|
+
var gaTrafficSectionSchema = z29.object({
|
|
4666
|
+
totalSessions: z29.number(),
|
|
4667
|
+
totalUsers: z29.number(),
|
|
4668
|
+
totalOrganicSessions: z29.number(),
|
|
4669
|
+
periodStart: z29.string(),
|
|
4670
|
+
periodEnd: z29.string(),
|
|
4671
|
+
topLandingPages: z29.array(z29.object({
|
|
4672
|
+
page: z29.string(),
|
|
4673
|
+
sessions: z29.number(),
|
|
4674
|
+
users: z29.number(),
|
|
4675
|
+
organicSessions: z29.number()
|
|
4444
4676
|
})),
|
|
4445
|
-
channelBreakdown:
|
|
4446
|
-
channel:
|
|
4447
|
-
sessions:
|
|
4448
|
-
sharePct:
|
|
4677
|
+
channelBreakdown: z29.array(z29.object({
|
|
4678
|
+
channel: z29.string(),
|
|
4679
|
+
sessions: z29.number(),
|
|
4680
|
+
sharePct: z29.number()
|
|
4449
4681
|
}))
|
|
4450
4682
|
});
|
|
4451
|
-
var socialReferralSectionSchema =
|
|
4452
|
-
totalSessions:
|
|
4453
|
-
organicSessions:
|
|
4454
|
-
paidSessions:
|
|
4455
|
-
channels:
|
|
4456
|
-
channelGroup:
|
|
4457
|
-
sessions:
|
|
4458
|
-
sharePct:
|
|
4683
|
+
var socialReferralSectionSchema = z29.object({
|
|
4684
|
+
totalSessions: z29.number(),
|
|
4685
|
+
organicSessions: z29.number(),
|
|
4686
|
+
paidSessions: z29.number(),
|
|
4687
|
+
channels: z29.array(z29.object({
|
|
4688
|
+
channelGroup: z29.string(),
|
|
4689
|
+
sessions: z29.number(),
|
|
4690
|
+
sharePct: z29.number()
|
|
4459
4691
|
})),
|
|
4460
|
-
topCampaigns:
|
|
4461
|
-
source:
|
|
4462
|
-
medium:
|
|
4463
|
-
sessions:
|
|
4692
|
+
topCampaigns: z29.array(z29.object({
|
|
4693
|
+
source: z29.string(),
|
|
4694
|
+
medium: z29.string(),
|
|
4695
|
+
sessions: z29.number()
|
|
4464
4696
|
}))
|
|
4465
4697
|
});
|
|
4466
|
-
var aiReferralSectionSchema =
|
|
4467
|
-
totalSessions:
|
|
4468
|
-
totalUsers:
|
|
4469
|
-
paidSessions:
|
|
4470
|
-
paidUsers:
|
|
4471
|
-
organicSessions:
|
|
4472
|
-
organicUsers:
|
|
4473
|
-
bySource:
|
|
4474
|
-
source:
|
|
4475
|
-
sessions:
|
|
4476
|
-
users:
|
|
4477
|
-
paidSessions:
|
|
4478
|
-
organicSessions:
|
|
4479
|
-
sharePct:
|
|
4698
|
+
var aiReferralSectionSchema = z29.object({
|
|
4699
|
+
totalSessions: z29.number(),
|
|
4700
|
+
totalUsers: z29.number(),
|
|
4701
|
+
paidSessions: z29.number(),
|
|
4702
|
+
paidUsers: z29.number(),
|
|
4703
|
+
organicSessions: z29.number(),
|
|
4704
|
+
organicUsers: z29.number(),
|
|
4705
|
+
bySource: z29.array(z29.object({
|
|
4706
|
+
source: z29.string(),
|
|
4707
|
+
sessions: z29.number(),
|
|
4708
|
+
users: z29.number(),
|
|
4709
|
+
paidSessions: z29.number(),
|
|
4710
|
+
organicSessions: z29.number(),
|
|
4711
|
+
sharePct: z29.number()
|
|
4480
4712
|
})),
|
|
4481
|
-
trend:
|
|
4482
|
-
topLandingPages:
|
|
4483
|
-
page:
|
|
4484
|
-
sessions:
|
|
4485
|
-
users:
|
|
4713
|
+
trend: z29.array(z29.object({ date: z29.string(), sessions: z29.number() })),
|
|
4714
|
+
topLandingPages: z29.array(z29.object({
|
|
4715
|
+
page: z29.string(),
|
|
4716
|
+
sessions: z29.number(),
|
|
4717
|
+
users: z29.number()
|
|
4486
4718
|
}))
|
|
4487
4719
|
});
|
|
4488
|
-
var serverActivitySectionSchema =
|
|
4720
|
+
var serverActivitySectionSchema = z29.object({
|
|
4489
4721
|
/** ISO8601 inclusive lower bound of the report window (default: 7 days). */
|
|
4490
|
-
windowStart:
|
|
4722
|
+
windowStart: z29.string(),
|
|
4491
4723
|
/** ISO8601 inclusive upper bound. */
|
|
4492
|
-
windowEnd:
|
|
4493
|
-
hasData:
|
|
4724
|
+
windowEnd: z29.string(),
|
|
4725
|
+
hasData: z29.boolean(),
|
|
4494
4726
|
/** Last-7d total verified crawler hits, with prior 7d for delta. */
|
|
4495
|
-
verifiedCrawlerHits:
|
|
4727
|
+
verifiedCrawlerHits: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
|
|
4496
4728
|
/** Last-7d total unverified crawler hits, separated from verified trust metrics. */
|
|
4497
|
-
unverifiedCrawlerHits:
|
|
4729
|
+
unverifiedCrawlerHits: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
|
|
4498
4730
|
/**
|
|
4499
4731
|
* Last-7d on-demand per-user fetches from AI surfaces (ChatGPT-User,
|
|
4500
4732
|
* Perplexity-User, MistralAI-User). Disjoint from `verifiedCrawlerHits` /
|
|
@@ -4503,19 +4735,34 @@ var serverActivitySectionSchema = z28.object({
|
|
|
4503
4735
|
* because the operational question for user-fetch is "is this happening?"
|
|
4504
4736
|
* not "is this a confirmed bot identity?"
|
|
4505
4737
|
*/
|
|
4506
|
-
aiUserFetchHits:
|
|
4507
|
-
/** Last-7d AI-referral sessions (sessionized from server-side request evidence). */
|
|
4508
|
-
referralArrivals:
|
|
4738
|
+
aiUserFetchHits: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
|
|
4739
|
+
/** Last-7d AI-referral sessions (sessionized from server-side request evidence). Paid + organic + unclassified. */
|
|
4740
|
+
referralArrivals: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
|
|
4741
|
+
/**
|
|
4742
|
+
* `referralArrivals` split by traffic class. The three buckets sum to it.
|
|
4743
|
+
*
|
|
4744
|
+
* `unclassified` counts sessions ingested before the classifier shipped: the
|
|
4745
|
+
* UTM tags that carry paid-ness were never persisted, so those sessions can
|
|
4746
|
+
* never be resolved. Reporting them as organic would overstate earned AI
|
|
4747
|
+
* traffic by exactly a client's ad volume.
|
|
4748
|
+
*/
|
|
4749
|
+
referralArrivalsByClass: z29.object({
|
|
4750
|
+
paid: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
|
|
4751
|
+
organic: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() }),
|
|
4752
|
+
unclassified: z29.object({ current: z29.number(), prior: z29.number(), deltaPct: z29.number().nullable() })
|
|
4753
|
+
}),
|
|
4754
|
+
/** Pre-rendered one-line breakdown, e.g. "Paid 1,200 · Organic 24". Empty when there is nothing to split. */
|
|
4755
|
+
referralArrivalsClassSummary: z29.string(),
|
|
4509
4756
|
/** Per-AI-operator breakdown (OpenAI, Anthropic, Google AI, Perplexity, …). */
|
|
4510
|
-
byOperator:
|
|
4511
|
-
operator:
|
|
4512
|
-
verifiedHits:
|
|
4757
|
+
byOperator: z29.array(z29.object({
|
|
4758
|
+
operator: z29.string(),
|
|
4759
|
+
verifiedHits: z29.number(),
|
|
4513
4760
|
/** Shown to agency audience only: claimed-bot UA, source IP not in a published range. */
|
|
4514
|
-
unverifiedHits:
|
|
4761
|
+
unverifiedHits: z29.number(),
|
|
4515
4762
|
/** Per-user fetches from this operator's AI surface (ChatGPT-User, …). */
|
|
4516
|
-
userFetchHits:
|
|
4517
|
-
referralArrivals:
|
|
4518
|
-
deltaPct:
|
|
4763
|
+
userFetchHits: z29.number(),
|
|
4764
|
+
referralArrivals: z29.number(),
|
|
4765
|
+
deltaPct: z29.number().nullable()
|
|
4519
4766
|
})),
|
|
4520
4767
|
/**
|
|
4521
4768
|
* Top crawled paths (verified only, last-7d). Path-level citation cross-reference
|
|
@@ -4525,88 +4772,88 @@ var serverActivitySectionSchema = z28.object({
|
|
|
4525
4772
|
* citation evidence can extend this entry with a `citationState` field without
|
|
4526
4773
|
* breaking the contract.
|
|
4527
4774
|
*/
|
|
4528
|
-
topCrawledPaths:
|
|
4529
|
-
path:
|
|
4530
|
-
verifiedHits:
|
|
4775
|
+
topCrawledPaths: z29.array(z29.object({
|
|
4776
|
+
path: z29.string(),
|
|
4777
|
+
verifiedHits: z29.number(),
|
|
4531
4778
|
/** How many distinct AI operators crawled this path in the window. */
|
|
4532
|
-
distinctOperators:
|
|
4779
|
+
distinctOperators: z29.number()
|
|
4533
4780
|
})),
|
|
4534
4781
|
/** AI products that sent ≥1 session in the window (referral by destination). */
|
|
4535
|
-
referralProducts:
|
|
4536
|
-
product:
|
|
4537
|
-
arrivals:
|
|
4538
|
-
distinctLandingPaths:
|
|
4782
|
+
referralProducts: z29.array(z29.object({
|
|
4783
|
+
product: z29.string(),
|
|
4784
|
+
arrivals: z29.number(),
|
|
4785
|
+
distinctLandingPaths: z29.number()
|
|
4539
4786
|
})),
|
|
4540
4787
|
/** Daily trend, last 14d for sparkline / chart rendering. */
|
|
4541
|
-
dailyTrend:
|
|
4542
|
-
date:
|
|
4543
|
-
verifiedCrawlerHits:
|
|
4544
|
-
userFetchHits:
|
|
4545
|
-
referralArrivals:
|
|
4788
|
+
dailyTrend: z29.array(z29.object({
|
|
4789
|
+
date: z29.string(),
|
|
4790
|
+
verifiedCrawlerHits: z29.number(),
|
|
4791
|
+
userFetchHits: z29.number(),
|
|
4792
|
+
referralArrivals: z29.number()
|
|
4546
4793
|
})),
|
|
4547
4794
|
/**
|
|
4548
4795
|
* Top landing paths for AI-referral sessions (last-7d).
|
|
4549
4796
|
* Complements `topCrawledPaths` (what bots fetch) with what humans actually land on.
|
|
4550
4797
|
*/
|
|
4551
|
-
topReferralLandingPaths:
|
|
4552
|
-
path:
|
|
4553
|
-
arrivals:
|
|
4554
|
-
distinctProducts:
|
|
4798
|
+
topReferralLandingPaths: z29.array(z29.object({
|
|
4799
|
+
path: z29.string(),
|
|
4800
|
+
arrivals: z29.number(),
|
|
4801
|
+
distinctProducts: z29.number()
|
|
4555
4802
|
}))
|
|
4556
4803
|
});
|
|
4557
|
-
var indexingHealthSectionSchema =
|
|
4804
|
+
var indexingHealthSectionSchema = z29.object({
|
|
4558
4805
|
/** Source: 'google' | 'bing' | null when neither is connected. */
|
|
4559
|
-
provider:
|
|
4560
|
-
total:
|
|
4561
|
-
indexed:
|
|
4562
|
-
notIndexed:
|
|
4806
|
+
provider: z29.enum(["google", "bing"]).nullable(),
|
|
4807
|
+
total: z29.number(),
|
|
4808
|
+
indexed: z29.number(),
|
|
4809
|
+
notIndexed: z29.number(),
|
|
4563
4810
|
/** Google-only — pages explicitly marked as deindexed. Bing reports 'unknown' instead. */
|
|
4564
|
-
deindexed:
|
|
4811
|
+
deindexed: z29.number(),
|
|
4565
4812
|
/** Bing-only — pages with no inspection data yet. */
|
|
4566
|
-
unknown:
|
|
4813
|
+
unknown: z29.number(),
|
|
4567
4814
|
/** 0..100. */
|
|
4568
|
-
indexedPct:
|
|
4815
|
+
indexedPct: z29.number()
|
|
4569
4816
|
});
|
|
4570
|
-
var citationsTrendPointSchema =
|
|
4817
|
+
var citationsTrendPointSchema = z29.object({
|
|
4571
4818
|
/** Run ID — anchor for cross-section linking. */
|
|
4572
|
-
runId:
|
|
4819
|
+
runId: z29.string(),
|
|
4573
4820
|
/** ISO timestamp when the run finished (or createdAt fallback). */
|
|
4574
|
-
date:
|
|
4821
|
+
date: z29.string(),
|
|
4575
4822
|
/**
|
|
4576
4823
|
* 0..100 — same per-query unique-cited definition as
|
|
4577
4824
|
* `ReportExecutiveSummary.citationRate`. Stable across runs with different
|
|
4578
4825
|
* provider counts so the trend line measures real movement rather than
|
|
4579
4826
|
* provider-count variance.
|
|
4580
4827
|
*/
|
|
4581
|
-
citationRate:
|
|
4828
|
+
citationRate: z29.number(),
|
|
4582
4829
|
/** Numerator of `citationRate` for this run. */
|
|
4583
|
-
citedQueryCount:
|
|
4830
|
+
citedQueryCount: z29.number(),
|
|
4584
4831
|
/** Denominator of `citationRate` for this run. */
|
|
4585
|
-
totalQueryCount:
|
|
4832
|
+
totalQueryCount: z29.number(),
|
|
4586
4833
|
/** 0..100 — same per-query unique-mentioned definition as `ReportExecutiveSummary.mentionRate`. */
|
|
4587
|
-
mentionRate:
|
|
4834
|
+
mentionRate: z29.number(),
|
|
4588
4835
|
/** Numerator of `mentionRate` for this run. */
|
|
4589
|
-
mentionedQueryCount:
|
|
4836
|
+
mentionedQueryCount: z29.number(),
|
|
4590
4837
|
/**
|
|
4591
4838
|
* Per-provider rates for the same run. Each provider's rate is per-pair
|
|
4592
4839
|
* within that provider (`cited / scanned`), so it remains comparable
|
|
4593
4840
|
* between providers in the same run.
|
|
4594
4841
|
*/
|
|
4595
|
-
providerRates:
|
|
4596
|
-
provider:
|
|
4597
|
-
citationRate:
|
|
4598
|
-
mentionRate:
|
|
4842
|
+
providerRates: z29.array(z29.object({
|
|
4843
|
+
provider: z29.string(),
|
|
4844
|
+
citationRate: z29.number(),
|
|
4845
|
+
mentionRate: z29.number()
|
|
4599
4846
|
}))
|
|
4600
4847
|
});
|
|
4601
|
-
var reportInsightSchema =
|
|
4602
|
-
id:
|
|
4603
|
-
type:
|
|
4604
|
-
severity:
|
|
4605
|
-
title:
|
|
4606
|
-
query:
|
|
4607
|
-
provider:
|
|
4608
|
-
recommendation:
|
|
4609
|
-
createdAt:
|
|
4848
|
+
var reportInsightSchema = z29.object({
|
|
4849
|
+
id: z29.string(),
|
|
4850
|
+
type: z29.enum(["regression", "gain", "opportunity"]),
|
|
4851
|
+
severity: z29.enum(["critical", "high", "medium", "low"]),
|
|
4852
|
+
title: z29.string(),
|
|
4853
|
+
query: z29.string(),
|
|
4854
|
+
provider: z29.string(),
|
|
4855
|
+
recommendation: z29.string().nullable(),
|
|
4856
|
+
createdAt: z29.string(),
|
|
4610
4857
|
/**
|
|
4611
4858
|
* How many times this insight fired across recent runs for the same
|
|
4612
4859
|
* `(query, provider, type)` tuple. Always ≥ 1. Insights returned by the
|
|
@@ -4614,64 +4861,64 @@ var reportInsightSchema = z28.object({
|
|
|
4614
4861
|
* surfacing the multiplicity. Use it directly instead of grouping again
|
|
4615
4862
|
* client-side — counts derived from raw insight rows will overcount.
|
|
4616
4863
|
*/
|
|
4617
|
-
instanceCount:
|
|
4864
|
+
instanceCount: z29.number()
|
|
4618
4865
|
});
|
|
4619
|
-
var recommendedNextStepSchema =
|
|
4866
|
+
var recommendedNextStepSchema = z29.object({
|
|
4620
4867
|
/** 'immediate' | 'short-term' | 'medium-term' — bucketed by severity heuristic. */
|
|
4621
|
-
horizon:
|
|
4622
|
-
title:
|
|
4623
|
-
rationale:
|
|
4868
|
+
horizon: z29.enum(["immediate", "short-term", "medium-term"]),
|
|
4869
|
+
title: z29.string(),
|
|
4870
|
+
rationale: z29.string()
|
|
4624
4871
|
});
|
|
4625
|
-
var reportRateDeltaSchema =
|
|
4872
|
+
var reportRateDeltaSchema = z29.object({
|
|
4626
4873
|
/** Current value (0..100 for rates, raw count otherwise). When `window`
|
|
4627
4874
|
* is present this is the average over the last `window` checks. */
|
|
4628
|
-
current:
|
|
4875
|
+
current: z29.number(),
|
|
4629
4876
|
/** Prior value compared against. When `window` is present this is the
|
|
4630
4877
|
* average over the prior `window` checks before that. */
|
|
4631
|
-
prior:
|
|
4878
|
+
prior: z29.number(),
|
|
4632
4879
|
/** Absolute delta (current − prior). Negative = decrease. */
|
|
4633
|
-
deltaAbs:
|
|
4880
|
+
deltaAbs: z29.number(),
|
|
4634
4881
|
/**
|
|
4635
4882
|
* Signed percent change vs `prior`, rounded to a whole number. Null when
|
|
4636
4883
|
* `prior <= 0` (percentage undefined). Renderers route count/traffic tiles
|
|
4637
4884
|
* through the "smart %" rule — percentage when the prior base is large
|
|
4638
4885
|
* enough (`MIN_PCT_BASE`), otherwise a rounded raw delta.
|
|
4639
4886
|
*/
|
|
4640
|
-
deltaPct:
|
|
4887
|
+
deltaPct: z29.number().nullable(),
|
|
4641
4888
|
/**
|
|
4642
4889
|
* Direction tag for tone mapping. Threshold is metric-specific (3pp for
|
|
4643
4890
|
* rates, 0.5 for counts) so small noise lands as 'flat' rather than
|
|
4644
4891
|
* flipping up/down each run.
|
|
4645
4892
|
*/
|
|
4646
|
-
direction:
|
|
4893
|
+
direction: z29.enum(["up", "down", "flat"]),
|
|
4647
4894
|
/**
|
|
4648
4895
|
* How many points went into each side of the average. Omitted (or 1)
|
|
4649
4896
|
* means point-to-point (legacy "since last check"). Higher values mean
|
|
4650
4897
|
* a rolling-average comparison — renderers should label it as
|
|
4651
4898
|
* "vs prior N checks" when this is ≥ 2.
|
|
4652
4899
|
*/
|
|
4653
|
-
window:
|
|
4900
|
+
window: z29.number().optional()
|
|
4654
4901
|
});
|
|
4655
|
-
var reportProviderMovementSchema =
|
|
4656
|
-
provider:
|
|
4657
|
-
current:
|
|
4658
|
-
prior:
|
|
4659
|
-
deltaAbs:
|
|
4660
|
-
direction:
|
|
4902
|
+
var reportProviderMovementSchema = z29.object({
|
|
4903
|
+
provider: z29.string(),
|
|
4904
|
+
current: z29.number(),
|
|
4905
|
+
prior: z29.number(),
|
|
4906
|
+
deltaAbs: z29.number(),
|
|
4907
|
+
direction: z29.enum(["up", "down", "flat"])
|
|
4661
4908
|
});
|
|
4662
|
-
var whatsChangedSectionSchema =
|
|
4909
|
+
var whatsChangedSectionSchema = z29.object({
|
|
4663
4910
|
/**
|
|
4664
4911
|
* False when there's no prior run (or fewer than the trend baseline),
|
|
4665
4912
|
* meaning all per-metric deltas will be null. Renderers use this to swap
|
|
4666
4913
|
* in a "establishing baseline" fallback rather than rendering empty
|
|
4667
4914
|
* delta tiles.
|
|
4668
4915
|
*/
|
|
4669
|
-
enoughHistory:
|
|
4916
|
+
enoughHistory: z29.boolean(),
|
|
4670
4917
|
/**
|
|
4671
4918
|
* One-sentence narrative summary suitable as a section subtitle.
|
|
4672
4919
|
* Always present — even on baseline, narrates whatever signal exists.
|
|
4673
4920
|
*/
|
|
4674
|
-
headline:
|
|
4921
|
+
headline: z29.string(),
|
|
4675
4922
|
/** Citation rate delta vs the prior completed run. Null when no prior run. */
|
|
4676
4923
|
citationRate: reportRateDeltaSchema.nullable(),
|
|
4677
4924
|
/** Mention rate delta vs the prior completed run. Null when no prior run. */
|
|
@@ -4699,30 +4946,30 @@ var whatsChangedSectionSchema = z28.object({
|
|
|
4699
4946
|
* deltas "vs prior {comparisonWindowDays} days" off this single value so the
|
|
4700
4947
|
* SPA and HTML stay verbatim-identical.
|
|
4701
4948
|
*/
|
|
4702
|
-
comparisonWindowDays:
|
|
4949
|
+
comparisonWindowDays: z29.number().int().positive(),
|
|
4703
4950
|
/**
|
|
4704
4951
|
* Per-provider citation rate movements (latest run vs prior run). Empty
|
|
4705
4952
|
* when no prior run. Sorted by |deltaAbs| desc — providers with the
|
|
4706
4953
|
* biggest swing first.
|
|
4707
4954
|
*/
|
|
4708
|
-
providerMovements:
|
|
4955
|
+
providerMovements: z29.array(reportProviderMovementSchema),
|
|
4709
4956
|
/**
|
|
4710
4957
|
* Top wins this period — gains surfaced by the intelligence engine.
|
|
4711
4958
|
* Capped at 5; sourced from `insights` filtered to `type: 'gain'`.
|
|
4712
4959
|
*/
|
|
4713
|
-
wins:
|
|
4960
|
+
wins: z29.array(reportInsightSchema),
|
|
4714
4961
|
/**
|
|
4715
4962
|
* Top regressions this period — citations or mentions lost. Capped at 5;
|
|
4716
4963
|
* sourced from `insights` filtered to `type: 'regression'`.
|
|
4717
4964
|
*/
|
|
4718
|
-
regressions:
|
|
4719
|
-
});
|
|
4720
|
-
var reportAudienceSchema =
|
|
4721
|
-
var reportActionAudienceSchema =
|
|
4722
|
-
var reportActionHorizonSchema =
|
|
4723
|
-
var reportActionConfidenceSchema =
|
|
4724
|
-
var reportToneSchema =
|
|
4725
|
-
var reportActionCategorySchema =
|
|
4965
|
+
regressions: z29.array(reportInsightSchema)
|
|
4966
|
+
});
|
|
4967
|
+
var reportAudienceSchema = z29.enum(["agency", "client"]);
|
|
4968
|
+
var reportActionAudienceSchema = z29.enum(["agency", "client", "both"]);
|
|
4969
|
+
var reportActionHorizonSchema = z29.enum(["immediate", "short-term", "medium-term"]);
|
|
4970
|
+
var reportActionConfidenceSchema = z29.enum(["high", "medium", "low"]);
|
|
4971
|
+
var reportToneSchema = z29.enum(["positive", "caution", "negative", "neutral"]);
|
|
4972
|
+
var reportActionCategorySchema = z29.enum([
|
|
4726
4973
|
"content",
|
|
4727
4974
|
"competitors",
|
|
4728
4975
|
"provider",
|
|
@@ -4731,23 +4978,23 @@ var reportActionCategorySchema = z28.enum([
|
|
|
4731
4978
|
"location",
|
|
4732
4979
|
"monitoring"
|
|
4733
4980
|
]);
|
|
4734
|
-
var reportActionPlanItemSchema =
|
|
4981
|
+
var reportActionPlanItemSchema = z29.object({
|
|
4735
4982
|
/** Which report audience should see this action. `both` renders in both modes. */
|
|
4736
4983
|
audience: reportActionAudienceSchema,
|
|
4737
4984
|
/** Stable sort priority. Lower numbers render earlier. */
|
|
4738
|
-
priority:
|
|
4985
|
+
priority: z29.number(),
|
|
4739
4986
|
/** When this should be tackled. */
|
|
4740
4987
|
horizon: reportActionHorizonSchema,
|
|
4741
4988
|
category: reportActionCategorySchema,
|
|
4742
|
-
title:
|
|
4989
|
+
title: z29.string(),
|
|
4743
4990
|
/** Direct next step written as an operator/client-friendly imperative. */
|
|
4744
|
-
action:
|
|
4991
|
+
action: z29.string(),
|
|
4745
4992
|
/** Why this matters. Keep each entry concise and evidence-backed. */
|
|
4746
|
-
why:
|
|
4993
|
+
why: z29.array(z29.string()),
|
|
4747
4994
|
/** Specific observations that justify the action. */
|
|
4748
|
-
evidence:
|
|
4995
|
+
evidence: z29.array(z29.string()),
|
|
4749
4996
|
/** What should move if the action worked. */
|
|
4750
|
-
successMetric:
|
|
4997
|
+
successMetric: z29.string(),
|
|
4751
4998
|
/** Confidence in the recommendation based on the available evidence. */
|
|
4752
4999
|
confidence: reportActionConfidenceSchema,
|
|
4753
5000
|
/**
|
|
@@ -4759,23 +5006,23 @@ var reportActionPlanItemSchema = z28.object({
|
|
|
4759
5006
|
* load. Actions sourced from other signals (competitor gaps, indexing
|
|
4760
5007
|
* issues, etc.) omit this and use their own dismiss flows.
|
|
4761
5008
|
*/
|
|
4762
|
-
targetRef:
|
|
5009
|
+
targetRef: z29.string().optional()
|
|
4763
5010
|
});
|
|
4764
|
-
var reportClientSummarySchema =
|
|
4765
|
-
headline:
|
|
4766
|
-
overview:
|
|
4767
|
-
actionItems:
|
|
4768
|
-
confidenceNotes:
|
|
5011
|
+
var reportClientSummarySchema = z29.object({
|
|
5012
|
+
headline: z29.string(),
|
|
5013
|
+
overview: z29.string(),
|
|
5014
|
+
actionItems: z29.array(reportActionPlanItemSchema),
|
|
5015
|
+
confidenceNotes: z29.array(z29.string())
|
|
4769
5016
|
});
|
|
4770
|
-
var reportAgencyDiagnosticSchema =
|
|
4771
|
-
title:
|
|
4772
|
-
detail:
|
|
4773
|
-
severity:
|
|
4774
|
-
evidence:
|
|
5017
|
+
var reportAgencyDiagnosticSchema = z29.object({
|
|
5018
|
+
title: z29.string(),
|
|
5019
|
+
detail: z29.string(),
|
|
5020
|
+
severity: z29.enum(["positive", "caution", "negative", "neutral"]),
|
|
5021
|
+
evidence: z29.array(z29.string())
|
|
4775
5022
|
});
|
|
4776
|
-
var reportAgencyDiagnosticsSchema =
|
|
4777
|
-
priorities:
|
|
4778
|
-
diagnostics:
|
|
5023
|
+
var reportAgencyDiagnosticsSchema = z29.object({
|
|
5024
|
+
priorities: z29.array(reportActionPlanItemSchema),
|
|
5025
|
+
diagnostics: z29.array(reportAgencyDiagnosticSchema)
|
|
4779
5026
|
});
|
|
4780
5027
|
function reportActionTone(action) {
|
|
4781
5028
|
if (action.horizon === "immediate") return "negative";
|
|
@@ -4833,7 +5080,7 @@ function reportConfidenceLabel(confidence) {
|
|
|
4833
5080
|
return "Low";
|
|
4834
5081
|
}
|
|
4835
5082
|
}
|
|
4836
|
-
var projectReportDtoSchema =
|
|
5083
|
+
var projectReportDtoSchema = z29.object({
|
|
4837
5084
|
meta: reportMetaSchema,
|
|
4838
5085
|
executiveSummary: reportExecutiveSummarySchema,
|
|
4839
5086
|
citationScorecard: citationScorecardSchema,
|
|
@@ -4847,16 +5094,16 @@ var projectReportDtoSchema = z28.object({
|
|
|
4847
5094
|
/** Server-side log-evidence visibility (crawls + click-through sessions). Null when no traffic source connected. */
|
|
4848
5095
|
serverActivity: serverActivitySectionSchema.nullable(),
|
|
4849
5096
|
indexingHealth: indexingHealthSectionSchema.nullable(),
|
|
4850
|
-
citationsTrend:
|
|
5097
|
+
citationsTrend: z29.array(citationsTrendPointSchema),
|
|
4851
5098
|
/**
|
|
4852
5099
|
* Trend-focused "what's changed" summary for the report's act 2. Always
|
|
4853
5100
|
* present; renderers gate empty/baseline states via `enoughHistory`.
|
|
4854
5101
|
*/
|
|
4855
5102
|
whatsChanged: whatsChangedSectionSchema,
|
|
4856
|
-
insights:
|
|
4857
|
-
recommendedNextSteps:
|
|
5103
|
+
insights: z29.array(reportInsightSchema),
|
|
5104
|
+
recommendedNextSteps: z29.array(recommendedNextStepSchema),
|
|
4858
5105
|
/** Canonical structured actions shared by the client and agency render modes. */
|
|
4859
|
-
actionPlan:
|
|
5106
|
+
actionPlan: z29.array(reportActionPlanItemSchema),
|
|
4860
5107
|
/** Polished client-facing summary and action shortlist. */
|
|
4861
5108
|
clientSummary: reportClientSummarySchema,
|
|
4862
5109
|
/** Technical, evidence-oriented operator diagnostics for agency mode. */
|
|
@@ -4866,17 +5113,17 @@ var projectReportDtoSchema = z28.object({
|
|
|
4866
5113
|
* intelligence layer (`buildContentTargetRows`). Empty when no run has
|
|
4867
5114
|
* produced candidate queries with demand or competitor signal.
|
|
4868
5115
|
*/
|
|
4869
|
-
contentOpportunities:
|
|
5116
|
+
contentOpportunities: z29.array(contentTargetRowDtoSchema),
|
|
4870
5117
|
/**
|
|
4871
5118
|
* Queries where competitors were cited but the project was not. Sourced
|
|
4872
5119
|
* from `buildContentGapRows`. Empty until the first answer-visibility run.
|
|
4873
5120
|
*/
|
|
4874
|
-
contentGaps:
|
|
5121
|
+
contentGaps: z29.array(contentGapRowDtoSchema),
|
|
4875
5122
|
/**
|
|
4876
5123
|
* Per-query grounding source map (own + competitor cited URLs). Sourced
|
|
4877
5124
|
* from `buildContentSourceRows`. Empty until the first answer-visibility run.
|
|
4878
5125
|
*/
|
|
4879
|
-
groundingSources:
|
|
5126
|
+
groundingSources: z29.array(contentSourceRowDtoSchema)
|
|
4880
5127
|
});
|
|
4881
5128
|
|
|
4882
5129
|
// ../contracts/src/report-dedup.ts
|
|
@@ -4947,10 +5194,10 @@ function dedupeReportOpportunities(report) {
|
|
|
4947
5194
|
}
|
|
4948
5195
|
|
|
4949
5196
|
// ../contracts/src/skills.ts
|
|
4950
|
-
import { z as
|
|
4951
|
-
var codingAgentSchema =
|
|
5197
|
+
import { z as z30 } from "zod";
|
|
5198
|
+
var codingAgentSchema = z30.enum(["claude", "codex"]);
|
|
4952
5199
|
var CodingAgents = codingAgentSchema.enum;
|
|
4953
|
-
var skillsClientSchema =
|
|
5200
|
+
var skillsClientSchema = z30.enum(["claude", "codex", "all"]);
|
|
4954
5201
|
var SkillsClients = skillsClientSchema.enum;
|
|
4955
5202
|
var SKILL_MANIFEST_FILENAME = ".canonry-skill-manifest.json";
|
|
4956
5203
|
function classifySkillFile(params) {
|
|
@@ -4968,22 +5215,22 @@ function coerceSkillManifest(parsed) {
|
|
|
4968
5215
|
}
|
|
4969
5216
|
|
|
4970
5217
|
// ../contracts/src/traffic.ts
|
|
4971
|
-
import { z as
|
|
4972
|
-
var trafficCrawlerSegmentsSchema =
|
|
4973
|
-
content:
|
|
4974
|
-
sitemap:
|
|
4975
|
-
robots:
|
|
4976
|
-
asset:
|
|
4977
|
-
other:
|
|
4978
|
-
});
|
|
4979
|
-
var trafficPathClassSchema =
|
|
5218
|
+
import { z as z31 } from "zod";
|
|
5219
|
+
var trafficCrawlerSegmentsSchema = z31.object({
|
|
5220
|
+
content: z31.number().int().nonnegative(),
|
|
5221
|
+
sitemap: z31.number().int().nonnegative(),
|
|
5222
|
+
robots: z31.number().int().nonnegative(),
|
|
5223
|
+
asset: z31.number().int().nonnegative(),
|
|
5224
|
+
other: z31.number().int().nonnegative()
|
|
5225
|
+
});
|
|
5226
|
+
var trafficPathClassSchema = z31.enum([
|
|
4980
5227
|
"content",
|
|
4981
5228
|
"sitemap",
|
|
4982
5229
|
"robots",
|
|
4983
5230
|
"asset",
|
|
4984
5231
|
"other"
|
|
4985
5232
|
]);
|
|
4986
|
-
var trafficSourceTypeSchema =
|
|
5233
|
+
var trafficSourceTypeSchema = z31.enum([
|
|
4987
5234
|
"cloud-run",
|
|
4988
5235
|
"wordpress",
|
|
4989
5236
|
"cloudflare",
|
|
@@ -4991,7 +5238,7 @@ var trafficSourceTypeSchema = z30.enum([
|
|
|
4991
5238
|
"generic-log"
|
|
4992
5239
|
]);
|
|
4993
5240
|
var TrafficSourceTypes = trafficSourceTypeSchema.enum;
|
|
4994
|
-
var trafficAdapterCapabilitySchema =
|
|
5241
|
+
var trafficAdapterCapabilitySchema = z31.enum([
|
|
4995
5242
|
"raw-request-events",
|
|
4996
5243
|
"aggregate-request-metrics",
|
|
4997
5244
|
"request-url",
|
|
@@ -5002,234 +5249,252 @@ var trafficAdapterCapabilitySchema = z30.enum([
|
|
|
5002
5249
|
"cursor-pull"
|
|
5003
5250
|
]);
|
|
5004
5251
|
var TrafficAdapterCapabilities = trafficAdapterCapabilitySchema.enum;
|
|
5005
|
-
var trafficEvidenceKindSchema =
|
|
5252
|
+
var trafficEvidenceKindSchema = z31.enum(["raw-request", "aggregate-bucket"]);
|
|
5006
5253
|
var TrafficEvidenceKinds = trafficEvidenceKindSchema.enum;
|
|
5007
|
-
var trafficEventConfidenceSchema =
|
|
5254
|
+
var trafficEventConfidenceSchema = z31.enum(["observed", "provider-aggregated", "inferred"]);
|
|
5008
5255
|
var TrafficEventConfidences = trafficEventConfidenceSchema.enum;
|
|
5009
|
-
var trafficProviderResourceSchema =
|
|
5010
|
-
type:
|
|
5011
|
-
labels:
|
|
5256
|
+
var trafficProviderResourceSchema = z31.object({
|
|
5257
|
+
type: z31.string().nullable(),
|
|
5258
|
+
labels: z31.record(z31.string(), z31.string())
|
|
5012
5259
|
});
|
|
5013
|
-
var normalizedTrafficRequestSchema =
|
|
5260
|
+
var normalizedTrafficRequestSchema = z31.object({
|
|
5014
5261
|
sourceType: trafficSourceTypeSchema,
|
|
5015
|
-
evidenceKind:
|
|
5016
|
-
confidence:
|
|
5017
|
-
eventId:
|
|
5018
|
-
observedAt:
|
|
5019
|
-
method:
|
|
5020
|
-
requestUrl:
|
|
5021
|
-
host:
|
|
5022
|
-
path:
|
|
5023
|
-
queryString:
|
|
5024
|
-
status:
|
|
5025
|
-
userAgent:
|
|
5026
|
-
remoteIp:
|
|
5027
|
-
referer:
|
|
5028
|
-
latencyMs:
|
|
5029
|
-
requestSizeBytes:
|
|
5030
|
-
responseSizeBytes:
|
|
5262
|
+
evidenceKind: z31.literal(TrafficEvidenceKinds["raw-request"]),
|
|
5263
|
+
confidence: z31.literal(TrafficEventConfidences.observed),
|
|
5264
|
+
eventId: z31.string().min(1),
|
|
5265
|
+
observedAt: z31.string().min(1),
|
|
5266
|
+
method: z31.string().nullable(),
|
|
5267
|
+
requestUrl: z31.string().nullable(),
|
|
5268
|
+
host: z31.string().nullable(),
|
|
5269
|
+
path: z31.string().min(1),
|
|
5270
|
+
queryString: z31.string().nullable(),
|
|
5271
|
+
status: z31.number().int().nullable(),
|
|
5272
|
+
userAgent: z31.string().nullable(),
|
|
5273
|
+
remoteIp: z31.string().nullable(),
|
|
5274
|
+
referer: z31.string().nullable(),
|
|
5275
|
+
latencyMs: z31.number().nullable(),
|
|
5276
|
+
requestSizeBytes: z31.number().int().nullable(),
|
|
5277
|
+
responseSizeBytes: z31.number().int().nullable(),
|
|
5031
5278
|
providerResource: trafficProviderResourceSchema,
|
|
5032
|
-
providerLabels:
|
|
5279
|
+
providerLabels: z31.record(z31.string(), z31.string())
|
|
5033
5280
|
});
|
|
5034
|
-
var normalizedTrafficPullPageSchema =
|
|
5035
|
-
events:
|
|
5036
|
-
rawEntryCount:
|
|
5037
|
-
skippedEntryCount:
|
|
5038
|
-
nextPageToken:
|
|
5039
|
-
filter:
|
|
5281
|
+
var normalizedTrafficPullPageSchema = z31.object({
|
|
5282
|
+
events: z31.array(normalizedTrafficRequestSchema),
|
|
5283
|
+
rawEntryCount: z31.number().int().nonnegative(),
|
|
5284
|
+
skippedEntryCount: z31.number().int().nonnegative(),
|
|
5285
|
+
nextPageToken: z31.string().optional(),
|
|
5286
|
+
filter: z31.string()
|
|
5040
5287
|
});
|
|
5041
|
-
var trafficSourceStatusSchema =
|
|
5288
|
+
var trafficSourceStatusSchema = z31.enum(["connected", "paused", "error", "archived"]);
|
|
5042
5289
|
var TrafficSourceStatuses = trafficSourceStatusSchema.enum;
|
|
5043
|
-
var trafficSourceAuthModeSchema =
|
|
5290
|
+
var trafficSourceAuthModeSchema = z31.enum(["oauth", "service-account"]);
|
|
5044
5291
|
var TrafficSourceAuthModes = trafficSourceAuthModeSchema.enum;
|
|
5045
|
-
var verificationStatusSchema =
|
|
5292
|
+
var verificationStatusSchema = z31.enum(["verified", "claimed_unverified", "unknown_ai_like"]);
|
|
5046
5293
|
var VerificationStatuses = verificationStatusSchema.enum;
|
|
5047
|
-
var cloudRunSourceConfigSchema =
|
|
5048
|
-
gcpProjectId:
|
|
5049
|
-
serviceName:
|
|
5050
|
-
location:
|
|
5294
|
+
var cloudRunSourceConfigSchema = z31.object({
|
|
5295
|
+
gcpProjectId: z31.string().min(1),
|
|
5296
|
+
serviceName: z31.string().nullable().optional(),
|
|
5297
|
+
location: z31.string().nullable().optional(),
|
|
5051
5298
|
authMode: trafficSourceAuthModeSchema
|
|
5052
5299
|
});
|
|
5053
|
-
var wordpressTrafficSourceConfigSchema =
|
|
5054
|
-
baseUrl:
|
|
5055
|
-
username:
|
|
5300
|
+
var wordpressTrafficSourceConfigSchema = z31.object({
|
|
5301
|
+
baseUrl: z31.string().url(),
|
|
5302
|
+
username: z31.string().min(1)
|
|
5056
5303
|
});
|
|
5057
|
-
var vercelTrafficEnvironmentSchema =
|
|
5304
|
+
var vercelTrafficEnvironmentSchema = z31.enum(["production", "preview"]);
|
|
5058
5305
|
var VercelTrafficEnvironments = vercelTrafficEnvironmentSchema.enum;
|
|
5059
|
-
var vercelTrafficSourceConfigSchema =
|
|
5306
|
+
var vercelTrafficSourceConfigSchema = z31.object({
|
|
5060
5307
|
/** Vercel project id (e.g. `prj_...`). */
|
|
5061
|
-
projectId:
|
|
5308
|
+
projectId: z31.string().min(1),
|
|
5062
5309
|
/** Vercel team or account id: the org that owns the project. */
|
|
5063
|
-
teamId:
|
|
5310
|
+
teamId: z31.string().min(1),
|
|
5064
5311
|
environment: vercelTrafficEnvironmentSchema
|
|
5065
5312
|
});
|
|
5066
|
-
var trafficSourceDtoSchema =
|
|
5067
|
-
id:
|
|
5068
|
-
projectId:
|
|
5313
|
+
var trafficSourceDtoSchema = z31.object({
|
|
5314
|
+
id: z31.string(),
|
|
5315
|
+
projectId: z31.string(),
|
|
5069
5316
|
sourceType: trafficSourceTypeSchema,
|
|
5070
|
-
displayName:
|
|
5317
|
+
displayName: z31.string(),
|
|
5071
5318
|
status: trafficSourceStatusSchema,
|
|
5072
|
-
lastSyncedAt:
|
|
5073
|
-
lastCursor:
|
|
5074
|
-
lastError:
|
|
5075
|
-
archivedAt:
|
|
5076
|
-
config:
|
|
5077
|
-
createdAt:
|
|
5078
|
-
updatedAt:
|
|
5079
|
-
});
|
|
5080
|
-
var trafficConnectCloudRunRequestSchema =
|
|
5081
|
-
gcpProjectId:
|
|
5082
|
-
serviceName:
|
|
5083
|
-
location:
|
|
5084
|
-
displayName:
|
|
5319
|
+
lastSyncedAt: z31.string().nullable(),
|
|
5320
|
+
lastCursor: z31.string().nullable(),
|
|
5321
|
+
lastError: z31.string().nullable(),
|
|
5322
|
+
archivedAt: z31.string().nullable(),
|
|
5323
|
+
config: z31.record(z31.string(), z31.unknown()),
|
|
5324
|
+
createdAt: z31.string(),
|
|
5325
|
+
updatedAt: z31.string()
|
|
5326
|
+
});
|
|
5327
|
+
var trafficConnectCloudRunRequestSchema = z31.object({
|
|
5328
|
+
gcpProjectId: z31.string().min(1),
|
|
5329
|
+
serviceName: z31.string().min(1).optional(),
|
|
5330
|
+
location: z31.string().min(1).optional(),
|
|
5331
|
+
displayName: z31.string().min(1).optional(),
|
|
5085
5332
|
/** Service-account JSON content (string). When omitted, defaults to OAuth via `canonry google connect <project> --type ga4` flow. */
|
|
5086
|
-
keyJson:
|
|
5333
|
+
keyJson: z31.string().optional()
|
|
5087
5334
|
});
|
|
5088
|
-
var trafficConnectWordpressRequestSchema =
|
|
5089
|
-
baseUrl:
|
|
5090
|
-
username:
|
|
5335
|
+
var trafficConnectWordpressRequestSchema = z31.object({
|
|
5336
|
+
baseUrl: z31.string().url(),
|
|
5337
|
+
username: z31.string().min(1),
|
|
5091
5338
|
/** WordPress Application Password (the same auth used by the content client). */
|
|
5092
|
-
applicationPassword:
|
|
5093
|
-
displayName:
|
|
5339
|
+
applicationPassword: z31.string().min(1),
|
|
5340
|
+
displayName: z31.string().min(1).optional()
|
|
5094
5341
|
});
|
|
5095
|
-
var trafficConnectVercelRequestSchema =
|
|
5342
|
+
var trafficConnectVercelRequestSchema = z31.object({
|
|
5096
5343
|
/** Vercel project id (e.g. `prj_...`) — from the Vercel dashboard or `.vercel/project.json`. */
|
|
5097
|
-
projectId:
|
|
5344
|
+
projectId: z31.string().min(1),
|
|
5098
5345
|
/** Vercel team or account id: the org that owns the project ("orgId" in .vercel/project.json). */
|
|
5099
|
-
teamId:
|
|
5346
|
+
teamId: z31.string().min(1),
|
|
5100
5347
|
/** Vercel personal access token. Stored in `~/.canonry/config.yaml`, never the DB. */
|
|
5101
|
-
token:
|
|
5348
|
+
token: z31.string().min(1),
|
|
5102
5349
|
/** Which deployment environment's request logs to pull. Default: `production`. */
|
|
5103
5350
|
environment: vercelTrafficEnvironmentSchema.optional(),
|
|
5104
|
-
displayName:
|
|
5351
|
+
displayName: z31.string().min(1).optional()
|
|
5105
5352
|
});
|
|
5106
|
-
var trafficSyncResponseSchema =
|
|
5107
|
-
sourceId:
|
|
5108
|
-
runId:
|
|
5109
|
-
syncedAt:
|
|
5110
|
-
pulledEvents:
|
|
5353
|
+
var trafficSyncResponseSchema = z31.object({
|
|
5354
|
+
sourceId: z31.string(),
|
|
5355
|
+
runId: z31.string(),
|
|
5356
|
+
syncedAt: z31.string(),
|
|
5357
|
+
pulledEvents: z31.number().int().nonnegative(),
|
|
5111
5358
|
/** Self-traffic events (Canonry's own tooling) dropped before rollup. */
|
|
5112
|
-
selfTrafficExcluded:
|
|
5113
|
-
crawlerHits:
|
|
5114
|
-
aiUserFetchHits:
|
|
5115
|
-
aiReferralHits:
|
|
5116
|
-
unknownHits:
|
|
5117
|
-
crawlerBucketRows:
|
|
5118
|
-
aiUserFetchBucketRows:
|
|
5119
|
-
aiReferralBucketRows:
|
|
5120
|
-
sampleRows:
|
|
5121
|
-
windowStart:
|
|
5122
|
-
windowEnd:
|
|
5123
|
-
});
|
|
5124
|
-
var trafficBackfillRequestSchema =
|
|
5359
|
+
selfTrafficExcluded: z31.number().int().nonnegative(),
|
|
5360
|
+
crawlerHits: z31.number().int().nonnegative(),
|
|
5361
|
+
aiUserFetchHits: z31.number().int().nonnegative(),
|
|
5362
|
+
aiReferralHits: z31.number().int().nonnegative(),
|
|
5363
|
+
unknownHits: z31.number().int().nonnegative(),
|
|
5364
|
+
crawlerBucketRows: z31.number().int().nonnegative(),
|
|
5365
|
+
aiUserFetchBucketRows: z31.number().int().nonnegative(),
|
|
5366
|
+
aiReferralBucketRows: z31.number().int().nonnegative(),
|
|
5367
|
+
sampleRows: z31.number().int().nonnegative(),
|
|
5368
|
+
windowStart: z31.string(),
|
|
5369
|
+
windowEnd: z31.string()
|
|
5370
|
+
});
|
|
5371
|
+
var trafficBackfillRequestSchema = z31.object({
|
|
5125
5372
|
/** Lookback window in days. Capped server-side at the upstream log retention ceiling (Cloud Logging _Default = 30d). Default: 30. */
|
|
5126
|
-
days:
|
|
5373
|
+
days: z31.number().int().positive().optional()
|
|
5127
5374
|
});
|
|
5128
|
-
var trafficResetRequestSchema =
|
|
5129
|
-
advanceToNow:
|
|
5375
|
+
var trafficResetRequestSchema = z31.object({
|
|
5376
|
+
advanceToNow: z31.literal(true)
|
|
5130
5377
|
});
|
|
5131
|
-
var trafficBackfillResponseSchema =
|
|
5132
|
-
sourceId:
|
|
5133
|
-
runId:
|
|
5378
|
+
var trafficBackfillResponseSchema = z31.object({
|
|
5379
|
+
sourceId: z31.string(),
|
|
5380
|
+
runId: z31.string(),
|
|
5134
5381
|
status: runStatusSchema,
|
|
5135
|
-
windowStart:
|
|
5136
|
-
windowEnd:
|
|
5382
|
+
windowStart: z31.string(),
|
|
5383
|
+
windowEnd: z31.string(),
|
|
5137
5384
|
/** Days actually used after server-side clamping (≤ requested). */
|
|
5138
|
-
daysRequested:
|
|
5139
|
-
daysApplied:
|
|
5385
|
+
daysRequested: z31.number().int().positive(),
|
|
5386
|
+
daysApplied: z31.number().int().positive()
|
|
5140
5387
|
});
|
|
5141
|
-
var trafficSourceTotalsSchema =
|
|
5388
|
+
var trafficSourceTotalsSchema = z31.object({
|
|
5142
5389
|
/**
|
|
5143
5390
|
* Total classified-crawler hits in the window. UNCHANGED contract — still the
|
|
5144
5391
|
* full count across every path class. Use `crawlerContentHits` for the
|
|
5145
5392
|
* "content was actually crawled" signal.
|
|
5146
5393
|
*/
|
|
5147
|
-
crawlerHits:
|
|
5394
|
+
crawlerHits: z31.number().int().nonnegative(),
|
|
5148
5395
|
/** Crawler hits against content/document paths only (= `crawlerSegments.content`). */
|
|
5149
|
-
crawlerContentHits:
|
|
5396
|
+
crawlerContentHits: z31.number().int().nonnegative(),
|
|
5150
5397
|
/** Infrastructure crawler hits — sitemap + robots + asset fetches (`crawlerSegments.{sitemap,robots,asset}`). */
|
|
5151
|
-
crawlerInfraHits:
|
|
5398
|
+
crawlerInfraHits: z31.number().int().nonnegative(),
|
|
5152
5399
|
/** Full per-class crawler-hit breakdown; the five buckets sum to `crawlerHits`. */
|
|
5153
5400
|
crawlerSegments: trafficCrawlerSegmentsSchema,
|
|
5154
|
-
aiUserFetchHits:
|
|
5155
|
-
aiReferralHits:
|
|
5156
|
-
sampleCount:
|
|
5401
|
+
aiUserFetchHits: z31.number().int().nonnegative(),
|
|
5402
|
+
aiReferralHits: z31.number().int().nonnegative(),
|
|
5403
|
+
sampleCount: z31.number().int().nonnegative()
|
|
5157
5404
|
});
|
|
5158
|
-
var trafficSourceListResponseSchema =
|
|
5159
|
-
sources:
|
|
5405
|
+
var trafficSourceListResponseSchema = z31.object({
|
|
5406
|
+
sources: z31.array(trafficSourceDtoSchema)
|
|
5160
5407
|
});
|
|
5161
5408
|
var trafficSourceDetailDtoSchema = trafficSourceDtoSchema.extend({
|
|
5162
5409
|
totals24h: trafficSourceTotalsSchema,
|
|
5163
|
-
latestRun:
|
|
5164
|
-
runId:
|
|
5410
|
+
latestRun: z31.object({
|
|
5411
|
+
runId: z31.string(),
|
|
5165
5412
|
status: runStatusSchema,
|
|
5166
|
-
startedAt:
|
|
5167
|
-
finishedAt:
|
|
5168
|
-
error:
|
|
5413
|
+
startedAt: z31.string().nullable(),
|
|
5414
|
+
finishedAt: z31.string().nullable(),
|
|
5415
|
+
error: z31.string().nullable()
|
|
5169
5416
|
}).nullable()
|
|
5170
5417
|
});
|
|
5171
|
-
var trafficStatusResponseSchema =
|
|
5172
|
-
sources:
|
|
5418
|
+
var trafficStatusResponseSchema = z31.object({
|
|
5419
|
+
sources: z31.array(trafficSourceDetailDtoSchema)
|
|
5173
5420
|
});
|
|
5174
|
-
var trafficEventKindSchema =
|
|
5421
|
+
var trafficEventKindSchema = z31.enum(["crawler", "ai-user-fetch", "ai-referral"]);
|
|
5175
5422
|
var TrafficEventKinds = trafficEventKindSchema.enum;
|
|
5176
|
-
var trafficCrawlerEventEntrySchema =
|
|
5177
|
-
kind:
|
|
5178
|
-
sourceId:
|
|
5179
|
-
tsHour:
|
|
5180
|
-
botId:
|
|
5181
|
-
operator:
|
|
5182
|
-
verificationStatus:
|
|
5183
|
-
pathNormalized:
|
|
5423
|
+
var trafficCrawlerEventEntrySchema = z31.object({
|
|
5424
|
+
kind: z31.literal(TrafficEventKinds.crawler),
|
|
5425
|
+
sourceId: z31.string(),
|
|
5426
|
+
tsHour: z31.string(),
|
|
5427
|
+
botId: z31.string(),
|
|
5428
|
+
operator: z31.string(),
|
|
5429
|
+
verificationStatus: z31.string(),
|
|
5430
|
+
pathNormalized: z31.string(),
|
|
5184
5431
|
/** Coarse class of the fetched path — lets the UI split content crawls from sitemap/robots/asset polling. */
|
|
5185
5432
|
pathClass: trafficPathClassSchema,
|
|
5186
|
-
status:
|
|
5187
|
-
hits:
|
|
5188
|
-
});
|
|
5189
|
-
var trafficAiUserFetchEventEntrySchema =
|
|
5190
|
-
kind:
|
|
5191
|
-
sourceId:
|
|
5192
|
-
tsHour:
|
|
5193
|
-
botId:
|
|
5194
|
-
operator:
|
|
5195
|
-
verificationStatus:
|
|
5196
|
-
pathNormalized:
|
|
5197
|
-
status:
|
|
5198
|
-
hits:
|
|
5199
|
-
});
|
|
5200
|
-
var trafficAiReferralEventEntrySchema =
|
|
5201
|
-
kind:
|
|
5202
|
-
sourceId:
|
|
5203
|
-
tsHour:
|
|
5204
|
-
product:
|
|
5205
|
-
operator:
|
|
5206
|
-
sourceDomain:
|
|
5207
|
-
evidenceType:
|
|
5208
|
-
landingPathNormalized:
|
|
5209
|
-
status:
|
|
5210
|
-
hits
|
|
5211
|
-
|
|
5212
|
-
|
|
5433
|
+
status: z31.number().int(),
|
|
5434
|
+
hits: z31.number().int().nonnegative()
|
|
5435
|
+
});
|
|
5436
|
+
var trafficAiUserFetchEventEntrySchema = z31.object({
|
|
5437
|
+
kind: z31.literal(TrafficEventKinds["ai-user-fetch"]),
|
|
5438
|
+
sourceId: z31.string(),
|
|
5439
|
+
tsHour: z31.string(),
|
|
5440
|
+
botId: z31.string(),
|
|
5441
|
+
operator: z31.string(),
|
|
5442
|
+
verificationStatus: z31.string(),
|
|
5443
|
+
pathNormalized: z31.string(),
|
|
5444
|
+
status: z31.number().int(),
|
|
5445
|
+
hits: z31.number().int().nonnegative()
|
|
5446
|
+
});
|
|
5447
|
+
var trafficAiReferralEventEntrySchema = z31.object({
|
|
5448
|
+
kind: z31.literal(TrafficEventKinds["ai-referral"]),
|
|
5449
|
+
sourceId: z31.string(),
|
|
5450
|
+
tsHour: z31.string(),
|
|
5451
|
+
product: z31.string(),
|
|
5452
|
+
operator: z31.string(),
|
|
5453
|
+
sourceDomain: z31.string(),
|
|
5454
|
+
evidenceType: z31.string(),
|
|
5455
|
+
landingPathNormalized: z31.string(),
|
|
5456
|
+
status: z31.number().int(),
|
|
5457
|
+
/** Total AI-referral sessions in the bucket. `paidHits + organicHits + unknownHits === hits`. */
|
|
5458
|
+
hits: z31.number().int().nonnegative(),
|
|
5459
|
+
/** Sessions carrying paid-attribution UTM evidence (`utm_medium=cpc`, …). */
|
|
5460
|
+
paidHits: z31.number().int().nonnegative(),
|
|
5461
|
+
/** Sessions with no paid-attribution evidence. Not proof the click was unpaid — an untagged ad click is indistinguishable. */
|
|
5462
|
+
organicHits: z31.number().int().nonnegative(),
|
|
5463
|
+
/**
|
|
5464
|
+
* Sessions ingested before the classifier shipped. Their UTM tags were never
|
|
5465
|
+
* persisted, so they can never be resolved to paid or organic. Never fold
|
|
5466
|
+
* these into `organicHits`.
|
|
5467
|
+
*/
|
|
5468
|
+
unknownHits: z31.number().int().nonnegative()
|
|
5469
|
+
});
|
|
5470
|
+
var trafficEventEntrySchema = z31.discriminatedUnion("kind", [
|
|
5213
5471
|
trafficCrawlerEventEntrySchema,
|
|
5214
5472
|
trafficAiUserFetchEventEntrySchema,
|
|
5215
5473
|
trafficAiReferralEventEntrySchema
|
|
5216
5474
|
]);
|
|
5217
|
-
var trafficEventsResponseSchema =
|
|
5218
|
-
windowStart:
|
|
5219
|
-
windowEnd:
|
|
5220
|
-
totals:
|
|
5475
|
+
var trafficEventsResponseSchema = z31.object({
|
|
5476
|
+
windowStart: z31.string(),
|
|
5477
|
+
windowEnd: z31.string(),
|
|
5478
|
+
totals: z31.object({
|
|
5221
5479
|
/** Total classified-crawler hits across the window. UNCHANGED contract. */
|
|
5222
|
-
crawlerHits:
|
|
5480
|
+
crawlerHits: z31.number().int().nonnegative(),
|
|
5223
5481
|
/** Crawler hits against content/document paths only (= `crawlerSegments.content`). */
|
|
5224
|
-
crawlerContentHits:
|
|
5482
|
+
crawlerContentHits: z31.number().int().nonnegative(),
|
|
5225
5483
|
/** Infrastructure crawler hits — sitemap + robots + asset fetches. */
|
|
5226
|
-
crawlerInfraHits:
|
|
5484
|
+
crawlerInfraHits: z31.number().int().nonnegative(),
|
|
5227
5485
|
/** Full per-class crawler-hit breakdown; the five buckets sum to `crawlerHits`. */
|
|
5228
5486
|
crawlerSegments: trafficCrawlerSegmentsSchema,
|
|
5229
|
-
aiUserFetchHits:
|
|
5230
|
-
|
|
5487
|
+
aiUserFetchHits: z31.number().int().nonnegative(),
|
|
5488
|
+
/** Total AI-referral sessions. The three class buckets below sum to it. */
|
|
5489
|
+
aiReferralHits: z31.number().int().nonnegative(),
|
|
5490
|
+
/** AI-referral sessions carrying paid-attribution UTM evidence. */
|
|
5491
|
+
aiReferralPaidHits: z31.number().int().nonnegative(),
|
|
5492
|
+
/** AI-referral sessions with no paid-attribution evidence. */
|
|
5493
|
+
aiReferralOrganicHits: z31.number().int().nonnegative(),
|
|
5494
|
+
/** AI-referral sessions ingested before the classifier shipped; unresolvable, never organic. */
|
|
5495
|
+
aiReferralUnknownHits: z31.number().int().nonnegative()
|
|
5231
5496
|
}),
|
|
5232
|
-
events:
|
|
5497
|
+
events: z31.array(trafficEventEntrySchema)
|
|
5233
5498
|
});
|
|
5234
5499
|
|
|
5235
5500
|
// ../contracts/src/traffic-path.ts
|
|
@@ -5367,199 +5632,134 @@ function sumInfraHits(segments) {
|
|
|
5367
5632
|
return segments.sitemap + segments.robots + segments.asset;
|
|
5368
5633
|
}
|
|
5369
5634
|
|
|
5370
|
-
// ../contracts/src/
|
|
5371
|
-
function
|
|
5372
|
-
|
|
5373
|
-
return
|
|
5374
|
-
}
|
|
5375
|
-
function
|
|
5376
|
-
if (!Number.isFinite(
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
if (Number.isNaN(d.getTime())) return iso;
|
|
5388
|
-
return d.toLocaleDateString("en-US", dateOnly ? { ...options, timeZone: "UTC" } : options);
|
|
5389
|
-
} catch {
|
|
5390
|
-
return iso;
|
|
5391
|
-
}
|
|
5392
|
-
}
|
|
5393
|
-
function formatIsoDate(iso) {
|
|
5394
|
-
if (!iso) return "\u2014";
|
|
5395
|
-
try {
|
|
5396
|
-
const d = new Date(iso);
|
|
5397
|
-
if (Number.isNaN(d.getTime())) return iso;
|
|
5398
|
-
const yyyy = d.getUTCFullYear();
|
|
5399
|
-
const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
|
|
5400
|
-
const dd = String(d.getUTCDate()).padStart(2, "0");
|
|
5401
|
-
return `${yyyy}-${mm}-${dd}`;
|
|
5402
|
-
} catch {
|
|
5403
|
-
return iso;
|
|
5404
|
-
}
|
|
5405
|
-
}
|
|
5406
|
-
function formatDateRange(start, end) {
|
|
5407
|
-
if (!start && !end) return "";
|
|
5408
|
-
if (start && end) return `${formatDate(start)} \u2192 ${formatDate(end)}`;
|
|
5409
|
-
return formatDate(start || end);
|
|
5410
|
-
}
|
|
5411
|
-
var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
5412
|
-
function parseInclusiveEndMs(iso) {
|
|
5413
|
-
const ms = Date.parse(iso);
|
|
5414
|
-
if (Number.isNaN(ms)) return null;
|
|
5415
|
-
return DATE_ONLY_PATTERN.test(iso) ? ms + 864e5 - 1 : ms;
|
|
5416
|
-
}
|
|
5417
|
-
function deltaPercent(current, prior) {
|
|
5418
|
-
if (prior <= 0) return null;
|
|
5419
|
-
return Math.round((current - prior) / prior * 100);
|
|
5420
|
-
}
|
|
5421
|
-
function deltaTone(deltaPct) {
|
|
5422
|
-
if (deltaPct === null || deltaPct === 0) return "neutral";
|
|
5423
|
-
return deltaPct > 0 ? "positive" : "negative";
|
|
5424
|
-
}
|
|
5425
|
-
function formatDeltaCopy(d, suffix, windowLabel = "vs prior 7 days") {
|
|
5426
|
-
if (d.deltaPct === null) {
|
|
5427
|
-
return d.prior === 0 ? "First baseline week" : "";
|
|
5428
|
-
}
|
|
5429
|
-
if (d.deltaPct > 0) return `Up ${d.deltaPct}% ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
|
|
5430
|
-
if (d.deltaPct < 0) return `Down ${Math.abs(d.deltaPct)}% ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
|
|
5431
|
-
return `Flat ${windowLabel} (${formatNumber(d.prior)} ${suffix})`;
|
|
5432
|
-
}
|
|
5433
|
-
var MIN_PCT_BASE = 30;
|
|
5434
|
-
function round1(value) {
|
|
5435
|
-
return Math.round(value * 10) / 10;
|
|
5436
|
-
}
|
|
5437
|
-
function formatAverageDelta(d) {
|
|
5438
|
-
if (d.prior >= MIN_PCT_BASE && d.deltaPct !== null) {
|
|
5439
|
-
const sign2 = d.deltaPct > 0 ? "+" : "";
|
|
5440
|
-
return `${sign2}${d.deltaPct}% vs prior`;
|
|
5441
|
-
}
|
|
5442
|
-
const sign = d.deltaAbs > 0 ? "+" : "";
|
|
5443
|
-
return `${sign}${round1(d.deltaAbs)} vs ${round1(d.prior)}`;
|
|
5444
|
-
}
|
|
5445
|
-
function formatWindowCountDelta(d, countLabel, windowLabel) {
|
|
5446
|
-
if (d.prior >= MIN_PCT_BASE && d.deltaPct !== null) {
|
|
5447
|
-
const sign2 = d.deltaPct > 0 ? "+" : "";
|
|
5448
|
-
return `${sign2}${d.deltaPct}% ${windowLabel}`;
|
|
5449
|
-
}
|
|
5450
|
-
const sign = d.deltaAbs > 0 ? "+" : "";
|
|
5451
|
-
return `${sign}${formatNumber(Math.round(d.deltaAbs))} ${countLabel} ${windowLabel}`;
|
|
5635
|
+
// ../contracts/src/statistics.ts
|
|
5636
|
+
function round(value, dp = 4) {
|
|
5637
|
+
const f = 10 ** dp;
|
|
5638
|
+
return (Math.round(value * f) + 0) / f;
|
|
5639
|
+
}
|
|
5640
|
+
function wilsonInterval(successes, n, z33 = 1.96) {
|
|
5641
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
5642
|
+
const s = Math.max(0, Math.min(successes, n));
|
|
5643
|
+
const p = s / n;
|
|
5644
|
+
const z210 = z33 * z33;
|
|
5645
|
+
const denom = 1 + z210 / n;
|
|
5646
|
+
const center = (p + z210 / (2 * n)) / denom;
|
|
5647
|
+
const margin = z33 / denom * Math.sqrt(p * (1 - p) / n + z210 / (4 * n * n));
|
|
5648
|
+
return {
|
|
5649
|
+
low: round(Math.max(0, center - margin)),
|
|
5650
|
+
high: round(Math.min(1, center + margin))
|
|
5651
|
+
};
|
|
5452
5652
|
}
|
|
5453
5653
|
|
|
5454
5654
|
// ../contracts/src/ads.ts
|
|
5455
|
-
import { z as
|
|
5456
|
-
var adsConnectRequestSchema =
|
|
5655
|
+
import { z as z32 } from "zod";
|
|
5656
|
+
var adsConnectRequestSchema = z32.object({
|
|
5457
5657
|
/** Ads Manager "SDK key" scoped to one ad account. Stored in config.yaml, never the DB. */
|
|
5458
|
-
apiKey:
|
|
5459
|
-
});
|
|
5460
|
-
var adsConnectionStatusDtoSchema =
|
|
5461
|
-
connected:
|
|
5462
|
-
adAccountId:
|
|
5463
|
-
displayName:
|
|
5464
|
-
currencyCode:
|
|
5465
|
-
timezone:
|
|
5466
|
-
status:
|
|
5467
|
-
lastSyncedAt:
|
|
5658
|
+
apiKey: z32.string().min(1)
|
|
5659
|
+
});
|
|
5660
|
+
var adsConnectionStatusDtoSchema = z32.object({
|
|
5661
|
+
connected: z32.boolean(),
|
|
5662
|
+
adAccountId: z32.string().nullable().optional(),
|
|
5663
|
+
displayName: z32.string().nullable().optional(),
|
|
5664
|
+
currencyCode: z32.string().nullable().optional(),
|
|
5665
|
+
timezone: z32.string().nullable().optional(),
|
|
5666
|
+
status: z32.string().nullable().optional(),
|
|
5667
|
+
lastSyncedAt: z32.string().nullable().optional(),
|
|
5468
5668
|
/** Whether the ad account has OpenAI conversion tracking (pixel or CAPI) configured,
|
|
5469
5669
|
* detected from synced campaigns carrying conversion_event_setting_ids. Optional:
|
|
5470
5670
|
* only present when connected. */
|
|
5471
|
-
conversionTrackingConfigured:
|
|
5472
|
-
});
|
|
5473
|
-
var adsDisconnectResponseSchema =
|
|
5474
|
-
disconnected:
|
|
5475
|
-
});
|
|
5476
|
-
var adsSyncResponseSchema =
|
|
5477
|
-
runId:
|
|
5478
|
-
status:
|
|
5479
|
-
});
|
|
5480
|
-
var adsCreativeDtoSchema =
|
|
5481
|
-
type:
|
|
5482
|
-
title:
|
|
5483
|
-
body:
|
|
5484
|
-
targetUrl:
|
|
5485
|
-
});
|
|
5486
|
-
var adsAdDtoSchema =
|
|
5487
|
-
id:
|
|
5488
|
-
adGroupId:
|
|
5489
|
-
name:
|
|
5490
|
-
status:
|
|
5491
|
-
reviewStatus:
|
|
5671
|
+
conversionTrackingConfigured: z32.boolean().optional()
|
|
5672
|
+
});
|
|
5673
|
+
var adsDisconnectResponseSchema = z32.object({
|
|
5674
|
+
disconnected: z32.boolean()
|
|
5675
|
+
});
|
|
5676
|
+
var adsSyncResponseSchema = z32.object({
|
|
5677
|
+
runId: z32.string(),
|
|
5678
|
+
status: z32.string()
|
|
5679
|
+
});
|
|
5680
|
+
var adsCreativeDtoSchema = z32.object({
|
|
5681
|
+
type: z32.string().nullable().optional(),
|
|
5682
|
+
title: z32.string().nullable().optional(),
|
|
5683
|
+
body: z32.string().nullable().optional(),
|
|
5684
|
+
targetUrl: z32.string().nullable().optional()
|
|
5685
|
+
});
|
|
5686
|
+
var adsAdDtoSchema = z32.object({
|
|
5687
|
+
id: z32.string(),
|
|
5688
|
+
adGroupId: z32.string(),
|
|
5689
|
+
name: z32.string(),
|
|
5690
|
+
status: z32.string(),
|
|
5691
|
+
reviewStatus: z32.string().nullable().optional(),
|
|
5492
5692
|
creative: adsCreativeDtoSchema.nullable().optional()
|
|
5493
5693
|
});
|
|
5494
|
-
var adsAdGroupDtoSchema =
|
|
5495
|
-
id:
|
|
5496
|
-
campaignId:
|
|
5497
|
-
name:
|
|
5498
|
-
status:
|
|
5499
|
-
billingEventType:
|
|
5500
|
-
maxBidMicros:
|
|
5694
|
+
var adsAdGroupDtoSchema = z32.object({
|
|
5695
|
+
id: z32.string(),
|
|
5696
|
+
campaignId: z32.string(),
|
|
5697
|
+
name: z32.string(),
|
|
5698
|
+
status: z32.string(),
|
|
5699
|
+
billingEventType: z32.string().nullable().optional(),
|
|
5700
|
+
maxBidMicros: z32.number().int().nullable().optional(),
|
|
5501
5701
|
/**
|
|
5502
5702
|
* The targeting primitive: entries are multi-line strings of
|
|
5503
5703
|
* newline-separated example queries (the live Ads Manager format).
|
|
5504
5704
|
*/
|
|
5505
|
-
contextHints:
|
|
5506
|
-
ads:
|
|
5507
|
-
});
|
|
5508
|
-
var adsCampaignDtoSchema =
|
|
5509
|
-
id:
|
|
5510
|
-
name:
|
|
5511
|
-
status:
|
|
5512
|
-
biddingType:
|
|
5513
|
-
dailySpendLimitMicros:
|
|
5514
|
-
lifetimeSpendLimitMicros:
|
|
5515
|
-
adGroups:
|
|
5516
|
-
});
|
|
5517
|
-
var adsCampaignListResponseSchema =
|
|
5518
|
-
campaigns:
|
|
5519
|
-
});
|
|
5520
|
-
var adsInsightLevelSchema =
|
|
5705
|
+
contextHints: z32.array(z32.string()).default([]),
|
|
5706
|
+
ads: z32.array(adsAdDtoSchema).default([])
|
|
5707
|
+
});
|
|
5708
|
+
var adsCampaignDtoSchema = z32.object({
|
|
5709
|
+
id: z32.string(),
|
|
5710
|
+
name: z32.string(),
|
|
5711
|
+
status: z32.string(),
|
|
5712
|
+
biddingType: z32.string().nullable().optional(),
|
|
5713
|
+
dailySpendLimitMicros: z32.number().int().nullable().optional(),
|
|
5714
|
+
lifetimeSpendLimitMicros: z32.number().int().nullable().optional(),
|
|
5715
|
+
adGroups: z32.array(adsAdGroupDtoSchema).default([])
|
|
5716
|
+
});
|
|
5717
|
+
var adsCampaignListResponseSchema = z32.object({
|
|
5718
|
+
campaigns: z32.array(adsCampaignDtoSchema)
|
|
5719
|
+
});
|
|
5720
|
+
var adsInsightLevelSchema = z32.enum(["campaign", "ad_group"]);
|
|
5521
5721
|
var AdsInsightLevels = adsInsightLevelSchema.enum;
|
|
5522
|
-
var adsInsightRowDtoSchema =
|
|
5722
|
+
var adsInsightRowDtoSchema = z32.object({
|
|
5523
5723
|
level: adsInsightLevelSchema,
|
|
5524
|
-
entityId:
|
|
5525
|
-
date:
|
|
5526
|
-
impressions:
|
|
5527
|
-
clicks:
|
|
5528
|
-
spendMicros:
|
|
5724
|
+
entityId: z32.string(),
|
|
5725
|
+
date: z32.string(),
|
|
5726
|
+
impressions: z32.number().int(),
|
|
5727
|
+
clicks: z32.number().int(),
|
|
5728
|
+
spendMicros: z32.number().int(),
|
|
5529
5729
|
/** Conversion count for the row. 0 when conversion tracking is not configured.
|
|
5530
5730
|
* Conversion VALUE (for ROAS) is a deliberate follow-up: the upstream value
|
|
5531
5731
|
* field is not yet captured against a live conversion-tracking account. */
|
|
5532
|
-
conversions:
|
|
5732
|
+
conversions: z32.number().int(),
|
|
5533
5733
|
/** clicks / impressions; null when impressions is 0. */
|
|
5534
|
-
ctr:
|
|
5734
|
+
ctr: z32.number().nullable(),
|
|
5535
5735
|
/** spendMicros / clicks, rounded to integer micros; null when clicks is 0. */
|
|
5536
|
-
cpcMicros:
|
|
5736
|
+
cpcMicros: z32.number().int().nullable()
|
|
5537
5737
|
});
|
|
5538
|
-
var adsInsightsResponseSchema =
|
|
5539
|
-
rows:
|
|
5738
|
+
var adsInsightsResponseSchema = z32.object({
|
|
5739
|
+
rows: z32.array(adsInsightRowDtoSchema),
|
|
5540
5740
|
/** Account currency for rendering spend/cpc; null before the first sync. */
|
|
5541
|
-
currencyCode:
|
|
5542
|
-
});
|
|
5543
|
-
var adsTotalsDtoSchema =
|
|
5544
|
-
impressions:
|
|
5545
|
-
clicks:
|
|
5546
|
-
spendMicros:
|
|
5547
|
-
conversions:
|
|
5548
|
-
ctr:
|
|
5549
|
-
cpcMicros:
|
|
5550
|
-
});
|
|
5551
|
-
var adsSummaryDtoSchema =
|
|
5552
|
-
connected:
|
|
5553
|
-
displayName:
|
|
5554
|
-
currencyCode:
|
|
5555
|
-
lastSyncedAt:
|
|
5556
|
-
campaignCount:
|
|
5557
|
-
adGroupCount:
|
|
5558
|
-
adCount:
|
|
5741
|
+
currencyCode: z32.string().nullable().optional()
|
|
5742
|
+
});
|
|
5743
|
+
var adsTotalsDtoSchema = z32.object({
|
|
5744
|
+
impressions: z32.number().int(),
|
|
5745
|
+
clicks: z32.number().int(),
|
|
5746
|
+
spendMicros: z32.number().int(),
|
|
5747
|
+
conversions: z32.number().int(),
|
|
5748
|
+
ctr: z32.number().nullable(),
|
|
5749
|
+
cpcMicros: z32.number().int().nullable()
|
|
5750
|
+
});
|
|
5751
|
+
var adsSummaryDtoSchema = z32.object({
|
|
5752
|
+
connected: z32.boolean(),
|
|
5753
|
+
displayName: z32.string().nullable().optional(),
|
|
5754
|
+
currencyCode: z32.string().nullable().optional(),
|
|
5755
|
+
lastSyncedAt: z32.string().nullable().optional(),
|
|
5756
|
+
campaignCount: z32.number().int(),
|
|
5757
|
+
adGroupCount: z32.number().int(),
|
|
5758
|
+
adCount: z32.number().int(),
|
|
5559
5759
|
/** Date range the totals cover (oldest/newest rollup date), null when empty. */
|
|
5560
|
-
window:
|
|
5561
|
-
from:
|
|
5562
|
-
to:
|
|
5760
|
+
window: z32.object({
|
|
5761
|
+
from: z32.string().nullable(),
|
|
5762
|
+
to: z32.string().nullable()
|
|
5563
5763
|
}),
|
|
5564
5764
|
/** Campaign-level rollup totals over the window (levels are not summed across). */
|
|
5565
5765
|
totals: adsTotalsDtoSchema
|
|
@@ -5934,9 +6134,23 @@ export {
|
|
|
5934
6134
|
parseWindow,
|
|
5935
6135
|
windowCutoff,
|
|
5936
6136
|
visibilityStatsDtoSchema,
|
|
6137
|
+
visibilityCompareDtoSchema,
|
|
5937
6138
|
calendarMonthBounds,
|
|
5938
|
-
|
|
5939
|
-
|
|
6139
|
+
formatRatio,
|
|
6140
|
+
formatNumber,
|
|
6141
|
+
formatDate,
|
|
6142
|
+
formatIsoDate,
|
|
6143
|
+
formatDateRange,
|
|
6144
|
+
parseInclusiveEndMs,
|
|
6145
|
+
deltaPercent,
|
|
6146
|
+
deltaTone,
|
|
6147
|
+
formatDeltaCopy,
|
|
6148
|
+
formatAverageDelta,
|
|
6149
|
+
formatWindowCountDelta,
|
|
6150
|
+
AiReferralTrafficClasses,
|
|
6151
|
+
classifyAiReferralTrafficClass,
|
|
6152
|
+
aiReferralClassCounts,
|
|
6153
|
+
formatAiReferralClassSummary,
|
|
5940
6154
|
ga4StatusDtoSchema,
|
|
5941
6155
|
ga4SyncResponseDtoSchema,
|
|
5942
6156
|
ga4AiReferralHistoryEntrySchema,
|
|
@@ -6034,17 +6248,7 @@ export {
|
|
|
6034
6248
|
classifyTrafficPath,
|
|
6035
6249
|
segmentCrawlerHits,
|
|
6036
6250
|
sumInfraHits,
|
|
6037
|
-
|
|
6038
|
-
formatNumber,
|
|
6039
|
-
formatDate,
|
|
6040
|
-
formatIsoDate,
|
|
6041
|
-
formatDateRange,
|
|
6042
|
-
parseInclusiveEndMs,
|
|
6043
|
-
deltaPercent,
|
|
6044
|
-
deltaTone,
|
|
6045
|
-
formatDeltaCopy,
|
|
6046
|
-
formatAverageDelta,
|
|
6047
|
-
formatWindowCountDelta,
|
|
6251
|
+
wilsonInterval,
|
|
6048
6252
|
adsConnectRequestSchema,
|
|
6049
6253
|
adsConnectionStatusDtoSchema,
|
|
6050
6254
|
adsDisconnectResponseSchema,
|