Skip to content

Commit d02c7c3

Browse files
authored
feat: Glamsterdam (gloas) support — fork registry, ePBS slot phases, payload lifecycle (#444)
* feat(forks): recognise gloas and derive slot phases per fork The fork registry carried a misspelled 'glaos' placeholder while networks already serve forks.consensus.gloas, so every fork-aware surface crashed on glamsterdam devnets (FORK_METADATA lookup returned undefined). Renames the placeholder to gloas with real Glamsterdam metadata (EIP-7732 ePBS + EIP-7805 FOCIL, execution fork amsterdam), gives ForkLabel a fallback so unknown future fork names degrade to a plain label instead of a crash, and adds isForkAtOrAfter so fork gates stop being string-equality checks that new forks silently miss (BlobDataColumnSpreadChart only matched fulu). Slot phases are now specified once per fork in basis points of the slot duration, mirroring the consensus config deadlines: pre-gloas keeps block/attestation/aggregation at 33/67%, gloas uses the ePBS structure (attestation 25%, aggregate 50%, payload reveal + PTC due 75%). getSlotPhases(fork) drives the live view phase bar. * chore(api): regenerate client with gloas ePBS tables Generated from a local cbt-api running the xatu-cbt gloas models (fct_block_payload_first_seen_by_node, fct_block_payload_available_by_node, fct_block_payload_ptc_vote[_head], fct_block_payload_bid, fct_payload_bid_highest_value_by_builder_chunked_50ms). * feat(slots): ePBS payload lifecycle card on slot detail Gloas slots get a Payload card showing the winning builder bid, when sentries first saw the revealed envelope, when payload+blobs verified locally, and the PTC verdict. derivePayloadStatus classifies the slot (payload delivered / late / withheld) from PTC majority with sentry sightings breaking the late-vs-withheld tie; self-builds are detected by builder index == proposer index. Renders nothing before gloas. * feat(slots): move ePBS payload lifecycle into its own tab with the bid race The payload card sat above the tab strip on every gloas slot, crowding the page. Gloas slots now get a dedicated Payload tab (hidden pre-gloas) holding the lifecycle summary and a Builder Bid Race chart — the fct_payload_bid_highest_value_by_builder_chunked_50ms series rendered through MevBiddingTimelineChart, whose title, anchor and y-axis label are now props so it serves both the relay-era and ePBS auctions. * feat(live): ePBS payload and bid race in the slot HUD On gloas networks the HUD swaps its relay-era MEV and Auction sections for the payload lifecycle: the Auction lane streams the on-chain builder bid race (bid count, distinct builders, best bid, first-bid time — negative when bids beat the slot start), and a Payload section shows envelope reveal timing across sentries, the revealing builder, and PTC votes with a delivered/late/withheld chip driven by derivePayloadStatus. Payload rows fade in as the replay clock crosses the envelope arrival; auction data shows immediately since bidding precedes the slot. * fix(live): clamp playhead to bounds intersection and poll near-head slots The playhead derived maxSlot from maxOfMaxes, so the fastest-moving table could drag playback into slots the slower tables had no data for. Use minOfMaxes — the newest slot every table has landed. Per-slot queries also fetched once at slot start and cached for 60s, baking whatever partial state the pipeline had (one node on the map, a lone attestation chunk) into the whole replay. Slots younger than 48s now poll every 5s until settled, then keep the cache-forever behaviour. * feat(live): PTC arrival stream and payload propagation wave The second attestation type gloas introduces — PTC payload attestations — now renders as its own arrivals chart under the committee attestation chart, fed by the new fct_payload_attestation_first_seen_chunked_50ms table and revealed progressively with the replay clock like everything else. Payload envelope sightings also join the map as a second purple-tinted wave, so the payload visibly washes over the sentry network after the block does. API client regenerated with the new endpoint. * feat(live): merge PTC stream into the arrivals chart and sidebar timeline The stacked mini-chart treatment for PTC payload attestations was cramped and unreadable. Both vote streams now share one MultiLine chart with a legend; the PTC series appears once votes actually exist, so pre-gloas networks and the first seconds of a slot stay single-series. The sidebar timeline gains the gloas events it was missing: payload envelope sightings (grouped by city like blocks) and PTC vote arrivals. Devnet sentries all sit in one datacenter city, which the sidebar rendered as a single anonymous Block row — city rows now carry their node count so one location no longer reads as one node. * fix(live): cap how far one lagging table holds playback behind head Pure minOfMaxes froze the playhead entirely when a single table's bounds trailed by minutes. Prefer the intersection but never fall more than three slots behind the freshest table — near-head slots poll and backfill as the lagging table lands. * feat(live): per-node block and payload rows in the sidebar timeline City aggregation collapsed every sentry in a datacenter into one anonymous row, which on devnets meant the whole fleet read as a single node. Block and payload sightings are now one row per node with the city as secondary text, on every network. * fix(live): location-first labels on per-node sidebar rows Per-node rows kept, but the visible label is the node's location, matching the established mainnet rendering; the node id rides along muted. * fix(live): keep per-node sidebar rows inside the row width Node ids repeated the network name and nothing in the row could shrink, so content ran off the page. The network prefix is stripped, the location stays visible, and the node id truncates with the full value on hover. * fix(live): render sidebar rows exactly as master does Back to the city-grouped location rows master ships. The only sidebar changes left are additive: payload envelope rows in the identical shape, and PTC vote rows in the attestation-row shape. * fix(forks): drop the FOCIL claim from gloas metadata EIP-7805 is not part of Glamsterdam — ePBS took the consensus headliner slot. A few leftover inclusion-list constants in the devnet config presets misled the description; the devnets expose no inclusion-list surface. * feat(slots): real gloas execution data and payload verdicts in the list The Execution tab rendered zero-filled values on gloas slots because the beacon block no longer carries the payload. Payload facts from the new fct_block_payload table (bid commitment joined with envelope contents) overlay the block's execution columns, and fields that genuinely no longer exist CL-side (gas used, base fee, block number) are hidden rather than shown as zeros. The slots list gains a Payload column on gloas networks: the PTC's verdict per slot, Delivered or Absent, with the vote tally on hover. * fix(live): show only real execution facts in the gloas HUD lane The CL block carries no payload post-gloas, so txns/gas/base/block arrive zero-filled at the head. The Execution lane now shows just the blob count on gloas networks instead of misleading zeros. * fix(slots): accept PTC votes as block evidence in payload status The proposer pipeline lags the head, so requiring it before judging the payload left slots showing Awaiting data despite a full PTC verdict. Votes and envelope sightings are themselves proof a block existed. * feat(slots): cap the bid race chart at the top ten builders One series per builder stops scaling long before the on-chain auction does. The ten strongest bidders by peak value keep individual series and the remainder collapses into a single field series carrying the best remaining bid per chunk, so hundreds of builders render as eleven lines and the fetch stays within one page. * feat(slots): drive the bid race from the auction frontier The frontier table bounds chart data by the 50ms grid regardless of builder count: the ten strongest bidders keep individual series, the frontier renders as the best-bid line when builders were cut from the top list, and if the by_builder fetch ever truncates the chart degrades to the frontier alone instead of silently lying. * feat(slots): payload propagation and PTC arrivals on the Payload tab Parity with the live view: envelope propagation across sentries reuses BlockPropagationChart and PTC vote arrivals reuse AttestationArrivalsChart, both parameterized for title and anchor so the components serve either stream. The payload data hook also exposes raw PTC chunks. * fix(slots): apply title prop on all arrivals chart render paths * feat(overview): payload delivery rate chart Hourly PTC-judged delivery outcomes from fct_block_payload_status_hourly, reusing the proposal-status chart shape (same record structure). Self-gates by data presence, so pre-gloas networks never show it. * feat(slots): payload reveal and PTC vote lanes in the timeline waterfall The trace timeline gains the two gloas phases it was missing: an envelope reveal span covering first-to-last sighting across sentries and a PTC vote span covering the vote arrival window, both fed by the payload data hook and only present post-gloas. * test(live): include intersection aggregates in bounds mock The playhead now reads minOfMaxes, which the fixture didn't provide.
1 parent 19e33f7 commit d02c7c3

60 files changed

Lines changed: 263681 additions & 128094 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/api/@tanstack/react-query.gen.ts

Lines changed: 10363 additions & 4278 deletions
Large diffs are not rendered by default.

src/api/client.gen.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import type { ClientOptions as ClientOptions2 } from './types.gen';
1313
* `setConfig()`. This is useful for example if you're using Next.js
1414
* to ensure your client always has the correct values.
1515
*/
16-
export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (override?: Config<ClientOptions & T>) => Config<Required<ClientOptions> & T>;
16+
export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (
17+
override?: Config<ClientOptions & T>
18+
) => Config<Required<ClientOptions> & T>;
1719

18-
export const client = createClient(createClientConfig(createConfig<ClientOptions2>({ baseUrl: 'https://cbt-api-mainnet.analytics.production.platform.ethpandaops.io' })));
20+
export const client = createClient(
21+
createClientConfig(createConfig<ClientOptions2>({ baseUrl: 'http://localhost:9091' }))
22+
);

src/api/client/client.gen.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export const createClient = (config: Config = {}): Client => {
6565
return { opts, url };
6666
};
6767

68-
const request: Client['request'] = async (options) => {
68+
const request: Client['request'] = async options => {
6969
// @ts-expect-error
7070
const { opts, url } = await beforeRequest(options);
7171
const requestInit: ReqInit = {
@@ -128,9 +128,7 @@ export const createClient = (config: Config = {}): Client => {
128128

129129
if (response.ok) {
130130
const parseAs =
131-
(opts.parseAs === 'auto'
132-
? getParseAs(response.headers.get('Content-Type'))
133-
: opts.parseAs) ?? 'json';
131+
(opts.parseAs === 'auto' ? getParseAs(response.headers.get('Content-Type')) : opts.parseAs) ?? 'json';
134132

135133
if (response.status === 204 || response.headers.get('Content-Length') === '0') {
136134
let emptyData: any;
@@ -234,8 +232,7 @@ export const createClient = (config: Config = {}): Client => {
234232
};
235233
};
236234

237-
const makeMethodFn = (method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
238-
request({ ...options, method });
235+
const makeMethodFn = (method: Uppercase<HttpMethod>) => (options: RequestOptions) => request({ ...options, method });
239236

240237
const makeSseFn = (method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
241238
const { opts, url } = await beforeRequest(options);

src/api/client/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22

33
export type { Auth } from '../core/auth.gen';
44
export type { QuerySerializerOptions } from '../core/bodySerializer.gen';
5-
export {
6-
formDataBodySerializer,
7-
jsonBodySerializer,
8-
urlSearchParamsBodySerializer,
9-
} from '../core/bodySerializer.gen';
5+
export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer } from '../core/bodySerializer.gen';
106
export { buildClientParams } from '../core/params.gen';
117
export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen';
128
export { createClient } from './client.gen';

src/api/client/types.gen.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
// This file is auto-generated by @hey-api/openapi-ts
22

33
import type { Auth } from '../core/auth.gen';
4-
import type {
5-
ServerSentEventsOptions,
6-
ServerSentEventsResult,
7-
} from '../core/serverSentEvents.gen';
4+
import type { ServerSentEventsOptions, ServerSentEventsResult } from '../core/serverSentEvents.gen';
85
import type { Client as CoreClient, Config as CoreConfig } from '../core/types.gen';
96
import type { Middleware } from './utils.gen';
107

@@ -66,11 +63,7 @@ export interface RequestOptions<
6663
}>,
6764
Pick<
6865
ServerSentEventsOptions<TData>,
69-
| 'onSseError'
70-
| 'onSseEvent'
71-
| 'sseDefaultRetryDelay'
72-
| 'sseMaxRetryAttempts'
73-
| 'sseMaxRetryDelay'
66+
'onSseError' | 'onSseEvent' | 'sseDefaultRetryDelay' | 'sseMaxRetryAttempts' | 'sseMaxRetryDelay'
7467
> {
7568
/**
7669
* Any body that you want to add to your request.
@@ -142,7 +135,7 @@ type MethodFn = <
142135
ThrowOnError extends boolean = false,
143136
TResponseStyle extends ResponseStyle = 'fields',
144137
>(
145-
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'>,
138+
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'>
146139
) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>;
147140

148141
type SseFn = <
@@ -151,7 +144,7 @@ type SseFn = <
151144
ThrowOnError extends boolean = false,
152145
TResponseStyle extends ResponseStyle = 'fields',
153146
>(
154-
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'>,
147+
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'>
155148
) => Promise<ServerSentEventsResult<TData, TError>>;
156149

157150
type RequestFn = <
@@ -161,7 +154,7 @@ type RequestFn = <
161154
TResponseStyle extends ResponseStyle = 'fields',
162155
>(
163156
options: Omit<RequestOptions<TData, TResponseStyle, ThrowOnError>, 'method'> &
164-
Pick<Required<RequestOptions<TData, TResponseStyle, ThrowOnError>>, 'method'>,
157+
Pick<Required<RequestOptions<TData, TResponseStyle, ThrowOnError>>, 'method'>
165158
) => RequestResult<TData, TError, ThrowOnError, TResponseStyle>;
166159

167160
type BuildUrlFn = <
@@ -172,7 +165,7 @@ type BuildUrlFn = <
172165
url: string;
173166
},
174167
>(
175-
options: TData & Options<TData>,
168+
options: TData & Options<TData>
176169
) => string;
177170

178171
export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn, SseFn> & {
@@ -188,7 +181,7 @@ export type Client = CoreClient<RequestFn, Config, MethodFn, BuildUrlFn, SseFn>
188181
* to ensure your client always has the correct values.
189182
*/
190183
export type CreateClientConfig<T extends ClientOptions = ClientOptions> = (
191-
override?: Config<ClientOptions & T>,
184+
override?: Config<ClientOptions & T>
192185
) => Config<Required<ClientOptions> & T>;
193186

194187
export interface TDataShape {
@@ -206,8 +199,5 @@ export type Options<
206199
ThrowOnError extends boolean = boolean,
207200
TResponse = unknown,
208201
TResponseStyle extends ResponseStyle = 'fields',
209-
> = OmitKeys<
210-
RequestOptions<TResponse, TResponseStyle, ThrowOnError>,
211-
'body' | 'path' | 'query' | 'url'
212-
> &
202+
> = OmitKeys<RequestOptions<TResponse, TResponseStyle, ThrowOnError>, 'body' | 'path' | 'query' | 'url'> &
213203
([TData] extends [never] ? unknown : Omit<TData, 'url'>);

src/api/client/utils.gen.ts

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,11 @@
33
import { getAuthToken } from '../core/auth.gen';
44
import type { QuerySerializerOptions } from '../core/bodySerializer.gen';
55
import { jsonBodySerializer } from '../core/bodySerializer.gen';
6-
import {
7-
serializeArrayParam,
8-
serializeObjectParam,
9-
serializePrimitiveParam,
10-
} from '../core/pathSerializer.gen';
6+
import { serializeArrayParam, serializeObjectParam, serializePrimitiveParam } from '../core/pathSerializer.gen';
117
import { getUrl } from '../core/utils.gen';
128
import type { Client, ClientOptions, Config, RequestOptions } from './types.gen';
139

14-
export const createQuerySerializer = <T = unknown>({
15-
parameters = {},
16-
...args
17-
}: QuerySerializerOptions = {}) => {
10+
export const createQuerySerializer = <T = unknown>({ parameters = {}, ...args }: QuerySerializerOptions = {}) => {
1811
const querySerializer = (queryParams: T) => {
1912
const search: string[] = [];
2013
if (queryParams && typeof queryParams === 'object') {
@@ -86,9 +79,7 @@ export const getParseAs = (contentType: string | null): Exclude<Config['parseAs'
8679
return 'formData';
8780
}
8881

89-
if (
90-
['application/', 'audio/', 'image/', 'video/'].some((type) => cleanContent.startsWith(type))
91-
) {
82+
if (['application/', 'audio/', 'image/', 'video/'].some(type => cleanContent.startsWith(type))) {
9283
return 'blob';
9384
}
9485

@@ -103,16 +94,12 @@ const checkForExistence = (
10394
options: Pick<RequestOptions, 'auth' | 'query'> & {
10495
headers: Headers;
10596
},
106-
name?: string,
97+
name?: string
10798
): boolean => {
10899
if (!name) {
109100
return false;
110101
}
111-
if (
112-
options.headers.has(name) ||
113-
options.query?.[name] ||
114-
options.headers.get('Cookie')?.includes(`${name}=`)
115-
) {
102+
if (options.headers.has(name) || options.query?.[name] || options.headers.get('Cookie')?.includes(`${name}=`)) {
116103
return true;
117104
}
118105
return false;
@@ -156,7 +143,7 @@ export const setAuthParams = async ({
156143
}
157144
};
158145

159-
export const buildUrl: Client['buildUrl'] = (options) =>
146+
export const buildUrl: Client['buildUrl'] = options =>
160147
getUrl({
161148
baseUrl: options.baseUrl as string,
162149
path: options.path,
@@ -185,9 +172,7 @@ const headersEntries = (headers: Headers): Array<[string, string]> => {
185172
return entries;
186173
};
187174

188-
export const mergeHeaders = (
189-
...headers: Array<Required<Config>['headers'] | undefined>
190-
): Headers => {
175+
export const mergeHeaders = (...headers: Array<Required<Config>['headers'] | undefined>): Headers => {
191176
const mergedHeaders = new Headers();
192177
for (const header of headers) {
193178
if (!header) {
@@ -206,10 +191,7 @@ export const mergeHeaders = (
206191
} else if (value !== undefined) {
207192
// assume object headers are meant to be JSON stringified, i.e. their
208193
// content value in OpenAPI specification is 'application/json'
209-
mergedHeaders.set(
210-
key,
211-
typeof value === 'object' ? JSON.stringify(value) : (value as string),
212-
);
194+
mergedHeaders.set(key, typeof value === 'object' ? JSON.stringify(value) : (value as string));
213195
}
214196
}
215197
}
@@ -220,16 +202,12 @@ type ErrInterceptor<Err, Res, Req, Options> = (
220202
error: Err,
221203
response: Res,
222204
request: Req,
223-
options: Options,
205+
options: Options
224206
) => Err | Promise<Err>;
225207

226208
type ReqInterceptor<Req, Options> = (request: Req, options: Options) => Req | Promise<Req>;
227209

228-
type ResInterceptor<Res, Req, Options> = (
229-
response: Res,
230-
request: Req,
231-
options: Options,
232-
) => Res | Promise<Res>;
210+
type ResInterceptor<Res, Req, Options> = (response: Res, request: Req, options: Options) => Res | Promise<Res>;
233211

234212
class Interceptors<Interceptor> {
235213
fns: Array<Interceptor | null> = [];
@@ -278,12 +256,7 @@ export interface Middleware<Req, Res, Err, Options> {
278256
response: Interceptors<ResInterceptor<Res, Req, Options>>;
279257
}
280258

281-
export const createInterceptors = <Req, Res, Err, Options>(): Middleware<
282-
Req,
283-
Res,
284-
Err,
285-
Options
286-
> => ({
259+
export const createInterceptors = <Req, Res, Err, Options>(): Middleware<Req, Res, Err, Options> => ({
287260
error: new Interceptors<ErrInterceptor<Err, Res, Req, Options>>(),
288261
request: new Interceptors<ReqInterceptor<Req, Options>>(),
289262
response: new Interceptors<ResInterceptor<Res, Req, Options>>(),
@@ -306,7 +279,7 @@ const defaultHeaders = {
306279
};
307280

308281
export const createConfig = <T extends ClientOptions = ClientOptions>(
309-
override: Config<Omit<ClientOptions, keyof T> & T> = {},
282+
override: Config<Omit<ClientOptions, keyof T> & T> = {}
310283
): Config<Omit<ClientOptions, keyof T> & T> => ({
311284
...jsonBodySerializer,
312285
headers: defaultHeaders,

src/api/core/auth.gen.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export interface Auth {
2121

2222
export const getAuthToken = async (
2323
auth: Auth,
24-
callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken,
24+
callback: ((auth: Auth) => Promise<AuthToken> | AuthToken) | AuthToken
2525
): Promise<string | undefined> => {
2626
const token = typeof callback === 'function' ? await callback(auth) : callback;
2727

src/api/core/bodySerializer.gen.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,15 @@ const serializeUrlSearchParamsPair = (data: URLSearchParams, key: string, value:
3939
};
4040

4141
export const formDataBodySerializer = {
42-
bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(
43-
body: T,
44-
): FormData => {
42+
bodySerializer: <T extends Record<string, any> | Array<Record<string, any>>>(body: T): FormData => {
4543
const data = new FormData();
4644

4745
Object.entries(body).forEach(([key, value]) => {
4846
if (value === undefined || value === null) {
4947
return;
5048
}
5149
if (Array.isArray(value)) {
52-
value.forEach((v) => serializeFormDataPair(data, key, v));
50+
value.forEach(v => serializeFormDataPair(data, key, v));
5351
} else {
5452
serializeFormDataPair(data, key, value);
5553
}
@@ -73,7 +71,7 @@ export const urlSearchParamsBodySerializer = {
7371
return;
7472
}
7573
if (Array.isArray(value)) {
76-
value.forEach((v) => serializeUrlSearchParamsPair(data, key, v));
74+
value.forEach(v => serializeUrlSearchParamsPair(data, key, v));
7775
} else {
7876
serializeUrlSearchParamsPair(data, key, value);
7977
}

src/api/core/pathSerializer.gen.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ export const serializeArrayParam = ({
7474
value: unknown[];
7575
}) => {
7676
if (!explode) {
77-
const joinedValues = (
78-
allowReserved ? value : value.map((v) => encodeURIComponent(v as string))
79-
).join(separatorArrayNoExplode(style));
77+
const joinedValues = (allowReserved ? value : value.map(v => encodeURIComponent(v as string))).join(
78+
separatorArrayNoExplode(style)
79+
);
8080
switch (style) {
8181
case 'label':
8282
return `.${joinedValues}`;
@@ -91,7 +91,7 @@ export const serializeArrayParam = ({
9191

9292
const separator = separatorArrayExplode(style);
9393
const joinedValues = value
94-
.map((v) => {
94+
.map(v => {
9595
if (style === 'label' || style === 'simple') {
9696
return allowReserved ? v : encodeURIComponent(v as string);
9797
}
@@ -106,18 +106,14 @@ export const serializeArrayParam = ({
106106
return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues;
107107
};
108108

109-
export const serializePrimitiveParam = ({
110-
allowReserved,
111-
name,
112-
value,
113-
}: SerializePrimitiveParam) => {
109+
export const serializePrimitiveParam = ({ allowReserved, name, value }: SerializePrimitiveParam) => {
114110
if (value === undefined || value === null) {
115111
return '';
116112
}
117113

118114
if (typeof value === 'object') {
119115
throw new Error(
120-
'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.',
116+
'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.'
121117
);
122118
}
123119

@@ -164,7 +160,7 @@ export const serializeObjectParam = ({
164160
allowReserved,
165161
name: style === 'deepObject' ? `${name}[${key}]` : key,
166162
value: v as string,
167-
}),
163+
})
168164
)
169165
.join(separator);
170166
return style === 'label' || style === 'matrix' ? separator + joinedValues : joinedValues;

src/api/core/queryKeySerializer.gen.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@
33
/**
44
* JSON-friendly union that mirrors what Pinia Colada can hash.
55
*/
6-
export type JsonValue =
7-
| null
8-
| string
9-
| number
10-
| boolean
11-
| JsonValue[]
12-
| { [key: string]: JsonValue };
6+
export type JsonValue = null | string | number | boolean | JsonValue[] | { [key: string]: JsonValue };
137

148
/**
159
* Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes.

0 commit comments

Comments
 (0)