@ethlete/components 1.0.0-next.34 → 1.0.0-next.35

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.
@@ -8,7 +8,8 @@ import * as _angular_platform_browser from '@angular/platform-browser';
8
8
  import { Locale } from 'date-fns';
9
9
  import * as _angular_forms_signals from '@angular/forms/signals';
10
10
  import { FieldTree, ValidationError, FormValueControl, FormCheckboxControl, PathKind, SchemaPath, SchemaPathRules, validate } from '@angular/forms/signals';
11
- import { QueryFormFields, QueryFormSignals, QueryFormModel, AnyQueryCreator, RequestArgs, QueryArgsOf, ResponseType, QueryErrorResponse, RequestError, QueryArgs, QueryCreator, AnyV2QueryCreator, AnyLegacyQueryCreator, QueryDataOf, ReadonlyQuery, QueryRepositoryEvent, QueryDevtoolsEntry, Query, QueryRepository, QueryRepositoryCacheEntry, QueryKeyLockState, QueryClient, AnyQueryStack, AnyPagedQueryStack, QuerySequence, AnyBearerAuthProvider, WebSocketDevtoolsHandle, QuerySequenceStatus, AnyQuerySnapshot, AnyV2Query, AnyLegacyQuery, AnyQueryCollection } from '@ethlete/query';
11
+ import * as _ethlete_query from '@ethlete/query';
12
+ import { QueryFormFields, QueryFormSignals, QueryFormModel, AnyQueryCreator, RequestArgs, QueryArgsOf, ResponseType, QueryErrorResponse, RequestError, QueryArgs, QueryCreator, AnyV2QueryCreator, AnyLegacyQueryCreator, QueryDataOf, ReadonlyQuery, QueryRepositoryEvent, QueryRefreshCause, QueryDevtoolsEntry, Query, QueryDevtoolsRun, QueryRepository, QueryRepositoryCacheEntry, QueryKeyLockState, QueryClient, HttpRequestRetryState, HttpRequestLoadingProgressState, QueryDevtoolsStats, QueryDevtoolsStatsHandle, AnyQueryStack, AnyPagedQueryStack, QuerySequence, AnyBearerAuthProvider, WebSocketDevtoolsHandle, QueryDevtoolsFormHandle, QuerySequenceStatus, AnyQuerySnapshot, QueryDevtoolsFeature, AnyV2Query, AnyLegacyQuery, AnyQueryCollection } from '@ethlete/query';
12
13
  import * as rxjs from 'rxjs';
13
14
  import { Observable } from 'rxjs';
14
15
  import { Placement, OffsetOptions, Padding } from '@floating-ui/dom';
@@ -16038,10 +16039,21 @@ declare class PaginationSeoDirective {
16038
16039
  }
16039
16040
 
16040
16041
  type JsonKind = 'string' | 'number' | 'boolean' | 'null' | 'undefined' | 'array' | 'object';
16042
+ type JsonEntry = {
16043
+ k: string;
16044
+ v: unknown;
16045
+ };
16046
+ /** A folded slice of a container's entries, rendered as its own collapsible row. */
16047
+ type JsonChunk = {
16048
+ start: number;
16049
+ end: number;
16050
+ label: string;
16051
+ };
16041
16052
  /**
16042
16053
  * A recursive, collapsible, searchable JSON tree used by the query devtools value explorer. Renders
16043
16054
  * the *transformed* value the app actually sees (post `transformResponse`). Self-recurses via its
16044
- * own selector.
16055
+ * own selector - both for child entries and for the folded slices of an oversized container, which
16056
+ * re-enter as the same component with a `chunk` window over the same value.
16045
16057
  */
16046
16058
  declare class QueryDevtoolsJsonComponent {
16047
16059
  value: _angular_core.InputSignal<unknown>;
@@ -16049,6 +16061,11 @@ declare class QueryDevtoolsJsonComponent {
16049
16061
  depth: _angular_core.InputSignalWithTransform<number, unknown>;
16050
16062
  /** Lowercased search term; when set, the tree auto-expands and matches are highlighted. */
16051
16063
  search: _angular_core.InputSignal<string>;
16064
+ /**
16065
+ * When set, this node is not the value itself but a folded window over its entries. It shares the
16066
+ * container's `path` so a child's persisted expansion does not depend on where the slice borders land.
16067
+ */
16068
+ chunk: _angular_core.InputSignal<JsonChunk | null>;
16052
16069
  /** Full path of this node from the explorer root, used as the key for persisted expansion. */
16053
16070
  path: _angular_core.InputSignal<string>;
16054
16071
  /** Persisted paths explicitly expanded (overrides the depth default). */
@@ -16059,12 +16076,18 @@ declare class QueryDevtoolsJsonComponent {
16059
16076
  toggleFn: _angular_core.InputSignal<((path: string, expand: boolean) => void) | null>;
16060
16077
  protected kind: _angular_core.Signal<JsonKind>;
16061
16078
  protected isContainer: _angular_core.Signal<boolean>;
16062
- protected entries: _angular_core.Signal<{
16063
- k: string;
16064
- v: unknown;
16065
- }[]>;
16079
+ /** The entries this node covers: the container's own, or just the window a chunk stands for. */
16080
+ ownEntries: _angular_core.Signal<JsonEntry[]>;
16081
+ protected childChunks: _angular_core.Signal<JsonChunk[]>;
16082
+ /** Entries rendered as rows - empty while they are folded into slices instead. */
16083
+ protected visibleEntries: _angular_core.Signal<JsonEntry[]>;
16084
+ /** Slices carry their window in the key so siblings persist independently of the container. */
16085
+ private togglePath;
16086
+ private defaultExpanded;
16066
16087
  private localExpanded;
16067
16088
  protected expanded: _angular_core.Signal<boolean>;
16089
+ /** Only slices that actually contain a match unfold while searching, so a filter stays cheap. */
16090
+ private chunkHasHit;
16068
16091
  protected effectiveExpanded: _angular_core.Signal<boolean>;
16069
16092
  protected copied: _angular_core.WritableSignal<boolean>;
16070
16093
  private copiedReset$;
@@ -16077,13 +16100,15 @@ declare class QueryDevtoolsJsonComponent {
16077
16100
  protected childPath(key: string): string;
16078
16101
  protected toggle(): void;
16079
16102
  /**
16080
- * Containers copy their whole subtree as JSON; leaves copy something pasteable - a raw string
16081
- * without the display quotes, so an id or url can go straight into a search box.
16103
+ * Containers copy their whole subtree as JSON, slices only the entries they cover; leaves copy
16104
+ * something pasteable - a raw string without the display quotes, so an id or url can go straight
16105
+ * into a search box.
16082
16106
  */
16083
16107
  protected copyValue(): void;
16108
+ private chunkValue;
16084
16109
  private flagCopied;
16085
16110
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<QueryDevtoolsJsonComponent, never>;
16086
- 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; }; "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; }; }, {}, never, never, true, never>;
16111
+ 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; }; }, {}, never, never, true, never>;
16087
16112
  }
