@nuxtseo/protocol 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/v1/gsc.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { canonicalDifferenceKindSchema, gscdumpAnalysisPresetSchema, gscdumpIndexingTransitionFieldSchema, gscdumpTotalsSchema, indexingIssueSchema, } from '@gscdump/contracts';
2
2
  import { z as schema } from 'zod';
3
- import { defineOperation, defineRegistry, defineResponseObject, defineSuccessEnvelope, publicV1ErrorEnvelope, publicV1StandardRatePolicy, } from './core.js';
3
+ import { defineOperation, defineRegistry, defineResponseObject, defineSuccessEnvelope, publicV1ErrorEnvelope, publicV1PageSchema, publicV1StandardRatePolicy, } from './core.js';
4
4
  export const searchConsoleSiteParamsSchema = schema.strictObject({
5
5
  siteId: schema.string().min(1),
6
6
  });
@@ -76,7 +76,16 @@ export const searchAnalyticsQuerySchema = schema.strictObject({
76
76
  });
77
77
  const totalsSchema = defineResponseObject(gscdumpTotalsSchema.shape, gscdumpTotalsSchema.shape);
78
78
  const representedTotalsSchema = defineResponseObject({ clicks: schema.number(), impressions: schema.number() }, { clicks: schema.number(), impressions: schema.number() });
