@gscdump/contracts 0.26.10 → 0.27.1

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.
@@ -0,0 +1,1554 @@
1
+ import { z } from "zod";
2
+ const GSCDUMP_ONBOARDING_CONTRACT_VERSION = "2026-05-11";
3
+ const GSCDUMP_REQUIRED_ANALYTICS_SCOPE = "https://www.googleapis.com/auth/webmasters.readonly";
4
+ const GSCDUMP_WRITE_ANALYTICS_SCOPE = "https://www.googleapis.com/auth/webmasters";
5
+ const GSCDUMP_OPTIONAL_INDEXING_SCOPE = "https://www.googleapis.com/auth/indexing";
6
+ const accountStatuses = [
7
+ "disconnected",
8
+ "oauth_received",
9
+ "scope_missing",
10
+ "refresh_missing",
11
+ "db_provisioning",
12
+ "ready",
13
+ "reauth_required"
14
+ ];
15
+ const accountNextActions = [
16
+ "connect_google",
17
+ "reconnect_google",
18
+ "wait_for_provisioning",
19
+ "none"
20
+ ];
21
+ const propertyStatuses = [
22
+ "no_local_site",
23
+ "no_gsc_property",
24
+ "unverified_property",
25
+ "verified_candidate",
26
+ "registered",
27
+ "linked"
28
+ ];
29
+ const propertyNextActions = [
30
+ "create_site",
31
+ "verify_gsc_property",
32
+ "choose_property",
33
+ "register_site",
34
+ "none"
35
+ ];
36
+ const analyticsStatuses = [
37
+ "not_registered",
38
+ "queued",
39
+ "preparing",
40
+ "syncing",
41
+ "queryable_live",
42
+ "queryable_partial",
43
+ "ready",
44
+ "failed"
45
+ ];
46
+ const analyticsNextActions = [
47
+ "wait_for_sync",
48
+ "retry_sync",
49
+ "none"
50
+ ];
51
+ const querySourceModes = [
52
+ "none",
53
+ "live",
54
+ "d1",
55
+ "r2",
56
+ "mixed"
57
+ ];
58
+ const sitemapStatuses = [
59
+ "unknown",
60
+ "discovering",
61
+ "none_found",
62
+ "auto_submitted",
63
+ "syncing",
64
+ "ready",
65
+ "failed"
66
+ ];
67
+ const sitemapNextActions = [
68
+ "submit_sitemap",
69
+ "wait_for_sitemaps",
70
+ "retry_sitemaps",
71
+ "none"
72
+ ];
73
+ const indexingStatuses = [
74
+ "not_requested",
75
+ "missing_scope",
76
+ "insufficient_permission",
77
+ "waiting_for_sitemaps",
78
+ "discovering",
79
+ "checking",
80
+ "ready",
81
+ "budget_exhausted",
82
+ "no_urls",
83
+ "failed"
84
+ ];
85
+ const indexingNextActions = [
86
+ "reconnect_google",
87
+ "fix_gsc_permission",
88
+ "wait_for_sitemaps",
89
+ "wait_for_indexing",
90
+ "retry_indexing",
91
+ "none"
92
+ ];
93
+ const lifecycleWebhookEvents = [
94
+ "user.lifecycle.changed",
95
+ "site.lifecycle.changed",
96
+ "site.analytics.ready",
97
+ "site.indexing.ready",
98
+ "site.auth.failed",
99
+ "job.failed"
100
+ ];
101
+ const lifecycleErrorCodes = [
102
+ "missing_refresh_token",
103
+ "missing_analytics_scope",
104
+ "missing_indexing_scope",
105
+ "token_refresh_failed",
106
+ "permission_lost",
107
+ "insufficient_gsc_permission",
108
+ "gsc_property_not_found",
109
+ "gsc_property_unverified",
110
+ "user_database_not_provisioned",
111
+ "sync_failed",
112
+ "sitemap_sync_failed",
113
+ "indexing_failed"
114
+ ];
115
+ function parseGrantedScopes(scopes) {
116
+ return (scopes ?? "").split(/\s+/).map((s) => s.trim()).filter(Boolean);
117
+ }
118
+ function hasRequiredAnalyticsScope(scopes) {
119
+ const granted = Array.isArray(scopes) ? scopes : parseGrantedScopes(scopes);
120
+ return granted.includes("https://www.googleapis.com/auth/webmasters.readonly") || granted.includes("https://www.googleapis.com/auth/webmasters");
121
+ }
122
+ function hasOptionalIndexingScope(scopes) {
123
+ return (Array.isArray(scopes) ? scopes : parseGrantedScopes(scopes)).includes(GSCDUMP_OPTIONAL_INDEXING_SCOPE);
124
+ }
125
+ const partnerRoutes = {
126
+ partner: {
127
+ users: {
128
+ byId: (userId) => `/partner/users/${encodeURIComponent(userId)}`,
129
+ lifecycle: (userId) => `/partner/users/${encodeURIComponent(userId)}/lifecycle`,
130
+ siteTeam: (userId, siteId) => `/partner/users/${encodeURIComponent(userId)}/sites/${encodeURIComponent(siteId)}`
131
+ },
132
+ sites: {
133
+ register: "/partner/sites/register",
134
+ bulkRegister: "/partner/sites/bulk-register"
135
+ }
136
+ },
137
+ users: {
138
+ register: "/users/register",
139
+ tokens: (userId) => `/users/${encodeURIComponent(userId)}/tokens`,
140
+ status: (userId) => `/users/${encodeURIComponent(userId)}/status`,
141
+ lifecycle: (userId) => `/users/${encodeURIComponent(userId)}/lifecycle`,
142
+ sites: (userId) => `/users/${encodeURIComponent(userId)}/sites`,
143
+ availableSites: (userId) => `/users/${encodeURIComponent(userId)}/available-sites`,
144
+ siteTeam: (userId, siteId) => `/partner/users/${encodeURIComponent(userId)}/sites/${encodeURIComponent(siteId)}`
145
+ },
146
+ sites: {
147
+ register: "/sites/register",
148
+ bulkRegister: "/sites/bulk-register",
149
+ byId: (siteId) => `/sites/${encodeURIComponent(siteId)}`,
150
+ analysisSources: (siteId) => `/sites/${encodeURIComponent(siteId)}/analysis-sources`,
151
+ syncStatus: (siteId) => `/sites/${encodeURIComponent(siteId)}/sync-status`,
152
+ data: (siteId) => `/sites/${encodeURIComponent(siteId)}/data`,
153
+ dataDetail: (siteId) => `/sites/${encodeURIComponent(siteId)}/data/detail`,
154
+ analysis: (siteId) => `/sites/${encodeURIComponent(siteId)}/analysis`,
155
+ sitemaps: (siteId) => `/sites/${encodeURIComponent(siteId)}/sitemaps`,
156
+ sitemapChanges: (siteId) => `/sites/${encodeURIComponent(siteId)}/sitemaps/changes`,
157
+ indexing: (siteId) => `/sites/${encodeURIComponent(siteId)}/indexing`,
158
+ indexingUrls: (siteId) => `/sites/${encodeURIComponent(siteId)}/indexing/urls`,
159
+ indexingDiagnostics: (siteId) => `/sites/${encodeURIComponent(siteId)}/indexing/diagnostics`,
160
+ indexingInspect: (siteId) => `/sites/${encodeURIComponent(siteId)}/indexing/inspect`,
161
+ recoverPermission: (siteId) => `/sites/${encodeURIComponent(siteId)}/recover-permission`,
162
+ canonicalMismatches: (siteId) => `/sites/${encodeURIComponent(siteId)}/canonical-mismatches`,
163
+ topAssociation: (siteId) => `/sites/${encodeURIComponent(siteId)}/data/top-association`,
164
+ keywordSparklines: (siteId) => `/sites/${encodeURIComponent(siteId)}/data/keyword-sparklines`,
165
+ queryTrend: (siteId) => `/sites/${encodeURIComponent(siteId)}/data/query-trend`,
166
+ pageTrend: (siteId) => `/sites/${encodeURIComponent(siteId)}/data/page-trend`,
167
+ contentVelocity: (siteId) => `/sites/${encodeURIComponent(siteId)}/content-velocity`,
168
+ ctrCurve: (siteId) => `/sites/${encodeURIComponent(siteId)}/ctr-curve`,
169
+ darkTraffic: (siteId) => `/sites/${encodeURIComponent(siteId)}/dark-traffic`,
170
+ deviceGap: (siteId) => `/sites/${encodeURIComponent(siteId)}/device-gap`,
171
+ indexPercent: (siteId) => `/sites/${encodeURIComponent(siteId)}/index-percent`,
172
+ keywordBreadth: (siteId) => `/sites/${encodeURIComponent(siteId)}/keyword-breadth`,
173
+ positionDistribution: (siteId) => `/sites/${encodeURIComponent(siteId)}/position-distribution`
174
+ },
175
+ settings: { user: "/user/settings" },
176
+ teams: {
177
+ create: "/partner/teams",
178
+ byId: (teamId) => `/partner/teams/${encodeURIComponent(teamId)}`,
179
+ members: (teamId) => `/partner/teams/${encodeURIComponent(teamId)}/members`,
180
+ member: (teamId, userId) => `/partner/teams/${encodeURIComponent(teamId)}/members/${encodeURIComponent(userId)}`
181
+ },
182
+ realtime: {
183
+ partner: "/ws/partner",
184
+ user: "/ws/user"
185
+ }
186
+ };
187
+ const analyticsRoutes = {
188
+ whoami: "/api/__gsc/whoami",
189
+ sites: "/api/__gsc/sites",
190
+ site: {
191
+ sourceInfo: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/source-info`,
192
+ analysisSources: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/analysis-sources`,
193
+ analyze: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/analyze`,
194
+ rows: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/rows`,
195
+ rollup: (siteId, rollupId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/rollup/${encodeURIComponent(rollupId)}`,
196
+ backfill: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/backfill`,
197
+ sitemaps: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/sitemaps`,
198
+ sitemapHistory: (siteId, hash) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/sitemaps/${encodeURIComponent(hash)}`,
199
+ sitemapChanges: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/sitemaps/changes`,
200
+ inspections: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/inspections`,
201
+ inspectionHistory: (siteId, hash) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/inspections/${encodeURIComponent(hash)}`,
202
+ indexingUrls: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/indexing/urls`,
203
+ indexingDiagnostics: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/indexing/diagnostics`,
204
+ indexingInspect: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/indexing/inspect`,
205
+ countries: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/countries`,
206
+ searchAppearance: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/search-appearance`,
207
+ topAssociation: (siteId) => `/api/__gsc/sites/${encodeURIComponent(siteId)}/data/top-association`
208
+ },
209
+ syncProgress: "/api/sync-progress"
210
+ };
211
+ const WEBHOOK_CONTRACT_VERSION = "2026-05-11";
212
+ const WEBHOOK_SIGNATURE_HEADER = "X-GSCDump-Signature";
213
+ const WEBHOOK_EVENT_HEADER = "X-GSCDump-Event";
214
+ const WEBHOOK_DELIVERY_HEADER = "X-GSCDump-Delivery";
215
+ const WEBHOOK_CONTRACT_VERSION_HEADER = "X-GSCDump-Contract-Version";
216
+ const WEBHOOK_TIMESTAMP_HEADER = "X-GSCDump-Timestamp";
217
+ const CANONICAL_WEBHOOK_EVENTS = [
218
+ "user.lifecycle.changed",
219
+ "site.lifecycle.changed",
220
+ "site.analytics.ready",
221
+ "site.indexing.ready",
222
+ "site.auth.failed",
223
+ "job.failed"
224
+ ];
225
+ const VALID_WEBHOOK_EVENTS = CANONICAL_WEBHOOK_EVENTS;
226
+ const unknownRecord = z.record(z.string(), z.unknown());
227
+ const searchTypeSchema = z.enum([
228
+ "web",
229
+ "image",
230
+ "video",
231
+ "news",
232
+ "discover",
233
+ "googleNews"
234
+ ]);
235
+ const builderStateSchema = z.object({ searchType: searchTypeSchema.optional() }).loose();
236
+ const gscComparisonFilterSchema = z.enum([
237
+ "new",
238
+ "lost",
239
+ "improving",
240
+ "declining"
241
+ ]);
242
+ const gscApiRangeSchema = z.object({
243
+ start: z.string(),
244
+ end: z.string()
245
+ });
246
+ const countryRowSchema = z.object({
247
+ country: z.string(),
248
+ clicks: z.number(),
249
+ impressions: z.number(),
250
+ sum_position: z.number()
251
+ }).loose();
252
+ const countriesResponseSchema = z.object({
253
+ rows: z.array(countryRowSchema),
254
+ range: gscApiRangeSchema,
255
+ generatedAt: z.string(),
256
+ source: z.enum(["gsc-api", "engine"])
257
+ }).loose();
258
+ const searchAppearanceRowSchema = z.object({
259
+ searchAppearance: z.string(),
260
+ clicks: z.number(),
261
+ impressions: z.number(),
262
+ sum_position: z.number()
263
+ }).loose();
264
+ const searchAppearanceResponseSchema = z.object({
265
+ rows: z.array(searchAppearanceRowSchema),
266
+ range: gscApiRangeSchema,
267
+ generatedAt: z.string(),
268
+ source: z.enum(["gsc-api", "engine"])
269
+ }).loose();
270
+ const siteListItemSchema = z.object({
271
+ id: z.string(),
272
+ label: z.string(),
273
+ hostname: z.string(),
274
+ propertyType: z.enum(["domain", "url-prefix"]),
275
+ readBackend: z.enum(["d1", "r2"]).optional()
276
+ }).loose();
277
+ const sitemapHistoryRecordSchema = z.object({
278
+ path: z.string(),
279
+ capturedAt: z.string()
280
+ }).loose();
281
+ const sitemapHistoryResponseSchema = z.object({
282
+ path: z.string().nullable(),
283
+ snapshots: z.array(sitemapHistoryRecordSchema)
284
+ }).loose();
285
+ const scheduleStateSchema = z.object({
286
+ nextAt: z.number(),
287
+ consecutiveUnchanged: z.number(),
288
+ policyVersion: z.number()
289
+ }).loose();
290
+ const inspectionRecordRawSchema = z.object({
291
+ schedule: scheduleStateSchema.optional(),
292
+ nextCheckAfter: z.number().optional(),
293
+ priority: z.enum([
294
+ "high",
295
+ "medium",
296
+ "low"
297
+ ]).optional(),
298
+ sitemaps: z.string().nullable().optional(),
299
+ referringUrls: z.string().nullable().optional(),
300
+ crawlingUserAgent: z.string().nullable().optional(),
301
+ mobileIssues: z.string().nullable().optional(),
302
+ richResultsItems: z.string().nullable().optional(),
303
+ inspectionResultLink: z.string().nullable().optional()
304
+ }).loose();
305
+ const inspectionHistoryRecordSchema = z.object({
306
+ url: z.string(),
307
+ inspectedAt: z.string(),
308
+ indexStatus: z.string().optional(),
309
+ lastCrawlTime: z.string().optional(),
310
+ googleCanonical: z.string().optional(),
311
+ userCanonical: z.string().optional(),
312
+ coverageState: z.string().optional(),
313
+ robotsTxtState: z.string().optional(),
314
+ indexingState: z.string().optional(),
315
+ pageFetchState: z.string().optional(),
316
+ mobileUsabilityVerdict: z.string().optional(),
317
+ richResultsVerdict: z.string().optional(),
318
+ raw: inspectionRecordRawSchema.optional()
319
+ }).loose();
320
+ const inspectionHistoryResponseSchema = z.object({
321
+ url: z.string().nullable(),
322
+ records: z.array(inspectionHistoryRecordSchema)
323
+ }).loose();
324
+ const sitemapIndexSchema = z.object({
325
+ version: z.literal(1),
326
+ records: z.record(z.string(), sitemapHistoryRecordSchema)
327
+ }).loose();
328
+ const inspectionIndexSchema = z.object({
329
+ version: z.literal(1),
330
+ records: z.record(z.string(), inspectionHistoryRecordSchema)
331
+ }).loose();
332
+ const rollupEnvelopeSchema = z.object({
333
+ version: z.literal(1),
334
+ id: z.string(),
335
+ builtAt: z.number(),
336
+ windowDays: z.number().nullable(),
337
+ payload: z.unknown()
338
+ }).loose();
339
+ const gscRowQueryMetaSchema = z.object({
340
+ sourceName: z.string(),
341
+ sourceKind: z.enum(["row", "sql"]),
342
+ queryMs: z.number()
343
+ }).loose();
344
+ const gscRowQueryResponseSchema = z.object({
345
+ rows: z.array(unknownRecord),
346
+ meta: gscRowQueryMetaSchema
347
+ }).loose();
348
+ const indexingUrlStatusSchema = z.enum([
349
+ "indexed",
350
+ "not_indexed",
351
+ "pending"
352
+ ]);
353
+ const indexingUrlRowSchema = z.object({
354
+ url: z.string(),
355
+ verdict: z.string().nullable(),
356
+ coverageState: z.string().nullable(),
357
+ indexingState: z.string().nullable(),
358
+ robotsTxtState: z.string().nullable(),
359
+ pageFetchState: z.string().nullable(),
360
+ lastCrawlTime: z.string().nullable(),
361
+ crawlingUserAgent: z.string().nullable(),
362
+ userCanonical: z.string().nullable(),
363
+ googleCanonical: z.string().nullable(),
364
+ sitemaps: z.array(z.string()).nullable(),
365
+ referringUrls: z.array(z.string()).nullable(),
366
+ mobileVerdict: z.string().nullable(),
367
+ mobileIssues: z.array(z.unknown()).nullable(),
368
+ richResultsVerdict: z.string().nullable(),
369
+ richResultsItems: z.array(z.unknown()).nullable(),
370
+ inspectionResultLink: z.string().nullable(),
371
+ firstCheckedAt: z.string(),
372
+ lastCheckedAt: z.string(),
373
+ checkCount: z.number()
374
+ }).loose();
375
+ const indexingUrlsResponseSchema = z.object({
376
+ urls: z.array(indexingUrlRowSchema),
377
+ pagination: z.object({
378
+ total: z.number(),
379
+ limit: z.number(),
380
+ offset: z.number(),
381
+ hasMore: z.boolean()
382
+ }).loose(),
383
+ meta: z.object({
384
+ siteUrl: z.string(),
385
+ gscPropertyUrl: z.string().optional(),
386
+ status: z.string(),
387
+ issue: z.string().nullable()
388
+ }).loose()
389
+ }).loose();
390
+ const indexingIssueSchema = z.object({
391
+ type: z.string(),
392
+ label: z.string(),
393
+ severity: z.enum([
394
+ "error",
395
+ "warning",
396
+ "info"
397
+ ]),
398
+ count: z.number()
399
+ }).loose();
400
+ const indexingDiagnosticsSchema = z.object({
401
+ summary: z.object({
402
+ totalUrls: z.number(),
403
+ indexed: z.number(),
404
+ indexedPercent: z.number()
405
+ }).loose(),
406
+ issues: z.array(indexingIssueSchema),
407
+ meta: z.object({ siteUrl: z.string() }).loose()
408
+ }).loose();
409
+ const indexingInspectRequestSchema = z.object({ urls: z.array(z.string().url()).min(1).max(10) });
410
+ const indexingInspectResultSchema = z.object({
411
+ url: z.string(),
412
+ verdict: z.string().nullable(),
413
+ coverageState: z.string().nullable(),
414
+ indexingState: z.string().nullable(),
415
+ robotsTxtState: z.string().nullable(),
416
+ pageFetchState: z.string().nullable(),
417
+ lastCrawlTime: z.string().nullable(),
418
+ crawlingUserAgent: z.string().nullable(),
419
+ userCanonical: z.string().nullable(),
420
+ googleCanonical: z.string().nullable(),
421
+ sitemaps: z.string().nullable(),
422
+ referringUrls: z.string().nullable(),
423
+ mobileVerdict: z.string().nullable(),
424
+ mobileIssues: z.string().nullable(),
425
+ richResultsVerdict: z.string().nullable(),
426
+ richResultsItems: z.string().nullable(),
427
+ inspectionResultLink: z.string().nullable()
428
+ }).loose();
429
+ const indexingInspectResponseSchema = z.object({
430
+ siteId: z.string(),
431
+ rateLimit: z.object({
432
+ reserved: z.number(),
433
+ remaining: z.number(),
434
+ limit: z.number()
435
+ }).loose(),
436
+ results: z.array(indexingInspectResultSchema),
437
+ errors: z.array(z.object({
438
+ url: z.string(),
439
+ error: z.string()
440
+ }).loose()),
441
+ skipped: z.array(z.object({
442
+ url: z.string(),
443
+ reason: z.string()
444
+ }).loose())
445
+ }).loose();
446
+ const indexingInspectRateLimitedSchema = z.object({
447
+ error: z.literal("rate_limited"),
448
+ message: z.string(),
449
+ rateLimit: z.object({
450
+ reserved: z.literal(0),
451
+ remaining: z.literal(0),
452
+ limit: z.number()
453
+ }).loose(),
454
+ retryAfterSeconds: z.number()
455
+ }).loose();
456
+ const indexingInspectAnyResponseSchema = z.union([indexingInspectResponseSchema, indexingInspectRateLimitedSchema]);
457
+ const sitemapChangesResponseSchema = z.object({
458
+ added: z.array(z.object({
459
+ url: z.string(),
460
+ sitemap: z.string(),
461
+ firstSeenAt: z.number()
462
+ }).loose()),
463
+ removed: z.array(z.object({
464
+ url: z.string(),
465
+ sitemap: z.string(),
466
+ removedAt: z.number()
467
+ }).loose()),
468
+ summary: z.object({
469
+ totalAdded: z.number(),
470
+ totalRemoved: z.number(),
471
+ period: z.object({ days: z.number() })
472
+ }).loose()
473
+ }).loose();
474
+ const sourceInfoResponseSchema = z.object({
475
+ name: z.string(),
476
+ kind: z.enum(["row", "sql"]),
477
+ capabilities: z.object({ attachedTables: z.boolean().optional() }).loose(),
478
+ supportedAnalyzerIds: z.array(z.string()),
479
+ browserAttachEligible: z.boolean(),
480
+ identityAttrs: unknownRecord.nullable().optional()
481
+ }).loose();
482
+ const whoamiResponseSchema = z.object({
483
+ userId: z.string().nullable().optional(),
484
+ plan: z.string().nullable().optional(),
485
+ attrs: unknownRecord.optional()
486
+ }).loose();
487
+ const backfillRangeSchema = z.object({
488
+ startDate: z.string(),
489
+ endDate: z.string()
490
+ }).loose();
491
+ const backfillResponseSchema = z.object({
492
+ ok: z.boolean().optional(),
493
+ queued: z.boolean().optional()
494
+ }).loose();
495
+ const gscdumpTotalsSchema = z.object({
496
+ clicks: z.number(),
497
+ impressions: z.number(),
498
+ ctr: z.number(),
499
+ position: z.number()
500
+ });
501
+ const gscdumpMetaSchema = z.object({
502
+ siteUrl: z.string(),
503
+ syncStatus: z.string(),
504
+ newestDateSynced: z.string().nullable(),
505
+ oldestDateSynced: z.string().nullable(),
506
+ dataDelay: z.string(),
507
+ dataEndDate: z.string().nullable().optional(),
508
+ warnings: z.array(z.string()).optional(),
509
+ enrichment: z.object({
510
+ lastEnriched: z.number(),
511
+ isDue: z.boolean()
512
+ }).optional(),
513
+ backfill: z.object({
514
+ percent: z.number(),
515
+ daysRemaining: z.number().optional()
516
+ }).optional()
517
+ }).loose();
518
+ const gscdumpDataRowSchema = z.object({
519
+ clicks: z.number(),
520
+ impressions: z.number(),
521
+ ctr: z.number(),
522
+ position: z.number()
523
+ }).loose();
524
+ const gscdumpDataResponseSchema = z.object({
525
+ rows: z.array(gscdumpDataRowSchema),
526
+ totalCount: z.number(),
527
+ totals: gscdumpTotalsSchema,
528
+ meta: gscdumpMetaSchema
529
+ });
530
+ const gscdumpDataDetailResponseSchema = z.object({
531
+ daily: z.array(z.object({
532
+ date: z.string(),
533
+ clicks: z.number(),
534
+ impressions: z.number(),
535
+ ctr: z.number(),
536
+ position: z.number()
537
+ }).loose()),
538
+ totals: gscdumpTotalsSchema,
539
+ previousTotals: gscdumpTotalsSchema.optional(),
540
+ meta: gscdumpMetaSchema
541
+ });
542
+ const registerPartnerUserSchema = z.object({
543
+ userGoogleId: z.string().min(1),
544
+ userEmail: z.email(),
545
+ userName: z.string().optional(),
546
+ accessToken: z.string().min(1),
547
+ refreshToken: z.string().min(1),
548
+ tokenExpiresAt: z.number().optional()
549
+ });
550
+ const updatePartnerUserTokensSchema = z.object({
551
+ accessToken: z.string().min(1),
552
+ refreshToken: z.string().min(1),
553
+ tokenExpiresAt: z.number().optional()
554
+ });
555
+ const gscdumpUserRegistrationSchema = z.object({
556
+ userId: z.string(),
557
+ apiKey: z.string().optional(),
558
+ isNew: z.boolean().optional(),
559
+ status: z.enum(["provisioning", "ready"]).optional()
560
+ }).loose();
561
+ const gscdumpUserStatusSchema = z.object({
562
+ userId: z.string(),
563
+ status: z.enum([
564
+ "provisioning",
565
+ "ready",
566
+ "reauth_required"
567
+ ]),
568
+ databaseReady: z.boolean().optional(),
569
+ needsReauth: z.boolean().optional(),
570
+ reauthRequired: z.boolean().optional(),
571
+ reauthReason: z.string().nullable().optional(),
572
+ authFailureCount: z.number().optional(),
573
+ nextAction: z.enum(["reconnect_google", "none"]).optional(),
574
+ grantedScopes: z.string().nullable().optional()
575
+ }).loose();
576
+ const lifecycleProgressSchema = z.object({
577
+ completed: z.number(),
578
+ failed: z.number(),
579
+ total: z.number(),
580
+ percent: z.number()
581
+ });
582
+ const lifecycleErrorSchema = z.object({
583
+ code: z.enum(lifecycleErrorCodes),
584
+ message: z.string(),
585
+ retryable: z.boolean()
586
+ });
587
+ const partnerLifecycleAccountSchema = z.object({
588
+ status: z.enum(accountStatuses),
589
+ grantedScopes: z.array(z.string()),
590
+ missingScopes: z.array(z.string()),
591
+ nextAction: z.enum(accountNextActions)
592
+ }).loose();
593
+ const partnerLifecycleSiteSchema = z.object({
594
+ siteId: z.string(),
595
+ externalSiteId: z.string().nullable(),
596
+ requestedUrl: z.string(),
597
+ gscPropertyUrl: z.string().nullable(),
598
+ permissionLevel: z.string().nullable(),
599
+ property: z.object({
600
+ status: z.enum(propertyStatuses),
601
+ nextAction: z.enum(propertyNextActions)
602
+ }).loose(),
603
+ analytics: z.object({
604
+ status: z.enum(analyticsStatuses),
605
+ progress: lifecycleProgressSchema,
606
+ queryable: z.boolean(),
607
+ sourceMode: z.enum(querySourceModes),
608
+ syncedRange: z.object({
609
+ oldest: z.string().nullable(),
610
+ newest: z.string().nullable()
611
+ }).loose(),
612
+ nextAction: z.enum(analyticsNextActions)
613
+ }).loose(),
614
+ sitemaps: z.object({
615
+ status: z.enum(sitemapStatuses),
616
+ discoveredCount: z.number(),
617
+ nextAction: z.enum(sitemapNextActions)
618
+ }).loose(),
619
+ indexing: z.object({
620
+ status: z.enum(indexingStatuses),
621
+ eligible: z.boolean(),
622
+ reason: z.string().nullable(),
623
+ progress: lifecycleProgressSchema,
624
+ nextAction: z.enum(indexingNextActions)
625
+ }).loose(),
626
+ latestError: lifecycleErrorSchema.nullable(),
627
+ lifecycleRevision: z.number(),
628
+ updatedAt: z.string()
629
+ }).loose();
630
+ const partnerLifecycleResponseSchema = z.object({
631
+ contractVersion: z.literal(GSCDUMP_ONBOARDING_CONTRACT_VERSION),
632
+ userId: z.string(),
633
+ partnerId: z.string().nullable(),
634
+ account: partnerLifecycleAccountSchema,
635
+ sites: z.array(partnerLifecycleSiteSchema)
636
+ }).loose();
637
+ const gscdumpAvailableSiteSchema = z.object({
638
+ siteUrl: z.string(),
639
+ permissionLevel: z.string(),
640
+ registered: z.boolean(),
641
+ siteId: z.string().optional(),
642
+ syncStatus: z.enum([
643
+ "pending",
644
+ "syncing",
645
+ "synced",
646
+ "error"
647
+ ]).nullable().optional(),
648
+ syncProgress: z.object({
649
+ completed: z.number(),
650
+ failed: z.number().optional(),
651
+ total: z.number(),
652
+ percent: z.number()
653
+ }).optional(),
654
+ lastSyncAt: z.number().nullable().optional(),
655
+ newestDateSynced: z.string().nullable().optional(),
656
+ oldestDateSynced: z.string().nullable().optional()
657
+ }).loose();
658
+ const gscdumpUserSiteSchema = z.object({
659
+ siteId: z.string(),
660
+ siteUrl: z.string(),
661
+ syncStatus: z.enum([
662
+ "idle",
663
+ "pending",
664
+ "syncing",
665
+ "synced",
666
+ "error"
667
+ ]),
668
+ lastSyncAt: z.number().nullable(),
669
+ newestDateSynced: z.string().nullable(),
670
+ oldestDateSynced: z.string().nullable()
671
+ }).loose();
672
+ const gscdumpSiteRegistrationSchema = z.object({
673
+ siteId: z.string(),
674
+ status: z.enum([
675
+ "idle",
676
+ "pending",
677
+ "syncing",
678
+ "synced",
679
+ "error"
680
+ ]),
681
+ message: z.string().optional(),
682
+ existing: z.boolean().optional(),
683
+ indexingEligible: z.boolean().optional(),
684
+ indexingIneligibleReason: z.enum(["missing_indexing_scope", "insufficient_gsc_permission"]).optional(),
685
+ indexingPermissionLevel: z.string().nullable().optional(),
686
+ grantedScopes: z.array(z.string()).optional()
687
+ }).loose();
688
+ const registerPartnerSiteSchema = z.object({
689
+ userId: z.string(),
690
+ siteUrl: z.string().min(1),
691
+ requestedUrl: z.string().optional(),
692
+ gscPropertyUrl: z.string().optional(),
693
+ externalSiteId: z.string().optional(),
694
+ externalSiteUrl: z.string().optional(),
695
+ webhookUrl: z.url().optional(),
696
+ webhookEvents: z.array(z.enum(CANONICAL_WEBHOOK_EVENTS)).optional(),
697
+ teamId: z.string().optional(),
698
+ enabledSearchTypes: z.array(searchTypeSchema).optional()
699
+ });
700
+ const bulkRegisterPartnerSitesSchema = z.object({
701
+ userId: z.string().optional(),
702
+ siteUrls: z.array(z.string().min(1)).optional(),
703
+ sites: z.array(z.object({
704
+ siteUrl: z.string().optional(),
705
+ requestedUrl: z.string().optional(),
706
+ gscPropertyUrl: z.string().optional(),
707
+ externalSiteId: z.string().optional(),
708
+ externalSiteUrl: z.string().optional(),
709
+ webhookUrl: z.url().optional(),
710
+ webhookEvents: z.array(z.enum(CANONICAL_WEBHOOK_EVENTS)).optional()
711
+ }).loose()).optional()
712
+ }).refine((value) => (value.siteUrls?.length ?? 0) > 0 || (value.sites?.length ?? 0) > 0, { message: "siteUrls or sites is required" });
713
+ const bulkRegisterPartnerSitesResponseSchema = z.object({
714
+ results: z.array(z.object({
715
+ siteUrl: z.string(),
716
+ siteId: z.string().optional(),
717
+ status: z.enum([
718
+ "registered",
719
+ "already_exists",
720
+ "not_found",
721
+ "error"
722
+ ]),
723
+ error: z.string().optional(),
724
+ site: unknownRecord.nullable().optional(),
725
+ indexingEligible: z.boolean().optional(),
726
+ indexingIneligibleReason: z.enum(["missing_indexing_scope", "insufficient_gsc_permission"]).optional(),
727
+ indexingPermissionLevel: z.string().nullable().optional(),
728
+ grantedScopes: z.array(z.string()).optional()
729
+ }).loose()),
730
+ summary: z.object({
731
+ registered: z.number(),
732
+ alreadyExists: z.number(),
733
+ notFound: z.number(),
734
+ errors: z.number()
735
+ })
736
+ }).loose();
737
+ const dataQueryOptionsSchema = z.object({
738
+ comparison: builderStateSchema.optional(),
739
+ filter: gscComparisonFilterSchema.optional(),
740
+ searchType: searchTypeSchema.optional()
741
+ }).optional();
742
+ const dataDetailOptionsSchema = z.object({
743
+ comparison: builderStateSchema.optional(),
744
+ searchType: searchTypeSchema.optional()
745
+ }).optional();
746
+ const gscdumpAnalysisPresetSchema = z.enum([
747
+ "striking-distance",
748
+ "opportunity",
749
+ "decay",
750
+ "zero-click",
751
+ "non-brand",
752
+ "brand-only",
753
+ "movers-rising",
754
+ "movers-declining"
755
+ ]);
756
+ const gscdumpAnalysisParamsSchema = z.object({
757
+ preset: gscdumpAnalysisPresetSchema,
758
+ startDate: z.string(),
759
+ endDate: z.string(),
760
+ prevStartDate: z.string().optional(),
761
+ prevEndDate: z.string().optional(),
762
+ brandTerms: z.string().optional(),
763
+ limit: z.number().optional(),
764
+ offset: z.number().optional(),
765
+ search: z.string().optional(),
766
+ minImpressions: z.number().optional(),
767
+ minPosition: z.number().optional(),
768
+ maxPosition: z.number().optional(),
769
+ maxCtr: z.number().optional(),
770
+ searchType: searchTypeSchema.optional()
771
+ }).superRefine((value, ctx) => {
772
+ if ((value.preset === "brand-only" || value.preset === "non-brand") && !value.brandTerms?.trim()) ctx.addIssue({
773
+ code: "custom",
774
+ path: ["brandTerms"],
775
+ message: "brandTerms is required for brand/non-brand presets"
776
+ });
777
+ });
778
+ const gscdumpAnalysisResponseSchema = z.object({
779
+ preset: gscdumpAnalysisPresetSchema,
780
+ keywords: z.array(unknownRecord),
781
+ totalCount: z.number(),
782
+ summary: unknownRecord.optional(),
783
+ meta: gscdumpMetaSchema
784
+ }).loose();
785
+ const gscdumpSitemapsResponseSchema = z.object({
786
+ sitemaps: z.array(z.object({
787
+ path: z.string(),
788
+ urlCount: z.number(),
789
+ errors: z.number(),
790
+ warnings: z.number(),
791
+ isIndex: z.boolean().optional(),
792
+ lastSubmitted: z.string().nullable().optional(),
793
+ lastDownloaded: z.string().nullable().optional()
794
+ }).loose()),
795
+ history: z.array(z.object({
796
+ date: z.string(),
797
+ errors: z.number(),
798
+ warnings: z.number(),
799
+ urlCount: z.number(),
800
+ urlDelta: z.number()
801
+ }).loose()),
802
+ perSitemapHistory: z.record(z.string(), z.array(z.object({
803
+ date: z.string(),
804
+ urlCount: z.number(),
805
+ changed: z.boolean(),
806
+ urlDelta: z.number()
807
+ }).loose())),
808
+ meta: z.object({
809
+ siteUrl: z.string(),
810
+ syncStatus: z.string().nullable()
811
+ }).loose()
812
+ }).loose();
813
+ const gscdumpSitemapChangesResponseSchema = z.object({
814
+ added: z.array(z.object({
815
+ url: z.string(),
816
+ sitemap: z.string(),
817
+ firstSeenAt: z.number()
818
+ }).loose()),
819
+ removed: z.array(z.object({
820
+ url: z.string(),
821
+ sitemap: z.string(),
822
+ removedAt: z.number()
823
+ }).loose()),
824
+ summary: z.object({
825
+ totalAdded: z.number(),
826
+ totalRemoved: z.number(),
827
+ period: z.object({ days: z.number() })
828
+ }).optional()
829
+ }).loose();
830
+ const indexingUrlsParamsSchema = z.object({
831
+ limit: z.number().optional(),
832
+ offset: z.number().optional(),
833
+ status: z.enum([
834
+ "indexed",
835
+ "not_indexed",
836
+ "pending"
837
+ ]).optional(),
838
+ issue: z.string().optional(),
839
+ search: z.string().optional()
840
+ }).optional();
841
+ const gscdumpIndexingResponseSchema = z.object({
842
+ trend: z.array(z.object({
843
+ date: z.string(),
844
+ totalUrls: z.number(),
845
+ indexedCount: z.number().nullable(),
846
+ notIndexedCount: z.number().nullable(),
847
+ errorCount: z.number().nullable(),
848
+ indexedPercent: z.number(),
849
+ issues: z.object({
850
+ blockedByRobots: z.number().nullable(),
851
+ noindexDetected: z.number().nullable(),
852
+ soft404: z.number().nullable(),
853
+ redirect: z.number().nullable(),
854
+ notFound: z.number().nullable(),
855
+ serverError: z.number().nullable()
856
+ }).loose(),
857
+ coverage: z.object({
858
+ submittedIndexed: z.number().nullable(),
859
+ crawledNotIndexed: z.number().nullable(),
860
+ discoveredNotCrawled: z.number().nullable()
861
+ }).loose(),
862
+ signals: z.object({
863
+ mobilePass: z.number(),
864
+ mobileFail: z.number(),
865
+ richResultsPass: z.number(),
866
+ richResultsFail: z.number()
867
+ }).loose()
868
+ }).loose()),
869
+ summary: z.object({
870
+ totalUrls: z.number(),
871
+ indexed: z.number(),
872
+ notIndexed: z.number(),
873
+ pending: z.number(),
874
+ indexedPercent: z.number(),
875
+ oldestCheck: z.string().nullable(),
876
+ newestCheck: z.string().nullable(),
877
+ change7d: z.number().nullable(),
878
+ change28d: z.number().nullable(),
879
+ signals: z.object({
880
+ mobilePass: z.number(),
881
+ mobileFail: z.number(),
882
+ mobileUnspecified: z.number(),
883
+ richResultsPass: z.number(),
884
+ richResultsFail: z.number(),
885
+ richResultTypes: z.array(z.object({
886
+ type: z.string(),
887
+ count: z.number()
888
+ }).loose()),
889
+ crawlingMobile: z.number(),
890
+ crawlingDesktop: z.number()
891
+ }).loose()
892
+ }).loose(),
893
+ meta: z.object({
894
+ siteUrl: z.string(),
895
+ syncStatus: z.string().nullable(),
896
+ indexingStatus: z.enum([
897
+ "pending",
898
+ "partial",
899
+ "complete"
900
+ ]),
901
+ indexingProgress: z.number(),
902
+ sitemapTotal: z.number(),
903
+ inspectedCount: z.number(),
904
+ noSitemapsSubmitted: z.boolean(),
905
+ sitemapsPending: z.boolean(),
906
+ rollupBuiltAt: z.number().optional()
907
+ }).loose()
908
+ }).loose();
909
+ const gscdumpIndexingUrlsResponseSchema = z.object({
910
+ urls: z.array(unknownRecord),
911
+ pagination: z.object({
912
+ total: z.number(),
913
+ limit: z.number(),
914
+ offset: z.number(),
915
+ hasMore: z.boolean()
916
+ }),
917
+ meta: z.object({
918
+ siteUrl: z.string(),
919
+ status: z.string(),
920
+ issue: z.string().nullable()
921
+ }).loose()
922
+ }).loose();
923
+ const gscdumpIndexingDiagnosticsResponseSchema = z.object({
924
+ summary: z.object({
925
+ totalUrls: z.number(),
926
+ indexed: z.number(),
927
+ indexedPercent: z.number()
928
+ }).loose(),
929
+ issues: z.array(unknownRecord),
930
+ meta: z.object({ siteUrl: z.string() }).loose()
931
+ }).loose();
932
+ const gscdumpUserSettingsSchema = z.object({ browserAnalyzerEnabled: z.boolean() }).loose();
933
+ const gscdumpPermissionRecoverySchema = z.object({
934
+ success: z.boolean(),
935
+ permissionLevel: z.string().nullable(),
936
+ jobsQueued: z.number(),
937
+ message: z.string()
938
+ }).loose();
939
+ const gscdumpUserMeResponseSchema = z.object({
940
+ id: z.string(),
941
+ name: z.string().optional(),
942
+ email: z.string(),
943
+ picture: z.string().optional(),
944
+ setupComplete: z.boolean(),
945
+ databaseReady: z.boolean(),
946
+ plan: z.string(),
947
+ accountStatus: z.enum(accountStatuses),
948
+ accountNextAction: z.enum(accountNextActions),
949
+ missingScopes: z.array(z.string()),
950
+ browserAnalyzerEnabled: z.boolean(),
951
+ stats: z.object({
952
+ activeSessions: z.number(),
953
+ totalApiCalls: z.number(),
954
+ lastUsed: z.number().nullable()
955
+ }).nullable()
956
+ }).loose();
957
+ const gscdumpHealthResponseSchema = z.object({
958
+ timestamp: z.number(),
959
+ jobs: z.object({
960
+ byStatus: z.record(z.string(), z.number()),
961
+ byQueue: z.record(z.string(), z.number()),
962
+ stuck: z.number(),
963
+ oldCompleted: z.number()
964
+ }).loose(),
965
+ failures: z.object({ lastHour: z.number() }).loose(),
966
+ users: z.object({ needsReauth: z.number() }).loose(),
967
+ throughput: z.object({ completedLastHour: z.number() }).loose(),
968
+ totals: z.object({
969
+ users: z.number(),
970
+ sites: z.number(),
971
+ byPlan: z.record(z.string(), z.number())
972
+ }).loose()
973
+ }).loose();
974
+ const syncProgressPhaseCountsSchema = z.object({
975
+ total: z.number(),
976
+ done: z.number(),
977
+ pending: z.number(),
978
+ stuck: z.number(),
979
+ failed: z.number(),
980
+ percent: z.number(),
981
+ rows: z.number()
982
+ }).loose();
983
+ const gscdumpSyncProgressResponseSchema = z.object({
984
+ summary: z.object({
985
+ total: z.number(),
986
+ inInitial: z.number(),
987
+ inBackfill: z.number(),
988
+ complete: z.number(),
989
+ stalled: z.number(),
990
+ authBlocked: z.number(),
991
+ totalRows: z.number(),
992
+ avgInitialPercent: z.number(),
993
+ avgBackfillPercent: z.number()
994
+ }).loose(),
995
+ sites: z.array(z.object({
996
+ id: z.string(),
997
+ userId: z.number(),
998
+ partnerId: z.number().nullable(),
999
+ siteUrl: z.string(),
1000
+ syncStatus: z.string().nullable(),
1001
+ lastError: z.string().nullable(),
1002
+ createdAt: z.number(),
1003
+ oldestDateSynced: z.string().nullable(),
1004
+ newestDateSynced: z.string().nullable(),
1005
+ oldestDateAvailable: z.string().nullable(),
1006
+ userEmail: z.string().nullable(),
1007
+ partnerName: z.string().nullable(),
1008
+ needsReauth: z.boolean(),
1009
+ authFailureCount: z.number(),
1010
+ currentPhase: z.enum([
1011
+ "initial",
1012
+ "backfill",
1013
+ "complete"
1014
+ ]),
1015
+ isStalled: z.boolean(),
1016
+ stallDays: z.number(),
1017
+ needsContinuation: z.boolean(),
1018
+ missingDays: z.number(),
1019
+ initial: syncProgressPhaseCountsSchema,
1020
+ backfill: syncProgressPhaseCountsSchema.extend({
1021
+ processing: z.number(),
1022
+ daysTarget: z.number(),
1023
+ daysQueued: z.number()
1024
+ }).loose(),
1025
+ tables: z.record(z.string(), z.object({
1026
+ percent: z.number(),
1027
+ rows: z.number()
1028
+ }).loose()),
1029
+ overallPercent: z.number(),
1030
+ totalRows: z.number(),
1031
+ dateStatuses: z.array(z.object({
1032
+ date: z.string(),
1033
+ status: z.enum([
1034
+ "completed",
1035
+ "queued",
1036
+ "processing",
1037
+ "failed"
1038
+ ])
1039
+ }).loose())
1040
+ }).loose()),
1041
+ pagination: z.object({
1042
+ total: z.number(),
1043
+ limit: z.number(),
1044
+ offset: z.number(),
1045
+ hasMore: z.boolean()
1046
+ }).loose(),
1047
+ hasD1Stats: z.boolean()
1048
+ }).loose();
1049
+ const syncJobsQueueCountsSchema = z.object({
1050
+ queued: z.number(),
1051
+ processing: z.number()
1052
+ }).loose();
1053
+ const gscdumpSyncJobsResponseSchema = z.object({
1054
+ jobs: z.array(z.object({
1055
+ id: z.number(),
1056
+ userSiteId: z.string().nullable(),
1057
+ tableName: z.string().optional(),
1058
+ tableTier: z.string().optional(),
1059
+ date: z.string().optional(),
1060
+ priority: z.string().optional(),
1061
+ status: z.enum([
1062
+ "queued",
1063
+ "processing",
1064
+ "completed",
1065
+ "failed",
1066
+ "scheduled"
1067
+ ]),
1068
+ siteUrl: z.string().nullable(),
1069
+ userEmail: z.string().nullable().optional(),
1070
+ rowsFetched: z.number().nullable(),
1071
+ rowsInserted: z.number().nullable(),
1072
+ error: z.string().nullable(),
1073
+ retryCount: z.number(),
1074
+ queuedAt: z.number(),
1075
+ startedAt: z.number().nullable(),
1076
+ completedAt: z.number().nullable()
1077
+ }).loose()),
1078
+ summary: z.object({
1079
+ queued: z.number(),
1080
+ processing: z.number(),
1081
+ completed: z.number(),
1082
+ failed: z.number()
1083
+ }).loose(),
1084
+ queues: z.record(z.string(), syncJobsQueueCountsSchema),
1085
+ tiers: z.object({
1086
+ critical: syncJobsQueueCountsSchema.optional(),
1087
+ standard: syncJobsQueueCountsSchema.optional(),
1088
+ extended: syncJobsQueueCountsSchema.optional()
1089
+ }).loose().optional()
1090
+ }).loose();
1091
+ const gscdumpSiteReportResponseSchema = z.object({
1092
+ status: z.enum(["done", "error"]),
1093
+ cached: z.boolean(),
1094
+ result: z.unknown().optional(),
1095
+ error: z.string().optional()
1096
+ }).loose();
1097
+ const gscdumpTopAssociationParamsSchema = z.object({
1098
+ type: z.enum(["topPage", "topKeyword"]),
1099
+ identifier: z.string().min(1),
1100
+ startDate: z.string(),
1101
+ endDate: z.string()
1102
+ });
1103
+ const gscdumpTopAssociationResponseSchema = z.object({ value: z.string().nullable() }).loose();
1104
+ const gscdumpAnalysisSourcesResponseSchema = z.object({
1105
+ tables: z.record(z.string(), z.array(z.string())),
1106
+ generatedAt: z.string(),
1107
+ manifestVersion: z.string()
1108
+ }).loose();
1109
+ const gscdumpKeywordSparklinesParamsSchema = z.object({
1110
+ keywords: z.array(z.string()).min(1).max(20),
1111
+ startDate: z.string(),
1112
+ endDate: z.string(),
1113
+ searchType: searchTypeSchema.optional()
1114
+ });
1115
+ const gscdumpKeywordSparklinesResponseSchema = z.object({ sparklines: z.record(z.string(), z.array(z.number())) }).loose();
1116
+ const gscdumpQueryTrendParamsSchema = z.object({
1117
+ startDate: z.string(),
1118
+ endDate: z.string(),
1119
+ prevStartDate: z.string().optional(),
1120
+ prevEndDate: z.string().optional(),
1121
+ searchType: searchTypeSchema.optional()
1122
+ });
1123
+ const gscdumpQueryTrendResponseSchema = z.object({
1124
+ daily: z.array(z.object({
1125
+ date: z.string(),
1126
+ queryCount: z.number()
1127
+ }).loose()),
1128
+ total: z.number(),
1129
+ previousTotal: z.number().optional(),
1130
+ meta: z.object({
1131
+ siteUrl: z.string(),
1132
+ syncStatus: z.string().nullable()
1133
+ }).loose()
1134
+ }).loose();
1135
+ const gscdumpPageTrendParamsSchema = z.object({
1136
+ startDate: z.string(),
1137
+ endDate: z.string(),
1138
+ prevStartDate: z.string().optional(),
1139
+ prevEndDate: z.string().optional(),
1140
+ searchType: searchTypeSchema.optional()
1141
+ });
1142
+ const gscdumpPageTrendResponseSchema = z.object({
1143
+ daily: z.array(z.object({
1144
+ date: z.string(),
1145
+ pageCount: z.number()
1146
+ }).loose()),
1147
+ total: z.number(),
1148
+ previousTotal: z.number().optional(),
1149
+ meta: z.object({
1150
+ siteUrl: z.string(),
1151
+ syncStatus: z.string().nullable()
1152
+ }).loose()
1153
+ }).loose();
1154
+ const gscdumpDateRangeParamsSchema = z.object({
1155
+ startDate: z.string(),
1156
+ endDate: z.string()
1157
+ });
1158
+ const gscdumpIndexPercentParamsSchema = z.object({
1159
+ invisibleLimit: z.number().optional(),
1160
+ invisibleOffset: z.number().optional(),
1161
+ orphanLimit: z.number().optional()
1162
+ }).optional();
1163
+ const gscdumpCanonicalMismatchesResponseSchema = z.object({
1164
+ mismatches: z.array(z.object({
1165
+ url: z.string(),
1166
+ userCanonical: z.string(),
1167
+ googleCanonical: z.string(),
1168
+ verdict: z.string().nullable(),
1169
+ coverageState: z.string().nullable(),
1170
+ lastCrawlTime: z.string().nullable(),
1171
+ lastCheckedAt: z.string().nullable()
1172
+ }).loose()),
1173
+ totalCount: z.number(),
1174
+ consolidationTargets: z.array(z.object({
1175
+ google_canonical: z.string(),
1176
+ count: z.number()
1177
+ }).loose()),
1178
+ trend: z.array(z.object({
1179
+ date: z.string(),
1180
+ count: z.number()
1181
+ }).loose()),
1182
+ meta: z.object({
1183
+ siteUrl: z.string(),
1184
+ syncStatus: z.string().nullable()
1185
+ }).loose()
1186
+ }).loose();
1187
+ const gscdumpIndexPercentResponseSchema = z.object({
1188
+ trend: z.array(z.object({
1189
+ date: z.string(),
1190
+ percent: z.number(),
1191
+ total: z.number(),
1192
+ visible: z.number(),
1193
+ added: z.number().nullable(),
1194
+ removed: z.number().nullable()
1195
+ }).loose()),
1196
+ invisibleUrls: z.array(z.object({
1197
+ url: z.string(),
1198
+ firstSeen: z.string().optional(),
1199
+ lastmod: z.string().nullable()
1200
+ }).loose()),
1201
+ invisibleCount: z.number(),
1202
+ orphanPages: z.array(z.object({
1203
+ url: z.string(),
1204
+ impressions: z.number().nullable(),
1205
+ clicks: z.number().nullable()
1206
+ }).loose()),
1207
+ orphanCount: z.number(),
1208
+ sitemaps: z.array(z.object({
1209
+ path: z.string(),
1210
+ urlCount: z.number(),
1211
+ isIndex: z.boolean()
1212
+ }).loose()),
1213
+ summary: z.object({
1214
+ currentPercent: z.number(),
1215
+ totalSitemapUrls: z.number(),
1216
+ visibleUrls: z.number(),
1217
+ change7d: z.number().nullable(),
1218
+ change28d: z.number().nullable(),
1219
+ dataDate: z.string()
1220
+ }).loose(),
1221
+ meta: z.object({
1222
+ siteUrl: z.string(),
1223
+ syncStatus: z.string().nullable(),
1224
+ newestDateSynced: z.string().nullable()
1225
+ }).loose()
1226
+ }).loose();
1227
+ const gscdumpDeletePartnerUserResponseSchema = z.object({
1228
+ ok: z.literal(true),
1229
+ queued: z.literal(true),
1230
+ userId: z.number(),
1231
+ publicId: z.string()
1232
+ }).loose();
1233
+ const gscdumpTeamRoleSchema = z.enum([
1234
+ "admin",
1235
+ "editor",
1236
+ "viewer"
1237
+ ]);
1238
+ const gscdumpTeamRowSchema = z.object({
1239
+ id: z.string(),
1240
+ ownerId: z.number(),
1241
+ name: z.string(),
1242
+ personalTeam: z.boolean(),
1243
+ createdAt: z.number(),
1244
+ updatedAt: z.number()
1245
+ }).loose();
1246
+ const gscdumpTeamMemberRowSchema = z.object({
1247
+ userId: z.number(),
1248
+ publicId: z.string(),
1249
+ name: z.string().nullable(),
1250
+ email: z.email(),
1251
+ picture: z.string().nullable(),
1252
+ role: gscdumpTeamRoleSchema,
1253
+ joinedAt: z.number()
1254
+ }).loose();
1255
+ const createPartnerTeamSchema = z.object({
1256
+ ownerUserId: z.string(),
1257
+ name: z.string().min(2).max(60),
1258
+ personalTeam: z.boolean().optional()
1259
+ });
1260
+ const addPartnerTeamMemberSchema = z.object({
1261
+ userId: z.string(),
1262
+ role: gscdumpTeamRoleSchema
1263
+ });
1264
+ const bindPartnerSiteTeamSchema = z.object({ teamId: z.string().nullable() });
1265
+ const partnerRealtimeEventSchema = z.discriminatedUnion("event", [
1266
+ z.object({
1267
+ event: z.literal("sync.progress"),
1268
+ siteId: z.string(),
1269
+ siteUrl: z.string(),
1270
+ table: z.string(),
1271
+ date: z.string(),
1272
+ progress: z.number()
1273
+ }).loose(),
1274
+ z.object({
1275
+ event: z.literal("sync.complete"),
1276
+ userId: z.number(),
1277
+ siteId: z.string(),
1278
+ siteUrl: z.string(),
1279
+ table: z.string(),
1280
+ date: z.string(),
1281
+ rowsFetched: z.number(),
1282
+ rowsInserted: z.number(),
1283
+ timestamp: z.number()
1284
+ }).loose(),
1285
+ z.object({
1286
+ event: z.literal("sync.job_complete"),
1287
+ userId: z.number(),
1288
+ siteId: z.string(),
1289
+ siteUrl: z.string(),
1290
+ table: z.string(),
1291
+ date: z.string(),
1292
+ rowsFetched: z.number(),
1293
+ rowsInserted: z.number(),
1294
+ syncStatus: z.string(),
1295
+ timestamp: z.number()
1296
+ }).loose(),
1297
+ z.object({
1298
+ event: z.literal("sync.site_complete"),
1299
+ userId: z.number(),
1300
+ siteId: z.string(),
1301
+ siteUrl: z.string(),
1302
+ syncStatus: z.string(),
1303
+ timestamp: z.number()
1304
+ }).loose(),
1305
+ z.object({
1306
+ event: z.literal("sync.failed"),
1307
+ userId: z.number(),
1308
+ siteId: z.string(),
1309
+ siteUrl: z.string(),
1310
+ table: z.string(),
1311
+ date: z.string(),
1312
+ error: z.string(),
1313
+ timestamp: z.number()
1314
+ }).loose(),
1315
+ z.object({
1316
+ event: z.literal("job.failed"),
1317
+ siteId: z.string(),
1318
+ siteUrl: z.string(),
1319
+ table: z.string(),
1320
+ date: z.string(),
1321
+ error: z.string(),
1322
+ timestamp: z.number()
1323
+ }).loose(),
1324
+ z.object({
1325
+ event: z.literal("site.added"),
1326
+ userId: z.number(),
1327
+ siteId: z.string(),
1328
+ siteUrl: z.string()
1329
+ }).loose(),
1330
+ z.object({
1331
+ event: z.literal("site.removed"),
1332
+ userId: z.number(),
1333
+ siteId: z.string(),
1334
+ siteUrl: z.string()
1335
+ }).loose(),
1336
+ z.object({
1337
+ event: z.literal("auth.failed"),
1338
+ userId: z.number(),
1339
+ siteId: z.string(),
1340
+ siteUrl: z.string(),
1341
+ error: z.string(),
1342
+ timestamp: z.number()
1343
+ }).loose(),
1344
+ z.object({
1345
+ event: z.literal("auth.needs_reauth"),
1346
+ userId: z.number(),
1347
+ failureCount: z.number(),
1348
+ timestamp: z.number()
1349
+ }).loose(),
1350
+ z.object({
1351
+ event: z.literal("enrichment.complete"),
1352
+ siteId: z.string(),
1353
+ userId: z.number(),
1354
+ timestamp: z.number()
1355
+ }).loose(),
1356
+ z.object({
1357
+ event: z.literal("sitemap.progress"),
1358
+ userId: z.number(),
1359
+ siteId: z.string(),
1360
+ siteUrl: z.string(),
1361
+ discovered: z.number(),
1362
+ total: z.number(),
1363
+ progress: z.number()
1364
+ }).loose(),
1365
+ z.object({
1366
+ event: z.literal("sitemap.complete"),
1367
+ userId: z.number(),
1368
+ siteId: z.string(),
1369
+ siteUrl: z.string(),
1370
+ discoveredCount: z.number(),
1371
+ timestamp: z.number()
1372
+ }).loose(),
1373
+ z.object({
1374
+ event: z.literal("indexing.progress"),
1375
+ userId: z.number(),
1376
+ siteId: z.string(),
1377
+ siteUrl: z.string(),
1378
+ checked: z.number(),
1379
+ total: z.number(),
1380
+ progress: z.number()
1381
+ }).loose(),
1382
+ z.object({
1383
+ event: z.literal("indexing.complete"),
1384
+ userId: z.number(),
1385
+ siteId: z.string(),
1386
+ siteUrl: z.string(),
1387
+ totalUrls: z.number(),
1388
+ indexedCount: z.number(),
1389
+ timestamp: z.number()
1390
+ }).loose()
1391
+ ]);
1392
+ const canonicalWebhookEventTypeSchema = z.enum(CANONICAL_WEBHOOK_EVENTS);
1393
+ const webhookEventTypeSchema = canonicalWebhookEventTypeSchema;
1394
+ const jobFailedWebhookPayloadSchema = z.object({
1395
+ event: z.literal("job.failed"),
1396
+ siteId: z.string(),
1397
+ siteUrl: z.string(),
1398
+ table: z.string(),
1399
+ date: z.string(),
1400
+ error: z.string(),
1401
+ timestamp: z.number()
1402
+ }).loose();
1403
+ const partnerWebhookDataSchema = unknownRecord;
1404
+ const partnerWebhookEnvelopeSchema = z.object({
1405
+ contractVersion: z.literal(WEBHOOK_CONTRACT_VERSION),
1406
+ deliveryId: z.string().min(1),
1407
+ event: canonicalWebhookEventTypeSchema,
1408
+ partnerId: z.string().min(1),
1409
+ userId: z.string().nullable(),
1410
+ siteId: z.string().optional(),
1411
+ externalUserId: z.string().nullable().optional(),
1412
+ externalSiteId: z.string().nullable().optional(),
1413
+ lifecycleRevision: z.number().int(),
1414
+ occurredAt: z.iso.datetime(),
1415
+ data: partnerWebhookDataSchema
1416
+ }).loose();
1417
+ const analyticsEndpointSchemas = {
1418
+ analyticsWhoami: { response: whoamiResponseSchema },
1419
+ analyticsSites: { response: z.array(siteListItemSchema) },
1420
+ analyticsCountries: { response: countriesResponseSchema },
1421
+ analyticsSearchAppearance: { response: searchAppearanceResponseSchema },
1422
+ analyticsSitemapHistory: { response: sitemapHistoryResponseSchema },
1423
+ analyticsSitemaps: { response: sitemapIndexSchema },
1424
+ analyticsInspectionHistory: { response: inspectionHistoryResponseSchema },
1425
+ analyticsInspections: { response: inspectionIndexSchema },
1426
+ analyticsRows: { response: gscRowQueryResponseSchema },
1427
+ analyticsRollup: { response: rollupEnvelopeSchema },
1428
+ analyticsBackfill: {
1429
+ body: backfillRangeSchema,
1430
+ response: backfillResponseSchema
1431
+ },
1432
+ analyticsIndexingUrls: { response: indexingUrlsResponseSchema },
1433
+ analyticsIndexingDiagnostics: { response: indexingDiagnosticsSchema },
1434
+ analyticsIndexingInspect: {
1435
+ body: indexingInspectRequestSchema,
1436
+ response: indexingInspectAnyResponseSchema
1437
+ },
1438
+ analyticsSitemapChanges: { response: sitemapChangesResponseSchema },
1439
+ analyticsAnalysisSources: { response: gscdumpAnalysisSourcesResponseSchema },
1440
+ analyticsSourceInfo: { response: sourceInfoResponseSchema }
1441
+ };
1442
+ const partnerControlEndpointSchemas = {
1443
+ appUser: { response: gscdumpUserMeResponseSchema },
1444
+ appHealth: { response: gscdumpHealthResponseSchema },
1445
+ appSyncProgress: { response: gscdumpSyncProgressResponseSchema },
1446
+ appSyncJobs: { response: gscdumpSyncJobsResponseSchema },
1447
+ registerUser: {
1448
+ body: registerPartnerUserSchema,
1449
+ response: gscdumpUserRegistrationSchema
1450
+ },
1451
+ updateUserTokens: {
1452
+ body: updatePartnerUserTokensSchema,
1453
+ response: z.object({
1454
+ userId: z.string(),
1455
+ updated: z.boolean(),
1456
+ sites: z.array(gscdumpAvailableSiteSchema)
1457
+ }).loose()
1458
+ },
1459
+ getUserStatus: { response: gscdumpUserStatusSchema },
1460
+ getUserLifecycle: { response: partnerLifecycleResponseSchema },
1461
+ getUserSites: { response: z.object({ sites: z.array(gscdumpUserSiteSchema) }).loose() },
1462
+ getAvailableSites: { response: z.object({ sites: z.array(gscdumpAvailableSiteSchema) }).loose() },
1463
+ registerSite: {
1464
+ body: registerPartnerSiteSchema,
1465
+ response: gscdumpSiteRegistrationSchema
1466
+ },
1467
+ bulkRegisterSites: {
1468
+ body: bulkRegisterPartnerSitesSchema,
1469
+ response: bulkRegisterPartnerSitesResponseSchema
1470
+ },
1471
+ deleteUser: { response: gscdumpDeletePartnerUserResponseSchema },
1472
+ getAnalysisSources: { response: gscdumpAnalysisSourcesResponseSchema },
1473
+ getData: {
1474
+ state: builderStateSchema,
1475
+ options: dataQueryOptionsSchema,
1476
+ response: gscdumpDataResponseSchema
1477
+ },
1478
+ getDataDetail: {
1479
+ state: builderStateSchema,
1480
+ options: dataDetailOptionsSchema,
1481
+ response: gscdumpDataDetailResponseSchema
1482
+ },
1483
+ getAnalysis: {
1484
+ query: gscdumpAnalysisParamsSchema,
1485
+ response: gscdumpAnalysisResponseSchema
1486
+ },
1487
+ getSitemaps: { response: gscdumpSitemapsResponseSchema },
1488
+ getSitemapChanges: { response: gscdumpSitemapChangesResponseSchema },
1489
+ getIndexing: { response: gscdumpIndexingResponseSchema },
1490
+ getIndexingUrls: {
1491
+ query: indexingUrlsParamsSchema,
1492
+ response: gscdumpIndexingUrlsResponseSchema
1493
+ },
1494
+ getIndexingDiagnostics: { response: gscdumpIndexingDiagnosticsResponseSchema },
1495
+ getIndexingInspect: {
1496
+ body: indexingInspectRequestSchema,
1497
+ response: indexingInspectAnyResponseSchema
1498
+ },
1499
+ getUserSettings: { response: gscdumpUserSettingsSchema },
1500
+ patchUserSettings: {
1501
+ body: gscdumpUserSettingsSchema.partial(),
1502
+ response: gscdumpUserSettingsSchema
1503
+ },
1504
+ recoverPermission: { response: gscdumpPermissionRecoverySchema },
1505
+ getTopAssociation: {
1506
+ query: gscdumpTopAssociationParamsSchema,
1507
+ response: gscdumpTopAssociationResponseSchema
1508
+ },
1509
+ getKeywordSparklines: {
1510
+ body: gscdumpKeywordSparklinesParamsSchema,
1511
+ response: gscdumpKeywordSparklinesResponseSchema
1512
+ },
1513
+ getQueryTrend: {
1514
+ query: gscdumpQueryTrendParamsSchema,
1515
+ response: gscdumpQueryTrendResponseSchema
1516
+ },
1517
+ getPageTrend: {
1518
+ query: gscdumpPageTrendParamsSchema,
1519
+ response: gscdumpPageTrendResponseSchema
1520
+ },
1521
+ getDateRangeInsight: {
1522
+ query: gscdumpDateRangeParamsSchema,
1523
+ response: unknownRecord
1524
+ },
1525
+ getCanonicalMismatches: { response: gscdumpCanonicalMismatchesResponseSchema },
1526
+ getIndexPercent: {
1527
+ query: gscdumpIndexPercentParamsSchema,
1528
+ response: gscdumpIndexPercentResponseSchema
1529
+ },
1530
+ getSiteReport: { response: gscdumpSiteReportResponseSchema },
1531
+ createTeam: {
1532
+ body: createPartnerTeamSchema,
1533
+ response: z.object({ team: gscdumpTeamRowSchema }).loose()
1534
+ },
1535
+ listTeamMembers: { response: z.object({ members: z.array(gscdumpTeamMemberRowSchema) }).loose() },
1536
+ addTeamMember: {
1537
+ body: addPartnerTeamMemberSchema,
1538
+ response: unknownRecord
1539
+ },
1540
+ bindSiteToTeam: {
1541
+ body: bindPartnerSiteTeamSchema,
1542
+ response: z.object({
1543
+ ok: z.literal(true),
1544
+ teamId: z.string().nullable()
1545
+ }).loose()
1546
+ },
1547
+ realtimeEvent: { message: partnerRealtimeEventSchema },
1548
+ webhook: { message: partnerWebhookEnvelopeSchema }
1549
+ };
1550
+ const partnerEndpointSchemas = {
1551
+ ...analyticsEndpointSchemas,
1552
+ ...partnerControlEndpointSchemas
1553
+ };
1554
+ export { CANONICAL_WEBHOOK_EVENTS, GSCDUMP_ONBOARDING_CONTRACT_VERSION, GSCDUMP_OPTIONAL_INDEXING_SCOPE, GSCDUMP_REQUIRED_ANALYTICS_SCOPE, GSCDUMP_WRITE_ANALYTICS_SCOPE, VALID_WEBHOOK_EVENTS, WEBHOOK_CONTRACT_VERSION, WEBHOOK_CONTRACT_VERSION_HEADER, WEBHOOK_DELIVERY_HEADER, WEBHOOK_EVENT_HEADER, WEBHOOK_SIGNATURE_HEADER, WEBHOOK_TIMESTAMP_HEADER, accountNextActions, accountStatuses, addPartnerTeamMemberSchema, analyticsEndpointSchemas, analyticsNextActions, analyticsRoutes, analyticsStatuses, backfillRangeSchema, backfillResponseSchema, bindPartnerSiteTeamSchema, builderStateSchema, bulkRegisterPartnerSitesResponseSchema, bulkRegisterPartnerSitesSchema, canonicalWebhookEventTypeSchema, countriesResponseSchema, countryRowSchema, createPartnerTeamSchema, dataDetailOptionsSchema, dataQueryOptionsSchema, gscApiRangeSchema, gscComparisonFilterSchema, gscRowQueryMetaSchema, gscRowQueryResponseSchema, gscdumpAnalysisParamsSchema, gscdumpAnalysisPresetSchema, gscdumpAnalysisResponseSchema, gscdumpAnalysisSourcesResponseSchema, gscdumpAvailableSiteSchema, gscdumpCanonicalMismatchesResponseSchema, gscdumpDataDetailResponseSchema, gscdumpDataResponseSchema, gscdumpDataRowSchema, gscdumpDateRangeParamsSchema, gscdumpDeletePartnerUserResponseSchema, gscdumpHealthResponseSchema, gscdumpIndexPercentParamsSchema, gscdumpIndexPercentResponseSchema, gscdumpIndexingDiagnosticsResponseSchema, gscdumpIndexingResponseSchema, gscdumpIndexingUrlsResponseSchema, gscdumpKeywordSparklinesParamsSchema, gscdumpKeywordSparklinesResponseSchema, gscdumpMetaSchema, gscdumpPageTrendParamsSchema, gscdumpPageTrendResponseSchema, gscdumpPermissionRecoverySchema, gscdumpQueryTrendParamsSchema, gscdumpQueryTrendResponseSchema, gscdumpSiteRegistrationSchema, gscdumpSiteReportResponseSchema, gscdumpSitemapChangesResponseSchema, gscdumpSitemapsResponseSchema, gscdumpSyncJobsResponseSchema, gscdumpSyncProgressResponseSchema, gscdumpTeamMemberRowSchema, gscdumpTeamRoleSchema, gscdumpTeamRowSchema, gscdumpTopAssociationParamsSchema, gscdumpTopAssociationResponseSchema, gscdumpTotalsSchema, gscdumpUserMeResponseSchema, gscdumpUserRegistrationSchema, gscdumpUserSettingsSchema, gscdumpUserSiteSchema, gscdumpUserStatusSchema, hasOptionalIndexingScope, hasRequiredAnalyticsScope, indexingDiagnosticsSchema, indexingInspectAnyResponseSchema, indexingInspectRateLimitedSchema, indexingInspectRequestSchema, indexingInspectResponseSchema, indexingInspectResultSchema, indexingIssueSchema, indexingNextActions, indexingStatuses, indexingUrlRowSchema, indexingUrlStatusSchema, indexingUrlsParamsSchema, indexingUrlsResponseSchema, inspectionHistoryRecordSchema, inspectionHistoryResponseSchema, inspectionIndexSchema, inspectionRecordRawSchema, jobFailedWebhookPayloadSchema, lifecycleErrorCodes, lifecycleErrorSchema, lifecycleProgressSchema, lifecycleWebhookEvents, parseGrantedScopes, partnerControlEndpointSchemas, partnerEndpointSchemas, partnerLifecycleAccountSchema, partnerLifecycleResponseSchema, partnerLifecycleSiteSchema, partnerRealtimeEventSchema, partnerRoutes, partnerWebhookDataSchema, partnerWebhookEnvelopeSchema, propertyNextActions, propertyStatuses, querySourceModes, registerPartnerSiteSchema, registerPartnerUserSchema, rollupEnvelopeSchema, scheduleStateSchema, searchAppearanceResponseSchema, searchAppearanceRowSchema, searchTypeSchema, siteListItemSchema, sitemapChangesResponseSchema, sitemapHistoryRecordSchema, sitemapHistoryResponseSchema, sitemapIndexSchema, sitemapNextActions, sitemapStatuses, sourceInfoResponseSchema, updatePartnerUserTokensSchema, webhookEventTypeSchema, whoamiResponseSchema };