@ethlete/components 1.0.0-next.28 → 1.0.0-next.30

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.
@@ -1,17 +1,18 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { ViewEncapsulation, Component, Directive, input, booleanAttribute, signal, effect, numberAttribute, computed, inject, ElementRef, afterNextRender, DestroyRef, InjectionToken, model, linkedSignal, output, Injector, runInInjectionContext, untracked, ApplicationRef, viewChild, ViewContainerRef, createEnvironmentInjector, DOCUMENT, EnvironmentInjector, inputBinding, createComponent, TemplateRef, afterRenderEffect, contentChild, viewChildren, outputBinding, afterEveryRender, isSignal, assertInInjectionContext, isDevMode, provideEnvironmentInitializer, PLATFORM_ID, contentChildren } from '@angular/core';
3
3
  import * as i1 from '@ethlete/core';
4
- import { injectStyleManager, SurfaceInteractiveDirective, RuntimeError, createCanAnimateSignal, ColorInteractiveDirective, ProvideColorDirective, injectErrorTheme, signalElementDimensions, createProvider, ProvideSurfaceDirective, COLOR_PROVIDER, SURFACE_PROVIDER, injectSurfaceThemes, injectSurfaceContextTracker, injectRenderer, injectBoundaryElement, ANIMATED_LIFECYCLE_TOKEN, resolveAppRootColorProvider, resolveSurfaceByElevation, provideBoundaryElement, AnimatedLifecycleDirective, injectBreakpointObserver, isHTMLElement, equal, animationDebugLog, createRootProvider, injectOverlayRuntime, injectAnimatedBlockSize, randomId, createStaticRootProvider, forceReflow, nextFrame, elementCanScroll, createSwipeTracker, createComponentId, injectObserveBreakpoint, AutoSurfaceDirective, AnimatableDirective, ColorInteractiveContainerDirective, ColorInteractiveExcludeDirective, createFlipAnimation, injectPrefersReducedMotion, createFlipAnimationGroup, ColorInteractiveHasFocusDirective, htmlToMarkdown, markdownToHtml, injectHasTouchInput, injectLocale, clamp, signalHostElementDimensions, DragHandleDirective, ResizeHandlesComponent, copyToClipboard, ScrollObserverDirective, signalElementScrollState, ScrollObserverStartDirective, ScrollObserverEndDirective, applyHostListener, injectQueryParam, createUnsavedChangesTracker, createDocumentElementSignal, fromNextFrame, injectRoute, AnimatedIfDirective, applyInitialFocus, syncSignal, typedBreakpointTransform, signalElementChildren, signalElementIntersection, signalHostAttributes, signalHostClasses, signalHostStyles, getScrollContainerTarget, getScrollItemTarget, getElementScrollCoordinates, provideBreakpointInstance, signalClasses, useCursorDragScroll, signalStyles, getScrollSnapTarget, injectViewportSize, signalHostElementIntersection, setInputSignal, injectHostElement, injectRouterEvent, canUseSessionMemory, createAutoSessionMemoryKey, createSessionMemory, injectFocusVisibleTracker } from '@ethlete/core';
4
+ import { injectStyleManager, SurfaceInteractiveDirective, RuntimeError, createCanAnimateSignal, ColorInteractiveDirective, ProvideColorDirective, injectErrorTheme, signalElementDimensions, createProvider, ProvideSurfaceDirective, COLOR_PROVIDER, SURFACE_PROVIDER, injectSurfaceThemes, injectSurfaceThemesPrefix, injectSurfaceContextTracker, injectRenderer, injectBoundaryElement, ANIMATED_LIFECYCLE_TOKEN, resolveAppRootColorProvider, resolveSurfaceByElevation, createCssSurfaceName, provideBoundaryElement, AnimatedLifecycleDirective, injectBreakpointObserver, isHTMLElement, equal, animationDebugLog, createRootProvider, injectOverlayRuntime, injectAnimatedBlockSize, randomId, createStaticRootProvider, forceReflow, nextFrame, elementCanScroll, createSwipeTracker, createComponentId, injectObserveBreakpoint, AutoSurfaceDirective, AnimatableDirective, ColorInteractiveContainerDirective, ColorInteractiveExcludeDirective, createFlipAnimation, injectPrefersReducedMotion, createFlipAnimationGroup, ColorInteractiveHasFocusDirective, htmlToMarkdown, markdownToHtml, injectHasTouchInput, injectLocale, clamp, signalHostElementDimensions, DragHandleDirective, ResizeHandlesComponent, copyToClipboard, ScrollObserverDirective, signalElementScrollState, ScrollObserverStartDirective, ScrollObserverEndDirective, applyHostListener, injectQueryParam, createUnsavedChangesTracker, createDocumentElementSignal, fromNextFrame, injectRoute, AnimatedIfDirective, applyInitialFocus, syncSignal, typedBreakpointTransform, signalElementChildren, signalElementIntersection, signalHostAttributes, signalHostClasses, signalHostStyles, getScrollContainerTarget, getScrollItemTarget, getElementScrollCoordinates, provideBreakpointInstance, signalClasses, useCursorDragScroll, signalStyles, getScrollSnapTarget, injectViewportSize, signalHostElementIntersection, setInputSignal, injectHostElement, injectRouterEvent, canUseSessionMemory, createAutoSessionMemoryKey, createSessionMemory, injectFocusVisibleTracker } from '@ethlete/core';
5
5
  import { NgTemplateOutlet, DOCUMENT as DOCUMENT$1, NgComponentOutlet, Location, isPlatformBrowser } from '@angular/common';
6
6
  import { DomSanitizer } from '@angular/platform-browser';
7
7
  import { endOfMonth, startOfWeek, startOfMonth, addDays, addYears, addMonths, startOfDay, isSameMonth, getDate, setDate, lastDayOfMonth, isBefore, format, isSameDay, isAfter, parse, isValid, setMilliseconds, setSeconds, setMinutes, setHours } from 'date-fns';
8
- import { withArgs, queryStateSignal, isQueryStateSuccess, isQueryStateFailure, isQueryStateLoading, queryComputed } from '@ethlete/query';
9
- import { defer, filter, take, map, finalize, timer, switchMap, EMPTY, tap, Subject, fromEvent, merge, takeUntil, of, isObservable, from, catchError, distinctUntilChanged, concat, scan, combineLatest, debounceTime, animationFrameScheduler, exhaustMap, Observable, shareReplay, interval } from 'rxjs';
8
+ import { withArgs, queryStateSignal, isQueryStateSuccess, isQueryStateFailure, isQueryStateLoading, queryComputed, queryDevtoolsEntries, createQueryErrorResponse } from '@ethlete/query';
9
+ import { defer, filter, take, map, finalize, timer, switchMap, EMPTY, tap, Subject, fromEvent, merge, takeUntil, of, isObservable, from, catchError, distinctUntilChanged, concat, scan, combineLatest, debounceTime, animationFrameScheduler, interval, exhaustMap, Observable, shareReplay } from 'rxjs';
10
10
  import { toObservable, takeUntilDestroyed, toSignal, outputToObservable, outputFromObservable, rxResource } from '@angular/core/rxjs-interop';
11
11
  import { createManagedMetadataKey, metadata, validate, FORM_FIELD } from '@angular/forms/signals';
12
12
  import { tap as tap$1 } from 'rxjs/operators';
13
13
  import * as i1$1 from '@angular/router';
14
14
  import { Router, RouterLink, RouterLinkActive, NavigationEnd } from '@angular/router';
15
+ import { HttpErrorResponse } from '@angular/common/http';
15
16
 
16
17
  class ButtonPropertiesStylesComponent {
17
18
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: ButtonPropertiesStylesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
@@ -2912,6 +2913,7 @@ const OVERLAY_ERROR_CODES = {
2912
2913
  NO_CLOSEST_OVERLAY: 1205,
2913
2914
  NESTED_OVERLAY_MAIN: 1206,
2914
2915
  REF_OUTSIDE_OVERLAY: 1207,
2916
+ MISSING_OVERLAY_MAIN: 1208,
2915
2917
  };
2916
2918
 
2917
2919
  const getClosestOverlay = (element, openOverlays) => {
@@ -3110,6 +3112,7 @@ class OverlayContainerComponent {
3110
3112
  this.appRef = inject(ApplicationRef);
3111
3113
  this.overlayRef = inject(OVERLAY_REF);
3112
3114
  this.surfaceThemes = injectSurfaceThemes({ optional: true });
3115
+ this.surfacePrefix = injectSurfaceThemesPrefix({ optional: true });
3113
3116
  this.surfaceContextTracker = injectSurfaceContextTracker();
3114
3117
  this.renderer = injectRenderer();
3115
3118
  this.rootBoundary = injectBoundaryElement();
@@ -3133,9 +3136,18 @@ class OverlayContainerComponent {
3133
3136
  this.ownColorProvider.syncWithProvider(contextColorProvider);
3134
3137
  }
3135
3138
  if (this.surfaceThemes) {
3136
- const parentType = this.parentSurfaceProvider?.surfaceType() ?? 'dark';
3139
+ // Resolve the surface the overlay's trigger visually sits on, so the overlay elevates one
3140
+ // level above it. The trigger keeps its *declaration* injector even when projected/portaled
3141
+ // into another overlay, so DI (`parentSurfaceProvider`) reports the wrong surface for a
3142
+ // trigger rendered inside a dialog/menu pane, and reports nothing for the anchored panel
3143
+ // overlays (select/menu/date) which mount with no DI link to the trigger at all. The
3144
+ // trigger's nearest painted surface ancestor *in the DOM* is authoritative in every case —
3145
+ // an overlay pane and a plain elevated card both carry the surface class — so read that,
3146
+ // and fall back to DI (openers that pass a viewContainerRef but no origin).
3147
+ const parentSurface = this.resolveOriginSurface() ?? this.parentDiSurface();
3148
+ const parentType = parentSurface?.type ?? 'dark';
3137
3149
  const hasBackdrop = this.overlayRef.config.hasBackdrop ?? this.overlayRef.config.mode !== 'non-modal';
3138
- const elevation = hasBackdrop || !this.parentSurfaceProvider ? 1 : this.parentSurfaceProvider.elevation() + 1;
3150
+ const elevation = hasBackdrop || !parentSurface ? 1 : parentSurface.elevation + 1;
3139
3151
  const resolved = resolveSurfaceByElevation(this.surfaceThemes, parentType, elevation);
3140
3152
  if (resolved) {
3141
3153
  this.ownSurfaceProvider.forceSurface(resolved.name);
@@ -3216,6 +3228,49 @@ class OverlayContainerComponent {
3216
3228
  * dialog), otherwise the first painted element in the rendered content (menu/tooltip/date-picker
3217
3229
  * paint a nested element, potentially at a higher elevation than the host's forced surface).
3218
3230
  */
3231
+ parentDiSurface() {
3232
+ const provider = this.parentSurfaceProvider;
3233
+ if (!provider)
3234
+ return null;
3235
+ return { elevation: provider.elevation(), type: provider.surfaceType() ?? 'dark' };
3236
+ }
3237
+ /**
3238
+ * The surface of the nearest ancestor of the overlay's origin (trigger) that paints a resolved
3239
+ * surface — walking the real DOM so it sees *through* the portal/projection boundary that hides
3240
+ * the true parent surface from DI. Returns null when there is no origin element or no surfaced
3241
+ * ancestor (the overlay then falls back to DI, and ultimately to elevation 1).
3242
+ */
3243
+ resolveOriginSurface() {
3244
+ const themes = this.surfaceThemes;
3245
+ if (!themes)
3246
+ return null;
3247
+ const origin = this.resolveOriginElement();
3248
+ if (!origin)
3249
+ return null;
3250
+ const prefix = this.surfacePrefix || 'et';
3251
+ const themeByClass = new Map();
3252
+ for (const theme of themes) {
3253
+ themeByClass.set(`${prefix}-surface--${createCssSurfaceName(theme.name)}`, theme);
3254
+ }
3255
+ for (let el = origin; el; el = el.parentElement) {
3256
+ for (const cls of Array.from(el.classList)) {
3257
+ const theme = themeByClass.get(cls);
3258
+ if (theme)
3259
+ return { elevation: theme.elevation, type: theme.type };
3260
+ }
3261
+ }
3262
+ return null;
3263
+ }
3264
+ resolveOriginElement() {
3265
+ const origin = this.overlayRef.config.origin;
3266
+ if (origin instanceof HTMLElement)
3267
+ return origin;
3268
+ if (origin instanceof Event) {
3269
+ const target = origin.target ?? origin.currentTarget;
3270
+ return target instanceof HTMLElement ? target : null;
3271
+ }
3272
+ return null;
3273
+ }
3219
3274
  isSheetHost(host) {
3220
3275
  return (host.classList.contains('et-overlay--bottom-sheet') ||
3221
3276
  host.classList.contains('et-overlay--top-sheet') ||
@@ -28364,6 +28419,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
28364
28419
  }]
28365
28420
  }], ctorParameters: () => [] });
28366
28421
 
28422
+ const OVERLAY_MAIN_TOKEN = new InjectionToken('OVERLAY_MAIN_TOKEN');
28423
+ class OverlayMainDirective {
28424
+ constructor() {
28425
+ this.parent = inject(OVERLAY_MAIN_TOKEN, { optional: true, skipSelf: true });
28426
+ this.elementRef = inject(ElementRef);
28427
+ this.overlayRef = inject(OVERLAY_REF, { optional: true });
28428
+ this.overlayManager = injectOverlayManager();
28429
+ this.enabled = input(true, { ...(ngDevMode ? { debugName: "enabled" } : /* istanbul ignore next */ {}), alias: 'etOverlayMain', transform: booleanAttribute });
28430
+ }
28431
+ ngOnInit() {
28432
+ this.overlayRef = resolveClosestOverlay({
28433
+ overlayRef: this.overlayRef,
28434
+ element: this.elementRef,
28435
+ openOverlays: this.overlayManager.openOverlays(),
28436
+ });
28437
+ if (this.parent) {
28438
+ if (this.overlayRef.id === this.parent.overlayRef?.id && this.parent.enabled() && this.enabled()) {
28439
+ throw new RuntimeError(OVERLAY_ERROR_CODES.NESTED_OVERLAY_MAIN, '[OverlayMainDirective] An overlay must not contain nested <et-overlay-main> elements or etOverlayMain directives.');
28440
+ }
28441
+ }
28442
+ }
28443
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: OverlayMainDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
28444
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: OverlayMainDirective, isStandalone: true, selector: "[etOverlayMain], et-overlay-main", inputs: { enabled: { classPropertyName: "enabled", publicName: "etOverlayMain", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.et-overlay-main": "enabled()" } }, providers: [
28445
+ {
28446
+ provide: OVERLAY_MAIN_TOKEN,
28447
+ useExisting: OverlayMainDirective,
28448
+ },
28449
+ ], ngImport: i0 }); }
28450
+ }
28451
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: OverlayMainDirective, decorators: [{
28452
+ type: Directive,
28453
+ args: [{
28454
+ selector: '[etOverlayMain], et-overlay-main',
28455
+ providers: [
28456
+ {
28457
+ provide: OVERLAY_MAIN_TOKEN,
28458
+ useExisting: OverlayMainDirective,
28459
+ },
28460
+ ],
28461
+ host: {
28462
+ '[class.et-overlay-main]': 'enabled()',
28463
+ },
28464
+ }]
28465
+ }], propDecorators: { enabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "etOverlayMain", required: false }] }] } });
28466
+
28367
28467
  const OVERLAY_BODY_TOKEN = new InjectionToken('OVERLAY_BODY_TOKEN');
28368
28468
  class OverlayBodyComponent {
28369
28469
  constructor() {
@@ -28371,6 +28471,7 @@ class OverlayBodyComponent {
28371
28471
  this.elementRef = inject(ElementRef);
28372
28472
  this.scrollObserver = inject(ScrollObserverDirective);
28373
28473
  this.overlayManager = injectOverlayManager();
28474
+ this.overlayMain = inject(OVERLAY_MAIN_TOKEN, { optional: true });
28374
28475
  this.dividers = input(false, /* @ts-ignore */
28375
28476
  ...(ngDevMode ? [{ debugName: "dividers" }] : /* istanbul ignore next */ []));
28376
28477
  this.dividersEnabled = computed(() => this.dividers() === 'dynamic' || this.dividers() === 'static', /* @ts-ignore */
@@ -28398,6 +28499,9 @@ class OverlayBodyComponent {
28398
28499
  nextFrame(() => this.enableDividerAnimations.set(true));
28399
28500
  }
28400
28501
  ngOnInit() {
28502
+ if (!this.overlayMain) {
28503
+ throw new RuntimeError(OVERLAY_ERROR_CODES.MISSING_OVERLAY_MAIN, '[OverlayBodyComponent] An overlay body must be used inside an <et-overlay-main> element or a host with the etOverlayMain directive.');
28504
+ }
28401
28505
  this.overlayRef = resolveClosestOverlay({
28402
28506
  overlayRef: this.overlayRef,
28403
28507
  element: this.elementRef,
@@ -28610,8 +28714,12 @@ class OverlayFooterDirective {
28610
28714
  this.overlayRef = inject(OVERLAY_REF, { optional: true });
28611
28715
  this.elementRef = inject(ElementRef);
28612
28716
  this.overlayManager = injectOverlayManager();
28717
+ this.overlayMain = inject(OVERLAY_MAIN_TOKEN, { optional: true });
28613
28718
  }
28614
28719
  ngOnInit() {
28720
+ if (!this.overlayMain) {
28721
+ throw new RuntimeError(OVERLAY_ERROR_CODES.MISSING_OVERLAY_MAIN, '[OverlayFooterDirective] An overlay footer must be used inside an <et-overlay-main> element or a host with the etOverlayMain directive.');
28722
+ }
28615
28723
  this.overlayRef = resolveClosestOverlay({
28616
28724
  overlayRef: this.overlayRef,
28617
28725
  element: this.elementRef,
@@ -28648,8 +28756,12 @@ class OverlayHeaderDirective {
28648
28756
  this.overlayRef = inject(OVERLAY_REF, { optional: true });
28649
28757
  this.elementRef = inject(ElementRef);
28650
28758
  this.overlayManager = injectOverlayManager();
28759
+ this.overlayMain = inject(OVERLAY_MAIN_TOKEN, { optional: true });
28651
28760
  }
28652
28761
  ngOnInit() {
28762
+ if (!this.overlayMain) {
28763
+ throw new RuntimeError(OVERLAY_ERROR_CODES.MISSING_OVERLAY_MAIN, '[OverlayHeaderDirective] An overlay header must be used inside an <et-overlay-main> element or a host with the etOverlayMain directive.');
28764
+ }
28653
28765
  this.overlayRef = resolveClosestOverlay({
28654
28766
  overlayRef: this.overlayRef,
28655
28767
  element: this.elementRef,
@@ -28723,51 +28835,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
28723
28835
  }]
28724
28836
  }], ctorParameters: () => [] });
28725
28837
 
28726
- const OVERLAY_MAIN_TOKEN = new InjectionToken('OVERLAY_MAIN_TOKEN');
28727
- class OverlayMainDirective {
28728
- constructor() {
28729
- this.parent = inject(OVERLAY_MAIN_TOKEN, { optional: true, skipSelf: true });
28730
- this.elementRef = inject(ElementRef);
28731
- this.overlayRef = inject(OVERLAY_REF, { optional: true });
28732
- this.overlayManager = injectOverlayManager();
28733
- this.enabled = input(true, { ...(ngDevMode ? { debugName: "enabled" } : /* istanbul ignore next */ {}), alias: 'etOverlayMain', transform: booleanAttribute });
28734
- }
28735
- ngOnInit() {
28736
- this.overlayRef = resolveClosestOverlay({
28737
- overlayRef: this.overlayRef,
28738
- element: this.elementRef,
28739
- openOverlays: this.overlayManager.openOverlays(),
28740
- });
28741
- if (this.parent) {
28742
- if (this.overlayRef.id === this.parent.overlayRef?.id && this.parent.enabled() && this.enabled()) {
28743
- throw new RuntimeError(OVERLAY_ERROR_CODES.NESTED_OVERLAY_MAIN, '[OverlayMainDirective] An overlay must not contain nested <et-overlay-main> elements or etOverlayMain directives.');
28744
- }
28745
- }
28746
- }
28747
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: OverlayMainDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
28748
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: OverlayMainDirective, isStandalone: true, selector: "[etOverlayMain], et-overlay-main", inputs: { enabled: { classPropertyName: "enabled", publicName: "etOverlayMain", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.et-overlay-main": "enabled()" } }, providers: [
28749
- {
28750
- provide: OVERLAY_MAIN_TOKEN,
28751
- useExisting: OverlayMainDirective,
28752
- },
28753
- ], ngImport: i0 }); }
28754
- }
28755
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: OverlayMainDirective, decorators: [{
28756
- type: Directive,
28757
- args: [{
28758
- selector: '[etOverlayMain], et-overlay-main',
28759
- providers: [
28760
- {
28761
- provide: OVERLAY_MAIN_TOKEN,
28762
- useExisting: OverlayMainDirective,
28763
- },
28764
- ],
28765
- host: {
28766
- '[class.et-overlay-main]': 'enabled()',
28767
- },
28768
- }]
28769
- }], propDecorators: { enabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "etOverlayMain", required: false }] }] } });
28770
-
28771
28838
  const isWritableSignal = (value) => isSignal(value) && typeof value.set === 'function';
28772
28839
  const splitOpenerConfig = (config) => {
28773
28840
  const { afterClosed, beforeClosed, afterOpened, ...overlayConfig } = config ?? {};
@@ -29884,6 +29951,977 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
29884
29951
  }]