79
- const pageRowSchema = defineResponseObject({
79
+ const pageRowShape = {
80
+ /**
81
+ * The canonical page URL: origin plus path, with the host twin, the scheme,
82
+ * the query string and the fragment folded away.
83
+ *
84
+ * Search Console reports one row per exact page string, so `https://x/a`,
85
+ * `https://www.x/a` and `https://x/a?utm=1` arrive as three rows that all
86
+ * canonicalise to one `url`. `url` therefore repeats across rows. Group by
87
+ * `gscPage` to reproduce `total`.
88
+ */
80
89
  url: schema.string().min(1),
81
90
  clicks: schema.number(),
82
91
  impressions: schema.number(),
@@ -84,9 +93,21 @@ const pageRowSchema = defineResponseObject({
84
93
  pos: schema.number(),
85
94
  keyword: schema.string().nullable(),
86
95
  prevClicks: schema.number().optional(),
96
+ prevImpressions: schema.number().optional(),
87
97
  prevPos: schema.number().optional(),
88
98
  change: schema.number().nullable(),
89
- });
99
+ };
100
+ /**
101
+ * The exact page string Search Console reported, before canonicalisation.
102
+ * This is the dimension `total` counts, so it is unique within `rows`.
103
+ *
104
+ * A producer must send it. A client treats it as optional, because a client
105
+ * newer than the deployed server reads answers that predate the field.
106
+ * `.nullable()` alone still demands the key, which fails every response the
107
+ * deployed server sends today.
108
+ */
109
+ const gscPageField = schema.string().nullable();
110
+ const pageRowSchema = defineResponseObject({ ...pageRowShape, gscPage: gscPageField }, { ...pageRowShape, gscPage: gscPageField.optional() });
90
111
  const keywordRowSchema = defineResponseObject({
91
112
  keyword: schema.string().min(1),
92
113
  clicks: schema.number(),
@@ -95,6 +116,7 @@ const keywordRowSchema = defineResponseObject({
95
116
  pos: schema.number(),
96
117
  topPage: schema.string().nullable(),
97
118
  prevClicks: schema.number().optional(),
119
+ prevImpressions: schema.number().optional(),
98
120
  prevPos: schema.number().optional(),
99
121
  change: schema.number().nullable(),
100
122
  });
@@ -126,6 +148,11 @@ function breakdownSchema(view, row) {
126
148
  representedTotals: representedTotalsSchema.producer,
127
149
  representedTotalsScope: schema.string().min(1),
128
150
  rows: schema.array(row.producer),
151
+ /**
152
+ * The converged list envelope. `page.total` repeats the flat `total`,
153
+ * which counts the whole upstream population rather than this page.
154
+ */
155
+ page: publicV1PageSchema.producer,
129
156
  }, {
130
157
  site: schema.string().min(1),
131
158
  view: schema.literal(view),
@@ -136,6 +163,7 @@ function breakdownSchema(view, row) {
136
163
  representedTotals: representedTotalsSchema.client,
137
164
  representedTotalsScope: schema.string().min(1),
138
165
  rows: schema.array(row.client),
166
+ page: publicV1PageSchema.client.optional(),
139
167
  });
140
168
  }
141
169
  const pagesDataSchema = breakdownSchema('pages', pageRowSchema);
@@ -221,9 +249,12 @@ const analysisRowSchema = defineResponseObject({
221
249
  clicks: schema.number(),
222
250
  impressions: schema.number(),
223
251
  ctr: schema.number(),
224
- pos: schema.number(),
252
+ // Null when the keyword has no row in the current window (a `decay` keyword
253
+ // that vanished). Zero would read as a ranking position.
254
+ pos: schema.number().nullable(),
225
255
  page: schema.string().optional(),
226
256
  prevClicks: schema.number().optional(),
257
+ prevImpressions: schema.number().optional(),
227
258
  prevPos: schema.number().optional(),
228
259
  potentialClicks: schema.number().optional(),
229
260
  opportunityScore: schema.number().optional(),
@@ -510,7 +541,7 @@ export const searchAnalyticsQueryOperation = defineOperation({
510
541
  tags: ['Sites', 'Search'],
511
542
  examples: [{
512
543
  request: { params: { siteId: 'site_01JXYZ' }, query: { view: 'keywords' } },
513
- response: { status: 200, body: { data: { site: 'https://example.com', view: 'keywords', period: '28d', total: 1, totals: { clicks: 12, impressions: 100, ctr: 0.12, position: 4 }, totalsScope: 'full upstream query population', representedTotals: { clicks: 12, impressions: 100 }, representedTotalsScope: 'returned rows', rows: [{ keyword: 'nuxt seo', clicks: 12, impressions: 100, ctr: 0.12, pos: 4, topPage: 'https://example.com/docs', change: 2 }] }, meta: { requestId: 'req_01JXYZ', version: '1.0' } } },
544
+ response: { status: 200, body: { data: { site: 'https://example.com', view: 'keywords', period: '28d', total: 1, totals: { clicks: 12, impressions: 100, ctr: 0.12, position: 4 }, totalsScope: 'full upstream query population', representedTotals: { clicks: 12, impressions: 100 }, representedTotalsScope: 'returned rows', rows: [{ keyword: 'nuxt seo', clicks: 12, impressions: 100, ctr: 0.12, pos: 4, topPage: 'https://example.com/docs', change: 2 }], page: { total: 1, limit: 25, offset: 0, hasMore: false } }, meta: { requestId: 'req_01JXYZ', version: '1.0' } } },
514
545
  }],
515
546
  },
516
547
  });
@@ -61,6 +61,11 @@ export declare const pageObservationSchema: import("./core.js").CompatibleRespon
61
61
  unmonitored: "unmonitored";
62
62
  }>>;
63
63
  }, z.core.$strip>>;
64
+ /**
65
+ * The latest Lighthouse Scan for one page. The four category scores run from 0
66
+ * to 100. `lcp` and `tbt` are milliseconds. `cls` is the unitless Cumulative
67
+ * Layout Shift value at the Lighthouse scale, so `0.1` is the "good" threshold.
68
+ */
64
69
  export declare const pageLighthouseSchema: import("./core.js").CompatibleResponseSchema<z.ZodObject<{
65
70
  readonly scanId: z.ZodString;
66
71
  readonly strategy: z.ZodEnum<{
@@ -142,6 +147,12 @@ export declare const pageInspectDataSchema: import("./core.js").CompatibleRespon
142
147
  unmonitored: "unmonitored";
143
148
  }>>;
144
149
  }, z.core.$strip>>;
150
+ page: z.ZodObject<{
151
+ readonly total: z.ZodNumber;
152
+ readonly limit: z.ZodNumber;
153
+ readonly offset: z.ZodNumber;
154
+ readonly hasMore: z.ZodBoolean;
155
+ }, z.core.$strip>;
145
156
  total: z.ZodNumber;
146
157
  coverage: z.ZodEnum<{
147
158
  "issues-open": "issues-open";
@@ -208,6 +219,12 @@ export declare const pageInspectDataSchema: import("./core.js").CompatibleRespon
208
219
  unmonitored: "unmonitored";
209
220
  }>>;
210
221
  }, z.core.$strip>>;
222
+ page: z.ZodOptional<z.ZodObject<{
223
+ readonly total: z.ZodNumber;
224
+ readonly limit: z.ZodNumber;
225
+ readonly offset: z.ZodNumber;
226
+ readonly hasMore: z.ZodBoolean;
227
+ }, z.core.$strip>>;
211
228
  total: z.ZodNumber;
212
229
  coverage: z.ZodEnum<{
213
230
  "issues-open": "issues-open";
@@ -276,6 +293,12 @@ export declare const pageInspectResponseSchema: import("./core.js").CompatibleRe
276
293
  unmonitored: "unmonitored";
277
294
  }>>;
278
295
  }, z.core.$strip>>;
