@ethlete/query 4.7.4 → 4.7.6

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,11 +1,12 @@
1
- import * as i1 from '@ethlete/core';
2
- import { getCookie, setCookie, deleteCookie, createDestroy, DelayableDirective, RouterStateService, clone, equal, ET_PROPERTY_REMOVED } from '@ethlete/core';
3
- import { Observable, filter, takeWhile, switchMap, of, tap, BehaviorSubject, shareReplay, Subject, takeUntil, interval, skip, map, timer, startWith, combineLatest, withLatestFrom, fromEvent, debounceTime, take, concat } from 'rxjs';
1
+ import * as i1$1 from '@ethlete/core';
2
+ import { getCookie, setCookie, deleteCookie, createDestroy, nextFrame, DelayableDirective, RouterStateService, clone, equal, ET_PROPERTY_REMOVED } from '@ethlete/core';
3
+ import { Observable, BehaviorSubject, Subject, fromEvent, timer, takeUntil, take, filter, takeWhile, switchMap, of, tap, shareReplay, interval, skip, map, startWith, combineLatest, withLatestFrom, debounceTime, concat } from 'rxjs';
4
4
  import * as i0 from '@angular/core';
5
- import { isDevMode, InjectionToken, inject, signal, computed, effect, Component, ChangeDetectionStrategy, ViewEncapsulation, TemplateRef, ViewContainerRef, ErrorHandler, ChangeDetectorRef, Directive, Input, ElementRef, NgZone, assertInInjectionContext } from '@angular/core';
6
- import { NgIf, NgFor, AsyncPipe, JsonPipe } from '@angular/common';
7
- import { toObservable, toSignal } from '@angular/core/rxjs-interop';
8
- import { FormGroup } from '@angular/forms';
5
+ import { isDevMode, Pipe, InjectionToken, inject, ChangeDetectorRef, signal, computed, effect, Component, ChangeDetectionStrategy, ViewEncapsulation, TemplateRef, ViewContainerRef, ErrorHandler, Directive, Input, ElementRef, NgZone, assertInInjectionContext } from '@angular/core';
6
+ import { NgIf, NgFor, AsyncPipe, JsonPipe, NgClass } from '@angular/common';
7
+ import { toSignal, toObservable } from '@angular/core/rxjs-interop';
8
+ import * as i1 from '@angular/forms';
9
+ import { FormControl, ReactiveFormsModule, FormGroup } from '@angular/forms';
9
10
  import { Router, ActivatedRoute } from '@angular/router';
10
11
 
11
12
  class CustomHeaderAuthProvider {
@@ -492,6 +493,270 @@ const request = (config) => {
492
493
  });
493
494
  };
494
495
 