16088
16113
 
16089
16114
  /**
@@ -16099,9 +16124,121 @@ declare class QueryDevtoolsToggleComponent {
16099
16124
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<QueryDevtoolsToggleComponent, "et-query-devtools-toggle", never, {}, { "openChange": "openChange"; }, never, never, true, never>;
16100
16125
  }
16101
16126
 
16127
+ /** Whether a path exists only in the newer value, only in the older one, or in both with a new value. */
16128
+ type QueryDevtoolsDiffKind = 'added' | 'removed' | 'changed';
16129
+ /** One difference between two values, located by the path it sits at. */
16130
+ type QueryDevtoolsDiffEntry = {
16131
+ /** Where the difference sits, e.g. `$.items[2].score` or `$.items[id=7].score`. */
16132
+ path: string;
16133
+ kind: QueryDevtoolsDiffKind;
16134
+ /** The older value, or `null` for an added path. */
16135
+ before: unknown;
16136
+ /** The newer value, or `null` for a removed path. */
16137
+ after: unknown;
16138
+ };
16139
+ type QueryDevtoolsDiff = {
16140
+ entries: QueryDevtoolsDiffEntry[];
16141
+ /** Whether the walk hit its cap, so {@link entries} is only part of the difference. */
16142
+ truncated: boolean;
16143
+ };
16144
+
16102
16145
  type AnyQuery = Query<any>;
16103
- type DevtoolsTab = 'queries' | 'stacks' | 'sequences' | 'auth' | 'ws' | 'cache' | 'events';
16146
+ type DevtoolsTab = 'queries' | 'stacks' | 'sequences' | 'forms' | 'auth' | 'ws' | 'cache' | 'timeline' | 'events' | 'faults';
16147
+ /**
16148
+ * The sections of the query detail drawer. The head and its actions stay pinned above them - the detail
16149
+ * holds more than fits a column, and everything below the actions is reading material.
16150
+ */
16151
+ type DetailTab = 'overview' | 'history' | 'data';
16104
16152
  type QueryStatus = 'idle' | 'loading' | 'success' | 'error';