296
+ page: z.ZodObject<{
297
+ readonly total: z.ZodNumber;
298
+ readonly limit: z.ZodNumber;
299
+ readonly offset: z.ZodNumber;
300
+ readonly hasMore: z.ZodBoolean;
301
+ }, z.core.$strip>;
279
302
  total: z.ZodNumber;
280
303
  coverage: z.ZodEnum<{
281
304
  "issues-open": "issues-open";
@@ -348,6 +371,12 @@ export declare const pageInspectResponseSchema: import("./core.js").CompatibleRe
348
371
  unmonitored: "unmonitored";
349
372
  }>>;
350
373
  }, z.core.$strip>>;
374
+ page: z.ZodOptional<z.ZodObject<{
375
+ readonly total: z.ZodNumber;
376
+ readonly limit: z.ZodNumber;
377
+ readonly offset: z.ZodNumber;
378
+ readonly hasMore: z.ZodBoolean;
379
+ }, z.core.$strip>>;
351
380
  total: z.ZodNumber;
352
381
  coverage: z.ZodEnum<{
353
382
  "issues-open": "issues-open";
@@ -480,6 +509,11 @@ export declare const pageScanResponseSchema: import("./core.js").CompatibleRespo
480
509
  }, z.core.$strip>>;
481
510
  export type PageInspectQuery = z.output<typeof pageInspectQuerySchema>;
482
511
  export type PageInspect = z.output<typeof pageInspectDataSchema.producer>;
512
+ /**
513
+ * What a consumer receives. Additive fields are optional here, so a client
514
+ * newer than the deployed server still parses its answers.
515
+ */
516
+ export type PageInspectData = z.output<typeof pageInspectDataSchema.client>;
483
517
  export type PageScanBody = z.output<typeof pageScanBodySchema>;
484
518
  export type PageScan = z.output<typeof pageScanDataSchema.producer>;
485
519
  export declare const pageInspectOperation: {
@@ -548,6 +582,12 @@ export declare const pageInspectOperation: {
548
582
  unmonitored: "unmonitored";
549
583
  }>>;
550
584
  }, z.core.$strip>>;
585
+ page: z.ZodObject<{
586
+ readonly total: z.ZodNumber;
587
+ readonly limit: z.ZodNumber;
588
+ readonly offset: z.ZodNumber;
589
+ readonly hasMore: z.ZodBoolean;
590
+ }, z.core.$strip>;
551
591
  total: z.ZodNumber;
552
592
  coverage: z.ZodEnum<{
553
593
  "issues-open": "issues-open";
@@ -620,6 +660,12 @@ export declare const pageInspectOperation: {
620
660
  unmonitored: "unmonitored";
621
661
  }>>;
622
662
  }, z.core.$strip>>;
663
+ page: z.ZodOptional<z.ZodObject<{
664
+ readonly total: z.ZodNumber;
665
+ readonly limit: z.ZodNumber;
666
+ readonly offset: z.ZodNumber;
667
+ readonly hasMore: z.ZodBoolean;
668
+ }, z.core.$strip>>;
623
669
  total: z.ZodNumber;
