@ainyc/canonry 3.3.9 → 3.4.5
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/dist/{chunk-24C7RMIS.js → chunk-5OYYYY4I.js} +76 -1967
- package/dist/chunk-D4YFX3X4.js +1918 -0
- package/dist/{chunk-P6D3O5JB.js → chunk-P3PS3ZSN.js} +571 -492
- package/dist/{chunk-ZCPZOVVE.js → chunk-ZOJLW6WR.js} +167 -6
- package/dist/cli.js +93 -33
- package/dist/index.js +4 -4
- package/dist/{intelligence-service-FNJTFSI3.js → intelligence-service-GV6CAJ3Q.js} +2 -2
- package/dist/mcp.js +2 -2
- package/package.json +7 -7
- package/dist/chunk-MLKGABMK.js +0 -9
|
@@ -0,0 +1,1918 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// ../contracts/src/run.ts
|
|
8
|
+
import { z as z2 } from "zod";
|
|
9
|
+
|
|
10
|
+
// ../contracts/src/provider.ts
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
var providerQuotaPolicySchema = z.object({
|
|
13
|
+
maxConcurrency: z.number().int().positive(),
|
|
14
|
+
maxRequestsPerMinute: z.number().int().positive(),
|
|
15
|
+
maxRequestsPerDay: z.number().int().positive()
|
|
16
|
+
});
|
|
17
|
+
var ProviderNames = {
|
|
18
|
+
gemini: "gemini",
|
|
19
|
+
openai: "openai",
|
|
20
|
+
claude: "claude",
|
|
21
|
+
perplexity: "perplexity",
|
|
22
|
+
local: "local",
|
|
23
|
+
cdpChatgpt: "cdp:chatgpt"
|
|
24
|
+
};
|
|
25
|
+
var providerNameSchema = z.string().min(1);
|
|
26
|
+
var apiProviderNameSchema = z.string().min(1);
|
|
27
|
+
function isBrowserProvider(name) {
|
|
28
|
+
return name.startsWith("cdp:");
|
|
29
|
+
}
|
|
30
|
+
var CDP_TARGETS = ["cdp:chatgpt"];
|
|
31
|
+
function resolveProviderInput(input) {
|
|
32
|
+
const lower = input.trim().toLowerCase();
|
|
33
|
+
if (lower === "cdp") {
|
|
34
|
+
return [...CDP_TARGETS];
|
|
35
|
+
}
|
|
36
|
+
return lower ? [lower] : [];
|
|
37
|
+
}
|
|
38
|
+
var locationContextSchema = z.object({
|
|
39
|
+
label: z.string().min(1),
|
|
40
|
+
city: z.string().min(1),
|
|
41
|
+
region: z.string().min(1),
|
|
42
|
+
country: z.string().length(2),
|
|
43
|
+
timezone: z.string().optional()
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ../contracts/src/run.ts
|
|
47
|
+
var runStatusSchema = z2.enum(["queued", "running", "completed", "partial", "failed", "cancelled"]);
|
|
48
|
+
var RunStatuses = runStatusSchema.enum;
|
|
49
|
+
var runKindSchema = z2.enum([
|
|
50
|
+
"answer-visibility",
|
|
51
|
+
"site-audit",
|
|
52
|
+
"gsc-sync",
|
|
53
|
+
"inspect-sitemap",
|
|
54
|
+
"ga-sync",
|
|
55
|
+
"bing-inspect",
|
|
56
|
+
"bing-inspect-sitemap",
|
|
57
|
+
"backlink-extract"
|
|
58
|
+
]);
|
|
59
|
+
var RunKinds = runKindSchema.enum;
|
|
60
|
+
var runTriggerSchema = z2.enum(["manual", "scheduled", "config-apply"]);
|
|
61
|
+
var RunTriggers = runTriggerSchema.enum;
|
|
62
|
+
var citationStateSchema = z2.enum(["cited", "not-cited"]);
|
|
63
|
+
var CitationStates = citationStateSchema.enum;
|
|
64
|
+
var visibilityStateSchema = z2.enum(["visible", "not-visible"]);
|
|
65
|
+
var VisibilityStates = visibilityStateSchema.enum;
|
|
66
|
+
var computedTransitionSchema = z2.enum(["new", "cited", "lost", "emerging", "not-cited"]);
|
|
67
|
+
var ComputedTransitions = computedTransitionSchema.enum;
|
|
68
|
+
var runTriggerRequestSchema = z2.object({
|
|
69
|
+
kind: z2.literal(RunKinds["answer-visibility"]).optional(),
|
|
70
|
+
trigger: z2.literal(RunTriggers.manual).optional(),
|
|
71
|
+
providers: z2.array(providerNameSchema).optional(),
|
|
72
|
+
location: z2.string().min(1).optional(),
|
|
73
|
+
allLocations: z2.boolean().optional(),
|
|
74
|
+
noLocation: z2.boolean().optional()
|
|
75
|
+
}).refine(
|
|
76
|
+
(data) => Number(Boolean(data.location)) + Number(Boolean(data.allLocations)) + Number(Boolean(data.noLocation)) <= 1,
|
|
77
|
+
{ message: 'Only one of "location", "allLocations", or "noLocation" may be provided' }
|
|
78
|
+
);
|
|
79
|
+
var runProviderErrorSchema = z2.object({
|
|
80
|
+
/** Human-readable error message (best-effort extracted from `raw.error.message` / `raw.message`, otherwise the raw text with any `[provider-X]` prefix stripped). */
|
|
81
|
+
message: z2.string(),
|
|
82
|
+
/** Original provider response payload, if the underlying error body parsed as JSON. Use this for structured fields like HTTP status, error code, etc. */
|
|
83
|
+
raw: z2.unknown().optional()
|
|
84
|
+
});
|
|
85
|
+
var runErrorSchema = z2.object({
|
|
86
|
+
/** Top-level message for runs that failed without a per-provider error (e.g. user cancellation, internal scheduling failures). */
|
|
87
|
+
message: z2.string().optional(),
|
|
88
|
+
/** Per-provider errors for visibility-sweep runs that had at least one provider fail. */
|
|
89
|
+
providers: z2.record(z2.string(), runProviderErrorSchema).optional()
|
|
90
|
+
});
|
|
91
|
+
var runDtoSchema = z2.object({
|
|
92
|
+
id: z2.string(),
|
|
93
|
+
projectId: z2.string(),
|
|
94
|
+
kind: runKindSchema,
|
|
95
|
+
status: runStatusSchema,
|
|
96
|
+
trigger: runTriggerSchema.default("manual"),
|
|
97
|
+
location: z2.string().nullable().optional(),
|
|
98
|
+
startedAt: z2.string().nullable().optional(),
|
|
99
|
+
finishedAt: z2.string().nullable().optional(),
|
|
100
|
+
error: runErrorSchema.nullable().optional(),
|
|
101
|
+
createdAt: z2.string()
|
|
102
|
+
});
|
|
103
|
+
var PROVIDER_PREFIX = /^\[provider-[a-zA-Z0-9_-]+\]\s+/;
|
|
104
|
+
function parseProviderErrorMessage(msg) {
|
|
105
|
+
const stripped = msg.replace(PROVIDER_PREFIX, "");
|
|
106
|
+
try {
|
|
107
|
+
const raw = JSON.parse(stripped);
|
|
108
|
+
if (raw && typeof raw === "object") {
|
|
109
|
+
const inner = raw;
|
|
110
|
+
const fromErrorMessage = typeof inner.error?.message === "string" ? inner.error.message : void 0;
|
|
111
|
+
const fromMessage = typeof inner.message === "string" ? inner.message : void 0;
|
|
112
|
+
return { message: fromErrorMessage ?? fromMessage ?? stripped, raw };
|
|
113
|
+
}
|
|
114
|
+
} catch {
|
|
115
|
+
}
|
|
116
|
+
return { message: stripped };
|
|
117
|
+
}
|
|
118
|
+
function parseRunError(raw) {
|
|
119
|
+
if (!raw) return null;
|
|
120
|
+
let parsed;
|
|
121
|
+
try {
|
|
122
|
+
parsed = JSON.parse(raw);
|
|
123
|
+
} catch {
|
|
124
|
+
return { message: raw };
|
|
125
|
+
}
|
|
126
|
+
if (!parsed || typeof parsed !== "object") {
|
|
127
|
+
return { message: raw };
|
|
128
|
+
}
|
|
129
|
+
const obj = parsed;
|
|
130
|
+
const hasProviders = obj.providers && typeof obj.providers === "object";
|
|
131
|
+
const hasMessage = typeof obj.message === "string";
|
|
132
|
+
if (hasProviders || hasMessage) {
|
|
133
|
+
return parsed;
|
|
134
|
+
}
|
|
135
|
+
const providers = {};
|
|
136
|
+
for (const [name, val] of Object.entries(obj)) {
|
|
137
|
+
providers[name] = parseProviderErrorMessage(typeof val === "string" ? val : JSON.stringify(val));
|
|
138
|
+
}
|
|
139
|
+
return { providers };
|
|
140
|
+
}
|
|
141
|
+
function buildRunErrorFromMessages(messages) {
|
|
142
|
+
const providers = {};
|
|
143
|
+
for (const [name, msg] of messages) {
|
|
144
|
+
providers[name] = parseProviderErrorMessage(msg);
|
|
145
|
+
}
|
|
146
|
+
return { providers };
|
|
147
|
+
}
|
|
148
|
+
function serializeRunError(err) {
|
|
149
|
+
return JSON.stringify(err);
|
|
150
|
+
}
|
|
151
|
+
function formatRunErrorOneLine(err) {
|
|
152
|
+
if (err.providers) {
|
|
153
|
+
const entries = Object.entries(err.providers);
|
|
154
|
+
if (entries.length === 1) {
|
|
155
|
+
const [provider, detail] = entries[0];
|
|
156
|
+
return `${provider}: ${detail.message}`;
|
|
157
|
+
}
|
|
158
|
+
if (entries.length > 1) {
|
|
159
|
+
return entries.map(([p, d]) => `${p}: ${d.message}`).join(" \u2022 ");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return err.message ?? "Run failed.";
|
|
163
|
+
}
|
|
164
|
+
var groundingSourceSchema = z2.object({
|
|
165
|
+
uri: z2.string(),
|
|
166
|
+
title: z2.string()
|
|
167
|
+
});
|
|
168
|
+
var querySnapshotDtoSchema = z2.object({
|
|
169
|
+
id: z2.string(),
|
|
170
|
+
runId: z2.string(),
|
|
171
|
+
keywordId: z2.string(),
|
|
172
|
+
keyword: z2.string().optional(),
|
|
173
|
+
provider: providerNameSchema,
|
|
174
|
+
citationState: citationStateSchema,
|
|
175
|
+
answerMentioned: z2.boolean().optional(),
|
|
176
|
+
visibilityState: visibilityStateSchema.optional(),
|
|
177
|
+
transition: computedTransitionSchema.optional(),
|
|
178
|
+
answerText: z2.string().nullable().optional(),
|
|
179
|
+
citedDomains: z2.array(z2.string()).default([]),
|
|
180
|
+
competitorOverlap: z2.array(z2.string()).default([]),
|
|
181
|
+
recommendedCompetitors: z2.array(z2.string()).default([]),
|
|
182
|
+
matchedTerms: z2.array(z2.string()).default([]),
|
|
183
|
+
groundingSources: z2.array(groundingSourceSchema).default([]),
|
|
184
|
+
searchQueries: z2.array(z2.string()).default([]),
|
|
185
|
+
model: z2.string().nullable().optional(),
|
|
186
|
+
location: z2.string().nullable().optional(),
|
|
187
|
+
createdAt: z2.string()
|
|
188
|
+
});
|
|
189
|
+
var snapshotListResponseSchema = z2.object({
|
|
190
|
+
snapshots: z2.array(querySnapshotDtoSchema),
|
|
191
|
+
total: z2.number().int().nonnegative()
|
|
192
|
+
});
|
|
193
|
+
var snapshotDiffRowSchema = z2.object({
|
|
194
|
+
keywordId: z2.string().nullable(),
|
|
195
|
+
keyword: z2.string().nullable(),
|
|
196
|
+
run1State: citationStateSchema.nullable(),
|
|
197
|
+
run2State: citationStateSchema.nullable(),
|
|
198
|
+
run1AnswerMentioned: z2.boolean().nullable(),
|
|
199
|
+
run2AnswerMentioned: z2.boolean().nullable(),
|
|
200
|
+
run1VisibilityState: visibilityStateSchema.nullable(),
|
|
201
|
+
run2VisibilityState: visibilityStateSchema.nullable(),
|
|
202
|
+
changed: z2.boolean(),
|
|
203
|
+
visibilityChanged: z2.boolean()
|
|
204
|
+
});
|
|
205
|
+
var snapshotDiffResponseSchema = z2.object({
|
|
206
|
+
run1: z2.string(),
|
|
207
|
+
run2: z2.string(),
|
|
208
|
+
diff: z2.array(snapshotDiffRowSchema)
|
|
209
|
+
});
|
|
210
|
+
var runDetailDtoSchema = runDtoSchema.extend({
|
|
211
|
+
snapshots: z2.array(querySnapshotDtoSchema).optional()
|
|
212
|
+
});
|
|
213
|
+
var latestProjectRunDtoSchema = z2.object({
|
|
214
|
+
totalRuns: z2.number().int().nonnegative(),
|
|
215
|
+
run: runDetailDtoSchema.nullable()
|
|
216
|
+
});
|
|
217
|
+
var auditLogEntrySchema = z2.object({
|
|
218
|
+
id: z2.string(),
|
|
219
|
+
projectId: z2.string().nullable().optional(),
|
|
220
|
+
actor: z2.string(),
|
|
221
|
+
action: z2.string(),
|
|
222
|
+
entityType: z2.string(),
|
|
223
|
+
entityId: z2.string().nullable().optional(),
|
|
224
|
+
diff: z2.unknown().optional(),
|
|
225
|
+
createdAt: z2.string()
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// ../contracts/src/config-schema.ts
|
|
229
|
+
import { z as z5 } from "zod";
|
|
230
|
+
|
|
231
|
+
// ../contracts/src/notification.ts
|
|
232
|
+
import { z as z3 } from "zod";
|
|
233
|
+
var notificationEventSchema = z3.enum([
|
|
234
|
+
"citation.lost",
|
|
235
|
+
"citation.gained",
|
|
236
|
+
"run.completed",
|
|
237
|
+
"run.failed",
|
|
238
|
+
"insight.critical",
|
|
239
|
+
"insight.high"
|
|
240
|
+
]);
|
|
241
|
+
var notificationDtoSchema = z3.object({
|
|
242
|
+
id: z3.string(),
|
|
243
|
+
projectId: z3.string(),
|
|
244
|
+
channel: z3.literal("webhook"),
|
|
245
|
+
url: z3.string().url(),
|
|
246
|
+
urlDisplay: z3.string(),
|
|
247
|
+
urlHost: z3.string(),
|
|
248
|
+
events: z3.array(notificationEventSchema),
|
|
249
|
+
enabled: z3.boolean().default(true),
|
|
250
|
+
/** Opaque tag identifying the creator (e.g. `"agent"` for Aero webhooks). */
|
|
251
|
+
source: z3.string().optional(),
|
|
252
|
+
webhookSecret: z3.string().optional(),
|
|
253
|
+
createdAt: z3.string(),
|
|
254
|
+
updatedAt: z3.string()
|
|
255
|
+
});
|
|
256
|
+
var notificationCreateRequestSchema = z3.object({
|
|
257
|
+
channel: z3.literal("webhook"),
|
|
258
|
+
url: z3.string().url(),
|
|
259
|
+
events: z3.array(notificationEventSchema).min(1),
|
|
260
|
+
source: z3.string().optional()
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// ../contracts/src/project.ts
|
|
264
|
+
import { z as z4 } from "zod";
|
|
265
|
+
var configSourceSchema = z4.enum(["cli", "api", "config-file"]);
|
|
266
|
+
function findDuplicateLocationLabels(locations) {
|
|
267
|
+
const seen = /* @__PURE__ */ new Set();
|
|
268
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
269
|
+
for (const location of locations) {
|
|
270
|
+
if (seen.has(location.label)) {
|
|
271
|
+
duplicates.add(location.label);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
seen.add(location.label);
|
|
275
|
+
}
|
|
276
|
+
return [...duplicates];
|
|
277
|
+
}
|
|
278
|
+
function hasLocationLabel(locations, label) {
|
|
279
|
+
if (!label) return true;
|
|
280
|
+
return locations.some((location) => location.label === label);
|
|
281
|
+
}
|
|
282
|
+
var projectUpsertRequestSchema = z4.object({
|
|
283
|
+
displayName: z4.string().min(1),
|
|
284
|
+
canonicalDomain: z4.string().min(1),
|
|
285
|
+
ownedDomains: z4.array(z4.string().min(1)).optional(),
|
|
286
|
+
country: z4.string().length(2),
|
|
287
|
+
language: z4.string().min(2),
|
|
288
|
+
tags: z4.array(z4.string()).optional(),
|
|
289
|
+
labels: z4.record(z4.string(), z4.string()).optional(),
|
|
290
|
+
providers: z4.array(providerNameSchema).optional(),
|
|
291
|
+
locations: z4.array(locationContextSchema).optional(),
|
|
292
|
+
defaultLocation: z4.string().nullable().optional(),
|
|
293
|
+
autoExtractBacklinks: z4.boolean().optional(),
|
|
294
|
+
configSource: configSourceSchema.optional()
|
|
295
|
+
});
|
|
296
|
+
var projectDtoSchema = z4.object({
|
|
297
|
+
id: z4.string(),
|
|
298
|
+
name: z4.string(),
|
|
299
|
+
displayName: z4.string().optional(),
|
|
300
|
+
canonicalDomain: z4.string(),
|
|
301
|
+
ownedDomains: z4.array(z4.string()).default([]),
|
|
302
|
+
country: z4.string().length(2),
|
|
303
|
+
language: z4.string().min(2),
|
|
304
|
+
tags: z4.array(z4.string()).default([]),
|
|
305
|
+
labels: z4.record(z4.string(), z4.string()).default({}),
|
|
306
|
+
locations: z4.array(locationContextSchema).default([]),
|
|
307
|
+
defaultLocation: z4.string().nullable().optional(),
|
|
308
|
+
autoExtractBacklinks: z4.boolean().default(false),
|
|
309
|
+
configSource: configSourceSchema.default("cli"),
|
|
310
|
+
configRevision: z4.number().int().positive().default(1),
|
|
311
|
+
createdAt: z4.string().optional(),
|
|
312
|
+
updatedAt: z4.string().optional()
|
|
313
|
+
});
|
|
314
|
+
var keywordDtoSchema = z4.object({
|
|
315
|
+
id: z4.string(),
|
|
316
|
+
keyword: z4.string(),
|
|
317
|
+
createdAt: z4.string()
|
|
318
|
+
});
|
|
319
|
+
var keywordBatchRequestSchema = z4.object({
|
|
320
|
+
keywords: z4.array(z4.string().trim().min(1)).min(1)
|
|
321
|
+
});
|
|
322
|
+
var keywordGenerateRequestSchema = z4.object({
|
|
323
|
+
provider: providerNameSchema,
|
|
324
|
+
count: z4.number().int().min(1).max(20).optional()
|
|
325
|
+
});
|
|
326
|
+
var competitorDtoSchema = z4.object({
|
|
327
|
+
id: z4.string(),
|
|
328
|
+
domain: z4.string(),
|
|
329
|
+
createdAt: z4.string()
|
|
330
|
+
});
|
|
331
|
+
var competitorBatchRequestSchema = z4.object({
|
|
332
|
+
competitors: z4.array(z4.string().trim().min(1)).min(1)
|
|
333
|
+
});
|
|
334
|
+
function normalizeProjectDomain(input) {
|
|
335
|
+
let domain = input.trim().toLowerCase();
|
|
336
|
+
try {
|
|
337
|
+
if (domain.includes("://")) {
|
|
338
|
+
domain = new URL(domain).hostname.toLowerCase();
|
|
339
|
+
}
|
|
340
|
+
} catch {
|
|
341
|
+
}
|
|
342
|
+
return domain.replace(/^www\./, "");
|
|
343
|
+
}
|
|
344
|
+
var MULTI_LABEL_PUBLIC_SUFFIXES = /* @__PURE__ */ new Set([
|
|
345
|
+
"ac.uk",
|
|
346
|
+
"co.id",
|
|
347
|
+
"co.il",
|
|
348
|
+
"co.in",
|
|
349
|
+
"co.jp",
|
|
350
|
+
"co.kr",
|
|
351
|
+
"co.nz",
|
|
352
|
+
"co.th",
|
|
353
|
+
"co.uk",
|
|
354
|
+
"co.za",
|
|
355
|
+
"com.au",
|
|
356
|
+
"com.br",
|
|
357
|
+
"com.cn",
|
|
358
|
+
"com.mx",
|
|
359
|
+
"com.ph",
|
|
360
|
+
"com.sg",
|
|
361
|
+
"com.tr",
|
|
362
|
+
"edu.au",
|
|
363
|
+
"edu.sg",
|
|
364
|
+
"gov.au",
|
|
365
|
+
"gov.uk",
|
|
366
|
+
"me.uk",
|
|
367
|
+
"ne.jp",
|
|
368
|
+
"net.au",
|
|
369
|
+
"net.br",
|
|
370
|
+
"net.cn",
|
|
371
|
+
"net.in",
|
|
372
|
+
"net.tr",
|
|
373
|
+
"or.jp",
|
|
374
|
+
"or.kr",
|
|
375
|
+
"org.au",
|
|
376
|
+
"org.br",
|
|
377
|
+
"org.in",
|
|
378
|
+
"org.nz",
|
|
379
|
+
"org.tr",
|
|
380
|
+
"org.uk",
|
|
381
|
+
"org.za"
|
|
382
|
+
]);
|
|
383
|
+
function registrableDomain(input) {
|
|
384
|
+
const normalized = normalizeProjectDomain(input);
|
|
385
|
+
if (!normalized) return "";
|
|
386
|
+
const hostname = normalized.split("/")[0]?.split(":")[0] ?? "";
|
|
387
|
+
if (!hostname) return "";
|
|
388
|
+
const labels = hostname.split(".").filter(Boolean);
|
|
389
|
+
if (labels.length < 2) return "";
|
|
390
|
+
if (labels.length === 2) return labels.join(".");
|
|
391
|
+
const lastTwo = labels.slice(-2).join(".");
|
|
392
|
+
if (MULTI_LABEL_PUBLIC_SUFFIXES.has(lastTwo)) {
|
|
393
|
+
return labels.length >= 3 ? labels.slice(-3).join(".") : "";
|
|
394
|
+
}
|
|
395
|
+
return labels.slice(-2).join(".");
|
|
396
|
+
}
|
|
397
|
+
function brandLabelFromDomain(input) {
|
|
398
|
+
const reg = registrableDomain(input);
|
|
399
|
+
if (!reg) return "";
|
|
400
|
+
return reg.split(".")[0] ?? "";
|
|
401
|
+
}
|
|
402
|
+
function effectiveDomains(project) {
|
|
403
|
+
const all = [project.canonicalDomain, ...project.ownedDomains ?? []];
|
|
404
|
+
const seen = /* @__PURE__ */ new Set();
|
|
405
|
+
const result = [];
|
|
406
|
+
for (const d of all) {
|
|
407
|
+
const trimmed = d.trim();
|
|
408
|
+
if (!trimmed) continue;
|
|
409
|
+
const norm = normalizeProjectDomain(trimmed);
|
|
410
|
+
if (seen.has(norm)) continue;
|
|
411
|
+
seen.add(norm);
|
|
412
|
+
result.push(trimmed);
|
|
413
|
+
}
|
|
414
|
+
return result;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ../contracts/src/config-schema.ts
|
|
418
|
+
var configMetadataSchema = z5.object({
|
|
419
|
+
name: z5.string().min(1).max(63).regex(/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/, {
|
|
420
|
+
message: "Name must be a lowercase slug (letters, numbers, hyphens)"
|
|
421
|
+
}),
|
|
422
|
+
labels: z5.record(z5.string(), z5.string()).optional().default({})
|
|
423
|
+
});
|
|
424
|
+
var configScheduleSchema = z5.object({
|
|
425
|
+
preset: z5.string().optional(),
|
|
426
|
+
cron: z5.string().optional(),
|
|
427
|
+
timezone: z5.string().optional().default("UTC"),
|
|
428
|
+
providers: z5.array(providerNameSchema).optional().default([])
|
|
429
|
+
}).refine(
|
|
430
|
+
(data) => data.preset && !data.cron || !data.preset && data.cron,
|
|
431
|
+
{ message: 'Exactly one of "preset" or "cron" must be provided' }
|
|
432
|
+
).optional();
|
|
433
|
+
var configNotificationSchema = z5.object({
|
|
434
|
+
channel: z5.literal("webhook"),
|
|
435
|
+
url: z5.string().url(),
|
|
436
|
+
events: z5.array(notificationEventSchema).min(1)
|
|
437
|
+
});
|
|
438
|
+
var configGoogleSchema = z5.object({
|
|
439
|
+
gsc: z5.object({
|
|
440
|
+
propertyUrl: z5.string()
|
|
441
|
+
}).optional(),
|
|
442
|
+
syncSchedule: z5.object({
|
|
443
|
+
preset: z5.string().optional(),
|
|
444
|
+
cron: z5.string().optional()
|
|
445
|
+
}).optional()
|
|
446
|
+
}).optional();
|
|
447
|
+
var configSpecSchema = z5.object({
|
|
448
|
+
displayName: z5.string().min(1),
|
|
449
|
+
canonicalDomain: z5.string().min(1),
|
|
450
|
+
ownedDomains: z5.array(z5.string().min(1)).optional().default([]),
|
|
451
|
+
country: z5.string().length(2),
|
|
452
|
+
language: z5.string().min(2),
|
|
453
|
+
keywords: z5.array(z5.string().min(1)).optional().default([]),
|
|
454
|
+
competitors: z5.array(z5.string().min(1)).optional().default([]),
|
|
455
|
+
providers: z5.array(providerNameSchema).optional().default([]),
|
|
456
|
+
locations: z5.array(locationContextSchema).optional().default([]),
|
|
457
|
+
defaultLocation: z5.string().optional(),
|
|
458
|
+
schedule: configScheduleSchema,
|
|
459
|
+
notifications: z5.array(configNotificationSchema).optional().default([]),
|
|
460
|
+
google: configGoogleSchema,
|
|
461
|
+
autoExtractBacklinks: z5.boolean().optional().default(false)
|
|
462
|
+
}).superRefine((spec, ctx) => {
|
|
463
|
+
const duplicateLabels = findDuplicateLocationLabels(spec.locations);
|
|
464
|
+
if (duplicateLabels.length > 0) {
|
|
465
|
+
ctx.addIssue({
|
|
466
|
+
code: "custom",
|
|
467
|
+
message: `Duplicate location labels are not allowed: ${duplicateLabels.join(", ")}`,
|
|
468
|
+
path: ["locations"]
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
if (!hasLocationLabel(spec.locations, spec.defaultLocation)) {
|
|
472
|
+
ctx.addIssue({
|
|
473
|
+
code: "custom",
|
|
474
|
+
message: `defaultLocation "${spec.defaultLocation}" must match a configured location label`,
|
|
475
|
+
path: ["defaultLocation"]
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
var projectConfigSchema = z5.object({
|
|
480
|
+
apiVersion: z5.literal("canonry/v1"),
|
|
481
|
+
kind: z5.literal("Project"),
|
|
482
|
+
metadata: configMetadataSchema,
|
|
483
|
+
spec: configSpecSchema
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// ../contracts/src/errors.ts
|
|
487
|
+
var AppError = class extends Error {
|
|
488
|
+
code;
|
|
489
|
+
statusCode;
|
|
490
|
+
details;
|
|
491
|
+
constructor(code, message, statusCode, details) {
|
|
492
|
+
super(message);
|
|
493
|
+
this.name = "AppError";
|
|
494
|
+
this.code = code;
|
|
495
|
+
this.statusCode = statusCode;
|
|
496
|
+
this.details = details;
|
|
497
|
+
}
|
|
498
|
+
toJSON() {
|
|
499
|
+
return {
|
|
500
|
+
error: {
|
|
501
|
+
code: this.code,
|
|
502
|
+
message: this.message,
|
|
503
|
+
...this.details ? { details: this.details } : {}
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
function notFound(entity, id) {
|
|
509
|
+
return new AppError("NOT_FOUND", `${entity} '${id}' not found`, 404);
|
|
510
|
+
}
|
|
511
|
+
function validationError(message, details) {
|
|
512
|
+
return new AppError("VALIDATION_ERROR", message, 400, details);
|
|
513
|
+
}
|
|
514
|
+
function authRequired() {
|
|
515
|
+
return new AppError("AUTH_REQUIRED", "Authentication required", 401);
|
|
516
|
+
}
|
|
517
|
+
function authInvalid() {
|
|
518
|
+
return new AppError("AUTH_INVALID", "Invalid API key", 401);
|
|
519
|
+
}
|
|
520
|
+
function providerError(message, details) {
|
|
521
|
+
return new AppError("PROVIDER_ERROR", message, 502, details);
|
|
522
|
+
}
|
|
523
|
+
function runInProgress(projectName) {
|
|
524
|
+
return new AppError("RUN_IN_PROGRESS", `A run is already in progress for '${projectName}'`, 409);
|
|
525
|
+
}
|
|
526
|
+
function runNotCancellable(runId, status) {
|
|
527
|
+
return new AppError("RUN_NOT_CANCELLABLE", `Run '${runId}' is already in terminal state '${status}' and cannot be cancelled`, 409);
|
|
528
|
+
}
|
|
529
|
+
function unsupportedKind(kind) {
|
|
530
|
+
return new AppError("UNSUPPORTED_KIND", `Kind '${kind}' is not supported in this version`, 400);
|
|
531
|
+
}
|
|
532
|
+
function notImplemented(message) {
|
|
533
|
+
return new AppError("NOT_IMPLEMENTED", message, 501);
|
|
534
|
+
}
|
|
535
|
+
function deliveryFailed(message) {
|
|
536
|
+
return new AppError("DELIVERY_FAILED", message, 502);
|
|
537
|
+
}
|
|
538
|
+
function agentBusy(projectName) {
|
|
539
|
+
return new AppError(
|
|
540
|
+
"AGENT_BUSY",
|
|
541
|
+
`Aero is already running a turn for '${projectName}'. Retry after the current turn settles.`,
|
|
542
|
+
409
|
|
543
|
+
);
|
|
544
|
+
}
|
|
545
|
+
function missingDependency(message, details) {
|
|
546
|
+
return new AppError("MISSING_DEPENDENCY", message, 422, details);
|
|
547
|
+
}
|
|
548
|
+
function internalError(message, details) {
|
|
549
|
+
return new AppError("INTERNAL_ERROR", message, 500, details);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ../contracts/src/google.ts
|
|
553
|
+
import { z as z6 } from "zod";
|
|
554
|
+
var googleConnectionTypeSchema = z6.enum(["gsc", "ga4"]);
|
|
555
|
+
var googleConnectionDtoSchema = z6.object({
|
|
556
|
+
id: z6.string(),
|
|
557
|
+
domain: z6.string(),
|
|
558
|
+
connectionType: googleConnectionTypeSchema,
|
|
559
|
+
propertyId: z6.string().nullable().optional(),
|
|
560
|
+
sitemapUrl: z6.string().nullable().optional(),
|
|
561
|
+
scopes: z6.array(z6.string()).default([]),
|
|
562
|
+
createdAt: z6.string(),
|
|
563
|
+
updatedAt: z6.string()
|
|
564
|
+
});
|
|
565
|
+
var gscSearchDataDtoSchema = z6.object({
|
|
566
|
+
date: z6.string(),
|
|
567
|
+
query: z6.string(),
|
|
568
|
+
page: z6.string(),
|
|
569
|
+
country: z6.string().nullable().optional(),
|
|
570
|
+
device: z6.string().nullable().optional(),
|
|
571
|
+
clicks: z6.number(),
|
|
572
|
+
impressions: z6.number(),
|
|
573
|
+
ctr: z6.number(),
|
|
574
|
+
position: z6.number()
|
|
575
|
+
});
|
|
576
|
+
var gscUrlInspectionDtoSchema = z6.object({
|
|
577
|
+
id: z6.string(),
|
|
578
|
+
url: z6.string(),
|
|
579
|
+
indexingState: z6.string().nullable().optional(),
|
|
580
|
+
verdict: z6.string().nullable().optional(),
|
|
581
|
+
coverageState: z6.string().nullable().optional(),
|
|
582
|
+
pageFetchState: z6.string().nullable().optional(),
|
|
583
|
+
robotsTxtState: z6.string().nullable().optional(),
|
|
584
|
+
crawlTime: z6.string().nullable().optional(),
|
|
585
|
+
lastCrawlResult: z6.string().nullable().optional(),
|
|
586
|
+
isMobileFriendly: z6.boolean().nullable().optional(),
|
|
587
|
+
richResults: z6.array(z6.string()).default([]),
|
|
588
|
+
inspectedAt: z6.string()
|
|
589
|
+
});
|
|
590
|
+
var indexTransitionSchema = z6.enum(["stable", "reindexed", "deindexed", "still-missing", "new"]);
|
|
591
|
+
var gscDeindexedRowSchema = z6.object({
|
|
592
|
+
url: z6.string(),
|
|
593
|
+
previousState: z6.string().nullable(),
|
|
594
|
+
currentState: z6.string().nullable(),
|
|
595
|
+
transitionDate: z6.string()
|
|
596
|
+
});
|
|
597
|
+
var gscReasonGroupSchema = z6.object({
|
|
598
|
+
reason: z6.string(),
|
|
599
|
+
count: z6.number(),
|
|
600
|
+
urls: z6.array(gscUrlInspectionDtoSchema).default([])
|
|
601
|
+
});
|
|
602
|
+
var gscCoverageSummaryDtoSchema = z6.object({
|
|
603
|
+
summary: z6.object({
|
|
604
|
+
total: z6.number(),
|
|
605
|
+
indexed: z6.number(),
|
|
606
|
+
notIndexed: z6.number(),
|
|
607
|
+
deindexed: z6.number(),
|
|
608
|
+
percentage: z6.number()
|
|
609
|
+
}),
|
|
610
|
+
lastInspectedAt: z6.string().nullable(),
|
|
611
|
+
indexed: z6.array(gscUrlInspectionDtoSchema).default([]),
|
|
612
|
+
notIndexed: z6.array(gscUrlInspectionDtoSchema).default([]),
|
|
613
|
+
deindexed: z6.array(gscDeindexedRowSchema).default([]),
|
|
614
|
+
reasonGroups: z6.array(gscReasonGroupSchema).default([])
|
|
615
|
+
});
|
|
616
|
+
var indexingNotificationDtoSchema = z6.object({
|
|
617
|
+
url: z6.string(),
|
|
618
|
+
type: z6.enum(["URL_UPDATED", "URL_DELETED"]),
|
|
619
|
+
notifiedAt: z6.string()
|
|
620
|
+
});
|
|
621
|
+
var indexingRequestResultDtoSchema = z6.object({
|
|
622
|
+
url: z6.string(),
|
|
623
|
+
type: z6.enum(["URL_UPDATED", "URL_DELETED"]),
|
|
624
|
+
notifiedAt: z6.string(),
|
|
625
|
+
status: z6.enum(["success", "error"]),
|
|
626
|
+
error: z6.string().optional()
|
|
627
|
+
});
|
|
628
|
+
var gscCoverageSnapshotDtoSchema = z6.object({
|
|
629
|
+
date: z6.string(),
|
|
630
|
+
indexed: z6.number(),
|
|
631
|
+
notIndexed: z6.number(),
|
|
632
|
+
reasonBreakdown: z6.record(z6.string(), z6.number()).default({})
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
// ../contracts/src/bing.ts
|
|
636
|
+
import { z as z7 } from "zod";
|
|
637
|
+
var bingConnectionDtoSchema = z7.object({
|
|
638
|
+
id: z7.string(),
|
|
639
|
+
domain: z7.string(),
|
|
640
|
+
siteUrl: z7.string().nullable().optional(),
|
|
641
|
+
createdAt: z7.string(),
|
|
642
|
+
updatedAt: z7.string()
|
|
643
|
+
});
|
|
644
|
+
var bingUrlInspectionDtoSchema = z7.object({
|
|
645
|
+
id: z7.string(),
|
|
646
|
+
url: z7.string(),
|
|
647
|
+
httpCode: z7.number().nullable().optional(),
|
|
648
|
+
inIndex: z7.boolean().nullable().optional(),
|
|
649
|
+
lastCrawledDate: z7.string().nullable().optional(),
|
|
650
|
+
inIndexDate: z7.string().nullable().optional(),
|
|
651
|
+
inspectedAt: z7.string(),
|
|
652
|
+
// Fields derived from GetUrlInfo response (more reliable than InIndex)
|
|
653
|
+
documentSize: z7.number().nullable().optional(),
|
|
654
|
+
anchorCount: z7.number().nullable().optional(),
|
|
655
|
+
discoveryDate: z7.string().nullable().optional()
|
|
656
|
+
});
|
|
657
|
+
var bingCoverageSummaryDtoSchema = z7.object({
|
|
658
|
+
summary: z7.object({
|
|
659
|
+
total: z7.number(),
|
|
660
|
+
indexed: z7.number(),
|
|
661
|
+
notIndexed: z7.number(),
|
|
662
|
+
unknown: z7.number().optional(),
|
|
663
|
+
percentage: z7.number()
|
|
664
|
+
}),
|
|
665
|
+
lastInspectedAt: z7.string().nullable(),
|
|
666
|
+
indexed: z7.array(bingUrlInspectionDtoSchema).default([]),
|
|
667
|
+
notIndexed: z7.array(bingUrlInspectionDtoSchema).default([]),
|
|
668
|
+
unknown: z7.array(bingUrlInspectionDtoSchema).default([]).optional()
|
|
669
|
+
});
|
|
670
|
+
var bingKeywordStatsDtoSchema = z7.object({
|
|
671
|
+
query: z7.string(),
|
|
672
|
+
impressions: z7.number(),
|
|
673
|
+
clicks: z7.number(),
|
|
674
|
+
ctr: z7.number(),
|
|
675
|
+
averagePosition: z7.number()
|
|
676
|
+
});
|
|
677
|
+
var bingCoverageSnapshotDtoSchema = z7.object({
|
|
678
|
+
date: z7.string(),
|
|
679
|
+
indexed: z7.number(),
|
|
680
|
+
notIndexed: z7.number(),
|
|
681
|
+
unknown: z7.number()
|
|
682
|
+
});
|
|
683
|
+
var bingSubmitResultDtoSchema = z7.object({
|
|
684
|
+
url: z7.string(),
|
|
685
|
+
status: z7.enum(["success", "error"]),
|
|
686
|
+
submittedAt: z7.string(),
|
|
687
|
+
error: z7.string().optional()
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// ../contracts/src/wordpress.ts
|
|
691
|
+
import { z as z8 } from "zod";
|
|
692
|
+
var wordpressEnvSchema = z8.enum(["live", "staging"]);
|
|
693
|
+
var wordpressConnectionDtoSchema = z8.object({
|
|
694
|
+
projectName: z8.string(),
|
|
695
|
+
url: z8.string(),
|
|
696
|
+
stagingUrl: z8.string().optional(),
|
|
697
|
+
username: z8.string(),
|
|
698
|
+
defaultEnv: wordpressEnvSchema,
|
|
699
|
+
createdAt: z8.string(),
|
|
700
|
+
updatedAt: z8.string()
|
|
701
|
+
});
|
|
702
|
+
var wordpressSiteStatusDtoSchema = z8.object({
|
|
703
|
+
url: z8.string(),
|
|
704
|
+
reachable: z8.boolean(),
|
|
705
|
+
pageCount: z8.number().nullable().optional(),
|
|
706
|
+
version: z8.string().nullable().optional(),
|
|
707
|
+
error: z8.string().nullable().optional(),
|
|
708
|
+
plugins: z8.array(z8.string()).optional(),
|
|
709
|
+
authenticatedUser: z8.object({
|
|
710
|
+
id: z8.number(),
|
|
711
|
+
slug: z8.string()
|
|
712
|
+
}).nullable().optional()
|
|
713
|
+
});
|
|
714
|
+
var wordpressStatusDtoSchema = z8.object({
|
|
715
|
+
connected: z8.boolean(),
|
|
716
|
+
projectName: z8.string(),
|
|
717
|
+
defaultEnv: wordpressEnvSchema,
|
|
718
|
+
live: wordpressSiteStatusDtoSchema.nullable(),
|
|
719
|
+
staging: wordpressSiteStatusDtoSchema.nullable(),
|
|
720
|
+
adminUrl: z8.string().nullable().optional()
|
|
721
|
+
});
|
|
722
|
+
var wordpressPageSummaryDtoSchema = z8.object({
|
|
723
|
+
id: z8.number(),
|
|
724
|
+
slug: z8.string(),
|
|
725
|
+
title: z8.string(),
|
|
726
|
+
status: z8.string(),
|
|
727
|
+
modifiedAt: z8.string().nullable().optional(),
|
|
728
|
+
link: z8.string().nullable().optional()
|
|
729
|
+
});
|
|
730
|
+
var wordpressSeoStateDtoSchema = z8.object({
|
|
731
|
+
title: z8.string().nullable(),
|
|
732
|
+
description: z8.string().nullable(),
|
|
733
|
+
noindex: z8.boolean().nullable(),
|
|
734
|
+
writable: z8.boolean().default(false),
|
|
735
|
+
writeTargets: z8.array(z8.string()).default([])
|
|
736
|
+
});
|
|
737
|
+
var wordpressSchemaBlockDtoSchema = z8.object({
|
|
738
|
+
type: z8.string(),
|
|
739
|
+
json: z8.record(z8.string(), z8.unknown())
|
|
740
|
+
});
|
|
741
|
+
var wordpressPageDetailDtoSchema = wordpressPageSummaryDtoSchema.extend({
|
|
742
|
+
env: wordpressEnvSchema,
|
|
743
|
+
content: z8.string(),
|
|
744
|
+
seo: wordpressSeoStateDtoSchema,
|
|
745
|
+
schemaBlocks: z8.array(wordpressSchemaBlockDtoSchema).default([])
|
|
746
|
+
});
|
|
747
|
+
var wordpressDiffPageDtoSchema = wordpressPageDetailDtoSchema.extend({
|
|
748
|
+
contentHash: z8.string(),
|
|
749
|
+
contentSnippet: z8.string()
|
|
750
|
+
});
|
|
751
|
+
var wordpressManualAssistDtoSchema = z8.object({
|
|
752
|
+
manualRequired: z8.literal(true),
|
|
753
|
+
targetUrl: z8.string(),
|
|
754
|
+
adminUrl: z8.string().nullable().optional(),
|
|
755
|
+
content: z8.string(),
|
|
756
|
+
nextSteps: z8.array(z8.string()).default([])
|
|
757
|
+
});
|
|
758
|
+
var wordpressAuditIssueDtoSchema = z8.object({
|
|
759
|
+
slug: z8.string(),
|
|
760
|
+
severity: z8.enum(["high", "medium", "low"]),
|
|
761
|
+
code: z8.enum([
|
|
762
|
+
"noindex",
|
|
763
|
+
"missing-seo-title",
|
|
764
|
+
"missing-meta-description",
|
|
765
|
+
"missing-schema",
|
|
766
|
+
"thin-content"
|
|
767
|
+
]),
|
|
768
|
+
message: z8.string()
|
|
769
|
+
});
|
|
770
|
+
var wordpressAuditPageDtoSchema = z8.object({
|
|
771
|
+
slug: z8.string(),
|
|
772
|
+
title: z8.string(),
|
|
773
|
+
status: z8.string(),
|
|
774
|
+
wordCount: z8.number(),
|
|
775
|
+
seo: wordpressSeoStateDtoSchema,
|
|
776
|
+
schemaPresent: z8.boolean(),
|
|
777
|
+
issues: z8.array(wordpressAuditIssueDtoSchema).default([])
|
|
778
|
+
});
|
|
779
|
+
var wordpressBulkMetaEntryResultDtoSchema = z8.object({
|
|
780
|
+
slug: z8.string(),
|
|
781
|
+
status: z8.enum(["applied", "skipped", "manual"]),
|
|
782
|
+
error: z8.string().optional(),
|
|
783
|
+
manualAssist: wordpressManualAssistDtoSchema.optional()
|
|
784
|
+
});
|
|
785
|
+
var wordpressBulkMetaResultDtoSchema = z8.object({
|
|
786
|
+
env: wordpressEnvSchema,
|
|
787
|
+
strategy: z8.enum(["plugin", "manual"]),
|
|
788
|
+
results: z8.array(wordpressBulkMetaEntryResultDtoSchema)
|
|
789
|
+
});
|
|
790
|
+
var wordpressSchemaDeployEntryResultDtoSchema = z8.object({
|
|
791
|
+
slug: z8.string(),
|
|
792
|
+
status: z8.enum(["deployed", "stripped", "skipped", "failed"]),
|
|
793
|
+
schemasInjected: z8.array(z8.string()).optional(),
|
|
794
|
+
manualAssist: wordpressManualAssistDtoSchema.optional(),
|
|
795
|
+
error: z8.string().optional()
|
|
796
|
+
});
|
|
797
|
+
var wordpressSchemaDeployResultDtoSchema = z8.object({
|
|
798
|
+
env: wordpressEnvSchema,
|
|
799
|
+
results: z8.array(wordpressSchemaDeployEntryResultDtoSchema)
|
|
800
|
+
});
|
|
801
|
+
var wordpressSchemaStatusPageDtoSchema = z8.object({
|
|
802
|
+
slug: z8.string(),
|
|
803
|
+
title: z8.string(),
|
|
804
|
+
canonrySchemas: z8.array(z8.string()),
|
|
805
|
+
thirdPartySchemas: z8.array(z8.string()),
|
|
806
|
+
hasCanonrySchema: z8.boolean()
|
|
807
|
+
});
|
|
808
|
+
var wordpressSchemaStatusResultDtoSchema = z8.object({
|
|
809
|
+
env: wordpressEnvSchema,
|
|
810
|
+
pages: z8.array(wordpressSchemaStatusPageDtoSchema)
|
|
811
|
+
});
|
|
812
|
+
var wordpressOnboardStepDtoSchema = z8.object({
|
|
813
|
+
name: z8.string(),
|
|
814
|
+
status: z8.enum(["completed", "skipped", "failed"]),
|
|
815
|
+
summary: z8.string().optional(),
|
|
816
|
+
error: z8.string().optional()
|
|
817
|
+
});
|
|
818
|
+
var wordpressOnboardResultDtoSchema = z8.object({
|
|
819
|
+
projectName: z8.string(),
|
|
820
|
+
steps: z8.array(wordpressOnboardStepDtoSchema)
|
|
821
|
+
});
|
|
822
|
+
var wordpressDiffDtoSchema = z8.object({
|
|
823
|
+
slug: z8.string(),
|
|
824
|
+
live: wordpressDiffPageDtoSchema,
|
|
825
|
+
staging: wordpressDiffPageDtoSchema,
|
|
826
|
+
hasDifferences: z8.boolean(),
|
|
827
|
+
differences: z8.object({
|
|
828
|
+
title: z8.boolean(),
|
|
829
|
+
slug: z8.boolean(),
|
|
830
|
+
content: z8.boolean(),
|
|
831
|
+
seoTitle: z8.boolean(),
|
|
832
|
+
seoDescription: z8.boolean(),
|
|
833
|
+
noindex: z8.boolean(),
|
|
834
|
+
schema: z8.boolean()
|
|
835
|
+
})
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
// ../contracts/src/providers.ts
|
|
839
|
+
var ProviderIds = {
|
|
840
|
+
claude: "claude",
|
|
841
|
+
openai: "openai",
|
|
842
|
+
gemini: "gemini",
|
|
843
|
+
perplexity: "perplexity",
|
|
844
|
+
local: "local",
|
|
845
|
+
cdpChatgpt: "cdp:chatgpt",
|
|
846
|
+
zai: "zai"
|
|
847
|
+
};
|
|
848
|
+
var PROVIDER_IDS = Object.values(ProviderIds);
|
|
849
|
+
var SweepProviderIds = {
|
|
850
|
+
claude: ProviderIds.claude,
|
|
851
|
+
openai: ProviderIds.openai,
|
|
852
|
+
gemini: ProviderIds.gemini,
|
|
853
|
+
perplexity: ProviderIds.perplexity,
|
|
854
|
+
local: ProviderIds.local,
|
|
855
|
+
cdpChatgpt: ProviderIds.cdpChatgpt
|
|
856
|
+
};
|
|
857
|
+
var SWEEP_PROVIDER_IDS = Object.values(SweepProviderIds);
|
|
858
|
+
var AgentProviderIds = {
|
|
859
|
+
claude: ProviderIds.claude,
|
|
860
|
+
openai: ProviderIds.openai,
|
|
861
|
+
gemini: ProviderIds.gemini,
|
|
862
|
+
zai: ProviderIds.zai
|
|
863
|
+
};
|
|
864
|
+
var AGENT_PROVIDER_IDS = Object.values(AgentProviderIds);
|
|
865
|
+
function isAgentProviderId(value) {
|
|
866
|
+
return AGENT_PROVIDER_IDS.includes(value);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// ../contracts/src/snapshot.ts
|
|
870
|
+
import { z as z9 } from "zod";
|
|
871
|
+
var snapshotAccuracySchema = z9.enum(["yes", "no", "unknown", "not-mentioned"]);
|
|
872
|
+
var snapshotRequestSchema = z9.object({
|
|
873
|
+
companyName: z9.string().min(1),
|
|
874
|
+
domain: z9.string().min(1),
|
|
875
|
+
phrases: z9.array(z9.string().min(1)).optional().default([]),
|
|
876
|
+
competitors: z9.array(z9.string().min(1)).optional().default([])
|
|
877
|
+
});
|
|
878
|
+
var snapshotCompetitorEntrySchema = z9.object({
|
|
879
|
+
name: z9.string(),
|
|
880
|
+
count: z9.number().int().nonnegative()
|
|
881
|
+
});
|
|
882
|
+
var snapshotAuditFactorSchema = z9.object({
|
|
883
|
+
id: z9.string(),
|
|
884
|
+
name: z9.string(),
|
|
885
|
+
weight: z9.number(),
|
|
886
|
+
score: z9.number(),
|
|
887
|
+
grade: z9.string(),
|
|
888
|
+
status: z9.enum(["pass", "partial", "fail"]),
|
|
889
|
+
findings: z9.array(z9.object({
|
|
890
|
+
type: z9.string(),
|
|
891
|
+
message: z9.string()
|
|
892
|
+
})).default([]),
|
|
893
|
+
recommendations: z9.array(z9.string()).default([])
|
|
894
|
+
});
|
|
895
|
+
var snapshotAuditSchema = z9.object({
|
|
896
|
+
url: z9.string(),
|
|
897
|
+
finalUrl: z9.string(),
|
|
898
|
+
auditedAt: z9.string(),
|
|
899
|
+
overallScore: z9.number(),
|
|
900
|
+
overallGrade: z9.string(),
|
|
901
|
+
summary: z9.string(),
|
|
902
|
+
factors: z9.array(snapshotAuditFactorSchema).default([])
|
|
903
|
+
});
|
|
904
|
+
var snapshotProfileSchema = z9.object({
|
|
905
|
+
industry: z9.string(),
|
|
906
|
+
summary: z9.string(),
|
|
907
|
+
services: z9.array(z9.string()).default([]),
|
|
908
|
+
categoryTerms: z9.array(z9.string()).default([])
|
|
909
|
+
});
|
|
910
|
+
var snapshotProviderResultSchema = z9.object({
|
|
911
|
+
provider: z9.string(),
|
|
912
|
+
displayName: z9.string(),
|
|
913
|
+
model: z9.string().nullable().optional(),
|
|
914
|
+
mentioned: z9.boolean(),
|
|
915
|
+
cited: z9.boolean(),
|
|
916
|
+
describedAccurately: snapshotAccuracySchema,
|
|
917
|
+
accuracyNotes: z9.string().nullable().optional(),
|
|
918
|
+
incorrectClaims: z9.array(z9.string()).default([]),
|
|
919
|
+
recommendedCompetitors: z9.array(z9.string()).default([]),
|
|
920
|
+
citedDomains: z9.array(z9.string()).default([]),
|
|
921
|
+
groundingSources: z9.array(groundingSourceSchema).default([]),
|
|
922
|
+
searchQueries: z9.array(z9.string()).default([]),
|
|
923
|
+
answerText: z9.string(),
|
|
924
|
+
error: z9.string().nullable().optional()
|
|
925
|
+
});
|
|
926
|
+
var snapshotQueryResultSchema = z9.object({
|
|
927
|
+
phrase: z9.string(),
|
|
928
|
+
providerResults: z9.array(snapshotProviderResultSchema).default([])
|
|
929
|
+
});
|
|
930
|
+
var snapshotSummarySchema = z9.object({
|
|
931
|
+
totalQueries: z9.number().int().nonnegative(),
|
|
932
|
+
totalProviders: z9.number().int().nonnegative(),
|
|
933
|
+
totalComparisons: z9.number().int().nonnegative(),
|
|
934
|
+
mentionCount: z9.number().int().nonnegative(),
|
|
935
|
+
citationCount: z9.number().int().nonnegative(),
|
|
936
|
+
topCompetitors: z9.array(snapshotCompetitorEntrySchema).default([]),
|
|
937
|
+
visibilityGap: z9.string(),
|
|
938
|
+
whatThisMeans: z9.array(z9.string()).default([]),
|
|
939
|
+
recommendedActions: z9.array(z9.string()).default([])
|
|
940
|
+
});
|
|
941
|
+
var snapshotReportSchema = z9.object({
|
|
942
|
+
companyName: z9.string(),
|
|
943
|
+
domain: z9.string(),
|
|
944
|
+
homepageUrl: z9.string(),
|
|
945
|
+
generatedAt: z9.string(),
|
|
946
|
+
phrases: z9.array(z9.string()).default([]),
|
|
947
|
+
competitors: z9.array(z9.string()).default([]),
|
|
948
|
+
profile: snapshotProfileSchema,
|
|
949
|
+
audit: snapshotAuditSchema,
|
|
950
|
+
queryResults: z9.array(snapshotQueryResultSchema).default([]),
|
|
951
|
+
summary: snapshotSummarySchema
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
// ../contracts/src/schedule.ts
|
|
955
|
+
import { z as z10 } from "zod";
|
|
956
|
+
var scheduleDtoSchema = z10.object({
|
|
957
|
+
id: z10.string(),
|
|
958
|
+
projectId: z10.string(),
|
|
959
|
+
cronExpr: z10.string(),
|
|
960
|
+
preset: z10.string().nullable().optional(),
|
|
961
|
+
timezone: z10.string().default("UTC"),
|
|
962
|
+
enabled: z10.boolean().default(true),
|
|
963
|
+
providers: z10.array(providerNameSchema).default([]),
|
|
964
|
+
lastRunAt: z10.string().nullable().optional(),
|
|
965
|
+
nextRunAt: z10.string().nullable().optional(),
|
|
966
|
+
createdAt: z10.string(),
|
|
967
|
+
updatedAt: z10.string()
|
|
968
|
+
});
|
|
969
|
+
var scheduleUpsertRequestSchema = z10.object({
|
|
970
|
+
preset: z10.string().optional(),
|
|
971
|
+
cron: z10.string().optional(),
|
|
972
|
+
timezone: z10.string().optional().default("UTC"),
|
|
973
|
+
enabled: z10.boolean().optional().default(true),
|
|
974
|
+
providers: z10.array(providerNameSchema).optional().default([])
|
|
975
|
+
}).refine(
|
|
976
|
+
(data) => data.preset && !data.cron || !data.preset && data.cron,
|
|
977
|
+
{ message: 'Exactly one of "preset" or "cron" must be provided' }
|
|
978
|
+
);
|
|
979
|
+
|
|
980
|
+
// ../contracts/src/analytics.ts
|
|
981
|
+
import { z as z11 } from "zod";
|
|
982
|
+
var visibilityMetricModeSchema = z11.enum(["mentioned", "cited"]);
|
|
983
|
+
var VisibilityMetricModes = visibilityMetricModeSchema.enum;
|
|
984
|
+
function parseWindow(value) {
|
|
985
|
+
if (value === "7d" || value === "30d" || value === "90d" || value === "all") return value;
|
|
986
|
+
return "all";
|
|
987
|
+
}
|
|
988
|
+
function windowCutoff(window) {
|
|
989
|
+
if (window === "all") return null;
|
|
990
|
+
const days = window === "7d" ? 7 : window === "30d" ? 30 : 90;
|
|
991
|
+
const d = /* @__PURE__ */ new Date();
|
|
992
|
+
d.setDate(d.getDate() - days);
|
|
993
|
+
return d.toISOString();
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// ../contracts/src/source-categories.ts
|
|
997
|
+
var SOURCE_CATEGORY_RULES = [
|
|
998
|
+
// Forums
|
|
999
|
+
{ pattern: "reddit.com", category: "forum", label: "Reddit" },
|
|
1000
|
+
{ pattern: "quora.com", category: "forum", label: "Quora" },
|
|
1001
|
+
{ pattern: "stackexchange.com", category: "forum", label: "Stack Exchange" },
|
|
1002
|
+
{ pattern: "stackoverflow.com", category: "forum", label: "Stack Overflow" },
|
|
1003
|
+
{ pattern: "discourse.org", category: "forum", label: "Discourse" },
|
|
1004
|
+
// Social
|
|
1005
|
+
{ pattern: "linkedin.com", category: "social", label: "LinkedIn" },
|
|
1006
|
+
{ pattern: "twitter.com", category: "social", label: "X (Twitter)" },
|
|
1007
|
+
{ pattern: "x.com", category: "social", label: "X (Twitter)" },
|
|
1008
|
+
{ pattern: "facebook.com", category: "social", label: "Facebook" },
|
|
1009
|
+
{ pattern: "instagram.com", category: "social", label: "Instagram" },
|
|
1010
|
+
{ pattern: "threads.net", category: "social", label: "Threads" },
|
|
1011
|
+
{ pattern: "pinterest.com", category: "social", label: "Pinterest" },
|
|
1012
|
+
{ pattern: "tiktok.com", category: "social", label: "TikTok" },
|
|
1013
|
+
// Video
|
|
1014
|
+
{ pattern: "youtube.com", category: "video", label: "YouTube" },
|
|
1015
|
+
{ pattern: "youtu.be", category: "video", label: "YouTube" },
|
|
1016
|
+
{ pattern: "vimeo.com", category: "video", label: "Vimeo" },
|
|
1017
|
+
// News
|
|
1018
|
+
{ pattern: "nytimes.com", category: "news", label: "NY Times" },
|
|
1019
|
+
{ pattern: "bbc.com", category: "news", label: "BBC" },
|
|
1020
|
+
{ pattern: "bbc.co.uk", category: "news", label: "BBC" },
|
|
1021
|
+
{ pattern: "cnn.com", category: "news", label: "CNN" },
|
|
1022
|
+
{ pattern: "reuters.com", category: "news", label: "Reuters" },
|
|
1023
|
+
{ pattern: "apnews.com", category: "news", label: "AP News" },
|
|
1024
|
+
{ pattern: "theguardian.com", category: "news", label: "The Guardian" },
|
|
1025
|
+
{ pattern: "washingtonpost.com", category: "news", label: "Washington Post" },
|
|
1026
|
+
{ pattern: "wsj.com", category: "news", label: "WSJ" },
|
|
1027
|
+
{ pattern: "forbes.com", category: "news", label: "Forbes" },
|
|
1028
|
+
{ pattern: "techcrunch.com", category: "news", label: "TechCrunch" },
|
|
1029
|
+
{ pattern: "theverge.com", category: "news", label: "The Verge" },
|
|
1030
|
+
{ pattern: "wired.com", category: "news", label: "Wired" },
|
|
1031
|
+
{ pattern: "arstechnica.com", category: "news", label: "Ars Technica" },
|
|
1032
|
+
// Reference
|
|
1033
|
+
{ pattern: "wikipedia.org", category: "reference", label: "Wikipedia" },
|
|
1034
|
+
{ pattern: "wikimedia.org", category: "reference", label: "Wikimedia" },
|
|
1035
|
+
{ pattern: "britannica.com", category: "reference", label: "Britannica" },
|
|
1036
|
+
{ pattern: "merriam-webster.com", category: "reference", label: "Merriam-Webster" },
|
|
1037
|
+
// Blog / Content platforms
|
|
1038
|
+
{ pattern: "medium.com", category: "blog", label: "Medium" },
|
|
1039
|
+
{ pattern: "substack.com", category: "blog", label: "Substack" },
|
|
1040
|
+
{ pattern: "dev.to", category: "blog", label: "DEV Community" },
|
|
1041
|
+
{ pattern: "hashnode.dev", category: "blog", label: "Hashnode" },
|
|
1042
|
+
{ pattern: "wordpress.com", category: "blog", label: "WordPress" },
|
|
1043
|
+
{ pattern: "blogger.com", category: "blog", label: "Blogger" },
|
|
1044
|
+
{ pattern: "hubspot.com", category: "blog", label: "HubSpot" },
|
|
1045
|
+
// E-commerce
|
|
1046
|
+
{ pattern: "amazon.com", category: "ecommerce", label: "Amazon" },
|
|
1047
|
+
{ pattern: "amazon.co.uk", category: "ecommerce", label: "Amazon UK" },
|
|
1048
|
+
{ pattern: "shopify.com", category: "ecommerce", label: "Shopify" },
|
|
1049
|
+
{ pattern: "ebay.com", category: "ecommerce", label: "eBay" },
|
|
1050
|
+
// Academic
|
|
1051
|
+
{ pattern: "scholar.google.com", category: "academic", label: "Google Scholar" },
|
|
1052
|
+
{ pattern: "arxiv.org", category: "academic", label: "arXiv" },
|
|
1053
|
+
{ pattern: "pubmed.ncbi.nlm.nih.gov", category: "academic", label: "PubMed" },
|
|
1054
|
+
{ pattern: "researchgate.net", category: "academic", label: "ResearchGate" },
|
|
1055
|
+
{ pattern: ".edu", category: "academic", label: "Academic (.edu)" }
|
|
1056
|
+
];
|
|
1057
|
+
var CATEGORY_LABELS = {
|
|
1058
|
+
social: "Social Media",
|
|
1059
|
+
forum: "Forums & Q&A",
|
|
1060
|
+
news: "News & Media",
|
|
1061
|
+
reference: "Reference",
|
|
1062
|
+
blog: "Blogs & Content",
|
|
1063
|
+
ecommerce: "E-commerce",
|
|
1064
|
+
video: "Video",
|
|
1065
|
+
academic: "Academic",
|
|
1066
|
+
other: "Other"
|
|
1067
|
+
};
|
|
1068
|
+
function categorizeSource(uri) {
|
|
1069
|
+
let domain;
|
|
1070
|
+
try {
|
|
1071
|
+
const url = new URL(uri.startsWith("http") ? uri : `https://${uri}`);
|
|
1072
|
+
domain = url.hostname.replace(/^www\./, "");
|
|
1073
|
+
} catch {
|
|
1074
|
+
domain = uri.replace(/^https?:\/\//, "").replace(/^www\./, "").split("/")[0] ?? uri;
|
|
1075
|
+
}
|
|
1076
|
+
const domainLower = domain.toLowerCase();
|
|
1077
|
+
for (const rule of SOURCE_CATEGORY_RULES) {
|
|
1078
|
+
if (domainLower === rule.pattern || domainLower.endsWith(`.${rule.pattern}`) || rule.pattern.startsWith(".") && domainLower.endsWith(rule.pattern)) {
|
|
1079
|
+
return { category: rule.category, label: rule.label, domain };
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
return { category: "other", label: CATEGORY_LABELS.other, domain };
|
|
1083
|
+
}
|
|
1084
|
+
function categoryLabel(category) {
|
|
1085
|
+
return CATEGORY_LABELS[category];
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// ../contracts/src/ga.ts
|
|
1089
|
+
import { z as z12 } from "zod";
|
|
1090
|
+
var ga4ConnectionDtoSchema = z12.object({
|
|
1091
|
+
id: z12.string(),
|
|
1092
|
+
projectId: z12.string(),
|
|
1093
|
+
propertyId: z12.string(),
|
|
1094
|
+
clientEmail: z12.string(),
|
|
1095
|
+
connected: z12.boolean(),
|
|
1096
|
+
createdAt: z12.string(),
|
|
1097
|
+
updatedAt: z12.string()
|
|
1098
|
+
});
|
|
1099
|
+
var ga4TrafficSnapshotDtoSchema = z12.object({
|
|
1100
|
+
date: z12.string(),
|
|
1101
|
+
landingPage: z12.string(),
|
|
1102
|
+
sessions: z12.number(),
|
|
1103
|
+
organicSessions: z12.number(),
|
|
1104
|
+
users: z12.number()
|
|
1105
|
+
});
|
|
1106
|
+
var ga4SourceDimensionSchema = z12.enum(["session", "first_user", "manual_utm"]);
|
|
1107
|
+
var ga4AiReferralDtoSchema = z12.object({
|
|
1108
|
+
source: z12.string(),
|
|
1109
|
+
medium: z12.string(),
|
|
1110
|
+
sessions: z12.number(),
|
|
1111
|
+
users: z12.number(),
|
|
1112
|
+
sourceDimension: ga4SourceDimensionSchema
|
|
1113
|
+
});
|
|
1114
|
+
var ga4AiReferralLandingPageDtoSchema = z12.object({
|
|
1115
|
+
source: z12.string(),
|
|
1116
|
+
medium: z12.string(),
|
|
1117
|
+
sourceDimension: ga4SourceDimensionSchema,
|
|
1118
|
+
landingPage: z12.string(),
|
|
1119
|
+
sessions: z12.number(),
|
|
1120
|
+
users: z12.number()
|
|
1121
|
+
});
|
|
1122
|
+
var ga4SocialReferralDtoSchema = z12.object({
|
|
1123
|
+
source: z12.string(),
|
|
1124
|
+
medium: z12.string(),
|
|
1125
|
+
sessions: z12.number(),
|
|
1126
|
+
users: z12.number(),
|
|
1127
|
+
/** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
|
|
1128
|
+
channelGroup: z12.string()
|
|
1129
|
+
});
|
|
1130
|
+
var ga4TrafficSummaryDtoSchema = z12.object({
|
|
1131
|
+
totalSessions: z12.number(),
|
|
1132
|
+
totalOrganicSessions: z12.number(),
|
|
1133
|
+
/** 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. */
|
|
1134
|
+
totalDirectSessions: z12.number(),
|
|
1135
|
+
totalUsers: z12.number(),
|
|
1136
|
+
topPages: z12.array(z12.object({
|
|
1137
|
+
landingPage: z12.string(),
|
|
1138
|
+
sessions: z12.number(),
|
|
1139
|
+
organicSessions: z12.number(),
|
|
1140
|
+
/** Per-page Direct-channel sessions. 0 for legacy rows. */
|
|
1141
|
+
directSessions: z12.number(),
|
|
1142
|
+
users: z12.number()
|
|
1143
|
+
})),
|
|
1144
|
+
aiReferrals: z12.array(ga4AiReferralDtoSchema),
|
|
1145
|
+
aiReferralLandingPages: z12.array(ga4AiReferralLandingPageDtoSchema),
|
|
1146
|
+
/** 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. */
|
|
1147
|
+
aiSessionsDeduped: z12.number(),
|
|
1148
|
+
/** Deduped AI user total: MAX(users) per date+source+medium across attribution dimensions, then summed. */
|
|
1149
|
+
aiUsersDeduped: z12.number(),
|
|
1150
|
+
/** AI sessions whose CURRENT sessionSource matched an AI engine. Disjoint from Direct/Organic/Social — safe for the channel breakdown. */
|
|
1151
|
+
aiSessionsBySession: z12.number(),
|
|
1152
|
+
/** AI users whose CURRENT sessionSource matched an AI engine. Disjoint from Direct/Organic/Social — safe for the channel breakdown. */
|
|
1153
|
+
aiUsersBySession: z12.number(),
|
|
1154
|
+
socialReferrals: z12.array(ga4SocialReferralDtoSchema),
|
|
1155
|
+
/** Total social sessions (session-scoped, no cross-dimension dedup needed). */
|
|
1156
|
+
socialSessions: z12.number(),
|
|
1157
|
+
/** Total social users (session-scoped, no cross-dimension dedup needed). */
|
|
1158
|
+
socialUsers: z12.number(),
|
|
1159
|
+
/** Organic sessions as a percentage of total sessions (0–100, rounded). */
|
|
1160
|
+
organicSharePct: z12.number(),
|
|
1161
|
+
/** Deduped AI sessions as a percentage of total sessions (0–100, rounded). Cross-cutting: can overlap with Direct/Organic/Social. */
|
|
1162
|
+
aiSharePct: z12.number(),
|
|
1163
|
+
/** Session-source-only AI sessions as a percentage of total sessions (0–100, rounded). Disjoint from Direct/Organic/Social. */
|
|
1164
|
+
aiSharePctBySession: z12.number(),
|
|
1165
|
+
/** Direct-channel sessions as a percentage of total sessions (0–100, rounded). */
|
|
1166
|
+
directSharePct: z12.number(),
|
|
1167
|
+
/** Social sessions as a percentage of total sessions (0–100, rounded). */
|
|
1168
|
+
socialSharePct: z12.number(),
|
|
1169
|
+
/** Display string for organicSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
1170
|
+
organicSharePctDisplay: z12.string(),
|
|
1171
|
+
/** Display string for aiSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
1172
|
+
aiSharePctDisplay: z12.string(),
|
|
1173
|
+
/** Display string for aiSharePctBySession: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
1174
|
+
aiSharePctBySessionDisplay: z12.string(),
|
|
1175
|
+
/** Display string for directSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
1176
|
+
directSharePctDisplay: z12.string(),
|
|
1177
|
+
/** Display string for socialSharePct: 'X%', '<1%' for non-zero shares that round below 1, or '—' when sessions exist but total is unknown (partial sync). */
|
|
1178
|
+
socialSharePctDisplay: z12.string(),
|
|
1179
|
+
lastSyncedAt: z12.string().nullable()
|
|
1180
|
+
});
|
|
1181
|
+
var ga4AiReferralHistoryEntrySchema = z12.object({
|
|
1182
|
+
date: z12.string(),
|
|
1183
|
+
source: z12.string(),
|
|
1184
|
+
medium: z12.string(),
|
|
1185
|
+
landingPage: z12.string(),
|
|
1186
|
+
sessions: z12.number(),
|
|
1187
|
+
users: z12.number(),
|
|
1188
|
+
/** Which GA4 dimension this row came from: session (sessionSource), first_user (firstUserSource), or manual_utm (utm_source parameter) */
|
|
1189
|
+
sourceDimension: ga4SourceDimensionSchema
|
|
1190
|
+
});
|
|
1191
|
+
var ga4SocialReferralHistoryEntrySchema = z12.object({
|
|
1192
|
+
date: z12.string(),
|
|
1193
|
+
source: z12.string(),
|
|
1194
|
+
medium: z12.string(),
|
|
1195
|
+
sessions: z12.number(),
|
|
1196
|
+
users: z12.number(),
|
|
1197
|
+
/** GA4 default channel group (e.g. 'Organic Social', 'Paid Social') */
|
|
1198
|
+
channelGroup: z12.string()
|
|
1199
|
+
});
|
|
1200
|
+
var ga4SessionHistoryEntrySchema = z12.object({
|
|
1201
|
+
date: z12.string(),
|
|
1202
|
+
sessions: z12.number(),
|
|
1203
|
+
organicSessions: z12.number(),
|
|
1204
|
+
users: z12.number()
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
// ../contracts/src/answer-visibility.ts
|
|
1208
|
+
var GENERIC_TOKENS = /* @__PURE__ */ new Set([
|
|
1209
|
+
"agency",
|
|
1210
|
+
"app",
|
|
1211
|
+
"company",
|
|
1212
|
+
"corp",
|
|
1213
|
+
"group",
|
|
1214
|
+
"health",
|
|
1215
|
+
"inc",
|
|
1216
|
+
"llc",
|
|
1217
|
+
"online",
|
|
1218
|
+
"platform",
|
|
1219
|
+
"services",
|
|
1220
|
+
"site",
|
|
1221
|
+
"solutions",
|
|
1222
|
+
"software",
|
|
1223
|
+
"systems",
|
|
1224
|
+
"tech"
|
|
1225
|
+
]);
|
|
1226
|
+
var MIN_BRAND_KEY_LENGTH = 6;
|
|
1227
|
+
var BUSINESS_SUFFIXES = [
|
|
1228
|
+
"incorporated",
|
|
1229
|
+
"corporation",
|
|
1230
|
+
"limited",
|
|
1231
|
+
"company",
|
|
1232
|
+
"gmbh",
|
|
1233
|
+
"pllc",
|
|
1234
|
+
"corp",
|
|
1235
|
+
"group",
|
|
1236
|
+
"llp",
|
|
1237
|
+
"plc",
|
|
1238
|
+
"llc",
|
|
1239
|
+
"inc",
|
|
1240
|
+
"ltd"
|
|
1241
|
+
];
|
|
1242
|
+
function extractAnswerMentions(answerText, displayName, domains) {
|
|
1243
|
+
if (!answerText) return { mentioned: false, matchedTerms: [] };
|
|
1244
|
+
const matchedTerms = [];
|
|
1245
|
+
const lowerAnswer = answerText.toLowerCase();
|
|
1246
|
+
for (const domain of domains) {
|
|
1247
|
+
const normalizedDomain = normalizeProjectDomain(domain);
|
|
1248
|
+
if (!normalizedDomain || !normalizedDomain.includes(".")) continue;
|
|
1249
|
+
if (domainMentioned(lowerAnswer, normalizedDomain)) {
|
|
1250
|
+
matchedTerms.push(normalizedDomain);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
const answerNormalized = normalizeText(answerText);
|
|
1254
|
+
const answerBrandKey = brandKeyFromText(answerText);
|
|
1255
|
+
const normalizedCandidates = brandNormalizedCandidates(displayName);
|
|
1256
|
+
const brandKeyCandidates = brandKeyCandidatesForMatch(displayName);
|
|
1257
|
+
const matchesNormalized = normalizedCandidates.some(
|
|
1258
|
+
(c) => new RegExp(`\\b${escapeRegExp(c)}\\b`).test(answerNormalized)
|
|
1259
|
+
);
|
|
1260
|
+
const matchesBrandKey = brandKeyCandidates.some(
|
|
1261
|
+
(c) => c.length >= MIN_BRAND_KEY_LENGTH && answerBrandKey.includes(c)
|
|
1262
|
+
);
|
|
1263
|
+
if (matchesNormalized || matchesBrandKey) {
|
|
1264
|
+
matchedTerms.push(displayName);
|
|
1265
|
+
}
|
|
1266
|
+
const tokens = collectDistinctiveTokens(displayName, domains);
|
|
1267
|
+
let tokenMatches = 0;
|
|
1268
|
+
const matchedTokens = [];
|
|
1269
|
+
for (const token of tokens) {
|
|
1270
|
+
if (new RegExp(`\\b${escapeRegExp(token)}\\b`).test(lowerAnswer)) {
|
|
1271
|
+
tokenMatches++;
|
|
1272
|
+
matchedTokens.push(token);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
const tokenThresholdMet = tokens.length > 0 && (tokens.length === 1 && tokenMatches >= 1 || tokenMatches >= Math.min(2, tokens.length));
|
|
1276
|
+
if (tokenThresholdMet) {
|
|
1277
|
+
matchedTerms.push(...matchedTokens);
|
|
1278
|
+
}
|
|
1279
|
+
const unique = [...new Set(matchedTerms)];
|
|
1280
|
+
const domainMatches = unique.filter((t) => t.includes("."));
|
|
1281
|
+
const dedupedFinal = unique.filter((term) => {
|
|
1282
|
+
if (term.includes(".")) return true;
|
|
1283
|
+
return !domainMatches.some((d) => d.toLowerCase().startsWith(term.toLowerCase() + "."));
|
|
1284
|
+
});
|
|
1285
|
+
return { mentioned: dedupedFinal.length > 0, matchedTerms: dedupedFinal };
|
|
1286
|
+
}
|
|
1287
|
+
function determineAnswerMentioned(answerText, displayName, domains) {
|
|
1288
|
+
return extractAnswerMentions(answerText, displayName, domains).mentioned;
|
|
1289
|
+
}
|
|
1290
|
+
function visibilityStateFromAnswerMentioned(answerMentioned) {
|
|
1291
|
+
return answerMentioned ? "visible" : "not-visible";
|
|
1292
|
+
}
|
|
1293
|
+
function brandKeyFromText(value) {
|
|
1294
|
+
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1295
|
+
}
|
|
1296
|
+
function domainMentioned(lowerAnswer, normalizedDomain) {
|
|
1297
|
+
const escapedDomain = escapeRegExp(normalizedDomain.toLowerCase());
|
|
1298
|
+
const patterns = [
|
|
1299
|
+
new RegExp(`(^|[^a-z0-9-])${escapedDomain}($|[^a-z0-9-])`),
|
|
1300
|
+
new RegExp(`https?://(?:www\\.)?${escapedDomain}(?:[/:?#]|$)`),
|
|
1301
|
+
new RegExp(`www\\.${escapedDomain}(?:[/:?#]|$)`)
|
|
1302
|
+
];
|
|
1303
|
+
return patterns.some((pattern) => pattern.test(lowerAnswer));
|
|
1304
|
+
}
|
|
1305
|
+
function collectDistinctiveTokens(displayName, domains) {
|
|
1306
|
+
const tokens = /* @__PURE__ */ new Set();
|
|
1307
|
+
for (const token of extractDistinctiveTokens(displayName)) {
|
|
1308
|
+
tokens.add(token);
|
|
1309
|
+
}
|
|
1310
|
+
for (const domain of domains) {
|
|
1311
|
+
const reg = registrableDomain(domain);
|
|
1312
|
+
const brand = reg ? brandLabelFromDomain(reg) : normalizeProjectDomain(domain).split("/")[0]?.split(".")[0] ?? "";
|
|
1313
|
+
const token = brand.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
1314
|
+
if (isDistinctiveToken(token)) tokens.add(token);
|
|
1315
|
+
}
|
|
1316
|
+
return [...tokens];
|
|
1317
|
+
}
|
|
1318
|
+
function extractDistinctiveTokens(value) {
|
|
1319
|
+
return normalizeText(value).split(" ").filter(isDistinctiveToken);
|
|
1320
|
+
}
|
|
1321
|
+
function isDistinctiveToken(token) {
|
|
1322
|
+
if (token.length < 4) return false;
|
|
1323
|
+
return !GENERIC_TOKENS.has(token);
|
|
1324
|
+
}
|
|
1325
|
+
function normalizeText(value) {
|
|
1326
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
1327
|
+
}
|
|
1328
|
+
function brandNormalizedCandidates(displayName) {
|
|
1329
|
+
const original = normalizeText(displayName);
|
|
1330
|
+
if (!original) return [];
|
|
1331
|
+
const stripped = stripBusinessSuffix(original, " ");
|
|
1332
|
+
if (!stripped || stripped === original) return [original];
|
|
1333
|
+
return [original, stripped];
|
|
1334
|
+
}
|
|
1335
|
+
function brandKeyCandidatesForMatch(displayName) {
|
|
1336
|
+
const original = brandKeyFromText(displayName);
|
|
1337
|
+
if (!original) return [];
|
|
1338
|
+
const stripped = stripBusinessSuffix(original, "");
|
|
1339
|
+
return stripped && stripped !== original ? [original, stripped] : [original];
|
|
1340
|
+
}
|
|
1341
|
+
function stripBusinessSuffix(value, separator) {
|
|
1342
|
+
for (const suffix of BUSINESS_SUFFIXES) {
|
|
1343
|
+
const trailing = `${separator}${suffix}`;
|
|
1344
|
+
if (value.endsWith(trailing) && value.length - trailing.length >= 3) {
|
|
1345
|
+
return value.slice(0, -trailing.length);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
return value;
|
|
1349
|
+
}
|
|
1350
|
+
function escapeRegExp(value) {
|
|
1351
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// ../contracts/src/agent.ts
|
|
1355
|
+
import { z as z13 } from "zod";
|
|
1356
|
+
var memorySourceSchema = z13.enum(["aero", "user", "compaction"]);
|
|
1357
|
+
var MemorySources = memorySourceSchema.enum;
|
|
1358
|
+
var AGENT_MEMORY_VALUE_MAX_BYTES = 2 * 1024;
|
|
1359
|
+
var AGENT_MEMORY_KEY_MAX_LENGTH = 128;
|
|
1360
|
+
var agentMemoryUpsertRequestSchema = z13.object({
|
|
1361
|
+
key: z13.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH),
|
|
1362
|
+
value: z13.string().min(1)
|
|
1363
|
+
});
|
|
1364
|
+
var agentMemoryDeleteRequestSchema = z13.object({
|
|
1365
|
+
key: z13.string().min(1).max(AGENT_MEMORY_KEY_MAX_LENGTH)
|
|
1366
|
+
});
|
|
1367
|
+
|
|
1368
|
+
// ../contracts/src/backlinks.ts
|
|
1369
|
+
import { z as z14 } from "zod";
|
|
1370
|
+
var ccReleaseSyncStatusSchema = z14.enum(["queued", "downloading", "querying", "ready", "failed"]);
|
|
1371
|
+
var CcReleaseSyncStatuses = ccReleaseSyncStatusSchema.enum;
|
|
1372
|
+
var ccReleaseSyncDtoSchema = z14.object({
|
|
1373
|
+
id: z14.string(),
|
|
1374
|
+
release: z14.string(),
|
|
1375
|
+
status: ccReleaseSyncStatusSchema,
|
|
1376
|
+
phaseDetail: z14.string().nullable().optional(),
|
|
1377
|
+
vertexPath: z14.string().nullable().optional(),
|
|
1378
|
+
edgesPath: z14.string().nullable().optional(),
|
|
1379
|
+
vertexSha256: z14.string().nullable().optional(),
|
|
1380
|
+
edgesSha256: z14.string().nullable().optional(),
|
|
1381
|
+
vertexBytes: z14.number().int().nullable().optional(),
|
|
1382
|
+
edgesBytes: z14.number().int().nullable().optional(),
|
|
1383
|
+
projectsProcessed: z14.number().int().nullable().optional(),
|
|
1384
|
+
domainsDiscovered: z14.number().int().nullable().optional(),
|
|
1385
|
+
downloadStartedAt: z14.string().nullable().optional(),
|
|
1386
|
+
downloadFinishedAt: z14.string().nullable().optional(),
|
|
1387
|
+
queryStartedAt: z14.string().nullable().optional(),
|
|
1388
|
+
queryFinishedAt: z14.string().nullable().optional(),
|
|
1389
|
+
error: z14.string().nullable().optional(),
|
|
1390
|
+
createdAt: z14.string(),
|
|
1391
|
+
updatedAt: z14.string()
|
|
1392
|
+
});
|
|
1393
|
+
var backlinkDomainDtoSchema = z14.object({
|
|
1394
|
+
linkingDomain: z14.string(),
|
|
1395
|
+
numHosts: z14.number().int()
|
|
1396
|
+
});
|
|
1397
|
+
var backlinkSummaryDtoSchema = z14.object({
|
|
1398
|
+
projectId: z14.string(),
|
|
1399
|
+
release: z14.string(),
|
|
1400
|
+
targetDomain: z14.string(),
|
|
1401
|
+
totalLinkingDomains: z14.number().int(),
|
|
1402
|
+
totalHosts: z14.number().int(),
|
|
1403
|
+
top10HostsShare: z14.string(),
|
|
1404
|
+
queriedAt: z14.string(),
|
|
1405
|
+
// Populated when the response is filtered (e.g. ?excludeCrawlers=1).
|
|
1406
|
+
// Counts the rows omitted from totalLinkingDomains/totalHosts so callers
|
|
1407
|
+
// can show "N hidden" hints without re-deriving them.
|
|
1408
|
+
excludedLinkingDomains: z14.number().int().optional(),
|
|
1409
|
+
excludedHosts: z14.number().int().optional()
|
|
1410
|
+
});
|
|
1411
|
+
var backlinkListResponseSchema = z14.object({
|
|
1412
|
+
summary: backlinkSummaryDtoSchema.nullable(),
|
|
1413
|
+
total: z14.number().int(),
|
|
1414
|
+
rows: z14.array(backlinkDomainDtoSchema)
|
|
1415
|
+
});
|
|
1416
|
+
var backlinkHistoryEntrySchema = z14.object({
|
|
1417
|
+
release: z14.string(),
|
|
1418
|
+
totalLinkingDomains: z14.number().int(),
|
|
1419
|
+
totalHosts: z14.number().int(),
|
|
1420
|
+
top10HostsShare: z14.string(),
|
|
1421
|
+
queriedAt: z14.string()
|
|
1422
|
+
});
|
|
1423
|
+
var backlinksInstallStatusDtoSchema = z14.object({
|
|
1424
|
+
duckdbInstalled: z14.boolean(),
|
|
1425
|
+
duckdbVersion: z14.string().nullable().optional(),
|
|
1426
|
+
duckdbSpec: z14.string(),
|
|
1427
|
+
pluginDir: z14.string()
|
|
1428
|
+
});
|
|
1429
|
+
var backlinksInstallResultDtoSchema = z14.object({
|
|
1430
|
+
installed: z14.boolean(),
|
|
1431
|
+
version: z14.string(),
|
|
1432
|
+
path: z14.string(),
|
|
1433
|
+
alreadyPresent: z14.boolean()
|
|
1434
|
+
});
|
|
1435
|
+
var ccAvailableReleaseSchema = z14.object({
|
|
1436
|
+
release: z14.string(),
|
|
1437
|
+
vertexUrl: z14.string(),
|
|
1438
|
+
edgesUrl: z14.string(),
|
|
1439
|
+
vertexBytes: z14.number().int().nullable(),
|
|
1440
|
+
edgesBytes: z14.number().int().nullable(),
|
|
1441
|
+
lastModified: z14.string().nullable()
|
|
1442
|
+
});
|
|
1443
|
+
var ccCachedReleaseSchema = z14.object({
|
|
1444
|
+
release: z14.string(),
|
|
1445
|
+
syncStatus: ccReleaseSyncStatusSchema.nullable(),
|
|
1446
|
+
bytes: z14.number().int(),
|
|
1447
|
+
lastUsedAt: z14.string().nullable()
|
|
1448
|
+
});
|
|
1449
|
+
|
|
1450
|
+
// ../contracts/src/composites.ts
|
|
1451
|
+
import { z as z15 } from "zod";
|
|
1452
|
+
var searchHitKindSchema = z15.enum(["snapshot", "insight"]);
|
|
1453
|
+
var projectSearchSnapshotHitSchema = z15.object({
|
|
1454
|
+
kind: z15.literal("snapshot"),
|
|
1455
|
+
id: z15.string(),
|
|
1456
|
+
runId: z15.string(),
|
|
1457
|
+
keyword: z15.string(),
|
|
1458
|
+
provider: z15.string(),
|
|
1459
|
+
model: z15.string().nullable(),
|
|
1460
|
+
citationState: citationStateSchema,
|
|
1461
|
+
matchedField: z15.enum(["answerText", "citedDomains", "searchQueries", "keyword"]),
|
|
1462
|
+
snippet: z15.string(),
|
|
1463
|
+
createdAt: z15.string()
|
|
1464
|
+
});
|
|
1465
|
+
var projectSearchInsightHitSchema = z15.object({
|
|
1466
|
+
kind: z15.literal("insight"),
|
|
1467
|
+
id: z15.string(),
|
|
1468
|
+
runId: z15.string().nullable(),
|
|
1469
|
+
type: z15.enum(["regression", "gain", "opportunity"]),
|
|
1470
|
+
severity: z15.enum(["critical", "high", "medium", "low"]),
|
|
1471
|
+
title: z15.string(),
|
|
1472
|
+
keyword: z15.string(),
|
|
1473
|
+
provider: z15.string(),
|
|
1474
|
+
matchedField: z15.enum(["title", "keyword", "recommendation", "cause"]),
|
|
1475
|
+
snippet: z15.string(),
|
|
1476
|
+
dismissed: z15.boolean(),
|
|
1477
|
+
createdAt: z15.string()
|
|
1478
|
+
});
|
|
1479
|
+
var projectSearchHitSchema = z15.discriminatedUnion("kind", [
|
|
1480
|
+
projectSearchSnapshotHitSchema,
|
|
1481
|
+
projectSearchInsightHitSchema
|
|
1482
|
+
]);
|
|
1483
|
+
var projectSearchResponseSchema = z15.object({
|
|
1484
|
+
query: z15.string(),
|
|
1485
|
+
totalHits: z15.number().int().nonnegative(),
|
|
1486
|
+
truncated: z15.boolean(),
|
|
1487
|
+
hits: z15.array(projectSearchHitSchema)
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
// ../contracts/src/content.ts
|
|
1491
|
+
import { z as z16 } from "zod";
|
|
1492
|
+
var contentActionSchema = z16.enum(["create", "expand", "refresh", "add-schema"]);
|
|
1493
|
+
var ContentActions = contentActionSchema.enum;
|
|
1494
|
+
var demandSourceSchema = z16.enum(["gsc", "competitor-evidence", "both"]);
|
|
1495
|
+
var DemandSources = demandSourceSchema.enum;
|
|
1496
|
+
var actionConfidenceSchema = z16.enum(["high", "medium", "low"]);
|
|
1497
|
+
var ActionConfidences = actionConfidenceSchema.enum;
|
|
1498
|
+
var pageTypeSchema = z16.enum([
|
|
1499
|
+
"blog-post",
|
|
1500
|
+
"comparison",
|
|
1501
|
+
"listicle",
|
|
1502
|
+
"how-to",
|
|
1503
|
+
"guide",
|
|
1504
|
+
"glossary"
|
|
1505
|
+
]);
|
|
1506
|
+
var PageTypes = pageTypeSchema.enum;
|
|
1507
|
+
var contentActionStateSchema = z16.enum([
|
|
1508
|
+
"proposed",
|
|
1509
|
+
"briefed",
|
|
1510
|
+
"payload-generated",
|
|
1511
|
+
"draft-created",
|
|
1512
|
+
"published",
|
|
1513
|
+
"validated",
|
|
1514
|
+
"dismissed"
|
|
1515
|
+
]);
|
|
1516
|
+
var ContentActionStates = contentActionStateSchema.enum;
|
|
1517
|
+
var ourBestPageSchema = z16.object({
|
|
1518
|
+
url: z16.string(),
|
|
1519
|
+
gscImpressions: z16.number().nonnegative(),
|
|
1520
|
+
gscClicks: z16.number().nonnegative(),
|
|
1521
|
+
// Null when the page came from the inventory fallback (no GSC ranking data).
|
|
1522
|
+
gscAvgPosition: z16.number().nonnegative().nullable(),
|
|
1523
|
+
organicSessions: z16.number().nonnegative()
|
|
1524
|
+
});
|
|
1525
|
+
var winningCompetitorSchema = z16.object({
|
|
1526
|
+
domain: z16.string(),
|
|
1527
|
+
url: z16.string(),
|
|
1528
|
+
title: z16.string(),
|
|
1529
|
+
citationCount: z16.number().int().nonnegative()
|
|
1530
|
+
});
|
|
1531
|
+
var scoreBreakdownSchema = z16.object({
|
|
1532
|
+
demand: z16.number(),
|
|
1533
|
+
competitor: z16.number(),
|
|
1534
|
+
absence: z16.number(),
|
|
1535
|
+
gapSeverity: z16.number()
|
|
1536
|
+
});
|
|
1537
|
+
var existingActionRefSchema = z16.object({
|
|
1538
|
+
actionId: z16.string(),
|
|
1539
|
+
state: contentActionStateSchema,
|
|
1540
|
+
lastUpdated: z16.string()
|
|
1541
|
+
});
|
|
1542
|
+
var contentTargetRowDtoSchema = z16.object({
|
|
1543
|
+
targetRef: z16.string(),
|
|
1544
|
+
query: z16.string(),
|
|
1545
|
+
action: contentActionSchema,
|
|
1546
|
+
ourBestPage: ourBestPageSchema.nullable(),
|
|
1547
|
+
winningCompetitor: winningCompetitorSchema.nullable(),
|
|
1548
|
+
score: z16.number(),
|
|
1549
|
+
scoreBreakdown: scoreBreakdownSchema,
|
|
1550
|
+
drivers: z16.array(z16.string()),
|
|
1551
|
+
demandSource: demandSourceSchema,
|
|
1552
|
+
actionConfidence: actionConfidenceSchema,
|
|
1553
|
+
existingAction: existingActionRefSchema.nullable()
|
|
1554
|
+
});
|
|
1555
|
+
var contentTargetsResponseDtoSchema = z16.object({
|
|
1556
|
+
targets: z16.array(contentTargetRowDtoSchema),
|
|
1557
|
+
contextMetrics: z16.object({
|
|
1558
|
+
totalAiReferralSessions: z16.number().int().nonnegative(),
|
|
1559
|
+
latestRunId: z16.string(),
|
|
1560
|
+
runTimestamp: z16.string()
|
|
1561
|
+
})
|
|
1562
|
+
});
|
|
1563
|
+
var contentGroundingSourceSchema = z16.object({
|
|
1564
|
+
uri: z16.string(),
|
|
1565
|
+
title: z16.string(),
|
|
1566
|
+
domain: z16.string(),
|
|
1567
|
+
isOurDomain: z16.boolean(),
|
|
1568
|
+
isCompetitor: z16.boolean(),
|
|
1569
|
+
citationCount: z16.number().int().nonnegative(),
|
|
1570
|
+
providers: z16.array(providerNameSchema)
|
|
1571
|
+
});
|
|
1572
|
+
var contentSourceRowDtoSchema = z16.object({
|
|
1573
|
+
query: z16.string(),
|
|
1574
|
+
groundingSources: z16.array(contentGroundingSourceSchema)
|
|
1575
|
+
});
|
|
1576
|
+
var contentSourcesResponseDtoSchema = z16.object({
|
|
1577
|
+
sources: z16.array(contentSourceRowDtoSchema),
|
|
1578
|
+
latestRunId: z16.string()
|
|
1579
|
+
});
|
|
1580
|
+
var contentGapRowDtoSchema = z16.object({
|
|
1581
|
+
query: z16.string(),
|
|
1582
|
+
competitorDomains: z16.array(z16.string()),
|
|
1583
|
+
competitorCount: z16.number().int().nonnegative(),
|
|
1584
|
+
missRate: z16.number().min(0).max(1),
|
|
1585
|
+
lastSeenInRunId: z16.string()
|
|
1586
|
+
});
|
|
1587
|
+
var contentGapsResponseDtoSchema = z16.object({
|
|
1588
|
+
gaps: z16.array(contentGapRowDtoSchema),
|
|
1589
|
+
latestRunId: z16.string()
|
|
1590
|
+
});
|
|
1591
|
+
|
|
1592
|
+
// ../contracts/src/doctor.ts
|
|
1593
|
+
import { z as z17 } from "zod";
|
|
1594
|
+
var checkStatusSchema = z17.enum(["ok", "warn", "fail", "skipped"]);
|
|
1595
|
+
var CheckStatuses = checkStatusSchema.enum;
|
|
1596
|
+
var checkScopeSchema = z17.enum(["global", "project"]);
|
|
1597
|
+
var CheckScopes = checkScopeSchema.enum;
|
|
1598
|
+
var checkCategorySchema = z17.enum([
|
|
1599
|
+
"auth",
|
|
1600
|
+
"config",
|
|
1601
|
+
"providers",
|
|
1602
|
+
"integrations",
|
|
1603
|
+
"database",
|
|
1604
|
+
"schedules"
|
|
1605
|
+
]);
|
|
1606
|
+
var CheckCategories = checkCategorySchema.enum;
|
|
1607
|
+
var checkResultSchema = z17.object({
|
|
1608
|
+
id: z17.string(),
|
|
1609
|
+
category: checkCategorySchema,
|
|
1610
|
+
scope: checkScopeSchema,
|
|
1611
|
+
title: z17.string(),
|
|
1612
|
+
status: checkStatusSchema,
|
|
1613
|
+
code: z17.string().describe('Stable machine-readable code (e.g. "google.token.refresh-failed"). Use this for filtering and remediation logic.'),
|
|
1614
|
+
summary: z17.string(),
|
|
1615
|
+
remediation: z17.string().nullable().optional().describe('Operator-facing next step. Null when status is "ok" or no specific remediation applies.'),
|
|
1616
|
+
details: z17.record(z17.string(), z17.unknown()).optional().describe("Structured context \u2014 principal email, redirect URI, missing scopes, etc. Stable per check id."),
|
|
1617
|
+
durationMs: z17.number().int().nonnegative().describe("How long the check took to execute.")
|
|
1618
|
+
});
|
|
1619
|
+
var doctorReportSchema = z17.object({
|
|
1620
|
+
scope: checkScopeSchema,
|
|
1621
|
+
project: z17.string().nullable().describe('Project name when scope is "project", null otherwise.'),
|
|
1622
|
+
generatedAt: z17.string().describe("ISO-8601 timestamp when this doctor run started."),
|
|
1623
|
+
durationMs: z17.number().int().nonnegative(),
|
|
1624
|
+
summary: z17.object({
|
|
1625
|
+
total: z17.number().int().nonnegative(),
|
|
1626
|
+
ok: z17.number().int().nonnegative(),
|
|
1627
|
+
warn: z17.number().int().nonnegative(),
|
|
1628
|
+
fail: z17.number().int().nonnegative(),
|
|
1629
|
+
skipped: z17.number().int().nonnegative()
|
|
1630
|
+
}),
|
|
1631
|
+
checks: z17.array(checkResultSchema)
|
|
1632
|
+
});
|
|
1633
|
+
function summarizeCheckResults(results) {
|
|
1634
|
+
const summary = { total: results.length, ok: 0, warn: 0, fail: 0, skipped: 0 };
|
|
1635
|
+
for (const result of results) {
|
|
1636
|
+
switch (result.status) {
|
|
1637
|
+
case CheckStatuses.ok:
|
|
1638
|
+
summary.ok += 1;
|
|
1639
|
+
break;
|
|
1640
|
+
case CheckStatuses.warn:
|
|
1641
|
+
summary.warn += 1;
|
|
1642
|
+
break;
|
|
1643
|
+
case CheckStatuses.fail:
|
|
1644
|
+
summary.fail += 1;
|
|
1645
|
+
break;
|
|
1646
|
+
case CheckStatuses.skipped:
|
|
1647
|
+
summary.skipped += 1;
|
|
1648
|
+
break;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
return summary;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
// ../contracts/src/url-normalize.ts
|
|
1655
|
+
var STRIP_KEYS = /* @__PURE__ */ new Set([
|
|
1656
|
+
// Click identifiers
|
|
1657
|
+
"fbclid",
|
|
1658
|
+
"gclid",
|
|
1659
|
+
"msclkid",
|
|
1660
|
+
"ttclid",
|
|
1661
|
+
"li_fat_id",
|
|
1662
|
+
"igshid",
|
|
1663
|
+
"yclid",
|
|
1664
|
+
"dclid",
|
|
1665
|
+
"gbraid",
|
|
1666
|
+
"wbraid",
|
|
1667
|
+
"bingid",
|
|
1668
|
+
// Mailchimp
|
|
1669
|
+
"mc_cid",
|
|
1670
|
+
"mc_eid",
|
|
1671
|
+
// Google Analytics linkers
|
|
1672
|
+
"_ga",
|
|
1673
|
+
"_gl",
|
|
1674
|
+
// Google Tag Manager debug
|
|
1675
|
+
"gtm_latency",
|
|
1676
|
+
"gtm_debug",
|
|
1677
|
+
// WordPress internal noise
|
|
1678
|
+
"preview",
|
|
1679
|
+
"preview_id",
|
|
1680
|
+
"preview_nonce",
|
|
1681
|
+
"_thumbnail_id",
|
|
1682
|
+
// Common cache-busters/versioning
|
|
1683
|
+
"v",
|
|
1684
|
+
"ver",
|
|
1685
|
+
"version"
|
|
1686
|
+
]);
|
|
1687
|
+
function shouldStrip(key) {
|
|
1688
|
+
if (STRIP_KEYS.has(key)) return true;
|
|
1689
|
+
if (key.startsWith("utm_")) return true;
|
|
1690
|
+
return false;
|
|
1691
|
+
}
|
|
1692
|
+
function parseQuery(query) {
|
|
1693
|
+
if (query === "") return [];
|
|
1694
|
+
return query.split("&").map((pair) => {
|
|
1695
|
+
const eq = pair.indexOf("=");
|
|
1696
|
+
if (eq === -1) return { key: pair, value: null };
|
|
1697
|
+
return { key: pair.slice(0, eq), value: pair.slice(eq + 1) };
|
|
1698
|
+
});
|
|
1699
|
+
}
|
|
1700
|
+
function encodeQuery(pairs) {
|
|
1701
|
+
return pairs.map((p) => p.value === null ? p.key : `${p.key}=${p.value}`).join("&");
|
|
1702
|
+
}
|
|
1703
|
+
function collapseRootIndex(path) {
|
|
1704
|
+
if (path === "/index.html" || path === "/index.php") return "/";
|
|
1705
|
+
return path;
|
|
1706
|
+
}
|
|
1707
|
+
function dropTrailingSlash(path) {
|
|
1708
|
+
if (path.length > 1 && path.endsWith("/")) {
|
|
1709
|
+
return path.replace(/\/+$/, "");
|
|
1710
|
+
}
|
|
1711
|
+
return path;
|
|
1712
|
+
}
|
|
1713
|
+
function normalizeUrlPath(input) {
|
|
1714
|
+
if (input == null) return null;
|
|
1715
|
+
let trimmed = input.trim();
|
|
1716
|
+
if (trimmed === "") return null;
|
|
1717
|
+
trimmed = trimmed.replace(/ /g, " ").replace(/\s+/g, " ").trim();
|
|
1718
|
+
if (trimmed === "" || trimmed === "/") return "/";
|
|
1719
|
+
if (trimmed === "(not set)") return null;
|
|
1720
|
+
trimmed = trimmed.replace(/([a-zA-Z0-9])([).]+)$/, "$1");
|
|
1721
|
+
if (trimmed.startsWith("/)") || trimmed.startsWith("/ ")) {
|
|
1722
|
+
trimmed = "/";
|
|
1723
|
+
}
|
|
1724
|
+
if (trimmed.includes(" ")) {
|
|
1725
|
+
trimmed = trimmed.split(" ")[0];
|
|
1726
|
+
}
|
|
1727
|
+
if (trimmed === "" || trimmed === "/") return "/";
|
|
1728
|
+
let pathPart;
|
|
1729
|
+
let queryPart;
|
|
1730
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
1731
|
+
let url;
|
|
1732
|
+
try {
|
|
1733
|
+
url = new URL(trimmed);
|
|
1734
|
+
} catch {
|
|
1735
|
+
return null;
|
|
1736
|
+
}
|
|
1737
|
+
pathPart = url.pathname || "/";
|
|
1738
|
+
queryPart = url.search.startsWith("?") ? url.search.slice(1) : url.search;
|
|
1739
|
+
} else {
|
|
1740
|
+
let raw = trimmed;
|
|
1741
|
+
const hashIdx = raw.indexOf("#");
|
|
1742
|
+
if (hashIdx !== -1) raw = raw.slice(0, hashIdx);
|
|
1743
|
+
const qIdx = raw.indexOf("?");
|
|
1744
|
+
if (qIdx === -1) {
|
|
1745
|
+
pathPart = raw;
|
|
1746
|
+
queryPart = "";
|
|
1747
|
+
} else {
|
|
1748
|
+
pathPart = raw.slice(0, qIdx);
|
|
1749
|
+
queryPart = raw.slice(qIdx + 1);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
if (pathPart === "") pathPart = "/";
|
|
1753
|
+
pathPart = collapseRootIndex(pathPart);
|
|
1754
|
+
pathPart = dropTrailingSlash(pathPart);
|
|
1755
|
+
const pairs = parseQuery(queryPart).filter((p) => !shouldStrip(p.key));
|
|
1756
|
+
pairs.sort((a, b) => {
|
|
1757
|
+
if (a.key < b.key) return -1;
|
|
1758
|
+
if (a.key > b.key) return 1;
|
|
1759
|
+
return 0;
|
|
1760
|
+
});
|
|
1761
|
+
if (pairs.length === 0) return pathPart;
|
|
1762
|
+
return `${pathPart}?${encodeQuery(pairs)}`;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
// ../contracts/src/citations.ts
|
|
1766
|
+
import { z as z18 } from "zod";
|
|
1767
|
+
var citationCoverageProviderSchema = z18.object({
|
|
1768
|
+
provider: z18.string(),
|
|
1769
|
+
citationState: citationStateSchema,
|
|
1770
|
+
cited: z18.boolean(),
|
|
1771
|
+
mentioned: z18.boolean(),
|
|
1772
|
+
runId: z18.string(),
|
|
1773
|
+
runCreatedAt: z18.string()
|
|
1774
|
+
});
|
|
1775
|
+
var citationCoverageRowSchema = z18.object({
|
|
1776
|
+
keywordId: z18.string(),
|
|
1777
|
+
keyword: z18.string(),
|
|
1778
|
+
providers: z18.array(citationCoverageProviderSchema),
|
|
1779
|
+
citedCount: z18.number().int().nonnegative(),
|
|
1780
|
+
mentionedCount: z18.number().int().nonnegative(),
|
|
1781
|
+
totalProviders: z18.number().int().nonnegative()
|
|
1782
|
+
});
|
|
1783
|
+
var competitorGapRowSchema = z18.object({
|
|
1784
|
+
keywordId: z18.string(),
|
|
1785
|
+
keyword: z18.string(),
|
|
1786
|
+
provider: z18.string(),
|
|
1787
|
+
citingCompetitors: z18.array(z18.string()),
|
|
1788
|
+
runId: z18.string(),
|
|
1789
|
+
runCreatedAt: z18.string()
|
|
1790
|
+
});
|
|
1791
|
+
var citationVisibilitySummarySchema = z18.object({
|
|
1792
|
+
providersConfigured: z18.number().int().nonnegative(),
|
|
1793
|
+
providersCiting: z18.number().int().nonnegative(),
|
|
1794
|
+
providersMentioning: z18.number().int().nonnegative(),
|
|
1795
|
+
totalKeywords: z18.number().int().nonnegative(),
|
|
1796
|
+
// Cross-tab buckets — each tracked keyword with at least one snapshot lands
|
|
1797
|
+
// in exactly one of these. Keywords with zero snapshots are not counted in
|
|
1798
|
+
// any bucket; (sum of buckets) ≤ totalKeywords.
|
|
1799
|
+
keywordsCitedAndMentioned: z18.number().int().nonnegative(),
|
|
1800
|
+
keywordsCitedOnly: z18.number().int().nonnegative(),
|
|
1801
|
+
keywordsMentionedOnly: z18.number().int().nonnegative(),
|
|
1802
|
+
keywordsInvisible: z18.number().int().nonnegative(),
|
|
1803
|
+
latestRunId: z18.string().nullable(),
|
|
1804
|
+
latestRunAt: z18.string().nullable()
|
|
1805
|
+
});
|
|
1806
|
+
var citationVisibilityResponseSchema = z18.object({
|
|
1807
|
+
summary: citationVisibilitySummarySchema,
|
|
1808
|
+
byKeyword: z18.array(citationCoverageRowSchema),
|
|
1809
|
+
competitorGaps: z18.array(competitorGapRowSchema),
|
|
1810
|
+
status: z18.enum(["ready", "no-data"]),
|
|
1811
|
+
reason: z18.enum(["no-runs-yet", "no-keywords"]).optional()
|
|
1812
|
+
});
|
|
1813
|
+
function emptyCitationVisibility(reason) {
|
|
1814
|
+
return {
|
|
1815
|
+
summary: {
|
|
1816
|
+
providersConfigured: 0,
|
|
1817
|
+
providersCiting: 0,
|
|
1818
|
+
providersMentioning: 0,
|
|
1819
|
+
totalKeywords: 0,
|
|
1820
|
+
keywordsCitedAndMentioned: 0,
|
|
1821
|
+
keywordsCitedOnly: 0,
|
|
1822
|
+
keywordsMentionedOnly: 0,
|
|
1823
|
+
keywordsInvisible: 0,
|
|
1824
|
+
latestRunId: null,
|
|
1825
|
+
latestRunAt: null
|
|
1826
|
+
},
|
|
1827
|
+
byKeyword: [],
|
|
1828
|
+
competitorGaps: [],
|
|
1829
|
+
status: "no-data",
|
|
1830
|
+
reason
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
function citationStateToCited(state) {
|
|
1834
|
+
return state === "cited";
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
// ../contracts/src/skills.ts
|
|
1838
|
+
import { z as z19 } from "zod";
|
|
1839
|
+
var codingAgentSchema = z19.enum(["claude", "codex"]);
|
|
1840
|
+
var CodingAgents = codingAgentSchema.enum;
|
|
1841
|
+
var skillsClientSchema = z19.enum(["claude", "codex", "all"]);
|
|
1842
|
+
var SkillsClients = skillsClientSchema.enum;
|
|
1843
|
+
|
|
1844
|
+
export {
|
|
1845
|
+
__export,
|
|
1846
|
+
providerQuotaPolicySchema,
|
|
1847
|
+
ProviderNames,
|
|
1848
|
+
isBrowserProvider,
|
|
1849
|
+
resolveProviderInput,
|
|
1850
|
+
locationContextSchema,
|
|
1851
|
+
notificationEventSchema,
|
|
1852
|
+
notificationCreateRequestSchema,
|
|
1853
|
+
findDuplicateLocationLabels,
|
|
1854
|
+
hasLocationLabel,
|
|
1855
|
+
projectUpsertRequestSchema,
|
|
1856
|
+
keywordBatchRequestSchema,
|
|
1857
|
+
keywordGenerateRequestSchema,
|
|
1858
|
+
competitorBatchRequestSchema,
|
|
1859
|
+
normalizeProjectDomain,
|
|
1860
|
+
registrableDomain,
|
|
1861
|
+
brandLabelFromDomain,
|
|
1862
|
+
effectiveDomains,
|
|
1863
|
+
projectConfigSchema,
|
|
1864
|
+
AppError,
|
|
1865
|
+
notFound,
|
|
1866
|
+
validationError,
|
|
1867
|
+
authRequired,
|
|
1868
|
+
authInvalid,
|
|
1869
|
+
providerError,
|
|
1870
|
+
runInProgress,
|
|
1871
|
+
runNotCancellable,
|
|
1872
|
+
unsupportedKind,
|
|
1873
|
+
notImplemented,
|
|
1874
|
+
deliveryFailed,
|
|
1875
|
+
agentBusy,
|
|
1876
|
+
missingDependency,
|
|
1877
|
+
internalError,
|
|
1878
|
+
wordpressEnvSchema,
|
|
1879
|
+
AgentProviderIds,
|
|
1880
|
+
AGENT_PROVIDER_IDS,
|
|
1881
|
+
isAgentProviderId,
|
|
1882
|
+
RunStatuses,
|
|
1883
|
+
RunKinds,
|
|
1884
|
+
RunTriggers,
|
|
1885
|
+
CitationStates,
|
|
1886
|
+
runTriggerRequestSchema,
|
|
1887
|
+
parseRunError,
|
|
1888
|
+
buildRunErrorFromMessages,
|
|
1889
|
+
serializeRunError,
|
|
1890
|
+
formatRunErrorOneLine,
|
|
1891
|
+
snapshotRequestSchema,
|
|
1892
|
+
scheduleUpsertRequestSchema,
|
|
1893
|
+
parseWindow,
|
|
1894
|
+
windowCutoff,
|
|
1895
|
+
categorizeSource,
|
|
1896
|
+
categoryLabel,
|
|
1897
|
+
extractAnswerMentions,
|
|
1898
|
+
determineAnswerMentioned,
|
|
1899
|
+
visibilityStateFromAnswerMentioned,
|
|
1900
|
+
brandKeyFromText,
|
|
1901
|
+
MemorySources,
|
|
1902
|
+
AGENT_MEMORY_VALUE_MAX_BYTES,
|
|
1903
|
+
AGENT_MEMORY_KEY_MAX_LENGTH,
|
|
1904
|
+
agentMemoryUpsertRequestSchema,
|
|
1905
|
+
agentMemoryDeleteRequestSchema,
|
|
1906
|
+
CcReleaseSyncStatuses,
|
|
1907
|
+
ContentActions,
|
|
1908
|
+
CheckStatuses,
|
|
1909
|
+
CheckScopes,
|
|
1910
|
+
CheckCategories,
|
|
1911
|
+
summarizeCheckResults,
|
|
1912
|
+
normalizeUrlPath,
|
|
1913
|
+
emptyCitationVisibility,
|
|
1914
|
+
citationStateToCited,
|
|
1915
|
+
CodingAgents,
|
|
1916
|
+
skillsClientSchema,
|
|
1917
|
+
SkillsClients
|
|
1918
|
+
};
|