16153
+ /** A live-state facet the Queries list can be narrowed to. */
16154
+ type QueryListFacet = 'error' | 'loading' | 'stale' | 'idle';
16155
+ /**
16156
+ * What a tab holds, as its badge reports it: how many entries, and how many of them are failing.
16157
+ * `errorNoun` renames what the red badge counts for a tab where it is not a failure.
16158
+ */
16159
+ type TabBadge = {
16160
+ count: number;
16161
+ errors: number;
16162
+ errorNoun?: string;
16163
+ };
16164
+ /** The fault fields the panel arms from a number input, as opposed to the status it picks from a list. */
16165
+ type NumericFaultField = 'latencyMs' | 'failNext' | 'failRate';
16166
+ /**
16167
+ * A chunk of a route as rendered: literal path text, a path param (`name` is the param it fills in) or
16168
+ * the query string of the request that ran. The kind becomes the segment's class.
16169
+ */
16170
+ type RouteSegment = {
16171
+ text: string;
16172
+ kind: 'static' | 'param' | 'query';
16173
+ name?: string;
16174
+ };
16175
+ /** A query reachable from a stack or sequence card, rendered as a row that opens the detail drawer. */
16176
+ type QueryLink = {
16177
+ id: string;
16178
+ query: AnyQuery;
16179
+ method: string;
16180
+ segments: RouteSegment[];
16181
+ clientBaseUrl: string;
16182
+ stats?: QueryDevtoolsStatsHandle;
16183
+ };
16184
+ /**
16185
+ * Query stats plus the numbers the panel derives from them rather than storing: an execution that never
16186
+ * reached the network was answered from the cache, and the averages come from the running totals.
16187
+ */
16188
+ type QueryActivity = {
16189
+ stats: QueryDevtoolsStats;
16190
+ cacheServed: number;
16191
+ avgDurationMs: number | null;
16192
+ avgResponseBytes: number | null;
16193
+ hasActivity: boolean;
16194
+ };
16195
+ /**
16196
+ * What a query's request is doing beyond `loading`: which attempt it is on, the backoff it is waiting
16197
+ * out, and how much of the payload has moved. Without it a request retried three times behind a 4s
16198
+ * backoff and a plain slow one are the same yellow dot.
16199
+ */
16200
+ type RequestProgress = {
16201
+ /** @see HttpRequestSubtle.attempts */
16202
+ attempts: number;
16203
+ retry: HttpRequestRetryState | null;
16204
+ /** How much of the pending backoff is left, or `null` when none is pending. */
16205
+ retryInMs: number | null;
16206
+ progress: HttpRequestLoadingProgressState | null;
16207
+ };
16208
+ /**
16209
+ * One run of one query, placed on the timeline's shared axis. `leftPct` / `widthPct` are percentages of
16210
+ * the window every row is laid out against, which is what makes overlapping runs visible as overlap.
16211
+ */
16212
+ type TimelineRow = {
16213
+ key: string;
16214
+ entryId: string;
16215
+ method: string;
16216
+ path: string;
16217
+ run: QueryDevtoolsRun;
16218
+ leftPct: number;
16219
+ widthPct: number;
16220
+ /** How long the run took, or `null` while it is still in flight. */
16221
+ durationMs: number | null;
16222
+ };
16223
+ type Timeline = {
16224
+ rows: TimelineRow[];
16225
+ /** The start of the window the rows are laid out against. */
16226
+ startedAt: number;
16227
+ /** How long that window is - the axis every bar is a fraction of. */
16228
+ windowMs: number;
16229
+ /** How many older runs the view left out, so a capped timeline does not read as a complete one. */
16230
+ hidden: number;
16231
+ };
16232
+ /**
16233
+ * One request a refresh re-executed, as an event row lists it. A cache key is shared by every query
16234
+ * bound to it, so the row carries all of their ids - one to open, and the rest so each of those queries
16235
+ * can still find the refresh under "Refetched by". Empty for a request no registered query holds.
16236
+ */
16237
+ type RefreshedRequest = {
16238
+ queryIds: string[];
16239
+ method: string;
16240
+ path: string;
16241
+ };
16105
16242
  type EventLogItem = {
16106
16243
  id: number;
16107
16244
  timestamp: number;
@@ -16112,6 +16249,31 @@ type EventLogItem = {
16112
16249
  url: string | null;
16113
16250
  isSecure: boolean;
16114
16251
  status: number | null;
16252
+ /**
16253
+ * The registered query the request belonged to when the event fired, so the row can open it. Resolved
16254
+ * here rather than at click time so the log holds an id instead of a reference to the request itself.
16255
+ */
16256
+ queryId: string | null;
16257
+ /** What asked for a refresh, for a `queries-refreshed` row. `null` for every other type. */
16258
+ cause: QueryRefreshCause | null;
16259
+ /** The requests that refresh re-executed - the fan-out the panel could not show before. */
16260
+ refreshed: RefreshedRequest[] | null;
16261
+ };
16262
+ type PersistedState = {
16263
+ open?: boolean;
16264
+ height?: number;
16265
+ activeTab?: DevtoolsTab;
16266
+ detailTab?: DetailTab;
16267
+ selectedClientName?: string | null;
16268
+ selectedQueryId?: string | null;
16269
+ selectedFormId?: string | null;
16270
+ inspectFilterIds?: string[] | null;
16271
+ queryFilter?: string;
16272
+ queryFacets?: QueryListFacet[];
16273
+ jsonSearch?: string;
16274
+ expandedSteps?: string[];
16275
+ jsonExpanded?: string[];
16276
+ jsonCollapsed?: string[];
16115
16277
  };
16116
16278
  /**
16117
16279
  * A floating, dockable panel that inspects the live state of the signals-first `@ethlete/query`
@@ -16123,10 +16285,11 @@ type EventLogItem = {
16123
16285
  */
16124
16286
  declare class QueryDevtoolsComponent {
16125
16287
  private hostEl;
16288
+ private renderer;
16126
16289
  protected document: Document;
16127
16290
  private eventIdCounter;
16128
16291
  private lastSelectionKey;
16129
- private readonly persisted;
16292
+ readonly persisted: PersistedState;
16130
16293
  protected readonly tabs: ({
16131
16294
  id: "queries";
16132
16295
  label: string;
@@ -16136,6 +16299,9 @@ declare class QueryDevtoolsComponent {
16136
16299
  } | {
16137
16300
  id: "sequences";
16138
16301
  label: string;
16302
+ } | {
16303
+ id: "forms";
16304
+ label: string;
16139
16305
  } | {
16140
16306
  id: "auth";
16141
16307
  label: string;
@@ -16145,60 +16311,168 @@ declare class QueryDevtoolsComponent {
16145
16311
  } | {
16146
16312
  id: "cache";
16147
16313
  label: string;
16314
+ } | {
16315
+ id: "timeline";
16316
+ label: string;
16148
16317
  } | {
16149
16318
  id: "events";
16150
16319
  label: string;
16320
+ } | {
16321
+ id: "faults";
16322
+ label: string;
16323
+ })[];
16324
+ protected readonly detailTabs: ({
16325
+ id: "overview";
16326
+ label: string;
16327
+ } | {
16328
+ id: "history";
16329
+ label: string;
16330
+ } | {
16331
+ id: "data";
16332
+ label: string;
16333
+ })[];
16334
+ /** The status chips above the Queries list, in the order a problem is usually looked for. */
16335
+ protected readonly facets: ({
16336
+ id: "error";
16337
+ label: string;
16338
+ } | {
16339
+ id: "loading";
16340
+ label: string;
16341
+ } | {
16342
+ id: "stale";
16343
+ label: string;
16344
+ } | {
16345
+ id: "idle";
16346
+ label: string;
16151
16347
  })[];
16152
16348
  protected readonly shortcut: string;
16153
- protected open: _angular_core.WritableSignal<boolean>;
16154
- protected panelHeight: _angular_core.WritableSignal<number>;
16155
- protected resizing: _angular_core.WritableSignal<boolean>;
16156
- protected activeTab: _angular_core.WritableSignal<DevtoolsTab>;
16157
- protected selectedClientName: _angular_core.WritableSignal<string | null>;
16158
- protected selectedQueryId: _angular_core.WritableSignal<string | null>;
16159
- protected stackSelectedQueryId: _angular_core.WritableSignal<string | null>;
16160
- protected sequenceSelectedQueryId: _angular_core.WritableSignal<string | null>;
16161
- protected eventLog: _angular_core.WritableSignal<EventLogItem[]>;
16349
+ protected open: WritableSignal<boolean>;
16350
+ protected panelHeight: WritableSignal<number>;
16351
+ protected resizing: WritableSignal<boolean>;
16352
+ protected activeTab: WritableSignal<DevtoolsTab>;
16353
+ /** Which section of the query detail is showing. Shared by the Queries tab and both drawers. */
16354
+ protected detailTab: WritableSignal<DetailTab>;
16355
+ protected selectedClientName: WritableSignal<string | null>;
16356
+ protected selectedQueryId: WritableSignal<string | null>;
16357
+ /** The form whose detail the Forms tab has expanded. */
16358
+ protected selectedFormId: WritableSignal<string | null>;
16359
+ protected stackSelectedQueryId: WritableSignal<string | null>;
16360
+ protected sequenceSelectedQueryId: WritableSignal<string | null>;
16361
+ protected formSelectedQueryId: WritableSignal<string | null>;
16362
+ protected timelineSelectedQueryId: WritableSignal<string | null>;
16363
+ /** Free-text narrowing of the Queries list. Every whitespace-separated term has to match. */
16364
+ protected queryFilter: WritableSignal<string>;
16365
+ /** The status facets the Queries list is narrowed to. Empty means no status narrowing. */
16366
+ queryFacets: WritableSignal<ReadonlySet<QueryListFacet>>;
16367
+ protected eventLog: WritableSignal<EventLogItem[]>;
16162
16368
  /** Keys (`<entryId>:<stepIndex>`) of the sequence steps whose in/out detail is expanded. */
16163
16369
  private expandedSteps;
16164
16370
  /** Shared value-explorer search term. */
16165
- protected jsonSearch: _angular_core.WritableSignal<string>;
16371
+ protected jsonSearch: WritableSignal<string>;
16166
16372
  protected jsonSearchTerm: _angular_core.Signal<string>;
16167
16373
  /** Path-keyed value-explorer expansion overrides (persisted so open trees survive a reload). */
16168
- protected jsonExpandedPaths: _angular_core.WritableSignal<ReadonlySet<string>>;
16169
- protected jsonCollapsedPaths: _angular_core.WritableSignal<ReadonlySet<string>>;
16374
+ protected jsonExpandedPaths: WritableSignal<ReadonlySet<string>>;
16375
+ protected jsonCollapsedPaths: WritableSignal<ReadonlySet<string>>;
16170
16376
  /** Bound callback passed into the value explorer to persist per-path expansion. Assigned in the constructor. */
16171
16377
  protected toggleJsonPath: (path: string, expand: boolean) => void;
16378
+ /** The run whose response the diff is comparing, by run index, or `null` while no diff is open. */
16379
+ protected diffRunIndex: WritableSignal<number | null>;
16172
16380
  /** JIT editor state (response / args editing on the selected query). */
16173
- protected editorMode: _angular_core.WritableSignal<"none" | "response" | "args">;
16174
- protected responseDraft: _angular_core.WritableSignal<string>;
16175
- protected argsDraft: _angular_core.WritableSignal<string>;
16176
- protected editError: _angular_core.WritableSignal<string | null>;
16177
- /** Transient "Copied!" feedback for the copy-report action. */
16178
- protected copiedReport: _angular_core.WritableSignal<boolean>;
16381
+ protected editorMode: WritableSignal<"none" | "response" | "args">;
16382
+ protected responseDraft: WritableSignal<string>;
16383
+ protected argsDraft: WritableSignal<string>;
16384
+ /**
16385
+ * The text the currently open editor was seeded with. The textareas bind `value` to this and not to
16386
+ * the live draft: a `value` binding fed by the draft is written back on every keystroke, and writing
16387
+ * a textarea's `value` puts the caret at the end.
16388
+ */
16389
+ protected editorSeed: string;
16390
+ protected editError: WritableSignal<string | null>;
16391
+ /** Transient "Copied!" feedback for the copy-report, copy-as-Insomnia and copy-document actions. */
16392
+ protected copiedReport: WritableSignal<boolean>;
16393
+ protected copiedInsomnia: WritableSignal<boolean>;
16394
+ protected copiedGql: WritableSignal<boolean>;
16395
+ private copiedReset$;
16179
16396
  /** 1-second tick driving the cache freshness countdowns. */
16180
16397
  private clock;
16181
16398
  /** "Inspect" mode: hover the live UI to find the query that a component created. */
16182
- protected inspectActive: _angular_core.WritableSignal<boolean>;
16183
- protected inspectHover: _angular_core.WritableSignal<{
16399
+ protected inspectActive: WritableSignal<boolean>;
16400
+ protected inspectHover: WritableSignal<{
16184
16401
  rect: DOMRect;
16185
16402
  entries: QueryDevtoolsEntry[];
16186
16403
  } | null>;
16187
16404
  /** When set (via inspect), the Queries list is filtered to exactly these entry ids. */
16188
- protected inspectFilterIds: _angular_core.WritableSignal<string[] | null>;
16405
+ protected inspectFilterIds: WritableSignal<string[] | null>;
16189
16406
  private queryEntries;
16190
16407
  protected stackEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
16191
16408
  protected sequenceEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
16409
+ protected formEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
16192
16410
  protected authEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
16193
16411
  protected wsEntries: _angular_core.Signal<QueryDevtoolsEntry[]>;
16194
16412
  /** Unique client names present across queries and auth providers, for the Queries-tab picker. */
16195
16413
  protected clientNames: _angular_core.Signal<string[]>;
16196
16414
  /** Unique repositories (with their client name + base URL) used by the Cache and Events tabs. */
16197
16415
  private repositories;
16416
+ /**
16417
+ * Every client the Faults tab can arm, with the fault it currently carries. Clients with nothing armed
16418
+ * read as {@link EMPTY_QUERY_DEVTOOLS_FAULT} so the inputs always have a value to show.
16419
+ */
16420
+ protected faultClients: _angular_core.Signal<{
16421
+ name: string;
16422
+ baseUrl: string;
16423
+ fault: _ethlete_query.QueryDevtoolsFault;
16424
+ armed: boolean;
16425
+ }[]>;
16426
+ /**
16427
+ * The names of the clients currently carrying a fault, or `null` when none do - so a template can `@if`
16428
+ * on it. Armed faults are the one state where the panel is lying to the app on purpose, and a badge on a
16429
+ * tab that isn't open cannot say that: whichever tab you are reading, a red response has to be
16430
+ * attributable to the injection rather than to the API.
16431
+ */
16432
+ protected armedFaultClients: _angular_core.Signal<string[] | null>;
16433
+ /**
16434
+ * The queries the list is scoped to before the search box and the status chips narrow them further:
16435
+ * either the picked client's, or exactly the inspected element's.
16436
+ */
16437
+ private scopedQueries;
16438
+ private searchedQueries;
16439
+ /**
16440
+ * How many queries each status chip would leave. Counted before the active chips are applied, so a
16441
+ * chip always states what picking it yields rather than what the current selection happens to show.
16442
+ */
16443
+ protected facetCounts: _angular_core.Signal<Record<QueryListFacet, number>>;
16198
16444
  protected filteredQueries: _angular_core.Signal<{
16199
16445
  entry: QueryDevtoolsEntry;
16200
16446
  query: AnyQuery;
16201
16447
  }[]>;
16448
+ /** How many queries are in scope, which is what the list would show unfiltered. */
16449
+ protected scopedQueryCount: _angular_core.Signal<number>;
16450
+ /** Whether the search box or a status chip is narrowing the list beyond its scope. */
16451
+ protected isQueryListNarrowed: _angular_core.Signal<boolean>;
16452
+ /**
16453
+ * Every run the scoped queries have recorded, oldest first. The client picker and the inspection
16454
+ * filter narrow the timeline the same way they narrow the Queries list.
16455
+ */
16456
+ private scopedRuns;
16457
+ /**
16458
+ * Every scoped run laid out on one axis, so a stampede reads as overlapping bars and a chain as a
16459
+ * staircase - which is what the Events tab's flat list of wall-clock times cannot show.
16460
+ */
16461
+ protected timeline: _angular_core.Signal<Timeline>;
16462
+ /** Axis labels for the timeline, as offsets from the window's start. */
16463
+ protected timelineTicks: _angular_core.Signal<{
16464
+ pct: number;
16465
+ label: string;
16466
+ }[]>;
16467
+ /** How many runs every query has recorded, for the Timeline tab's badge. */
16468
+ private runTotals;
16469
+ /** Cache entries across every client, for the Cache tab's badge. */
16470
+ private cacheEntryCount;
16471
+ /**
16472
+ * What each tab holds, so a failing query in a tab that is not open is still visible. Reading it
16473
+ * subscribes the tab bar to every entry's live state - which is the point of the badges.
16474
+ */
16475
+ protected tabBadges: _angular_core.Signal<Record<DevtoolsTab, TabBadge>>;
16202
16476
  protected selectedQuery: _angular_core.Signal<{
16203
16477
  entry: QueryDevtoolsEntry;
16204
16478
  query: AnyQuery;
@@ -16211,6 +16485,14 @@ declare class QueryDevtoolsComponent {
16211
16485
  entry: QueryDevtoolsEntry;
16212
16486
  query: AnyQuery;
16213
16487
  } | null>;
16488
+ protected formSelectedQuery: _angular_core.Signal<{
16489
+ entry: QueryDevtoolsEntry;
16490
+ query: AnyQuery;
16491
+ } | null>;
16492
+ protected timelineSelectedQuery: _angular_core.Signal<{
16493
+ entry: QueryDevtoolsEntry;
16494
+ query: AnyQuery;
16495
+ } | null>;
16214
16496
  protected cacheView: _angular_core.Signal<{
16215
16497
  name: string;
16216
16498
  baseUrl: string;
@@ -16221,16 +16503,98 @@ declare class QueryDevtoolsComponent {
16221
16503
  }[]>;
16222
16504
  /** Map of a component's host element to the query entries it created (for the inspect tool). */
16223
16505
  private elementQueryMap;
16506
+ protected readonly FAULT_STATUSES: {
16507
+ status: number;
16508
+ label: string;
16509
+ retryable: boolean;
16510
+ }[];
16511
+ protected readonly CLEAR_FAULTS: (clientName?: string) => void;
16512
+ /** The ceiling each numeric fault field is clamped to. */
16513
+ private readonly FAULT_LIMITS;
16224
16514
  constructor();
16225
16515
  protected toggleOpen(): void;
16226
16516
  protected clearEvents(): void;
16227
16517
  protected selectClient(name: string | null): void;
16228
16518
  protected clearInspectFilter(): void;
16519
+ protected toggleFacet(facet: QueryListFacet): void;
16520
+ /** Drops the search term and the status chips, keeping the client / inspection scope. */
16521
+ protected clearQueryFilters(): void;
16229
16522
  protected toggleInspect(): void;
16230
16523
  protected startResize(event: PointerEvent): void;
16231
16524
  protected inspectLabel(entries: QueryDevtoolsEntry[]): string;
16525
+ /**
16526
+ * A query's route, split so the template can tell its static path from its path params (each carrying
16527
+ * the value the query used, or `:<name>` while it has none yet) and from the query string of the
16528
+ * request that ran - which is what tells two requests to the same endpoint apart.
16529
+ */
16530
+ protected routeSegments(entry: QueryDevtoolsEntry | undefined, query: AnyQuery): RouteSegment[];
16531
+ /** The full URL of the request a query last made, or `null` while it has not executed. */
16532
+ protected requestUrl(query: AnyQuery): string | null;
16533
+ /**
16534
+ * The args of a query. A query executed imperatively (`execute({ args })`, a sequence step, an auth
16535
+ * query) never writes them to its own `args` signal - only the `withArgs` feature does - so the args
16536
+ * its current request was built from stand in.
16537
+ */
16538
+ protected queryArgs(query: AnyQuery): Omit<any, "response"> | null;
16232
16539
  protected queryStatus(query: AnyQuery): QueryStatus;
16540
+ /**
16541
+ * A request in flight is already refreshing, so reporting it as stale on top of `loading` is noise -
16542
+ * the same precedence the cache tab's freshness column applies.
16543
+ */
16233
16544
  protected isStale(query: AnyQuery): boolean;
16545
+ /**
16546
+ * What a query's current request is doing beyond being loading, or `null` when there is nothing beyond
16547
+ * the status dot to say - so the readout only takes up room while it carries something.
16548
+ */
16549
+ protected requestProgress(query: AnyQuery): RequestProgress | null;
16550
+ /** Why a retry was scheduled, as the panel spells it out. A status of 0 never reached the server. */
16551
+ protected retryCause(status: number): string;
16552
+ protected formatPercent(percentage: number): string;
16553
+ /** A countdown in whole seconds, spelled out the way the cache freshness column does. */
16554
+ protected formatCountdown(ms: number | null): string;
16555
+ protected queryActivity(entry: QueryDevtoolsEntry): QueryActivity;
16556
+ protected linkActivity(link: QueryLink): QueryActivity;
16557
+ protected stackActivity(stack: AnyQueryStack | AnyPagedQueryStack): QueryActivity;
16558
+ protected sequenceActivity(sequence: QuerySequence<unknown[]>): QueryActivity;
16559
+ /** Clears an entry's counters and run history, so the next interaction can be measured on its own. */
16560
+ protected resetStats(entry: QueryDevtoolsEntry): void;
16561
+ /** The same, for every query the timeline covers. */
16562
+ protected resetTimeline(): void;
16563
+ /** A query's runs, newest first - the order a history is read in. */
16564
+ protected queryRuns(entry: QueryDevtoolsEntry): QueryDevtoolsRun[];
16565
+ /**
16566
+ * What a run's status dot and timeline bar colour by. `pending` reuses the panel's loading colour;
16567
+ * `aborted` matches no rule and so falls back to the neutral one.
16568
+ */
16569
+ protected runStatus(run: QueryDevtoolsRun): "loading" | "error" | "success" | "aborted";
16570
+ /** Whether a run can be diffed: it still holds its body, and so does an older run of the same query. */
16571
+ protected canDiffRun(entry: QueryDevtoolsEntry, run: QueryDevtoolsRun): boolean;
16572
+ protected toggleRunDiff(run: QueryDevtoolsRun): void;
16573
+ /**
16574
+ * The response diff of the picked run against the newest older run that still holds a body - which is
16575
+ * not necessarily the run right before it, since a failed run has none to compare.
16576
+ *
16577
+ * `null` unless a run is picked, so a closed diff costs nothing to walk.
16578
+ */
16579
+ protected responseDiff(entry: QueryDevtoolsEntry): {
16580
+ before: QueryDevtoolsRun;
16581
+ after: QueryDevtoolsRun;
16582
+ diff: QueryDevtoolsDiff;
16583
+ } | null;
16584
+ /** Opens a query in the Queries tab - the Events tab is a way in, not a dead end. */
16585
+ protected selectQuery(id: string): void;
16586
+ protected selectTimelineRow(row: TimelineRow): void;
16587
+ protected selectEventRow(item: EventLogItem): void;
16588
+ /** A value on one line, for a diff row or a form field. The full tree would bury the row it sits in. */
16589
+ protected inlineValue(value: unknown): string;
16590
+ /** A byte count the way a network panel spells one out. */
16591
+ protected formatBytes(bytes: number): string;
16592
+ /**
16593
+ * A transferred size, marked `≈` when any part of it was measured from a decoded body instead of read
16594
+ * from a `content-length` header - such a size ignores transport compression.
16595
+ */
16596
+ protected formatTransferred(bytes: number, isEstimated: boolean): string;
16597
+ protected formatDuration(ms: number | null): string;
16234
16598
  protected executeQuery(query: AnyQuery, allowCache: boolean): void;
16235
16599
  protected resetQuery(query: AnyQuery): void;
16236
16600
  /**
@@ -16239,6 +16603,15 @@ declare class QueryDevtoolsComponent {
16239
16603
  * a plain-text fallback.
16240
16604
  */
16241
16605
  protected copyReport(entry: QueryDevtoolsEntry, query: AnyQuery): void;
16606
+ /** Copies one query as an Insomnia collection, for `Import > From Clipboard`. */
16607
+ protected copyInsomniaRequest(entry: QueryDevtoolsEntry, query: AnyQuery): void;
16608
+ /** Copies the GraphQL document as displayed — dedented, so it pastes straight into a playground. */
16609
+ protected copyGqlDocument(doc: string): void;
16610
+ /**
16611
+ * Downloads every query currently listed (so the client filter and the inspection filter both apply)
16612
+ * as one Insomnia collection, filed into a folder per query client.
16613
+ */
16614
+ protected downloadInsomniaCollection(): void;
16242
16615
  protected openResponseEditor(query: AnyQuery): void;
16243
16616
  protected openArgsEditor(query: AnyQuery): void;
16244
16617
  protected applyResponse(query: AnyQuery): void;
@@ -16248,6 +16621,21 @@ declare class QueryDevtoolsComponent {
16248
16621
  protected forceError(query: AnyQuery): void;
16249
16622
  protected forceEmpty(query: AnyQuery): void;
16250
16623
  protected clearForced(query: AnyQuery): void;
16624
+ /**
16625
+ * Arms one numeric field of a client's fault from an input's raw value. Clamped here rather than left
16626
+ * to the input's `min`/`max`, which a typed-in (or pasted) value ignores.
16627
+ */
16628
+ protected armFaultValue(options: {
16629
+ clientName: string;
16630
+ field: NumericFaultField;
16631
+ value: string;
16632
+ }): void;
16633
+ protected armFaultStatus(options: {
16634
+ clientName: string;
16635
+ value: string;
16636
+ }): void;
16637
+ /** Whether the default retry policy retries the status a client is armed to fail with. */
16638
+ protected isFaultStatusRetryable(status: number): boolean;
16251
16639
  protected refetchCacheEntry(entry: QueryRepositoryCacheEntry): void;
16252
16640
  protected evictCacheEntry(repository: QueryRepository, key: string): void;
16253
16641
  protected cacheFreshness(entry: QueryRepositoryCacheEntry): string;
@@ -16271,33 +16659,40 @@ declare class QueryDevtoolsComponent {
16271
16659
  protected asSequence(entry: QueryDevtoolsEntry): QuerySequence<unknown[]>;
16272
16660
  protected asAuth(entry: QueryDevtoolsEntry): AnyBearerAuthProvider;
16273
16661
  protected asWs(entry: QueryDevtoolsEntry): WebSocketDevtoolsHandle;
16662
+ protected asForm(entry: QueryDevtoolsEntry): QueryDevtoolsFormHandle;
16663
+ /**
16664
+ * The queries a form feeds, discovered from the reads its `value()` recorded while their args were
16665
+ * built - so a form that nothing consumes yet reads as exactly that.
16666
+ */
16667
+ protected queriesDrivenByForm(entry: QueryDevtoolsEntry): QueryLink[];
16668
+ /** The reverse: the forms whose value a query's args read. */
16669
+ protected formsDrivingQuery(entry: QueryDevtoolsEntry): QueryDevtoolsEntry[];
16670
+ protected selectForm(id: string): void;
16671
+ /**
16672
+ * The refreshes that re-executed a query, newest first - the answer to "why did this refetch?". Read
16673
+ * off the event log, so it goes back exactly as far as the log does.
16674
+ */
16675
+ protected refreshesFor(entryId: string): {
16676
+ id: number;
16677
+ timestamp: number;
16678
+ label: string;
16679
+ }[];
16680
+ /** What asked for a refresh, on one line. */
16681
+ protected causeLabel(cause: QueryRefreshCause): string;
16274
16682
  /** Derives the per-step status of a sequence step from its live progress signals. */
16275
16683
  protected sequenceStepStatus(sequence: QuerySequence<unknown[]>, index: number): QuerySequenceStatus;
16276
16684
  protected authTokenPayload(auth: AnyBearerAuthProvider): Record<string, unknown> | null;
16277
- protected queriesForStack(stack: AnyQueryStack | AnyPagedQueryStack): {
16278
- id: string;
16279
- query: AnyQuery;
16280
- method: string;
16281
- route: string;
16282
- clientName: string;
16283
- clientBaseUrl: string;
16284
- }[];
16685
+ protected queriesForStack(stack: AnyQueryStack | AnyPagedQueryStack): QueryLink[];
16285
16686
  /** Identifying info for a stack, derived from its (uniform) inner queries. */
16286
16687
  protected stackIdentity(stack: AnyQueryStack | AnyPagedQueryStack): {
16287
16688
  method: string;
16288
- route: string;
16689
+ segments: RouteSegment[];
16289
16690
  baseUrl: string;
16290
16691
  };
16291
- protected authFeatures(auth: AnyBearerAuthProvider): string[];
16292
16692
  protected authQueryKeys(auth: AnyBearerAuthProvider): string[];
16293
16693
  /** Countdown to the access-token's `exp` (the point a refresh becomes due), or `null` if unknown. */
16294
16694
  protected authTokenExpiry(auth: AnyBearerAuthProvider): string | null;
16295
- protected queriesForSequence(sequence: QuerySequence<unknown[]>): {
16296
- id: string;
16297
- query: AnyQuery;
16298
- method: string;
16299
- route: string;
16300
- }[];
16695
+ protected queriesForSequence(sequence: QuerySequence<unknown[]>): QueryLink[];
16301
16696
  /** The snapshot of a sequence step, once it has run (holds the args in and the response/error out). */
16302
16697
  protected stepSnapshot(sequence: QuerySequence<unknown[]>, index: number): AnyQuerySnapshot | null;
16303
16698
  protected isStepExpanded(entryId: string, index: number): boolean;
@@ -16305,17 +16700,72 @@ declare class QueryDevtoolsComponent {
16305
16700
  /** Dedents a GraphQL document (template-literal indentation) for readable display. */
16306
16701
  protected gqlDocument(doc: string): string;
16307
16702
  protected featureLabel(type: string): string;
16703
+ /** A feature and its options on one line, for a report or a chip's tooltip. */
16704
+ featureSummary(feature: QueryDevtoolsFeature): string;
16705
+ /** The features of the client behind a cache tab card, or `null` for a client without any. */
16706
+ protected clientFeatures(client: QueryClient | null | undefined): QueryDevtoolsFeature[] | null;
16308
16707
  protected formatTime(timestamp: number | null): string;
16309
16708
  protected eventTypeLabel(event: EventLogItem): string;
16709
+ /** A registered query as a row that opens the detail drawer. */
16710
+ private queryLinkFor;
16711
+ /** The activity of one entry, or the total of a group of them (a stack's queries, a whole tab). */
16712
+ private activityOf;
16713
+ /** An activity summary on one line, for a report. `null` for a query that has not run. */
16714
+ private activitySummary;
16715
+ /** A query matches the chips if it is in any of the picked states - chips widen, they don't intersect. */
16716
+ private matchesFacets;
16717
+ /**
16718
+ * What the search box matches against: what the row shows, plus the path of the request that ran for a
16719
+ * query whose template differs from it.
16720
+ *
16721
+ * Deliberately not the origin or the client name - both repeat across most entries, so a short term
16722
+ * ("p") would match nearly everything through the host name. Scoping to a client is the picker's job.
16723
+ */
16724
+ private queryHaystack;
16725
+ /** {@link routeSegments} as a plain string, for the places that cannot render markup. */
16726
+ private queryRoute;
16727
+ /**
16728
+ * Describes a query the way a replay outside the app needs it: the URL, headers and body of the
16729
+ * request it last made, or - for a query that has not run - what its current args would send.
16730
+ */
16731
+ private insomniaRequest;
16732
+ /**
16733
+ * The token refreshes the given requests authenticate with - one per auth provider they name, and
16734
+ * only for a provider that is logged in, since a refresh request without a refresh token has
16735
+ * nothing to send.
16736
+ */
16737
+ private insomniaTokenRefreshes;
16738
+ private insomniaTokenRefresh;
16739
+ /**
16740
+ * Where the access token sits in the refresh response. A provider's `extractTokens` can pull it out
16741
+ * of any shape, so the path is recovered by finding the live token in the last auth response - with
16742
+ * the default extractor's `$.accessToken` as the fallback.
16743
+ */
16744
+ private accessTokenPath;
16745
+ /**
16746
+ * How long Insomnia may reuse a stored refresh response: the access token's own lifetime with a
16747
+ * margin, so the chain refreshes shortly before the token it hands out would expire. Capped at an
16748
+ * hour - a long-lived (or bogus) `exp` should still hand out a token minted this session.
16749
+ */
16750
+ private accessTokenMaxAge;
16751
+ /**
16752
+ * The headers a replay needs, including the ones the query client adds. Header providers can throw
16753
+ * (a secure query's needs an access token), in which case the request is exported without them.
16754
+ */
16755
+ private insomniaHeaders;
16756
+ private downloadFile;
16310
16757
  private stepKey;
16311
16758
  private applyResize;
16312
16759
  private updateInspectHover;
16313
16760
  private selectInspectedQuery;
16314
16761
  private selectionKey;
16762
+ /** Writes to the clipboard and ticks `copied` on success. `html` is omitted for plain-text payloads. */
16315
16763
  private writeToClipboard;
16316
16764
  private responseStatus;
16317
16765
  private findQuery;
16318
16766
  private pushEvent;
16767
+ private refreshedRequestOf;
16768
+ private pushEventItem;
16319
16769
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<QueryDevtoolsComponent, never>;
16320
16770
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<QueryDevtoolsComponent, "et-query-devtools", never, {}, {}, never, never, true, never>;
16321
16771
  }