624
670
  coverage: z.ZodEnum<{
625
671
  "issues-open": "issues-open";
@@ -760,6 +806,12 @@ export declare const pageInspectOperation: {
760
806
  };
761
807
  readonly observations: {
762
808
  readonly rows: readonly [];
809
+ readonly page: {
810
+ readonly total: 0;
811
+ readonly limit: 100;
812
+ readonly offset: 0;
813
+ readonly hasMore: false;
814
+ };
763
815
  readonly total: 0;
764
816
  readonly coverage: 'never-scanned';
765
817
  readonly pagination: {
@@ -1804,6 +1856,12 @@ export declare const pagesV1Registry: import("./core.js").PublicV1Registry<{
1804
1856
  unmonitored: "unmonitored";
1805
1857
  }>>;
1806
1858
  }, z.core.$strip>>;
1859
+ page: z.ZodObject<{
1860
+ readonly total: z.ZodNumber;
1861
+ readonly limit: z.ZodNumber;
1862
+ readonly offset: z.ZodNumber;
1863
+ readonly hasMore: z.ZodBoolean;
1864
+ }, z.core.$strip>;
1807
1865
  total: z.ZodNumber;
1808
1866
  coverage: z.ZodEnum<{
1809
1867
  "issues-open": "issues-open";
@@ -1876,6 +1934,12 @@ export declare const pagesV1Registry: import("./core.js").PublicV1Registry<{
1876
1934
  unmonitored: "unmonitored";
1877
1935
  }>>;
1878
1936
  }, z.core.$strip>>;
1937
+ page: z.ZodOptional<z.ZodObject<{
1938
+ readonly total: z.ZodNumber;
1939
+ readonly limit: z.ZodNumber;
1940
+ readonly offset: z.ZodNumber;
1941
+ readonly hasMore: z.ZodBoolean;
1942
+ }, z.core.$strip>>;
1879
1943
  total: z.ZodNumber;
1880
1944
  coverage: z.ZodEnum<{
1881
1945
  "issues-open": "issues-open";
@@ -2016,6 +2080,12 @@ export declare const pagesV1Registry: import("./core.js").PublicV1Registry<{
2016
2080
  };
2017
2081
  readonly observations: {
2018
2082
  readonly rows: readonly [];
2083
+ readonly page: {
2084
+ readonly total: 0;
2085
+ readonly limit: 100;
2086
+ readonly offset: 0;
2087
+ readonly hasMore: false;
2088
+ };
2019
2089
  readonly total: 0;
2020
2090
  readonly coverage: 'never-scanned';
2021
2091
  readonly pagination: {
package/dist/v1/pages.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z as schema } from 'zod';
2
- import { defineOperation, defineRegistry, defineResponseObject, defineSuccessEnvelope, publicV1BooleanFlag, publicV1ErrorEnvelope, publicV1StandardRatePolicy, } from './core.js';
2
+ import { defineOperation, defineRegistry, defineResponseObject, defineSuccessEnvelope, publicV1BooleanFlag, publicV1ErrorEnvelope, publicV1PageSchema, publicV1StandardRatePolicy, } from './core.js';
3
3
  export const pageSiteParamsSchema = schema.strictObject({
4
4
  siteId: schema.string().min(1),
5
5
  });
@@ -37,6 +37,11 @@ export const pageObservationSchema = defineResponseObject({
37
37
  resolvedAt: schema.string().datetime().nullable(),
38
38
  resolution: schema.enum(['fixed', 'removed', 'excluded', 'unmonitored', 'expired']).nullable(),
39
39
  });
40
+ /**
41
+ * The latest Lighthouse Scan for one page. The four category scores run from 0
42
+ * to 100. `lcp` and `tbt` are milliseconds. `cls` is the unitless Cumulative
43
+ * Layout Shift value at the Lighthouse scale, so `0.1` is the "good" threshold.
44
+ */
40
45
  export const pageLighthouseSchema = defineResponseObject({
41
46
  scanId: schema.string().min(1),
42
47
  strategy: schema.enum(['mobile', 'desktop']),
@@ -86,8 +91,16 @@ export const pageInspectDataSchema = defineResponseObject({
86
91
  }),
87
92
  observations: schema.strictObject({
88
93
  rows: schema.array(pageObservationSchema.producer),
94
+ /**
95
+ * The converged list envelope. `data.page` already names the inspected
96
+ * Page here, so the list envelope sits beside its own `rows`.
97
+ */
98
+ page: publicV1PageSchema.producer,
89
99
  total: schema.number().int().nonnegative(),
90
100
  coverage: pageObservationCoverageSchema,
101
+ /**
102
+ * Deprecated alias of `page`. Kept for one minor version, then removed.
103
+ */
91
104
  pagination: schema.strictObject({
92
105
  limit: schema.number().int().positive(),
93
106
  offset: schema.number().int().nonnegative(),
@@ -107,6 +120,7 @@ export const pageInspectDataSchema = defineResponseObject({
107
120
  }),
108
121
  observations: schema.looseObject({
109
122
  rows: schema.array(pageObservationSchema.client),
123
+ page: publicV1PageSchema.client.optional(),
110
124
  total: schema.number().int().nonnegative(),
111
125
  coverage: pageObservationCoverageSchema,
112
126
  pagination: schema.looseObject({
@@ -200,6 +214,7 @@ export const pageInspectOperation = defineOperation({
200
214
  page: { url: 'https://example.com/guide', path: '/guide' },
201
215
  observations: {
202
216
  rows: [],
217
+ page: { total: 0, limit: 100, offset: 0, hasMore: false },
203
218
  total: 0,
204
219
  coverage: 'never-scanned',
205
220
  pagination: { limit: 100, offset: 0, hasMore: false },
@@ -35,6 +35,12 @@ export declare const performanceVitalSummarySchema: import("./core.js").Compatib
35
35
  poor: z.ZodNumber;
36
36
  }, z.core.$loose>;
37
37
  }, z.core.$strip>>;
38
+ /**
39
+ * Medians across the Site's latest Scans. Units follow the field: `perf`,
40
+ * `a11y`, `bestPractices` and `seo` are scores from 0 to 100, `lcp` and `tbt`
41
+ * are milliseconds, and `cls` is the unitless Cumulative Layout Shift value.
42
+ * `statusCounts` counts Scans in the good, needs-improvement and poor bands.
43
+ */
38
44
  export declare const sitePerformanceHealthSchema: import("./core.js").CompatibleResponseSchema<z.ZodObject<{
39
45
  readonly perf: z.ZodObject<{
40
46
  readonly median: z.ZodNullable<z.ZodNumber>;
@@ -962,8 +968,8 @@ export declare const sitePerformanceOverviewOperation: {
962
968
  };
963
969
  };
964
970
  readonly cls: {
965
- readonly median: 80;
966
- readonly worst: 260;
971
+ readonly median: 0.08;
972
+ readonly worst: 0.26;
967
973
  readonly statusCounts: {
968
974
  readonly good: 4;
969
975
  readonly ni: 0;
@@ -3739,8 +3745,8 @@ export declare const performanceV1Registry: import("./core.js").PublicV1Registry
3739
3745
  };
3740
3746
  };
3741
3747
  readonly cls: {
3742
- readonly median: 80;
3743
- readonly worst: 260;
3748
+ readonly median: 0.08;
3749
+ readonly worst: 0.26;
3744
3750
  readonly statusCounts: {
3745
3751
  readonly good: 4;
3746
3752
  readonly ni: 0;
@@ -4,6 +4,19 @@ export const performanceSiteParamsSchema = schema.strictObject({
4
4
  siteId: schema.string().min(1),
5
5
  });
6
6
  const nullableMetricSchema = schema.number().nullable();
7
+ /**
8
+ * A Lighthouse category score, 0 to 100, where a higher number is better.
9
+ * Used for `perf`, `a11y`, `bestPractices` and `seo`.
10
+ */
11
+ const nullableScoreSchema = nullableMetricSchema;
12
+ /** A measured duration in milliseconds. Used for `lcp` and `tbt`. */
13
+ const nullableMillisecondsSchema = nullableMetricSchema;
14
+ /**
15
+ * Cumulative Layout Shift, unitless, normally between 0 and about 1. A lower
16
+ * number is better. Reported at the same scale as Lighthouse reports it, so
17
+ * `0.1` is the "good" threshold, not `100`.
18
+ */
19
+ const nullableLayoutShiftSchema = nullableMetricSchema;
7
20
  export const performanceScoreSummarySchema = defineResponseObject({
8
21
  median: nullableMetricSchema,
9
22
  worst: nullableMetricSchema,
@@ -37,6 +50,12 @@ export const performanceVitalSummarySchema = defineResponseObject({
37
50
  poor: schema.number().int().nonnegative(),
38
51
  }),
39
52
  });
53
+ /**
54
+ * Medians across the Site's latest Scans. Units follow the field: `perf`,
55
+ * `a11y`, `bestPractices` and `seo` are scores from 0 to 100, `lcp` and `tbt`
56
+ * are milliseconds, and `cls` is the unitless Cumulative Layout Shift value.
57
+ * `statusCounts` counts Scans in the good, needs-improvement and poor bands.
58
+ */
40
59
  export const sitePerformanceHealthSchema = defineResponseObject({
41
60
  perf: performanceScoreSummarySchema.producer,
42
61
  a11y: performanceCategorySummarySchema.producer,
@@ -62,24 +81,24 @@ export const sitePerformanceScanSchema = defineResponseObject({
62
81
  scanId: schema.string().min(1),
63
82
  url: schema.string().url(),
64
83
  completedAt: schema.string().datetime().nullable(),
65
- perf: nullableMetricSchema,
66
- a11y: nullableMetricSchema,
67
- bestPractices: nullableMetricSchema,
68
- seo: nullableMetricSchema,
69
- lcp: nullableMetricSchema,
70
- tbt: nullableMetricSchema,
71
- cls: nullableMetricSchema,
84
+ perf: nullableScoreSchema,
85
+ a11y: nullableScoreSchema,
86
+ bestPractices: nullableScoreSchema,
87
+ seo: nullableScoreSchema,
88
+ lcp: nullableMillisecondsSchema,
89
+ tbt: nullableMillisecondsSchema,
90
+ cls: nullableLayoutShiftSchema,
72
91
  }, {
73
92
  scanId: schema.string().min(1),
74
93
  url: schema.string().url(),
75
94
  completedAt: schema.string().datetime().nullable(),
76
- perf: nullableMetricSchema,
77
- a11y: nullableMetricSchema,
78
- bestPractices: nullableMetricSchema,
79
- seo: nullableMetricSchema,
80
- lcp: nullableMetricSchema,
81
- tbt: nullableMetricSchema,
82
- cls: nullableMetricSchema,
95
+ perf: nullableScoreSchema,
96
+ a11y: nullableScoreSchema,
97
+ bestPractices: nullableScoreSchema,
98
+ seo: nullableScoreSchema,
99
+ lcp: nullableMillisecondsSchema,
100
+ tbt: nullableMillisecondsSchema,
101
+ cls: nullableLayoutShiftSchema,
83
102
  });
84
103
  export const sitePerformanceRouteSchema = defineResponseObject({
85
104
  pageId: schema.string().min(1),
@@ -87,26 +106,26 @@ export const sitePerformanceRouteSchema = defineResponseObject({
87
106
  url: schema.string().url(),
88
107
  latestScanId: schema.string().min(1).nullable(),
89
108
  completedAt: schema.string().datetime().nullable(),
90
- perf: nullableMetricSchema,
91
- a11y: nullableMetricSchema,
92
- bestPractices: nullableMetricSchema,
93
- seo: nullableMetricSchema,
94
- lcp: nullableMetricSchema,
95
- tbt: nullableMetricSchema,
96
- cls: nullableMetricSchema,
109
+ perf: nullableScoreSchema,
110
+ a11y: nullableScoreSchema,
111
+ bestPractices: nullableScoreSchema,
112
+ seo: nullableScoreSchema,
113
+ lcp: nullableMillisecondsSchema,
114
+ tbt: nullableMillisecondsSchema,
115
+ cls: nullableLayoutShiftSchema,
97
116
  }, {
98
117
  pageId: schema.string().min(1),
99
118
  path: schema.string(),
100
119
  url: schema.string().url(),
101
120
  latestScanId: schema.string().min(1).nullable(),
102
121
  completedAt: schema.string().datetime().nullable(),
103
- perf: nullableMetricSchema,
104
- a11y: nullableMetricSchema,
105
- bestPractices: nullableMetricSchema,
106
- seo: nullableMetricSchema,
107
- lcp: nullableMetricSchema,
108
- tbt: nullableMetricSchema,
109
- cls: nullableMetricSchema,
122
+ perf: nullableScoreSchema,
123
+ a11y: nullableScoreSchema,
124
+ bestPractices: nullableScoreSchema,
125
+ seo: nullableScoreSchema,
126
+ lcp: nullableMillisecondsSchema,
127
+ tbt: nullableMillisecondsSchema,
128
+ cls: nullableLayoutShiftSchema,
110
129
  });
111
130
  export const sitePerformanceOverviewDataSchema = defineResponseObject({
112
131
  health: sitePerformanceHealthSchema.producer.nullable(),
@@ -175,7 +194,7 @@ export const sitePerformanceOverviewOperation = defineOperation({
175
194
  seo: { median: 98, worst: 94 },
176
195
  lcp: { median: 2200, worst: 4100, statusCounts: { good: 3, ni: 1, poor: 1 } },
177
196
  tbt: { median: 180, worst: 650, statusCounts: { good: 3, ni: 1, poor: 1 } },
178
- cls: { median: 80, worst: 260, statusCounts: { good: 4, ni: 0, poor: 1 } },
197
+ cls: { median: 0.08, worst: 0.26, statusCounts: { good: 4, ni: 0, poor: 1 } },
179
198
  scannedPageCount: 5,
180
199
  lastScanAt: '2026-08-10T01:00:00.000Z',
181
200
  },