@ethlete/query-devtools 1.0.0-next.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.
@@ -0,0 +1,1777 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { Signal, WritableSignal, OnInit } from '@angular/core';
3
+ import * as _ethlete_query from '@ethlete/query';
4
+ import { JsonPath, QueryDevtoolsOverridesRecorder, QueryRefreshCause, QueryRepositoryEntryDestroyedCause, Query, QueryRepositoryCacheEntry, HttpRequestRetryState, HttpRequestLoadingProgressState, QueryDevtoolsStats, QueryDevtoolsStatsHandle, QueryDevtoolsEntry, QueryRepository, QueryClient, QueryKeyLockState, QueryDevtoolsFeature, QueryDevtoolsFault, AnyQueryStack, AnyPagedQueryStack, QuerySequence, AnyBearerAuthProvider, WebSocketDevtoolsHandle, QueryDevtoolsFormHandle, QuerySequenceStatus, AnyQuerySnapshot, QueryDevtoolsRun, QueryDevtoolsRunError, WebSocketDevtoolsMessage, QueryDevtoolsStorageScope } from '@ethlete/query';
5
+ import { ResizeEdge, DragMoveEvent, ResizeMoveEvent } from '@ethlete/core';
6
+
7
+ type AboutRow = {
8
+ label: string;
9
+ value: string;
10
+ };
11
+ type AboutGroup = {
12
+ title: string;
13
+ rows: AboutRow[];
14
+ };
15
+ /**
16
+ * What is running: the loaded `@ethlete/*` versions, the Angular version and whatever the app handed to
17
+ * `provideQueryDevtools({ about })`. A section rather than a tab body, so it can be shown anywhere.
18
+ */
19
+ declare class QueryDevtoolsAboutComponent {
20
+ protected readonly groups: AboutGroup[];
21
+ protected copied: _angular_core.WritableSignal<boolean>;
22
+ protected copy(): void;
23
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<QueryDevtoolsAboutComponent, never>;
24
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<QueryDevtoolsAboutComponent, "et-query-devtools-about", never, {}, {}, never, never, true, never>;
25
+ }
26
+
27
+ /** Which of a node's four pasteable forms a copy action asks for. */
28
+ type QueryDevtoolsCopyPayload = 'value' | 'key' | 'path' | 'entry';
29
+
30
+ /** A JSON value's kind as the value explorer (and its per-node override menu) categorizes it. */
31
+ type JsonKind = 'string' | 'number' | 'boolean' | 'null' | 'undefined' | 'array' | 'object';
32
+ type JsonEntry = {
33
+ k: string;
34
+ v: unknown;
35
+ };
36
+ /** A folded slice of a container's entries, rendered as its own collapsible row. */
37
+ type JsonChunk = {
38
+ start: number;
39
+ end: number;
40
+ label: string;
41
+ };
42
+ declare const kindOf: (value: unknown) => JsonKind;
43
+ /**
44
+ * A recursive, collapsible, searchable JSON tree used by the query devtools value explorer. Renders
45
+ * the *transformed* value the app actually sees (post `transformResponse`). Self-recurses via its
46
+ * own selector - both for child entries and for the folded slices of an oversized container, which
47
+ * re-enter as the same component with a `chunk` window over the same value.
48
+ */
49
+ declare class QueryDevtoolsJsonComponent {
50
+ value: _angular_core.InputSignal<unknown>;
51
+ nodeKey: _angular_core.InputSignal<string | null>;
52
+ depth: _angular_core.InputSignalWithTransform<number, unknown>;
53
+ /** Lowercased search term; when set, the tree auto-expands and matches are highlighted. */
54
+ search: _angular_core.InputSignal<string>;
55
+ /**
56
+ * When set, this node is not the value itself but a folded window over its entries. It shares the
57
+ * container's `path` so a child's persisted expansion does not depend on where the slice borders land.
58
+ */
59
+ chunk: _angular_core.InputSignal<JsonChunk | null>;
60
+ /** Full path of this node from the explorer root, used as the key for persisted expansion. */
61
+ path: _angular_core.InputSignal<string>;
62
+ /** Persisted paths explicitly expanded (overrides the depth default). */
63
+ expandedPaths: _angular_core.InputSignal<ReadonlySet<string> | null>;
64
+ /** Persisted paths explicitly collapsed (overrides the depth default). */
65
+ collapsedPaths: _angular_core.InputSignal<ReadonlySet<string> | null>;
66
+ /** When provided, expansion is externally persisted via this callback instead of local state. */
67
+ toggleFn: _angular_core.InputSignal<((path: string, expand: boolean) => void) | null>;
68
+ /** Same node as {@link path}, but as the array-of-steps an override op targets rather than a display key. */
69
+ jsonPath: _angular_core.InputSignal<JsonPath>;
70
+ /** The kind of the container holding this node, so its override menu can offer "duplicate this item". */
71
+ parentKind: _angular_core.InputSignal<JsonKind | null>;
72
+ /** When provided, every node renders an override menu that arms/clears ops through it. */
73
+ overrides: _angular_core.InputSignal<QueryDevtoolsOverridesRecorder | null>;
74
+ /**
75
+ * The declared type of each field, keyed by its path with array indices as `*` (`items.*.id`) - as
76
+ * {@link QueryDevtoolsSchemaSeed} produces it. A node with an entry labels itself with it.
77
+ */
78
+ annotations: _angular_core.InputSignal<ReadonlyMap<string, string> | null>;
79
+ protected kind: _angular_core.Signal<JsonKind>;
80
+ private exotic;
81
+ /** One annotation covers every element of an array, so the lookup path forgets which index this is. */
82
+ private shapePath;
83
+ protected annotation: _angular_core.Signal<string | null>;
84
+ protected annotationTitle: _angular_core.Signal<string>;
85
+ /** A `Date` or a `File` is object-typed but has nothing to expand into, so it renders as a leaf. */
86
+ protected isContainer: _angular_core.Signal<boolean>;
87
+ /** The entries this node covers: the container's own, or just the window a chunk stands for. */
88
+ ownEntries: _angular_core.Signal<JsonEntry[]>;
89
+ protected childChunks: _angular_core.Signal<JsonChunk[]>;
90
+ /** Entries rendered as rows - empty while they are folded into slices instead. */
91
+ protected visibleEntries: _angular_core.Signal<JsonEntry[]>;
92
+ /** Slices carry their window in the key so siblings persist independently of the container. */
93
+ private togglePath;
94
+ private defaultExpanded;
95
+ private localExpanded;
96
+ protected expanded: _angular_core.Signal<boolean>;
97
+ /** Only slices that actually contain a match unfold while searching, so a filter stays cheap. */
98
+ private chunkHasHit;
99
+ protected effectiveExpanded: _angular_core.Signal<boolean>;
100
+ /** What the last copy put on the clipboard, or `null` once the tick has expired. */
101
+ protected copied: _angular_core.WritableSignal<QueryDevtoolsCopyPayload | null>;
102
+ private copiedReset$;
103
+ /**
104
+ * Whether this node has an address of its own to copy. An explorer root has no key, and a folded
105
+ * slice stands for a range of entries rather than one path - neither can name itself.
106
+ */
107
+ protected addressable: _angular_core.Signal<boolean>;
108
+ private valueLabel;
109
+ /**
110
+ * Doubles as the button's `title` and its `aria-label`, and names what landed while the tick is up -
111
+ * a bare `✓` stopped being unambiguous once the menu put four payloads behind one control.
112
+ */
113
+ protected copyLabel: _angular_core.Signal<string>;
114
+ protected preview: _angular_core.Signal<string>;
115
+ protected display: _angular_core.Signal<string>;
116
+ protected keyHit: _angular_core.Signal<boolean>;
117
+ protected valueHit: _angular_core.Signal<boolean>;
118
+ constructor();
119
+ protected childPath(key: string): string;
120
+ protected childJsonPath(key: string): JsonPath;
121
+ protected toggle(): void;
122
+ /**
123
+ * Writes one of the node's four pasteable forms. All of them go through here so the tick stays a
124
+ * single readout of what actually landed.
125
+ */
126
+ protected copy(payload: QueryDevtoolsCopyPayload): void;
127
+ /**
128
+ * Containers copy their whole subtree as JSON, slices only the entries they cover; leaves copy
129
+ * something pasteable - a raw string without the display quotes, so an id or url can go straight
130
+ * into a search box. `null` for a subtree that cannot be serialized at all.
131
+ */
132
+ private copyTextFor;
133
+ /** An exotic container copies the entries the tree shows, not the private fields `JSON.stringify` finds. */
134
+ private copyableValue;
135
+ private chunkValue;
136
+ private flagCopied;
137
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<QueryDevtoolsJsonComponent, never>;
138
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<QueryDevtoolsJsonComponent, "et-query-devtools-json", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "nodeKey": { "alias": "nodeKey"; "required": false; "isSignal": true; }; "depth": { "alias": "depth"; "required": false; "isSignal": true; }; "search": { "alias": "search"; "required": false; "isSignal": true; }; "chunk": { "alias": "chunk"; "required": false; "isSignal": true; }; "path": { "alias": "path"; "required": false; "isSignal": true; }; "expandedPaths": { "alias": "expandedPaths"; "required": false; "isSignal": true; }; "collapsedPaths": { "alias": "collapsedPaths"; "required": false; "isSignal": true; }; "toggleFn": { "alias": "toggleFn"; "required": false; "isSignal": true; }; "jsonPath": { "alias": "jsonPath"; "required": false; "isSignal": true; }; "parentKind": { "alias": "parentKind"; "required": false; "isSignal": true; }; "overrides": { "alias": "overrides"; "required": false; "isSignal": true; }; "annotations": { "alias": "annotations"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
139
+ }
140
+
141
+ /**
142
+ * The value explorer's tree chrome, as a styles-only component the tree mounts itself - a session that
143
+ * never opens a JSON view does not inject it.
144
+ *
145
+ * @internal
146
+ */
147
+ declare class QueryDevtoolsJsonStylesComponent {
148
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<QueryDevtoolsJsonStylesComponent, never>;
149
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<QueryDevtoolsJsonStylesComponent, "et-query-devtools-json-styles", never, {}, {}, never, never, true, never>;
150
+ }
151
+
152
+ /** Whether a path exists only in the newer value, only in the older one, or in both with a new value. */
153
+ type QueryDevtoolsDiffKind = 'added' | 'removed' | 'changed';
154
+ /** One difference between two values, located by the path it sits at. */
155
+ type QueryDevtoolsDiffEntry = {
156
+ /** Where the difference sits, e.g. `$.items[2].score` or `$.items[id=7].score`. */
157
+ path: string;
158
+ kind: QueryDevtoolsDiffKind;
159
+ /** The older value, or `null` for an added path. */
160
+ before: unknown;
161
+ /** The newer value, or `null` for a removed path. */
162
+ after: unknown;
163
+ };
164
+ type QueryDevtoolsDiff = {
165
+ entries: QueryDevtoolsDiffEntry[];
166
+ /** Whether the walk hit its cap, so {@link entries} is only part of the difference. */
167
+ truncated: boolean;
168
+ };
169
+
170
+ type AnyQuery = Query<any>;
171
+ /** The query a detail pane is showing, together with the registry entry it was registered as. */
172
+ type QueryDevtoolsSelection = {
173
+ entry: QueryDevtoolsEntry;
174
+ query: AnyQuery;
175
+ };
176
+ /**
177
+ * A body the panel can show. Every one of them but `settings` is a tab in the bar: Settings holds nothing
178
+ * to count, so the badge/overflow logic would push it behind **More** - it is reached from the header's
179
+ * gear instead.
180
+ */
181
+ type DevtoolsTab = 'queries' | 'stacks' | 'sequences' | 'forms' | 'auth' | 'ws' | 'cache' | 'timeline' | 'events' | 'faults' | 'mocks' | 'about' | 'settings';
182
+ /**
183
+ * The sections of the query detail drawer. The head and its actions stay pinned above them - the detail
184
+ * holds more than fits a column, and everything below the actions is reading material.
185
+ */
186
+ type DetailTab = 'overview' | 'history' | 'data';
187
+ /**
188
+ * Which pane of a two-pane tab a divider drag sizes: the Queries tab's list, or the drawer every
189
+ * split view opens a query in.
190
+ */
191
+ type PaneTarget = 'list' | 'drawer';
192
+ /** The axis a two-pane tab splits along: the panes sit side by side, or stack in a side dock. */
193
+ type PaneAxis = 'inline' | 'block';
194
+ type QueryStatus = 'idle' | 'loading' | 'success' | 'error';
195
+ /**
196
+ * A facet the Queries list can be narrowed to. The first four describe live state; `gone` is the odd
197
+ * one out - it is the only way a destroyed query's tombstone enters the list at all, which is why it
198
+ * is off by default rather than just another filter over what is already shown.
199
+ */
200
+ type QueryListFacet = 'error' | 'loading' | 'stale' | 'idle' | 'gone';
201
+ /**
202
+ * A chunk of a route as rendered: literal path text, a path param (`name` is the param it fills in) or
203
+ * the query string of the request that ran. The kind becomes the segment's class.
204
+ */
205
+ type RouteSegment = {
206
+ text: string;
207
+ kind: 'static' | 'param' | 'query';
208
+ name?: string;
209
+ };
210
+ /**
211
+ * Which tab refreshes an auth provider's tokens, as the auth tab's chip renders it. `title` carries
212
+ * the caveats the label has no room for - why every tab reads as the leader, or how approximate the
213
+ * tab count is.
214
+ */
215
+ type QueryDevtoolsLeadership = {
216
+ label: string;
217
+ tone: 'success' | 'muted';
218
+ title: string;
219
+ };
220
+ /** An auth provider's access-token expiry as the auth tab renders it, armed override included. */
221
+ type QueryDevtoolsTokenLifetime = {
222
+ /** Countdown to the expiry the app currently acts on, overridden or not. */
223
+ expiresIn: string | null;
224
+ /** Countdown to the token's own `exp`, and only set while an override is making it read differently. */
225
+ realExpiresIn: string | null;
226
+ /** The armed lifetime in seconds, or `null` while the token is on the one it was issued with. */
227
+ ttlSeconds: number | null;
228
+ /** Whether an override can be applied at all - the token needs an `exp` plus an `iat` or `nbf`. */
229
+ overridable: boolean;
230
+ };
231
+ /** A query reachable from a stack or sequence card, rendered as a row that opens the detail drawer. */
232
+ type QueryLink = {
233
+ id: string;
234
+ query: AnyQuery;
235
+ method: string;
236
+ segments: RouteSegment[];
237
+ clientBaseUrl: string;
238
+ stats?: QueryDevtoolsStatsHandle;
239
+ };
240
+ /**
241
+ * Query stats plus the numbers the panel derives from them rather than storing: an execution that never
242
+ * reached the network was answered from the cache, and the averages come from the running totals.
243
+ */
244
+ type QueryActivity = {
245
+ stats: QueryDevtoolsStats;
246
+ cacheServed: number;
247
+ avgDurationMs: number | null;
248
+ avgResponseBytes: number | null;
249
+ hasActivity: boolean;
250
+ };
251
+ /**
252
+ * What a query's request is doing beyond `loading`: which attempt it is on, the backoff it is waiting
253
+ * out, and how much of the payload has moved. Without it a request retried three times behind a 4s
254
+ * backoff and a plain slow one are the same yellow dot.
255
+ */
256
+ type RequestProgress = {
257
+ /** @see HttpRequestSubtle.attempts */
258
+ attempts: number;
259
+ retry: HttpRequestRetryState | null;
260
+ /** How much of the pending backoff is left, or `null` when none is pending. */
261
+ retryInMs: number | null;
262
+ progress: HttpRequestLoadingProgressState | null;
263
+ };
264
+ /**
265
+ * One request a refresh re-executed, as an event row lists it. A cache key is shared by every query
266
+ * bound to it, so the row carries all of their ids - one to open, and the rest so each of those queries
267
+ * can still find the refresh under "Refetched by". Empty for a request no registered query holds.
268
+ */
269
+ type RefreshedRequest = {
270
+ queryIds: string[];
271
+ method: string;
272
+ path: string;
273
+ };
274
+ type EventLogItem = {
275
+ id: number;
276
+ timestamp: number;
277
+ client: string;
278
+ type: 'entry-created' | 'request-success' | 'request-error' | 'entry-destroyed' | 'unbind-all-secure' | 'queries-refreshed';
279
+ /** `null` for events that are not about a single request, e.g. the logout-wide secure unbind. */
280
+ method: string | null;
281
+ url: string | null;
282
+ isSecure: boolean;
283
+ status: number | null;
284
+ /**
285
+ * The registered query the request belonged to when the event fired, so the row can open it. Resolved
286
+ * here rather than at click time so the log holds an id instead of a reference to the request itself.
287
+ */
288
+ queryId: string | null;
289
+ /** What asked for a refresh, for a `queries-refreshed` row. `null` for every other type. */
290
+ cause: QueryRefreshCause | null;
291
+ /** Why a cache entry was torn down, for an `entry-destroyed` row. `null` for every other type. */
292
+ destroyCause: QueryRepositoryEntryDestroyedCause | null;
293
+ /** The requests that refresh re-executed - the fan-out the panel could not show before. */
294
+ refreshed: RefreshedRequest[] | null;
295
+ /**
296
+ * How long the request took and how big its response was, read off the request as the event fired.
297
+ * Both `null` for an event that is not one request settling, and the size also for a failure - an
298
+ * error body is not a payload worth a column.
299
+ */
300
+ durationMs: number | null;
301
+ bytes: number | null;
302
+ /** @see QueryDevtoolsStats.hasEstimatedBytes */
303
+ isEstimatedBytes: boolean;
304
+ };
305
+ /**
306
+ * A cache entry that is gone, as the Cache tab still lists it. Deliberately not a {@link CacheRow}: a
307
+ * destroyed entry has no consumers, no size, no freshness and nothing to act on, so a full row would be
308
+ * seven columns of dashes. What is left worth showing is what it was and why it went.
309
+ */
310
+ type DroppedCacheEntry = {
311
+ client: string;
312
+ method: string;
313
+ url: string;
314
+ cause: QueryRepositoryEntryDestroyedCause;
315
+ at: number;
316
+ };
317
+ /** One cache entry as the Cache tab lists it: the repository's own snapshot plus its measured size. */
318
+ type CacheRow = {
319
+ entry: QueryRepositoryCacheEntry;
320
+ bytes: number;
321
+ isEstimatedBytes: boolean;
322
+ };
323
+ /** A repository the panel found, together with the client name/base URL it was registered under. */
324
+ type RepositoryInfo = {
325
+ repository: QueryRepository;
326
+ name: string;
327
+ baseUrl: string;
328
+ client: QueryClient | null;
329
+ };
330
+ /** One client's cache, as the Cache tab lists it - every entry measured, with the client's poll states. */
331
+ type CacheView = {
332
+ name: string;
333
+ baseUrl: string;
334
+ repository: QueryRepository;
335
+ rows: CacheRow[];
336
+ bytes: number;
337
+ isEstimatedBytes: boolean;
338
+ unused: number;
339
+ pollStates: Record<string, QueryKeyLockState>;
340
+ client: QueryClient | null;
341
+ /** The entries this client has lost since the panel was loaded, newest first. */
342
+ dropped: DroppedCacheEntry[];
343
+ };
344
+ /** What each tab holds, as its badge reports it: how many entries, and how many of them are failing. */
345
+ type TabBadge = {
346
+ count: number;
347
+ errors: number;
348
+ errorNoun?: string;
349
+ };
350
+
351
+ /**
352
+ * Everything a tab or the shared detail drawer reads from - or writes back into - the always-mounted
353
+ * `<et-query-devtools>` panel: cross-tab registries, the JIT editor, copy/export actions, the value
354
+ * explorer and pane sizing. A tab component only ever exists while it is the active one, so anything a
355
+ * tab needs to survive being switched away from (a drawer's own selection, the persisted panel chrome)
356
+ * has to live here instead of on the tab itself. Every member below is named and shaped exactly like the
357
+ * panel's own field/method it stands in for.
358
+ *
359
+ * @see injectQueryDevtoolsHost
360
+ */
361
+ type QueryDevtoolsHost = {
362
+ selectTab(tab: DevtoolsTab): void;
363
+ /** Jumps to the Queries tab with this query selected - the Events tab is a way in, not a dead end. */
364
+ selectQuery(id: string): void;
365
+ /** Jumps to the Forms tab with this form selected. */
366
+ selectForm(id: string): void;
367
+ queryEntries: Signal<QueryDevtoolsEntry[]>;
368
+ stackEntries: Signal<QueryDevtoolsEntry[]>;
369
+ sequenceEntries: Signal<QueryDevtoolsEntry[]>;
370
+ formEntries: Signal<QueryDevtoolsEntry[]>;
371
+ authEntries: Signal<QueryDevtoolsEntry[]>;
372
+ wsEntries: Signal<QueryDevtoolsEntry[]>;
373
+ repositories: Signal<RepositoryInfo[]>;
374
+ /**
375
+ * The cache per client, with every entry's size measured. Reading each response inside the computed is
376
+ * what keeps the totals current: a cache mutation bumps `cacheVersion`, but a response landing in an
377
+ * entry that is already there does not.
378
+ */
379
+ cacheView: Signal<CacheView[]>;
380
+ refetchCacheEntry(entry: QueryRepositoryCacheEntry): void;
381
+ evictCacheEntry(repository: QueryRepository, key: string): void;
382
+ /** Drops every entry of one client, consumers included - the cold-start check that does not need a reload. */
383
+ evictAllCacheEntries(repository: QueryRepository): void;
384
+ toggleCacheValue(clientName: string, key: string): void;
385
+ isCacheValueExpanded(clientName: string, key: string): boolean;
386
+ cacheFreshness(entry: QueryRepositoryCacheEntry): string;
387
+ cacheSync(entry: QueryRepositoryCacheEntry, pollStates: Record<string, unknown>): string;
388
+ cachePersistence(entry: QueryRepositoryCacheEntry): string;
389
+ /** How many responses this client has on disk, which is usually more than it has in memory. */
390
+ persistedCount(client: QueryClient): number;
391
+ clearPersistedQueries(client: QueryClient): void;
392
+ /** The features of the client behind a cache tab card, or `null` for a client without any. */
393
+ clientFeatures(client: QueryClient | null | undefined): QueryDevtoolsFeature[] | null;
394
+ /** Unique client names present across queries and auth providers, for the Queries/Timeline pickers. */
395
+ clientNames: Signal<string[]>;
396
+ /** What each tab holds - also drives the tab bar's own badges, so it lives here rather than per-tab. */
397
+ tabBadges: Signal<Record<DevtoolsTab, TabBadge>>;
398
+ /**
399
+ * Every client the Faults tab can arm, with the fault it currently carries. Also feeds the
400
+ * cross-tab "Faults armed" banner and the session export, so it lives here rather than on the tab.
401
+ */
402
+ faultClients: Signal<{
403
+ name: string;
404
+ baseUrl: string;
405
+ fault: QueryDevtoolsFault;
406
+ armed: boolean;
407
+ }[]>;
408
+ /**
409
+ * The queries in scope before the Queries tab's own search box and status chips narrow them further:
410
+ * either the picked client's, or exactly the inspected element's. Also what the Timeline tab scopes
411
+ * its runs to, which is why this lives here rather than on the Queries tab alone.
412
+ */
413
+ scopedQueries: Signal<QueryDevtoolsEntry[]>;
414
+ /** 1-second tick driving every countdown/freshness readout across tabs. */
415
+ clock: Signal<number>;
416
+ findQuery(id: string | null): QueryDevtoolsSelection | null;
417
+ queryLinkFor(entry: QueryDevtoolsEntry | undefined, query?: AnyQuery): QueryLink;
418
+ asStack(entry: QueryDevtoolsEntry): AnyQueryStack;
419
+ asPagedStack(entry: QueryDevtoolsEntry): AnyPagedQueryStack;
420
+ asSequence(entry: QueryDevtoolsEntry): QuerySequence<unknown[]>;
421
+ asAuth(entry: QueryDevtoolsEntry): AnyBearerAuthProvider;
422
+ asWs(entry: QueryDevtoolsEntry): WebSocketDevtoolsHandle;
423
+ asForm(entry: QueryDevtoolsEntry): QueryDevtoolsFormHandle;
424
+ authTokenPayload(auth: AnyBearerAuthProvider): Record<string, unknown> | null;
425
+ authQueryKeys(auth: AnyBearerAuthProvider): string[];
426
+ /** The access-token expiry the app acts on, plus the lifetime the panel has armed over it. */
427
+ authTokenLifetime(entry: QueryDevtoolsEntry): QueryDevtoolsTokenLifetime;
428
+ /** The multi-tab leadership chip, or `null` for a provider without `withBearerAuthMultiTabSync`. */
429
+ authLeadership(auth: AnyBearerAuthProvider): QueryDevtoolsLeadership | null;
430
+ queryStatus(query: AnyQuery): 'idle' | 'loading' | 'success' | 'error';
431
+ isStale(query: AnyQuery): boolean;
432
+ /** Whether an entry is showing an armed response override or a devtools-faulted outcome. */
433
+ isTampered(entry: QueryDevtoolsEntry): boolean;
434
+ /** Whether armed response overrides are stored and replayed on the next load. */
435
+ overridesPersist(): boolean;
436
+ toggleOverridesPersist(): void;
437
+ /** Where they are kept, named for a title or a hint (`sessionStorage`, `localStorage`, `not kept`). */
438
+ overridesScopeLabel(): string;
439
+ /** Puts the panel's layout, filters, pins and stored overrides back to defaults, keeping the settings. */
440
+ resetDevtools(): void;
441
+ requestProgress(query: AnyQuery): RequestProgress | null;
442
+ retryCause(status: number): string;
443
+ requestUrl(query: AnyQuery): string | null;
444
+ requestPath(url: string): string;
445
+ queryArgs(query: AnyQuery): unknown;
446
+ routeSegments(entry: QueryDevtoolsEntry | undefined, query: AnyQuery): RouteSegment[];
447
+ queryActivity(entry: QueryDevtoolsEntry): QueryActivity;
448
+ linkActivity(link: QueryLink): QueryActivity;
449
+ stackActivity(stack: AnyQueryStack | AnyPagedQueryStack): QueryActivity;
450
+ sequenceActivity(sequence: QuerySequence<unknown[]>): QueryActivity;
451
+ queriesForStack(stack: AnyQueryStack | AnyPagedQueryStack): QueryLink[];
452
+ queriesForSequence(sequence: QuerySequence<unknown[]>): QueryLink[];
453
+ /**
454
+ * The queries a form feeds, discovered from the reads its `value()` recorded while their args were
455
+ * built - so a form that nothing consumes yet reads as exactly that.
456
+ */
457
+ queriesDrivenByForm(entry: QueryDevtoolsEntry): QueryLink[];
458
+ sequenceStepStatus(sequence: QuerySequence<unknown[]>, index: number): QuerySequenceStatus;
459
+ stepSnapshot(sequence: QuerySequence<unknown[]>, index: number): AnyQuerySnapshot | null;
460
+ /** Whether a sequence step's in/out detail (`<entryId>:<stepIndex>`) is expanded - persisted. */
461
+ isStepExpanded(entryId: string, index: number): boolean;
462
+ toggleStep(entryId: string, index: number): void;
463
+ /** Keys of the Queries-list groups of identical rows the user opened - persisted. */
464
+ expandedQueryGroups: Signal<ReadonlySet<string>>;
465
+ toggleQueryGroup(key: string): void;
466
+ expandQueryGroup(key: string): void;
467
+ /** Which way the Queries list sorts by last-executed time - persisted. */
468
+ queryRecentFirst: WritableSignal<boolean>;
469
+ /** Whether the Queries list is arranged as a tree of route paths instead of a flat list - persisted. */
470
+ queryTreeView: WritableSignal<boolean>;
471
+ /** The path folders the user closed. Collapsed, not expanded - a tree opens open. */
472
+ collapsedQueryPaths: Signal<ReadonlySet<string>>;
473
+ toggleQueryPath(key: string): void;
474
+ /** The element a query was created in, or `null` for one created outside a component/directive. */
475
+ locatableElement(entry: QueryDevtoolsEntry): HTMLElement | null;
476
+ locateQuery(entry: QueryDevtoolsEntry): void;
477
+ locateState: Signal<'idle' | 'located' | 'offscreen'>;
478
+ resetStats(entry: QueryDevtoolsEntry): void;
479
+ executeQuery(selection: QueryDevtoolsSelection, allowCache: boolean): void;
480
+ resetQuery(query: AnyQuery): void;
481
+ formatDuration(ms: number | null): string;
482
+ formatBytes(bytes: number): string;
483
+ formatTransferred(bytes: number, isEstimated: boolean): string;
484
+ formatSpeed(bytesPerSecond: number): string;
485
+ formatPercent(percentage: number): string;
486
+ formatCountdown(ms: number | null): string;
487
+ formatTime(timestamp: number | null): string;
488
+ gqlDocument(doc: string): string;
489
+ inlineValue(value: unknown): string;
490
+ featureLabel(type: string): string;
491
+ featureSummary(feature: QueryDevtoolsFeature): string;
492
+ detailTab: WritableSignal<DetailTab>;
493
+ editorMode: WritableSignal<'none' | 'response' | 'args'>;
494
+ responseDraft: WritableSignal<string>;
495
+ argsDraft: WritableSignal<string>;
496
+ /** The text the currently open editor was seeded with - not a signal, see the field's own doc. */
497
+ editorSeed: string;
498
+ editError: Signal<string | null>;
499
+ openResponseEditor(query: AnyQuery): void;
500
+ openArgsEditor(selection: QueryDevtoolsSelection): void;
501
+ applyResponse(query: AnyQuery): void;
502
+ applyArgs(query: AnyQuery): void;
503
+ cancelEditor(): void;
504
+ forceLoading(query: AnyQuery): void;
505
+ forceError(query: AnyQuery): void;
506
+ forceEmpty(query: AnyQuery): void;
507
+ clearForced(query: AnyQuery): void;
508
+ diffRunIndex: WritableSignal<number | null>;
509
+ /** The run the diff compares against, or `null` to derive it - see the panel's own field. */
510
+ diffBaseRunIndex: WritableSignal<number | null>;
511
+ toggleRunDiff(entry: QueryDevtoolsEntry, run: QueryDevtoolsRun): void;
512
+ /** Which end of the open diff a run is, or `null` if it is not one of the two. */
513
+ diffRunRole(entry: QueryDevtoolsEntry, run: QueryDevtoolsRun): 'base' | 'compare' | null;
514
+ responseDiff(entry: QueryDevtoolsEntry): {
515
+ before: QueryDevtoolsRun;
516
+ after: QueryDevtoolsRun;
517
+ diff: QueryDevtoolsDiff;
518
+ } | null;
519
+ canDiffRun(entry: QueryDevtoolsEntry, run: QueryDevtoolsRun): boolean;
520
+ queryRuns(entry: QueryDevtoolsEntry): QueryDevtoolsRun[];
521
+ runStatus(run: QueryDevtoolsRun): string;
522
+ /** How many bodies a query retains, which is what bounds how far back a diff can reach. */
523
+ retainedResponseCount: Signal<number>;
524
+ /** Moves the whole response diff one run older or newer, from the diff header. */
525
+ canStepRunDiff(entry: QueryDevtoolsEntry, older: boolean): boolean;
526
+ stepRunDiff(entry: QueryDevtoolsEntry, older: boolean): void;
527
+ errorRunIndex: WritableSignal<number | null>;
528
+ toggleRunError(run: QueryDevtoolsRun): void;
529
+ pickedRunError(entry: QueryDevtoolsEntry): {
530
+ run: QueryDevtoolsRun;
531
+ error: QueryDevtoolsRunError;
532
+ } | null;
533
+ copiedReport: Signal<boolean>;
534
+ copiedInsomnia: Signal<boolean>;
535
+ copiedCurl: Signal<boolean>;
536
+ copiedGql: Signal<boolean>;
537
+ copiedRoute: Signal<boolean>;
538
+ copyReport(entry: QueryDevtoolsEntry, query: AnyQuery): void;
539
+ copyInsomniaRequest(entry: QueryDevtoolsEntry, query: AnyQuery): void;
540
+ copyCurlRequest(entry: QueryDevtoolsEntry, query: AnyQuery): void;
541
+ copyGqlDocument(doc: string): void;
542
+ /** The absolute URL of the last request, or the rendered route for a query that has not run. */
543
+ copyableRoute(entry: QueryDevtoolsEntry, query: AnyQuery): string;
544
+ copyableRouteTitle(entry: QueryDevtoolsEntry, query: AnyQuery): string;
545
+ copyRoute(entry: QueryDevtoolsEntry, query: AnyQuery): void;
546
+ refreshesFor(entryId: string): {
547
+ id: number;
548
+ timestamp: number;
549
+ label: string;
550
+ }[];
551
+ /** What asked for a refresh, on one line - shared by the drawer's "Refetched by" and the Events tab. */
552
+ causeLabel(cause: QueryRefreshCause): string;
553
+ formsDrivingQuery(entry: QueryDevtoolsEntry): QueryDevtoolsEntry[];
554
+ /** Shared value-explorer search term, read (and set) from every drawer's Data sub-tab. */
555
+ jsonSearch: WritableSignal<string>;
556
+ jsonSearchTerm: Signal<string>;
557
+ jsonExpandedPaths: Signal<ReadonlySet<string>>;
558
+ jsonCollapsedPaths: Signal<ReadonlySet<string>>;
559
+ toggleJsonPath(path: string, expand: boolean): void;
560
+ selectedClientName: WritableSignal<string | null>;
561
+ selectedQueryId: WritableSignal<string | null>;
562
+ selectedQuery: Signal<{
563
+ entry: QueryDevtoolsEntry;
564
+ query: AnyQuery;
565
+ } | null>;
566
+ queryFilter: WritableSignal<string>;
567
+ queryFacets: WritableSignal<ReadonlySet<QueryListFacet>>;
568
+ /** The entry ids sorted to the top of the Queries list. Persisted apart from the rest - see the panel. */
569
+ pinnedQueryIds: Signal<ReadonlySet<string>>;
570
+ isQueryPinned(entry: QueryDevtoolsEntry): boolean;
571
+ toggleQueryPin(entry: QueryDevtoolsEntry): void;
572
+ /** When set (via inspect), the Queries list is filtered to exactly these entry ids. */
573
+ inspectFilterIds: WritableSignal<string[] | null>;
574
+ selectClient(name: string | null): void;
575
+ clearInspectFilter(): void;
576
+ toggleFacet(facet: QueryListFacet): void;
577
+ /** Drops the search term and the status chips, keeping the client / inspection scope. */
578
+ clearQueryFilters(): void;
579
+ /**
580
+ * Downloads the given queries (already scoped/filtered by the caller) as one Insomnia collection,
581
+ * filed into a folder per query client.
582
+ */
583
+ downloadInsomniaCollection(items: {
584
+ entry: QueryDevtoolsEntry;
585
+ query: AnyQuery;
586
+ }[], clientLabel: string | null): void;
587
+ /** Downloads a file a tab generated. The panel does it because the panel owns the download injector. */
588
+ downloadTextFile(file: {
589
+ name: string;
590
+ content: string;
591
+ type: string;
592
+ }): void;
593
+ stackSelectedQueryId: WritableSignal<string | null>;
594
+ stackSelectedQuery: Signal<{
595
+ entry: QueryDevtoolsEntry;
596
+ query: AnyQuery;
597
+ } | null>;
598
+ sequenceSelectedQueryId: WritableSignal<string | null>;
599
+ sequenceSelectedQuery: Signal<{
600
+ entry: QueryDevtoolsEntry;
601
+ query: AnyQuery;
602
+ } | null>;
603
+ formSelectedQueryId: WritableSignal<string | null>;
604
+ formSelectedQuery: Signal<{
605
+ entry: QueryDevtoolsEntry;
606
+ query: AnyQuery;
607
+ } | null>;
608
+ /** The form whose detail the Forms tab has expanded - persisted. */
609
+ selectedFormId: WritableSignal<string | null>;
610
+ /** The rolling event log - persisted only in the sense that it survives a tab switch, not a reload. */
611
+ eventLog: WritableSignal<EventLogItem[]>;
612
+ /** The client (by base URL) the event log is scoped to, or `null` for all of them - persisted. */
613
+ eventClient: WritableSignal<string | null>;
614
+ /** Whether the event log is narrowed to failures - persisted. */
615
+ eventErrorsOnly: WritableSignal<boolean>;
616
+ /** Free-text narrowing of every socket's message log - persisted. */
617
+ socketFilter: WritableSignal<string>;
618
+ socketMessages(ws: WebSocketDevtoolsHandle): WebSocketDevtoolsMessage[];
619
+ socketDirectionLabel(message: WebSocketDevtoolsMessage): string;
620
+ emitSocketMessage(options: {
621
+ entry: QueryDevtoolsEntry;
622
+ event: string;
623
+ data: string;
624
+ }): void;
625
+ /** The message an emit box last failed with, if this is the socket it failed on. */
626
+ socketEmitErrorFor(entryId: string): string | null;
627
+ timelineSelectedQueryId: WritableSignal<string | null>;
628
+ timelineSelectedQuery: Signal<{
629
+ entry: QueryDevtoolsEntry;
630
+ query: AnyQuery;
631
+ } | null>;
632
+ paneAxis: Signal<PaneAxis>;
633
+ listWidth: Signal<number | null>;
634
+ drawerWidth: Signal<number | null>;
635
+ listHeight: Signal<number | null>;
636
+ drawerHeight: Signal<number | null>;
637
+ startPaneResize(event: PointerEvent, target: {
638
+ pane: PaneTarget;
639
+ container: HTMLElement;
640
+ }): void;
641
+ resetPaneSize(pane: PaneTarget): void;
642
+ };
643
+
644
+ type ScopeKey = 'viewState' | 'pins' | 'overrides' | 'mocks' | 'armedMocks' | 'armedFaults';
645
+ type ScopeRow = {
646
+ key: ScopeKey;
647
+ label: string;
648
+ /** What is kept, and what `none` costs - the one thing a scope picker cannot show on its own. */
649
+ hint: string;
650
+ /**
651
+ * What to say on the scopes that let this state outlive the page that armed it - a picker cannot show
652
+ * that an app is about to start lying to itself before anyone opens the panel.
653
+ */
654
+ warn?: {
655
+ scopes: QueryDevtoolsStorageScope[];
656
+ text: string;
657
+ };
658
+ };
659
+ type LimitKey = 'maxEvents' | 'maxDroppedCacheEntries';
660
+ type LimitRow = {
661
+ key: LimitKey;
662
+ label: string;
663
+ min: number;
664
+ max: number;
665
+ step: number;
666
+ hint: string;
667
+ };
668
+ /**
669
+ * Every panel-wide switch in one place - the storage each kind of state uses, the limits the panel would
670
+ * otherwise hold as constants, and the toggles their own tabs also carry.
671
+ */
672
+ declare class QueryDevtoolsSettingsComponent {
673
+ protected host: QueryDevtoolsHost;
674
+ protected readonly SCOPE_ROWS: ScopeRow[];
675
+ protected readonly SCOPES: {
676
+ value: QueryDevtoolsStorageScope;
677
+ label: string;
678
+ }[];
679
+ protected readonly INDEXED_DB_TITLE = "Unavailable: these are read synchronously - view state before the first render, overrides before the first fetch - and IndexedDB cannot answer in time. Only the mock library could tolerate an async store, and it is not wired to one yet.";
680
+ protected readonly LIMIT_ROWS: LimitRow[];
681
+ protected readonly ARM_ALL_MOCKS: () => void;
682
+ protected readonly DISARM_ALL_MOCKS: () => void;
683
+ protected readonly DISARM_ALL_FAULTS: (clientName?: string) => void;
684
+ /** What queries actually retain, which is the application's value unless this panel raised it. */
685
+ protected responseHistory: _angular_core.Signal<number>;
686
+ protected mockCount: _angular_core.Signal<number>;
687
+ protected armedMockCount: _angular_core.Signal<number>;
688
+ protected armedFaultCount: _angular_core.Signal<number>;
689
+ protected resetConfirming: _angular_core.WritableSignal<boolean>;
690
+ protected settings(): _ethlete_query.QueryDevtoolsSettings;
691
+ protected scopeOf(key: ScopeKey): QueryDevtoolsStorageScope;
692
+ protected limitOf(key: LimitKey): number;
693
+ /** What the picked scope means beyond where the state is kept, or `null` while it means nothing extra. */
694
+ protected warningFor(row: ScopeRow, scope: QueryDevtoolsStorageScope): string | null;
695
+ protected setScope(key: ScopeKey, scope: QueryDevtoolsStorageScope): void;
696
+ protected setLimit(key: LimitKey, value: string): void;
697
+ protected setResponseHistory(value: string): void;
698
+ /** Hands retention back to `provideQueryDevtools({ responseHistory })`. */
699
+ protected clearResponseHistory(): void;
700
+ protected reset(): void;
701
+ protected toggleGoneQueries(): void;
702
+ protected showsGoneQueries(): boolean;
703
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<QueryDevtoolsSettingsComponent, never>;
704
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<QueryDevtoolsSettingsComponent, "et-query-devtools-settings", never, {}, {}, never, never, true, never>;
705
+ }
706
+
707
+ /**
708
+ * The waterfall's grid, axis and bars, as a styles-only component the panel mounts when the Timeline tab
709
+ * is first opened.
710
+ *
711
+ * @internal
712
+ */
713
+ declare class QueryDevtoolsTimelineStylesComponent {
714
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<QueryDevtoolsTimelineStylesComponent, never>;
715
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<QueryDevtoolsTimelineStylesComponent, "et-query-devtools-timeline-styles", never, {}, {}, never, never, true, never>;
716
+ }
717
+
718
+ /**
719
+ * Where the panel sits: attached to an edge, or floating over the page as a window of its own. A
720
+ * pop-out is not one of these - that is a real browser window, not a position in this document.
721
+ */
722
+ type DevtoolsDock = 'bottom' | 'top' | 'left' | 'right' | 'float';
723
+ /** What the layout menu offers, in the order it lists them. `popout` is a window, not a position. */
724
+ type DevtoolsLayout = DevtoolsDock | 'popout';
725
+ /** The floating panel's position and size, in CSS px from the viewport's top-left. */
726
+ type FloatRect = {
727
+ x: number;
728
+ y: number;
729
+ width: number;
730
+ height: number;
731
+ };
732
+ /**
733
+ * A drag in progress: the panel's docked edge, the floating panel being moved or resized, or the
734
+ * divider between a two-pane tab's panes.
735
+ */
736
+ type ResizeDrag = {
737
+ /** The document the pointer moves in - a popped-out panel lives in a window of its own. */
738
+ doc: Document;
739
+ } & ({
740
+ kind: 'panel';
741
+ } | {
742
+ kind: 'pane';
743
+ pane: PaneTarget;
744
+ axis: PaneAxis;
745
+ container: HTMLElement;
746
+ });
747
+ type PersistedState = {
748
+ open?: boolean;
749
+ height?: number;
750
+ width?: number;
751
+ listWidth?: number | null;
752
+ drawerWidth?: number | null;
753
+ listHeight?: number | null;
754
+ drawerHeight?: number | null;
755
+ dock?: DevtoolsDock;
756
+ floatRect?: FloatRect;
757
+ floatParked?: boolean;
758
+ activeTab?: DevtoolsTab;
759
+ detailTab?: DetailTab;
760
+ selectedClientName?: string | null;
761
+ selectedQueryId?: string | null;
762
+ selectedFormId?: string | null;
763
+ inspectFilterIds?: string[] | null;
764
+ queryFilter?: string;
765
+ queryFacets?: QueryListFacet[];
766
+ queryRecentFirst?: boolean;
767
+ queryTreeView?: boolean;
768
+ collapsedQueryPaths?: string[];
769
+ eventClient?: string | null;
770
+ eventErrorsOnly?: boolean;
771
+ socketFilter?: string;
772
+ jsonSearch?: string;
773
+ expandedSteps?: string[];
774
+ expandedQueryGroups?: string[];
775
+ jsonExpanded?: string[];
776
+ jsonCollapsed?: string[];
777
+ };
778
+ type ViewportSize = {
779
+ width: number;
780
+ height: number;
781
+ };
782
+ /**
783
+ * The loosest a float may sit: shoved off an edge with only {@link FLOAT_PEEK} left, which is what a
784
+ * drag and a parked panel are held to. **North is never a parking edge** - the title bar is the only
785
+ * thing that drags the panel back, so it may never be the part that leaves.
786
+ */
787
+ declare const clampFloatToPeek: (rect: FloatRect, viewport: ViewportSize) => FloatRect;
788
+ /**
789
+ * Where a float settles when the pointer is released. Dragged more than halfway off an edge, it stays
790
+ * parked there with {@link FLOAT_PEEK} showing - the panel gets out of the way of the thing you are
791
+ * debugging without being closed. Anything short of halfway is pulled back in, so a slightly clumsy
792
+ * drag does not park it.
793
+ */
794
+ declare const settledFloatRect: (rect: FloatRect, viewport: ViewportSize) => {
795
+ rect: FloatRect;
796
+ collapsed: boolean;
797
+ };
798
+ /**
799
+ * The rect a resize gesture from `edge` produces, given where the panel was when the gesture started.
800
+ * A west or north drag grows away from the edge that stays pinned, so the origin follows whatever
801
+ * size the drag settled on - including when the size hit its floor and the origin must stop with it.
802
+ */
803
+ declare const resizedFloatRect: (base: FloatRect, { move, viewport }: {
804
+ move: ResizeMoveEvent;
805
+ viewport: ViewportSize;
806
+ }) => FloatRect;
807
+ /**
808
+ * A floating, dockable panel that inspects the live state of the signals-first `@ethlete/query`
809
+ * system: queries, stacks, sequences, bearer auth providers, the repository cache and a rolling
810
+ * event log.
811
+ *
812
+ * Requires `provideQueryDevtools()` in the application providers - without it the registry stays
813
+ * empty and the panel shows nothing.
814
+ */
815
+ declare class QueryDevtoolsComponent implements OnInit {
816
+ private hostEl;
817
+ private renderer;
818
+ private download;
819
+ private styleManager;
820
+ private zone;
821
+ private destroyRef;
822
+ private document;
823
+ private viewport;
824
+ /**
825
+ * Opens the panel as soon as it is created, whatever the stored view state says. Set by
826
+ * `<et-query-devtools-lazy>`, which downloads the panel on the click that was meant to open it.
827
+ */
828
+ startOpen: _angular_core.InputSignalWithTransform<boolean, unknown>;
829
+ /** The panel itself, so a pop-out can move it into another window's document. */
830
+ private panelEl;
831
+ private eventIdCounter;
832
+ private lastSelectionKey;
833
+ /** Where the gear goes back to, so opening Settings does not lose the tab it was opened over. */
834
+ private tabBeforeSettings;
835
+ readonly persisted: PersistedState;
836
+ protected readonly tabs: ({
837
+ id: "queries";
838
+ label: string;
839
+ } | {
840
+ id: "stacks";
841
+ label: string;
842
+ } | {
843
+ id: "sequences";
844
+ label: string;
845
+ } | {
846
+ id: "forms";
847
+ label: string;
848
+ } | {
849
+ id: "auth";
850
+ label: string;
851
+ } | {
852
+ id: "ws";
853
+ label: string;
854
+ } | {
855
+ id: "cache";
856
+ label: string;
857
+ } | {
858
+ id: "timeline";
859
+ label: string;
860
+ } | {
861
+ id: "events";
862
+ label: string;
863
+ } | {
864
+ id: "faults";
865
+ label: string;
866
+ } | {
867
+ id: "mocks";
868
+ label: string;
869
+ } | {
870
+ id: "about";
871
+ label: string;
872
+ })[];
873
+ /**
874
+ * Tabs the bar keeps whether they hold anything or not: the one the panel opens on, and the one that
875
+ * arms faults - which has entries to arm rather than to count.
876
+ */
877
+ private readonly PINNED_TABS;
878
+ protected readonly shortcut: "⌘⌥Q" | "Ctrl+Alt+Q";
879
+ protected readonly LAYOUTS: readonly [{
880
+ readonly id: "bottom";
881
+ readonly glyph: "⬓";
882
+ readonly label: "Bottom";
883
+ readonly hint: "Dock to the bottom edge";
884
+ }, {
885
+ readonly id: "top";
886
+ readonly glyph: "⬒";
887
+ readonly label: "Top";
888
+ readonly hint: "Dock to the top edge";
889
+ }, {
890
+ readonly id: "left";
891
+ readonly glyph: "◧";
892
+ readonly label: "Left";
893
+ readonly hint: "Dock to the left edge";
894
+ }, {
895
+ readonly id: "right";
896
+ readonly glyph: "◨";
897
+ readonly label: "Right";
898
+ readonly hint: "Dock to the right edge";
899
+ }, {
900
+ readonly id: "float";
901
+ readonly glyph: "❐";
902
+ readonly label: "Float";
903
+ readonly hint: "A window inside the page - move it, resize it, shove it off an edge to park it";
904
+ }, {
905
+ readonly id: "popout";
906
+ readonly glyph: "⧉";
907
+ readonly label: "Pop out";
908
+ readonly hint: "Move the panel into a window of its own - the same live panel, on your other screen";
909
+ }];
910
+ protected open: WritableSignal<boolean>;
911
+ private panelHeight;
912
+ private panelWidth;
913
+ protected activeTab: WritableSignal<DevtoolsTab>;
914
+ /**
915
+ * The dragged sizes of the two-pane tabs, in px, one pair per axis - a side dock stacks the panes, so
916
+ * the same divider sizes them along the block axis there and a dock switch has to keep both. `null`
917
+ * keeps the stylesheet's proportional default, which is also what a double-click on a divider restores.
918
+ */
919
+ protected listWidth: WritableSignal<number | null>;
920
+ protected drawerWidth: WritableSignal<number | null>;
921
+ protected listHeight: WritableSignal<number | null>;
922
+ protected drawerHeight: WritableSignal<number | null>;
923
+ protected drag: WritableSignal<ResizeDrag | null>;
924
+ protected resizing: _angular_core.Signal<boolean>;
925
+ /** Which edge the panel is docked to, or `float` for a window of its own inside the page. */
926
+ protected dock: WritableSignal<DevtoolsDock>;
927
+ /** The entry the layout button shows, so it names where the panel is rather than where it could go. */
928
+ protected currentLayout: _angular_core.Signal<{
929
+ readonly id: "bottom";
930
+ readonly glyph: "⬓";
931
+ readonly label: "Bottom";
932
+ readonly hint: "Dock to the bottom edge";
933
+ } | {
934
+ readonly id: "top";
935
+ readonly glyph: "⬒";
936
+ readonly label: "Top";
937
+ readonly hint: "Dock to the top edge";
938
+ } | {
939
+ readonly id: "left";
940
+ readonly glyph: "◧";
941
+ readonly label: "Left";
942
+ readonly hint: "Dock to the left edge";
943
+ } | {
944
+ readonly id: "right";
945
+ readonly glyph: "◨";
946
+ readonly label: "Right";
947
+ readonly hint: "Dock to the right edge";
948
+ } | {
949
+ readonly id: "float";
950
+ readonly glyph: "❐";
951
+ readonly label: "Float";
952
+ readonly hint: "A window inside the page - move it, resize it, shove it off an edge to park it";
953
+ } | {
954
+ readonly id: "popout";
955
+ readonly glyph: "⧉";
956
+ readonly label: "Pop out";
957
+ readonly hint: "Move the panel into a window of its own - the same live panel, on your other screen";
958
+ }>;
959
+ /** Where the floating panel sits and how big it is. Kept while docked, so a return to float restores it. */
960
+ private floatRect;
961
+ /** The rect a resize gesture started from, or `null` outside one. */
962
+ private floatResizeBase;
963
+ /**
964
+ * Whether the float is shoved off an edge with only its peek showing. Persisted with the rect, since
965
+ * restoring the rect without it would either strand the panel off screen or silently un-park it.
966
+ */
967
+ protected floatParked: WritableSignal<boolean>;
968
+ /**
969
+ * The browser refused the last pop-out. `window.open` returns `null` with no error to catch, so
970
+ * without this the button simply did nothing - and floating is the fallback it should have offered.
971
+ */
972
+ protected popOutBlocked: WritableSignal<boolean>;
973
+ /**
974
+ * Whether the panel currently lives in a window of its own. Deliberately not persisted: a reload
975
+ * cannot re-adopt a window the previous document opened, so it always starts docked.
976
+ */
977
+ protected poppedOut: WritableSignal<boolean>;
978
+ protected floating: _angular_core.Signal<boolean>;
979
+ /**
980
+ * Below `md` a side-by-side split cannot fit: the list alone asks for `22rem` and the drawer for
981
+ * `26rem`, which is wider than a phone. Narrow gets the same stacked layout a right dock does.
982
+ */
983
+ private narrowViewport;
984
+ /** Whether the panel is docked to a side edge, which is what sizes it along the inline axis. */
985
+ private sideDocked;
986
+ /** Which axis the two-pane tabs split along. The stylesheet keys the stacked layout off the same value. */
987
+ protected paneAxis: _angular_core.Signal<PaneAxis>;
988
+ private popup;
989
+ /** Stops mirroring the host document's stylesheets into the pop-up. @see syncStylesInto */
990
+ private popOutStyleSync;
991
+ protected panelBlockSize: _angular_core.Signal<number | null>;
992
+ protected panelInlineSize: _angular_core.Signal<number | null>;
993
+ /** The floating panel's offset from the viewport's top-left, or `null` while it is docked. */
994
+ protected panelInsetBlock: _angular_core.Signal<number | null>;
995
+ protected panelInsetInline: _angular_core.Signal<number | null>;
996
+ /** Which section of the query detail is showing. Shared by the Queries tab and both drawers. */
997
+ detailTab: WritableSignal<DetailTab>;
998
+ selectedClientName: WritableSignal<string | null>;
999
+ selectedQueryId: WritableSignal<string | null>;
1000
+ /** The form whose detail the Forms tab has expanded. */
1001
+ selectedFormId: WritableSignal<string | null>;
1002
+ stackSelectedQueryId: WritableSignal<string | null>;
1003
+ sequenceSelectedQueryId: WritableSignal<string | null>;
1004
+ formSelectedQueryId: WritableSignal<string | null>;
1005
+ timelineSelectedQueryId: WritableSignal<string | null>;
1006
+ /** Free-text narrowing of the Queries list. Every whitespace-separated term has to match. */
1007
+ queryFilter: WritableSignal<string>;
1008
+ /** The status facets the Queries list is narrowed to. Empty means no status narrowing. */
1009
+ queryFacets: WritableSignal<ReadonlySet<QueryListFacet>>;
1010
+ /**
1011
+ * Which way the Queries list sorts by last-executed time. Only the direction is switchable - the field
1012
+ * is not, because "which one just ran" is the question the column exists to answer.
1013
+ */
1014
+ queryRecentFirst: WritableSignal<boolean>;
1015
+ /**
1016
+ * Whether the Queries list is arranged as a tree of route paths. Off by default: the flat list is
1017
+ * still the right shape for "what ran just now", which is what the tab is opened for most of the time.
1018
+ */
1019
+ queryTreeView: WritableSignal<boolean>;
1020
+ /**
1021
+ * The path folders the user closed. Collapsed rather than expanded, because a tree that opens closed
1022
+ * shows nothing but the top segment of every route - which answers no question the flat list didn't.
1023
+ */
1024
+ collapsedQueryPaths: WritableSignal<ReadonlySet<string>>;
1025
+ /**
1026
+ * The entry ids sorted to the top of the Queries list. Ids are derived from a stable descriptor plus a
1027
+ * per-descriptor sequence number, so a pin survives a reload as long as queries are created in the same
1028
+ * order - the same property the restored selection already relies on.
1029
+ */
1030
+ pinnedQueryIds: WritableSignal<ReadonlySet<string>>;
1031
+ eventLog: WritableSignal<EventLogItem[]>;
1032
+ /**
1033
+ * The cache entries that are gone, newest first. Kept here rather than in the repository, which stays
1034
+ * lean in production - it emits the teardown and forgets it.
1035
+ */
1036
+ droppedCacheEntries: WritableSignal<DroppedCacheEntry[]>;
1037
+ /** The client (by base URL) the event log is scoped to, or `null` for all of them. */
1038
+ eventClient: WritableSignal<string | null>;
1039
+ /** Whether the event log is narrowed to failures - the rows a bug report is about. */
1040
+ eventErrorsOnly: WritableSignal<boolean>;
1041
+ /** Free-text narrowing of every socket's message log. Matches the event, the room and the direction. */
1042
+ socketFilter: WritableSignal<string>;
1043
+ /** The cache entry whose response is expanded, as `<client>|<key>`, or `null` while none is. */
1044
+ private expandedCacheKey;
1045
+ /** The socket whose emit box last failed, so the message shows on that card and no other. */
1046
+ private socketEmitError;
1047
+ /** Keys (`<entryId>:<stepIndex>`) of the sequence steps whose in/out detail is expanded. */
1048
+ private expandedSteps;
1049
+ /** Keys of the Queries-list groups the user opened - the tab is rebuilt on every switch back. */
1050
+ expandedQueryGroups: WritableSignal<ReadonlySet<string>>;
1051
+ /** Shared value-explorer search term. */
1052
+ jsonSearch: WritableSignal<string>;
1053
+ jsonSearchTerm: _angular_core.Signal<string>;
1054
+ /** Path-keyed value-explorer expansion overrides (persisted so open trees survive a reload). */
1055
+ jsonExpandedPaths: WritableSignal<ReadonlySet<string>>;
1056
+ jsonCollapsedPaths: WritableSignal<ReadonlySet<string>>;
1057
+ /** Bound callback passed into the value explorer to persist per-path expansion. Assigned in the constructor. */
1058
+ toggleJsonPath: (path: string, expand: boolean) => void;
1059
+ /** The run whose response the diff is comparing, by run index, or `null` while no diff is open. */
1060
+ diffRunIndex: WritableSignal<number | null>;
1061
+ /**
1062
+ * The run the diff compares it *against*, or `null` to derive that side - the newest older run that
1063
+ * still holds a body, which is what one click on **Diff** gives you.
1064
+ */
1065
+ diffBaseRunIndex: WritableSignal<number | null>;
1066
+ errorRunIndex: WritableSignal<number | null>;
1067
+ /** How many bodies a query retains, which is what bounds how far back a diff can reach. */
1068
+ retainedResponseCount: _angular_core.Signal<number>;
1069
+ /** JIT editor state (response / args editing on the selected query). */
1070
+ editorMode: WritableSignal<"none" | "response" | "args">;
1071
+ responseDraft: WritableSignal<string>;
1072
+ argsDraft: WritableSignal<string>;
1073
+ /**
1074
+ * The text the currently open editor was seeded with. The textareas bind `value` to this and not to
1075
+ * the live draft: a `value` binding fed by the draft is written back on every keystroke, and writing
1076
+ * a textarea's `value` puts the caret at the end.
1077
+ */
1078
+ editorSeed: string;
1079
+ editError: WritableSignal<string | null>;
1080
+ /** The args the open editor was seeded from, so applying can put back what the draft cannot carry. */
1081
+ private editedArgsSource;
1082
+ /** Transient "Copied!" feedback for the copy-report, copy-as-Insomnia / cURL and copy-document actions. */
1083
+ copiedReport: WritableSignal<boolean>;
1084
+ copiedInsomnia: WritableSignal<boolean>;
1085
+ copiedCurl: WritableSignal<boolean>;
1086
+ copiedGql: WritableSignal<boolean>;
1087
+ copiedRoute: WritableSignal<boolean>;
1088
+ private copiedReset$;
1089
+ /** 1-second tick driving the cache freshness countdowns. */
1090
+ private clock;
1091
+ /** "Inspect" mode: hover the live UI to find the query that a component created. */
1092
+ protected inspectActive: WritableSignal<boolean>;
1093
+ protected inspectHover: WritableSignal<{
1094
+ rect: DOMRect;
1095
+ entries: QueryDevtoolsEntry[];
1096
+ } | null>;
1097
+ /** Inspect run backwards: the box drawn over the element the selected query was created in. */
1098
+ protected locatedRect: WritableSignal<DOMRect | null>;
1099
+ locateState: WritableSignal<"idle" | "located" | "offscreen">;
1100
+ private locate$;
1101
+ /** When set (via inspect), the Queries list is filtered to exactly these entry ids. */
1102
+ inspectFilterIds: WritableSignal<string[] | null>;
1103
+ private queryEntries;
1104
+ /**
1105
+ * The queries that still exist. Everything that measures what the application is doing right now -
1106
+ * the tab bar's error flags, the tamper dot, the timeline, the identity match behind an event row -
1107
+ * reads this rather than {@link queryEntries}, so a tombstone never inflates a live number.
1108
+ */
1109
+ private liveQueryEntries;
1110
+ protected panelTampered: _angular_core.Signal<boolean>;
1111
+ stackEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
1112
+ sequenceEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
1113
+ formEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
1114
+ authEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
1115
+ wsEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
1116
+ /** Unique client names present across queries and auth providers, for the Queries-tab picker. */
1117
+ clientNames: _angular_core.Signal<string[]>;
1118
+ /** Unique repositories (with their client name + base URL) used by the Cache and Events tabs. */
1119
+ private repositories;
1120
+ /**
1121
+ * Every client the Faults tab can arm, with the fault it currently carries. Clients with nothing armed
1122
+ * read as {@link EMPTY_QUERY_DEVTOOLS_FAULT} so the inputs always have a value to show.
1123
+ */
1124
+ faultClients: _angular_core.Signal<{
1125
+ name: string;
1126
+ baseUrl: string;
1127
+ fault: _ethlete_query.QueryDevtoolsFault;
1128
+ armed: boolean;
1129
+ }[]>;
1130
+ /**
1131
+ * The names of the clients currently carrying a fault, or `null` when none do - so a template can `@if`
1132
+ * on it. Armed faults are the one state where the panel is lying to the app on purpose, and a badge on a
1133
+ * tab that isn't open cannot say that: whichever tab you are reading, a red response has to be
1134
+ * attributable to the injection rather than to the API.
1135
+ */
1136
+ protected armedFaultClients: _angular_core.Signal<string[] | null>;
1137
+ /**
1138
+ * The routes currently answered by a designed mock, for the shell's banner. A mocked response is a
1139
+ * stronger lie than an override - nothing was sent at all - so it is named above every tab, not just
1140
+ * badged on the one that armed it.
1141
+ */
1142
+ protected armedMockRoutes: _angular_core.Signal<string[] | null>;
1143
+ /**
1144
+ * The queries the list is scoped to before the search box and the status chips narrow them further:
1145
+ * either the picked client's, or exactly the inspected element's.
1146
+ */
1147
+ scopedQueries: _angular_core.Signal<QueryDevtoolsEntry[]>;
1148
+ /** How many runs every query has recorded, for the Timeline tab's badge. */
1149
+ private runTotals;
1150
+ /** Cache entries across every client, for the Cache tab's badge. */
1151
+ private cacheEntryCount;
1152
+ /**
1153
+ * What each tab holds, so a failing query in a tab that is not open is still visible. Reading it
1154
+ * subscribes the tab bar to every entry's live state - which is the point of the badges.
1155
+ */
1156
+ protected tabBadges: _angular_core.Signal<Record<DevtoolsTab, TabBadge>>;
1157
+ /**
1158
+ * The tabs the bar shows: the pinned ones, the one that is open, and every tab that currently holds
1159
+ * something.
1160
+ */
1161
+ protected visibleTabs: _angular_core.Signal<({
1162
+ id: "queries";
1163
+ label: string;
1164
+ } | {
1165
+ id: "stacks";
1166
+ label: string;
1167
+ } | {
1168
+ id: "sequences";
1169
+ label: string;
1170
+ } | {
1171
+ id: "forms";
1172
+ label: string;
1173
+ } | {
1174
+ id: "auth";
1175
+ label: string;
1176
+ } | {
1177
+ id: "ws";
1178
+ label: string;
1179
+ } | {
1180
+ id: "cache";
1181
+ label: string;
1182
+ } | {
1183
+ id: "timeline";
1184
+ label: string;
1185
+ } | {
1186
+ id: "events";
1187
+ label: string;
1188
+ } | {
1189
+ id: "faults";
1190
+ label: string;
1191
+ } | {
1192
+ id: "mocks";
1193
+ label: string;
1194
+ } | {
1195
+ id: "about";
1196
+ label: string;
1197
+ })[]>;
1198
+ /** The empty tabs, which the bar offers behind "More" instead. */
1199
+ protected overflowTabs: _angular_core.Signal<({
1200
+ id: "queries";
1201
+ label: string;
1202
+ } | {
1203
+ id: "stacks";
1204
+ label: string;
1205
+ } | {
1206
+ id: "sequences";
1207
+ label: string;
1208
+ } | {
1209
+ id: "forms";
1210
+ label: string;
1211
+ } | {
1212
+ id: "auth";
1213
+ label: string;
1214
+ } | {
1215
+ id: "ws";
1216
+ label: string;
1217
+ } | {
1218
+ id: "cache";
1219
+ label: string;
1220
+ } | {
1221
+ id: "timeline";
1222
+ label: string;
1223
+ } | {
1224
+ id: "events";
1225
+ label: string;
1226
+ } | {
1227
+ id: "faults";
1228
+ label: string;
1229
+ } | {
1230
+ id: "mocks";
1231
+ label: string;
1232
+ } | {
1233
+ id: "about";
1234
+ label: string;
1235
+ })[]>;
1236
+ selectedQuery: _angular_core.Signal<{
1237
+ entry: QueryDevtoolsEntry;
1238
+ query: AnyQuery;
1239
+ } | null>;
1240
+ stackSelectedQuery: _angular_core.Signal<{
1241
+ entry: QueryDevtoolsEntry;
1242
+ query: AnyQuery;
1243
+ } | null>;
1244
+ sequenceSelectedQuery: _angular_core.Signal<{
1245
+ entry: QueryDevtoolsEntry;
1246
+ query: AnyQuery;
1247
+ } | null>;
1248
+ formSelectedQuery: _angular_core.Signal<{
1249
+ entry: QueryDevtoolsEntry;
1250
+ query: AnyQuery;
1251
+ } | null>;
1252
+ timelineSelectedQuery: _angular_core.Signal<{
1253
+ entry: QueryDevtoolsEntry;
1254
+ query: AnyQuery;
1255
+ } | null>;
1256
+ /**
1257
+ * The cache per client, with every entry's size measured. Reading each response inside the computed is
1258
+ * what keeps the totals current: a cache mutation bumps `cacheVersion`, but a response landing in an
1259
+ * entry that is already there does not.
1260
+ */
1261
+ cacheView: _angular_core.Signal<{
1262
+ name: string;
1263
+ baseUrl: string;
1264
+ repository: QueryRepository;
1265
+ rows: CacheRow[];
1266
+ bytes: number;
1267
+ isEstimatedBytes: boolean;
1268
+ unused: number;
1269
+ pollStates: Record<string, QueryKeyLockState>;
1270
+ client: QueryClient | null;
1271
+ dropped: DroppedCacheEntry[];
1272
+ }[]>;
1273
+ /** Map of a component's host element to the query entries it created (for the inspect tool). */
1274
+ private elementQueryMap;
1275
+ /** Disarms every client's fault - the shell's "Faults armed" banner offers this above every tab. */
1276
+ protected readonly CLEAR_FAULTS: (clientName?: string) => void;
1277
+ /** Whether what is armed came back from a previous page load, which is what the bars have to say. */
1278
+ protected faultsRestored: _angular_core.Signal<boolean>;
1279
+ protected mocksRestored: _angular_core.Signal<boolean>;
1280
+ /** Stops serving every designed mock - the shell's "Mocks armed" banner offers the same way out. */
1281
+ protected readonly DISARM_MOCKS: () => void;
1282
+ /**
1283
+ * What the previous page load left armed: how many ops came back, how many queries took them, and the
1284
+ * ids nothing claimed. `null` when this page inherited nothing, so a template can `@if` on it.
1285
+ */
1286
+ protected restoredOverrides: _angular_core.Signal<{
1287
+ queries: number;
1288
+ ops: number;
1289
+ firstId: string | null;
1290
+ orphaned: string[];
1291
+ fromLocalStorage: boolean;
1292
+ } | null>;
1293
+ /** Drops everything the reload re-armed, and empties the store it came from. */
1294
+ protected readonly DROP_RESTORED_OVERRIDES: () => void;
1295
+ /** Every edge, so a float resizes the way a window does rather than only from one corner. */
1296
+ protected readonly FLOAT_RESIZE_EDGES: ResizeEdge[];
1297
+ constructor();
1298
+ ngOnInit(): void;
1299
+ /** @see queryDevtoolsOverridePersistence */
1300
+ overridesPersist(): boolean;
1301
+ toggleOverridesPersist(): void;
1302
+ /** Where armed overrides are kept, as the drawer's toggle and the Settings picker both name it. */
1303
+ overridesScopeLabel(): string;
1304
+ /**
1305
+ * Puts the panel back the way it ships: layout, filters, selections, pins and the stored overrides.
1306
+ * The settings themselves stay - a panel behaving oddly is a reason to reset its state, not to lose the
1307
+ * scopes and limits that were chosen deliberately.
1308
+ *
1309
+ * Resetting the live state is the point, not just clearing the keys: the persistence effects would
1310
+ * write the current state straight back into whatever store the scopes name.
1311
+ */
1312
+ resetDevtools(): void;
1313
+ /**
1314
+ * Opens Settings over whatever tab is showing, and the same click closes it again. Settings is not in
1315
+ * the tab bar - see {@link DevtoolsTab}.
1316
+ */
1317
+ protected toggleSettings(): void;
1318
+ /** Opens the first query the reload re-armed, so the banner leads somewhere rather than just warning. */
1319
+ protected reviewRestoredOverrides(id: string): void;
1320
+ /**
1321
+ * Closing while popped out docks back instead: the panel a pop-out shows is the very element the
1322
+ * `@if` below would destroy, so it has to come home before it can be closed.
1323
+ */
1324
+ protected toggleOpen(): void;
1325
+ protected floatPanel(): void;
1326
+ protected selectLayout(layout: DevtoolsLayout): void;
1327
+ /**
1328
+ * Moves the panel into a window of its own - the same element, adopted by the pop-up's document, so
1329
+ * every signal binding in it keeps updating from the app it is inspecting.
1330
+ *
1331
+ * The panel's styles are global `<style>` tags in the host document, and the theming tokens it reads
1332
+ * hang off the root element, so both are copied over; without them the pop-out renders unstyled.
1333
+ */
1334
+ popOut(): void;
1335
+ selectClient(name: string | null): void;
1336
+ clearInspectFilter(): void;
1337
+ toggleFacet(facet: QueryListFacet): void;
1338
+ /** Drops the search term and the status chips, keeping the client / inspection scope. */
1339
+ clearQueryFilters(): void;
1340
+ isQueryPinned(entry: QueryDevtoolsEntry): boolean;
1341
+ toggleQueryPath(key: string): void;
1342
+ toggleQueryPin(entry: QueryDevtoolsEntry): void;
1343
+ protected toggleInspect(): void;
1344
+ protected startResize(event: PointerEvent): void;
1345
+ protected moveFloat(move: DragMoveEvent): void;
1346
+ /** Parks the panel where a drag left it, or pulls it back in if the drag stopped short of an edge. */
1347
+ protected endFloatMove(): void;
1348
+ /** A click on the title bar of a parked panel brings it back - the gesture that parked it, reversed. */
1349
+ protected restoreFloat(): void;
1350
+ /** A resize reports a delta from where the pointer went down, so the rect it started from is kept. */
1351
+ protected startFloatResize(): void;
1352
+ protected resizeFloat(move: ResizeMoveEvent): void;
1353
+ protected endFloatResize(): void;
1354
+ startPaneResize(event: PointerEvent, target: {
1355
+ pane: PaneTarget;
1356
+ container: HTMLElement;
1357
+ }): void;
1358
+ /** Hands a pane back to the stylesheet's proportional default, on the axis it is being sized along. */
1359
+ resetPaneSize(pane: PaneTarget): void;
1360
+ protected inspectLabel(entries: QueryDevtoolsEntry[]): string;
1361
+ /**
1362
+ * A query's route, split so the template can tell its static path from its path params (each carrying
1363
+ * the value the query used, or `:<name>` while it has none yet) and from the query string of the
1364
+ * request that ran - which is what tells two requests to the same endpoint apart.
1365
+ */
1366
+ routeSegments(entry: QueryDevtoolsEntry | undefined, query: AnyQuery): RouteSegment[];
1367
+ /** The full URL of the request a query last made, or `null` while it has not executed. */
1368
+ requestUrl(query: AnyQuery): string | null;
1369
+ /**
1370
+ * The args of a query. A query executed imperatively (`execute({ args })`, a sequence step, an auth
1371
+ * query) never writes them to its own `args` signal - only the `withArgs` feature does - so the args
1372
+ * its current request was built from stand in.
1373
+ */
1374
+ queryArgs(query: AnyQuery): Omit<any, "response"> | null;
1375
+ queryStatus(query: AnyQuery): QueryStatus;
1376
+ /**
1377
+ * A request in flight is already refreshing, so reporting it as stale on top of `loading` is noise -
1378
+ * the same precedence the cache tab's freshness column applies.
1379
+ */
1380
+ isStale(query: AnyQuery): boolean;
1381
+ /**
1382
+ * Whether an entry is showing something other than what the server actually sent - an armed response
1383
+ * override, or a devtools fault that decided its last completed run. Deliberately not "a fault is
1384
+ * armed nearby": an armed-but-idle fault (a `failRate` under 100, say) mostly lets requests through
1385
+ * untouched, so that would over-claim for the vast majority of queries on that client.
1386
+ */
1387
+ isTampered(entry: QueryDevtoolsEntry): boolean;
1388
+ /** Whether a designed mock is armed for this query's own route, so nothing it shows came off the wire. */
1389
+ isMocked(entry: QueryDevtoolsEntry): boolean;
1390
+ /**
1391
+ * What a query's current request is doing beyond being loading, or `null` when there is nothing beyond
1392
+ * the status dot to say - so the readout only takes up room while it carries something.
1393
+ */
1394
+ requestProgress(query: AnyQuery): RequestProgress | null;
1395
+ /** Why a retry was scheduled, as the panel spells it out. A status of 0 never reached the server. */
1396
+ retryCause(status: number): string;
1397
+ formatPercent(percentage: number): string;
1398
+ /** A countdown in whole seconds, spelled out the way the cache freshness column does. */
1399
+ formatCountdown(ms: number | null): string;
1400
+ queryActivity(entry: QueryDevtoolsEntry): QueryActivity;
1401
+ linkActivity(link: QueryLink): QueryActivity;
1402
+ stackActivity(stack: AnyQueryStack | AnyPagedQueryStack): QueryActivity;
1403
+ sequenceActivity(sequence: QuerySequence<unknown[]>): QueryActivity;
1404
+ /** Clears an entry's counters and run history, so the next interaction can be measured on its own. */
1405
+ resetStats(entry: QueryDevtoolsEntry): void;
1406
+ /** A query's runs, newest first - the order a history is read in. */
1407
+ queryRuns(entry: QueryDevtoolsEntry): QueryDevtoolsRun[];
1408
+ /**
1409
+ * What a run's status dot and timeline bar colour by. `pending` reuses the panel's loading colour;
1410
+ * `aborted` matches no rule and so falls back to the neutral one.
1411
+ */
1412
+ runStatus(run: QueryDevtoolsRun): "error" | "loading" | "success" | "aborted";
1413
+ /**
1414
+ * Whether a run can be an end of the diff: it still holds its body, and so does some other run of the
1415
+ * same query. Deliberately not "an *older* run" - a free pair means the oldest body held is pickable
1416
+ * too, as the base of a comparison against something newer.
1417
+ */
1418
+ canDiffRun(entry: QueryDevtoolsEntry, run: QueryDevtoolsRun): boolean;
1419
+ /**
1420
+ * Arms a run as one end of the diff, or - with one end already armed - as the other. Clicking either
1421
+ * end clears both.
1422
+ */
1423
+ toggleRunDiff(entry: QueryDevtoolsEntry, run: QueryDevtoolsRun): void;
1424
+ /** Which end of the comparison a run is, so a row says so and the two ends read differently. */
1425
+ diffRunRole(entry: QueryDevtoolsEntry, run: QueryDevtoolsRun): 'base' | 'compare' | null;
1426
+ responseDiff(entry: QueryDevtoolsEntry): {
1427
+ before: QueryDevtoolsRun;
1428
+ after: QueryDevtoolsRun;
1429
+ diff: QueryDevtoolsDiff;
1430
+ } | null;
1431
+ /** Whether the diff header's older/newer control has a pair to move to. */
1432
+ canStepRunDiff(entry: QueryDevtoolsEntry, older: boolean): boolean;
1433
+ /**
1434
+ * Moves the whole comparison one run older or newer, so re-picking a pair does not mean scrolling back
1435
+ * up to the runs table. Within the handful of retained bodies there are only a few pairs to walk.
1436
+ */
1437
+ stepRunDiff(entry: QueryDevtoolsEntry, older: boolean): void;
1438
+ toggleRunError(run: QueryDevtoolsRun): void;
1439
+ /**
1440
+ * The error body of the picked run. Read off the run rather than off `query.error()`, which is the
1441
+ * only other place a failure is legible and is blanked by anything that resets the query - a logout
1442
+ * resets every secure query, so a 401 is gone from there by the time it is looked for.
1443
+ */
1444
+ pickedRunError(entry: QueryDevtoolsEntry): {
1445
+ run: QueryDevtoolsRun;
1446
+ error: _ethlete_query.QueryDevtoolsRunError;
1447
+ } | null;
1448
+ /** Opens a query in the Queries tab - the Events tab is a way in, not a dead end. */
1449
+ selectQuery(id: string): void;
1450
+ /** A value on one line, for a diff row or a form field. The full tree would bury the row it sits in. */
1451
+ inlineValue(value: unknown): string;
1452
+ formatBytes(bytes: number): string;
1453
+ /**
1454
+ * A transferred size, marked `≈` when any part of it was measured from a decoded body instead of read
1455
+ * from a `content-length` header - such a size ignores transport compression.
1456
+ */
1457
+ formatTransferred(bytes: number, isEstimated: boolean): string;
1458
+ /** A transfer rate, given in bytes per second the way `HttpRequestLoadingProgressState.speed` reports it. */
1459
+ formatSpeed(bytesPerSecond: number): string;
1460
+ formatDuration(ms: number | null): string;
1461
+ /**
1462
+ * Replays a query with the args the panel is already showing. `execute()` would otherwise default to
1463
+ * `state.args()`, which only `withArgs` ever writes - so a query executed imperatively, by a sequence
1464
+ * step or as an auth query would replay with no args at all and a function route would throw.
1465
+ */
1466
+ executeQuery(selection: QueryDevtoolsSelection, allowCache: boolean): void;
1467
+ resetQuery(query: AnyQuery): void;
1468
+ /**
1469
+ * Copies a shareable report (path, args, status, slimmed response) for handing to an API dev.
1470
+ * Writes both rich `text/html` (Slack applies formatting on paste - it does not parse markdown) and
1471
+ * a plain-text fallback.
1472
+ */
1473
+ copyReport(entry: QueryDevtoolsEntry, query: AnyQuery): void;
1474
+ /** Copies one query as an Insomnia collection, for `Import > From Clipboard`. */
1475
+ copyInsomniaRequest(entry: QueryDevtoolsEntry, query: AnyQuery): void;
1476
+ /**
1477
+ * Copies one query as a `curl` command - what goes into a terminal, a ticket or a chat message, where
1478
+ * an Insomnia collection is too heavy to be read at all.
1479
+ */
1480
+ copyCurlRequest(entry: QueryDevtoolsEntry, query: AnyQuery): void;
1481
+ /** Copies the GraphQL document as displayed — dedented, so it pastes straight into a playground. */
1482
+ copyGqlDocument(doc: string): void;
1483
+ /**
1484
+ * The endpoint of a query as one string: the absolute URL its last request used, or - for a query
1485
+ * that has not run - the rendered route on screen.
1486
+ */
1487
+ copyableRoute(entry: QueryDevtoolsEntry, query: AnyQuery): string;
1488
+ /** Names which of the two strings {@link copyableRoute} is offering, so the button says what it copies. */
1489
+ copyableRouteTitle(entry: QueryDevtoolsEntry, query: AnyQuery): string;
1490
+ /** Copies {@link copyableRoute} - the endpoint on its own, where the exports are a whole document. */
1491
+ copyRoute(entry: QueryDevtoolsEntry, query: AnyQuery): void;
1492
+ /**
1493
+ * Downloads the given queries (already scoped/filtered by the caller) as one Insomnia collection,
1494
+ * filed into a folder per query client.
1495
+ */
1496
+ downloadInsomniaCollection(items: {
1497
+ entry: QueryDevtoolsEntry;
1498
+ query: AnyQuery;
1499
+ }[], clientLabel: string | null): void;
1500
+ /**
1501
+ * Downloads the whole panel as one JSON file: every registered entry with what it ran and what it
1502
+ * holds, the event log, the cache totals and anything armed in the Faults tab. Unlike **Copy report**
1503
+ * this is not scoped to one query - it is the attachment for a bug report about a screen.
1504
+ *
1505
+ * Deliberately unfiltered: a report is read by someone who was not there, and a dump that silently
1506
+ * left out the client you were not looking at is worse than no dump.
1507
+ */
1508
+ protected downloadSession(): void;
1509
+ openResponseEditor(query: AnyQuery): void;
1510
+ openArgsEditor({ entry, query }: QueryDevtoolsSelection): void;
1511
+ applyResponse(query: AnyQuery): void;
1512
+ applyArgs(query: AnyQuery): void;
1513
+ cancelEditor(): void;
1514
+ forceLoading(query: AnyQuery): void;
1515
+ forceError(query: AnyQuery): void;
1516
+ forceEmpty(query: AnyQuery): void;
1517
+ clearForced(query: AnyQuery): void;
1518
+ refetchCacheEntry(entry: QueryRepositoryCacheEntry): void;
1519
+ evictCacheEntry(repository: QueryRepository, key: string): void;
1520
+ /**
1521
+ * Drops every entry of one client, consumers included - the cold-start check that does not need a
1522
+ * reload. A query still bound to an evicted entry requests again on its next execution.
1523
+ */
1524
+ evictAllCacheEntries(repository: QueryRepository): void;
1525
+ /**
1526
+ * Expands the response held under a cache key, which is the only way to read an entry no live query is
1527
+ * bound to any more - the Queries tab has nothing to select for it.
1528
+ */
1529
+ toggleCacheValue(clientName: string, key: string): void;
1530
+ isCacheValueExpanded(clientName: string, key: string): boolean;
1531
+ cacheFreshness(entry: QueryRepositoryCacheEntry): string;
1532
+ /**
1533
+ * What multi-tab sync is doing for a cache entry: whether this tab is the one polling the key, and
1534
+ * how long ago it last took a response from another tab. Empty when the client has no sync.
1535
+ */
1536
+ cacheSync(entry: QueryRepositoryCacheEntry, pollStates: Record<string, QueryKeyLockState>): string;
1537
+ /**
1538
+ * Whether a cache entry is showing data that came off the disk rather than the network - the answer
1539
+ * to "why is this here already?" on a cold start. Empty when the client does not persist responses.
1540
+ */
1541
+ cachePersistence(entry: QueryRepositoryCacheEntry): string;
1542
+ /** How many responses this client has on disk, which is usually more than it has in memory. */
1543
+ persistedCount(client: QueryClient): number;
1544
+ clearPersistedQueries(client: QueryClient): void;
1545
+ /** The path + query of a request URL (origin stripped), for readable cache/event identifiers. */
1546
+ requestPath(url: string): string;
1547
+ asStack(entry: QueryDevtoolsEntry): AnyQueryStack;
1548
+ asPagedStack(entry: QueryDevtoolsEntry): AnyPagedQueryStack;
1549
+ asSequence(entry: QueryDevtoolsEntry): QuerySequence<unknown[]>;
1550
+ asAuth(entry: QueryDevtoolsEntry): AnyBearerAuthProvider;
1551
+ asWs(entry: QueryDevtoolsEntry): WebSocketDevtoolsHandle;
1552
+ /**
1553
+ * A socket's messages, narrowed by the filter box. Every whitespace-separated term has to match the
1554
+ * event, the room or the direction, so `out join` finds the room joins the client sent.
1555
+ */
1556
+ socketMessages(ws: WebSocketDevtoolsHandle): WebSocketDevtoolsMessage[];
1557
+ /** How the message log labels a direction: what the client sent, versus what the server pushed. */
1558
+ socketDirectionLabel(message: WebSocketDevtoolsMessage): "↑ sent" | "↓ received";
1559
+ /**
1560
+ * Sends a message as the app would, so a server that only answers a client that asked can be provoked
1561
+ * from the panel. An empty payload sends nothing rather than `""` - a plain event is a valid message.
1562
+ */
1563
+ emitSocketMessage(options: {
1564
+ entry: QueryDevtoolsEntry;
1565
+ event: string;
1566
+ data: string;
1567
+ }): void;
1568
+ socketEmitErrorFor(entryId: string): string | null;
1569
+ asForm(entry: QueryDevtoolsEntry): QueryDevtoolsFormHandle;
1570
+ /**
1571
+ * The queries a form feeds, discovered from the reads its `value()` recorded while their args were
1572
+ * built - so a form that nothing consumes yet reads as exactly that.
1573
+ */
1574
+ queriesDrivenByForm(entry: QueryDevtoolsEntry): QueryLink[];
1575
+ /** The reverse: the forms whose value a query's args read. */
1576
+ formsDrivingQuery(entry: QueryDevtoolsEntry): QueryDevtoolsEntry[];
1577
+ selectForm(id: string): void;
1578
+ /**
1579
+ * The refreshes that re-executed a query, newest first - the answer to "why did this refetch?". Read
1580
+ * off the event log, so it goes back exactly as far as the log does.
1581
+ */
1582
+ refreshesFor(entryId: string): {
1583
+ id: number;
1584
+ timestamp: number;
1585
+ label: string;
1586
+ }[];
1587
+ /** What asked for a refresh, on one line. */
1588
+ causeLabel(cause: QueryRefreshCause): string;
1589
+ /** Derives the per-step status of a sequence step from its live progress signals. */
1590
+ sequenceStepStatus(sequence: QuerySequence<unknown[]>, index: number): QuerySequenceStatus;
1591
+ authTokenPayload(auth: AnyBearerAuthProvider): Record<string, unknown> | null;
1592
+ queriesForStack(stack: AnyQueryStack | AnyPagedQueryStack): QueryLink[];
1593
+ authQueryKeys(auth: AnyBearerAuthProvider): string[];
1594
+ /**
1595
+ * Which tab refreshes this provider's tokens, as a chip: whether it is this one, how many tabs are
1596
+ * in the election, and - when there is no election - why every tab reads as the leader.
1597
+ */
1598
+ authLeadership(auth: AnyBearerAuthProvider): QueryDevtoolsLeadership | null;
1599
+ /**
1600
+ * The access token's expiry as the app sees it, plus whatever the panel is doing to it: an armed
1601
+ * lifetime replaces the countdown, and the token's own is reported next to it so the card never claims
1602
+ * a refresh is due at a time the API would disagree with.
1603
+ */
1604
+ authTokenLifetime(entry: QueryDevtoolsEntry): QueryDevtoolsTokenLifetime;
1605
+ queriesForSequence(sequence: QuerySequence<unknown[]>): QueryLink[];
1606
+ /** The snapshot of a sequence step, once it has run (holds the args in and the response/error out). */
1607
+ stepSnapshot(sequence: QuerySequence<unknown[]>, index: number): AnyQuerySnapshot | null;
1608
+ toggleQueryGroup(key: string): void;
1609
+ expandQueryGroup(key: string): void;
1610
+ isStepExpanded(entryId: string, index: number): boolean;
1611
+ toggleStep(entryId: string, index: number): void;
1612
+ /** Dedents a GraphQL document (template-literal indentation) for readable display. */
1613
+ gqlDocument(doc: string): string;
1614
+ featureLabel(type: string): string;
1615
+ /** A feature and its options on one line, for a report or a chip's tooltip. */
1616
+ featureSummary(feature: QueryDevtoolsFeature): string;
1617
+ /** The features of the client behind a cache tab card, or `null` for a client without any. */
1618
+ clientFeatures(client: QueryClient | null | undefined): QueryDevtoolsFeature[] | null;
1619
+ formatTime(timestamp: number | null): string;
1620
+ /**
1621
+ * The element a query was created in, which is what {@link locateQuery} can point at. `null` for a
1622
+ * query created outside a component or directive injector - a root service, a resolver, a guard.
1623
+ */
1624
+ locatableElement(entry: QueryDevtoolsEntry): HTMLElement | null;
1625
+ /**
1626
+ * Scrolls the element the selected query was created in into view and draws the inspect box over it -
1627
+ * inspect run backwards. Where a query was *created* is not necessarily where its data is rendered,
1628
+ * which is what the button's "created here" wording is for.
1629
+ */
1630
+ locateQuery(entry: QueryDevtoolsEntry): void;
1631
+ /** Downloads a file a tab generated. @see QueryDevtoolsHost.downloadTextFile */
1632
+ downloadTextFile(file: {
1633
+ name: string;
1634
+ content: string;
1635
+ type: string;
1636
+ }): void;
1637
+ /**
1638
+ * The two runs the diff compares, oldest first - normalised by run index, so picking a pair in either
1639
+ * order reads the same way round as the `#before → #after` header prints it.
1640
+ *
1641
+ * `null` unless a run is picked, so a closed diff costs nothing to walk.
1642
+ */
1643
+ /**
1644
+ * The two runs the comparison spans, oldest first - normalised by run index, so picking a pair in
1645
+ * either order reads the same way round as the `#before → #after` header prints it. With only one end
1646
+ * armed, `after` is absent unless an older body-holding run can stand in for the base.
1647
+ *
1648
+ * `null` unless a run is armed, so a closed diff costs nothing to walk.
1649
+ */
1650
+ /**
1651
+ * The pair one step older or newer than the current comparison, keeping whatever gap it has, or `null`
1652
+ * at either end of the retained bodies.
1653
+ */
1654
+ private steppedDiffPair;
1655
+ private diffEnds;
1656
+ private isTabPrimary;
1657
+ /** Moves the panel into a pop-up whose document has finished loading. @see popOut */
1658
+ private mountPopOut;
1659
+ /**
1660
+ * The panel's chrome tokens as the app currently resolves them, so a pop-out keeps the surface it was
1661
+ * docked in. Its own CSS resolves them from the host app's theme, and that theme is set on an ancestor
1662
+ * the pop-out does not have - inherited afresh over there, the panel would take the document's theme
1663
+ * (usually the light one) rather than the one it was just being read against.
1664
+ *
1665
+ * Empty on a browser that does not enumerate custom properties, which leaves the tokens to be
1666
+ * inherited as before.
1667
+ */
1668
+ private chromeTokens;
1669
+ /** Whether a `<head>` child carries CSS the pop-out needs a copy of. */
1670
+ private isStyleNode;
1671
+ /**
1672
+ * Copies the host document's stylesheets into the pop-up and keeps mirroring them while it is
1673
+ * open. Styles keep arriving after the move: Angular inserts a component's CSS and the style
1674
+ * manager mounts overlay strategy CSS the first time each is used - e.g. the first menu opened
1675
+ * over there - so a one-time copy would leave everything first used after the pop-out unstyled.
1676
+ */
1677
+ private syncStylesInto;
1678
+ /** Brings the panel back into the host element and closes the window it was living in. */
1679
+ private dockBack;
1680
+ /** A registered query as a row that opens the detail drawer. */
1681
+ private queryLinkFor;
1682
+ /** The activity of one entry, or the total of a group of them (a stack's queries, a whole tab). */
1683
+ private activityOf;
1684
+ /** An activity summary on one line, for a report. `null` for a query that has not run. */
1685
+ private activitySummary;
1686
+ /** {@link routeSegments} as a plain string, for the places that cannot render markup. */
1687
+ private queryRoute;
1688
+ /**
1689
+ * Describes a query the way a replay outside the app needs it: the URL, headers and body of the
1690
+ * request it last made, or - for a query that has not run - what its current args would send.
1691
+ */
1692
+ private exportedRequest;
1693
+ /**
1694
+ * The token refreshes the given requests authenticate with - one per auth provider they name, and
1695
+ * only for a provider that is logged in, since a refresh request without a refresh token has
1696
+ * nothing to send.
1697
+ */
1698
+ private insomniaTokenRefreshes;
1699
+ private insomniaTokenRefresh;
1700
+ /**
1701
+ * Where the access token sits in the refresh response. A provider's `extractTokens` can pull it out
1702
+ * of any shape, so the path is recovered by finding the live token in the last auth response - with
1703
+ * the default extractor's `$.accessToken` as the fallback.
1704
+ */
1705
+ private accessTokenPath;
1706
+ /**
1707
+ * How long Insomnia may reuse a stored refresh response: the access token's own lifetime with a
1708
+ * margin, so the chain refreshes shortly before the token it hands out would expire. Capped at an
1709
+ * hour - a long-lived (or bogus) `exp` should still hand out a token minted this session.
1710
+ */
1711
+ private accessTokenMaxAge;
1712
+ /**
1713
+ * The headers a replay needs, including the ones the query client adds. Header providers can throw
1714
+ * (a secure query's needs an access token), in which case the request is exported without them.
1715
+ */
1716
+ private insomniaHeaders;
1717
+ private sessionClients;
1718
+ /** Every registered entry, described by whatever its kind carries. */
1719
+ private sessionEntries;
1720
+ private sessionQuery;
1721
+ private sessionActivity;
1722
+ private sessionSequence;
1723
+ private sessionForm;
1724
+ /**
1725
+ * An auth provider without its tokens. A session report is a file that gets attached to a ticket, and
1726
+ * the access token is the one thing in the panel that must never travel with it - which is why only
1727
+ * its presence and its remaining lifetime are exported.
1728
+ */
1729
+ private sessionAuth;
1730
+ private sessionSocket;
1731
+ private sessionEvents;
1732
+ private sessionFaults;
1733
+ /** Only the armed ones: a capture taken while the panel was answering requests has to say which. */
1734
+ private sessionMocks;
1735
+ private downloadFile;
1736
+ private stepKey;
1737
+ private paneSize;
1738
+ /** The panel's size is the distance from the pointer to the edge it is docked to. */
1739
+ private applyResize;
1740
+ /** A pane's size is the distance from the pointer to the container edge that pane sits against. */
1741
+ private applyPaneResize;
1742
+ private closePopup;
1743
+ private updateInspectHover;
1744
+ private selectInspectedQuery;
1745
+ private selectionKey;
1746
+ /** Writes to the clipboard and ticks `copied` on success. `html` is omitted for plain-text payloads. */
1747
+ private writeToClipboard;
1748
+ private responseStatus;
1749
+ private findQuery;
1750
+ private pushEvent;
1751
+ /**
1752
+ * The registered query an event's request belongs to. A request is shared by every query on the same
1753
+ * cache key, so the first owner is as good as any - they all show the same response.
1754
+ *
1755
+ * The url fallback is what makes a row clickable when the query is already gone: an error that fires
1756
+ * as the component holding it is being destroyed (a `401` that redirects to login) has no live owner
1757
+ * left to match on identity, and its tombstone holds a copy of the request rather than the request.
1758
+ */
1759
+ private resolveEventQueryId;
1760
+ /**
1761
+ * The size of a settled request's response, taken from its `content-length` when the response carried
1762
+ * one and measured from the decoded body otherwise.
1763
+ */
1764
+ private measureResponse;
1765
+ private refreshedRequestOf;
1766
+ private pushEventItem;
1767
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<QueryDevtoolsComponent, never>;
1768
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<QueryDevtoolsComponent, "et-query-devtools", never, { "startOpen": { "alias": "startOpen"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1769
+ }
1770
+
1771
+ declare const QUERY_DEVTOOLS_IMPORTS: readonly [typeof QueryDevtoolsComponent];
1772
+
1773
+ /** The version of `@ethlete/query-devtools` this build was cut from. */
1774
+ declare const QUERY_DEVTOOLS_VERSION = "1.0.0-next.0";
1775
+
1776
+ export { QUERY_DEVTOOLS_IMPORTS, QUERY_DEVTOOLS_VERSION, QueryDevtoolsAboutComponent, QueryDevtoolsComponent, QueryDevtoolsJsonComponent, QueryDevtoolsJsonStylesComponent, QueryDevtoolsSettingsComponent, QueryDevtoolsTimelineStylesComponent, clampFloatToPeek, kindOf, resizedFloatRect, settledFloatRect };
1777
+ export type { JsonKind };