29885
29952
  }], ctorParameters: () => [], propDecorators: { pageDividers: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageDividers", required: false }] }], sidebarContent: [{ type: i0.ViewChild, args: ['sidebarContentTpl', { isSignal: true }] }], sidebarHeaderContent: [{ type: i0.ContentChild, args: [i0.forwardRef(() => OVERLAY_HEADER_TEMPLATE_TOKEN), { isSignal: true }] }] } });
29886
29953
 
29954
+ const kindOf = (value) => {
29955
+ if (value === null)
29956
+ return 'null';
29957
+ if (value === undefined)
29958
+ return 'undefined';
29959
+ if (Array.isArray(value))
29960
+ return 'array';
29961
+ return typeof value;
29962
+ };
29963
+ /**
29964
+ * A recursive, collapsible, searchable JSON tree used by the query devtools value explorer. Renders
29965
+ * the *transformed* value the app actually sees (post `transformResponse`). Self-recurses via its
29966
+ * own selector.
29967
+ */
29968
+ class QueryDevtoolsJsonComponent {
29969
+ constructor() {
29970
+ this.value = input(/* @ts-ignore */
29971
+ ...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
29972
+ this.nodeKey = input(null, /* @ts-ignore */
29973
+ ...(ngDevMode ? [{ debugName: "nodeKey" }] : /* istanbul ignore next */ []));
29974
+ this.depth = input(0, /* @ts-ignore */
29975
+ ...(ngDevMode ? [{ debugName: "depth" }] : /* istanbul ignore next */ []));
29976
+ /** Lowercased search term; when set, the tree auto-expands and matches are highlighted. */
29977
+ this.search = input('', /* @ts-ignore */
29978
+ ...(ngDevMode ? [{ debugName: "search" }] : /* istanbul ignore next */ []));
29979
+ /** Full path of this node from the explorer root, used as the key for persisted expansion. */
29980
+ this.path = input('', /* @ts-ignore */
29981
+ ...(ngDevMode ? [{ debugName: "path" }] : /* istanbul ignore next */ []));
29982
+ /** Persisted paths explicitly expanded (overrides the depth default). */
29983
+ this.expandedPaths = input(null, /* @ts-ignore */
29984
+ ...(ngDevMode ? [{ debugName: "expandedPaths" }] : /* istanbul ignore next */ []));
29985
+ /** Persisted paths explicitly collapsed (overrides the depth default). */
29986
+ this.collapsedPaths = input(null, /* @ts-ignore */
29987
+ ...(ngDevMode ? [{ debugName: "collapsedPaths" }] : /* istanbul ignore next */ []));
29988
+ /** When provided, expansion is externally persisted via this callback instead of local state. */
29989
+ this.toggleFn = input(null, /* @ts-ignore */
29990
+ ...(ngDevMode ? [{ debugName: "toggleFn" }] : /* istanbul ignore next */ []));
29991
+ this.kind = computed(() => kindOf(this.value()), /* @ts-ignore */
29992
+ ...(ngDevMode ? [{ debugName: "kind" }] : /* istanbul ignore next */ []));
29993
+ this.isContainer = computed(() => this.kind() === 'array' || this.kind() === 'object', /* @ts-ignore */
29994
+ ...(ngDevMode ? [{ debugName: "isContainer" }] : /* istanbul ignore next */ []));
29995
+ this.entries = computed(() => {
29996
+ const value = this.value();
29997
+ if (Array.isArray(value))
29998
+ return value.map((v, i) => ({ k: String(i), v }));
29999
+ if (value && typeof value === 'object') {
30000
+ return Object.entries(value).map(([k, v]) => ({ k, v }));
30001
+ }
30002
+ return [];
30003
+ }, /* @ts-ignore */
30004
+ ...(ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []));
30005
+ this.localExpanded = linkedSignal(() => this.depth() < 1, /* @ts-ignore */
30006
+ ...(ngDevMode ? [{ debugName: "localExpanded" }] : /* istanbul ignore next */ []));
30007
+ this.expanded = computed(() => {
30008
+ // Externally-persisted mode: resolve from the expanded/collapsed path sets, else the depth default.
30009
+ if (this.toggleFn()) {
30010
+ const path = this.path();
30011
+ if (this.collapsedPaths()?.has(path))
30012
+ return false;
30013
+ if (this.expandedPaths()?.has(path))
30014
+ return true;
30015
+ return this.depth() < 1;
30016
+ }
30017
+ return this.localExpanded();
30018
+ }, /* @ts-ignore */
30019
+ ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
30020
+ this.effectiveExpanded = computed(() => (this.search() ? true : this.expanded()), /* @ts-ignore */
30021
+ ...(ngDevMode ? [{ debugName: "effectiveExpanded" }] : /* istanbul ignore next */ []));
30022
+ this.preview = computed(() => {
30023
+ const value = this.value();
30024
+ if (Array.isArray(value))
30025
+ return value.length ? `[…] ${value.length}` : '[]';
30026
+ const count = value ? Object.keys(value).length : 0;
30027
+ return count ? `{…} ${count}` : '{}';
30028
+ }, /* @ts-ignore */
30029
+ ...(ngDevMode ? [{ debugName: "preview" }] : /* istanbul ignore next */ []));
30030
+ this.display = computed(() => {
30031
+ const value = this.value();
30032
+ const kind = this.kind();
30033
+ if (kind === 'string')
30034
+ return `"${value}"`;
30035
+ if (kind === 'null')
30036
+ return 'null';
30037
+ if (kind === 'undefined')
30038
+ return 'undefined';
30039
+ return String(value);
30040
+ }, /* @ts-ignore */
30041
+ ...(ngDevMode ? [{ debugName: "display" }] : /* istanbul ignore next */ []));
30042
+ this.keyHit = computed(() => {
30043
+ const term = this.search();
30044
+ const key = this.nodeKey();
30045
+ return !!term && !!key && key.toLowerCase().includes(term);
30046
+ }, /* @ts-ignore */
30047
+ ...(ngDevMode ? [{ debugName: "keyHit" }] : /* istanbul ignore next */ []));
30048
+ this.valueHit = computed(() => {
30049
+ const term = this.search();
30050
+ return !!term && this.display().toLowerCase().includes(term);
30051
+ }, /* @ts-ignore */
30052
+ ...(ngDevMode ? [{ debugName: "valueHit" }] : /* istanbul ignore next */ []));
30053
+ }
30054
+ childPath(key) {
30055
+ const path = this.path();
30056
+ return path ? `${path}.${key}` : key;
30057
+ }
30058
+ toggle() {
30059
+ const toggleFn = this.toggleFn();
30060
+ if (toggleFn) {
30061
+ toggleFn(this.path(), !this.expanded());
30062
+ return;
30063
+ }
30064
+ this.localExpanded.set(!this.localExpanded());
30065
+ }
30066
+ copyValue() {
30067
+ try {
30068
+ navigator.clipboard?.writeText(JSON.stringify(this.value(), null, 2));
30069
+ }
30070
+ catch {
30071
+ // clipboard unavailable
30072
+ }
30073
+ }
30074
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: QueryDevtoolsJsonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
30075
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: QueryDevtoolsJsonComponent, isStandalone: true, selector: "et-query-devtools-json", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, nodeKey: { classPropertyName: "nodeKey", publicName: "nodeKey", isSignal: true, isRequired: false, transformFunction: null }, depth: { classPropertyName: "depth", publicName: "depth", isSignal: true, isRequired: false, transformFunction: null }, search: { classPropertyName: "search", publicName: "search", isSignal: true, isRequired: false, transformFunction: null }, path: { classPropertyName: "path", publicName: "path", isSignal: true, isRequired: false, transformFunction: null }, expandedPaths: { classPropertyName: "expandedPaths", publicName: "expandedPaths", isSignal: true, isRequired: false, transformFunction: null }, collapsedPaths: { classPropertyName: "collapsedPaths", publicName: "collapsedPaths", isSignal: true, isRequired: false, transformFunction: null }, toggleFn: { classPropertyName: "toggleFn", publicName: "toggleFn", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
30076
+ @if (isContainer()) {
30077
+ <div class="et-query-devtools-json-node">
30078
+ <button
30079
+ [attr.aria-expanded]="effectiveExpanded()"
30080
+ (click)="toggle()"
30081
+ class="et-query-devtools-json-row et-query-devtools-json-toggle"
30082
+ type="button"
30083
+ >
30084
+ <span class="et-query-devtools-json-caret">{{ effectiveExpanded() ? '▾' : '▸' }}</span>
30085
+ @if (nodeKey() !== null) {
30086
+ <span [class.et-query-devtools-json-hit]="keyHit()" class="et-query-devtools-json-key">{{
30087
+ nodeKey()
30088
+ }}</span>
30089
+ <span class="et-query-devtools-json-colon">:</span>
30090
+ }
30091
+ <span class="et-query-devtools-json-preview">{{ preview() }}</span>
30092
+ </button>
30093
+
30094
+ @if (effectiveExpanded()) {
30095
+ <div class="et-query-devtools-json-children">
30096
+ @for (entry of entries(); track entry.k) {
30097
+ <et-query-devtools-json
30098
+ [value]="entry.v"
30099
+ [nodeKey]="entry.k"
30100
+ [depth]="depth() + 1"
30101
+ [path]="childPath(entry.k)"
30102
+ [search]="search()"
30103
+ [expandedPaths]="expandedPaths()"
30104
+ [collapsedPaths]="collapsedPaths()"
30105
+ [toggleFn]="toggleFn()"
30106
+ />
30107
+ }
30108
+ </div>
30109
+ }
30110
+ </div>
30111
+ } @else {
30112
+ <div [attr.data-kind]="kind()" class="et-query-devtools-json-row et-query-devtools-json-leaf">
30113
+ @if (nodeKey() !== null) {
30114
+ <span [class.et-query-devtools-json-hit]="keyHit()" class="et-query-devtools-json-key">{{ nodeKey() }}</span>
30115
+ <span class="et-query-devtools-json-colon">:</span>
30116
+ }
30117
+ <span [class.et-query-devtools-json-hit]="valueHit()" class="et-query-devtools-json-value">{{
30118
+ display()
30119
+ }}</span>
30120
+ <button (click)="copyValue()" class="et-query-devtools-json-copy" type="button" title="Copy value">⧉</button>
30121
+ </div>
30122
+ }
30123
+ `, isInline: true, dependencies: [{ kind: "component", type: QueryDevtoolsJsonComponent, selector: "et-query-devtools-json", inputs: ["value", "nodeKey", "depth", "search", "path", "expandedPaths", "collapsedPaths", "toggleFn"] }], encapsulation: i0.ViewEncapsulation.None }); }
30124
+ }
30125
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: QueryDevtoolsJsonComponent, decorators: [{
30126
+ type: Component,
30127
+ args: [{
30128
+ selector: 'et-query-devtools-json',
30129
+ template: `
30130
+ @if (isContainer()) {
30131
+ <div class="et-query-devtools-json-node">
30132
+ <button
30133
+ [attr.aria-expanded]="effectiveExpanded()"
30134
+ (click)="toggle()"
30135
+ class="et-query-devtools-json-row et-query-devtools-json-toggle"
30136
+ type="button"
30137
+ >
30138
+ <span class="et-query-devtools-json-caret">{{ effectiveExpanded() ? '▾' : '▸' }}</span>
30139
+ @if (nodeKey() !== null) {
30140
+ <span [class.et-query-devtools-json-hit]="keyHit()" class="et-query-devtools-json-key">{{
30141
+ nodeKey()
30142
+ }}</span>
30143
+ <span class="et-query-devtools-json-colon">:</span>
30144
+ }
30145
+ <span class="et-query-devtools-json-preview">{{ preview() }}</span>
30146
+ </button>
30147
+
30148
+ @if (effectiveExpanded()) {
30149
+ <div class="et-query-devtools-json-children">
30150
+ @for (entry of entries(); track entry.k) {
30151
+ <et-query-devtools-json
30152
+ [value]="entry.v"
30153
+ [nodeKey]="entry.k"
30154
+ [depth]="depth() + 1"
30155
+ [path]="childPath(entry.k)"
30156
+ [search]="search()"
30157
+ [expandedPaths]="expandedPaths()"
30158
+ [collapsedPaths]="collapsedPaths()"
30159
+ [toggleFn]="toggleFn()"
30160
+ />
30161
+ }
30162
+ </div>
30163
+ }
30164
+ </div>
30165
+ } @else {
30166
+ <div [attr.data-kind]="kind()" class="et-query-devtools-json-row et-query-devtools-json-leaf">
30167
+ @if (nodeKey() !== null) {
30168
+ <span [class.et-query-devtools-json-hit]="keyHit()" class="et-query-devtools-json-key">{{ nodeKey() }}</span>
30169
+ <span class="et-query-devtools-json-colon">:</span>
30170
+ }
30171
+ <span [class.et-query-devtools-json-hit]="valueHit()" class="et-query-devtools-json-value">{{
30172
+ display()
30173
+ }}</span>
30174
+ <button (click)="copyValue()" class="et-query-devtools-json-copy" type="button" title="Copy value">⧉</button>
30175
+ </div>
30176
+ }
30177
+ `,
30178
+ encapsulation: ViewEncapsulation.None,
30179
+ imports: [QueryDevtoolsJsonComponent],
30180
+ }]
30181
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], nodeKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "nodeKey", required: false }] }], depth: [{ type: i0.Input, args: [{ isSignal: true, alias: "depth", required: false }] }], search: [{ type: i0.Input, args: [{ isSignal: true, alias: "search", required: false }] }], path: [{ type: i0.Input, args: [{ isSignal: true, alias: "path", required: false }] }], expandedPaths: [{ type: i0.Input, args: [{ isSignal: true, alias: "expandedPaths", required: false }] }], collapsedPaths: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsedPaths", required: false }] }], toggleFn: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleFn", required: false }] }] } });
30182
+
30183
+ /**
30184
+ * The floating toggle button for the query devtools. Rendered with `ShadowDom` encapsulation so the
30185
+ * host application's global CSS (Tailwind resets, button styles, …) can never touch it and its own
30186
+ * styles can never leak out. Uses px (not rem) internally so it doesn't depend on the host's root
30187
+ * font-size; theme accent colour is read from an inherited custom property with a fallback.
30188
+ */
30189
+ class QueryDevtoolsToggleComponent {
30190
+ constructor() {
30191
+ this.openChange = output();
30192
+ }
30193
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: QueryDevtoolsToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
30194
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.7", type: QueryDevtoolsToggleComponent, isStandalone: true, selector: "et-query-devtools-toggle", outputs: { openChange: "openChange" }, ngImport: i0, template: `
30195
+ <button (click)="openChange.emit()" type="button" title="Toggle query devtools (Ctrl/Cmd + Alt + Q)">
30196
+ <svg viewBox="0 0 24 24" aria-hidden="true">
30197
+ <ellipse cx="12" cy="6" rx="7.5" ry="3" />
30198
+ <path d="M4.5 6v6c0 1.66 3.36 3 7.5 3s7.5-1.34 7.5-3V6" />
30199
+ <path d="M4.5 12v6c0 1.66 3.36 3 7.5 3s7.5-1.34 7.5-3v-6" />
30200
+ </svg>
30201
+ <span class="label">Query</span>
30202
+ </button>
30203
+ `, isInline: true, styles: [":host{position:fixed;inset-block-end:16px;inset-inline-end:16px;z-index:2147483001}button{--_accent: var(--et-theme-color-primary-solid, #60a5fa);position:relative;display:inline-flex;align-items:center;gap:8px;padding:10px 16px 10px 14px;border:none;border-radius:999px;background:linear-gradient(135deg,#1f1f23,#2a2a30);color:#fafafa;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;font-weight:600;letter-spacing:.02em;cursor:pointer;box-shadow:0 6px 20px #00000059,inset 0 0 0 1px #ffffff14;transition:transform .15s ease,box-shadow .2s ease}button:before{content:\"\";position:absolute;inset:0;border-radius:inherit;padding:1px;background:linear-gradient(135deg,var(--_accent),transparent 60%);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:.7;transition:opacity .2s ease}button:hover{transform:translateY(-2px);box-shadow:0 10px 28px #0006,0 0 0 4px color-mix(in srgb,var(--_accent) 22%,transparent),inset 0 0 0 1px #ffffff1a}button:hover:before{opacity:1}svg{inline-size:16px;block-size:16px;fill:none;stroke:var(--_accent);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}.label{line-height:1}\n"], encapsulation: i0.ViewEncapsulation.ShadowDom }); }
30204
+ }
30205
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: QueryDevtoolsToggleComponent, decorators: [{
30206
+ type: Component,
30207
+ args: [{ selector: 'et-query-devtools-toggle', template: `
30208
+ <button (click)="openChange.emit()" type="button" title="Toggle query devtools (Ctrl/Cmd + Alt + Q)">
30209
+ <svg viewBox="0 0 24 24" aria-hidden="true">
30210
+ <ellipse cx="12" cy="6" rx="7.5" ry="3" />
30211
+ <path d="M4.5 6v6c0 1.66 3.36 3 7.5 3s7.5-1.34 7.5-3V6" />
30212
+ <path d="M4.5 12v6c0 1.66 3.36 3 7.5 3s7.5-1.34 7.5-3v-6" />
30213
+ </svg>
30214
+ <span class="label">Query</span>
30215
+ </button>
30216
+ `, encapsulation: ViewEncapsulation.ShadowDom, styles: [":host{position:fixed;inset-block-end:16px;inset-inline-end:16px;z-index:2147483001}button{--_accent: var(--et-theme-color-primary-solid, #60a5fa);position:relative;display:inline-flex;align-items:center;gap:8px;padding:10px 16px 10px 14px;border:none;border-radius:999px;background:linear-gradient(135deg,#1f1f23,#2a2a30);color:#fafafa;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;font-weight:600;letter-spacing:.02em;cursor:pointer;box-shadow:0 6px 20px #00000059,inset 0 0 0 1px #ffffff14;transition:transform .15s ease,box-shadow .2s ease}button:before{content:\"\";position:absolute;inset:0;border-radius:inherit;padding:1px;background:linear-gradient(135deg,var(--_accent),transparent 60%);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;opacity:.7;transition:opacity .2s ease}button:hover{transform:translateY(-2px);box-shadow:0 10px 28px #0006,0 0 0 4px color-mix(in srgb,var(--_accent) 22%,transparent),inset 0 0 0 1px #ffffff1a}button:hover:before{opacity:1}svg{inline-size:16px;block-size:16px;fill:none;stroke:var(--_accent);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}.label{line-height:1}\n"] }]
30217
+ }], propDecorators: { openChange: [{ type: i0.Output, args: ["openChange"] }] } });
30218
+
30219
+ const STORAGE_KEY = 'ethlete:query:devtools:v4';
30220
+ const MAX_EVENTS = 100;
30221
+ const DEFAULT_HEIGHT = 360;
30222
+ const MIN_HEIGHT = 200;
30223
+ const readPersistedState = () => {
30224
+ try {
30225
+ const raw = sessionStorage.getItem(STORAGE_KEY);
30226
+ return raw ? JSON.parse(raw) : {};
30227
+ }
30228
+ catch {
30229
+ return {};
30230
+ }
30231
+ };
30232
+ /**
30233
+ * Slims a value for a shareable report: long strings are truncated and long arrays keep only the
30234
+ * first couple of entries, replacing the repetitive tail with a `… (N more)` marker, so a big
30235
+ * response collapses to a representative sample.
30236
+ */
30237
+ const slimForReport = (value, depth = 0) => {
30238
+ if (typeof value === 'string')
30239
+ return value.length > 200 ? `${value.slice(0, 200)}…` : value;
30240
+ if (depth > 6)
30241
+ return '…';
30242
+ if (Array.isArray(value)) {
30243
+ if (value.length > 3) {
30244
+ return [...value.slice(0, 2).map((v) => slimForReport(v, depth + 1)), `… (${value.length - 2} more)`];
30245
+ }
30246
+ return value.map((v) => slimForReport(v, depth + 1));
30247
+ }
30248
+ if (value && typeof value === 'object') {
30249
+ const out = {};
30250
+ for (const [key, val] of Object.entries(value))
30251
+ out[key] = slimForReport(val, depth + 1);
30252
+ return out;
30253
+ }
30254
+ return value;
30255
+ };
30256
+ /** Best-effort decode of a JWT payload for the auth tab. Returns `null` for anything non-decodable. */
30257
+ const decodeJwtPayload = (token) => {
30258
+ if (!token)
30259
+ return null;
30260
+ try {
30261
+ const payload = token.split('.')[1];
30262
+ if (!payload)
30263
+ return null;
30264
+ const normalized = payload.replace(/-/g, '+').replace(/_/g, '/');
30265
+ const decoded = atob(normalized);
30266
+ const json = decodeURIComponent(decoded
30267
+ .split('')
30268
+ .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
30269
+ .join(''));
30270
+ return JSON.parse(json);
30271
+ }
30272
+ catch {
30273
+ return null;
30274
+ }
30275
+ };
30276
+ /**
30277
+ * A floating, dockable panel that inspects the live state of the signals-first `@ethlete/query`
30278
+ * system: queries, stacks, sequences, bearer auth providers, the repository cache and a rolling
30279
+ * event log.
30280
+ *
30281
+ * Requires `provideQueryDevtools()` in the application providers — without it the registry stays
30282
+ * empty and the panel shows nothing.
30283
+ */
30284
+ class QueryDevtoolsComponent {
30285
+ constructor() {
30286
+ this.hostEl = inject(ElementRef);
30287
+ this.document = inject(DOCUMENT$1);
30288
+ this.eventIdCounter = 0;
30289
+ this.lastSelectionKey = '';
30290
+ this.persisted = readPersistedState();
30291
+ this.tabs = [
30292
+ { id: 'queries', label: 'Queries' },
30293
+ { id: 'stacks', label: 'Stacks' },
30294
+ { id: 'sequences', label: 'Sequences' },
30295
+ { id: 'auth', label: 'Auth' },
30296
+ { id: 'ws', label: 'Sockets' },
30297
+ { id: 'cache', label: 'Cache' },
30298
+ { id: 'events', label: 'Events' },
30299
+ ];
30300
+ this.open = signal(this.persisted.open ?? false, /* @ts-ignore */
30301
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
30302
+ this.panelHeight = signal(this.persisted.height ?? DEFAULT_HEIGHT, /* @ts-ignore */
30303
+ ...(ngDevMode ? [{ debugName: "panelHeight" }] : /* istanbul ignore next */ []));
30304
+ this.resizing = signal(false, /* @ts-ignore */
30305
+ ...(ngDevMode ? [{ debugName: "resizing" }] : /* istanbul ignore next */ []));
30306
+ this.activeTab = signal(this.persisted.activeTab ?? 'queries', /* @ts-ignore */
30307
+ ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
30308
+ this.selectedClientName = signal(this.persisted.selectedClientName ?? null, /* @ts-ignore */
30309
+ ...(ngDevMode ? [{ debugName: "selectedClientName" }] : /* istanbul ignore next */ []));
30310
+ this.selectedQueryId = signal(this.persisted.selectedQueryId ?? null, /* @ts-ignore */
30311
+ ...(ngDevMode ? [{ debugName: "selectedQueryId" }] : /* istanbul ignore next */ []));
30312
+ // Independent per-drawer selection so the Stacks / Sequences drawers don't share the Queries tab's.
30313
+ this.stackSelectedQueryId = signal(null, /* @ts-ignore */
30314
+ ...(ngDevMode ? [{ debugName: "stackSelectedQueryId" }] : /* istanbul ignore next */ []));
30315
+ this.sequenceSelectedQueryId = signal(null, /* @ts-ignore */
30316
+ ...(ngDevMode ? [{ debugName: "sequenceSelectedQueryId" }] : /* istanbul ignore next */ []));
30317
+ this.eventLog = signal([], /* @ts-ignore */
30318
+ ...(ngDevMode ? [{ debugName: "eventLog" }] : /* istanbul ignore next */ []));
30319
+ /** Keys (`<entryId>:<stepIndex>`) of the sequence steps whose in/out detail is expanded. */
30320
+ this.expandedSteps = signal(new Set(this.persisted.expandedSteps ?? []), /* @ts-ignore */
30321
+ ...(ngDevMode ? [{ debugName: "expandedSteps" }] : /* istanbul ignore next */ []));
30322
+ /** Shared value-explorer search term. */
30323
+ this.jsonSearch = signal(this.persisted.jsonSearch ?? '', /* @ts-ignore */
30324
+ ...(ngDevMode ? [{ debugName: "jsonSearch" }] : /* istanbul ignore next */ []));
30325
+ this.jsonSearchTerm = computed(() => this.jsonSearch().trim().toLowerCase(), /* @ts-ignore */
30326
+ ...(ngDevMode ? [{ debugName: "jsonSearchTerm" }] : /* istanbul ignore next */ []));
30327
+ /** Path-keyed value-explorer expansion overrides (persisted so open trees survive a reload). */
30328
+ this.jsonExpandedPaths = signal(new Set(this.persisted.jsonExpanded ?? []), /* @ts-ignore */
30329
+ ...(ngDevMode ? [{ debugName: "jsonExpandedPaths" }] : /* istanbul ignore next */ []));
30330
+ this.jsonCollapsedPaths = signal(new Set(this.persisted.jsonCollapsed ?? []), /* @ts-ignore */
30331
+ ...(ngDevMode ? [{ debugName: "jsonCollapsedPaths" }] : /* istanbul ignore next */ []));
30332
+ /** JIT editor state (response / args editing on the selected query). */
30333
+ this.editorMode = signal('none', /* @ts-ignore */
30334
+ ...(ngDevMode ? [{ debugName: "editorMode" }] : /* istanbul ignore next */ []));
30335
+ this.responseDraft = signal('', /* @ts-ignore */
30336
+ ...(ngDevMode ? [{ debugName: "responseDraft" }] : /* istanbul ignore next */ []));
30337
+ this.argsDraft = signal('', /* @ts-ignore */
30338
+ ...(ngDevMode ? [{ debugName: "argsDraft" }] : /* istanbul ignore next */ []));
30339
+ this.editError = signal(null, /* @ts-ignore */
30340
+ ...(ngDevMode ? [{ debugName: "editError" }] : /* istanbul ignore next */ []));
30341
+ /** Transient "Copied!" feedback for the copy-report action. */
30342
+ this.copiedReport = signal(false, /* @ts-ignore */
30343
+ ...(ngDevMode ? [{ debugName: "copiedReport" }] : /* istanbul ignore next */ []));
30344
+ /** 1-second tick driving the cache freshness countdowns. */
30345
+ this.clock = toSignal(interval(1000), { initialValue: 0 });
30346
+ /** "Inspect" mode: hover the live UI to find the query that a component created. */
30347
+ this.inspectActive = signal(false, /* @ts-ignore */
30348
+ ...(ngDevMode ? [{ debugName: "inspectActive" }] : /* istanbul ignore next */ []));
30349
+ this.inspectHover = signal(null, /* @ts-ignore */
30350
+ ...(ngDevMode ? [{ debugName: "inspectHover" }] : /* istanbul ignore next */ []));
30351
+ /** When set (via inspect), the Queries list is filtered to exactly these entry ids. */
30352
+ this.inspectFilterIds = signal(this.persisted.inspectFilterIds ?? null, /* @ts-ignore */
30353
+ ...(ngDevMode ? [{ debugName: "inspectFilterIds" }] : /* istanbul ignore next */ []));
30354
+ this.queryEntries = computed(() => queryDevtoolsEntries().filter((e) => e.kind === 'query'), /* @ts-ignore */
30355
+ ...(ngDevMode ? [{ debugName: "queryEntries" }] : /* istanbul ignore next */ []));
30356
+ this.stackEntries = computed(() => queryDevtoolsEntries().filter((e) => e.kind === 'query-stack' || e.kind === 'paged-query-stack'), /* @ts-ignore */
30357
+ ...(ngDevMode ? [{ debugName: "stackEntries" }] : /* istanbul ignore next */ []));
30358
+ this.sequenceEntries = computed(() => queryDevtoolsEntries().filter((e) => e.kind === 'query-sequence'), /* @ts-ignore */
30359
+ ...(ngDevMode ? [{ debugName: "sequenceEntries" }] : /* istanbul ignore next */ []));
30360
+ this.authEntries = computed(() => queryDevtoolsEntries().filter((e) => e.kind === 'auth-provider'), /* @ts-ignore */
30361
+ ...(ngDevMode ? [{ debugName: "authEntries" }] : /* istanbul ignore next */ []));
30362
+ this.wsEntries = computed(() => queryDevtoolsEntries().filter((e) => e.kind === 'ws-client'), /* @ts-ignore */
30363
+ ...(ngDevMode ? [{ debugName: "wsEntries" }] : /* istanbul ignore next */ []));
30364
+ /** Unique client names present across queries and auth providers, for the Queries-tab picker. */
30365
+ this.clientNames = computed(() => {
30366
+ const names = new Set();
30367
+ for (const entry of queryDevtoolsEntries()) {
30368
+ if (entry.meta.clientName)
30369
+ names.add(entry.meta.clientName);
30370
+ }
30371
+ return Array.from(names).sort();
30372
+ }, /* @ts-ignore */
30373
+ ...(ngDevMode ? [{ debugName: "clientNames" }] : /* istanbul ignore next */ []));
30374
+ /** Unique repositories (with their client name + base URL) used by the Cache and Events tabs. */
30375
+ this.repositories = computed(() => {
30376
+ const map = new Map();
30377
+ for (const entry of queryDevtoolsEntries()) {
30378
+ const repo = entry.meta.repository;
30379
+ if (repo && !map.has(repo)) {
30380
+ map.set(repo, { name: entry.meta.clientName ?? 'unknown', baseUrl: entry.meta.clientBaseUrl ?? '' });
30381
+ }
30382
+ }
30383
+ return Array.from(map, ([repository, info]) => ({ repository, name: info.name, baseUrl: info.baseUrl }));
30384
+ }, /* @ts-ignore */
30385
+ ...(ngDevMode ? [{ debugName: "repositories" }] : /* istanbul ignore next */ []));
30386
+ this.filteredQueries = computed(() => {
30387
+ const entries = this.queryEntries();
30388
+ const inspectIds = this.inspectFilterIds();
30389
+ let filtered;
30390
+ if (inspectIds) {
30391
+ filtered = entries.filter((e) => inspectIds.includes(e.id));
30392
+ }
30393
+ else {
30394
+ const client = this.selectedClientName();
30395
+ filtered = client ? entries.filter((e) => e.meta.clientName === client) : entries;
30396
+ }
30397
+ return filtered.map((entry) => ({ entry, query: entry.handle }));
30398
+ }, /* @ts-ignore */
30399
+ ...(ngDevMode ? [{ debugName: "filteredQueries" }] : /* istanbul ignore next */ []));
30400
+ this.selectedQuery = computed(() => this.findQuery(this.selectedQueryId()), /* @ts-ignore */
30401
+ ...(ngDevMode ? [{ debugName: "selectedQuery" }] : /* istanbul ignore next */ []));
30402
+ this.stackSelectedQuery = computed(() => this.findQuery(this.stackSelectedQueryId()), /* @ts-ignore */
30403
+ ...(ngDevMode ? [{ debugName: "stackSelectedQuery" }] : /* istanbul ignore next */ []));
30404
+ this.sequenceSelectedQuery = computed(() => this.findQuery(this.sequenceSelectedQueryId()), /* @ts-ignore */
30405
+ ...(ngDevMode ? [{ debugName: "sequenceSelectedQuery" }] : /* istanbul ignore next */ []));
30406
+ this.cacheView = computed(() => this.repositories().map(({ repository, name, baseUrl }) => {
30407
+ // Read the version signal so this recomputes on every cache mutation.
30408
+ repository.subtle.cacheVersion();
30409
+ return { name, baseUrl, repository, entries: repository.subtle.cacheEntries() };
30410
+ }), /* @ts-ignore */
30411
+ ...(ngDevMode ? [{ debugName: "cacheView" }] : /* istanbul ignore next */ []));
30412
+ /** Map of a component's host element to the query entries it created (for the inspect tool). */
30413
+ this.elementQueryMap = computed(() => {
30414
+ const map = new Map();
30415
+ for (const entry of this.queryEntries()) {
30416
+ const el = entry.meta.element;
30417
+ if (!el)
30418
+ continue;
30419
+ const list = map.get(el);
30420
+ if (list)
30421
+ list.push(entry);
30422
+ else
30423
+ map.set(el, [entry]);
30424
+ }
30425
+ return map;
30426
+ }, /* @ts-ignore */
30427
+ ...(ngDevMode ? [{ debugName: "elementQueryMap" }] : /* istanbul ignore next */ []));
30428
+ // Assigned here (not as an arrow property) so `this` is bound for the value-explorer callback.
30429
+ this.toggleJsonPath = (path, expand) => {
30430
+ const expanded = new Set(this.jsonExpandedPaths());
30431
+ const collapsed = new Set(this.jsonCollapsedPaths());
30432
+ if (expand) {
30433
+ expanded.add(path);
30434
+ collapsed.delete(path);
30435
+ }
30436
+ else {
30437
+ collapsed.add(path);
30438
+ expanded.delete(path);
30439
+ }
30440
+ this.jsonExpandedPaths.set(expanded);
30441
+ this.jsonCollapsedPaths.set(collapsed);
30442
+ };
30443
+ // Merge every live repository's event stream into the rolling log, re-subscribing as the set of
30444
+ // repositories changes. Composed with RxJS (not a subscribe-in-effect) per the styleguide.
30445
+ toObservable(this.repositories)
30446
+ .pipe(switchMap((repos) => merge(...repos.map(({ repository, name, baseUrl }) => repository.events$.pipe(map((event) => ({ event, client: baseUrl || name })))))), tap(({ event, client }) => this.pushEvent(event, client)), takeUntilDestroyed())
30447
+ .subscribe();
30448
+ effect(() => {
30449
+ const state = {
30450
+ open: this.open(),
30451
+ height: this.panelHeight(),
30452
+ activeTab: this.activeTab(),
30453
+ selectedClientName: this.selectedClientName(),
30454
+ selectedQueryId: this.selectedQueryId(),
30455
+ inspectFilterIds: this.inspectFilterIds(),
30456
+ jsonSearch: this.jsonSearch(),
30457
+ expandedSteps: [...this.expandedSteps()],
30458
+ jsonExpanded: [...this.jsonExpandedPaths()],
30459
+ jsonCollapsed: [...this.jsonCollapsedPaths()],
30460
+ };
30461
+ try {
30462
+ sessionStorage.setItem(STORAGE_KEY, JSON.stringify(state));
30463
+ }
30464
+ catch {
30465
+ // ignore (private mode / disabled storage)
30466
+ }
30467
+ });
30468
+ // Close any open JIT editor on any selection / tab change, and reset the value-explorer search
30469
+ // when the *selected query* actually changes (but not on the initial restore, so a persisted
30470
+ // search survives a reload).
30471
+ this.lastSelectionKey = this.selectionKey();
30472
+ effect(() => {
30473
+ const key = this.selectionKey();
30474
+ this.activeTab();
30475
+ this.editorMode.set('none');
30476
+ this.editError.set(null);
30477
+ this.copiedReport.set(false);
30478
+ if (key !== this.lastSelectionKey) {
30479
+ this.lastSelectionKey = key;
30480
+ this.jsonSearch.set('');
30481
+ }
30482
+ });
30483
+ const doc = this.document;
30484
+ // Global toggle shortcut: Ctrl/Cmd + Alt + Q ("Q" for Query) — uncommon, no browser/OS conflict.
30485
+ fromEvent(doc, 'keydown')
30486
+ .pipe(filter((e) => (e.ctrlKey || e.metaKey) && e.altKey && e.key.toLowerCase() === 'q'), tap((e) => {
30487
+ e.preventDefault();
30488
+ this.open.update((v) => !v);
30489
+ }), takeUntilDestroyed())
30490
+ .subscribe();
30491
+ // Drag-to-resize: while a resize is in progress, track pointer movement on the document.
30492
+ toObservable(this.resizing)
30493
+ .pipe(switchMap((active) => active
30494
+ ? merge(fromEvent(doc, 'pointermove').pipe(tap((e) => this.applyResize(e))), fromEvent(doc, 'pointerup').pipe(tap(() => this.resizing.set(false))))
30495
+ : EMPTY), takeUntilDestroyed())
30496
+ .subscribe();
30497
+ // Inspect mode: while active, listen on the document to map the hovered element to a query.
30498
+ const capture = { capture: true };
30499
+ toObservable(this.inspectActive)
30500
+ .pipe(tap((active) => {
30501
+ if (!active)
30502
+ this.inspectHover.set(null);
30503
+ }), switchMap((active) => active
30504
+ ? merge(fromEvent(doc, 'mousemove', capture).pipe(tap((e) => this.updateInspectHover(e))), fromEvent(doc, 'click', capture).pipe(tap((e) => this.selectInspectedQuery(e))), fromEvent(doc, 'keydown', capture).pipe(tap((e) => {
30505
+ if (e.key === 'Escape')
30506
+ this.inspectActive.set(false);
30507
+ })))
30508
+ : EMPTY), takeUntilDestroyed())
30509
+ .subscribe();
30510
+ }
30511
+ toggleOpen() {
30512
+ this.open.update((v) => !v);
30513
+ }
30514
+ clearEvents() {
30515
+ this.eventLog.set([]);
30516
+ }
30517
+ selectClient(name) {
30518
+ this.selectedClientName.set(name);
30519
+ this.inspectFilterIds.set(null);
30520
+ }
30521
+ clearInspectFilter() {
30522
+ this.inspectFilterIds.set(null);
30523
+ }
30524
+ toggleInspect() {
30525
+ this.inspectActive.update((v) => !v);
30526
+ }
30527
+ startResize(event) {
30528
+ event.preventDefault();
30529
+ this.resizing.set(true);
30530
+ }
30531
+ inspectLabel(entries) {
30532
+ const first = entries[0];
30533
+ if (entries.length === 1 && first)
30534
+ return `${first.meta.method ?? ''} ${first.meta.route ?? ''}`.trim();
30535
+ return `${entries.length} queries`;
30536
+ }
30537
+ queryStatus(query) {
30538
+ const state = query.executionState();
30539
+ if (!state)
30540
+ return 'idle';
30541
+ if (state.type === 'loading')
30542
+ return 'loading';
30543
+ if (state.type === 'failure')
30544
+ return 'error';
30545
+ return 'success';
30546
+ }
30547
+ isStale(query) {
30548
+ try {
30549
+ return query.subtle.request()?.isStale() ?? false;
30550
+ }
30551
+ catch {
30552
+ return false;
30553
+ }
30554
+ }
30555
+ executeQuery(query, allowCache) {
30556
+ query.execute(allowCache ? { options: { allowCache: true } } : undefined);
30557
+ }
30558
+ resetQuery(query) {
30559
+ query.reset();
30560
+ }
30561
+ /**
30562
+ * Copies a shareable report (path, args, status, slimmed response) for handing to an API dev.
30563
+ * Writes both rich `text/html` (Slack applies formatting on paste — it does not parse markdown) and
30564
+ * a plain-text fallback.
30565
+ */
30566
+ copyReport(entry, query) {
30567
+ const error = query.error();
30568
+ const httpStatus = error ? error.raw.status : this.responseStatus(query);
30569
+ const method = entry.meta.method ?? '';
30570
+ const route = entry.meta.route || '—';
30571
+ const client = entry.meta.clientBaseUrl ?? entry.meta.clientName ?? '';
30572
+ const statusLine = `status: ${this.queryStatus(query)}${httpStatus !== null ? ` (${httpStatus})` : ''} · ${this.formatTime(query.lastTimeExecutedAt())}`;
30573
+ const gqlDoc = entry.meta.gqlQuery ? this.gqlDocument(entry.meta.gqlQuery) : null;
30574
+ const args = query.args();
30575
+ const argsLabel = gqlDoc ? 'Variables' : 'Args';
30576
+ const argsJson = args !== null && args !== undefined ? JSON.stringify(args, null, 2) : null;
30577
+ const bodyLabel = error ? `Error (${error.raw.status})` : 'Response';
30578
+ const bodyContent = error
30579
+ ? error.isList
30580
+ ? error.errors.map((e) => e.message).join('\n')
30581
+ : error.error.message
30582
+ : JSON.stringify(slimForReport(query.response()), null, 2);
30583
+ const textParts = [`${method} ${route}${client ? ` — ${client}` : ''}`, statusLine];
30584
+ if (gqlDoc)
30585
+ textParts.push('', 'GraphQL document', gqlDoc);
30586
+ if (argsJson)
30587
+ textParts.push('', argsLabel, argsJson);
30588
+ textParts.push('', bodyLabel, bodyContent);
30589
+ const text = textParts.join('\n');
30590
+ const esc = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
30591
+ const htmlParts = [
30592
+ `<b>${esc(method)}</b> <code>${esc(route)}</code>${client ? ` — <code>${esc(client)}</code>` : ''}`,
30593
+ esc(statusLine),
30594
+ ];
30595
+ if (gqlDoc)
30596
+ htmlParts.push('<b>GraphQL document</b>', `<pre><code>${esc(gqlDoc)}</code></pre>`);
30597
+ if (argsJson)
30598
+ htmlParts.push(`<b>${argsLabel}</b>`, `<pre><code>${esc(argsJson)}</code></pre>`);
30599
+ htmlParts.push(`<b>${esc(bodyLabel)}</b>`, `<pre><code>${esc(bodyContent)}</code></pre>`);
30600
+ const html = htmlParts.join('<br>');
30601
+ this.writeToClipboard(html, text);
30602
+ }
30603
+ // --- JIT editing ---
30604
+ openResponseEditor(query) {
30605
+ this.responseDraft.set(JSON.stringify(query.response() ?? null, null, 2));
30606
+ this.editError.set(null);
30607
+ this.editorMode.set('response');
30608
+ }
30609
+ openArgsEditor(query) {
30610
+ this.argsDraft.set(JSON.stringify(query.args() ?? {}, null, 2));
30611
+ this.editError.set(null);
30612
+ this.editorMode.set('args');
30613
+ }
30614
+ applyResponse(query) {
30615
+ try {
30616
+ query.subtle.setResponse(JSON.parse(this.responseDraft()));
30617
+ this.editorMode.set('none');
30618
+ this.editError.set(null);
30619
+ }
30620
+ catch {
30621
+ this.editError.set('Invalid JSON');
30622
+ }
30623
+ }
30624
+ applyArgs(query) {
30625
+ try {
30626
+ query.execute({ args: JSON.parse(this.argsDraft()) });
30627
+ this.editorMode.set('none');
30628
+ this.editError.set(null);
30629
+ }
30630
+ catch {
30631
+ this.editError.set('Invalid JSON');
30632
+ }
30633
+ }
30634
+ cancelEditor() {
30635
+ this.editorMode.set('none');
30636
+ this.editError.set(null);
30637
+ }
30638
+ // --- Force states ---
30639
+ forceLoading(query) {
30640
+ // `executionState` prioritises loading > error > response, so clear the others to switch cleanly.
30641
+ query.subtle.setError(null);
30642
+ query.subtle.setLoading({ executeTime: Date.now(), progress: null });
30643
+ }
30644
+ forceError(query) {
30645
+ query.subtle.setLoading(null);
30646
+ // A real failed execution also drops the response, so mirror that for consumers bound to it.
30647
+ query.subtle.setResponse(null);
30648
+ query.subtle.setError(createQueryErrorResponse(new HttpErrorResponse({
30649
+ status: 500,
30650
+ statusText: 'Forced',
30651
+ error: { message: 'Forced error (devtools)' },
30652
+ })));
30653
+ }
30654
+ forceEmpty(query) {
30655
+ query.subtle.setLoading(null);
30656
+ query.subtle.setError(null);
30657
+ query.subtle.setResponse(null);
30658
+ }
30659
+ clearForced(query) {
30660
+ query.subtle.setLoading(null);
30661
+ query.subtle.setError(null);
30662
+ }
30663
+ // --- Cache actions ---
30664
+ refetchCacheEntry(entry) {
30665
+ entry.request.execute();
30666
+ }
30667
+ evictCacheEntry(repository, key) {
30668
+ repository.subtle.evict(key);
30669
+ }
30670
+ cacheFreshness(entry) {
30671
+ this.clock();
30672
+ if (entry.request.loading())
30673
+ return 'refreshing…';
30674
+ const expiresAt = entry.request.expiresAt();
30675
+ if (expiresAt === null)
30676
+ return 'uncacheable';
30677
+ const ms = expiresAt - Date.now();
30678
+ return ms <= 0 ? 'stale' : `${Math.ceil(ms / 1000)}s`;
30679
+ }
30680
+ /** The path + query of a request URL (origin stripped), for readable cache/event identifiers. */
30681
+ requestPath(url) {
30682
+ try {
30683
+ const parsed = new URL(url);
30684
+ return parsed.pathname + parsed.search;
30685
+ }
30686
+ catch {
30687
+ return url;
30688
+ }
30689
+ }
30690
+ // --- Typed template accessors (entry.handle is `unknown`) ---
30691
+ asStack(entry) {
30692
+ return entry.handle;
30693
+ }
30694
+ asPagedStack(entry) {
30695
+ return entry.handle;
30696
+ }
30697
+ asSequence(entry) {
30698
+ return entry.handle.current;
30699
+ }
30700
+ asAuth(entry) {
30701
+ return entry.handle;
30702
+ }
30703
+ asWs(entry) {
30704
+ return entry.handle;
30705
+ }
30706
+ /** Derives the per-step status of a sequence step from its live progress signals. */
30707
+ sequenceStepStatus(sequence, index) {
30708
+ const failedAt = sequence.failedAt();
30709
+ if (failedAt !== null && index === failedAt)
30710
+ return 'error';
30711
+ const current = sequence.currentStep();
30712
+ const running = sequence.running();
30713
+ if (index < current - 1)
30714
+ return 'success';
30715
+ if (index === current - 1 && running)
30716
+ return 'running';
30717
+ if (sequence.status() === 'success')
30718
+ return 'success';
30719
+ return 'idle';
30720
+ }
30721
+ authTokenPayload(auth) {
30722
+ return decodeJwtPayload(auth.accessToken());
30723
+ }
30724
+ queriesForStack(stack) {
30725
+ const inner = stack.queries();
30726
+ const queryEntries = this.queryEntries();
30727
+ return inner.map((query) => {
30728
+ const entry = queryEntries.find((e) => e.handle === query);
30729
+ return {
30730
+ id: entry?.id ?? '',
30731
+ query: query,
30732
+ method: entry?.meta.method ?? '',
30733
+ route: entry?.meta.route ?? '',
30734
+ clientName: entry?.meta.clientName ?? '',
30735
+ clientBaseUrl: entry?.meta.clientBaseUrl ?? '',
30736
+ };
30737
+ });
30738
+ }
30739
+ /** Identifying info for a stack, derived from its (uniform) inner queries. */
30740
+ stackIdentity(stack) {
30741
+ const first = this.queriesForStack(stack)[0];
30742
+ return { method: first?.method ?? '', route: first?.route ?? '', baseUrl: first?.clientBaseUrl ?? '' };
30743
+ }
30744
+ authFeatures(auth) {
30745
+ return Object.keys(auth.features ?? {});
30746
+ }
30747
+ authQueryKeys(auth) {
30748
+ return Object.keys(auth.queries ?? {});
30749
+ }
30750
+ /** Countdown to the access-token's `exp` (the point a refresh becomes due), or `null` if unknown. */
30751
+ authTokenExpiry(auth) {
30752
+ this.clock();
30753
+ const payload = decodeJwtPayload(auth.accessToken());
30754
+ const exp = typeof payload?.['exp'] === 'number' ? payload['exp'] : null;
30755
+ if (exp === null)
30756
+ return null;
30757
+ const seconds = Math.round((exp * 1000 - Date.now()) / 1000);
30758
+ if (seconds <= 0)
30759
+ return 'expired';
30760
+ if (seconds < 120)
30761
+ return `${seconds}s`;
30762
+ if (seconds > 86400)
30763
+ return `${Math.floor(seconds / 86400)}d`;
30764
+ return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
30765
+ }
30766
+ queriesForSequence(sequence) {
30767
+ const queryEntries = this.queryEntries();
30768
+ return sequence.queries.map((query) => {
30769
+ const entry = queryEntries.find((e) => e.handle === query);
30770
+ return {
30771
+ id: entry?.id ?? '',
30772
+ query: query,
30773
+ method: entry?.meta.method ?? '',
30774
+ route: entry?.meta.route ?? '',
30775
+ };
30776
+ });
30777
+ }
30778
+ /** The snapshot of a sequence step, once it has run (holds the args in and the response/error out). */
30779
+ stepSnapshot(sequence, index) {
30780
+ return sequence.snapshots()[index] ?? null;
30781
+ }
30782
+ isStepExpanded(entryId, index) {
30783
+ return this.expandedSteps().has(this.stepKey(entryId, index));
30784
+ }
30785
+ toggleStep(entryId, index) {
30786
+ const key = this.stepKey(entryId, index);
30787
+ const next = new Set(this.expandedSteps());
30788
+ if (next.has(key)) {
30789
+ next.delete(key);
30790
+ }
30791
+ else {
30792
+ next.add(key);
30793
+ }
30794
+ this.expandedSteps.set(next);
30795
+ }
30796
+ /** Dedents a GraphQL document (template-literal indentation) for readable display. */
30797
+ gqlDocument(doc) {
30798
+ const lines = doc.replace(/\t/g, ' ').split('\n');
30799
+ while (lines.length && !lines[0]?.trim())
30800
+ lines.shift();
30801
+ while (lines.length && !lines[lines.length - 1]?.trim())
30802
+ lines.pop();
30803
+ const indents = lines.filter((l) => l.trim()).map((l) => l.match(/^ */)?.[0].length ?? 0);
30804
+ const min = indents.length ? Math.min(...indents) : 0;
30805
+ return lines.map((l) => l.slice(min)).join('\n');
30806
+ }
30807
+ featureLabel(type) {
30808
+ return type
30809
+ .replace(/^WITH_/, '')
30810
+ .replace(/_/g, ' ')
30811
+ .toLowerCase();
30812
+ }
30813
+ formatTime(timestamp) {
30814
+ if (!timestamp)
30815
+ return '—';
30816
+ return new Date(timestamp).toLocaleTimeString(undefined, { hour12: false });
30817
+ }
30818
+ stepKey(entryId, index) {
30819
+ return `${entryId}:${index}`;
30820
+ }
30821
+ applyResize(event) {
30822
+ // The panel is docked to the bottom, so its height is the distance from the pointer to the viewport bottom.
30823
+ const viewport = this.document.documentElement.clientHeight;
30824
+ const next = viewport - event.clientY;
30825
+ const max = Math.round(viewport * 0.9);
30826
+ this.panelHeight.set(Math.min(Math.max(next, MIN_HEIGHT), max));
30827
+ }
30828
+ updateInspectHover(event) {
30829
+ const host = this.hostEl.nativeElement;
30830
+ const map = this.elementQueryMap();
30831
+ let node = event.target;
30832
+ // Ignore the devtools UI itself.
30833
+ if (node && host.contains(node)) {
30834
+ this.inspectHover.set(null);
30835
+ return;
30836
+ }
30837
+ while (node) {
30838
+ const entries = map.get(node);
30839
+ if (entries) {
30840
+ this.inspectHover.set({ rect: node.getBoundingClientRect(), entries });
30841
+ return;
30842
+ }
30843
+ node = node.parentElement;
30844
+ }
30845
+ this.inspectHover.set(null);
30846
+ }
30847
+ selectInspectedQuery(event) {
30848
+ const hover = this.inspectHover();
30849
+ const first = hover?.entries[0];
30850
+ if (!hover || !first)
30851
+ return;
30852
+ event.preventDefault();
30853
+ event.stopPropagation();
30854
+ const ids = hover.entries.map((e) => e.id);
30855
+ this.open.set(true);
30856
+ this.activeTab.set('queries');
30857
+ this.selectedClientName.set(null);
30858
+ this.inspectFilterIds.set(ids);
30859
+ // Auto-select when the element owns a single query, otherwise let the user pick from the filtered list.
30860
+ this.selectedQueryId.set(ids.length === 1 ? first.id : null);
30861
+ this.inspectActive.set(false);
30862
+ }
30863
+ selectionKey() {
30864
+ return `${this.selectedQueryId()}|${this.stackSelectedQueryId()}|${this.sequenceSelectedQueryId()}`;
30865
+ }
30866
+ writeToClipboard(html, text) {
30867
+ const clipboard = navigator.clipboard;
30868
+ if (!clipboard)
30869
+ return;
30870
+ // Prefer rich HTML (Slack keeps the formatting on paste); fall back to plain text.
30871
+ if ('write' in clipboard && typeof ClipboardItem !== 'undefined') {
30872
+ const item = new ClipboardItem({
30873
+ 'text/html': new Blob([html], { type: 'text/html' }),
30874
+ 'text/plain': new Blob([text], { type: 'text/plain' }),
30875
+ });
30876
+ clipboard
30877
+ .write([item])
30878
+ .then(() => this.copiedReport.set(true))
30879
+ .catch(() => clipboard
30880
+ .writeText(text)
30881
+ .then(() => this.copiedReport.set(true))
30882
+ .catch(() => undefined));
30883
+ return;
30884
+ }
30885
+ clipboard
30886
+ .writeText(text)
30887
+ .then(() => this.copiedReport.set(true))
30888
+ .catch(() => undefined);
30889
+ }
30890
+ responseStatus(query) {
30891
+ const event = query.latestHttpEvent();
30892
+ return typeof event?.status === 'number' ? event.status : null;
30893
+ }
30894
+ findQuery(id) {
30895
+ if (!id)
30896
+ return null;
30897
+ const entry = this.queryEntries().find((e) => e.id === id);
30898
+ return entry ? { entry, query: entry.handle } : null;
30899
+ }
30900
+ pushEvent(event, client) {
30901
+ const item = {
30902
+ id: this.eventIdCounter++,
30903
+ timestamp: Date.now(),
30904
+ client,
30905
+ type: event.type,
30906
+ method: event.request.method,
30907
+ url: event.request.url,
30908
+ isSecure: event.isSecure,
30909
+ status: event.type === 'request-error' ? event.error.status : null,
30910
+ };
30911
+ this.eventLog.update((log) => [item, ...log].slice(0, MAX_EVENTS));
30912
+ }
30913
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: QueryDevtoolsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
30914
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: QueryDevtoolsComponent, isStandalone: true, selector: "et-query-devtools", host: { classAttribute: "et-query-devtools-host" }, ngImport: i0, template: "@if (open()) {\n <section\n [class.et-query-devtools-panel--resizing]=\"resizing()\"\n [style.height.px]=\"panelHeight()\"\n class=\"et-query-devtools-panel\"\n >\n <div\n (pointerdown)=\"startResize($event)\"\n class=\"et-query-devtools-resize\"\n title=\"Drag to resize\"\n aria-hidden=\"true\"\n ></div>\n <header class=\"et-query-devtools-header\">\n <nav class=\"et-query-devtools-tabs\" role=\"tablist\">\n @for (tab of tabs; track tab.id) {\n <button\n [class.et-query-devtools-tab--active]=\"activeTab() === tab.id\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n (click)=\"activeTab.set(tab.id)\"\n class=\"et-query-devtools-tab\"\n type=\"button\"\n role=\"tab\"\n >\n {{ tab.label }}\n </button>\n }\n </nav>\n\n <div class=\"et-query-devtools-header-actions\">\n <button\n [class.et-query-devtools-inspect-btn--active]=\"inspectActive()\"\n (click)=\"toggleInspect()\"\n class=\"et-query-devtools-inspect-btn\"\n type=\"button\"\n title=\"Inspect: hover the UI to find its query\"\n >\n \u2316 Inspect\n </button>\n <button (click)=\"toggleOpen()\" class=\"et-query-devtools-close\" type=\"button\" title=\"Close devtools\">\u2715</button>\n </div>\n </header>\n\n <div class=\"et-query-devtools-body\">\n @switch (activeTab()) {\n <!-- QUERIES -->\n @case ('queries') {\n <div class=\"et-query-devtools-master-detail\">\n <div class=\"et-query-devtools-list\">\n <div class=\"et-query-devtools-toolbar\">\n @if (inspectFilterIds(); as ids) {\n <span class=\"et-query-devtools-filter-chip\">\u2316 inspected \u00B7 {{ ids.length }}</span>\n <button (click)=\"clearInspectFilter()\" class=\"et-query-devtools-btn\" type=\"button\">Reset</button>\n } @else {\n <label class=\"et-query-devtools-field\">\n <span>Client</span>\n <select\n #clientSelect\n [value]=\"selectedClientName() ?? ''\"\n (change)=\"selectClient(clientSelect.value || null)\"\n class=\"et-query-devtools-select\"\n >\n <option value=\"\">All clients</option>\n @for (name of clientNames(); track name) {\n <option [value]=\"name\">{{ name }}</option>\n }\n </select>\n </label>\n }\n <span class=\"et-query-devtools-count\">{{ filteredQueries().length }}</span>\n </div>\n\n @for (item of filteredQueries(); track item.entry.id) {\n <button\n [class.et-query-devtools-row--active]=\"selectedQueryId() === item.entry.id\"\n (click)=\"selectedQueryId.set(item.entry.id)\"\n class=\"et-query-devtools-row\"\n type=\"button\"\n >\n <span\n [attr.data-status]=\"queryStatus(item.query)\"\n [title]=\"queryStatus(item.query)\"\n class=\"et-query-devtools-status\"\n ></span>\n <span class=\"et-query-devtools-method\">{{ item.entry.meta.method }}</span>\n <span class=\"et-query-devtools-route\">{{ item.entry.meta.route || '\u2014' }}</span>\n @if (isStale(item.query)) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">stale</span>\n }\n </button>\n } @empty {\n @if (inspectFilterIds()) {\n <p class=\"et-query-devtools-empty\">\n The inspected element's queries are no longer registered. Reset the filter.\n </p>\n } @else {\n <p class=\"et-query-devtools-empty\">No queries registered.</p>\n }\n }\n </div>\n\n <div class=\"et-query-devtools-detail\">\n @if (selectedQuery(); as sel) {\n <ng-container *ngTemplateOutlet=\"queryDetailTpl; context: { sel }\" />\n } @else {\n <p class=\"et-query-devtools-empty\">Select a query to inspect it.</p>\n }\n </div>\n </div>\n }\n\n <!-- STACKS -->\n @case ('stacks') {\n <div class=\"et-query-devtools-split\">\n <div class=\"et-query-devtools-scroll\">\n @for (entry of stackEntries(); track entry.id) {\n @if (entry.kind === 'paged-query-stack') {\n @let paged = asPagedStack(entry);\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <span class=\"et-query-devtools-chip\">paged stack</span>\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">{{ paged.direction() }}</span>\n @if (paged.loading()) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--loading\">loading</span>\n }\n </div>\n\n @let pagedId = stackIdentity(paged);\n <div class=\"et-query-devtools-card-title\">\n <span class=\"et-query-devtools-method\">{{ pagedId.method }}</span>\n <span class=\"et-query-devtools-route\">{{ pagedId.route }}</span>\n </div>\n <div class=\"et-query-devtools-card-sub et-query-devtools-mono\">{{ pagedId.baseUrl }}</div>\n @if (entry.meta.featureTypes?.length) {\n <div class=\"et-query-devtools-chips\">\n @for (feature of entry.meta.featureTypes; track feature) {\n <span class=\"et-query-devtools-chip\">{{ featureLabel(feature) }}</span>\n }\n </div>\n }\n\n <dl class=\"et-query-devtools-meta\">\n <dt>Pages loaded</dt>\n <dd>{{ paged.queries().length }}</dd>\n <dt>Items</dt>\n <dd>{{ paged.items().length }}</dd>\n <dt>First page loaded</dt>\n <dd>{{ paged.isFirstPageLoaded() }}</dd>\n <dt>Last page loaded</dt>\n <dd>{{ paged.isLastPageLoaded() }}</dd>\n </dl>\n <ng-container *ngTemplateOutlet=\"innerQueriesTpl; context: { links: queriesForStack(paged) }\" />\n </div>\n } @else {\n @let stack = asStack(entry);\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <span class=\"et-query-devtools-chip\">stack</span>\n @if (stack.anyLoading()) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--loading\">loading</span>\n }\n @if (stack.anyError()) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--error\">error</span>\n }\n </div>\n\n @let stackId = stackIdentity(stack);\n <div class=\"et-query-devtools-card-title\">\n <span class=\"et-query-devtools-method\">{{ stackId.method }}</span>\n <span class=\"et-query-devtools-route\">{{ stackId.route }}</span>\n </div>\n <div class=\"et-query-devtools-card-sub et-query-devtools-mono\">{{ stackId.baseUrl }}</div>\n @if (entry.meta.featureTypes?.length) {\n <div class=\"et-query-devtools-chips\">\n @for (feature of entry.meta.featureTypes; track feature) {\n <span class=\"et-query-devtools-chip\">{{ featureLabel(feature) }}</span>\n }\n </div>\n }\n\n <dl class=\"et-query-devtools-meta\">\n <dt>Queries</dt>\n <dd>{{ stack.queries().length }}</dd>\n <dt>Loading</dt>\n <dd>{{ stack.loadingProgress().loaded }} / {{ stack.loadingProgress().total }}</dd>\n <dt>Errors</dt>\n <dd>{{ stack.errors().length }}</dd>\n </dl>\n <ng-container *ngTemplateOutlet=\"innerQueriesTpl; context: { links: queriesForStack(stack) }\" />\n </div>\n }\n } @empty {\n <p class=\"et-query-devtools-empty\">No stacks registered.</p>\n }\n </div>\n\n @if (stackSelectedQuery(); as sel) {\n <aside class=\"et-query-devtools-drawer\">\n <div class=\"et-query-devtools-drawer-head\">\n <span>Query detail</span>\n <button\n (click)=\"stackSelectedQueryId.set(null)\"\n class=\"et-query-devtools-close\"\n type=\"button\"\n title=\"Close detail\"\n >\n \u2715\n </button>\n </div>\n <ng-container *ngTemplateOutlet=\"queryDetailTpl; context: { sel }\" />\n </aside>\n }\n </div>\n }\n\n <!-- SEQUENCES -->\n @case ('sequences') {\n <div class=\"et-query-devtools-split\">\n <div class=\"et-query-devtools-scroll\">\n @for (entry of sequenceEntries(); track entry.id) {\n @let sequence = asSequence(entry);\n @if (sequence) {\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <span class=\"et-query-devtools-chip\">sequence</span>\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">{{ sequence.status() }}</span>\n <span class=\"et-query-devtools-count\"\n >step {{ sequence.currentStep() }} / {{ sequence.total }}</span\n >\n </div>\n\n <ol class=\"et-query-devtools-step-list\">\n @for (link of queriesForSequence(sequence); track $index; let i = $index) {\n <li [attr.data-status]=\"sequenceStepStatus(sequence, i)\" class=\"et-query-devtools-step-item\">\n <div class=\"et-query-devtools-step-head\">\n <button\n [class.et-query-devtools-inner-row--active]=\"sequenceSelectedQueryId() === link.id\"\n [disabled]=\"!link.id\"\n (click)=\"sequenceSelectedQueryId.set(link.id)\"\n class=\"et-query-devtools-inner-row\"\n type=\"button\"\n >\n <span\n [attr.data-status]=\"sequenceStepStatus(sequence, i)\"\n class=\"et-query-devtools-status\"\n ></span>\n <span class=\"et-query-devtools-method\">{{ i + 1 }}. {{ link.method }}</span>\n <span class=\"et-query-devtools-route\">{{ link.route || 'step ' + (i + 1) }}</span>\n </button>\n <button\n [attr.aria-expanded]=\"isStepExpanded(entry.id, i)\"\n (click)=\"toggleStep(entry.id, i)\"\n class=\"et-query-devtools-step-caret-btn\"\n type=\"button\"\n title=\"Toggle in / out\"\n >\n {{ isStepExpanded(entry.id, i) ? '\u25BE' : '\u25B8' }}\n </button>\n </div>\n\n @if (isStepExpanded(entry.id, i)) {\n @let snap = stepSnapshot(sequence, i);\n <div class=\"et-query-devtools-step-io\">\n <h5 class=\"et-query-devtools-section\">In \u00B7 args</h5>\n @if (sequence.stepArgs().length > i) {\n <et-query-devtools-json [value]=\"sequence.stepArgs()[i]\" />\n } @else {\n <p class=\"et-query-devtools-empty\">Not run yet.</p>\n }\n\n @if (snap?.error(); as error) {\n <h5 class=\"et-query-devtools-section\">Out \u00B7 error ({{ error.raw.status }})</h5>\n <et-query-devtools-json [value]=\"error.isList ? error.errors : error.error\" />\n } @else {\n <h5 class=\"et-query-devtools-section\">Out \u00B7 response</h5>\n @if (snap) {\n <et-query-devtools-json [value]=\"snap.response()\" />\n } @else {\n <p class=\"et-query-devtools-empty\">Not run yet.</p>\n }\n }\n </div>\n }\n </li>\n }\n </ol>\n </div>\n }\n } @empty {\n <p class=\"et-query-devtools-empty\">No sequences registered.</p>\n }\n </div>\n\n @if (sequenceSelectedQuery(); as sel) {\n <aside class=\"et-query-devtools-drawer\">\n <div class=\"et-query-devtools-drawer-head\">\n <span>Query detail</span>\n <button\n (click)=\"sequenceSelectedQueryId.set(null)\"\n class=\"et-query-devtools-close\"\n type=\"button\"\n title=\"Close detail\"\n >\n \u2715\n </button>\n </div>\n <ng-container *ngTemplateOutlet=\"queryDetailTpl; context: { sel }\" />\n </aside>\n }\n </div>\n }\n\n <!-- AUTH -->\n @case ('auth') {\n <div class=\"et-query-devtools-scroll\">\n @for (entry of authEntries(); track entry.id) {\n @let auth = asAuth(entry);\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>{{ entry.meta.name || 'auth provider' }}</strong>\n <span\n [class.et-query-devtools-chip--success]=\"auth.isAuthenticated()\"\n [class.et-query-devtools-chip--muted]=\"!auth.isAuthenticated()\"\n class=\"et-query-devtools-chip\"\n >\n {{ auth.isAuthenticated() ? 'authenticated' : 'anonymous' }}\n </span>\n </div>\n <dl class=\"et-query-devtools-meta\">\n <dt>Base URL</dt>\n <dd class=\"et-query-devtools-mono\">{{ entry.meta.clientBaseUrl || entry.meta.clientName || '\u2014' }}</dd>\n <dt>Access token</dt>\n <dd>{{ auth.accessToken() ? 'present' : 'none' }}</dd>\n <dt>Refresh token</dt>\n <dd>{{ auth.refreshToken() ? 'present' : 'none' }}</dd>\n @if (authTokenExpiry(auth); as expiry) {\n <dt>Token expires in</dt>\n <dd>{{ expiry }}</dd>\n }\n @if (auth.executionState(); as state) {\n <dt>Execution state</dt>\n <dd>{{ state.type }} \u00B7 {{ state.state }}</dd>\n }\n @if (authQueryKeys(auth).length) {\n <dt>Queries</dt>\n <dd>\n <div class=\"et-query-devtools-chips\">\n @for (key of authQueryKeys(auth); track key) {\n <span class=\"et-query-devtools-chip\">{{ key }}</span>\n }\n </div>\n </dd>\n }\n @if (authFeatures(auth).length) {\n <dt>Features</dt>\n <dd>\n <div class=\"et-query-devtools-chips\">\n @for (feature of authFeatures(auth); track feature) {\n <span class=\"et-query-devtools-chip\">{{ feature }}</span>\n }\n </div>\n </dd>\n }\n </dl>\n\n @if (authTokenPayload(auth); as payload) {\n <h4 class=\"et-query-devtools-section\">Access token payload</h4>\n <et-query-devtools-json [value]=\"payload\" />\n }\n\n @if (auth.latestNonInternalQuery(); as latest) {\n <h4 class=\"et-query-devtools-section\">Latest auth query \u00B7 {{ latest.key }}</h4>\n <et-query-devtools-json [value]=\"latest.snapshot.response()\" />\n }\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No auth providers registered.</p>\n }\n </div>\n }\n\n <!-- SOCKETS -->\n @case ('ws') {\n <div class=\"et-query-devtools-scroll\">\n @for (entry of wsEntries(); track entry.id) {\n @let ws = asWs(entry);\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>{{ entry.meta.name || 'socket' }}</strong>\n <span\n [class.et-query-devtools-chip--success]=\"ws.connected()\"\n [class.et-query-devtools-chip--muted]=\"!ws.connected()\"\n class=\"et-query-devtools-chip\"\n >\n {{ ws.connected() ? 'connected' : 'disconnected' }}\n </span>\n </div>\n <dl class=\"et-query-devtools-meta\">\n <dt>URL</dt>\n <dd class=\"et-query-devtools-mono\">{{ entry.meta.url }}</dd>\n <dt>Rooms</dt>\n <dd>\n @if (ws.rooms().length) {\n <div class=\"et-query-devtools-chips\">\n @for (room of ws.rooms(); track room) {\n <span class=\"et-query-devtools-chip\">{{ room }}</span>\n }\n </div>\n } @else {\n none\n }\n </dd>\n </dl>\n\n <h5 class=\"et-query-devtools-section\">Messages ({{ ws.messages().length }})</h5>\n @for (message of ws.messages(); track message.id) {\n <div class=\"et-query-devtools-ws-message\">\n <div class=\"et-query-devtools-card-head\">\n <span class=\"et-query-devtools-count\">{{ formatTime(message.timestamp) }}</span>\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">{{ message.room }}</span>\n <span class=\"et-query-devtools-method\">{{ message.event }}</span>\n </div>\n <et-query-devtools-json [value]=\"message.data\" />\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No messages received yet.</p>\n }\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No web socket clients registered.</p>\n }\n </div>\n }\n\n <!-- CACHE -->\n @case ('cache') {\n <div class=\"et-query-devtools-scroll\">\n @for (client of cacheView(); track client.name) {\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong class=\"et-query-devtools-mono\">{{ client.baseUrl || client.name }}</strong>\n <span class=\"et-query-devtools-count\">{{ client.entries.length }} entries</span>\n </div>\n <table class=\"et-query-devtools-table\">\n <thead>\n <tr>\n <th>Request</th>\n <th>Consumers</th>\n <th>Secure</th>\n <th>Freshness</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (cacheEntry of client.entries; track cacheEntry.key) {\n <tr>\n <td [title]=\"cacheEntry.request.method + ' ' + cacheEntry.request.url\">\n <span class=\"et-query-devtools-method\">{{ cacheEntry.request.method }}</span>\n <span class=\"et-query-devtools-url\">{{ requestPath(cacheEntry.request.url) }}</span>\n </td>\n <td>{{ cacheEntry.consumerCount }}</td>\n <td>{{ cacheEntry.isSecure ? 'yes' : 'no' }}</td>\n <td>{{ cacheFreshness(cacheEntry) }}</td>\n <td class=\"et-query-devtools-cell-actions\">\n <button (click)=\"refetchCacheEntry(cacheEntry)\" class=\"et-query-devtools-btn\" type=\"button\">\n Refetch\n </button>\n <button\n (click)=\"evictCacheEntry(client.repository, cacheEntry.key)\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n >\n Evict\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td class=\"et-query-devtools-empty\" colspan=\"5\">Cache empty.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No clients with a cache yet.</p>\n }\n </div>\n }\n\n <!-- EVENTS -->\n @case ('events') {\n <div class=\"et-query-devtools-scroll\">\n <div class=\"et-query-devtools-toolbar\">\n <span class=\"et-query-devtools-count\">{{ eventLog().length }} events</span>\n <button (click)=\"clearEvents()\" class=\"et-query-devtools-btn\" type=\"button\">Clear</button>\n </div>\n <table class=\"et-query-devtools-table\">\n <thead>\n <tr>\n <th>Time</th>\n <th>Base URL</th>\n <th>Type</th>\n <th>Request</th>\n </tr>\n </thead>\n <tbody>\n @for (event of eventLog(); track event.id) {\n <tr [attr.data-status]=\"event.type === 'request-error' ? 'error' : 'success'\">\n <td>{{ formatTime(event.timestamp) }}</td>\n <td class=\"et-query-devtools-mono\">{{ event.client }}</td>\n <td>\n {{ event.type === 'request-error' ? 'error ' + event.status : 'success' }}\n @if (event.isSecure) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">secure</span>\n }\n </td>\n <td [title]=\"event.method + ' ' + event.url\">\n <span class=\"et-query-devtools-method\">{{ event.method }}</span>\n <span class=\"et-query-devtools-url\">{{ requestPath(event.url) }}</span>\n </td>\n </tr>\n } @empty {\n <tr>\n <td class=\"et-query-devtools-empty\" colspan=\"4\">No events captured yet.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n </div>\n\n <!-- Reusable inner-query list (stacks) \u2014 clicking opens the split-view drawer, not a tab switch. -->\n <ng-template #innerQueriesTpl let-links=\"links\">\n <div class=\"et-query-devtools-inner-queries\">\n @for (link of links; track $index) {\n <button\n [class.et-query-devtools-inner-row--active]=\"stackSelectedQueryId() === link.id\"\n [disabled]=\"!link.id\"\n (click)=\"stackSelectedQueryId.set(link.id)\"\n class=\"et-query-devtools-inner-row\"\n type=\"button\"\n >\n <span [attr.data-status]=\"queryStatus(link.query)\" class=\"et-query-devtools-status\"></span>\n <span class=\"et-query-devtools-method\">{{ link.method }}</span>\n <span class=\"et-query-devtools-route\">{{ link.route || link.query.id() || 'query ' + ($index + 1) }}</span>\n </button>\n }\n </div>\n </ng-template>\n\n <!-- Reusable query detail \u2014 used by the Queries tab and the stacks split-view drawer. -->\n <ng-template #queryDetailTpl let-sel=\"sel\">\n <div class=\"et-query-devtools-detail-head\">\n <span class=\"et-query-devtools-method\">{{ sel.entry.meta.method }}</span>\n <span class=\"et-query-devtools-route\">{{ sel.entry.meta.route || '\u2014' }}</span>\n </div>\n\n <div class=\"et-query-devtools-actions\">\n <span class=\"et-query-devtools-actions-label\">Run</span>\n <button (click)=\"executeQuery(sel.query, false)\" class=\"et-query-devtools-btn\" type=\"button\">Execute</button>\n <button (click)=\"executeQuery(sel.query, true)\" class=\"et-query-devtools-btn\" type=\"button\">Cached</button>\n <button (click)=\"resetQuery(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Reset</button>\n <button (click)=\"copyReport(sel.entry, sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">\n {{ copiedReport() ? '\u2713 Copied' : 'Copy report' }}\n </button>\n <span class=\"et-query-devtools-actions-label\">Edit</span>\n <button (click)=\"openResponseEditor(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Response</button>\n <button (click)=\"openArgsEditor(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Replay args</button>\n <span class=\"et-query-devtools-actions-label\">Force</span>\n <button (click)=\"forceLoading(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Loading</button>\n <button (click)=\"forceError(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Error</button>\n <button (click)=\"forceEmpty(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Empty</button>\n <button (click)=\"clearForced(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Clear</button>\n </div>\n\n @if (editorMode() !== 'none') {\n <div class=\"et-query-devtools-editor\">\n @if (editorMode() === 'response') {\n <h4 class=\"et-query-devtools-section\">Edit response \u2192 setResponse()</h4>\n <textarea\n #respArea\n [value]=\"responseDraft()\"\n (input)=\"responseDraft.set(respArea.value)\"\n class=\"et-query-devtools-textarea\"\n spellcheck=\"false\"\n ></textarea>\n <div class=\"et-query-devtools-actions\">\n <button (click)=\"applyResponse(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Apply</button>\n <button (click)=\"cancelEditor()\" class=\"et-query-devtools-btn\" type=\"button\">Cancel</button>\n </div>\n } @else {\n <h4 class=\"et-query-devtools-section\">Replay with edited args \u2192 execute()</h4>\n <textarea\n #argsArea\n [value]=\"argsDraft()\"\n (input)=\"argsDraft.set(argsArea.value)\"\n class=\"et-query-devtools-textarea\"\n spellcheck=\"false\"\n ></textarea>\n <div class=\"et-query-devtools-actions\">\n <button (click)=\"applyArgs(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Execute</button>\n <button (click)=\"cancelEditor()\" class=\"et-query-devtools-btn\" type=\"button\">Cancel</button>\n </div>\n }\n @if (editError(); as err) {\n <p class=\"et-query-devtools-editor-error\">{{ err }}</p>\n }\n </div>\n }\n\n <dl class=\"et-query-devtools-meta\">\n <dt>Base URL</dt>\n <dd class=\"et-query-devtools-mono\">{{ sel.entry.meta.clientBaseUrl || sel.entry.meta.clientName || '\u2014' }}</dd>\n <dt>Status</dt>\n <dd>{{ queryStatus(sel.query) }}</dd>\n <dt>Cache key</dt>\n <dd class=\"et-query-devtools-mono\">{{ sel.query.id() || '\u2014' }}</dd>\n <dt>Last executed</dt>\n <dd>{{ formatTime(sel.query.lastTimeExecutedAt()) }}</dd>\n <dt>Triggered by</dt>\n <dd>{{ sel.query.triggeredBy() || 'user' }}</dd>\n @if (sel.entry.meta.featureTypes?.length) {\n <dt>Features</dt>\n <dd>\n <div class=\"et-query-devtools-chips\">\n @for (feature of sel.entry.meta.featureTypes; track feature) {\n <span class=\"et-query-devtools-chip\">{{ featureLabel(feature) }}</span>\n }\n </div>\n </dd>\n }\n </dl>\n\n @if (sel.entry.meta.gqlQuery; as gqlQuery) {\n <h5 class=\"et-query-devtools-section\">GraphQL document</h5>\n <pre class=\"et-query-devtools-code\">{{ gqlDocument(gqlQuery) }}</pre>\n }\n\n <div class=\"et-query-devtools-section-row\">\n <h4 class=\"et-query-devtools-section\">Value explorer</h4>\n <input\n #jsonSearchInput\n [value]=\"jsonSearch()\"\n (input)=\"jsonSearch.set(jsonSearchInput.value)\"\n class=\"et-query-devtools-search\"\n type=\"search\"\n placeholder=\"filter keys / values\u2026\"\n />\n </div>\n\n <h5 class=\"et-query-devtools-section\">Args</h5>\n <et-query-devtools-json\n [value]=\"sel.query.args()\"\n [search]=\"jsonSearchTerm()\"\n [expandedPaths]=\"jsonExpandedPaths()\"\n [collapsedPaths]=\"jsonCollapsedPaths()\"\n [toggleFn]=\"toggleJsonPath\"\n path=\"args\"\n />\n\n @if (sel.query.error(); as error) {\n <h5 class=\"et-query-devtools-section\">Error ({{ error.raw.status }})</h5>\n <et-query-devtools-json\n [value]=\"error.isList ? error.errors : error.error\"\n [search]=\"jsonSearchTerm()\"\n [expandedPaths]=\"jsonExpandedPaths()\"\n [collapsedPaths]=\"jsonCollapsedPaths()\"\n [toggleFn]=\"toggleJsonPath\"\n path=\"error\"\n />\n } @else {\n <h5 class=\"et-query-devtools-section\">Response</h5>\n <et-query-devtools-json\n [value]=\"sel.query.response()\"\n [search]=\"jsonSearchTerm()\"\n [expandedPaths]=\"jsonExpandedPaths()\"\n [collapsedPaths]=\"jsonCollapsedPaths()\"\n [toggleFn]=\"toggleJsonPath\"\n path=\"response\"\n />\n }\n </ng-template>\n </section>\n}\n\n@if (!open()) {\n <et-query-devtools-toggle (openChange)=\"toggleOpen()\" />\n}\n\n@if (inspectActive() && inspectHover(); as hover) {\n <div\n [style.left.px]=\"hover.rect.left\"\n [style.top.px]=\"hover.rect.top\"\n [style.width.px]=\"hover.rect.width\"\n [style.height.px]=\"hover.rect.height\"\n class=\"et-query-devtools-inspect-box\"\n >\n <span class=\"et-query-devtools-inspect-tag\">{{ inspectLabel(hover.entries) }}</span>\n </div>\n}\n", styles: ["@layer components{.et-query-devtools-host{--_et-qdt-bg: var(--et-surface-background-solid, rgb(24 24 27));--_et-qdt-fg: var(--et-surface-color-solid, rgb(244 244 245));--_et-qdt-muted: var(--et-surface-color-muted-solid, rgb(161 161 170));--_et-qdt-subtle: var(--et-surface-color-subtle-solid, rgb(113 113 122));--_et-qdt-border: var(--et-surface-border-solid, rgb(255 255 255 / .12));--_et-qdt-interaction: var(--et-surface-interaction-solid, rgb(255 255 255));--_et-qdt-accent: var(--et-theme-color-primary-solid, rgb(96 165 250));--_et-qdt-hover: color-mix(in srgb, var(--_et-qdt-interaction) 10%, transparent);--_et-qdt-active: color-mix(in srgb, var(--_et-qdt-interaction) 18%, transparent);--_et-qdt-success: rgb(34 197 94);--_et-qdt-error: rgb(239 68 68);--_et-qdt-loading: rgb(234 179 8);position:fixed;inset-inline:0;inset-block-end:0;z-index:2147483000;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,Liberation Mono,monospace;font-size:1.2rem;line-height:1.4;pointer-events:none}.et-query-devtools-host>*{pointer-events:auto}.et-query-devtools-panel{position:relative;display:flex;flex-direction:column;inline-size:100%;min-block-size:20rem;max-block-size:90vh;background:var(--_et-qdt-bg);color:var(--_et-qdt-fg);border-block-start:1px solid var(--_et-qdt-border);box-shadow:0 -.4rem 2rem #00000059}.et-query-devtools-panel--resizing{-webkit-user-select:none;user-select:none}.et-query-devtools-resize{position:absolute;inset-inline:0;inset-block-start:0;block-size:.6rem;cursor:ns-resize;z-index:1}.et-query-devtools-resize:after{content:\"\";position:absolute;inset-inline:50%;inset-block-start:.2rem;inline-size:4rem;margin-inline-start:-2rem;block-size:.2rem;border-radius:999px;background:var(--_et-qdt-border)}.et-query-devtools-resize:hover:after{background:var(--_et-qdt-accent)}.et-query-devtools-header{display:flex;align-items:center;justify-content:space-between;gap:.8rem;padding-inline:.8rem;border-block-end:1px solid var(--_et-qdt-border);flex:0 0 auto}.et-query-devtools-tabs{display:flex;flex-wrap:wrap;gap:.2rem}.et-query-devtools-tab{appearance:none;border:none;background:transparent;color:var(--_et-qdt-muted);padding:.9rem 1rem;cursor:pointer;border-block-end:2px solid transparent;font:inherit}.et-query-devtools-tab:hover{color:var(--_et-qdt-fg)}.et-query-devtools-tab--active{color:var(--_et-qdt-fg);border-block-end-color:var(--_et-qdt-accent)}.et-query-devtools-header-actions{display:flex;align-items:center;gap:.4rem;flex:0 0 auto}.et-query-devtools-inspect-btn{appearance:none;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:transparent;color:var(--_et-qdt-muted);cursor:pointer;padding:.4rem .8rem;font:inherit}.et-query-devtools-inspect-btn:hover{color:var(--_et-qdt-fg)}.et-query-devtools-inspect-btn--active{color:#09090b;background:var(--_et-qdt-accent);border-color:var(--_et-qdt-accent)}.et-query-devtools-close{appearance:none;border:none;background:transparent;color:var(--_et-qdt-muted);cursor:pointer;padding:.6rem;font:inherit}.et-query-devtools-inspect-box{position:fixed;z-index:2147483002;pointer-events:none;border:2px solid var(--_et-qdt-accent);background:color-mix(in srgb,var(--_et-qdt-accent) 15%,transparent);border-radius:.2rem}.et-query-devtools-inspect-tag{position:absolute;inset-block-start:100%;inset-inline-start:0;margin-block-start:.2rem;padding:.2rem .5rem;border-radius:.3rem;background:var(--_et-qdt-accent);color:#09090b;font-family:ui-monospace,monospace;font-size:1.1rem;font-weight:600;white-space:nowrap}.et-query-devtools-close:hover{color:var(--_et-qdt-fg)}.et-query-devtools-body{flex:1 1 auto;min-block-size:0;overflow:hidden}.et-query-devtools-master-detail{display:flex;block-size:100%;min-block-size:0}.et-query-devtools-list{flex:0 0 34%;min-inline-size:22rem;max-inline-size:40rem;overflow-y:auto;border-inline-end:1px solid var(--_et-qdt-border)}.et-query-devtools-detail{flex:1 1 auto;min-inline-size:0;overflow-y:auto;padding:1rem}.et-query-devtools-detail .et-query-devtools-detail-head,.et-query-devtools-drawer .et-query-devtools-detail-head{padding-block-end:.8rem;border-block-end:1px solid var(--_et-qdt-border)}.et-query-devtools-detail .et-query-devtools-meta,.et-query-devtools-drawer .et-query-devtools-meta{padding:.8rem;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:color-mix(in srgb,var(--_et-qdt-interaction) 3%,transparent)}.et-query-devtools-detail .et-query-devtools-section-row,.et-query-devtools-drawer .et-query-devtools-section-row{padding-block-start:.8rem;border-block-start:1px solid var(--_et-qdt-border)}.et-query-devtools-filter-chip{display:inline-flex;align-items:center;padding:.3rem .7rem;border-radius:999px;background:var(--_et-qdt-accent);color:#09090b;font-size:1.1rem;font-weight:600}.et-query-devtools-scroll{block-size:100%;overflow:auto;padding:1rem;display:flex;flex-direction:column;gap:1rem}.et-query-devtools-split{display:flex;block-size:100%;min-block-size:0}.et-query-devtools-split .et-query-devtools-scroll{flex:1 1 auto;min-inline-size:0}.et-query-devtools-drawer{flex:0 0 42%;min-inline-size:26rem;max-inline-size:48rem;overflow-y:auto;padding:0 1rem 1rem;border-inline-start:1px solid var(--_et-qdt-border)}.et-query-devtools-drawer-head{display:flex;align-items:center;justify-content:space-between;gap:.8rem;padding:.8rem 0;position:sticky;inset-block-start:0;background:var(--_et-qdt-bg);color:var(--_et-qdt-muted);border-block-end:1px solid var(--_et-qdt-border);margin-block-end:.8rem}.et-query-devtools-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.8rem;padding:.8rem;position:sticky;inset-block-start:0;background:var(--_et-qdt-bg)}.et-query-devtools-field{display:inline-flex;align-items:center;gap:.6rem;color:var(--_et-qdt-muted)}.et-query-devtools-select{background:var(--_et-qdt-bg);color:var(--_et-qdt-fg);border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.4rem .6rem;font:inherit}.et-query-devtools-count{color:var(--_et-qdt-subtle)}.et-query-devtools-row{display:flex;align-items:center;gap:.8rem;inline-size:100%;text-align:start;appearance:none;border:none;border-block-end:1px solid var(--_et-qdt-border);background:transparent;color:var(--_et-qdt-fg);padding:.8rem;cursor:pointer;font:inherit}.et-query-devtools-row:hover{background:var(--_et-qdt-hover)}.et-query-devtools-row--active{background:var(--_et-qdt-active)}.et-query-devtools-method{font-weight:700;font-size:1.1rem;color:var(--_et-qdt-muted);white-space:nowrap}.et-query-devtools-route{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto;min-inline-size:0}.et-query-devtools-status{inline-size:.8rem;block-size:.8rem;border-radius:999px;flex:0 0 auto;background:var(--_et-qdt-subtle)}.et-query-devtools-status[data-status=success]{background:var(--_et-qdt-success)}.et-query-devtools-status[data-status=error]{background:var(--_et-qdt-error)}.et-query-devtools-status[data-status=loading],.et-query-devtools-status[data-status=running]{background:var(--_et-qdt-loading)}.et-query-devtools-detail-head{display:flex;align-items:center;gap:.8rem;margin-block-end:.8rem}.et-query-devtools-actions{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem;margin-block-end:.8rem}.et-query-devtools-btn{appearance:none;border:1px solid var(--_et-qdt-border);border-radius:.3rem;background:transparent;color:var(--_et-qdt-fg);padding:.25rem .55rem;font-size:1.15rem;line-height:1.4;cursor:pointer}.et-query-devtools-btn:hover{background:var(--_et-qdt-hover)}.et-query-devtools-meta{display:grid;grid-template-columns:auto 1fr;gap:.3rem 1rem;margin:0 0 1rem}.et-query-devtools-meta dt{color:var(--_et-qdt-muted)}.et-query-devtools-meta dd{margin:0;overflow-wrap:anywhere}.et-query-devtools-section{margin:1rem 0 .4rem;font-size:1.1rem;text-transform:uppercase;letter-spacing:.05em;color:var(--_et-qdt-muted)}.et-query-devtools-json{margin:0;padding:.8rem;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:color-mix(in srgb,var(--_et-qdt-interaction) 4%,transparent);overflow-x:auto;white-space:pre-wrap;overflow-wrap:anywhere;max-block-size:24rem;overflow-y:auto}.et-query-devtools-json--error{border-color:color-mix(in srgb,var(--_et-qdt-error) 50%,transparent)}.et-query-devtools-mono{font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.et-query-devtools-code{margin:0 0 .8rem;padding:.8rem;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:color-mix(in srgb,var(--_et-qdt-interaction) 4%,transparent);overflow:auto;white-space:pre;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:1.15rem;max-block-size:20rem}.et-query-devtools-ws-message{border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.6rem;margin-block-end:.4rem}.et-query-devtools-url{display:inline-block;max-inline-size:32rem;margin-inline-start:.6rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.et-query-devtools-chips,.et-query-devtools-chip-row{display:flex;flex-wrap:wrap;gap:.4rem;margin-block-end:.8rem}.et-query-devtools-chip{display:inline-flex;align-items:center;gap:.3rem;padding:.2rem .6rem;border-radius:999px;border:1px solid var(--_et-qdt-border);font-size:1.1rem;color:var(--_et-qdt-fg);background:color-mix(in srgb,var(--_et-qdt-interaction) 6%,transparent)}.et-query-devtools-chip--muted{color:var(--_et-qdt-muted)}.et-query-devtools-chip--loading{color:var(--_et-qdt-loading);border-color:color-mix(in srgb,var(--_et-qdt-loading) 50%,transparent)}.et-query-devtools-chip--error{color:var(--_et-qdt-error);border-color:color-mix(in srgb,var(--_et-qdt-error) 50%,transparent)}.et-query-devtools-chip--success{color:var(--_et-qdt-success);border-color:color-mix(in srgb,var(--_et-qdt-success) 50%,transparent)}.et-query-devtools-card{border:1px solid var(--_et-qdt-border);border-radius:.6rem;padding:1rem}.et-query-devtools-card-head{display:flex;align-items:center;gap:.6rem;margin-block-end:.8rem}.et-query-devtools-card-title{display:flex;align-items:baseline;gap:.5rem;margin-block-end:.2rem}.et-query-devtools-card-sub{color:var(--_et-qdt-subtle);font-size:1.1rem;margin-block-end:.6rem;overflow-wrap:anywhere}.et-query-devtools-inner-queries{display:flex;flex-direction:column;gap:.3rem}.et-query-devtools-inner-row{display:flex;align-items:center;gap:.6rem;inline-size:100%;text-align:start;appearance:none;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:transparent;color:var(--_et-qdt-fg);padding:.4rem .6rem;cursor:pointer;font:inherit}.et-query-devtools-inner-row:hover:not([disabled]){background:var(--_et-qdt-hover)}.et-query-devtools-inner-row--active{background:var(--_et-qdt-active);border-color:var(--_et-qdt-accent)}.et-query-devtools-inner-row[disabled]{cursor:default;opacity:.6}.et-query-devtools-step-list{display:flex;flex-direction:column;gap:.4rem;margin:0;padding:0;list-style:none}.et-query-devtools-step-item{border:1px solid var(--_et-qdt-border);border-radius:.4rem;overflow:hidden}.et-query-devtools-step-item[data-status=error]{border-color:color-mix(in srgb,var(--_et-qdt-error) 50%,transparent)}.et-query-devtools-step-head{display:flex;align-items:stretch}.et-query-devtools-step-head .et-query-devtools-inner-row{border:none;border-radius:0;flex:1 1 auto}.et-query-devtools-step-caret-btn{appearance:none;border:none;border-inline-start:1px solid var(--_et-qdt-border);background:transparent;color:var(--_et-qdt-muted);cursor:pointer;padding:0 .7rem;font:inherit}.et-query-devtools-step-caret-btn:hover{background:var(--_et-qdt-hover);color:var(--_et-qdt-fg)}.et-query-devtools-step-toggle{display:flex;align-items:center;gap:.6rem;inline-size:100%;text-align:start;appearance:none;border:none;background:transparent;color:var(--_et-qdt-fg);padding:.6rem;cursor:pointer;font:inherit}.et-query-devtools-step-toggle:hover{background:var(--_et-qdt-hover)}.et-query-devtools-step-index{flex:1 1 auto;min-inline-size:0}.et-query-devtools-step-caret{color:var(--_et-qdt-muted)}.et-query-devtools-step-io{padding:0 .8rem .8rem;border-block-start:1px solid var(--_et-qdt-border)}.et-query-devtools-table{inline-size:100%;border-collapse:collapse}.et-query-devtools-table th,.et-query-devtools-table td{text-align:start;padding:.5rem .6rem;border-block-end:1px solid var(--_et-qdt-border);vertical-align:top}.et-query-devtools-table th{color:var(--_et-qdt-muted);font-weight:600;position:sticky;inset-block-start:0;background:var(--_et-qdt-bg)}.et-query-devtools-table tr[data-status=error] td{color:var(--_et-qdt-error)}.et-query-devtools-empty{color:var(--_et-qdt-subtle);padding:1.2rem;text-align:center}.et-query-devtools-actions-label{color:var(--_et-qdt-subtle);align-self:center;text-transform:uppercase;font-size:1rem;letter-spacing:.05em}.et-query-devtools-actions-label:not(:first-child){margin-inline-start:.7rem}.et-query-devtools-cell-actions{display:flex;gap:.4rem;white-space:nowrap}.et-query-devtools-section-row{display:flex;align-items:center;justify-content:space-between;gap:.8rem;margin-block-start:1rem}.et-query-devtools-search{flex:0 1 20rem;background:var(--_et-qdt-bg);color:var(--_et-qdt-fg);border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.4rem .6rem;font:inherit}.et-query-devtools-editor{border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.8rem;margin-block-end:1rem}.et-query-devtools-textarea{inline-size:100%;min-block-size:12rem;resize:vertical;background:color-mix(in srgb,var(--_et-qdt-interaction) 4%,transparent);color:var(--_et-qdt-fg);border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.6rem;font-family:inherit;font-size:1.2rem}.et-query-devtools-editor-error{color:var(--_et-qdt-error);margin:.4rem 0 0}.et-query-devtools-json-node,.et-query-devtools-json-children{display:flex;flex-direction:column}.et-query-devtools-json-children{padding-inline-start:1.4rem;border-inline-start:1px solid var(--_et-qdt-border);margin-inline-start:.4rem}.et-query-devtools-json-row{display:flex;align-items:baseline;gap:.4rem;padding:.1rem 0;font-family:inherit;line-height:1.5}.et-query-devtools-json-toggle{appearance:none;border:none;background:transparent;color:var(--_et-qdt-fg);cursor:pointer;text-align:start;font:inherit;padding:.1rem 0}.et-query-devtools-json-toggle:hover{background:var(--_et-qdt-hover)}.et-query-devtools-json-caret{color:var(--_et-qdt-subtle);inline-size:1rem;flex:0 0 auto}.et-query-devtools-json-key{color:var(--_et-qdt-accent)}.et-query-devtools-json-colon,.et-query-devtools-json-preview{color:var(--_et-qdt-subtle)}.et-query-devtools-json-value{overflow-wrap:anywhere}.et-query-devtools-json-leaf[data-kind=string] .et-query-devtools-json-value{color:var(--_et-qdt-success)}.et-query-devtools-json-leaf[data-kind=number] .et-query-devtools-json-value,.et-query-devtools-json-leaf[data-kind=boolean] .et-query-devtools-json-value{color:var(--_et-qdt-loading)}.et-query-devtools-json-leaf[data-kind=null] .et-query-devtools-json-value,.et-query-devtools-json-leaf[data-kind=undefined] .et-query-devtools-json-value{color:var(--_et-qdt-subtle)}.et-query-devtools-json-hit{background:color-mix(in srgb,var(--_et-qdt-loading) 40%,transparent);border-radius:.2rem}.et-query-devtools-json-copy{appearance:none;border:none;background:transparent;color:var(--_et-qdt-subtle);cursor:pointer;opacity:0;font:inherit}.et-query-devtools-json-row:hover .et-query-devtools-json-copy{opacity:1}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: QueryDevtoolsJsonComponent, selector: "et-query-devtools-json", inputs: ["value", "nodeKey", "depth", "search", "path", "expandedPaths", "collapsedPaths", "toggleFn"] }, { kind: "component", type: QueryDevtoolsToggleComponent, selector: "et-query-devtools-toggle", outputs: ["openChange"] }], encapsulation: i0.ViewEncapsulation.None }); }
30915
+ }
30916
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: QueryDevtoolsComponent, decorators: [{
30917
+ type: Component,
30918
+ args: [{ selector: 'et-query-devtools', encapsulation: ViewEncapsulation.None, imports: [NgTemplateOutlet, QueryDevtoolsJsonComponent, QueryDevtoolsToggleComponent], host: {
30919
+ class: 'et-query-devtools-host',
30920
+ }, template: "@if (open()) {\n <section\n [class.et-query-devtools-panel--resizing]=\"resizing()\"\n [style.height.px]=\"panelHeight()\"\n class=\"et-query-devtools-panel\"\n >\n <div\n (pointerdown)=\"startResize($event)\"\n class=\"et-query-devtools-resize\"\n title=\"Drag to resize\"\n aria-hidden=\"true\"\n ></div>\n <header class=\"et-query-devtools-header\">\n <nav class=\"et-query-devtools-tabs\" role=\"tablist\">\n @for (tab of tabs; track tab.id) {\n <button\n [class.et-query-devtools-tab--active]=\"activeTab() === tab.id\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n (click)=\"activeTab.set(tab.id)\"\n class=\"et-query-devtools-tab\"\n type=\"button\"\n role=\"tab\"\n >\n {{ tab.label }}\n </button>\n }\n </nav>\n\n <div class=\"et-query-devtools-header-actions\">\n <button\n [class.et-query-devtools-inspect-btn--active]=\"inspectActive()\"\n (click)=\"toggleInspect()\"\n class=\"et-query-devtools-inspect-btn\"\n type=\"button\"\n title=\"Inspect: hover the UI to find its query\"\n >\n \u2316 Inspect\n </button>\n <button (click)=\"toggleOpen()\" class=\"et-query-devtools-close\" type=\"button\" title=\"Close devtools\">\u2715</button>\n </div>\n </header>\n\n <div class=\"et-query-devtools-body\">\n @switch (activeTab()) {\n <!-- QUERIES -->\n @case ('queries') {\n <div class=\"et-query-devtools-master-detail\">\n <div class=\"et-query-devtools-list\">\n <div class=\"et-query-devtools-toolbar\">\n @if (inspectFilterIds(); as ids) {\n <span class=\"et-query-devtools-filter-chip\">\u2316 inspected \u00B7 {{ ids.length }}</span>\n <button (click)=\"clearInspectFilter()\" class=\"et-query-devtools-btn\" type=\"button\">Reset</button>\n } @else {\n <label class=\"et-query-devtools-field\">\n <span>Client</span>\n <select\n #clientSelect\n [value]=\"selectedClientName() ?? ''\"\n (change)=\"selectClient(clientSelect.value || null)\"\n class=\"et-query-devtools-select\"\n >\n <option value=\"\">All clients</option>\n @for (name of clientNames(); track name) {\n <option [value]=\"name\">{{ name }}</option>\n }\n </select>\n </label>\n }\n <span class=\"et-query-devtools-count\">{{ filteredQueries().length }}</span>\n </div>\n\n @for (item of filteredQueries(); track item.entry.id) {\n <button\n [class.et-query-devtools-row--active]=\"selectedQueryId() === item.entry.id\"\n (click)=\"selectedQueryId.set(item.entry.id)\"\n class=\"et-query-devtools-row\"\n type=\"button\"\n >\n <span\n [attr.data-status]=\"queryStatus(item.query)\"\n [title]=\"queryStatus(item.query)\"\n class=\"et-query-devtools-status\"\n ></span>\n <span class=\"et-query-devtools-method\">{{ item.entry.meta.method }}</span>\n <span class=\"et-query-devtools-route\">{{ item.entry.meta.route || '\u2014' }}</span>\n @if (isStale(item.query)) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">stale</span>\n }\n </button>\n } @empty {\n @if (inspectFilterIds()) {\n <p class=\"et-query-devtools-empty\">\n The inspected element's queries are no longer registered. Reset the filter.\n </p>\n } @else {\n <p class=\"et-query-devtools-empty\">No queries registered.</p>\n }\n }\n </div>\n\n <div class=\"et-query-devtools-detail\">\n @if (selectedQuery(); as sel) {\n <ng-container *ngTemplateOutlet=\"queryDetailTpl; context: { sel }\" />\n } @else {\n <p class=\"et-query-devtools-empty\">Select a query to inspect it.</p>\n }\n </div>\n </div>\n }\n\n <!-- STACKS -->\n @case ('stacks') {\n <div class=\"et-query-devtools-split\">\n <div class=\"et-query-devtools-scroll\">\n @for (entry of stackEntries(); track entry.id) {\n @if (entry.kind === 'paged-query-stack') {\n @let paged = asPagedStack(entry);\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <span class=\"et-query-devtools-chip\">paged stack</span>\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">{{ paged.direction() }}</span>\n @if (paged.loading()) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--loading\">loading</span>\n }\n </div>\n\n @let pagedId = stackIdentity(paged);\n <div class=\"et-query-devtools-card-title\">\n <span class=\"et-query-devtools-method\">{{ pagedId.method }}</span>\n <span class=\"et-query-devtools-route\">{{ pagedId.route }}</span>\n </div>\n <div class=\"et-query-devtools-card-sub et-query-devtools-mono\">{{ pagedId.baseUrl }}</div>\n @if (entry.meta.featureTypes?.length) {\n <div class=\"et-query-devtools-chips\">\n @for (feature of entry.meta.featureTypes; track feature) {\n <span class=\"et-query-devtools-chip\">{{ featureLabel(feature) }}</span>\n }\n </div>\n }\n\n <dl class=\"et-query-devtools-meta\">\n <dt>Pages loaded</dt>\n <dd>{{ paged.queries().length }}</dd>\n <dt>Items</dt>\n <dd>{{ paged.items().length }}</dd>\n <dt>First page loaded</dt>\n <dd>{{ paged.isFirstPageLoaded() }}</dd>\n <dt>Last page loaded</dt>\n <dd>{{ paged.isLastPageLoaded() }}</dd>\n </dl>\n <ng-container *ngTemplateOutlet=\"innerQueriesTpl; context: { links: queriesForStack(paged) }\" />\n </div>\n } @else {\n @let stack = asStack(entry);\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <span class=\"et-query-devtools-chip\">stack</span>\n @if (stack.anyLoading()) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--loading\">loading</span>\n }\n @if (stack.anyError()) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--error\">error</span>\n }\n </div>\n\n @let stackId = stackIdentity(stack);\n <div class=\"et-query-devtools-card-title\">\n <span class=\"et-query-devtools-method\">{{ stackId.method }}</span>\n <span class=\"et-query-devtools-route\">{{ stackId.route }}</span>\n </div>\n <div class=\"et-query-devtools-card-sub et-query-devtools-mono\">{{ stackId.baseUrl }}</div>\n @if (entry.meta.featureTypes?.length) {\n <div class=\"et-query-devtools-chips\">\n @for (feature of entry.meta.featureTypes; track feature) {\n <span class=\"et-query-devtools-chip\">{{ featureLabel(feature) }}</span>\n }\n </div>\n }\n\n <dl class=\"et-query-devtools-meta\">\n <dt>Queries</dt>\n <dd>{{ stack.queries().length }}</dd>\n <dt>Loading</dt>\n <dd>{{ stack.loadingProgress().loaded }} / {{ stack.loadingProgress().total }}</dd>\n <dt>Errors</dt>\n <dd>{{ stack.errors().length }}</dd>\n </dl>\n <ng-container *ngTemplateOutlet=\"innerQueriesTpl; context: { links: queriesForStack(stack) }\" />\n </div>\n }\n } @empty {\n <p class=\"et-query-devtools-empty\">No stacks registered.</p>\n }\n </div>\n\n @if (stackSelectedQuery(); as sel) {\n <aside class=\"et-query-devtools-drawer\">\n <div class=\"et-query-devtools-drawer-head\">\n <span>Query detail</span>\n <button\n (click)=\"stackSelectedQueryId.set(null)\"\n class=\"et-query-devtools-close\"\n type=\"button\"\n title=\"Close detail\"\n >\n \u2715\n </button>\n </div>\n <ng-container *ngTemplateOutlet=\"queryDetailTpl; context: { sel }\" />\n </aside>\n }\n </div>\n }\n\n <!-- SEQUENCES -->\n @case ('sequences') {\n <div class=\"et-query-devtools-split\">\n <div class=\"et-query-devtools-scroll\">\n @for (entry of sequenceEntries(); track entry.id) {\n @let sequence = asSequence(entry);\n @if (sequence) {\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <span class=\"et-query-devtools-chip\">sequence</span>\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">{{ sequence.status() }}</span>\n <span class=\"et-query-devtools-count\"\n >step {{ sequence.currentStep() }} / {{ sequence.total }}</span\n >\n </div>\n\n <ol class=\"et-query-devtools-step-list\">\n @for (link of queriesForSequence(sequence); track $index; let i = $index) {\n <li [attr.data-status]=\"sequenceStepStatus(sequence, i)\" class=\"et-query-devtools-step-item\">\n <div class=\"et-query-devtools-step-head\">\n <button\n [class.et-query-devtools-inner-row--active]=\"sequenceSelectedQueryId() === link.id\"\n [disabled]=\"!link.id\"\n (click)=\"sequenceSelectedQueryId.set(link.id)\"\n class=\"et-query-devtools-inner-row\"\n type=\"button\"\n >\n <span\n [attr.data-status]=\"sequenceStepStatus(sequence, i)\"\n class=\"et-query-devtools-status\"\n ></span>\n <span class=\"et-query-devtools-method\">{{ i + 1 }}. {{ link.method }}</span>\n <span class=\"et-query-devtools-route\">{{ link.route || 'step ' + (i + 1) }}</span>\n </button>\n <button\n [attr.aria-expanded]=\"isStepExpanded(entry.id, i)\"\n (click)=\"toggleStep(entry.id, i)\"\n class=\"et-query-devtools-step-caret-btn\"\n type=\"button\"\n title=\"Toggle in / out\"\n >\n {{ isStepExpanded(entry.id, i) ? '\u25BE' : '\u25B8' }}\n </button>\n </div>\n\n @if (isStepExpanded(entry.id, i)) {\n @let snap = stepSnapshot(sequence, i);\n <div class=\"et-query-devtools-step-io\">\n <h5 class=\"et-query-devtools-section\">In \u00B7 args</h5>\n @if (sequence.stepArgs().length > i) {\n <et-query-devtools-json [value]=\"sequence.stepArgs()[i]\" />\n } @else {\n <p class=\"et-query-devtools-empty\">Not run yet.</p>\n }\n\n @if (snap?.error(); as error) {\n <h5 class=\"et-query-devtools-section\">Out \u00B7 error ({{ error.raw.status }})</h5>\n <et-query-devtools-json [value]=\"error.isList ? error.errors : error.error\" />\n } @else {\n <h5 class=\"et-query-devtools-section\">Out \u00B7 response</h5>\n @if (snap) {\n <et-query-devtools-json [value]=\"snap.response()\" />\n } @else {\n <p class=\"et-query-devtools-empty\">Not run yet.</p>\n }\n }\n </div>\n }\n </li>\n }\n </ol>\n </div>\n }\n } @empty {\n <p class=\"et-query-devtools-empty\">No sequences registered.</p>\n }\n </div>\n\n @if (sequenceSelectedQuery(); as sel) {\n <aside class=\"et-query-devtools-drawer\">\n <div class=\"et-query-devtools-drawer-head\">\n <span>Query detail</span>\n <button\n (click)=\"sequenceSelectedQueryId.set(null)\"\n class=\"et-query-devtools-close\"\n type=\"button\"\n title=\"Close detail\"\n >\n \u2715\n </button>\n </div>\n <ng-container *ngTemplateOutlet=\"queryDetailTpl; context: { sel }\" />\n </aside>\n }\n </div>\n }\n\n <!-- AUTH -->\n @case ('auth') {\n <div class=\"et-query-devtools-scroll\">\n @for (entry of authEntries(); track entry.id) {\n @let auth = asAuth(entry);\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>{{ entry.meta.name || 'auth provider' }}</strong>\n <span\n [class.et-query-devtools-chip--success]=\"auth.isAuthenticated()\"\n [class.et-query-devtools-chip--muted]=\"!auth.isAuthenticated()\"\n class=\"et-query-devtools-chip\"\n >\n {{ auth.isAuthenticated() ? 'authenticated' : 'anonymous' }}\n </span>\n </div>\n <dl class=\"et-query-devtools-meta\">\n <dt>Base URL</dt>\n <dd class=\"et-query-devtools-mono\">{{ entry.meta.clientBaseUrl || entry.meta.clientName || '\u2014' }}</dd>\n <dt>Access token</dt>\n <dd>{{ auth.accessToken() ? 'present' : 'none' }}</dd>\n <dt>Refresh token</dt>\n <dd>{{ auth.refreshToken() ? 'present' : 'none' }}</dd>\n @if (authTokenExpiry(auth); as expiry) {\n <dt>Token expires in</dt>\n <dd>{{ expiry }}</dd>\n }\n @if (auth.executionState(); as state) {\n <dt>Execution state</dt>\n <dd>{{ state.type }} \u00B7 {{ state.state }}</dd>\n }\n @if (authQueryKeys(auth).length) {\n <dt>Queries</dt>\n <dd>\n <div class=\"et-query-devtools-chips\">\n @for (key of authQueryKeys(auth); track key) {\n <span class=\"et-query-devtools-chip\">{{ key }}</span>\n }\n </div>\n </dd>\n }\n @if (authFeatures(auth).length) {\n <dt>Features</dt>\n <dd>\n <div class=\"et-query-devtools-chips\">\n @for (feature of authFeatures(auth); track feature) {\n <span class=\"et-query-devtools-chip\">{{ feature }}</span>\n }\n </div>\n </dd>\n }\n </dl>\n\n @if (authTokenPayload(auth); as payload) {\n <h4 class=\"et-query-devtools-section\">Access token payload</h4>\n <et-query-devtools-json [value]=\"payload\" />\n }\n\n @if (auth.latestNonInternalQuery(); as latest) {\n <h4 class=\"et-query-devtools-section\">Latest auth query \u00B7 {{ latest.key }}</h4>\n <et-query-devtools-json [value]=\"latest.snapshot.response()\" />\n }\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No auth providers registered.</p>\n }\n </div>\n }\n\n <!-- SOCKETS -->\n @case ('ws') {\n <div class=\"et-query-devtools-scroll\">\n @for (entry of wsEntries(); track entry.id) {\n @let ws = asWs(entry);\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong>{{ entry.meta.name || 'socket' }}</strong>\n <span\n [class.et-query-devtools-chip--success]=\"ws.connected()\"\n [class.et-query-devtools-chip--muted]=\"!ws.connected()\"\n class=\"et-query-devtools-chip\"\n >\n {{ ws.connected() ? 'connected' : 'disconnected' }}\n </span>\n </div>\n <dl class=\"et-query-devtools-meta\">\n <dt>URL</dt>\n <dd class=\"et-query-devtools-mono\">{{ entry.meta.url }}</dd>\n <dt>Rooms</dt>\n <dd>\n @if (ws.rooms().length) {\n <div class=\"et-query-devtools-chips\">\n @for (room of ws.rooms(); track room) {\n <span class=\"et-query-devtools-chip\">{{ room }}</span>\n }\n </div>\n } @else {\n none\n }\n </dd>\n </dl>\n\n <h5 class=\"et-query-devtools-section\">Messages ({{ ws.messages().length }})</h5>\n @for (message of ws.messages(); track message.id) {\n <div class=\"et-query-devtools-ws-message\">\n <div class=\"et-query-devtools-card-head\">\n <span class=\"et-query-devtools-count\">{{ formatTime(message.timestamp) }}</span>\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">{{ message.room }}</span>\n <span class=\"et-query-devtools-method\">{{ message.event }}</span>\n </div>\n <et-query-devtools-json [value]=\"message.data\" />\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No messages received yet.</p>\n }\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No web socket clients registered.</p>\n }\n </div>\n }\n\n <!-- CACHE -->\n @case ('cache') {\n <div class=\"et-query-devtools-scroll\">\n @for (client of cacheView(); track client.name) {\n <div class=\"et-query-devtools-card\">\n <div class=\"et-query-devtools-card-head\">\n <strong class=\"et-query-devtools-mono\">{{ client.baseUrl || client.name }}</strong>\n <span class=\"et-query-devtools-count\">{{ client.entries.length }} entries</span>\n </div>\n <table class=\"et-query-devtools-table\">\n <thead>\n <tr>\n <th>Request</th>\n <th>Consumers</th>\n <th>Secure</th>\n <th>Freshness</th>\n <th>Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (cacheEntry of client.entries; track cacheEntry.key) {\n <tr>\n <td [title]=\"cacheEntry.request.method + ' ' + cacheEntry.request.url\">\n <span class=\"et-query-devtools-method\">{{ cacheEntry.request.method }}</span>\n <span class=\"et-query-devtools-url\">{{ requestPath(cacheEntry.request.url) }}</span>\n </td>\n <td>{{ cacheEntry.consumerCount }}</td>\n <td>{{ cacheEntry.isSecure ? 'yes' : 'no' }}</td>\n <td>{{ cacheFreshness(cacheEntry) }}</td>\n <td class=\"et-query-devtools-cell-actions\">\n <button (click)=\"refetchCacheEntry(cacheEntry)\" class=\"et-query-devtools-btn\" type=\"button\">\n Refetch\n </button>\n <button\n (click)=\"evictCacheEntry(client.repository, cacheEntry.key)\"\n class=\"et-query-devtools-btn\"\n type=\"button\"\n >\n Evict\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td class=\"et-query-devtools-empty\" colspan=\"5\">Cache empty.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @empty {\n <p class=\"et-query-devtools-empty\">No clients with a cache yet.</p>\n }\n </div>\n }\n\n <!-- EVENTS -->\n @case ('events') {\n <div class=\"et-query-devtools-scroll\">\n <div class=\"et-query-devtools-toolbar\">\n <span class=\"et-query-devtools-count\">{{ eventLog().length }} events</span>\n <button (click)=\"clearEvents()\" class=\"et-query-devtools-btn\" type=\"button\">Clear</button>\n </div>\n <table class=\"et-query-devtools-table\">\n <thead>\n <tr>\n <th>Time</th>\n <th>Base URL</th>\n <th>Type</th>\n <th>Request</th>\n </tr>\n </thead>\n <tbody>\n @for (event of eventLog(); track event.id) {\n <tr [attr.data-status]=\"event.type === 'request-error' ? 'error' : 'success'\">\n <td>{{ formatTime(event.timestamp) }}</td>\n <td class=\"et-query-devtools-mono\">{{ event.client }}</td>\n <td>\n {{ event.type === 'request-error' ? 'error ' + event.status : 'success' }}\n @if (event.isSecure) {\n <span class=\"et-query-devtools-chip et-query-devtools-chip--muted\">secure</span>\n }\n </td>\n <td [title]=\"event.method + ' ' + event.url\">\n <span class=\"et-query-devtools-method\">{{ event.method }}</span>\n <span class=\"et-query-devtools-url\">{{ requestPath(event.url) }}</span>\n </td>\n </tr>\n } @empty {\n <tr>\n <td class=\"et-query-devtools-empty\" colspan=\"4\">No events captured yet.</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n }\n </div>\n\n <!-- Reusable inner-query list (stacks) \u2014 clicking opens the split-view drawer, not a tab switch. -->\n <ng-template #innerQueriesTpl let-links=\"links\">\n <div class=\"et-query-devtools-inner-queries\">\n @for (link of links; track $index) {\n <button\n [class.et-query-devtools-inner-row--active]=\"stackSelectedQueryId() === link.id\"\n [disabled]=\"!link.id\"\n (click)=\"stackSelectedQueryId.set(link.id)\"\n class=\"et-query-devtools-inner-row\"\n type=\"button\"\n >\n <span [attr.data-status]=\"queryStatus(link.query)\" class=\"et-query-devtools-status\"></span>\n <span class=\"et-query-devtools-method\">{{ link.method }}</span>\n <span class=\"et-query-devtools-route\">{{ link.route || link.query.id() || 'query ' + ($index + 1) }}</span>\n </button>\n }\n </div>\n </ng-template>\n\n <!-- Reusable query detail \u2014 used by the Queries tab and the stacks split-view drawer. -->\n <ng-template #queryDetailTpl let-sel=\"sel\">\n <div class=\"et-query-devtools-detail-head\">\n <span class=\"et-query-devtools-method\">{{ sel.entry.meta.method }}</span>\n <span class=\"et-query-devtools-route\">{{ sel.entry.meta.route || '\u2014' }}</span>\n </div>\n\n <div class=\"et-query-devtools-actions\">\n <span class=\"et-query-devtools-actions-label\">Run</span>\n <button (click)=\"executeQuery(sel.query, false)\" class=\"et-query-devtools-btn\" type=\"button\">Execute</button>\n <button (click)=\"executeQuery(sel.query, true)\" class=\"et-query-devtools-btn\" type=\"button\">Cached</button>\n <button (click)=\"resetQuery(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Reset</button>\n <button (click)=\"copyReport(sel.entry, sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">\n {{ copiedReport() ? '\u2713 Copied' : 'Copy report' }}\n </button>\n <span class=\"et-query-devtools-actions-label\">Edit</span>\n <button (click)=\"openResponseEditor(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Response</button>\n <button (click)=\"openArgsEditor(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Replay args</button>\n <span class=\"et-query-devtools-actions-label\">Force</span>\n <button (click)=\"forceLoading(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Loading</button>\n <button (click)=\"forceError(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Error</button>\n <button (click)=\"forceEmpty(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Empty</button>\n <button (click)=\"clearForced(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Clear</button>\n </div>\n\n @if (editorMode() !== 'none') {\n <div class=\"et-query-devtools-editor\">\n @if (editorMode() === 'response') {\n <h4 class=\"et-query-devtools-section\">Edit response \u2192 setResponse()</h4>\n <textarea\n #respArea\n [value]=\"responseDraft()\"\n (input)=\"responseDraft.set(respArea.value)\"\n class=\"et-query-devtools-textarea\"\n spellcheck=\"false\"\n ></textarea>\n <div class=\"et-query-devtools-actions\">\n <button (click)=\"applyResponse(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Apply</button>\n <button (click)=\"cancelEditor()\" class=\"et-query-devtools-btn\" type=\"button\">Cancel</button>\n </div>\n } @else {\n <h4 class=\"et-query-devtools-section\">Replay with edited args \u2192 execute()</h4>\n <textarea\n #argsArea\n [value]=\"argsDraft()\"\n (input)=\"argsDraft.set(argsArea.value)\"\n class=\"et-query-devtools-textarea\"\n spellcheck=\"false\"\n ></textarea>\n <div class=\"et-query-devtools-actions\">\n <button (click)=\"applyArgs(sel.query)\" class=\"et-query-devtools-btn\" type=\"button\">Execute</button>\n <button (click)=\"cancelEditor()\" class=\"et-query-devtools-btn\" type=\"button\">Cancel</button>\n </div>\n }\n @if (editError(); as err) {\n <p class=\"et-query-devtools-editor-error\">{{ err }}</p>\n }\n </div>\n }\n\n <dl class=\"et-query-devtools-meta\">\n <dt>Base URL</dt>\n <dd class=\"et-query-devtools-mono\">{{ sel.entry.meta.clientBaseUrl || sel.entry.meta.clientName || '\u2014' }}</dd>\n <dt>Status</dt>\n <dd>{{ queryStatus(sel.query) }}</dd>\n <dt>Cache key</dt>\n <dd class=\"et-query-devtools-mono\">{{ sel.query.id() || '\u2014' }}</dd>\n <dt>Last executed</dt>\n <dd>{{ formatTime(sel.query.lastTimeExecutedAt()) }}</dd>\n <dt>Triggered by</dt>\n <dd>{{ sel.query.triggeredBy() || 'user' }}</dd>\n @if (sel.entry.meta.featureTypes?.length) {\n <dt>Features</dt>\n <dd>\n <div class=\"et-query-devtools-chips\">\n @for (feature of sel.entry.meta.featureTypes; track feature) {\n <span class=\"et-query-devtools-chip\">{{ featureLabel(feature) }}</span>\n }\n </div>\n </dd>\n }\n </dl>\n\n @if (sel.entry.meta.gqlQuery; as gqlQuery) {\n <h5 class=\"et-query-devtools-section\">GraphQL document</h5>\n <pre class=\"et-query-devtools-code\">{{ gqlDocument(gqlQuery) }}</pre>\n }\n\n <div class=\"et-query-devtools-section-row\">\n <h4 class=\"et-query-devtools-section\">Value explorer</h4>\n <input\n #jsonSearchInput\n [value]=\"jsonSearch()\"\n (input)=\"jsonSearch.set(jsonSearchInput.value)\"\n class=\"et-query-devtools-search\"\n type=\"search\"\n placeholder=\"filter keys / values\u2026\"\n />\n </div>\n\n <h5 class=\"et-query-devtools-section\">Args</h5>\n <et-query-devtools-json\n [value]=\"sel.query.args()\"\n [search]=\"jsonSearchTerm()\"\n [expandedPaths]=\"jsonExpandedPaths()\"\n [collapsedPaths]=\"jsonCollapsedPaths()\"\n [toggleFn]=\"toggleJsonPath\"\n path=\"args\"\n />\n\n @if (sel.query.error(); as error) {\n <h5 class=\"et-query-devtools-section\">Error ({{ error.raw.status }})</h5>\n <et-query-devtools-json\n [value]=\"error.isList ? error.errors : error.error\"\n [search]=\"jsonSearchTerm()\"\n [expandedPaths]=\"jsonExpandedPaths()\"\n [collapsedPaths]=\"jsonCollapsedPaths()\"\n [toggleFn]=\"toggleJsonPath\"\n path=\"error\"\n />\n } @else {\n <h5 class=\"et-query-devtools-section\">Response</h5>\n <et-query-devtools-json\n [value]=\"sel.query.response()\"\n [search]=\"jsonSearchTerm()\"\n [expandedPaths]=\"jsonExpandedPaths()\"\n [collapsedPaths]=\"jsonCollapsedPaths()\"\n [toggleFn]=\"toggleJsonPath\"\n path=\"response\"\n />\n }\n </ng-template>\n </section>\n}\n\n@if (!open()) {\n <et-query-devtools-toggle (openChange)=\"toggleOpen()\" />\n}\n\n@if (inspectActive() && inspectHover(); as hover) {\n <div\n [style.left.px]=\"hover.rect.left\"\n [style.top.px]=\"hover.rect.top\"\n [style.width.px]=\"hover.rect.width\"\n [style.height.px]=\"hover.rect.height\"\n class=\"et-query-devtools-inspect-box\"\n >\n <span class=\"et-query-devtools-inspect-tag\">{{ inspectLabel(hover.entries) }}</span>\n </div>\n}\n", styles: ["@layer components{.et-query-devtools-host{--_et-qdt-bg: var(--et-surface-background-solid, rgb(24 24 27));--_et-qdt-fg: var(--et-surface-color-solid, rgb(244 244 245));--_et-qdt-muted: var(--et-surface-color-muted-solid, rgb(161 161 170));--_et-qdt-subtle: var(--et-surface-color-subtle-solid, rgb(113 113 122));--_et-qdt-border: var(--et-surface-border-solid, rgb(255 255 255 / .12));--_et-qdt-interaction: var(--et-surface-interaction-solid, rgb(255 255 255));--_et-qdt-accent: var(--et-theme-color-primary-solid, rgb(96 165 250));--_et-qdt-hover: color-mix(in srgb, var(--_et-qdt-interaction) 10%, transparent);--_et-qdt-active: color-mix(in srgb, var(--_et-qdt-interaction) 18%, transparent);--_et-qdt-success: rgb(34 197 94);--_et-qdt-error: rgb(239 68 68);--_et-qdt-loading: rgb(234 179 8);position:fixed;inset-inline:0;inset-block-end:0;z-index:2147483000;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,Liberation Mono,monospace;font-size:1.2rem;line-height:1.4;pointer-events:none}.et-query-devtools-host>*{pointer-events:auto}.et-query-devtools-panel{position:relative;display:flex;flex-direction:column;inline-size:100%;min-block-size:20rem;max-block-size:90vh;background:var(--_et-qdt-bg);color:var(--_et-qdt-fg);border-block-start:1px solid var(--_et-qdt-border);box-shadow:0 -.4rem 2rem #00000059}.et-query-devtools-panel--resizing{-webkit-user-select:none;user-select:none}.et-query-devtools-resize{position:absolute;inset-inline:0;inset-block-start:0;block-size:.6rem;cursor:ns-resize;z-index:1}.et-query-devtools-resize:after{content:\"\";position:absolute;inset-inline:50%;inset-block-start:.2rem;inline-size:4rem;margin-inline-start:-2rem;block-size:.2rem;border-radius:999px;background:var(--_et-qdt-border)}.et-query-devtools-resize:hover:after{background:var(--_et-qdt-accent)}.et-query-devtools-header{display:flex;align-items:center;justify-content:space-between;gap:.8rem;padding-inline:.8rem;border-block-end:1px solid var(--_et-qdt-border);flex:0 0 auto}.et-query-devtools-tabs{display:flex;flex-wrap:wrap;gap:.2rem}.et-query-devtools-tab{appearance:none;border:none;background:transparent;color:var(--_et-qdt-muted);padding:.9rem 1rem;cursor:pointer;border-block-end:2px solid transparent;font:inherit}.et-query-devtools-tab:hover{color:var(--_et-qdt-fg)}.et-query-devtools-tab--active{color:var(--_et-qdt-fg);border-block-end-color:var(--_et-qdt-accent)}.et-query-devtools-header-actions{display:flex;align-items:center;gap:.4rem;flex:0 0 auto}.et-query-devtools-inspect-btn{appearance:none;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:transparent;color:var(--_et-qdt-muted);cursor:pointer;padding:.4rem .8rem;font:inherit}.et-query-devtools-inspect-btn:hover{color:var(--_et-qdt-fg)}.et-query-devtools-inspect-btn--active{color:#09090b;background:var(--_et-qdt-accent);border-color:var(--_et-qdt-accent)}.et-query-devtools-close{appearance:none;border:none;background:transparent;color:var(--_et-qdt-muted);cursor:pointer;padding:.6rem;font:inherit}.et-query-devtools-inspect-box{position:fixed;z-index:2147483002;pointer-events:none;border:2px solid var(--_et-qdt-accent);background:color-mix(in srgb,var(--_et-qdt-accent) 15%,transparent);border-radius:.2rem}.et-query-devtools-inspect-tag{position:absolute;inset-block-start:100%;inset-inline-start:0;margin-block-start:.2rem;padding:.2rem .5rem;border-radius:.3rem;background:var(--_et-qdt-accent);color:#09090b;font-family:ui-monospace,monospace;font-size:1.1rem;font-weight:600;white-space:nowrap}.et-query-devtools-close:hover{color:var(--_et-qdt-fg)}.et-query-devtools-body{flex:1 1 auto;min-block-size:0;overflow:hidden}.et-query-devtools-master-detail{display:flex;block-size:100%;min-block-size:0}.et-query-devtools-list{flex:0 0 34%;min-inline-size:22rem;max-inline-size:40rem;overflow-y:auto;border-inline-end:1px solid var(--_et-qdt-border)}.et-query-devtools-detail{flex:1 1 auto;min-inline-size:0;overflow-y:auto;padding:1rem}.et-query-devtools-detail .et-query-devtools-detail-head,.et-query-devtools-drawer .et-query-devtools-detail-head{padding-block-end:.8rem;border-block-end:1px solid var(--_et-qdt-border)}.et-query-devtools-detail .et-query-devtools-meta,.et-query-devtools-drawer .et-query-devtools-meta{padding:.8rem;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:color-mix(in srgb,var(--_et-qdt-interaction) 3%,transparent)}.et-query-devtools-detail .et-query-devtools-section-row,.et-query-devtools-drawer .et-query-devtools-section-row{padding-block-start:.8rem;border-block-start:1px solid var(--_et-qdt-border)}.et-query-devtools-filter-chip{display:inline-flex;align-items:center;padding:.3rem .7rem;border-radius:999px;background:var(--_et-qdt-accent);color:#09090b;font-size:1.1rem;font-weight:600}.et-query-devtools-scroll{block-size:100%;overflow:auto;padding:1rem;display:flex;flex-direction:column;gap:1rem}.et-query-devtools-split{display:flex;block-size:100%;min-block-size:0}.et-query-devtools-split .et-query-devtools-scroll{flex:1 1 auto;min-inline-size:0}.et-query-devtools-drawer{flex:0 0 42%;min-inline-size:26rem;max-inline-size:48rem;overflow-y:auto;padding:0 1rem 1rem;border-inline-start:1px solid var(--_et-qdt-border)}.et-query-devtools-drawer-head{display:flex;align-items:center;justify-content:space-between;gap:.8rem;padding:.8rem 0;position:sticky;inset-block-start:0;background:var(--_et-qdt-bg);color:var(--_et-qdt-muted);border-block-end:1px solid var(--_et-qdt-border);margin-block-end:.8rem}.et-query-devtools-toolbar{display:flex;align-items:center;justify-content:space-between;gap:.8rem;padding:.8rem;position:sticky;inset-block-start:0;background:var(--_et-qdt-bg)}.et-query-devtools-field{display:inline-flex;align-items:center;gap:.6rem;color:var(--_et-qdt-muted)}.et-query-devtools-select{background:var(--_et-qdt-bg);color:var(--_et-qdt-fg);border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.4rem .6rem;font:inherit}.et-query-devtools-count{color:var(--_et-qdt-subtle)}.et-query-devtools-row{display:flex;align-items:center;gap:.8rem;inline-size:100%;text-align:start;appearance:none;border:none;border-block-end:1px solid var(--_et-qdt-border);background:transparent;color:var(--_et-qdt-fg);padding:.8rem;cursor:pointer;font:inherit}.et-query-devtools-row:hover{background:var(--_et-qdt-hover)}.et-query-devtools-row--active{background:var(--_et-qdt-active)}.et-query-devtools-method{font-weight:700;font-size:1.1rem;color:var(--_et-qdt-muted);white-space:nowrap}.et-query-devtools-route{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1 1 auto;min-inline-size:0}.et-query-devtools-status{inline-size:.8rem;block-size:.8rem;border-radius:999px;flex:0 0 auto;background:var(--_et-qdt-subtle)}.et-query-devtools-status[data-status=success]{background:var(--_et-qdt-success)}.et-query-devtools-status[data-status=error]{background:var(--_et-qdt-error)}.et-query-devtools-status[data-status=loading],.et-query-devtools-status[data-status=running]{background:var(--_et-qdt-loading)}.et-query-devtools-detail-head{display:flex;align-items:center;gap:.8rem;margin-block-end:.8rem}.et-query-devtools-actions{display:flex;flex-wrap:wrap;align-items:center;gap:.35rem;margin-block-end:.8rem}.et-query-devtools-btn{appearance:none;border:1px solid var(--_et-qdt-border);border-radius:.3rem;background:transparent;color:var(--_et-qdt-fg);padding:.25rem .55rem;font-size:1.15rem;line-height:1.4;cursor:pointer}.et-query-devtools-btn:hover{background:var(--_et-qdt-hover)}.et-query-devtools-meta{display:grid;grid-template-columns:auto 1fr;gap:.3rem 1rem;margin:0 0 1rem}.et-query-devtools-meta dt{color:var(--_et-qdt-muted)}.et-query-devtools-meta dd{margin:0;overflow-wrap:anywhere}.et-query-devtools-section{margin:1rem 0 .4rem;font-size:1.1rem;text-transform:uppercase;letter-spacing:.05em;color:var(--_et-qdt-muted)}.et-query-devtools-json{margin:0;padding:.8rem;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:color-mix(in srgb,var(--_et-qdt-interaction) 4%,transparent);overflow-x:auto;white-space:pre-wrap;overflow-wrap:anywhere;max-block-size:24rem;overflow-y:auto}.et-query-devtools-json--error{border-color:color-mix(in srgb,var(--_et-qdt-error) 50%,transparent)}.et-query-devtools-mono{font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.et-query-devtools-code{margin:0 0 .8rem;padding:.8rem;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:color-mix(in srgb,var(--_et-qdt-interaction) 4%,transparent);overflow:auto;white-space:pre;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:1.15rem;max-block-size:20rem}.et-query-devtools-ws-message{border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.6rem;margin-block-end:.4rem}.et-query-devtools-url{display:inline-block;max-inline-size:32rem;margin-inline-start:.6rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;vertical-align:bottom;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.et-query-devtools-chips,.et-query-devtools-chip-row{display:flex;flex-wrap:wrap;gap:.4rem;margin-block-end:.8rem}.et-query-devtools-chip{display:inline-flex;align-items:center;gap:.3rem;padding:.2rem .6rem;border-radius:999px;border:1px solid var(--_et-qdt-border);font-size:1.1rem;color:var(--_et-qdt-fg);background:color-mix(in srgb,var(--_et-qdt-interaction) 6%,transparent)}.et-query-devtools-chip--muted{color:var(--_et-qdt-muted)}.et-query-devtools-chip--loading{color:var(--_et-qdt-loading);border-color:color-mix(in srgb,var(--_et-qdt-loading) 50%,transparent)}.et-query-devtools-chip--error{color:var(--_et-qdt-error);border-color:color-mix(in srgb,var(--_et-qdt-error) 50%,transparent)}.et-query-devtools-chip--success{color:var(--_et-qdt-success);border-color:color-mix(in srgb,var(--_et-qdt-success) 50%,transparent)}.et-query-devtools-card{border:1px solid var(--_et-qdt-border);border-radius:.6rem;padding:1rem}.et-query-devtools-card-head{display:flex;align-items:center;gap:.6rem;margin-block-end:.8rem}.et-query-devtools-card-title{display:flex;align-items:baseline;gap:.5rem;margin-block-end:.2rem}.et-query-devtools-card-sub{color:var(--_et-qdt-subtle);font-size:1.1rem;margin-block-end:.6rem;overflow-wrap:anywhere}.et-query-devtools-inner-queries{display:flex;flex-direction:column;gap:.3rem}.et-query-devtools-inner-row{display:flex;align-items:center;gap:.6rem;inline-size:100%;text-align:start;appearance:none;border:1px solid var(--_et-qdt-border);border-radius:.4rem;background:transparent;color:var(--_et-qdt-fg);padding:.4rem .6rem;cursor:pointer;font:inherit}.et-query-devtools-inner-row:hover:not([disabled]){background:var(--_et-qdt-hover)}.et-query-devtools-inner-row--active{background:var(--_et-qdt-active);border-color:var(--_et-qdt-accent)}.et-query-devtools-inner-row[disabled]{cursor:default;opacity:.6}.et-query-devtools-step-list{display:flex;flex-direction:column;gap:.4rem;margin:0;padding:0;list-style:none}.et-query-devtools-step-item{border:1px solid var(--_et-qdt-border);border-radius:.4rem;overflow:hidden}.et-query-devtools-step-item[data-status=error]{border-color:color-mix(in srgb,var(--_et-qdt-error) 50%,transparent)}.et-query-devtools-step-head{display:flex;align-items:stretch}.et-query-devtools-step-head .et-query-devtools-inner-row{border:none;border-radius:0;flex:1 1 auto}.et-query-devtools-step-caret-btn{appearance:none;border:none;border-inline-start:1px solid var(--_et-qdt-border);background:transparent;color:var(--_et-qdt-muted);cursor:pointer;padding:0 .7rem;font:inherit}.et-query-devtools-step-caret-btn:hover{background:var(--_et-qdt-hover);color:var(--_et-qdt-fg)}.et-query-devtools-step-toggle{display:flex;align-items:center;gap:.6rem;inline-size:100%;text-align:start;appearance:none;border:none;background:transparent;color:var(--_et-qdt-fg);padding:.6rem;cursor:pointer;font:inherit}.et-query-devtools-step-toggle:hover{background:var(--_et-qdt-hover)}.et-query-devtools-step-index{flex:1 1 auto;min-inline-size:0}.et-query-devtools-step-caret{color:var(--_et-qdt-muted)}.et-query-devtools-step-io{padding:0 .8rem .8rem;border-block-start:1px solid var(--_et-qdt-border)}.et-query-devtools-table{inline-size:100%;border-collapse:collapse}.et-query-devtools-table th,.et-query-devtools-table td{text-align:start;padding:.5rem .6rem;border-block-end:1px solid var(--_et-qdt-border);vertical-align:top}.et-query-devtools-table th{color:var(--_et-qdt-muted);font-weight:600;position:sticky;inset-block-start:0;background:var(--_et-qdt-bg)}.et-query-devtools-table tr[data-status=error] td{color:var(--_et-qdt-error)}.et-query-devtools-empty{color:var(--_et-qdt-subtle);padding:1.2rem;text-align:center}.et-query-devtools-actions-label{color:var(--_et-qdt-subtle);align-self:center;text-transform:uppercase;font-size:1rem;letter-spacing:.05em}.et-query-devtools-actions-label:not(:first-child){margin-inline-start:.7rem}.et-query-devtools-cell-actions{display:flex;gap:.4rem;white-space:nowrap}.et-query-devtools-section-row{display:flex;align-items:center;justify-content:space-between;gap:.8rem;margin-block-start:1rem}.et-query-devtools-search{flex:0 1 20rem;background:var(--_et-qdt-bg);color:var(--_et-qdt-fg);border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.4rem .6rem;font:inherit}.et-query-devtools-editor{border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.8rem;margin-block-end:1rem}.et-query-devtools-textarea{inline-size:100%;min-block-size:12rem;resize:vertical;background:color-mix(in srgb,var(--_et-qdt-interaction) 4%,transparent);color:var(--_et-qdt-fg);border:1px solid var(--_et-qdt-border);border-radius:.4rem;padding:.6rem;font-family:inherit;font-size:1.2rem}.et-query-devtools-editor-error{color:var(--_et-qdt-error);margin:.4rem 0 0}.et-query-devtools-json-node,.et-query-devtools-json-children{display:flex;flex-direction:column}.et-query-devtools-json-children{padding-inline-start:1.4rem;border-inline-start:1px solid var(--_et-qdt-border);margin-inline-start:.4rem}.et-query-devtools-json-row{display:flex;align-items:baseline;gap:.4rem;padding:.1rem 0;font-family:inherit;line-height:1.5}.et-query-devtools-json-toggle{appearance:none;border:none;background:transparent;color:var(--_et-qdt-fg);cursor:pointer;text-align:start;font:inherit;padding:.1rem 0}.et-query-devtools-json-toggle:hover{background:var(--_et-qdt-hover)}.et-query-devtools-json-caret{color:var(--_et-qdt-subtle);inline-size:1rem;flex:0 0 auto}.et-query-devtools-json-key{color:var(--_et-qdt-accent)}.et-query-devtools-json-colon,.et-query-devtools-json-preview{color:var(--_et-qdt-subtle)}.et-query-devtools-json-value{overflow-wrap:anywhere}.et-query-devtools-json-leaf[data-kind=string] .et-query-devtools-json-value{color:var(--_et-qdt-success)}.et-query-devtools-json-leaf[data-kind=number] .et-query-devtools-json-value,.et-query-devtools-json-leaf[data-kind=boolean] .et-query-devtools-json-value{color:var(--_et-qdt-loading)}.et-query-devtools-json-leaf[data-kind=null] .et-query-devtools-json-value,.et-query-devtools-json-leaf[data-kind=undefined] .et-query-devtools-json-value{color:var(--_et-qdt-subtle)}.et-query-devtools-json-hit{background:color-mix(in srgb,var(--_et-qdt-loading) 40%,transparent);border-radius:.2rem}.et-query-devtools-json-copy{appearance:none;border:none;background:transparent;color:var(--_et-qdt-subtle);cursor:pointer;opacity:0;font:inherit}.et-query-devtools-json-row:hover .et-query-devtools-json-copy{opacity:1}}\n"] }]
30921
+ }], ctorParameters: () => [] });
30922
+
30923
+ const QUERY_DEVTOOLS_IMPORTS = [QueryDevtoolsComponent];
30924
+
29887
30925
  // codes 2100-2199
29888
30926
  const SCROLLABLE_ERROR_CODES = {
29889
30927
  MISSING_SCROLL_CONTAINER: 2100,
@@ -36704,5 +37742,5 @@ const TOOLTIP_IMPORTS = [TooltipDirective, TooltipComponent];
36704
37742
  * Generated bundle index. Do not edit.
36705
37743
  */
36706
37744
 
36707
- export { ALIGN_CENTER_ICON, ALIGN_JUSTIFY_ICON, ALIGN_LEFT_ICON, ALIGN_RIGHT_ICON, ARROW_OUT_UP_RIGHT_ICON, ARROW_RIGHT_ICON, BOLD_ICON, BUTTON_ICON_ALIGNMENTS, BUTTON_IMPORTS, BUTTON_SIZES, BUTTON_SPINNER_CONFIG, BUTTON_TYPES, BUTTON_VARIANTS, BrandLoaderComponent, ButtonComponent, ButtonDirective, ButtonStylesDirective, CALENDAR_ERROR_CODES, CALENDAR_ICON, CALENDAR_IMPORTS, CASCADER_ERROR_CODES, CASCADER_IMPORTS, CASCADER_SELECTABLE_LEVELS, CHECKBOX_IMPORTS, CHECK_ICON, CHEVRON_ICON, CHIP_ERROR_CODES, CHIP_IMPORTS, CHOICE_FIELD_IMPORTS, CLIPBOARD_CHECK_ICON, CLOCK_ICON, CODE_ICON, COLOR_INPUT_IMPORTS, CalendarCellDirective, CalendarComponent, CalendarDirective, CalendarGridDirective, CascaderColumnDirective, CascaderComponent, CascaderDirective, CascaderNodeDirective, CascaderPanelComponent, CascaderSearchDirective, CascaderSearchOptionDirective, CascaderSurfaceDirective, CascaderTriggerDirective, CheckboxComponent, CheckboxDirective, CheckboxGroupComponent, CheckboxOptionComponent, ChipComponent, ChipDirective, ChipRemoveDirective, ChoiceFieldComponent, ColorInputComponent, ColorInputDirective, DAILYMOTION_PLAYER_TOKEN, DATE_FORMAT, DATE_INPUT_ERROR_CODES, DATE_INPUT_IMPORTS, DATE_LOCALE, DATE_PICKER_HOST, DATE_RANGE_INPUT_ERROR_CODES, DATE_RANGE_INPUT_IMPORTS, DATE_TIME_INPUT_ERROR_CODES, DATE_TIME_INPUT_IMPORTS, DEFAULT_BREAKPOINTS, DEFAULT_ICON_VARIANT, DEFAULT_NOTIFICATION_MANAGER_CONFIG, DEFAULT_PIP_WINDOW_CONFIG, DEFAULT_RICH_TEXT_EDITOR_TOOLS, DEFAULT_STREAM_PLAYER_STATE, DROPZONE_ENTRY_STATUSES, DROPZONE_ERROR_CODES, DROPZONE_FILES_ERROR_KIND, DROPZONE_FILE_CONSTRAINTS, DROPZONE_FILE_REJECTION_REASONS, DROPZONE_IMPORTS, DURATION_INPUT_ERROR_CODES, DURATION_INPUT_IMPORTS, DailymotionPlayerComponent, DailymotionPlayerDirective, DailymotionPlayerParamsDirective, DailymotionPlayerSlotComponent, DateInputComponent, DateInputDirective, DateInputFieldDirective, DatePickerPanelComponent, DatePickerSurfaceDirective, DatePickerTriggerDirective, DateRangeInputComponent, DateRangeInputDirective, DateRangeInputFieldDirective, DateTimeInputComponent, DateTimeInputDirective, DateTimeInputFieldDirective, DescriptionComponent, DropzoneComponent, DropzoneDirective, DurationInputComponent, DurationInputDirective, DurationInputFieldDirective, ET_BUILT_IN_ICON_NAMES, EYE_ICON, EYE_SLASH_ICON, FACEBOOK_PLAYER_TOKEN, FILE_ICON, FLOPPY_DISK_ICON, FOCUS_FRAME_ICON, FORM_ERROR_MESSAGE_RESOLVER, FORM_FIELD_APPEARANCES, FORM_FIELD_CONTROL_TYPES, FORM_FIELD_ERROR_CODES, FORM_FIELD_FILLS, FORM_FIELD_IMPORTS, FORM_FIELD_LABEL_MODES, FORM_FIELD_SIZES, FORM_FIELD_TOKEN, FabComponent, FacebookPlayerComponent, FacebookPlayerDirective, FacebookPlayerParamsDirective, FacebookPlayerSlotComponent, FocusRingDirective, FormErrorComponent, FormFieldComponent, FormFieldDirective, GRID_2X2_ICON, GRID_ERROR_CODES, GRID_IMPORTS, GRID_TOKEN, GridComponent, GridDebugComponent, GridDirective, GridDragDirective, GridItemComponent, GridItemDefaultActionsComponent, GridItemDirective, GridItemRef, GridItemToolbarComponent, GridResizeDirective, HEADING_1_ICON, HEADING_2_ICON, HEADING_3_ICON, HintComponent, ICONS_TOKEN, ICON_DIRECTIVE_TOKEN, ICON_ERROR_CODES, ICON_IMPORTS, ICON_OVERRIDES_TOKEN, INITIAL_SUPPORT_PRESENTATION_STATE, INPUT_IMPORTS, INPUT_MASK_HOST, INPUT_TEXT_ALIGNMENTS, INPUT_TYPES, INTERACTIVE_TAGS, ITALIC_ICON, IconButtonComponent, IconDirective, InputComponent, InputDirective, InputMaskDirective, InputPrefixDirective, InputSuffixDirective, KICK_PLAYER_TOKEN, KickPlayerComponent, KickPlayerDirective, KickPlayerParamsDirective, KickPlayerSlotComponent, LINK_ICON, LIST_BULLETED_ICON, LIST_NUMBERED_ICON, LOCK_ICON, LabelDirective, MASKED_INPUT_ERROR_CODES, MASKED_INPUT_IMPORTS, MASK_VALUE_MODES, MENU_ERROR_CODES, MENU_IMPORTS, MENU_ITEM_VARIANTS, MENU_SELECTION_GROUP_MULTIPLE, MENU_SELECTION_GROUP_TOKEN, MENU_SELECTION_ITEM_KIND, MINUS_ICON, MULTI_LANGUAGE_RICH_TEXT_EDITOR_ERROR_CODES, MULTI_LANGUAGE_RICH_TEXT_EDITOR_IMPORTS, MenuCheckboxGroupComponent, MenuCheckboxItemComponent, MenuComponent, MenuContextTriggerDirective, MenuDirective, MenuGroupLabelComponent, MenuItemComponent, MenuItemDirective, MenuItemShortcutComponent, MenuPanelDirective, MenuRadioGroupComponent, MenuRadioItemComponent, MenuSearchDirective, MenuSelectionGroupDirective, MenuSelectionItemDirective, MenuSeparatorComponent, MenuSurfaceDirective, MenuTriggerDirective, MultiLanguageRichTextEditorComponent, MultiLanguageRichTextEditorDirective, MultiLanguageRichTextEditorLanguageToolComponent, NAV_TABS_TOKEN, NAV_TAB_IMPORTS, NOTIFICATION_ERROR_CODES, NOTIFICATION_IMPORTS, NOTIFICATION_STACK_CONTEXT_TOKEN, NOTIFICATION_STATUS, NUMBER_INPUT_IMPORTS, NavTabLinkComponent, NavTabLinkDirective, NavTabsComponent, NavTabsDirective, NavTabsOutletComponent, NavTabsOutletDirective, NotificationActionDirective, NotificationComponent, NotificationDirective, NotificationDismissDirective, NotificationItemDirective, NotificationStackDirective, NumberInputComponent, NumberInputDirective, OTP_INPUT_IMPORTS, OVERLAY_BACK_OR_CLOSE_TOKEN, OVERLAY_BODY_TOKEN, OVERLAY_CONFIG_CLASS_KEYS, OVERLAY_CONTENT_IMPORTS, OVERLAY_ERROR_CODES, OVERLAY_FOOTER_TOKEN, OVERLAY_HEADER_TEMPLATE_TOKEN, OVERLAY_HEADER_TOKEN, OVERLAY_IMPORTS, OVERLAY_MAIN_TOKEN, OVERLAY_QUERY_PARAM_INPUT_NAME, OVERLAY_REF, OVERLAY_ROUTER_CONFIG_TOKEN, OVERLAY_ROUTER_LINK_TOKEN, OVERLAY_ROUTER_OUTLET_DISABLED_TEMPLATE_TOKEN, OVERLAY_ROUTER_OUTLET_TOKEN, OVERLAY_ROUTER_TOKEN, OVERLAY_SHARED_ROUTE_TEMPLATE_TOKEN, OtpInputComponent, OtpInputDirective, OverlayAnchorDirective, OverlayBackOrCloseDirective, OverlayBodyComponent, OverlayCloseDirective, OverlayContainerComponent, OverlayDirective, OverlayFooterDirective, OverlayHeaderDirective, OverlayHeaderTemplateDirective, OverlayMainDirective, OverlayOriginCloneComponent, OverlayRouteHeaderTemplateOutletComponent, OverlayRouterLinkDirective, OverlayRouterOutletComponent, OverlayRouterOutletDisabledTemplateDirective, OverlaySharedRouteTemplateDirective, OverlaySharedRouteTemplateOutletComponent, OverlaySidebarComponent, OverlaySidebarPageComponent, OverlaySurfaceDirective, OverlayTitleDirective, OverlayTriggerDirective, PARAGRAPH_ICON, PASSWORD_INPUT_IMPORTS, PENCIL_ICON, PHONE_COUNTRIES, PHONE_INPUT_ERROR_CODES, PHONE_INPUT_IMPORTS, PIP_CHROME_REF_TOKEN, PIP_ENTRY_TOKEN, PIP_WINDOW_ASPECT_RATIO_TOKEN, PLUS_ICON, PasswordInputComponent, PasswordInputDirective, PhoneInputComponent, PhoneInputDirective, PhoneInputFieldDirective, PhoneInputFlagDirective, PipBackDirective, PipBringBackDirective, PipCellDirective, PipCloseDirective, PipCollapseOverlayDirective, PipGridToggleDirective, PipPlayerComponent, PipSlotPlaceholderComponent, PipStageDirective, PipTitleBarDirective, PipTitleBarTemplateDirective, PipWindowComponent, PipWindowParamsDirective, ProgressBarComponent, QueryParamOverlayLinkDirective, RATING_IMPORTS, RICH_TEXT_EDITOR_HEADING_OPTIONS, RICH_TEXT_EDITOR_IMPORTS, RICH_TEXT_EDITOR_INLINE_TOOLS, RICH_TEXT_EDITOR_LANGUAGE_TOOL, RICH_TEXT_EDITOR_TOKEN_CODEC, RICH_TEXT_EDITOR_TOKEN_PALETTE_IMPORTS, RICH_TEXT_EDITOR_TOOL, RICH_TEXT_EDITOR_TOOLS, RICH_TEXT_EDITOR_TOOL_BUTTONS, RICH_TEXT_EDITOR_TRIGGERS_IMPORTS, ROTATE_RIGHT_ICON, RadioComponent, RadioGroupComponent, RangeSliderComponent, RangeSliderDirective, RatingComponent, RatingDirective, RatingIconDirective, RichTextEditorAlignToolComponent, RichTextEditorComponent, RichTextEditorDirective, RichTextEditorFloatingToolbarComponent, RichTextEditorFloatingToolbarDirective, RichTextEditorLinkEditorComponent, RichTextEditorLinkEditorDirective, RichTextEditorTableToolComponent, RichTextEditorTokenPaletteComponent, RichTextEditorTokenPopupComponent, RichTextEditorTriggersDirective, SCROLLABLE_ERROR_CODES, SCROLLABLE_IMPORTS, SELECTION_LIST_MULTIPLE, SELECTION_LIST_TOKEN, SELECT_ERROR_CODES, SELECT_FILTER_MODES, SELECT_IMPORTS, SIDEBAR_OVERLAY_CONFIG, SIDEBAR_OVERLAY_TOKEN, SLIDER_ERROR_CODES, SLIDER_IMPORTS, SLIDER_TOKEN, SNAP_HYSTERESIS, SOOP_PLAYER_TOKEN, SPLIT_BUTTON_ERROR_CODES, STAR_ICON, STREAM_CONSENT_TOKEN, STREAM_IMPORTS, STREAM_PLAYER_COMPONENT_TOKEN, STREAM_PLAYER_ERROR_CONTEXT_TOKEN, STREAM_PLAYER_ERROR_TOKEN, STREAM_PLAYER_PARAMS_TOKEN, STREAM_PLAYER_SLOT_TOKEN, STREAM_PLAYER_TOKEN, STREAM_SLOT_PLAYER_ID_TOKEN, STREAM_USER_CONSENT_PROVIDER_TOKEN, STRIKETHROUGH_ICON, SUPPORT_CONTENT_STATE, SUPPORT_TRANSITION_DIRECTION, SWITCH_IMPORTS, ScrollableActiveChildDirective, ScrollableButtonsDirective, ScrollableComponent, ScrollableDarkenDirective, ScrollableDirective, ScrollableDragDirective, ScrollableIgnoreChildDirective, ScrollableLoadingTemplateDirective, ScrollableMasksDirective, ScrollableNavigationDirective, ScrollableSnapDirective, SegmentedButtonComponent, SegmentedButtonGroupComponent, SelectComponent, SelectDirective, SelectEmptyDirective, SelectErrorDirective, SelectListboxDirective, SelectLoadingDirective, SelectOptionComponent, SelectOptionDirective, SelectOptionGroupComponent, SelectOptionGroupDirective, SelectOptionTemplateDirective, SelectOptionsDirective, SelectPanelComponent, SelectSearchDirective, SelectSurfaceDirective, SelectTriggerDirective, SelectValueDirective, SelectViewportDirective, SelectVirtualOptionComponent, SelectVirtualOptionDirective, SelectionListControlDirective, SelectionListDirective, SelectionOptionDirective, SliderComponent, SliderDirective, SliderThumbDirective, SliderThumbLabelDirective, SliderTrackDirective, SoopPlayerComponent, SoopPlayerDirective, SoopPlayerParamsDirective, SoopPlayerSlotComponent, SpinnerComponent, SplitButtonActionDirective, SplitButtonComponent, SplitButtonDirective, SplitButtonTriggerDirective, StreamConsentAcceptDirective, StreamConsentComponent, StreamConsentDirective, StreamPipChromeComponent, StreamPlayerErrorComponent, StreamPlayerErrorDirective, StreamPlayerLoadingComponent, StreamPlayerSlotDirective, SwitchComponent, SwitchDirective, TABLE_ICON, TAB_BAR_FITS, TAB_BAR_ORIENTATIONS, TAB_BAR_TOKEN, TAB_BAR_TRIGGER_TOKEN, TAB_BAR_VARIANTS, TAB_ERROR_CODES, TAB_GROUP_TOKEN, TAB_IMPORTS, TAB_PANEL_TOKEN, TAB_SIZES, TAG_INPUT_ERROR_CODES, TAG_INPUT_IMPORTS, TEXTAREA_IMPORTS, TEXTAREA_RESIZE_MODES, TEXT_ALIGNS, TIKTOK_PLAYER_TOKEN, TIMES_ICON, TIME_FORMAT, TIME_INPUT_ERROR_CODES, TIME_INPUT_IMPORTS, TIME_PICKER_ERROR_CODES, TIME_PICKER_IMPORTS, TOGGLETIP_ERROR_CODES, TOGGLETIP_IMPORTS, TOOLTIP_ERROR_CODES, TOOLTIP_IMPORTS, TRIANGLE_EXCLAMATION_ICON, TWITCH_PLAYER_TOKEN, TabBarDirective, TabBarTriggerDirective, TabBarUnderlineDirective, TabComponent, TabGroupComponent, TabGroupDirective, TabLabelDirective, TabPanelDirective, TabTriggerDirective, TagInputComponent, TagInputDirective, TagInputFieldDirective, TextButtonComponent, TextFieldControlDirective, TextareaComponent, TextareaDirective, TikTokPlayerComponent, TikTokPlayerDirective, TikTokPlayerParamsDirective, TikTokPlayerSlotComponent, TimeInputComponent, TimeInputDirective, TimeInputFieldDirective, TimePickerColumnDirective, TimePickerComponent, TimePickerDirective, TimePickerOptionDirective, ToggletipCloseDirective, ToggletipComponent, ToggletipDirective, ToggletipTriggerDirective, TooltipComponent, TooltipDirective, TwitchPlayerComponent, TwitchPlayerDirective, TwitchPlayerParamsDirective, TwitchPlayerSlotComponent, UNDERLINE_ICON, UNIT_MS, UPLOAD_ICON, VIMEO_PLAYER_TOKEN, VimeoPlayerComponent, VimeoPlayerDirective, VimeoPlayerParamsDirective, VimeoPlayerSlotComponent, WINDOW_CONTROL_BUTTON_KINDS, WINDOW_CONTROL_BUTTON_SIZES, WindowControlButtonComponent, YOUTUBE_PLAYER_SLOT_TOKEN, YOUTUBE_PLAYER_TOKEN, YoutubePlayerComponent, YoutubePlayerDirective, YoutubePlayerParamsDirective, YoutubePlayerSlotComponent, YoutubePlayerSlotDirective, abortFullscreenAnimation, anchoredDialogOverlayStrategy, anchoredOverlayStrategy, autoPlace, bottomSheetOverlayStrategy, buildAnchoredRuntimePositionStrategy, canHaveChildren, cascaderFromQuery, centeredOverlayStrategy, clampPosition, clampResizeRect, cleanupFullscreenAnimation, cleanupFullscreenAnimationStyles, compactLayout, computeGeometry, computeGridHeight, createAnchoredPanelController, createAutoScroller, createCardMask, createCurrencyMask, createDefaultDropzoneArgs, createDropzoneUpload, createExistingDropzoneEntry, createFileDropzoneEntry, createGridAdapter, createIbanMask, createNotificationRef, createOverlayOpener, createOverlayRef, createOverlayStrategyController, createOverlayUnsavedChangesGuard, createPipChromeAnimations, createPipChromeState, createRichTextEditorTrigger, createRichTextEditorTriggerWithQuery, createStreamConfig, createStreamPlayerSlot, createV2DropzoneUpload, defaultCompareWith, defaultDropzoneRejectionMessage, defineOverlay, defineQueryParamOverlay, deriveDurationFormatSpec, deserializeGridLayout, dialogOverlayStrategy, disposeDropzoneEntry, dropzoneFiles, enableDragToDismiss, findCollision, findNextRelevantHtmlElement, findScrollableAncestor, formatDuration, formatFileSize, fromGridPosition, fullScreenDialogOverlayStrategy, getClosestOverlay, getOriginCoordinatesAndDimensions, hysteresisRound, iconRegistryKey, indexOfNode, injectAnchoredDialogStrategy, injectAnchoredDialogStrategyDefaults, injectBottomSheetStrategy, injectBottomSheetStrategyDefaults, injectDateFormat, injectDateLocale, injectDialogStrategy, injectDialogStrategyDefaults, injectFormSupport, injectFullscreenDialogStrategy, injectFullscreenDialogStrategyDefaults, injectGridConfig, injectLeftSheetStrategy, injectLeftSheetStrategyDefaults, injectNotificationManager, injectNotificationManagerConfig, injectOverlayManager, injectOverlayRouter, injectOverlayScrollBlocker, injectOverlaySurfaceContext, injectPipChromeManager, injectPipManager, injectPipSlotPlaceholderConfig, injectRichTextEditorTools, injectRightSheetStrategy, injectRightSheetStrategyDefaults, injectSidebarOverlay, injectStreamConfig, injectStreamConsentConfig, injectStreamManager, injectStreamPlayerErrorConfig, injectStreamPlayerLoadingConfig, injectStreamScriptLoader, injectStreamUserConsentProvider, injectTimeFormat, injectTopSheetStrategy, injectTopSheetStrategyDefaults, isFileAccepted, isHtmlElement, isInteractiveElement, isPointerEvent, isTargetInsideOverlayTree, isTouchEvent, itemsCollide, leftSheetOverlayStrategy, mapLayoutToBreakpoint, matchCountryByDialCode, mergeOverlayBreakpointConfigs, mergeOverlayConfigs, nodesEqual, parseDuration, phoneCountryFlag, phoneCountryName, pixelRectsEqual, positionToPixelRect, positionsEqual, projectDragCell, provideAnchoredDialogStrategy, provideAnchoredDialogStrategyDefaults, provideBottomSheetStrategy, provideBottomSheetStrategyDefaults, provideDateFormat, provideDateLocale, provideDialogStrategy, provideDialogStrategyDefaults, provideFormErrorMessageResolver, provideFormSupport, provideFullscreenDialogStrategy, provideFullscreenDialogStrategyDefaults, provideGridConfig, provideIconOverrides, provideIcons, provideLeftSheetStrategy, provideLeftSheetStrategyDefaults, provideNotificationManager, provideNotificationManagerConfig, provideNotificationManagerInstance, provideOverlay, provideOverlayManager, provideOverlayRouter, provideOverlayRouterConfig, provideOverlayRouterService, provideOverlayScrollBlocker, providePipChromeManager, providePipManager, providePipSlotPlaceholderConfig, provideRichTextEditorAlignmentTool, provideRichTextEditorLanguageTool, provideRichTextEditorTableTool, provideRichTextEditorTokenRendering, provideRichTextEditorTools, provideRightSheetStrategy, provideRightSheetStrategyDefaults, provideSidebarOverlay, provideSidebarOverlayConfig, provideSidebarOverlayService, provideStreamConfig, provideStreamConsentConfig, provideStreamManager, provideStreamPlayerErrorConfig, provideStreamPlayerLoadingConfig, provideTimeFormat, provideTopSheetStrategy, provideTopSheetStrategyDefaults, reduceSupportPresentation, registerSingleton, requiredLanguages, resizeSpanBounds, resolveBreakpoint, resolveClosestOverlay, resolveCollisions, rightSheetOverlayStrategy, rowsToPixelHeight, selectOptionsFromQuery, selectOptionsFromV2Query, serializeGridLayout, snapResizeSpan, spanHeight, spanWidth, startFullscreenEnterAnimation, startFullscreenLeaveAnimation, stripTrunkZero, supportPresentationIncludesState, toChildrenObservable, toGridPosition, toPathObservable, toSearchObservable, topSheetOverlayStrategy, transformingBottomSheetToDialogOverlayStrategy, transformingFullScreenDialogToDialogOverlayStrategy, transformingFullScreenDialogToRightSheetOverlayStrategy, wireFormSupport };
37745
+ export { ALIGN_CENTER_ICON, ALIGN_JUSTIFY_ICON, ALIGN_LEFT_ICON, ALIGN_RIGHT_ICON, ARROW_OUT_UP_RIGHT_ICON, ARROW_RIGHT_ICON, BOLD_ICON, BUTTON_ICON_ALIGNMENTS, BUTTON_IMPORTS, BUTTON_SIZES, BUTTON_SPINNER_CONFIG, BUTTON_TYPES, BUTTON_VARIANTS, BrandLoaderComponent, ButtonComponent, ButtonDirective, ButtonStylesDirective, CALENDAR_ERROR_CODES, CALENDAR_ICON, CALENDAR_IMPORTS, CASCADER_ERROR_CODES, CASCADER_IMPORTS, CASCADER_SELECTABLE_LEVELS, CHECKBOX_IMPORTS, CHECK_ICON, CHEVRON_ICON, CHIP_ERROR_CODES, CHIP_IMPORTS, CHOICE_FIELD_IMPORTS, CLIPBOARD_CHECK_ICON, CLOCK_ICON, CODE_ICON, COLOR_INPUT_IMPORTS, CalendarCellDirective, CalendarComponent, CalendarDirective, CalendarGridDirective, CascaderColumnDirective, CascaderComponent, CascaderDirective, CascaderNodeDirective, CascaderPanelComponent, CascaderSearchDirective, CascaderSearchOptionDirective, CascaderSurfaceDirective, CascaderTriggerDirective, CheckboxComponent, CheckboxDirective, CheckboxGroupComponent, CheckboxOptionComponent, ChipComponent, ChipDirective, ChipRemoveDirective, ChoiceFieldComponent, ColorInputComponent, ColorInputDirective, DAILYMOTION_PLAYER_TOKEN, DATE_FORMAT, DATE_INPUT_ERROR_CODES, DATE_INPUT_IMPORTS, DATE_LOCALE, DATE_PICKER_HOST, DATE_RANGE_INPUT_ERROR_CODES, DATE_RANGE_INPUT_IMPORTS, DATE_TIME_INPUT_ERROR_CODES, DATE_TIME_INPUT_IMPORTS, DEFAULT_BREAKPOINTS, DEFAULT_ICON_VARIANT, DEFAULT_NOTIFICATION_MANAGER_CONFIG, DEFAULT_PIP_WINDOW_CONFIG, DEFAULT_RICH_TEXT_EDITOR_TOOLS, DEFAULT_STREAM_PLAYER_STATE, DROPZONE_ENTRY_STATUSES, DROPZONE_ERROR_CODES, DROPZONE_FILES_ERROR_KIND, DROPZONE_FILE_CONSTRAINTS, DROPZONE_FILE_REJECTION_REASONS, DROPZONE_IMPORTS, DURATION_INPUT_ERROR_CODES, DURATION_INPUT_IMPORTS, DailymotionPlayerComponent, DailymotionPlayerDirective, DailymotionPlayerParamsDirective, DailymotionPlayerSlotComponent, DateInputComponent, DateInputDirective, DateInputFieldDirective, DatePickerPanelComponent, DatePickerSurfaceDirective, DatePickerTriggerDirective, DateRangeInputComponent, DateRangeInputDirective, DateRangeInputFieldDirective, DateTimeInputComponent, DateTimeInputDirective, DateTimeInputFieldDirective, DescriptionComponent, DropzoneComponent, DropzoneDirective, DurationInputComponent, DurationInputDirective, DurationInputFieldDirective, ET_BUILT_IN_ICON_NAMES, EYE_ICON, EYE_SLASH_ICON, FACEBOOK_PLAYER_TOKEN, FILE_ICON, FLOPPY_DISK_ICON, FOCUS_FRAME_ICON, FORM_ERROR_MESSAGE_RESOLVER, FORM_FIELD_APPEARANCES, FORM_FIELD_CONTROL_TYPES, FORM_FIELD_ERROR_CODES, FORM_FIELD_FILLS, FORM_FIELD_IMPORTS, FORM_FIELD_LABEL_MODES, FORM_FIELD_SIZES, FORM_FIELD_TOKEN, FabComponent, FacebookPlayerComponent, FacebookPlayerDirective, FacebookPlayerParamsDirective, FacebookPlayerSlotComponent, FocusRingDirective, FormErrorComponent, FormFieldComponent, FormFieldDirective, GRID_2X2_ICON, GRID_ERROR_CODES, GRID_IMPORTS, GRID_TOKEN, GridComponent, GridDebugComponent, GridDirective, GridDragDirective, GridItemComponent, GridItemDefaultActionsComponent, GridItemDirective, GridItemRef, GridItemToolbarComponent, GridResizeDirective, HEADING_1_ICON, HEADING_2_ICON, HEADING_3_ICON, HintComponent, ICONS_TOKEN, ICON_DIRECTIVE_TOKEN, ICON_ERROR_CODES, ICON_IMPORTS, ICON_OVERRIDES_TOKEN, INITIAL_SUPPORT_PRESENTATION_STATE, INPUT_IMPORTS, INPUT_MASK_HOST, INPUT_TEXT_ALIGNMENTS, INPUT_TYPES, INTERACTIVE_TAGS, ITALIC_ICON, IconButtonComponent, IconDirective, InputComponent, InputDirective, InputMaskDirective, InputPrefixDirective, InputSuffixDirective, KICK_PLAYER_TOKEN, KickPlayerComponent, KickPlayerDirective, KickPlayerParamsDirective, KickPlayerSlotComponent, LINK_ICON, LIST_BULLETED_ICON, LIST_NUMBERED_ICON, LOCK_ICON, LabelDirective, MASKED_INPUT_ERROR_CODES, MASKED_INPUT_IMPORTS, MASK_VALUE_MODES, MENU_ERROR_CODES, MENU_IMPORTS, MENU_ITEM_VARIANTS, MENU_SELECTION_GROUP_MULTIPLE, MENU_SELECTION_GROUP_TOKEN, MENU_SELECTION_ITEM_KIND, MINUS_ICON, MULTI_LANGUAGE_RICH_TEXT_EDITOR_ERROR_CODES, MULTI_LANGUAGE_RICH_TEXT_EDITOR_IMPORTS, MenuCheckboxGroupComponent, MenuCheckboxItemComponent, MenuComponent, MenuContextTriggerDirective, MenuDirective, MenuGroupLabelComponent, MenuItemComponent, MenuItemDirective, MenuItemShortcutComponent, MenuPanelDirective, MenuRadioGroupComponent, MenuRadioItemComponent, MenuSearchDirective, MenuSelectionGroupDirective, MenuSelectionItemDirective, MenuSeparatorComponent, MenuSurfaceDirective, MenuTriggerDirective, MultiLanguageRichTextEditorComponent, MultiLanguageRichTextEditorDirective, MultiLanguageRichTextEditorLanguageToolComponent, NAV_TABS_TOKEN, NAV_TAB_IMPORTS, NOTIFICATION_ERROR_CODES, NOTIFICATION_IMPORTS, NOTIFICATION_STACK_CONTEXT_TOKEN, NOTIFICATION_STATUS, NUMBER_INPUT_IMPORTS, NavTabLinkComponent, NavTabLinkDirective, NavTabsComponent, NavTabsDirective, NavTabsOutletComponent, NavTabsOutletDirective, NotificationActionDirective, NotificationComponent, NotificationDirective, NotificationDismissDirective, NotificationItemDirective, NotificationStackDirective, NumberInputComponent, NumberInputDirective, OTP_INPUT_IMPORTS, OVERLAY_BACK_OR_CLOSE_TOKEN, OVERLAY_BODY_TOKEN, OVERLAY_CONFIG_CLASS_KEYS, OVERLAY_CONTENT_IMPORTS, OVERLAY_ERROR_CODES, OVERLAY_FOOTER_TOKEN, OVERLAY_HEADER_TEMPLATE_TOKEN, OVERLAY_HEADER_TOKEN, OVERLAY_IMPORTS, OVERLAY_MAIN_TOKEN, OVERLAY_QUERY_PARAM_INPUT_NAME, OVERLAY_REF, OVERLAY_ROUTER_CONFIG_TOKEN, OVERLAY_ROUTER_LINK_TOKEN, OVERLAY_ROUTER_OUTLET_DISABLED_TEMPLATE_TOKEN, OVERLAY_ROUTER_OUTLET_TOKEN, OVERLAY_ROUTER_TOKEN, OVERLAY_SHARED_ROUTE_TEMPLATE_TOKEN, OtpInputComponent, OtpInputDirective, OverlayAnchorDirective, OverlayBackOrCloseDirective, OverlayBodyComponent, OverlayCloseDirective, OverlayContainerComponent, OverlayDirective, OverlayFooterDirective, OverlayHeaderDirective, OverlayHeaderTemplateDirective, OverlayMainDirective, OverlayOriginCloneComponent, OverlayRouteHeaderTemplateOutletComponent, OverlayRouterLinkDirective, OverlayRouterOutletComponent, OverlayRouterOutletDisabledTemplateDirective, OverlaySharedRouteTemplateDirective, OverlaySharedRouteTemplateOutletComponent, OverlaySidebarComponent, OverlaySidebarPageComponent, OverlaySurfaceDirective, OverlayTitleDirective, OverlayTriggerDirective, PARAGRAPH_ICON, PASSWORD_INPUT_IMPORTS, PENCIL_ICON, PHONE_COUNTRIES, PHONE_INPUT_ERROR_CODES, PHONE_INPUT_IMPORTS, PIP_CHROME_REF_TOKEN, PIP_ENTRY_TOKEN, PIP_WINDOW_ASPECT_RATIO_TOKEN, PLUS_ICON, PasswordInputComponent, PasswordInputDirective, PhoneInputComponent, PhoneInputDirective, PhoneInputFieldDirective, PhoneInputFlagDirective, PipBackDirective, PipBringBackDirective, PipCellDirective, PipCloseDirective, PipCollapseOverlayDirective, PipGridToggleDirective, PipPlayerComponent, PipSlotPlaceholderComponent, PipStageDirective, PipTitleBarDirective, PipTitleBarTemplateDirective, PipWindowComponent, PipWindowParamsDirective, ProgressBarComponent, QUERY_DEVTOOLS_IMPORTS, QueryDevtoolsComponent, QueryDevtoolsJsonComponent, QueryDevtoolsToggleComponent, QueryParamOverlayLinkDirective, RATING_IMPORTS, RICH_TEXT_EDITOR_HEADING_OPTIONS, RICH_TEXT_EDITOR_IMPORTS, RICH_TEXT_EDITOR_INLINE_TOOLS, RICH_TEXT_EDITOR_LANGUAGE_TOOL, RICH_TEXT_EDITOR_TOKEN_CODEC, RICH_TEXT_EDITOR_TOKEN_PALETTE_IMPORTS, RICH_TEXT_EDITOR_TOOL, RICH_TEXT_EDITOR_TOOLS, RICH_TEXT_EDITOR_TOOL_BUTTONS, RICH_TEXT_EDITOR_TRIGGERS_IMPORTS, ROTATE_RIGHT_ICON, RadioComponent, RadioGroupComponent, RangeSliderComponent, RangeSliderDirective, RatingComponent, RatingDirective, RatingIconDirective, RichTextEditorAlignToolComponent, RichTextEditorComponent, RichTextEditorDirective, RichTextEditorFloatingToolbarComponent, RichTextEditorFloatingToolbarDirective, RichTextEditorLinkEditorComponent, RichTextEditorLinkEditorDirective, RichTextEditorTableToolComponent, RichTextEditorTokenPaletteComponent, RichTextEditorTokenPopupComponent, RichTextEditorTriggersDirective, SCROLLABLE_ERROR_CODES, SCROLLABLE_IMPORTS, SELECTION_LIST_MULTIPLE, SELECTION_LIST_TOKEN, SELECT_ERROR_CODES, SELECT_FILTER_MODES, SELECT_IMPORTS, SIDEBAR_OVERLAY_CONFIG, SIDEBAR_OVERLAY_TOKEN, SLIDER_ERROR_CODES, SLIDER_IMPORTS, SLIDER_TOKEN, SNAP_HYSTERESIS, SOOP_PLAYER_TOKEN, SPLIT_BUTTON_ERROR_CODES, STAR_ICON, STREAM_CONSENT_TOKEN, STREAM_IMPORTS, STREAM_PLAYER_COMPONENT_TOKEN, STREAM_PLAYER_ERROR_CONTEXT_TOKEN, STREAM_PLAYER_ERROR_TOKEN, STREAM_PLAYER_PARAMS_TOKEN, STREAM_PLAYER_SLOT_TOKEN, STREAM_PLAYER_TOKEN, STREAM_SLOT_PLAYER_ID_TOKEN, STREAM_USER_CONSENT_PROVIDER_TOKEN, STRIKETHROUGH_ICON, SUPPORT_CONTENT_STATE, SUPPORT_TRANSITION_DIRECTION, SWITCH_IMPORTS, ScrollableActiveChildDirective, ScrollableButtonsDirective, ScrollableComponent, ScrollableDarkenDirective, ScrollableDirective, ScrollableDragDirective, ScrollableIgnoreChildDirective, ScrollableLoadingTemplateDirective, ScrollableMasksDirective, ScrollableNavigationDirective, ScrollableSnapDirective, SegmentedButtonComponent, SegmentedButtonGroupComponent, SelectComponent, SelectDirective, SelectEmptyDirective, SelectErrorDirective, SelectListboxDirective, SelectLoadingDirective, SelectOptionComponent, SelectOptionDirective, SelectOptionGroupComponent, SelectOptionGroupDirective, SelectOptionTemplateDirective, SelectOptionsDirective, SelectPanelComponent, SelectSearchDirective, SelectSurfaceDirective, SelectTriggerDirective, SelectValueDirective, SelectViewportDirective, SelectVirtualOptionComponent, SelectVirtualOptionDirective, SelectionListControlDirective, SelectionListDirective, SelectionOptionDirective, SliderComponent, SliderDirective, SliderThumbDirective, SliderThumbLabelDirective, SliderTrackDirective, SoopPlayerComponent, SoopPlayerDirective, SoopPlayerParamsDirective, SoopPlayerSlotComponent, SpinnerComponent, SplitButtonActionDirective, SplitButtonComponent, SplitButtonDirective, SplitButtonTriggerDirective, StreamConsentAcceptDirective, StreamConsentComponent, StreamConsentDirective, StreamPipChromeComponent, StreamPlayerErrorComponent, StreamPlayerErrorDirective, StreamPlayerLoadingComponent, StreamPlayerSlotDirective, SwitchComponent, SwitchDirective, TABLE_ICON, TAB_BAR_FITS, TAB_BAR_ORIENTATIONS, TAB_BAR_TOKEN, TAB_BAR_TRIGGER_TOKEN, TAB_BAR_VARIANTS, TAB_ERROR_CODES, TAB_GROUP_TOKEN, TAB_IMPORTS, TAB_PANEL_TOKEN, TAB_SIZES, TAG_INPUT_ERROR_CODES, TAG_INPUT_IMPORTS, TEXTAREA_IMPORTS, TEXTAREA_RESIZE_MODES, TEXT_ALIGNS, TIKTOK_PLAYER_TOKEN, TIMES_ICON, TIME_FORMAT, TIME_INPUT_ERROR_CODES, TIME_INPUT_IMPORTS, TIME_PICKER_ERROR_CODES, TIME_PICKER_IMPORTS, TOGGLETIP_ERROR_CODES, TOGGLETIP_IMPORTS, TOOLTIP_ERROR_CODES, TOOLTIP_IMPORTS, TRIANGLE_EXCLAMATION_ICON, TWITCH_PLAYER_TOKEN, TabBarDirective, TabBarTriggerDirective, TabBarUnderlineDirective, TabComponent, TabGroupComponent, TabGroupDirective, TabLabelDirective, TabPanelDirective, TabTriggerDirective, TagInputComponent, TagInputDirective, TagInputFieldDirective, TextButtonComponent, TextFieldControlDirective, TextareaComponent, TextareaDirective, TikTokPlayerComponent, TikTokPlayerDirective, TikTokPlayerParamsDirective, TikTokPlayerSlotComponent, TimeInputComponent, TimeInputDirective, TimeInputFieldDirective, TimePickerColumnDirective, TimePickerComponent, TimePickerDirective, TimePickerOptionDirective, ToggletipCloseDirective, ToggletipComponent, ToggletipDirective, ToggletipTriggerDirective, TooltipComponent, TooltipDirective, TwitchPlayerComponent, TwitchPlayerDirective, TwitchPlayerParamsDirective, TwitchPlayerSlotComponent, UNDERLINE_ICON, UNIT_MS, UPLOAD_ICON, VIMEO_PLAYER_TOKEN, VimeoPlayerComponent, VimeoPlayerDirective, VimeoPlayerParamsDirective, VimeoPlayerSlotComponent, WINDOW_CONTROL_BUTTON_KINDS, WINDOW_CONTROL_BUTTON_SIZES, WindowControlButtonComponent, YOUTUBE_PLAYER_SLOT_TOKEN, YOUTUBE_PLAYER_TOKEN, YoutubePlayerComponent, YoutubePlayerDirective, YoutubePlayerParamsDirective, YoutubePlayerSlotComponent, YoutubePlayerSlotDirective, abortFullscreenAnimation, anchoredDialogOverlayStrategy, anchoredOverlayStrategy, autoPlace, bottomSheetOverlayStrategy, buildAnchoredRuntimePositionStrategy, canHaveChildren, cascaderFromQuery, centeredOverlayStrategy, clampPosition, clampResizeRect, cleanupFullscreenAnimation, cleanupFullscreenAnimationStyles, compactLayout, computeGeometry, computeGridHeight, createAnchoredPanelController, createAutoScroller, createCardMask, createCurrencyMask, createDefaultDropzoneArgs, createDropzoneUpload, createExistingDropzoneEntry, createFileDropzoneEntry, createGridAdapter, createIbanMask, createNotificationRef, createOverlayOpener, createOverlayRef, createOverlayStrategyController, createOverlayUnsavedChangesGuard, createPipChromeAnimations, createPipChromeState, createRichTextEditorTrigger, createRichTextEditorTriggerWithQuery, createStreamConfig, createStreamPlayerSlot, createV2DropzoneUpload, defaultCompareWith, defaultDropzoneRejectionMessage, defineOverlay, defineQueryParamOverlay, deriveDurationFormatSpec, deserializeGridLayout, dialogOverlayStrategy, disposeDropzoneEntry, dropzoneFiles, enableDragToDismiss, findCollision, findNextRelevantHtmlElement, findScrollableAncestor, formatDuration, formatFileSize, fromGridPosition, fullScreenDialogOverlayStrategy, getClosestOverlay, getOriginCoordinatesAndDimensions, hysteresisRound, iconRegistryKey, indexOfNode, injectAnchoredDialogStrategy, injectAnchoredDialogStrategyDefaults, injectBottomSheetStrategy, injectBottomSheetStrategyDefaults, injectDateFormat, injectDateLocale, injectDialogStrategy, injectDialogStrategyDefaults, injectFormSupport, injectFullscreenDialogStrategy, injectFullscreenDialogStrategyDefaults, injectGridConfig, injectLeftSheetStrategy, injectLeftSheetStrategyDefaults, injectNotificationManager, injectNotificationManagerConfig, injectOverlayManager, injectOverlayRouter, injectOverlayScrollBlocker, injectOverlaySurfaceContext, injectPipChromeManager, injectPipManager, injectPipSlotPlaceholderConfig, injectRichTextEditorTools, injectRightSheetStrategy, injectRightSheetStrategyDefaults, injectSidebarOverlay, injectStreamConfig, injectStreamConsentConfig, injectStreamManager, injectStreamPlayerErrorConfig, injectStreamPlayerLoadingConfig, injectStreamScriptLoader, injectStreamUserConsentProvider, injectTimeFormat, injectTopSheetStrategy, injectTopSheetStrategyDefaults, isFileAccepted, isHtmlElement, isInteractiveElement, isPointerEvent, isTargetInsideOverlayTree, isTouchEvent, itemsCollide, leftSheetOverlayStrategy, mapLayoutToBreakpoint, matchCountryByDialCode, mergeOverlayBreakpointConfigs, mergeOverlayConfigs, nodesEqual, parseDuration, phoneCountryFlag, phoneCountryName, pixelRectsEqual, positionToPixelRect, positionsEqual, projectDragCell, provideAnchoredDialogStrategy, provideAnchoredDialogStrategyDefaults, provideBottomSheetStrategy, provideBottomSheetStrategyDefaults, provideDateFormat, provideDateLocale, provideDialogStrategy, provideDialogStrategyDefaults, provideFormErrorMessageResolver, provideFormSupport, provideFullscreenDialogStrategy, provideFullscreenDialogStrategyDefaults, provideGridConfig, provideIconOverrides, provideIcons, provideLeftSheetStrategy, provideLeftSheetStrategyDefaults, provideNotificationManager, provideNotificationManagerConfig, provideNotificationManagerInstance, provideOverlay, provideOverlayManager, provideOverlayRouter, provideOverlayRouterConfig, provideOverlayRouterService, provideOverlayScrollBlocker, providePipChromeManager, providePipManager, providePipSlotPlaceholderConfig, provideRichTextEditorAlignmentTool, provideRichTextEditorLanguageTool, provideRichTextEditorTableTool, provideRichTextEditorTokenRendering, provideRichTextEditorTools, provideRightSheetStrategy, provideRightSheetStrategyDefaults, provideSidebarOverlay, provideSidebarOverlayConfig, provideSidebarOverlayService, provideStreamConfig, provideStreamConsentConfig, provideStreamManager, provideStreamPlayerErrorConfig, provideStreamPlayerLoadingConfig, provideTimeFormat, provideTopSheetStrategy, provideTopSheetStrategyDefaults, reduceSupportPresentation, registerSingleton, requiredLanguages, resizeSpanBounds, resolveBreakpoint, resolveClosestOverlay, resolveCollisions, rightSheetOverlayStrategy, rowsToPixelHeight, selectOptionsFromQuery, selectOptionsFromV2Query, serializeGridLayout, snapResizeSpan, spanHeight, spanWidth, startFullscreenEnterAnimation, startFullscreenLeaveAnimation, stripTrunkZero, supportPresentationIncludesState, toChildrenObservable, toGridPosition, toPathObservable, toSearchObservable, topSheetOverlayStrategy, transformingBottomSheetToDialogOverlayStrategy, transformingFullScreenDialogToDialogOverlayStrategy, transformingFullScreenDialogToRightSheetOverlayStrategy, wireFormSupport };
36708
37746
  //# sourceMappingURL=ethlete-components.mjs.map