496
+ class QueryCreator {
497
+ constructor(_queryConfig, _client, _store) {
498
+ this._queryConfig = _queryConfig;
499
+ this._client = _client;
500
+ this._store = _store;
501
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
502
+ // @ts-ignore
503
+ this.prepare = (args) => {
504
+ const route = buildRoute({
505
+ base: this._client.config.baseRoute,
506
+ route: this._queryConfig.route,
507
+ pathParams: args?.pathParams,
508
+ queryParams: computeQueryQueryParams({ config: this._queryConfig, client: this._client, args }),
509
+ queryParamConfig: this._client.config.request?.queryParams,
510
+ });
511
+ const cacheKey = isGqlQueryConfig(this._queryConfig) ? buildGqlCacheKey(this._queryConfig, args) : route;
512
+ if (shouldCacheQuery(this._queryConfig.method)) {
513
+ const existingQuery = this._store.get(cacheKey);
514
+ if (existingQuery) {
515
+ return existingQuery;
516
+ }
517
+ }
518
+ const query = new Query(this._client, this._queryConfig, route, args ?? {});
519
+ if (shouldCacheQuery(this._queryConfig.method)) {
520
+ this._store.add(cacheKey, query);
521
+ }
522
+ this._store._dispatchQueryCreated(query);
523
+ return query;
524
+ };
525
+ this.behaviorSubject = (initialValue) => new BehaviorSubject(initialValue ?? null);
526
+ }
527
+ }
528
+
529
+ class QueryStore {
530
+ constructor(_config) {
531
+ this._config = _config;
532
+ /**
533
+ * @internal
534
+ */
535
+ this._store = new Map();
536
+ this._garbageCollector = null;
537
+ this._isInLowResourceMode = false;
538
+ this._lastBlurTimestamp = Date.now();
539
+ this._storeChange$ = new Subject();
540
+ this._queryCreated$ = new Subject();
541
+ this.storeChange$ = this._storeChange$.asObservable();
542
+ this.queryCreated$ = this._queryCreated$.asObservable();
543
+ this._initSmartQueryHandling();
544
+ }
545
+ add(id, query) {
546
+ this._store.set(id, query);
547
+ this._initGarbageCollector();
548
+ this._logState(id, query, 'SET');
549
+ this._storeChange$.next(id);
550
+ }
551
+ get(id) {
552
+ return this._store.get(id) ?? null;
553
+ }
554
+ remove(id) {
555
+ this._store.delete(id);
556
+ this._logState(id, null, 'REMOVE');
557
+ this._storeChange$.next(id);
558
+ }
559
+ forEach(callback) {
560
+ for (const [key, query] of this._store) {
561
+ callback(query, key);
562
+ }
563
+ }
564
+ refreshQueriesInUse(config) {
565
+ const { ignoreCacheValidity, purgeUnused } = config ?? {};
566
+ for (const [key, query] of this._store) {
567
+ if (query.isInUse &&
568
+ (query.isExpired || ignoreCacheValidity) &&
569
+ query.autoRefreshOnConfig.queryClientDefaultHeadersChange) {
570
+ query.execute({ skipCache: true, _triggeredVia: 'auto' });
571
+ }
572
+ else if (purgeUnused && !query.isInUse) {
573
+ this.remove(key);
574
+ }
575
+ }
576
+ }
577
+ /**
578
+ * @internal
579
+ */
580
+ _dispatchQueryCreated(query) {
581
+ this._queryCreated$.next(query);
582
+ }
583
+ _initSmartQueryHandling() {
584
+ const windowBlur$ = fromEvent(window, 'blur');
585
+ const windowFocus$ = fromEvent(window, 'focus');
586
+ windowBlur$.subscribe(() => {
587
+ timer(5000)
588
+ .pipe(takeUntil(windowFocus$), take(1))
589
+ .subscribe(() => {
590
+ this._lastBlurTimestamp = Date.now();
591
+ this._isInLowResourceMode = true;
592
+ this._stopGarbageCollector();
593
+ if (this._config?.enableSmartPolling) {
594
+ this.forEach((query) => {
595
+ if (!query.isPolling || !query._enableSmartPolling) {
596
+ return;
597
+ }
598
+ query.pausePolling();
599
+ });
600
+ }
601
+ });
602
+ });
603
+ windowFocus$.subscribe(() => {
604
+ if (!this._isInLowResourceMode) {
605
+ return;
606
+ }
607
+ this._isInLowResourceMode = false;
608
+ if (this._config?.enableSmartPolling || this._config?.autoRefreshQueriesOnWindowFocus) {
609
+ this.forEach((query) => {
610
+ if (this._config?.enableSmartPolling && query._isPollingPaused) {
611
+ query.resumePolling();
612
+ }
613
+ if (Date.now() - this._lastBlurTimestamp > 15000) {
614
+ if (this._config?.autoRefreshQueriesOnWindowFocus &&
615
+ query.isExpired &&
616
+ query.isInUse &&
617
+ query.autoRefreshOnConfig.windowFocus) {
618
+ query.execute({ skipCache: true, _triggeredVia: 'auto' });
619
+ }
620
+ }
621
+ });
622
+ }
623
+ this._initGarbageCollector();
624
+ });
625
+ }
626
+ _logState(key, item, operation) {
627
+ if (!this._config?.enableChangeLogging)
628
+ return;
629
+ const stateAsJson = {};
630
+ this._store.forEach((value, key) => {
631
+ stateAsJson[key] = value;
632
+ });
633
+ console.log(`%c[${operation}] ${key}`, 'font-weight: bold');
634
+ console.log({ operation, key, item });
635
+ console.log(stateAsJson);
636
+ }
637
+ _initGarbageCollector() {
638
+ if (this._garbageCollector !== null) {
639
+ return;
640
+ }
641
+ this._logGarbageCollector('Start');
642
+ this._garbageCollector = window.setInterval(() => {
643
+ this._runGarbageCollector();
644
+ }, 15000);
645
+ }
646
+ _stopGarbageCollector() {
647
+ if (this._garbageCollector !== null) {
648
+ window.clearInterval(this._garbageCollector);
649
+ this._garbageCollector = null;
650
+ this._logGarbageCollector('Stop');
651
+ }
652
+ }
653
+ _runGarbageCollector() {
654
+ this._logGarbageCollector('Collecting...');
655
+ this._store.forEach((item, key) => {
656
+ if (item.isExpired && !item.isInUse) {
657
+ this.remove(key);
658
+ }
659
+ });
660
+ this._logGarbageCollector('Collection done');
661
+ if (!this._store.size) {
662
+ this._stopGarbageCollector();
663
+ }
664
+ }
665
+ _logGarbageCollector(action) {
666
+ if (!this._config?.enableGarbageCollectorLogging)
667
+ return;
668
+ console.log(`%cGC: ${action}`, 'color: yellow; font-weight: bold');
669
+ }
670
+ }
671
+
672
+ class QueryClient {
673
+ get config() {
674
+ return this._clientConfig;
675
+ }
676
+ get authProvider() {
677
+ return this._authProvider$.getValue() ?? this._clientConfig.parent?._authProvider$.getValue() ?? null;
678
+ }
679
+ get authProvider$() {
680
+ return this._authProvider$.asObservable() ?? this._clientConfig.parent?._authProvider$.asObservable() ?? null;
681
+ }
682
+ constructor(_clientConfig) {
683
+ this._clientConfig = _clientConfig;
684
+ this._authProvider$ = new BehaviorSubject(null);
685
+ this.get = (queryConfig) => this.fetch({
686
+ ...queryConfig,
687
+ method: 'GET',
688
+ });
689
+ this.post = (queryConfig) => this.fetch({
690
+ ...queryConfig,
691
+ method: 'POST',
692
+ });
693
+ this.put = (queryConfig) => this.fetch({
694
+ ...queryConfig,
695
+ method: 'PUT',
696
+ });
697
+ this.patch = (queryConfig) => this.fetch({
698
+ ...queryConfig,
699
+ method: 'PATCH',
700
+ });
701
+ this.delete = (queryConfig) => this.fetch({
702
+ ...queryConfig,
703
+ method: 'DELETE',
704
+ });
705
+ this.gqlQuery = (queryConfig) => this.fetch({
706
+ ...queryConfig,
707
+ method: 'GQL_QUERY',
708
+ });
709
+ this.gqlMutate = (queryConfig) => this.fetch({
710
+ ...queryConfig,
711
+ method: 'GQL_MUTATE',
712
+ });
713
+ this.fetch = (queryConfig) => new QueryCreator(queryConfig, this, this._store);
714
+ this.setAuthProvider = (authProvider) => {
715
+ if (this.authProvider) {
716
+ throw new Error('The auth provider is already set. Please call clearAuthProvider() first.');
717
+ }
718
+ this._authProvider$.next(authProvider);
719
+ };
720
+ this.setDefaultHeaders = (config) => {
721
+ if (!this._clientConfig.request) {
722
+ this._clientConfig.request = {
723
+ headers: config.headers ?? undefined,
724
+ };
725
+ }
726
+ else {
727
+ this._clientConfig.request.headers = config.headers ?? undefined;
728
+ }
729
+ if (config.refreshQueriesInUse) {
730
+ this._store.refreshQueriesInUse({ purgeUnused: true, ignoreCacheValidity: true });
731
+ }
732
+ };
733
+ this.clearAuthProvider = () => {
734
+ this.authProvider?.cleanUp();
735
+ this._authProvider$.next(null);
736
+ };
737
+ this._store = new QueryStore({
738
+ enableChangeLogging: _clientConfig.logging?.queryStateChanges,
739
+ enableGarbageCollectorLogging: _clientConfig.logging?.queryStateGarbageCollector,
740
+ autoRefreshQueriesOnWindowFocus: _clientConfig.request?.autoRefreshQueriesOnWindowFocus ?? true,
741
+ enableSmartPolling: _clientConfig.request?.enableSmartPolling ?? true,
742
+ });
743
+ }
744
+ }
745
+
746
+ const shouldCacheQuery = (method) => {
747
+ return method === 'GET' || method === 'OPTIONS' || method === 'HEAD' || method === 'GQL_QUERY';
748
+ };
749
+ const buildGqlCacheKey = (config, args) => {
750
+ const { query } = config;
751
+ const variables = args?.variables || {};
752
+ const queryWithoutNewLinesAndWhitespace = query.replace(/\s/g, '');
753
+ const queryFirst50Chars = queryWithoutNewLinesAndWhitespace.slice(0, 50);
754
+ const queryMiddle25Chars = queryWithoutNewLinesAndWhitespace.slice(queryWithoutNewLinesAndWhitespace.length / 2 - 25, queryWithoutNewLinesAndWhitespace.length / 2 + 25);
755
+ const queryLast50Chars = queryWithoutNewLinesAndWhitespace.slice(-50);
756
+ const vars = JSON.stringify(variables);
757
+ return `${queryFirst50Chars}...${queryMiddle25Chars}...${queryLast50Chars}...${vars}`;
758
+ };
759
+
495
760
  const getOpName = /(query|mutation) ?([\w\d-_]+)? ?\(.*?\)? \{/;
496
761
  const transformGql = (str) => {
497
762
  str = Array.isArray(str) ? str.join('') : str;
@@ -742,9 +1007,10 @@ const paginatedEntityValueUpdater = (findFn) => ({ response, rawResponse, entity
742
1007
  };
743
1008
  };
744
1009
 
1010
+ let _nextQueryId = 0;
745
1011
  class Query {
746
1012
  get _nextId() {
747
- return ++this._currentId;
1013
+ return ++this._currentLocalId;
748
1014
  }
749
1015
  get state$() {
750
1016
  return this._state$.pipe(tap((s) => {
@@ -800,27 +1066,32 @@ class Query {
800
1066
  get _enableSmartPolling() {
801
1067
  return this._queryConfig.enableSmartPolling ?? true;
802
1068
  }
803
- /**
804
- * @internal
805
- */
806
- get _arguments() {
807
- return this._args;
808
- }
809
1069
  get store() {
810
1070
  return this._queryConfig.entity?.store ?? null;
811
1071
  }
812
- constructor(_client, _queryConfig,
1072
+ get canBeCached() {
1073
+ return shouldCacheQuery(this._queryConfig.method);
1074
+ }
1075
+ constructor(_client,
813
1076
  /**
814
1077
  * @internal
815
1078
  */
816
- _routeWithParams, _args) {
1079
+ _queryConfig,
1080
+ /**
1081
+ * @internal
1082
+ */
1083
+ _routeWithParams,
1084
+ /**
1085
+ * @internal
1086
+ */
1087
+ _arguments) {
817
1088
  this._client = _client;
818
1089
  this._queryConfig = _queryConfig;
819
1090
  this._routeWithParams = _routeWithParams;
820
- this._args = _args;
821
- this._currentId = 0;
1091
+ this._arguments = _arguments;
1092
+ this._id = _nextQueryId++;
1093
+ this._currentLocalId = 0;
822
1094
  this._pollingSubscription = null;
823
- this._requestSubscription = null;
824
1095
  this._onAbort$ = new Subject();
825
1096
  this._currentPollConfig = null;
826
1097
  /**
@@ -829,7 +1100,7 @@ class Query {
829
1100
  this._isPollingPaused = false;
830
1101
  this._state$ = new BehaviorSubject({
831
1102
  type: "PREPARED" /* QueryStateType.Prepared */,
832
- meta: { id: this._currentId, triggeredVia: 'program' },
1103
+ meta: { id: this._currentLocalId, triggeredVia: 'program' },
833
1104
  });
834
1105
  }
835
1106
  execute(options = {}) {
@@ -854,9 +1125,9 @@ class Query {
854
1125
  }
855
1126
  this._state$.next({ type: "LOADING" /* QueryStateType.Loading */, meta });
856
1127
  const method = computeQueryMethod({ config: queryConfig, client: this._client });
857
- const body = computeQueryBody({ config: queryConfig, client: this._client, args: this._args, method });
858
- const headers = computeQueryHeaders({ client: this._client, config: queryConfig, args: this._args });
859
- this._requestSubscription = request({
1128
+ const body = computeQueryBody({ config: queryConfig, client: this._client, args: this._arguments, method });
1129
+ const headers = computeQueryHeaders({ client: this._client, config: queryConfig, args: this._arguments });
1130
+ request({
860
1131
  urlWithParams: this._routeWithParams,
861
1132
  method,
862
1133
  body,
@@ -943,9 +1214,9 @@ class Query {
943
1214
  ? response['data']
944
1215
  : response;
945
1216
  if (this._queryConfig.entity && this._queryConfig.entity.set) {
946
- const id = this._queryConfig.entity?.id({ args: this._args, response: responseData });
1217
+ const id = this._queryConfig.entity?.id({ args: this._arguments, response: responseData });
947
1218
  this._queryConfig.entity?.set({
948
- args: this._args,
1219
+ args: this._arguments,
949
1220
  response: responseData,
950
1221
  id,
951
1222
  store: this._queryConfig.entity.store,
@@ -989,9 +1260,9 @@ class Query {
989
1260
  if (!isQueryStateSuccess(s) || !this._queryConfig.entity?.get) {
990
1261
  return of(s);
991
1262
  }
992
- const id = this._queryConfig.entity.id({ args: this._args, response: s.response });
1263
+ const id = this._queryConfig.entity.id({ args: this._arguments, response: s.response });
993
1264
  return this._queryConfig.entity
994
- .get({ args: this._args, id, response: s.response, store: this._queryConfig.entity.store })
1265
+ .get({ args: this._arguments, id, response: s.response, store: this._queryConfig.entity.store })
995
1266
  .pipe(map((v) => ({ ...s, response: v })));
996
1267
  }
997
1268
  }
@@ -1173,6 +1444,29 @@ const isClassValidatorError = (error) => {
1173
1444
  return typeof error === 'object' && error !== null && 'statusCode' in error && 'message' in error && 'error' in error;
1174
1445
  };
1175
1446
 
1447
+ class QueryShortNamePipe {
1448
+ transform(value) {
1449
+ const route = value._routeWithParams;
1450
+ if (typeof route === 'string') {
1451
+ // given an url that looks like this: https://api.github.com/repos/EladBezalel/etimo-achievements or https://api.github.com/repos/EladBezalel/etimo-achievements?test=1&test2=2
1452
+ // we want to extract the last 3 parts of the url, so we get: repos/EladBezalel/etimo-achievements
1453
+ const parts = route.split('/');
1454
+ if (parts.length > 3) {
1455
+ return `.../${parts.slice(parts.length - 3).join('/')}`;
1456
+ }
1457
+ return route;
1458
+ }
1459
+ console.log(route);
1460
+ return route;
1461
+ }
1462
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: QueryShortNamePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
1463
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "16.1.6", ngImport: i0, type: QueryShortNamePipe, isStandalone: true, name: "queryShortName" }); }
1464
+ }
1465
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: QueryShortNamePipe, decorators: [{
1466
+ type: Pipe,
1467
+ args: [{ name: 'queryShortName', standalone: true }]
1468
+ }] });
1469
+
1176
1470
  const QUERY_CLIENT_DEVTOOLS_TOKEN = new InjectionToken('QUERY_CLIENT_DEVTOOLS_TOKEN');
1177
1471
  const provideQueryClientForDevtools = (queryClient) => {
1178
1472
  if (!isDevMode()) {
@@ -1187,6 +1481,7 @@ const provideQueryClientForDevtools = (queryClient) => {
1187
1481
 
1188
1482
  class QueryDevtoolsComponent {
1189
1483
  constructor() {
1484
+ this._cdr = inject(ChangeDetectorRef);
1190
1485
  this._destroy$ = createDestroy();
1191
1486
  this.queryClientConfigs = inject(QUERY_CLIENT_DEVTOOLS_TOKEN);
1192
1487
  this.isOpen = signal(false);
@@ -1195,13 +1490,17 @@ class QueryDevtoolsComponent {
1195
1490
  this.queryListMode = signal('live');
1196
1491
  this.showResponse = signal(false);
1197
1492
  this.showRawResponse = signal(false);
1198
- this.selectedClientId = signal(0);
1199
- this.selectedQueryPath = signal(null);
1493
+ this.showQueryConfig = signal(false);
1494
+ this.showEntityStoreValue = signal(false);
1495
+ this.showArguments = signal(false);
1496
+ this.selectedClientIdCtrl = new FormControl(0);
1497
+ this.selectedClientId = toSignal(this.selectedClientIdCtrl.valueChanges.pipe(startWith(this.selectedClientIdCtrl.value)));
1498
+ this.selectedQueryId = signal(null);
1200
1499
  this.selectedClientConfig = computed(() => {
1201
1500
  if (this.queryClientConfigs.length === 1) {
1202
1501
  return this.queryClientConfigs[0];
1203
1502
  }
1204
- const index = this.selectedClientId();
1503
+ const index = this.selectedClientId() ?? 0;
1205
1504
  return this.queryClientConfigs[index];
1206
1505
  });
1207
1506
  this.queryStore = computed(() => {
@@ -1212,22 +1511,74 @@ class QueryDevtoolsComponent {
1212
1511
  this._queries$ = new BehaviorSubject([]);
1213
1512
  this.queries = toSignal(this._queries$);
1214
1513
  this.selectedQuery = computed(() => {
1215
- const path = this.selectedQueryPath();
1514
+ const id = this.selectedQueryId();
1216
1515
  const mode = this.queryListMode();
1217
- if (!path || !mode) {
1516
+ if (id === null || !mode) {
1218
1517
  return null;
1219
1518
  }
1220
1519
  if (mode === 'live') {
1221
- return this.queries()?.find((q) => q._routeWithParams === path) ?? null;
1520
+ return this.queries()?.find((q) => q._id === id) ?? null;
1222
1521
  }
1223
- return this.queryHistory()?.find((q) => q._routeWithParams === path) ?? null;
1522
+ return this.queryHistory()?.find((q) => q._id === id) ?? null;
1523
+ });
1524
+ this.selectedQueryEntityStore = computed(() => {
1525
+ const query = this.selectedQuery();
1526
+ if (!query)
1527
+ return null;
1528
+ return query.store;
1224
1529
  });
1530
+ this.selectedQueryEntityStore$ = toObservable(this.selectedQueryEntityStore);
1531
+ this.selectedQueryEntityStoreValue$ = this.selectedQueryEntityStore$.pipe(switchMap((s) => s?._change$.pipe(startWith(''), map(() => s?._dictionary ?? null)) ?? of(null)));
1225
1532
  this.queryHistory = signal([]);
1533
+ this.stringifiedQueryConfig = computed(() => {
1534
+ const query = this.selectedQuery();
1535
+ if (!query)
1536
+ return null;
1537
+ const cfg = query._queryConfig;
1538
+ const route = typeof cfg.route === 'string' ? cfg.route : cfg.route ? cfg.route({}).replace(/undefined/g, ':param') : null;
1539
+ const sharedCfg = {
1540
+ method: cfg.method,
1541
+ secure: cfg.secure,
1542
+ route,
1543
+ reportProgress: cfg.reportProgress,
1544
+ responseType: cfg.responseType,
1545
+ withCredentials: cfg.withCredentials,
1546
+ autoRefreshOn: cfg.autoRefreshOn,
1547
+ enableSmartPolling: cfg.enableSmartPolling,
1548
+ hasEntityStore: !!cfg.entity,
1549
+ };
1550
+ if (isGqlQueryConfig(cfg)) {
1551
+ return {
1552
+ ...sharedCfg,
1553
+ query: cfg.query,
1554
+ transferVia: cfg.transferVia,
1555
+ };
1556
+ }
1557
+ return {
1558
+ ...sharedCfg,
1559
+ };
1560
+ });
1226
1561
  this.trackByClient = (_, { client }) => client.config.baseRoute;
1227
- this.trackByQuery = (_, { _routeWithParams }) => _routeWithParams;
1562
+ this.trackByQuery = (_, { _id }) => _id;
1563
+ this.selectedClientIdCtrl.valueChanges
1564
+ .pipe(tap(() => this.clearQueryHistory()), takeUntil(this._destroy$))
1565
+ .subscribe();
1228
1566
  this.queryStore$
1229
1567
  .pipe(switchMap((s) => s.storeChange$.pipe(startWith(''), map(() => s._store), tap((s) => this._queries$.next(Array.from(s.values()))), takeUntil(this._destroy$))))
1230
1568
  .subscribe();
1569
+ this.queryStore$
1570
+ .pipe(switchMap((s) => s.queryCreated$.pipe(tap((query) => {
1571
+ const currentHistory = this.queryHistory();
1572
+ if (!currentHistory)
1573
+ return;
1574
+ const currentHistoryClone = [...currentHistory];
1575
+ if (!currentHistory.some((q) => q._id === query._id)) {
1576
+ currentHistoryClone.unshift(query);
1577
+ }
1578
+ currentHistoryClone.splice(50);
1579
+ this.queryHistory.set(currentHistoryClone);
1580
+ }), takeUntil(this._destroy$))))
1581
+ .subscribe();
1231
1582
  this._queries$
1232
1583
  .pipe(tap((queries) => {
1233
1584
  const currentHistory = this.queryHistory();
@@ -1235,8 +1586,8 @@ class QueryDevtoolsComponent {
1235
1586
  return;
1236
1587
  const currentHistoryClone = [...currentHistory];
1237
1588
  for (const query of queries) {
1238
- if (!currentHistoryClone.includes(query)) {
1239
- currentHistoryClone.push(query);
1589
+ if (!currentHistoryClone.some((q) => q._id === query._id)) {
1590
+ currentHistoryClone.unshift(query);
1240
1591
  }
1241
1592
  }
1242
1593
  currentHistoryClone.splice(50);
@@ -1250,8 +1601,11 @@ class QueryDevtoolsComponent {
1250
1601
  snapLayout: this.snapLayout(),
1251
1602
  showResponse: this.showResponse(),
1252
1603
  showRawResponse: this.showRawResponse(),
1604
+ showQueryConfig: this.showQueryConfig(),
1605
+ showEntityStoreValue: this.showEntityStoreValue(),
1606
+ showArguments: this.showArguments(),
1253
1607
  selectedClientId: this.selectedClientId(),
1254
- selectedQueryPath: this.selectedQueryPath(),
1608
+ selectedQueryPath: this.selectedQueryId(),
1255
1609
  queryListMode: this.queryListMode(),
1256
1610
  };
1257
1611
  window.localStorage.setItem('ethlete:query:devtools', JSON.stringify(devtoolConfig));
@@ -1264,8 +1618,11 @@ class QueryDevtoolsComponent {
1264
1618
  this.snapLayout.set(parsed.snapLayout ?? 'full');
1265
1619
  this.showResponse.set(parsed.showResponse ?? false);
1266
1620
  this.showRawResponse.set(parsed.showRawResponse ?? false);
1267
- this.selectedClientId.set(parsed.selectedClientId ?? 0);
1268
- this.selectedQueryPath.set(parsed.selectedQueryPath ?? null);
1621
+ this.showQueryConfig.set(parsed.showQueryConfig ?? false);
1622
+ this.showEntityStoreValue.set(parsed.showEntityStoreValue ?? false);
1623
+ this.showArguments.set(parsed.showArguments ?? false);
1624
+ this.selectedClientIdCtrl.setValue(parsed.selectedClientId ?? 0);
1625
+ this.selectedQueryId.set(parsed.selectedQueryPath ?? null);
1269
1626
  this.queryListMode.set(parsed.queryListMode ?? 'live');
1270
1627
  }
1271
1628
  }
@@ -1277,34 +1634,42 @@ class QueryDevtoolsComponent {
1277
1634
  }
1278
1635
  toggleShowResponse() {
1279
1636
  this.showResponse.set(!this.showResponse());
1637
+ nextFrame(() => {
1638
+ this._cdr.markForCheck();
1639
+ });
1640
+ }
1641
+ toggleShowQueryConfig() {
1642
+ this.showQueryConfig.set(!this.showQueryConfig());
1280
1643
  }
1281
1644
  toggleShowRawResponse() {
1282
1645
  this.showRawResponse.set(!this.showRawResponse());
1283
1646
  }
1647
+ toggleShowEntityStoreValue() {
1648
+ this.showEntityStoreValue.set(!this.showEntityStoreValue());
1649
+ }
1650
+ toggleShowArguments() {
1651
+ this.showArguments.set(!this.showArguments());
1652
+ }
1284
1653
  selectSnapLayout(layout) {
1285
1654
  this.snapLayout.set(layout);
1286
1655
  }
1287
1656
  selectQueryListMode(mode) {
1288
1657
  this.queryListMode.set(mode);
1289
1658
  }
1290
- selectClient(clientId) {
1291
- this.selectedClientId.set(clientId);
1292
- this.clearQueryHistory();
1293
- }
1294
1659
  selectQuery(query) {
1295
- this.selectedQueryPath.set(query._routeWithParams);
1660
+ this.selectedQueryId.set(query._id);
1296
1661
  }
1297
1662
  clearQueryHistory() {
1298
1663
  this.queryHistory.set([]);
1299
1664
  }
1300
1665
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: QueryDevtoolsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1301
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.1.6", type: QueryDevtoolsComponent, isStandalone: true, selector: "et-query-devtools", host: { classAttribute: "et-query-devtools" }, ngImport: i0, template: "<button *ngIf=\"!isOpen()\" (click)=\"toggleOpen()\" class=\"et-query-devtools-open-btn\">Open Devtools</button>\n\n<div\n *ngIf=\"isOpen()\"\n [class.et-query-devtools-container--transparent]=\"isTranslucent()\"\n class=\"et-query-devtools-container et-query-devtools-container--snap-{{ snapLayout() }}\"\n>\n <button (click)=\"toggleOpen()\" class=\"et-query-devtools-close-btn\">Close Devtools</button>\n <button (click)=\"toggleTranslucent()\" class=\"et-query-devtools-translucent-btn\">Toggle Translucent</button>\n <button (click)=\"selectSnapLayout('full')\" class=\"et-query-devtools-snap-btn\">Snap full</button>\n <button (click)=\"selectSnapLayout('top')\" class=\"et-query-devtools-snap-btn\">Snap top</button>\n <button (click)=\"selectSnapLayout('bottom')\" class=\"et-query-devtools-snap-btn\">Snap bottom</button>\n <button (click)=\"selectSnapLayout('left')\" class=\"et-query-devtools-snap-btn\">Snap left</button>\n <button (click)=\"selectSnapLayout('right')\" class=\"et-query-devtools-snap-btn\">Snap right</button>\n\n <ng-container *ngIf=\"queryClientConfigs.length > 1\">\n <p>Client instance:</p>\n <ul>\n <li *ngFor=\"let clientConfig of queryClientConfigs; index as i; trackBy: trackByClient\">\n <button (click)=\"selectClient(i)\">\n {{ clientConfig.displayName ?? clientConfig.client.config.baseRoute }}\n </button>\n </li>\n </ul>\n </ng-container>\n\n <ng-container *ngIf=\"selectedClientConfig() as cfg\">\n <p>{{ cfg.displayName ?? cfg.client.config.baseRoute }}</p>\n\n <button (click)=\"cfg.client._store.refreshQueriesInUse()\">Force Refresh of in use queries</button> <br />\n <button (click)=\"cfg.client._store.refreshQueriesInUse({ ignoreCacheValidity: true })\">HARD Force Refresh</button>\n <br />\n <button (click)=\"cfg.client._store.refreshQueriesInUse({ ignoreCacheValidity: true, purgeUnused: true })\">\n PURGE & HARD Force Refresh\n </button>\n\n <br />\n <br />\n\n <button (click)=\"selectQueryListMode('live')\">Live</button>\n <button (click)=\"selectQueryListMode('history')\">History (last {{ queryHistory().length }} )</button>\n <button *ngIf=\"queryListMode() === 'history'\" (click)=\"clearQueryHistory()\">Clear</button>\n <ul *ngIf=\"queryListMode() === 'live'\">\n <li *ngFor=\"let query of queries(); trackBy: trackByQuery\">\n <button (click)=\"selectQuery(query)\">\n {{ query._routeWithParams }}\n </button>\n </li>\n </ul>\n\n <ul *ngIf=\"queryListMode() === 'history'\">\n <li *ngFor=\"let query of queryHistory(); trackBy: trackByQuery\">\n <button (click)=\"selectQuery(query)\">\n {{ query._routeWithParams }}\n </button>\n </li>\n </ul>\n </ng-container>\n\n <ng-container *ngIf=\"selectedQuery() as query\">\n <p>{{ query._routeWithParams }}</p>\n\n <button (click)=\"query.execute()\">Execute</button> <br />\n <button (click)=\"query.execute({ skipCache: true })\">FORCE Execute</button> <br />\n <button (click)=\"query.abort()\">Abort</button> <br />\n <button (click)=\"query.poll({ interval: 5000, takeUntil: _destroy$ })\">Poll</button> <br />\n <button (click)=\"query.stopPolling()\">Stop polling</button> <br />\n <button (click)=\"query.pausePolling()\">Pause polling</button> <br />\n <button (click)=\"query.resumePolling()\">Resume polling</button> <br />\n\n <div>\n <p>state type</p>\n <pre>{{ query.rawState.type }}</pre>\n </div>\n\n <div>\n <p>_enableSmartPolling:</p>\n <pre>{{ query._enableSmartPolling }}</pre>\n </div>\n\n <div>\n <p>isExpired:</p>\n <pre>{{ query.isExpired }}</pre>\n </div>\n\n <div>\n <p>isInUse:</p>\n <pre>{{ query.isInUse }}</pre>\n </div>\n\n <div>\n <p>_subscriberCount:</p>\n <pre>{{ query._subscriberCount }}</pre>\n </div>\n\n <div>\n <p>isPolling:</p>\n <pre>{{ query.isPolling }}</pre>\n </div>\n\n <div>\n <p>_arguments:</p>\n <pre>{{ query._arguments | json }}</pre>\n </div>\n\n <div>\n <p>state$:</p>\n <p><i> Warning, this will cause a subscription if shown </i></p>\n <button (click)=\"toggleShowResponse()\">Show/Hide</button>\n <pre *ngIf=\"showResponse()\">{{ query.state$ | async | json }}</pre>\n </div>\n\n <div>\n <p>rawState:</p>\n <button (click)=\"toggleShowRawResponse()\">Show/Hide</button>\n <pre *ngIf=\"showRawResponse()\">{{ query.rawState | json }}</pre>\n </div>\n </ng-container>\n</div>\n", styles: [".et-query-devtools-container{position:fixed;inset:0;background-color:#000;z-index:10000;overflow:auto}.et-query-devtools-container.et-query-devtools-container--transparent{opacity:.25;pointer-events:none}.et-query-devtools-container.et-query-devtools-container--transparent .et-query-devtools-translucent-btn{pointer-events:all}.et-query-devtools-container.et-query-devtools-container--snap-left{inset-inline-start:0;inset-block-start:0;inset-block-end:0;width:50%}.et-query-devtools-container.et-query-devtools-container--snap-right{inset-inline-start:unset;inset-inline-end:0;inset-block-start:0;inset-block-end:0;width:50%}.et-query-devtools-container.et-query-devtools-container--snap-top{inset-inline-start:0;inset-block-start:0;inset-inline-end:0;height:50%}.et-query-devtools-container.et-query-devtools-container--snap-bottom{inset-inline-start:0;inset-block-start:unset;inset-block-end:0;inset-inline-end:0;height:50%}.et-query-devtools-open-btn{position:fixed;inset-inline-start:0;inset-block-start:0;z-index:9999}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: JsonPipe, name: "json" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom }); }
1666
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.1.6", type: QueryDevtoolsComponent, isStandalone: true, selector: "et-query-devtools", host: { classAttribute: "et-query-devtools" }, ngImport: i0, template: "<button *ngIf=\"!isOpen()\" (click)=\"toggleOpen()\" class=\"et-query-devtools-open-btn et-qd-chip\">Open Devtools</button>\n\n<div\n *ngIf=\"isOpen()\"\n [class.et-query-devtools-container--transparent]=\"isTranslucent()\"\n class=\"et-query-devtools-container et-query-devtools-container--snap-{{ snapLayout() }}\"\n>\n <header class=\"et-qd-header\">\n <h3 class=\"et-qd-header__title\">Query Devtools</h3>\n\n <div class=\"et-qd-header__select\">\n <label for=\"et-query-client-instance\">Client instance</label>\n <select [formControl]=\"selectedClientIdCtrl\" id=\"et-query-client-instance\">\n <option *ngFor=\"let clientConfig of queryClientConfigs; index as i; trackBy: trackByClient\" [value]=\"i\">\n {{ clientConfig.displayName ?? clientConfig.client.config.baseRoute }}\n </option>\n </select>\n </div>\n\n <button (click)=\"toggleOpen()\" class=\"et-qd-btn et-qd-btn-secondary\">Close</button>\n </header>\n\n <div class=\"et-qd-sub-header\">\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !isTranslucent() }\"\n (click)=\"toggleTranslucent()\"\n class=\"et-qd-chip et-qd-translucent-clickable\"\n >\n Translucent\n </button>\n <div class=\"et-qd-sub-header__divider\"></div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'full' }\"\n (click)=\"selectSnapLayout('full')\"\n class=\"et-qd-chip\"\n >\n Fullscreen\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'top' }\"\n (click)=\"selectSnapLayout('top')\"\n class=\"et-qd-chip\"\n >\n Top\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'bottom' }\"\n (click)=\"selectSnapLayout('bottom')\"\n class=\"et-qd-chip\"\n >\n Bottom\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'left' }\"\n (click)=\"selectSnapLayout('left')\"\n class=\"et-qd-chip\"\n >\n Left\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'right' }\"\n (click)=\"selectSnapLayout('right')\"\n class=\"et-qd-chip\"\n >\n Right\n </button>\n </div>\n\n <div *ngIf=\"selectedClientConfig() as cfg\" class=\"et-qd-client\">\n <p class=\"et-qd-client__title\">{{ cfg.displayName ?? cfg.client.config.baseRoute }}</p>\n\n <div class=\"et-qd-client__actions\">\n <button (click)=\"cfg.client._store.refreshQueriesInUse()\" class=\"et-qd-chip\">Refresh all</button>\n <button (click)=\"cfg.client._store.refreshQueriesInUse({ ignoreCacheValidity: true })\" class=\"et-qd-chip\">\n Refresh all (ignore caches)\n </button>\n <button\n (click)=\"cfg.client._store.refreshQueriesInUse({ ignoreCacheValidity: true, purgeUnused: true })\"\n class=\"et-qd-chip\"\n >\n Refresh all (ignore caches & purge unused)\n </button>\n </div>\n </div>\n\n <div *ngIf=\"selectedClientConfig() as cfg\" class=\"et-qd-queries\">\n <p class=\"et-qd-queries__title\">Query browser</p>\n <div class=\"et-qd-queries__actions\">\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': queryListMode() !== 'live' }\"\n (click)=\"selectQueryListMode('live')\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Live ({{ queries()?.length ?? 0 }})\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': queryListMode() !== 'history' }\"\n (click)=\"selectQueryListMode('history')\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n History ({{ queryHistory().length }}/50)\n </button>\n <ng-container *ngIf=\"queryListMode() === 'history'\">\n <div class=\"et-qd-queries__divider\"></div>\n <button (click)=\"clearQueryHistory()\" class=\"et-qd-chip et-qd-chip-bordered\">Clear history</button>\n </ng-container>\n </div>\n\n <div class=\"et-qd-queries__browser\">\n <ul *ngIf=\"queryListMode() === 'live'\" class=\"et-qd-queries__browser-list\">\n <li *ngFor=\"let query of queries(); trackBy: trackByQuery\">\n <button [class.et-qd-query--active]=\"selectedQuery()?._id === query._id\" (click)=\"selectQuery(query)\">\n <span> {{ query._queryConfig.method }}</span>\n <span> {{ query | queryShortName }}</span>\n </button>\n </li>\n </ul>\n <ul *ngIf=\"queryListMode() === 'history'\" class=\"et-qd-queries__browser-list\">\n <li *ngFor=\"let query of queryHistory(); trackBy: trackByQuery\">\n <button [class.et-qd-query--active]=\"selectedQuery()?._id === query._id\" (click)=\"selectQuery(query)\">\n <span> {{ query._queryConfig.method }}</span>\n <span> {{ query | queryShortName }}</span>\n </button>\n </li>\n </ul>\n <div class=\"et-qd-queries__browser-detail\">\n <ng-container *ngIf=\"selectedQuery() as query\">\n <p class=\"et-qd-queries__browser-detail__title\">\n {{ query._queryConfig.method }} {{ query._routeWithParams }}\n </p>\n\n <div class=\"et-qd-queries__actions\">\n <button (click)=\"query.execute()\" class=\"et-qd-chip et-qd-chip-bordered\">Execute</button>\n <button (click)=\"query.execute({ skipCache: true })\" class=\"et-qd-chip et-qd-chip-bordered\">\n Execute (ignore cache)\n </button>\n\n <button (click)=\"query.abort()\" class=\"et-qd-chip et-qd-chip-bordered\">Abort</button>\n <div class=\"et-qd-queries__divider\"></div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !query.isPolling }\"\n (click)=\"query.poll({ interval: 5000, takeUntil: _destroy$ })\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Poll\n </button>\n\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': query.isPolling }\"\n (click)=\"query.stopPolling()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Stop polling\n </button>\n <ng-container *ngIf=\"query.isPolling || query._isPollingPaused\">\n <button (click)=\"query.pausePolling()\" class=\"et-qd-chip et-qd-chip-secondary et-qd-chip-bordered\">\n Pause polling\n </button>\n <button (click)=\"query.resumePolling()\" class=\"et-qd-chip et-qd-chip-secondary et-qd-chip-bordered\">\n Resume polling\n </button>\n </ng-container>\n </div>\n\n <div class=\"et-qd-list\">\n <div class=\"et-qd-list-item\">\n <p>State type</p>\n <pre>{{ query.rawState.type }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Is expired</p>\n <pre>{{ query.isExpired }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Is in use</p>\n <pre>{{ query.isInUse }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Subscriber count</p>\n <pre>{{ query._subscriberCount }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Is polling</p>\n <pre>{{ query.isPolling }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Is polling paused</p>\n <pre>{{ query._isPollingPaused }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Enable smart polling</p>\n <pre>{{ query._enableSmartPolling }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Arguments</p>\n\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showArguments() }\"\n (click)=\"toggleShowArguments()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showArguments()\">{{ query._arguments | json }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>State</p>\n <p><i> Warning, this will cause a subscription if shown!</i></p>\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showResponse() }\"\n (click)=\"toggleShowResponse()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showResponse()\">{{ query.state$ | async | json }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Raw state</p>\n <p><i> The query state unaffected by entity stores</i></p>\n\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showRawResponse() }\"\n (click)=\"toggleShowRawResponse()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showRawResponse()\">{{ query.rawState | json }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Query config</p>\n\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showQueryConfig() }\"\n (click)=\"toggleShowQueryConfig()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showQueryConfig()\">{{ stringifiedQueryConfig() | json }}</pre>\n </div>\n\n <div *ngIf=\"!!query._queryConfig.entity\" class=\"et-qd-list-item\">\n <p>Entity store value</p>\n\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showEntityStoreValue() }\"\n (click)=\"toggleShowEntityStoreValue()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showEntityStoreValue()\">{{ selectedQueryEntityStoreValue$ | async | json }}</pre>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n</div>\n", styles: [".et-query-devtools{position:fixed;top:0;z-index:1000000;font-size:14px}.et-query-devtools-container{position:fixed;inset:0;background-color:#25292e;z-index:10000;overflow:auto;font-family:monospace;display:grid;grid-template-rows:auto auto auto minmax(0,1fr)}.et-query-devtools-container.et-query-devtools-container--transparent{opacity:.25;pointer-events:none}.et-query-devtools-container.et-query-devtools-container--transparent .et-query-devtools-translucent-btn{pointer-events:all}.et-query-devtools-container.et-query-devtools-container--snap-left{inset-inline-start:0;inset-block-start:0;inset-block-end:0;width:50%;border-right:1px solid rgb(37,41,46)}.et-query-devtools-container.et-query-devtools-container--snap-right{inset-inline-start:unset;inset-inline-end:0;inset-block-start:0;inset-block-end:0;width:50%;border-left:1px solid rgb(37,41,46)}.et-query-devtools-container.et-query-devtools-container--snap-top{inset-inline-start:0;inset-block-start:0;inset-inline-end:0;height:50%;border-bottom:1px solid rgb(37,41,46)}.et-query-devtools-container.et-query-devtools-container--snap-bottom{inset-inline-start:0;inset-block-start:unset;inset-block-end:0;inset-inline-end:0;height:50%;border-top:1px solid rgb(37,41,46)}.et-query-devtools-open-btn{position:fixed;inset-inline-start:0;inset-block-start:0;z-index:9999;all:unset;font-family:monospace}.et-qd-header{background-color:#00ffa1;padding:12px;display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center}.et-qd-header__title{color:#25292e;margin:0;font-size:22px}.et-qd-header__select{display:grid;background-color:#25292e;border-radius:10px;overflow:hidden}.et-qd-header__select select{width:250px;color:#fff;padding:3px 10px 2px;border:none;background-color:#25292e;font-family:monospace}.et-qd-header__select label{padding:3px 10px;color:#25292e;font-size:12px;font-weight:700;color:#fff;border-bottom:1px solid gray}.et-qd-sub-header{background-color:#00b371;padding:12px;display:flex;gap:12px;flex-wrap:wrap}.et-qd-sub-header__divider{width:1px;background-color:#25292e}.et-qd-btn{background-color:#00ffa1;color:#25292e;border:none;padding:12px;cursor:pointer;font-size:14px;font-weight:700;text-align:center;text-decoration:none;display:inline-block;width:100%;transition:all .3s ease-in-out;font-family:monospace;border-radius:10px}.et-qd-btn:hover{background-color:#00cc81}.et-qd-btn.et-qd-btn-secondary{background-color:#25292e;color:#00ffa1}.et-qd-btn.et-qd-btn-secondary:hover{background-color:#3c424a}.et-qd-chip{background-color:#00ffa1;color:#25292e;border:none;padding:4px 9px;cursor:pointer;font-size:12px;font-weight:700;text-align:center;text-decoration:none;display:inline-block;transition:all .3s ease-in-out;font-family:monospace;border-radius:10px}.et-qd-chip:hover{background-color:#00cc81}.et-qd-chip.et-qd-chip-secondary{background-color:#25292e;color:#00ffa1}.et-qd-chip.et-qd-chip-secondary:hover{background-color:#3c424a}.et-qd-chip.et-qd-chip-bordered{border:1px solid rgb(0,255,161)}.et-qd-translucent-clickable{pointer-events:all}.et-qd-client{border-bottom:1px solid #3c424a;padding:12px}.et-qd-client__title{margin:0;font-size:14px;color:#fff;font-weight:700}.et-qd-client__actions{display:flex;gap:12px;margin-top:12px}.et-qd-queries{padding:12px;display:grid;grid-template-rows:auto auto minmax(0,1fr)}.et-qd-queries__actions{display:flex;gap:12px;margin-top:12px;margin-bottom:16px}.et-qd-queries__title{margin:0;font-size:14px;color:#fff;font-weight:700}.et-qd-queries__divider{width:1px;background-color:#3c424a}.et-qd-queries__browser{display:grid;grid-template-columns:1fr minmax(0,3fr);margin-left:-12px;margin-right:-12px;border-top:1px solid #3c424a;margin-bottom:-12px}.et-qd-queries__browser-list{border-right:1px solid #3c424a;list-style:none;padding:0;margin:0;display:grid;grid-auto-flow:row;grid-auto-rows:55px;overflow-y:auto;overflow-x:hidden}.et-qd-queries__browser-list li{margin:0}.et-qd-queries__browser-list li button{all:unset;font-family:monospace;box-sizing:border-box;width:100%;border:none;border-bottom:1px solid #3c424a;background-color:#25292e;color:#fff;text-align:left;padding:12px;height:100%;transition:all .3s ease-in-out;cursor:pointer;display:grid;gap:2px}.et-qd-queries__browser-list li button span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.et-qd-queries__browser-list li button span:first-of-type{font-weight:700}.et-qd-queries__browser-list li button:hover,.et-qd-queries__browser-list li button.et-qd-query--active{background-color:#30363c}.et-qd-queries__browser-detail{padding:12px;overflow-y:auto;overflow-x:hidden}.et-qd-queries__browser-detail__title{margin:0;font-size:14px;color:#fff;font-weight:700}.et-qd-list{display:grid;gap:12px}.et-qd-list-item{display:grid}.et-qd-list-item p{font-weight:700;margin:0;padding-bottom:8px}.et-qd-list-item p+p{margin-top:-8px}.et-qd-list-item div+pre{margin-top:8px}.et-qd-list-item pre{margin:0;padding:4px;background-color:#0e1012;border-radius:4px;max-height:450px;overflow:auto}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: NgFor, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: JsonPipe, name: "json" }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: QueryShortNamePipe, name: "queryShortName" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.ShadowDom }); }
1302
1667
  }
1303
1668
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: QueryDevtoolsComponent, decorators: [{
1304
1669
  type: Component,
1305
1670
  args: [{ selector: 'et-query-devtools', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.ShadowDom, host: {
1306
1671
  class: 'et-query-devtools',
1307
- }, imports: [NgIf, NgFor, AsyncPipe, JsonPipe], hostDirectives: [], template: "<button *ngIf=\"!isOpen()\" (click)=\"toggleOpen()\" class=\"et-query-devtools-open-btn\">Open Devtools</button>\n\n<div\n *ngIf=\"isOpen()\"\n [class.et-query-devtools-container--transparent]=\"isTranslucent()\"\n class=\"et-query-devtools-container et-query-devtools-container--snap-{{ snapLayout() }}\"\n>\n <button (click)=\"toggleOpen()\" class=\"et-query-devtools-close-btn\">Close Devtools</button>\n <button (click)=\"toggleTranslucent()\" class=\"et-query-devtools-translucent-btn\">Toggle Translucent</button>\n <button (click)=\"selectSnapLayout('full')\" class=\"et-query-devtools-snap-btn\">Snap full</button>\n <button (click)=\"selectSnapLayout('top')\" class=\"et-query-devtools-snap-btn\">Snap top</button>\n <button (click)=\"selectSnapLayout('bottom')\" class=\"et-query-devtools-snap-btn\">Snap bottom</button>\n <button (click)=\"selectSnapLayout('left')\" class=\"et-query-devtools-snap-btn\">Snap left</button>\n <button (click)=\"selectSnapLayout('right')\" class=\"et-query-devtools-snap-btn\">Snap right</button>\n\n <ng-container *ngIf=\"queryClientConfigs.length > 1\">\n <p>Client instance:</p>\n <ul>\n <li *ngFor=\"let clientConfig of queryClientConfigs; index as i; trackBy: trackByClient\">\n <button (click)=\"selectClient(i)\">\n {{ clientConfig.displayName ?? clientConfig.client.config.baseRoute }}\n </button>\n </li>\n </ul>\n </ng-container>\n\n <ng-container *ngIf=\"selectedClientConfig() as cfg\">\n <p>{{ cfg.displayName ?? cfg.client.config.baseRoute }}</p>\n\n <button (click)=\"cfg.client._store.refreshQueriesInUse()\">Force Refresh of in use queries</button> <br />\n <button (click)=\"cfg.client._store.refreshQueriesInUse({ ignoreCacheValidity: true })\">HARD Force Refresh</button>\n <br />\n <button (click)=\"cfg.client._store.refreshQueriesInUse({ ignoreCacheValidity: true, purgeUnused: true })\">\n PURGE & HARD Force Refresh\n </button>\n\n <br />\n <br />\n\n <button (click)=\"selectQueryListMode('live')\">Live</button>\n <button (click)=\"selectQueryListMode('history')\">History (last {{ queryHistory().length }} )</button>\n <button *ngIf=\"queryListMode() === 'history'\" (click)=\"clearQueryHistory()\">Clear</button>\n <ul *ngIf=\"queryListMode() === 'live'\">\n <li *ngFor=\"let query of queries(); trackBy: trackByQuery\">\n <button (click)=\"selectQuery(query)\">\n {{ query._routeWithParams }}\n </button>\n </li>\n </ul>\n\n <ul *ngIf=\"queryListMode() === 'history'\">\n <li *ngFor=\"let query of queryHistory(); trackBy: trackByQuery\">\n <button (click)=\"selectQuery(query)\">\n {{ query._routeWithParams }}\n </button>\n </li>\n </ul>\n </ng-container>\n\n <ng-container *ngIf=\"selectedQuery() as query\">\n <p>{{ query._routeWithParams }}</p>\n\n <button (click)=\"query.execute()\">Execute</button> <br />\n <button (click)=\"query.execute({ skipCache: true })\">FORCE Execute</button> <br />\n <button (click)=\"query.abort()\">Abort</button> <br />\n <button (click)=\"query.poll({ interval: 5000, takeUntil: _destroy$ })\">Poll</button> <br />\n <button (click)=\"query.stopPolling()\">Stop polling</button> <br />\n <button (click)=\"query.pausePolling()\">Pause polling</button> <br />\n <button (click)=\"query.resumePolling()\">Resume polling</button> <br />\n\n <div>\n <p>state type</p>\n <pre>{{ query.rawState.type }}</pre>\n </div>\n\n <div>\n <p>_enableSmartPolling:</p>\n <pre>{{ query._enableSmartPolling }}</pre>\n </div>\n\n <div>\n <p>isExpired:</p>\n <pre>{{ query.isExpired }}</pre>\n </div>\n\n <div>\n <p>isInUse:</p>\n <pre>{{ query.isInUse }}</pre>\n </div>\n\n <div>\n <p>_subscriberCount:</p>\n <pre>{{ query._subscriberCount }}</pre>\n </div>\n\n <div>\n <p>isPolling:</p>\n <pre>{{ query.isPolling }}</pre>\n </div>\n\n <div>\n <p>_arguments:</p>\n <pre>{{ query._arguments | json }}</pre>\n </div>\n\n <div>\n <p>state$:</p>\n <p><i> Warning, this will cause a subscription if shown </i></p>\n <button (click)=\"toggleShowResponse()\">Show/Hide</button>\n <pre *ngIf=\"showResponse()\">{{ query.state$ | async | json }}</pre>\n </div>\n\n <div>\n <p>rawState:</p>\n <button (click)=\"toggleShowRawResponse()\">Show/Hide</button>\n <pre *ngIf=\"showRawResponse()\">{{ query.rawState | json }}</pre>\n </div>\n </ng-container>\n</div>\n", styles: [".et-query-devtools-container{position:fixed;inset:0;background-color:#000;z-index:10000;overflow:auto}.et-query-devtools-container.et-query-devtools-container--transparent{opacity:.25;pointer-events:none}.et-query-devtools-container.et-query-devtools-container--transparent .et-query-devtools-translucent-btn{pointer-events:all}.et-query-devtools-container.et-query-devtools-container--snap-left{inset-inline-start:0;inset-block-start:0;inset-block-end:0;width:50%}.et-query-devtools-container.et-query-devtools-container--snap-right{inset-inline-start:unset;inset-inline-end:0;inset-block-start:0;inset-block-end:0;width:50%}.et-query-devtools-container.et-query-devtools-container--snap-top{inset-inline-start:0;inset-block-start:0;inset-inline-end:0;height:50%}.et-query-devtools-container.et-query-devtools-container--snap-bottom{inset-inline-start:0;inset-block-start:unset;inset-block-end:0;inset-inline-end:0;height:50%}.et-query-devtools-open-btn{position:fixed;inset-inline-start:0;inset-block-start:0;z-index:9999}\n"] }]
1672
+ }, imports: [NgIf, NgFor, AsyncPipe, JsonPipe, ReactiveFormsModule, NgClass, QueryShortNamePipe], hostDirectives: [], template: "<button *ngIf=\"!isOpen()\" (click)=\"toggleOpen()\" class=\"et-query-devtools-open-btn et-qd-chip\">Open Devtools</button>\n\n<div\n *ngIf=\"isOpen()\"\n [class.et-query-devtools-container--transparent]=\"isTranslucent()\"\n class=\"et-query-devtools-container et-query-devtools-container--snap-{{ snapLayout() }}\"\n>\n <header class=\"et-qd-header\">\n <h3 class=\"et-qd-header__title\">Query Devtools</h3>\n\n <div class=\"et-qd-header__select\">\n <label for=\"et-query-client-instance\">Client instance</label>\n <select [formControl]=\"selectedClientIdCtrl\" id=\"et-query-client-instance\">\n <option *ngFor=\"let clientConfig of queryClientConfigs; index as i; trackBy: trackByClient\" [value]=\"i\">\n {{ clientConfig.displayName ?? clientConfig.client.config.baseRoute }}\n </option>\n </select>\n </div>\n\n <button (click)=\"toggleOpen()\" class=\"et-qd-btn et-qd-btn-secondary\">Close</button>\n </header>\n\n <div class=\"et-qd-sub-header\">\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !isTranslucent() }\"\n (click)=\"toggleTranslucent()\"\n class=\"et-qd-chip et-qd-translucent-clickable\"\n >\n Translucent\n </button>\n <div class=\"et-qd-sub-header__divider\"></div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'full' }\"\n (click)=\"selectSnapLayout('full')\"\n class=\"et-qd-chip\"\n >\n Fullscreen\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'top' }\"\n (click)=\"selectSnapLayout('top')\"\n class=\"et-qd-chip\"\n >\n Top\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'bottom' }\"\n (click)=\"selectSnapLayout('bottom')\"\n class=\"et-qd-chip\"\n >\n Bottom\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'left' }\"\n (click)=\"selectSnapLayout('left')\"\n class=\"et-qd-chip\"\n >\n Left\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': snapLayout() !== 'right' }\"\n (click)=\"selectSnapLayout('right')\"\n class=\"et-qd-chip\"\n >\n Right\n </button>\n </div>\n\n <div *ngIf=\"selectedClientConfig() as cfg\" class=\"et-qd-client\">\n <p class=\"et-qd-client__title\">{{ cfg.displayName ?? cfg.client.config.baseRoute }}</p>\n\n <div class=\"et-qd-client__actions\">\n <button (click)=\"cfg.client._store.refreshQueriesInUse()\" class=\"et-qd-chip\">Refresh all</button>\n <button (click)=\"cfg.client._store.refreshQueriesInUse({ ignoreCacheValidity: true })\" class=\"et-qd-chip\">\n Refresh all (ignore caches)\n </button>\n <button\n (click)=\"cfg.client._store.refreshQueriesInUse({ ignoreCacheValidity: true, purgeUnused: true })\"\n class=\"et-qd-chip\"\n >\n Refresh all (ignore caches & purge unused)\n </button>\n </div>\n </div>\n\n <div *ngIf=\"selectedClientConfig() as cfg\" class=\"et-qd-queries\">\n <p class=\"et-qd-queries__title\">Query browser</p>\n <div class=\"et-qd-queries__actions\">\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': queryListMode() !== 'live' }\"\n (click)=\"selectQueryListMode('live')\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Live ({{ queries()?.length ?? 0 }})\n </button>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': queryListMode() !== 'history' }\"\n (click)=\"selectQueryListMode('history')\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n History ({{ queryHistory().length }}/50)\n </button>\n <ng-container *ngIf=\"queryListMode() === 'history'\">\n <div class=\"et-qd-queries__divider\"></div>\n <button (click)=\"clearQueryHistory()\" class=\"et-qd-chip et-qd-chip-bordered\">Clear history</button>\n </ng-container>\n </div>\n\n <div class=\"et-qd-queries__browser\">\n <ul *ngIf=\"queryListMode() === 'live'\" class=\"et-qd-queries__browser-list\">\n <li *ngFor=\"let query of queries(); trackBy: trackByQuery\">\n <button [class.et-qd-query--active]=\"selectedQuery()?._id === query._id\" (click)=\"selectQuery(query)\">\n <span> {{ query._queryConfig.method }}</span>\n <span> {{ query | queryShortName }}</span>\n </button>\n </li>\n </ul>\n <ul *ngIf=\"queryListMode() === 'history'\" class=\"et-qd-queries__browser-list\">\n <li *ngFor=\"let query of queryHistory(); trackBy: trackByQuery\">\n <button [class.et-qd-query--active]=\"selectedQuery()?._id === query._id\" (click)=\"selectQuery(query)\">\n <span> {{ query._queryConfig.method }}</span>\n <span> {{ query | queryShortName }}</span>\n </button>\n </li>\n </ul>\n <div class=\"et-qd-queries__browser-detail\">\n <ng-container *ngIf=\"selectedQuery() as query\">\n <p class=\"et-qd-queries__browser-detail__title\">\n {{ query._queryConfig.method }} {{ query._routeWithParams }}\n </p>\n\n <div class=\"et-qd-queries__actions\">\n <button (click)=\"query.execute()\" class=\"et-qd-chip et-qd-chip-bordered\">Execute</button>\n <button (click)=\"query.execute({ skipCache: true })\" class=\"et-qd-chip et-qd-chip-bordered\">\n Execute (ignore cache)\n </button>\n\n <button (click)=\"query.abort()\" class=\"et-qd-chip et-qd-chip-bordered\">Abort</button>\n <div class=\"et-qd-queries__divider\"></div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !query.isPolling }\"\n (click)=\"query.poll({ interval: 5000, takeUntil: _destroy$ })\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Poll\n </button>\n\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': query.isPolling }\"\n (click)=\"query.stopPolling()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Stop polling\n </button>\n <ng-container *ngIf=\"query.isPolling || query._isPollingPaused\">\n <button (click)=\"query.pausePolling()\" class=\"et-qd-chip et-qd-chip-secondary et-qd-chip-bordered\">\n Pause polling\n </button>\n <button (click)=\"query.resumePolling()\" class=\"et-qd-chip et-qd-chip-secondary et-qd-chip-bordered\">\n Resume polling\n </button>\n </ng-container>\n </div>\n\n <div class=\"et-qd-list\">\n <div class=\"et-qd-list-item\">\n <p>State type</p>\n <pre>{{ query.rawState.type }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Is expired</p>\n <pre>{{ query.isExpired }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Is in use</p>\n <pre>{{ query.isInUse }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Subscriber count</p>\n <pre>{{ query._subscriberCount }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Is polling</p>\n <pre>{{ query.isPolling }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Is polling paused</p>\n <pre>{{ query._isPollingPaused }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Enable smart polling</p>\n <pre>{{ query._enableSmartPolling }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Arguments</p>\n\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showArguments() }\"\n (click)=\"toggleShowArguments()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showArguments()\">{{ query._arguments | json }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>State</p>\n <p><i> Warning, this will cause a subscription if shown!</i></p>\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showResponse() }\"\n (click)=\"toggleShowResponse()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showResponse()\">{{ query.state$ | async | json }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Raw state</p>\n <p><i> The query state unaffected by entity stores</i></p>\n\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showRawResponse() }\"\n (click)=\"toggleShowRawResponse()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showRawResponse()\">{{ query.rawState | json }}</pre>\n </div>\n\n <div class=\"et-qd-list-item\">\n <p>Query config</p>\n\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showQueryConfig() }\"\n (click)=\"toggleShowQueryConfig()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showQueryConfig()\">{{ stringifiedQueryConfig() | json }}</pre>\n </div>\n\n <div *ngIf=\"!!query._queryConfig.entity\" class=\"et-qd-list-item\">\n <p>Entity store value</p>\n\n <div>\n <button\n [ngClass]=\"{ 'et-qd-chip-secondary': !showEntityStoreValue() }\"\n (click)=\"toggleShowEntityStoreValue()\"\n class=\"et-qd-chip et-qd-chip-bordered\"\n >\n Toggle\n </button>\n </div>\n <pre *ngIf=\"showEntityStoreValue()\">{{ selectedQueryEntityStoreValue$ | async | json }}</pre>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n</div>\n", styles: [".et-query-devtools{position:fixed;top:0;z-index:1000000;font-size:14px}.et-query-devtools-container{position:fixed;inset:0;background-color:#25292e;z-index:10000;overflow:auto;font-family:monospace;display:grid;grid-template-rows:auto auto auto minmax(0,1fr)}.et-query-devtools-container.et-query-devtools-container--transparent{opacity:.25;pointer-events:none}.et-query-devtools-container.et-query-devtools-container--transparent .et-query-devtools-translucent-btn{pointer-events:all}.et-query-devtools-container.et-query-devtools-container--snap-left{inset-inline-start:0;inset-block-start:0;inset-block-end:0;width:50%;border-right:1px solid rgb(37,41,46)}.et-query-devtools-container.et-query-devtools-container--snap-right{inset-inline-start:unset;inset-inline-end:0;inset-block-start:0;inset-block-end:0;width:50%;border-left:1px solid rgb(37,41,46)}.et-query-devtools-container.et-query-devtools-container--snap-top{inset-inline-start:0;inset-block-start:0;inset-inline-end:0;height:50%;border-bottom:1px solid rgb(37,41,46)}.et-query-devtools-container.et-query-devtools-container--snap-bottom{inset-inline-start:0;inset-block-start:unset;inset-block-end:0;inset-inline-end:0;height:50%;border-top:1px solid rgb(37,41,46)}.et-query-devtools-open-btn{position:fixed;inset-inline-start:0;inset-block-start:0;z-index:9999;all:unset;font-family:monospace}.et-qd-header{background-color:#00ffa1;padding:12px;display:grid;grid-template-columns:1fr auto auto;gap:12px;align-items:center}.et-qd-header__title{color:#25292e;margin:0;font-size:22px}.et-qd-header__select{display:grid;background-color:#25292e;border-radius:10px;overflow:hidden}.et-qd-header__select select{width:250px;color:#fff;padding:3px 10px 2px;border:none;background-color:#25292e;font-family:monospace}.et-qd-header__select label{padding:3px 10px;color:#25292e;font-size:12px;font-weight:700;color:#fff;border-bottom:1px solid gray}.et-qd-sub-header{background-color:#00b371;padding:12px;display:flex;gap:12px;flex-wrap:wrap}.et-qd-sub-header__divider{width:1px;background-color:#25292e}.et-qd-btn{background-color:#00ffa1;color:#25292e;border:none;padding:12px;cursor:pointer;font-size:14px;font-weight:700;text-align:center;text-decoration:none;display:inline-block;width:100%;transition:all .3s ease-in-out;font-family:monospace;border-radius:10px}.et-qd-btn:hover{background-color:#00cc81}.et-qd-btn.et-qd-btn-secondary{background-color:#25292e;color:#00ffa1}.et-qd-btn.et-qd-btn-secondary:hover{background-color:#3c424a}.et-qd-chip{background-color:#00ffa1;color:#25292e;border:none;padding:4px 9px;cursor:pointer;font-size:12px;font-weight:700;text-align:center;text-decoration:none;display:inline-block;transition:all .3s ease-in-out;font-family:monospace;border-radius:10px}.et-qd-chip:hover{background-color:#00cc81}.et-qd-chip.et-qd-chip-secondary{background-color:#25292e;color:#00ffa1}.et-qd-chip.et-qd-chip-secondary:hover{background-color:#3c424a}.et-qd-chip.et-qd-chip-bordered{border:1px solid rgb(0,255,161)}.et-qd-translucent-clickable{pointer-events:all}.et-qd-client{border-bottom:1px solid #3c424a;padding:12px}.et-qd-client__title{margin:0;font-size:14px;color:#fff;font-weight:700}.et-qd-client__actions{display:flex;gap:12px;margin-top:12px}.et-qd-queries{padding:12px;display:grid;grid-template-rows:auto auto minmax(0,1fr)}.et-qd-queries__actions{display:flex;gap:12px;margin-top:12px;margin-bottom:16px}.et-qd-queries__title{margin:0;font-size:14px;color:#fff;font-weight:700}.et-qd-queries__divider{width:1px;background-color:#3c424a}.et-qd-queries__browser{display:grid;grid-template-columns:1fr minmax(0,3fr);margin-left:-12px;margin-right:-12px;border-top:1px solid #3c424a;margin-bottom:-12px}.et-qd-queries__browser-list{border-right:1px solid #3c424a;list-style:none;padding:0;margin:0;display:grid;grid-auto-flow:row;grid-auto-rows:55px;overflow-y:auto;overflow-x:hidden}.et-qd-queries__browser-list li{margin:0}.et-qd-queries__browser-list li button{all:unset;font-family:monospace;box-sizing:border-box;width:100%;border:none;border-bottom:1px solid #3c424a;background-color:#25292e;color:#fff;text-align:left;padding:12px;height:100%;transition:all .3s ease-in-out;cursor:pointer;display:grid;gap:2px}.et-qd-queries__browser-list li button span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.et-qd-queries__browser-list li button span:first-of-type{font-weight:700}.et-qd-queries__browser-list li button:hover,.et-qd-queries__browser-list li button.et-qd-query--active{background-color:#30363c}.et-qd-queries__browser-detail{padding:12px;overflow-y:auto;overflow-x:hidden}.et-qd-queries__browser-detail__title{margin:0;font-size:14px;color:#fff;font-weight:700}.et-qd-list{display:grid;gap:12px}.et-qd-list-item{display:grid}.et-qd-list-item p{font-weight:700;margin:0;padding-bottom:8px}.et-qd-list-item p+p{margin-top:-8px}.et-qd-list-item div+pre{margin-top:8px}.et-qd-list-item pre{margin:0;padding:4px;background-color:#0e1012;border-radius:4px;max-height:450px;overflow:auto}\n"] }]
1308
1673
  }], ctorParameters: function () { return []; } });
1309
1674
 
1310
1675
  class QueryDirective {
@@ -1734,7 +2099,7 @@ class InfinityQueryDirective {
1734
2099
  this._data$.next([]);
1735
2100
  }
1736
2101
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: InfinityQueryDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
1737
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.1.6", type: InfinityQueryDirective, isStandalone: true, selector: "[etInfinityQuery]", inputs: { infinityQuery: ["etInfinityQuery", "infinityQuery"] }, providers: [{ provide: INFINITY_QUERY_TOKEN, useExisting: InfinityQueryDirective }], exportAs: ["etInfinityQuery"], hostDirectives: [{ directive: i1.DelayableDirective }], ngImport: i0 }); }
2102
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.1.6", type: InfinityQueryDirective, isStandalone: true, selector: "[etInfinityQuery]", inputs: { infinityQuery: ["etInfinityQuery", "infinityQuery"] }, providers: [{ provide: INFINITY_QUERY_TOKEN, useExisting: InfinityQueryDirective }], exportAs: ["etInfinityQuery"], hostDirectives: [{ directive: i1$1.DelayableDirective }], ngImport: i0 }); }
1738
2103
  }
1739
2104
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.1.6", ngImport: i0, type: InfinityQueryDirective, decorators: [{
1740
2105
  type: Directive,
@@ -1814,7 +2179,13 @@ function flatten(arr) {
1814
2179
  class EntityStore {
1815
2180
  constructor(_config) {
1816
2181
  this._config = _config;
2182
+ /**
2183
+ * @internal
2184
+ */
1817
2185
  this._dictionary = new Map();
2186
+ /**
2187
+ * @internal
2188
+ */
1818
2189
  this._change$ = new Subject();
1819
2190
  }
1820
2191
  select(keyOrKeys) {
@@ -1945,261 +2316,6 @@ const removeFrom = (store, { where, id }) => {
1945
2316
  .subscribe();
1946
2317
  };
1947
2318
 
1948
- class QueryCreator {
1949
- constructor(_queryConfig, _client, _store) {
1950
- this._queryConfig = _queryConfig;
1951
- this._client = _client;
1952
- this._store = _store;
1953
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1954
- // @ts-ignore
1955
- this.prepare = (args) => {
1956
- const route = buildRoute({
1957
- base: this._client.config.baseRoute,
1958
- route: this._queryConfig.route,
1959
- pathParams: args?.pathParams,
1960
- queryParams: computeQueryQueryParams({ config: this._queryConfig, client: this._client, args }),
1961
- queryParamConfig: this._client.config.request?.queryParams,
1962
- });
1963
- const cacheKey = isGqlQueryConfig(this._queryConfig) ? buildGqlCacheKey(this._queryConfig, args) : route;
1964
- if (shouldCacheQuery(this._queryConfig.method)) {
1965
- const existingQuery = this._store.get(cacheKey);
1966
- if (existingQuery) {
1967
- return existingQuery;
1968
- }
1969
- }
1970
- const query = new Query(this._client, this._queryConfig, route, args ?? {});
1971
- if (shouldCacheQuery(this._queryConfig.method)) {
1972
- this._store.add(cacheKey, query);
1973
- }
1974
- return query;
1975
- };
1976
- this.behaviorSubject = (initialValue) => new BehaviorSubject(initialValue ?? null);
1977
- }
1978
- }
1979
-
1980
- class QueryStore {
1981
- constructor(_config) {
1982
- this._config = _config;
1983
- /**
1984
- * @internal
1985
- */
1986
- this._store = new Map();
1987
- this._garbageCollector = null;
1988
- this._isInLowResourceMode = false;
1989
- this._lastBlurTimestamp = Date.now();
1990
- this._storeChange$ = new Subject();
1991
- this.storeChange$ = this._storeChange$.asObservable();
1992
- this._initSmartQueryHandling();
1993
- }
1994
- add(id, query) {
1995
- this._store.set(id, query);
1996
- this._initGarbageCollector();
1997
- this._logState(id, query, 'SET');
1998
- this._storeChange$.next(id);
1999
- }
2000
- get(id) {
2001
- return this._store.get(id) ?? null;
2002
- }
2003
- remove(id) {
2004
- this._store.delete(id);
2005
- this._logState(id, null, 'REMOVE');
2006
- this._storeChange$.next(id);
2007
- }
2008
- forEach(callback) {
2009
- for (const [key, query] of this._store) {
2010
- callback(query, key);
2011
- }
2012
- }
2013
- refreshQueriesInUse(config) {
2014
- const { ignoreCacheValidity, purgeUnused } = config ?? {};
2015
- for (const [key, query] of this._store) {
2016
- if (query.isInUse &&
2017
- (query.isExpired || ignoreCacheValidity) &&
2018
- query.autoRefreshOnConfig.queryClientDefaultHeadersChange) {
2019
- query.execute({ skipCache: true, _triggeredVia: 'auto' });
2020
- }
2021
- else if (purgeUnused && !query.isInUse) {
2022
- this.remove(key);
2023
- }
2024
- }
2025
- }
2026
- _initSmartQueryHandling() {
2027
- const windowBlur$ = fromEvent(window, 'blur');
2028
- const windowFocus$ = fromEvent(window, 'focus');
2029
- windowBlur$.subscribe(() => {
2030
- timer(5000)
2031
- .pipe(takeUntil(windowFocus$), take(1))
2032
- .subscribe(() => {
2033
- this._lastBlurTimestamp = Date.now();
2034
- this._isInLowResourceMode = true;
2035
- this._stopGarbageCollector();
2036
- if (this._config?.enableSmartPolling) {
2037
- this.forEach((query) => {
2038
- if (!query.isPolling || !query._enableSmartPolling) {
2039
- return;
2040
- }
2041
- query.pausePolling();
2042
- });
2043
- }
2044
- });
2045
- });
2046
- windowFocus$.subscribe(() => {
2047
- if (!this._isInLowResourceMode) {
2048
- return;
2049
- }
2050
- this._isInLowResourceMode = false;
2051
- if (this._config?.enableSmartPolling || this._config?.autoRefreshQueriesOnWindowFocus) {
2052
- this.forEach((query) => {
2053
- if (this._config?.enableSmartPolling && query._isPollingPaused) {
2054
- query.resumePolling();
2055
- }
2056
- if (Date.now() - this._lastBlurTimestamp > 15000) {
2057
- if (this._config?.autoRefreshQueriesOnWindowFocus &&
2058
- query.isExpired &&
2059
- query.isInUse &&
2060
- query.autoRefreshOnConfig.windowFocus) {
2061
- query.execute({ skipCache: true, _triggeredVia: 'auto' });
2062
- }
2063
- }
2064
- });
2065
- }
2066
- this._initGarbageCollector();
2067
- });
2068
- }
2069
- _logState(key, item, operation) {
2070
- if (!this._config?.enableChangeLogging)
2071
- return;
2072
- const stateAsJson = {};
2073
- this._store.forEach((value, key) => {
2074
- stateAsJson[key] = value;
2075
- });
2076
- console.log(`%c[${operation}] ${key}`, 'font-weight: bold');
2077
- console.log({ operation, key, item });
2078
- console.log(stateAsJson);
2079
- }
2080
- _initGarbageCollector() {
2081
- if (this._garbageCollector !== null) {
2082
- return;
2083
- }
2084
- this._logGarbageCollector('Start');
2085
- this._garbageCollector = window.setInterval(() => {
2086
- this._runGarbageCollector();
2087
- }, 15000);
2088
- }
2089
- _stopGarbageCollector() {
2090
- if (this._garbageCollector !== null) {
2091
- window.clearInterval(this._garbageCollector);
2092
- this._garbageCollector = null;
2093
- this._logGarbageCollector('Stop');
2094
- }
2095
- }
2096
- _runGarbageCollector() {
2097
- this._logGarbageCollector('Collecting...');
2098
- this._store.forEach((item, key) => {
2099
- if (item.isExpired && !item.isInUse) {
2100
- this.remove(key);
2101
- }
2102
- });
2103
- this._logGarbageCollector('Collection done');
2104
- if (!this._store.size) {
2105
- this._stopGarbageCollector();
2106
- }
2107
- }
2108
- _logGarbageCollector(action) {
2109
- if (!this._config?.enableGarbageCollectorLogging)
2110
- return;
2111
- console.log(`%cGC: ${action}`, 'color: yellow; font-weight: bold');
2112
- }
2113
- }
2114
-
2115
- class QueryClient {
2116
- get config() {
2117
- return this._clientConfig;
2118
- }
2119
- get authProvider() {
2120
- return this._authProvider$.getValue() ?? this._clientConfig.parent?._authProvider$.getValue() ?? null;
2121
- }
2122
- get authProvider$() {
2123
- return this._authProvider$.asObservable() ?? this._clientConfig.parent?._authProvider$.asObservable() ?? null;
2124
- }
2125
- constructor(_clientConfig) {
2126
- this._clientConfig = _clientConfig;
2127
- this._authProvider$ = new BehaviorSubject(null);
2128
- this.get = (queryConfig) => this.fetch({
2129
- ...queryConfig,
2130
- method: 'GET',
2131
- });
2132
- this.post = (queryConfig) => this.fetch({
2133
- ...queryConfig,
2134
- method: 'POST',
2135
- });
2136
- this.put = (queryConfig) => this.fetch({
2137
- ...queryConfig,
2138
- method: 'PUT',
2139
- });
2140
- this.patch = (queryConfig) => this.fetch({
2141
- ...queryConfig,
2142
- method: 'PATCH',
2143
- });
2144
- this.delete = (queryConfig) => this.fetch({
2145
- ...queryConfig,
2146
- method: 'DELETE',
2147
- });
2148
- this.gqlQuery = (queryConfig) => this.fetch({
2149
- ...queryConfig,
2150
- method: 'GQL_QUERY',
2151
- });
2152
- this.gqlMutate = (queryConfig) => this.fetch({
2153
- ...queryConfig,
2154
- method: 'GQL_MUTATE',
2155
- });
2156
- this.fetch = (queryConfig) => new QueryCreator(queryConfig, this, this._store);
2157
- this.setAuthProvider = (authProvider) => {
2158
- if (this.authProvider) {
2159
- throw new Error('The auth provider is already set. Please call clearAuthProvider() first.');
2160
- }
2161
- this._authProvider$.next(authProvider);
2162
- };
2163
- this.setDefaultHeaders = (config) => {
2164
- if (!this._clientConfig.request) {
2165
- this._clientConfig.request = {
2166
- headers: config.headers ?? undefined,
2167
- };
2168
- }
2169
- else {
2170
- this._clientConfig.request.headers = config.headers ?? undefined;
2171
- }
2172
- if (config.refreshQueriesInUse) {
2173
- this._store.refreshQueriesInUse({ purgeUnused: true, ignoreCacheValidity: true });
2174
- }
2175
- };
2176
- this.clearAuthProvider = () => {
2177
- this.authProvider?.cleanUp();
2178
- this._authProvider$.next(null);
2179
- };
2180
- this._store = new QueryStore({
2181
- enableChangeLogging: _clientConfig.logging?.queryStateChanges,
2182
- enableGarbageCollectorLogging: _clientConfig.logging?.queryStateGarbageCollector,
2183
- autoRefreshQueriesOnWindowFocus: _clientConfig.request?.autoRefreshQueriesOnWindowFocus ?? true,
2184
- enableSmartPolling: _clientConfig.request?.enableSmartPolling ?? true,
2185
- });
2186
- }
2187
- }
2188
-
2189
- const shouldCacheQuery = (method) => {
2190
- return method === 'GET' || method === 'OPTIONS' || method === 'HEAD' || method === 'GQL_QUERY';
2191
- };
2192
- const buildGqlCacheKey = (config, args) => {
2193
- const { query } = config;
2194
- const variables = args?.variables || {};
2195
- const queryWithoutNewLinesAndWhitespace = query.replace(/\s/g, '');
2196
- const queryFirst50Chars = queryWithoutNewLinesAndWhitespace.slice(0, 50);
2197
- const queryMiddle25Chars = queryWithoutNewLinesAndWhitespace.slice(queryWithoutNewLinesAndWhitespace.length / 2 - 25, queryWithoutNewLinesAndWhitespace.length / 2 + 25);
2198
- const queryLast50Chars = queryWithoutNewLinesAndWhitespace.slice(-50);
2199
- const vars = JSON.stringify(variables);
2200
- return `${queryFirst50Chars}...${queryMiddle25Chars}...${queryLast50Chars}...${vars}`;
2201
- };
2202
-
2203
2319
  const ET_ARR_PREFIX = 'ET_ARR__';
2204
2320
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2205
2321
  class QueryFormGroup extends FormGroup {
@@ -2560,5 +2676,5 @@ const def = () => null;
2560
2676
  * Generated bundle index. Do not edit.
2561
2677
  */
2562
2678
 
2563
- export { BasicAuthProvider, BearerAuthProvider, CustomHeaderAuthProvider, EntityStore, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, QUERY_CLIENT_DEVTOOLS_TOKEN, Query, QueryClient, QueryCreator, QueryDevtoolsComponent, QueryDirective, QueryError, QueryField, QueryForm, QueryFormGroup, QueryStore, buildErrorMessage, buildGqlCacheKey, buildQueryString, buildRoute, buildTimestampFromSeconds, castQueryCreatorTypes, computeQueryAuthHeader, computeQueryBody, computeQueryHeaders, computeQueryMethod, computeQueryQueryParams, createInfinityQueryConfig, createQueryCollection, decryptBearer, deepFreeze, def, detectContentTypeHeader, extractExpiresInSeconds, extractQuery, filterFailure, filterNull, filterQueryStates, filterSuccess, forEachHeader, getDefaultHeaders, getResponseUrl, gql, hasHeader, ignoreAutoRefresh, insertFrom, invalidBaseRouteError, invalidBodyError, invalidRouteError, isAutoRefresh, isBasicAuthProvider, isBearerAuthProvider, isClassValidatorError, isCustomHeaderAuthProvider, isEmptyString, isGqlQueryConfig, isNaN$1 as isNaN, isQuery, isQueryCollection, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, isSymfonyError, isSymfonyFormViolationListError, isSymfonyPagerfantaOutOfRangeError, mapToPaginated, mergeHeaders, paginatedEntityValueUpdater, parseAllXhrResponseHeaders, pathParamsMissingInRouteFunctionError, provideQueryClientForDevtools, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, removeFrom, request, resetPageOnError, serializeBody, shouldCacheQuery, shouldRetryRequest, skipPaginationPageParamCalculator, switchQueryCollectionState, switchQueryState, takeUntilResponse, transformGql, transformMethod, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray };
2679
+ export { BasicAuthProvider, BearerAuthProvider, CustomHeaderAuthProvider, EntityStore, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, QUERY_CLIENT_DEVTOOLS_TOKEN, Query, QueryClient, QueryCreator, QueryDevtoolsComponent, QueryDirective, QueryError, QueryField, QueryForm, QueryFormGroup, QueryShortNamePipe, QueryStore, buildErrorMessage, buildGqlCacheKey, buildQueryString, buildRoute, buildTimestampFromSeconds, castQueryCreatorTypes, computeQueryAuthHeader, computeQueryBody, computeQueryHeaders, computeQueryMethod, computeQueryQueryParams, createInfinityQueryConfig, createQueryCollection, decryptBearer, deepFreeze, def, detectContentTypeHeader, extractExpiresInSeconds, extractQuery, filterFailure, filterNull, filterQueryStates, filterSuccess, forEachHeader, getDefaultHeaders, getResponseUrl, gql, hasHeader, ignoreAutoRefresh, insertFrom, invalidBaseRouteError, invalidBodyError, invalidRouteError, isAutoRefresh, isBasicAuthProvider, isBearerAuthProvider, isClassValidatorError, isCustomHeaderAuthProvider, isEmptyString, isGqlQueryConfig, isNaN$1 as isNaN, isQuery, isQueryCollection, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, isSymfonyError, isSymfonyFormViolationListError, isSymfonyPagerfantaOutOfRangeError, mapToPaginated, mergeHeaders, paginatedEntityValueUpdater, parseAllXhrResponseHeaders, pathParamsMissingInRouteFunctionError, provideQueryClientForDevtools, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, removeFrom, request, resetPageOnError, serializeBody, shouldCacheQuery, shouldRetryRequest, skipPaginationPageParamCalculator, switchQueryCollectionState, switchQueryState, takeUntilResponse, transformGql, transformMethod, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray };
2564
2680
  //# sourceMappingURL=ethlete-query.mjs.map