@ethlete/query 6.0.0-next.46 → 6.0.0-next.49

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,9 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injector, effect, DestroyRef, computed, InjectionToken, EnvironmentInjector, ElementRef, ErrorHandler, createEnvironmentInjector, untracked, signal, linkedSignal, runInInjectionContext, isDevMode, PLATFORM_ID, resource, DOCUMENT, VERSION, provideEnvironmentInitializer, afterNextRender, makeEnvironmentProviders, assertInInjectionContext, ChangeDetectorRef, ViewContainerRef, TemplateRef, Input, Directive, input, Pipe, ViewEncapsulation, Component, NgZone } from '@angular/core';
2
+ import { inject, ApplicationRef, untracked, effect, Injector, runInInjectionContext, DestroyRef, computed, InjectionToken, EnvironmentInjector, ElementRef, ErrorHandler, createEnvironmentInjector, signal, linkedSignal, isDevMode, PLATFORM_ID, resource, DOCUMENT, VERSION, provideEnvironmentInitializer, afterNextRender, makeEnvironmentProviders, assertInInjectionContext, ChangeDetectorRef, ViewContainerRef, TemplateRef, Input, Directive, input, Pipe, ViewEncapsulation, Component, NgZone } from '@angular/core';
3
3
  import { UrlTree, Router, RedirectCommand, ActivatedRoute } from '@angular/router';
4
4
  import { Observable, defer, from, filter, Subject, Subscription, throwError, of, timer, switchMap, tap, retry, catchError, EMPTY as EMPTY$1, take, takeUntil, finalize, mergeMap, toArray, map, race, NEVER, merge, takeWhile, firstValueFrom, concatMap, fromEvent, throttleTime, interval, startWith, pairwise, BehaviorSubject, combineLatest, ReplaySubject, distinctUntilChanged, shareReplay, withLatestFrom, debounceTime } from 'rxjs';
5
5
  import { defineStaticProvider, RuntimeError, clamp, randomId, defineRootProvider, isObject, injectUnsavedChangesCoordinator, injectRoute, getCookie, setCookie, deleteCookie, CORE_VERSION, createDestroy, computedTillTruthy, defineProvider, toProvideFn, toInjectFn, nextFrame, injectQueryParams, injectQueryParamChanges, clone, equal, ET_PROPERTY_REMOVED, previousSignalValue } from '@ethlete/core';
6
- import { HttpClient, HttpErrorResponse, HttpEventType, HttpResponse, HttpHeaders, HttpStatusCode } from '@angular/common/http';
6
+ import { HttpClient, HttpErrorResponse, HttpEventType, HttpHeaders, HttpResponse, HttpStatusCode } from '@angular/common/http';
7
7
  import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
8
8
  import { getActiveConsumer, setActiveConsumer, SIGNAL } from '@angular/core/primitives/signals';
9
9
  import { isPlatformBrowser, NgClass, AsyncPipe, JsonPipe } from '@angular/common';
@@ -13,6 +13,7 @@ import { FormControl, ReactiveFormsModule, FormGroup } from '@angular/forms';
13
13
 
14
14
  const DEFAULT_RETURN_URL_PARAM = 'returnUrl';
15
15
  const DEFAULT_URL = '/';
16
+ const DEFAULT_RESTORE_TIMEOUT_MS = 10000;
16
17
  const resolveTarget = (router, target) => {
17
18
  if (target instanceof UrlTree)
18
19
  return target;
@@ -45,51 +46,137 @@ const readReturnUrl = (router, url, param) => {
45
46
  * ];
46
47
  * ```
47
48
  *
48
- * A guard pends while a session restore is in flight rather than redirecting against a session that
49
- * is about to exist, so a hard reload of a protected URL stays on that URL.
49
+ * A guard pends while a session restore is in flight, for up to `restoreTimeoutMs`, rather than
50
+ * redirecting against a session that is about to exist, so a hard reload of a protected URL stays on
51
+ * that URL.
50
52
  */
51
53
  const createAuthGuard = (providerRef, config) => {
52
54
  const param = config.returnUrlParam === false ? null : (config.returnUrlParam ?? DEFAULT_RETURN_URL_PARAM);
53
55
  const behavior = config.navigationBehaviorOptions ?? { replaceUrl: true };
54
- const guardFor = (requiresSession) => () => {
56
+ const restoreTimeoutMs = config.restoreTimeoutMs ?? DEFAULT_RESTORE_TIMEOUT_MS;
57
+ const sessionGuards = new Set();
58
+ const watchedRouters = new WeakSet();
59
+ const anonymousGuards = new Set();
60
+ const watchedStartRouters = new WeakSet();
61
+ const timedOutRestores = new WeakSet();
62
+ const loginRedirect = (router, returnUrl) => {
63
+ const loginTree = resolveTarget(router, config.loginUrl);
64
+ return param && returnUrl ? withQueryParam(loginTree, param, returnUrl) : loginTree;
65
+ };
66
+ const usesGuard = (route, guardSet) => {
67
+ const guards = [...(route.routeConfig?.canMatch ?? []), ...(route.routeConfig?.canActivate ?? [])];
68
+ return guards.some((guard) => guardSet.has(guard)) || route.children.some((child) => usesGuard(child, guardSet));
69
+ };
70
+ const watchSessionStart = (router, provider) => {
71
+ if (!config.redirectOnSessionStart || watchedStartRouters.has(router))
72
+ return;
73
+ watchedStartRouters.add(router);
74
+ const appRef = inject(ApplicationRef);
75
+ let wasAuthenticated = untracked(provider.sessionStatus) === 'authenticated';
76
+ effect(() => {
77
+ const isAuthenticated = provider.sessionStatus() === 'authenticated';
78
+ const started = isAuthenticated && !wasAuthenticated;
79
+ wasAuthenticated = isAuthenticated;
80
+ if (!started)
81
+ return;
82
+ untracked(() => {
83
+ if (!usesGuard(router.routerState.snapshot.root, anonymousGuards))
84
+ return;
85
+ const returnUrl = readReturnUrl(router, router.url, param);
86
+ void router.navigateByUrl(returnUrl ? router.parseUrl(returnUrl) : resolveTarget(router, config.defaultUrl ?? DEFAULT_URL), behavior);
87
+ });
88
+ }, { injector: appRef.injector });
89
+ };
90
+ const watchSessionEnd = (router, provider) => {
91
+ const causes = config.redirectOnSessionEnd;
92
+ if (!causes?.length || watchedRouters.has(router))
93
+ return;
94
+ watchedRouters.add(router);
95
+ const appRef = inject(ApplicationRef);
96
+ let handled = untracked(provider.executionState);
97
+ effect(() => {
98
+ const state = provider.executionState();
99
+ const cause = provider.sessionEndCause();
100
+ if (state === handled || state?.type !== 'logout')
101
+ return;
102
+ handled = state;
103
+ if (!cause || !causes.includes(cause))
104
+ return;
105
+ untracked(() => {
106
+ if (!usesGuard(router.routerState.snapshot.root, sessionGuards))
107
+ return;
108
+ void router.navigateByUrl(loginRedirect(router, cause === 'user' ? null : router.url), behavior);
109
+ });
110
+ }, { injector: appRef.injector });
111
+ };
112
+ const guardFor = (requiresSession, permission, options) => () => {
55
113
  const router = inject(Router);
56
114
  const injector = inject(Injector);
57
115
  const provider = providerRef.inject();
116
+ if (requiresSession)
117
+ watchSessionEnd(router, provider);
118
+ else
119
+ watchSessionStart(router, provider);
58
120
  // Captured before the wait below: once the session settles the router has moved on, and the URL
59
121
  // the visitor actually asked for is no longer reachable from it.
60
122
  const attemptedUrl = router.currentNavigation()?.extractedUrl.toString() ?? router.url;
123
+ // A restore a guard stopped waiting for is not waited for again, or the redirect to the login
124
+ // would wait out a second timeout on the login route's own guard.
61
125
  const settled = () => {
62
126
  const status = provider.sessionStatus();
63
- return status !== 'unknown' && status !== 'restoring';
127
+ if (status === 'unknown' || status === 'restoring')
128
+ return timedOutRestores.has(provider);
129
+ timedOutRestores.delete(provider);
130
+ return true;
64
131
  };
65
132
  const decide = () => {
66
133
  const authenticated = provider.sessionStatus() === 'authenticated';
67
- if (authenticated === requiresSession)
68
- return true;
69
- if (requiresSession) {
70
- const loginTree = resolveTarget(router, config.loginUrl);
71
- return new RedirectCommand(param ? withQueryParam(loginTree, param, attemptedUrl) : loginTree, behavior);
134
+ if (authenticated !== requiresSession) {
135
+ if (requiresSession)
136
+ return new RedirectCommand(loginRedirect(router, attemptedUrl), behavior);
137
+ const returnUrl = readReturnUrl(router, attemptedUrl, param);
138
+ return new RedirectCommand(returnUrl ? router.parseUrl(returnUrl) : resolveTarget(router, config.defaultUrl ?? DEFAULT_URL), behavior);
72
139
  }
73
- const returnUrl = readReturnUrl(router, attemptedUrl, param);
74
- return new RedirectCommand(returnUrl ? router.parseUrl(returnUrl) : resolveTarget(router, config.defaultUrl ?? DEFAULT_URL), behavior);
140
+ if (!permission || untracked(() => runInInjectionContext(injector, () => permission(provider))))
141
+ return true;
142
+ return new RedirectCommand(resolveTarget(router, options?.redirectTo ?? config.defaultUrl ?? DEFAULT_URL), behavior);
75
143
  };
76
144
  if (settled())
77
145
  return decide();
78
146
  return new Observable((subscriber) => {
79
- const watcher = effect(() => {
80
- if (!settled())
81
- return;
147
+ const finish = () => {
82
148
  subscriber.next(decide());
83
149
  subscriber.complete();
150
+ };
151
+ const timeout = restoreTimeoutMs === false
152
+ ? null
153
+ : setTimeout(() => {
154
+ timedOutRestores.add(provider);
155
+ finish();
156
+ }, restoreTimeoutMs);
157
+ const watcher = effect(() => {
158
+ if (settled())
159
+ untracked(finish);
84
160
  }, { ...(ngDevMode ? { debugName: "watcher" } : /* istanbul ignore next */ {}), injector });
85
- return () => watcher.destroy();
161
+ return () => {
162
+ watcher.destroy();
163
+ if (timeout !== null)
164
+ clearTimeout(timeout);
165
+ };
86
166
  });
87
167
  };
88
- const requireSession = guardFor(true);
168
+ const sessionGuard = (guard) => {
169
+ sessionGuards.add(guard);
170
+ return guard;
171
+ };
172
+ const requireSession = sessionGuard(guardFor(true));
89
173
  const requireAnonymous = guardFor(false);
174
+ anonymousGuards.add(requireAnonymous);
90
175
  return {
91
176
  canMatch: requireSession,
92
177
  canActivate: requireSession,
178
+ canMatchWith: (permission, options) => sessionGuard(guardFor(true, permission, options)),
179
+ canActivateWith: (permission, options) => sessionGuard(guardFor(true, permission, options)),
93
180
  canMatchAnonymous: requireAnonymous,
94
181
  canActivateAnonymous: requireAnonymous,
95
182
  returnUrl: () => {
@@ -397,6 +484,7 @@ const createQueryContext = (context) => defineStaticProvider(context, {
397
484
  /** @internal */
398
485
  const injectQueryContext = () => inject(QUERY_CONTEXT_TOKEN);
399
486
 
487
+ /** @internal */
400
488
  const setupQueryDependencies = (options) => {
401
489
  const hostInjector = options.queryConfig?.injector ?? inject(Injector);
402
490
  const environmentInjector = options.queryConfig?.injector?.get(EnvironmentInjector) ?? hostInjector.get(EnvironmentInjector);
@@ -469,7 +557,6 @@ const QueryRuntimeErrorCode = {
469
557
  // Query Stack
470
558
  QUERY_STACK_WITH_ARGS_USED: 500,
471
559
  QUERY_STACK_WITH_RESPONSE_UPDATE_USED: 501,
472
- QUERY_STACK_TOTAL_QUERIES_AND_EXPECTED_QUERIES_MISMATCH: 502,
473
560
  // GQL
474
561
  GQL_DATA_PROPERTY_MISSING_IN_RESPONSE: 600,
475
562
  GQL_ERRORS_IN_RESPONSE: 601,
@@ -552,9 +639,6 @@ const queryStackWithArgsUsed = () => {
552
639
  const queryStackWithResponseUpdateUsed = () => {
553
640
  return new RuntimeError(QueryRuntimeErrorCode.QUERY_STACK_WITH_RESPONSE_UPDATE_USED, `withResponseUpdate() has been used in a query stack or a paged query stack. This is not supported.`);
554
641
  };
555
- const queryStackTotalQueriesAndExpectedQueriesMismatch = (totalQueries, expectedQueries) => {
556
- return new RuntimeError(QueryRuntimeErrorCode.QUERY_STACK_TOTAL_QUERIES_AND_EXPECTED_QUERIES_MISMATCH, `The total queries "${totalQueries}" and the expected queries "${expectedQueries}" do not match. This usually happens if a query depends on the response of the previous query to calculate its pagination values. Set blockExecutionDuringLoading to true to prevent this.`);
557
- };
558
642
  const gqlDataPropertyMissingInResponse = () => {
559
643
  return new RuntimeError(QueryRuntimeErrorCode.GQL_DATA_PROPERTY_MISSING_IN_RESPONSE, `The GraphQL response is missing the required "data" property. Please add a custom transformResponse param to the query creator to handle this response format.`);
560
644
  };
@@ -700,6 +784,7 @@ const withArgs = (args) => {
700
784
  },
701
785
  });
702
786
  };
787
+ const readPollingInterval = (interval) => typeof interval === 'function' ? interval() : interval;
703
788
  /**
704
789
  * A query feature that will automatically execute the query at a given interval.
705
790
  * The interval will be cleared when the query is destroyed.
@@ -717,14 +802,21 @@ const withPolling = (options) => {
717
802
  return createQueryFeature({
718
803
  type: QueryFeatureType.WITH_POLLING,
719
804
  devtools: () => [
720
- { label: 'interval', value: formatQueryDevtoolsDuration(options.interval) },
805
+ { label: 'interval', value: formatQueryDevtoolsDuration(untracked(() => readPollingInterval(options.interval))) },
721
806
  { label: 'execute initially', value: options.executeInitially ? 'yes' : 'no' },
807
+ ...(options.pauseWhileHidden ? [{ label: 'pause while hidden', value: 'yes' }] : []),
808
+ ...(options.refetchOnFocus ? [{ label: 'refetch on focus', value: 'yes' }] : []),
809
+ ...(options.refetchOnReconnect ? [{ label: 'refetch on reconnect', value: 'yes' }] : []),
722
810
  ],
723
811
  fn: (context) => {
724
812
  if (!context.flags.shouldAutoExecuteMethod) {
725
813
  throw withPollingUsedOnUnsupportedHttpMethod(context.flags.method);
726
814
  }
727
815
  let intervalId = null;
816
+ let timeoutId = null;
817
+ let active = false;
818
+ let currentArgs = null;
819
+ let lastTickAt = 0;
728
820
  const sync = context.deps.client.subtle.sync;
729
821
  const lockManager = sync?.isPollingDedupeEnabled && context.flags.isMultiTabSyncEnabled ? sync.lockManager : null;
730
822
  // Only ever set while dedup is active. A tick runs unless we know another tab is taking care of
@@ -757,28 +849,98 @@ const withPolling = (options) => {
757
849
  hold?.release();
758
850
  });
759
851
  }
852
+ const currentInterval = () => untracked(() => readPollingInterval(options.interval));
853
+ const isPaused = () => options.pauseWhileHidden === true && document.hidden;
854
+ const stop = () => {
855
+ if (intervalId !== null)
856
+ clearInterval(intervalId);
857
+ if (timeoutId !== null)
858
+ clearTimeout(timeoutId);
859
+ intervalId = null;
860
+ timeoutId = null;
861
+ };
862
+ const tick = () => {
863
+ lastTickAt = Date.now();
864
+ // The interval keeps running while another tab is the holder, so leadership arriving
865
+ // mid-flight needs no restart and `executeInitially`/interval semantics stay identical.
866
+ if (hold && !hold.isHolder())
867
+ return;
868
+ context.execute({ args: currentArgs });
869
+ };
870
+ const schedule = (firstDelay) => {
871
+ stop();
872
+ if (!active || isPaused())
873
+ return;
874
+ const interval = currentInterval();
875
+ if (firstDelay >= interval) {
876
+ intervalId = window.setInterval(tick, interval);
877
+ return;
878
+ }
879
+ timeoutId = window.setTimeout(() => {
880
+ timeoutId = null;
881
+ tick();
882
+ intervalId = window.setInterval(tick, currentInterval());
883
+ }, Math.max(0, firstDelay));
884
+ };
885
+ const remainingDelay = () => lastTickAt + currentInterval() - Date.now();
760
886
  nestedEffect(() => {
761
887
  const args = context.state.args();
762
888
  untracked(() => {
763
- if (intervalId !== null)
764
- clearInterval(intervalId);
889
+ stop();
890
+ currentArgs = args;
765
891
  // Don't start polling if the query doesn't have args.
766
892
  // It should have args because a withArgs feature is present.
767
- if (args === null && context.flags.hasWithArgsFeature)
893
+ active = !(args === null && context.flags.hasWithArgsFeature);
894
+ if (!active)
768
895
  return;
769
- if (options.executeInitially && (!hold || hold.isHolder())) {
770
- context.execute({ args });
896
+ lastTickAt = Date.now();
897
+ if (options.executeInitially) {
898
+ if (isPaused())
899
+ lastTickAt -= currentInterval();
900
+ else if (!hold || hold.isHolder())
901
+ context.execute({ args });
771
902
  }
772
- intervalId = window.setInterval(() => {
773
- // The interval keeps running while another tab is the holder, so leadership arriving
774
- // mid-flight needs no restart and `executeInitially`/interval semantics stay identical.
775
- if (hold && !hold.isHolder())
776
- return;
777
- context.execute({ args });
778
- }, options.interval);
903
+ schedule(currentInterval());
779
904
  });
780
905
  }, { injector: context.deps.injector });
781
- context.deps.destroyRef.onDestroy(() => intervalId !== null && clearInterval(intervalId));
906
+ if (typeof options.interval === 'function') {
907
+ const interval = options.interval;
908
+ nestedEffect(() => {
909
+ interval();
910
+ untracked(() => {
911
+ if (intervalId === null && timeoutId === null)
912
+ return;
913
+ schedule(remainingDelay());
914
+ });
915
+ }, { injector: context.deps.injector });
916
+ }
917
+ const listen = (target, type, listener) => {
918
+ target.addEventListener(type, listener);
919
+ context.deps.destroyRef.onDestroy(() => target.removeEventListener(type, listener));
920
+ };
921
+ if (options.pauseWhileHidden) {
922
+ listen(document, 'visibilitychange', () => {
923
+ if (!active)
924
+ return;
925
+ if (document.hidden) {
926
+ stop();
927
+ }
928
+ else if (intervalId === null && timeoutId === null) {
929
+ schedule(remainingDelay());
930
+ }
931
+ });
932
+ }
933
+ const refetchNow = () => {
934
+ if (!active || isPaused())
935
+ return;
936
+ tick();
937
+ schedule(currentInterval());
938
+ };
939
+ if (options.refetchOnFocus)
940
+ listen(window, 'focus', refetchNow);
941
+ if (options.refetchOnReconnect)
942
+ listen(window, 'online', refetchNow);
943
+ context.deps.destroyRef.onDestroy(stop);
782
944
  },
783
945
  });
784
946
  };
@@ -1068,6 +1230,74 @@ const setDefaultQueryRetryFn = (fn) => {
1068
1230
  /** @internal */
1069
1231
  const runDefaultQueryRetry = (options) => defaultRetryFn$1?.(options) ?? NO_RETRY;
1070
1232
 
1233
+ const DEFAULT_MAX_ATTEMPTS = 3;
1234
+ const DEFAULT_BASE_DELAY_MS = 1000;
1235
+ const DEFAULT_MAX_DELAY_MS = 30_000;
1236
+ const DEFAULT_JITTER = 0.25;
1237
+ // 500 is left out on purpose: an internal server error is a bug in the backend far more often than a
1238
+ // blip, and repeating the request that triggered it does not make it go away.
1239
+ const isRetryableByDefault = (status) => status === 0 || status === 408 || status === 425 || status === 429 || status >= 501;
1240
+ /**
1241
+ * Builds a retry policy: an exponentially backing off, jittered {@link ShouldRetryRequestFn} bounded by
1242
+ * an attempt ceiling. What {@link withDefaultRetry} installs - reach for this directly to hand a single
1243
+ * client or creator its own `retryFn`.
1244
+ *
1245
+ * @example
1246
+ * const flakyEndpointRetry = createDefaultRetryFn({ maxAttempts: 8, maxDelayMs: 10_000 });
1247
+ *
1248
+ * const getReport = myApiGet<GetReportArgs>('/report').clone({ retryFn: flakyEndpointRetry });
1249
+ */
1250
+ const createDefaultRetryFn = (options = {}) => {
1251
+ const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
1252
+ const baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
1253
+ const maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
1254
+ const jitter = clamp(options.jitter ?? DEFAULT_JITTER, 0, 1);
1255
+ const retryableStatusCodes = options.retryableStatusCodes;
1256
+ const retryNonIdempotent = options.retryNonIdempotent ?? false;
1257
+ const isRetryableStatus = (status) => retryableStatusCodes ? retryableStatusCodes.includes(status) : isRetryableByDefault(status);
1258
+ const backoff = (retryCount) => {
1259
+ const exponential = baseDelayMs * Math.pow(2, retryCount);
1260
+ const spread = jitter === 0 ? 1 : 1 + (Math.random() * 2 - 1) * jitter;
1261
+ return Math.round(clamp(exponential * spread, 0, maxDelayMs));
1262
+ };
1263
+ return ({ retryCount, error, idempotent }) => {
1264
+ // Not an HTTP failure at all, so nothing about it says a second attempt would go any better.
1265
+ if (!(error instanceof HttpErrorResponse))
1266
+ return { retry: false };
1267
+ if (!idempotent && !retryNonIdempotent)
1268
+ return { retry: false };
1269
+ if (maxAttempts > 0 && retryCount > maxAttempts)
1270
+ return { retry: false };
1271
+ const { status, error: detail, headers } = error;
1272
+ if (!isRetryableStatus(status))
1273
+ return { retry: false };
1274
+ // A page past the end of a Pagerfanta collection answers 5xx, and asking for it again keeps
1275
+ // answering 5xx - the request is wrong, not the server.
1276
+ if (status >= 501 && isSymfonyPagerfantaOutOfRangeError(detail))
1277
+ return { retry: false };
1278
+ if (status === 429) {
1279
+ const retryAfter = headers.get('retry-after') || headers.get('x-retry-after');
1280
+ const delay = retryAfter ? parseInt(retryAfter) * 1000 : NaN;
1281
+ // The server named a time, so it is used as named rather than jittered - only capped, since a
1282
+ // `retry-after` of an hour would otherwise arm a timer nobody is still around for.
1283
+ if (!Number.isNaN(delay))
1284
+ return { retry: true, delay: Math.min(delay, maxDelayMs) };
1285
+ }
1286
+ return { retry: true, delay: backoff(retryCount) };
1287
+ };
1288
+ };
1289
+ const defaultRetryFn = /* @__PURE__ */ createDefaultRetryFn();
1290
+ /**
1291
+ * The SDK's default retry policy, installed by {@link withDefaultRetry}: three retries with an
1292
+ * exponentially backing off, jittered delay, for a connection failure, a `408`, a `425`, a `429`
1293
+ * (honouring `retry-after`) and a 5xx above `500` - never for a `POST`, a `PATCH` or a GraphQL mutation.
1294
+ *
1295
+ * Pass {@link createDefaultRetryFn} options to {@link withDefaultRetry} to configure it.
1296
+ */
1297
+ const shouldRetryRequest = (options) => defaultRetryFn(options instanceof HttpErrorResponse ? { retryCount: 0, error: options, method: null, idempotent: true } : options);
1298
+ /** Whether a request with this method may be sent again without changing the outcome - everything but `POST` and `PATCH`. */
1299
+ const isIdempotentQueryMethod = (method) => method !== 'POST' && method !== 'PATCH';
1300
+
1071
1301
  const isQueryErrorResponse = (error) => {
1072
1302
  return typeof error === 'object' && error !== null && 'raw' in error && 'retryState' in error && 'isList' in error;
1073
1303
  };
@@ -1110,7 +1340,13 @@ const createQueryErrorResponse = (error, retry) => {
1110
1340
  statusText: 'Unknown Error',
1111
1341
  });
1112
1342
  }
1113
- const retryOptions = { retryCount: retry?.retryCount ?? 0, error: err };
1343
+ const method = retry?.method ?? null;
1344
+ const retryOptions = {
1345
+ retryCount: retry?.retryCount ?? 0,
1346
+ error: err,
1347
+ method,
1348
+ idempotent: retry?.idempotent ?? (method ? isIdempotentQueryMethod(method) : true),
1349
+ };
1114
1350
  const retryState = typeof retry?.retryFn === 'function' ? retry.retryFn(retryOptions) : runDefaultQueryRetry(retryOptions);
1115
1351
  const detail = err.error;
1116
1352
  const errorList = [];
@@ -1574,6 +1810,14 @@ const createBaseQuery = (options) => {
1574
1810
  });
1575
1811
  };
1576
1812
 
1813
+ /** Resolves a {@link QueryHeadersInput} to `HttpHeaders`, or `undefined` when none is given. */
1814
+ const resolveQueryHeaders = (input) => {
1815
+ const headers = typeof input === 'function' ? input() : input;
1816
+ if (!headers)
1817
+ return undefined;
1818
+ return headers instanceof HttpHeaders ? headers : new HttpHeaders(headers);
1819
+ };
1820
+
1577
1821
  const extractExpiresInSeconds = (headers) => {
1578
1822
  const cacheControl = headers.get('cache-control');
1579
1823
  const age = headers.get('age');
@@ -1629,7 +1873,7 @@ const sortObjectKeys = (_key, value) => {
1629
1873
  * entries written by an older version are stored under.
1630
1874
  */
1631
1875
  const buildQueryCacheKey = (route, args, method) => {
1632
- const headers = typeof args?.headers === 'function' ? args.headers() : args?.headers;
1876
+ const headers = resolveQueryHeaders(args?.headers);
1633
1877
  const serializedHeaders = headers
1634
1878
  ?.keys()
1635
1879
  .map((name) => name.toLowerCase())
@@ -1970,6 +2214,7 @@ const createHttpRequest = (options) => {
1970
2214
  ...(ngDevMode ? [{ debugName: "attempts" }] : /* istanbul ignore next */ []));
1971
2215
  const retryState = signal(null, /* @ts-ignore */
1972
2216
  ...(ngDevMode ? [{ debugName: "retryState" }] : /* istanbul ignore next */ []));
2217
+ const idempotent = options.idempotent ?? isIdempotentQueryMethod(options.method);
1973
2218
  const lastDurationMs = signal(null, /* @ts-ignore */
1974
2219
  ...(ngDevMode ? [{ debugName: "lastDurationMs" }] : /* istanbul ignore next */ []));
1975
2220
  // Set at the top of every `sendWithFaults` attempt (including unfaulted ones), so it always reflects
@@ -1986,8 +2231,8 @@ const createHttpRequest = (options) => {
1986
2231
  // Resolved per execution rather than once at creation, so a client whose headers are a function
1987
2232
  // reading a signal (a preview token, a tenant id) sees the current value on every re-run.
1988
2233
  const resolveHeaders = () => {
1989
- const clientHeaders = typeof options.clientHeaders === 'function' ? options.clientHeaders() : options.clientHeaders;
1990
- const argHeaders = typeof args?.headers === 'function' ? args.headers() : args?.headers;
2234
+ const clientHeaders = resolveQueryHeaders(options.clientHeaders);
2235
+ const argHeaders = resolveQueryHeaders(args?.headers);
1991
2236
  if (!clientHeaders)
1992
2237
  return argHeaders;
1993
2238
  if (!argHeaders)
@@ -2073,7 +2318,7 @@ const createHttpRequest = (options) => {
2073
2318
  const source$ = isQueryDevtoolsRequestInterceptionEnabled() ? sendWithFaults(headers) : send(headers);
2074
2319
  return source$.pipe(tap((event) => updateState(event)), retry({
2075
2320
  delay: (error, retryCount) => {
2076
- const retryOptions = { error, retryCount };
2321
+ const retryOptions = { error, retryCount, method: options.method, idempotent };
2077
2322
  const retryResult = options.retryFn?.(retryOptions) || runDefaultQueryRetry(retryOptions);
2078
2323
  if (!retryResult.retry) {
2079
2324
  return throwError(() => error);
@@ -2178,6 +2423,8 @@ const createHttpRequest = (options) => {
2178
2423
  const errorRes = createQueryErrorResponse(errorResponse, {
2179
2424
  retryCount: attempts(),
2180
2425
  retryFn: options.retryFn,
2426
+ method: options.method,
2427
+ idempotent,
2181
2428
  });
2182
2429
  error.set(errorRes);
2183
2430
  loading.set(null);
@@ -2643,13 +2890,6 @@ const createPagedQueryStack = (options) => {
2643
2890
  const isOneMinusMinPagination = pageToLoad === currentMinPagination.currentPage - 1;
2644
2891
  const isOnePlusMaxPagination = pageToLoad === currentMaxPagination.currentPage + 1;
2645
2892
  if (!options.blockExecutionDuringLoading) {
2646
- const totalQueries = stack.queries().length;
2647
- const expectedQueries = Math.abs(loadedMaxPage() - loadedMinPage()) + 1;
2648
- if (totalQueries !== expectedQueries) {
2649
- if (isDevMode()) {
2650
- throw queryStackTotalQueriesAndExpectedQueriesMismatch(totalQueries, expectedQueries);
2651
- }
2652
- }
2653
2893
  return true;
2654
2894
  }
2655
2895
  return isOneMinusMinPagination || isOnePlusMaxPagination;
@@ -3777,69 +4017,6 @@ const symfonyQueryErrorParser = (detail) => {
3777
4017
  return null;
3778
4018
  };
3779
4019
 
3780
- const DEFAULT_MAX_ATTEMPTS = 3;
3781
- const DEFAULT_BASE_DELAY_MS = 1000;
3782
- const DEFAULT_MAX_DELAY_MS = 30_000;
3783
- const DEFAULT_JITTER = 0.25;
3784
- // 500 is left out on purpose: an internal server error is a bug in the backend far more often than a
3785
- // blip, and repeating the request that triggered it does not make it go away.
3786
- const isRetryableByDefault = (status) => status === 0 || status === 408 || status === 425 || status === 429 || status >= 501;
3787
- /**
3788
- * Builds a retry policy: an exponentially backing off, jittered {@link ShouldRetryRequestFn} bounded by
3789
- * an attempt ceiling. What {@link withDefaultRetry} installs - reach for this directly to hand a single
3790
- * client or creator its own `retryFn`.
3791
- *
3792
- * @example
3793
- * const flakyEndpointRetry = createDefaultRetryFn({ maxAttempts: 8, maxDelayMs: 10_000 });
3794
- *
3795
- * const getReport = myApiGet<GetReportArgs>('/report').clone({ retryFn: flakyEndpointRetry });
3796
- */
3797
- const createDefaultRetryFn = (options = {}) => {
3798
- const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
3799
- const baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
3800
- const maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
3801
- const jitter = clamp(options.jitter ?? DEFAULT_JITTER, 0, 1);
3802
- const retryableStatusCodes = options.retryableStatusCodes;
3803
- const isRetryableStatus = (status) => retryableStatusCodes ? retryableStatusCodes.includes(status) : isRetryableByDefault(status);
3804
- const backoff = (retryCount) => {
3805
- const exponential = baseDelayMs * Math.pow(2, retryCount);
3806
- const spread = jitter === 0 ? 1 : 1 + (Math.random() * 2 - 1) * jitter;
3807
- return Math.round(clamp(exponential * spread, 0, maxDelayMs));
3808
- };
3809
- return ({ retryCount, error }) => {
3810
- // Not an HTTP failure at all, so nothing about it says a second attempt would go any better.
3811
- if (!(error instanceof HttpErrorResponse))
3812
- return { retry: false };
3813
- if (maxAttempts > 0 && retryCount > maxAttempts)
3814
- return { retry: false };
3815
- const { status, error: detail, headers } = error;
3816
- if (!isRetryableStatus(status))
3817
- return { retry: false };
3818
- // A page past the end of a Pagerfanta collection answers 5xx, and asking for it again keeps
3819
- // answering 5xx - the request is wrong, not the server.
3820
- if (status >= 501 && isSymfonyPagerfantaOutOfRangeError(detail))
3821
- return { retry: false };
3822
- if (status === 429) {
3823
- const retryAfter = headers.get('retry-after') || headers.get('x-retry-after');
3824
- const delay = retryAfter ? parseInt(retryAfter) * 1000 : NaN;
3825
- // The server named a time, so it is used as named rather than jittered - only capped, since a
3826
- // `retry-after` of an hour would otherwise arm a timer nobody is still around for.
3827
- if (!Number.isNaN(delay))
3828
- return { retry: true, delay: Math.min(delay, maxDelayMs) };
3829
- }
3830
- return { retry: true, delay: backoff(retryCount) };
3831
- };
3832
- };
3833
- const defaultRetryFn = /* @__PURE__ */ createDefaultRetryFn();
3834
- /**
3835
- * The SDK's default retry policy, installed by {@link withDefaultRetry}: three retries with an
3836
- * exponentially backing off, jittered delay, for a connection failure, a `408`, a `425`, a `429`
3837
- * (honouring `retry-after`) and a 5xx above `500`.
3838
- *
3839
- * Pass {@link createDefaultRetryFn} options to {@link withDefaultRetry} to configure it.
3840
- */
3841
- const shouldRetryRequest = (options) => defaultRetryFn(options instanceof HttpErrorResponse ? { retryCount: 0, error: options } : options);
3842
-
3843
4020
  const noop$2 = () => undefined;
3844
4021
  /** Shared because it never changes: without Web Locks, every hold is granted and never lost. */
3845
4022
  const alwaysHolder = { isHolder: /* @__PURE__ */ signal(true).asReadonly(), release: noop$2 };
@@ -3926,6 +4103,7 @@ const createQueryKeyLockManager = (namespace) => {
3926
4103
  return { hold, isSupported: true, keyStates: keyStates.asReadonly() };
3927
4104
  };
3928
4105
 
4106
+ /** @internal */
3929
4107
  const createQuerySyncEngine = (options) => {
3930
4108
  const { config, repository, transport, lockManager } = options;
3931
4109
  const syncResponses = config.syncResponses ?? true;
@@ -4259,7 +4437,8 @@ const withSymfonyErrors = () => () => {
4259
4437
  /**
4260
4438
  * Gives this client the SDK's default retry policy for every request that does not bring its own `retryFn`: a
4261
4439
  * connection failure, a 5xx above 500, a 408/425 and a 429 (honouring `retry-after`) are retried three
4262
- * times with an exponentially backing off, jittered delay - and a Pagerfanta out-of-range page never is.
4440
+ * times with an exponentially backing off, jittered delay - and a Pagerfanta out-of-range page never is. A `POST`,
4441
+ * a `PATCH` and a GraphQL mutation are never retried unless the options set `retryNonIdempotent`.
4263
4442
  *
4264
4443
  * The policy belongs to the client the feature is on, so another client of the same app is not
4265
4444
  * affected. Without it nothing is retried automatically and `error.retryState` always reads
@@ -4397,6 +4576,7 @@ const createQueryRepository = (config) => {
4397
4576
  clientHeaders: config.headers,
4398
4577
  cacheAdapter: config.cacheAdapter,
4399
4578
  retryFn: options.retryFn ?? config.retryFn,
4579
+ idempotent: options.isRefreshable ?? isIdempotentQueryMethod(options.method),
4400
4580
  });
4401
4581
  const executed = request.execute();
4402
4582
  bind({
@@ -4749,7 +4929,7 @@ const createQueryClient = (options) => defineRootProvider(() => {
4749
4929
  name: `QueryClient_${options.name}`,
4750
4930
  });
4751
4931
 
4752
- const AUTH_HEADER = 'Authorization';
4932
+ const AUTH_HEADER$1 = 'Authorization';
4753
4933
  /**
4754
4934
  * How long a request waits for the refresh of an expired access token before it sends the expired one
4755
4935
  * anyway. Nothing guarantees the refresh arrives - the refresh token can be spent, and the request can
@@ -4819,12 +4999,12 @@ const createSecureExecuteFactory = (options) => {
4819
4999
  const args = executeArgs.args;
4820
5000
  const headerProvider = () => {
4821
5001
  const accessToken = options.authProvider.accessToken();
4822
- const baseHeaders = typeof args?.headers === 'function' ? args.headers() : args?.headers || new HttpHeaders();
5002
+ const baseHeaders = resolveQueryHeaders(args?.headers) ?? new HttpHeaders();
4823
5003
  if (!accessToken) {
4824
5004
  throw tokensNotAvailableInsideAuthAndExec();
4825
5005
  }
4826
- if (!baseHeaders.has(AUTH_HEADER)) {
4827
- return baseHeaders.set(AUTH_HEADER, `Bearer ${accessToken}`);
5006
+ if (!baseHeaders.has(AUTH_HEADER$1)) {
5007
+ return baseHeaders.set(AUTH_HEADER$1, `Bearer ${accessToken}`);
4828
5008
  }
4829
5009
  return baseHeaders;
4830
5010
  };
@@ -5809,6 +5989,7 @@ const createBearerAuthProviderImpl = (config) => {
5809
5989
  activityCoordination: isLeader.activityCoordination,
5810
5990
  afterTokenRefresh$,
5811
5991
  queries: queries,
5992
+ queryBuilders: config.queries,
5812
5993
  latestExecutedQuery: latestExecutedQuery.asReadonly(),
5813
5994
  executionState,
5814
5995
  sessionStatus: sessionStatus.asReadonly(),
@@ -5845,6 +6026,11 @@ const createBearerAuthProviderImpl = (config) => {
5845
6026
  executionState: executionState.asReadonly(),
5846
6027
  sessionStatus: sessionStatus.asReadonly(),
5847
6028
  sessionEndCause: sessionEndCause.asReadonly(),
6029
+ session: computed(() => ({
6030
+ status: sessionStatus(),
6031
+ endCause: sessionEndCause(),
6032
+ executionState: executionState(),
6033
+ })),
5848
6034
  setTokens,
5849
6035
  logout,
5850
6036
  afterTokenRefresh$,
@@ -5946,7 +6132,7 @@ const withAuthenticationQuery = (key, config, setup) => ({
5946
6132
  const withRefreshQuery = (key, config) => {
5947
6133
  const retryableStatusCodes = config.retryConfig?.retryableStatusCodes ?? [0, 408, 425, 429, 500, 502, 503, 504];
5948
6134
  const maxRetryDelayMs = config.retryConfig?.maxRetryDelayMs ?? 30000; // 30 seconds
5949
- const maxAttempts = config.retryConfig?.maxAttempts ?? 0; // 0 = unlimited
6135
+ const maxAttempts = config.retryConfig?.maxAttempts ?? 8;
5950
6136
  const refreshRetryFn = ({ error, retryCount }) => {
5951
6137
  const { status } = error;
5952
6138
  if (maxAttempts > 0 && retryCount > maxAttempts) {
@@ -7327,12 +7513,20 @@ const createPersistentAuthFeature = (config, context) => {
7327
7513
  if (wasRejected && !context.isTabLocalSession())
7328
7514
  removeCookie();
7329
7515
  });
7516
+ const autoLoginBuilder = context.queryBuilders.find((builder) => builder.key === config.autoLogin.queryKey);
7517
+ const buildAutoLoginArgs = (token) => {
7518
+ if (config.autoLogin.buildArgs)
7519
+ return config.autoLogin.buildArgs(token);
7520
+ if (autoLoginBuilder?._type === 'tokenRefreshQuery')
7521
+ return autoLoginBuilder.buildArgs(token);
7522
+ return { body: { token } };
7523
+ };
7330
7524
  const exchangeCookie = () => {
7331
7525
  const storedToken = getCookie(cookieName);
7332
7526
  if (!storedToken)
7333
7527
  return;
7334
7528
  const decryptedToken = decryptToken(storedToken);
7335
- const args = config.autoLogin.buildArgs(decryptedToken);
7529
+ const args = buildAutoLoginArgs(decryptedToken);
7336
7530
  context.queries[config.autoLogin.queryKey].execute(args, { triggeredBy: 'persistent-auth' });
7337
7531
  };
7338
7532
  const tryLogin = () => {
@@ -7380,7 +7574,10 @@ const withTokenExpirationWarning = (config = {}) => {
7380
7574
  return (context) => {
7381
7575
  const warningThreshold = config.warningThreshold ?? 5 * 60 * 1000;
7382
7576
  const checkInterval = config.checkInterval ?? 1000;
7383
- const expiresInPropertyName = config.expiresInPropertyName ?? 'exp';
7577
+ const refreshBuilder = context.queryBuilders.find((builder) => builder._type === 'tokenRefreshQuery');
7578
+ const expiresInPropertyName = config.expiresInPropertyName ??
7579
+ (refreshBuilder?._type === 'tokenRefreshQuery' ? refreshBuilder.config.expiresInPropertyName : undefined) ??
7580
+ 'exp';
7384
7581
  const expiresAt = computed(() => {
7385
7582
  const decoded = context.bearerData();
7386
7583
  if (!decoded)
@@ -7424,9 +7621,21 @@ const withTokenExpirationWarning = (config = {}) => {
7424
7621
  };
7425
7622
  };
7426
7623
 
7624
+ const AUTH_HEADER = 'Authorization';
7427
7625
  const withTokenRevocation = (config) => {
7428
7626
  return (context) => {
7429
7627
  const revokeOnLogout = config.revokeOnLogout ?? true;
7628
+ const withBearerHeader = (args, accessToken) => {
7629
+ if (!config.bearer || !accessToken)
7630
+ return args;
7631
+ const argHeaders = args.headers;
7632
+ const headers = () => {
7633
+ const base = resolveQueryHeaders(argHeaders) ?? new HttpHeaders();
7634
+ return base.has(AUTH_HEADER) ? base : base.set(AUTH_HEADER, `Bearer ${accessToken}`);
7635
+ };
7636
+ return { ...args, headers };
7637
+ };
7638
+ const shouldRevokeFor = (cause) => !config.revokeOn || (cause !== null && config.revokeOn.includes(cause));
7430
7639
  const enabled = signal(true, /* @ts-ignore */
7431
7640
  ...(ngDevMode ? [{ debugName: "enabled" }] : /* istanbul ignore next */ []));
7432
7641
  let previousAccessToken = null;
@@ -7438,7 +7647,7 @@ const withTokenRevocation = (config) => {
7438
7647
  if (!accessToken && !refreshToken) {
7439
7648
  return null;
7440
7649
  }
7441
- const args = config.buildArgs({ accessToken, refreshToken });
7650
+ const args = withBearerHeader(config.buildArgs?.({ accessToken, refreshToken }) ?? {}, accessToken);
7442
7651
  context.executionState.set({ type: 'revocation', state: 'loading' });
7443
7652
  currentRevocationSnapshot = context.queries[config.queryKey].execute(args, {
7444
7653
  triggeredBy: 'token-revocation',
@@ -7458,7 +7667,10 @@ const withTokenRevocation = (config) => {
7458
7667
  effect(() => {
7459
7668
  const currentToken = context.accessToken();
7460
7669
  const currentRefreshToken = context.refreshToken();
7461
- if (previousAccessToken && !currentToken && enabled()) {
7670
+ if (previousAccessToken &&
7671
+ !currentToken &&
7672
+ enabled() &&
7673
+ shouldRevokeFor(untracked(context.sessionEndCause))) {
7462
7674
  untracked(() => revokeWithTokens(previousAccessToken, previousRefreshToken));
7463
7675
  }
7464
7676
  previousAccessToken = currentToken;
@@ -7476,7 +7688,8 @@ const withTokenRevocation = (config) => {
7476
7688
  instance,
7477
7689
  devtools: () => [
7478
7690
  { label: 'query', value: config.queryKey },
7479
- { label: 'on logout', value: revokeOnLogout ? 'yes' : 'no' },
7691
+ { label: 'on logout', value: revokeOnLogout ? (config.revokeOn?.join(', ') ?? 'yes') : 'no' },
7692
+ { label: 'bearer', value: config.bearer ? 'yes' : 'no' },
7480
7693
  ],
7481
7694
  };
7482
7695
  };
@@ -8485,12 +8698,13 @@ const asScope = (value, fallback) => value === 'none' || value === 'session' ||
8485
8698
  * Whether the session vault may be kept in `localStorage`. Only a development build may: the vault
8486
8699
  * holds live access and refresh tokens plus the credentials somebody typed in, and a deployed
8487
8700
  * application that mounts the panel for its debugging value must not leave those on the machine of
8488
- * whoever opened it. Every other scope stays on offer, so the vault still works for one tab.
8701
+ * whoever opened it. Outside a development build a stored or default `local` reads back as `none`, so a
8702
+ * plain login keeps nothing; `session` stays on offer for whoever picks it in Settings.
8489
8703
  *
8490
8704
  * Part of the devtools contract. **Not part of the general public contract.**
8491
8705
  */
8492
8706
  const queryDevtoolsAllowsLocalAuthSessions = () => isDevMode();
8493
- const clampAuthSessions = (scope) => scope === 'local' && !queryDevtoolsAllowsLocalAuthSessions() ? 'session' : scope;
8707
+ const clampAuthSessions = (scope) => scope === 'local' && !queryDevtoolsAllowsLocalAuthSessions() ? 'none' : scope;
8494
8708
  const asCount = (value, limits, fallback) => {
8495
8709
  const count = Math.floor(Number(value));
8496
8710
  if (!Number.isFinite(count))
@@ -8695,11 +8909,15 @@ let listening = false;
8695
8909
  * @internal
8696
8910
  */
8697
8911
  const initQueryDevtoolsAuthSessions = (accounts) => {
8698
- // Before the read below, which never looks at `localStorage` once the scope is clamped: a vault an
8699
- // earlier version left there would otherwise sit on a deployed origin with nothing left to remove it.
8700
- if (!queryDevtoolsAllowsLocalAuthSessions())
8701
- queryDevtoolsStorage('local')?.removeItem(STORE_KEY);
8702
8912
  const scope = queryDevtoolsSettings().authSessions;
8913
+ // The read below only looks at the store the scope names: a vault an earlier version or scope left in
8914
+ // another store would otherwise sit there, tokens and all, with nothing left to remove it.
8915
+ for (const other of ['local', 'session']) {
8916
+ if (other !== scope)
8917
+ queryDevtoolsStorage(other)?.removeItem(STORE_KEY);
8918
+ }
8919
+ if (scope === 'none')
8920
+ queryDevtoolsStorage('session')?.removeItem(ACTIVE_KEY);
8703
8921
  declared.set(accounts ?? []);
8704
8922
  store.set(sanitize(readQueryDevtoolsStore(scope, STORE_KEY)));
8705
8923
  active.set(readQueryDevtoolsStore(scope === 'none' ? 'none' : 'session', ACTIVE_KEY) ?? {});
@@ -10294,7 +10512,7 @@ const armQueryDevtoolsOverrideTransfer = (recorder, ops) => {
10294
10512
 
10295
10513
  // Generated from package.json by tools/scripts/generate-version.js - do not edit.
10296
10514
  /** The version of `@ethlete/query` this build was cut from. */
10297
- const QUERY_VERSION = '6.0.0-next.46';
10515
+ const QUERY_VERSION = '6.0.0-next.49';
10298
10516
 
10299
10517
  /** How deep a schema is expanded before a nested branch is given up on. */
10300
10518
  const MAX_DEPTH = 12;
@@ -10536,7 +10754,7 @@ const clampToBounds = (value, bounds) => {
10536
10754
  const numberSample = (schema, ctx) => {
10537
10755
  const bounds = boundsOf(schema);
10538
10756
  if (ctx.style === 'placeholder')
10539
- return bounds.min ?? finiteNumber(schema['default']) ?? 0;
10757
+ return bounds.min ?? 0;
10540
10758
  if (ctx.style === 'realistic') {
10541
10759
  return generateQueryDevtoolsSampleNumber({ ...bounds, fractional: typeOf(schema) === 'number' });
10542
10760
  }
@@ -11691,6 +11909,7 @@ const gql = (strings, ...values) => {
11691
11909
  return str;
11692
11910
  };
11693
11911
 
11912
+ /** @internal */
11694
11913
  const createGqlExecuteFn = (executeOptions) => {
11695
11914
  const executeState = setupQueryExecuteState();
11696
11915
  const circularChecker = circularQueryDependencyChecker();
@@ -11748,7 +11967,7 @@ const createGqlQuery = (options) => createBaseQuery({
11748
11967
  executeFactory: createGqlExecuteFn,
11749
11968
  });
11750
11969
 
11751
- const unwrapGqlResponse = (rawResponse) => {
11970
+ const readGqlEnvelope = (rawResponse) => {
11752
11971
  if (!rawResponse || typeof rawResponse !== 'object')
11753
11972
  throw gqlDataPropertyMissingInResponse();
11754
11973
  const data = 'data' in rawResponse ? rawResponse.data : undefined;
@@ -11757,8 +11976,14 @@ const unwrapGqlResponse = (rawResponse) => {
11757
11976
  throw gqlErrorsInResponse(errors);
11758
11977
  if (!('data' in rawResponse))
11759
11978
  throw gqlDataPropertyMissingInResponse();
11760
- return data;
11979
+ return { data, errors: Array.isArray(errors) ? errors : [] };
11761
11980
  };
11981
+ const unwrapGqlResponse = (rawResponse) => readGqlEnvelope(rawResponse).data;
11982
+ /**
11983
+ * A GQL `transformResponse` that keeps the server's `errors` next to partial data. Declare the
11984
+ * args' `response` as `GqlDataWithErrors<TData>`; a response without data still fails with ET601.
11985
+ */
11986
+ const unwrapGqlResponseWithErrors = (rawResponse) => readGqlEnvelope(rawResponse);
11762
11987
 
11763
11988
  const createGqlQueryCreator = (options, internals) => createBaseQueryCreator({
11764
11989
  options: {
@@ -11769,6 +11994,7 @@ const createGqlQueryCreator = (options, internals) => createBaseQueryCreator({
11769
11994
  queryFactory: createGqlQuery,
11770
11995
  });
11771
11996
 
11997
+ /** @internal */
11772
11998
  const createSecureGqlExecuteFn = (executeOptions) => {
11773
11999
  const authProvider = executeOptions.creatorInternals.authProvider.inject();
11774
12000
  return createSecureExecuteFactory({
@@ -16887,6 +17113,36 @@ const normalizeLive = (fieldDefs, rawLive, defaults) => {
16887
17113
  }
16888
17114
  return live;
16889
17115
  };
17116
+ const resolveStorage = (storage) => {
17117
+ if (typeof storage !== 'string')
17118
+ return storage;
17119
+ try {
17120
+ if (typeof globalThis.window === 'undefined')
17121
+ return null;
17122
+ return (storage === 'session' ? globalThis.sessionStorage : globalThis.localStorage) ?? null;
17123
+ }
17124
+ catch {
17125
+ return null;
17126
+ }
17127
+ };
17128
+ const readStoredParams = (persistence) => {
17129
+ try {
17130
+ const raw = resolveStorage(persistence.storage)?.getItem(persistence.key);
17131
+ const parsed = raw ? JSON.parse(raw) : null;
17132
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
17133
+ }
17134
+ catch {
17135
+ return null;
17136
+ }
17137
+ };
17138
+ const writeStoredParams = (persistence, params) => {
17139
+ try {
17140
+ resolveStorage(persistence.storage)?.setItem(persistence.key, JSON.stringify(params));
17141
+ }
17142
+ catch {
17143
+ return;
17144
+ }
17145
+ };
16890
17146
  const computeFilterCount = (fields, value, defaults) => {
16891
17147
  let count = 0;
16892
17148
  for (const [key, def] of Object.entries(fields)) {
@@ -16984,6 +17240,11 @@ const createBranch = (fields, initial, injector) => {
16984
17240
  model.set(clone(next));
16985
17241
  }
16986
17242
  };
17243
+ const write = (next, options) => {
17244
+ model.set(next);
17245
+ if (!options?.debounce)
17246
+ flush();
17247
+ };
16987
17248
  effect(() => {
16988
17249
  const live = normalizedLive();
16989
17250
  untracked(() => {
@@ -17006,13 +17267,13 @@ const createBranch = (fields, initial, injector) => {
17006
17267
  value: committed.asReadonly(),
17007
17268
  liveValue: model.asReadonly(),
17008
17269
  activeFilterCount: computed(() => computeFilterCount(fields, committed(), defaults)),
17009
- setValue: (value) => model.set(clone(value)),
17010
- patchValue: (value) => model.update((cur) => ({ ...cur, ...value })),
17011
- resetFieldToDefault: (key) => model.update((cur) => ({ ...cur, [key]: defaultFor(key) })),
17012
- resetAllFieldsToDefault: () => {
17270
+ setValue: (value, options) => write(clone(value), options),
17271
+ patchValue: (value, options) => write({ ...model(), ...value }, options),
17272
+ resetFieldToDefault: (key, options) => write({ ...model(), [key]: defaultFor(key) }, options),
17273
+ resetAllFieldsToDefault: (options) => {
17013
17274
  for (const key of Object.keys(fields))
17014
17275
  defaultFor(key);
17015
- model.set(clone(defaults));
17276
+ write(clone(defaults), options);
17016
17277
  },
17017
17278
  };
17018
17279
  };
@@ -17074,6 +17335,7 @@ const defineQueryForm = (config) => {
17074
17335
  const skipResetsFor = new Set();
17075
17336
  let skipNextResetsFor;
17076
17337
  let urlWriteVersion = 0;
17338
+ let changesSeenAtObserve;
17077
17339
  const urlNavigationMarker = {};
17078
17340
  const pathOf = (tree) => router.serializeUrl(tree).split(/[?#]/)[0];
17079
17341
  const fields = form(model);
@@ -17091,6 +17353,9 @@ const defineQueryForm = (config) => {
17091
17353
  const writeDefault = def.appendDefaultValueToUrl === true;
17092
17354
  if (!writeToUrl || (isDefault && !writeDefault))
17093
17355
  return undefined;
17356
+ return serialize(key, def, value);
17357
+ };
17358
+ const serialize = (key, def, value) => {
17094
17359
  if (def.valueToQueryParam)
17095
17360
  return def.valueToQueryParam(value);
17096
17361
  if (value === '' && defaults[key] === null)
@@ -17099,16 +17364,47 @@ const defineQueryForm = (config) => {
17099
17364
  return ET_EMPTY_ARRAY_VALUE;
17100
17365
  return value === null ? ET_NULL_VALUE : value;
17101
17366
  };
17367
+ const persistedKeys = () => {
17368
+ const keys = observeOptions?.persistence?.fields ?? Object.keys(fieldDefs);
17369
+ return keys.filter((key) => key in fieldDefs);
17370
+ };
17371
+ const persist = () => {
17372
+ const persistence = observeOptions?.persistence;
17373
+ if (!persistence || !observing())
17374
+ return;
17375
+ const value = committed();
17376
+ const stored = {};
17377
+ for (const key of persistedKeys()) {
17378
+ if (equal(value[key], defaults[key]))
17379
+ continue;
17380
+ stored[key] = serialize(key, fieldDefs[key], value[key]);
17381
+ }
17382
+ writeStoredParams(persistence, stored);
17383
+ };
17384
+ const restoreFromStorage = (persistence, urlParams) => {
17385
+ const keys = persistedKeys();
17386
+ if (keys.some((key) => urlParams[paramKey(key)] !== undefined))
17387
+ return;
17388
+ const stored = readStoredParams(persistence);
17389
+ if (!stored)
17390
+ return;
17391
+ const params = {};
17392
+ for (const key of keys) {
17393
+ if (stored[key] !== undefined)
17394
+ params[paramKey(key)] = stored[key];
17395
+ }
17396
+ applyFromUrl(params, { base: model() });
17397
+ };
17102
17398
  const deserialize = (def, raw) => {
17103
17399
  if (!def.skipAutoTransform && raw === ET_NULL_VALUE)
17104
17400
  return null;
17105
17401
  if (!def.skipAutoTransform && raw === ET_EMPTY_ARRAY_VALUE)
17106
17402
  return [];
17107
17403
  if (def.queryParamToValue)
17108
- return def.queryParamToValue(raw);
17404
+ return def.queryParamToValue(raw) ?? resolveDefault(def);
17109
17405
  if (def.skipAutoTransform)
17110
17406
  return raw;
17111
- return autoCoerce(raw, resolveDefault(def));
17407
+ return autoCoerce(raw, resolveDefault(def)) ?? resolveDefault(def);
17112
17408
  };
17113
17409
  const clearTimer = () => {
17114
17410
  if (pendingTimer !== null) {
@@ -17184,6 +17480,7 @@ const defineQueryForm = (config) => {
17184
17480
  if (observing() && observeOptions?.writeToQueryParams !== false) {
17185
17481
  writeToUrl(next);
17186
17482
  }
17483
+ persist();
17187
17484
  };
17188
17485
  const onLiveChange = (live) => {
17189
17486
  if (equal(live, committed())) {
@@ -17214,6 +17511,7 @@ const defineQueryForm = (config) => {
17214
17511
  committed.set(clone(next));
17215
17512
  // Feed the URL value into the live model & bound controls; the effect no-ops (model === committed).
17216
17513
  model.set(clone(next));
17514
+ persist();
17217
17515
  };
17218
17516
  const applyFromUrl = (params, options) => {
17219
17517
  const current = { ...(options?.base ?? committed()) };
@@ -17259,6 +17557,12 @@ const defineQueryForm = (config) => {
17259
17557
  }
17260
17558
  }
17261
17559
  model.set(next);
17560
+ if (options?.debounce) {
17561
+ onLiveChange(next);
17562
+ }
17563
+ else {
17564
+ flush();
17565
+ }
17262
17566
  };
17263
17567
  const setValue = (value, options) => writeModel(clone(value), options);
17264
17568
  const patchValue = (value, options) => writeModel({ ...model(), ...value }, options);
@@ -17289,6 +17593,10 @@ const defineQueryForm = (config) => {
17289
17593
  }
17290
17594
  observeOptions = options;
17291
17595
  observing.set(true);
17596
+ changesSeenAtObserve = untracked(queryParamChanges);
17597
+ if (options?.persistence) {
17598
+ restoreFromStorage(options.persistence, route.snapshot.queryParams);
17599
+ }
17292
17600
  if (options?.syncOnNavigation !== false) {
17293
17601
  // A value written before `observe()` sits in the model only, so the URL merges onto the model
17294
17602
  // here. Against `committed()` any single URL param would drop that value.
@@ -17304,6 +17612,7 @@ const defineQueryForm = (config) => {
17304
17612
  if (options?.writeToQueryParams !== false && writesAnyParam) {
17305
17613
  writeToUrl(committed());
17306
17614
  }
17615
+ persist();
17307
17616
  return queryForm;
17308
17617
  },
17309
17618
  unobserve: () => {
@@ -17336,6 +17645,10 @@ const defineQueryForm = (config) => {
17336
17645
  untracked(() => {
17337
17646
  if (!observing() || observeOptions?.syncOnNavigation === false)
17338
17647
  return;
17648
+ // The changes are shared app-wide, so the diff present at `observe()` describes a navigation this
17649
+ // form never saw - applying its removals would wipe a restored or seeded value.
17650
+ if (changes === changesSeenAtObserve)
17651
+ return;
17339
17652
  const info = router.lastSuccessfulNavigation()?.extras.info;
17340
17653
  // Including the newest write: re-parsing the form's own output would coerce a committed
17341
17654
  // string back to a number and drop the milliseconds of a committed Date.
@@ -17485,10 +17798,12 @@ const messageMalformed = () => {
17485
17798
  const MAX_DEVTOOLS_MESSAGES = 100;
17486
17799
  const createWebSocketClient = (options) => {
17487
17800
  return defineRootProvider(() => {
17801
+ const auth = options.auth;
17488
17802
  const socket = options.io(options.url, {
17489
- withCredentials: true,
17803
+ withCredentials: options.withCredentials ?? true,
17490
17804
  autoConnect: false,
17491
17805
  transports: options.transports,
17806
+ auth: auth ? (cb) => cb(typeof auth === 'function' ? auth() : auth) : undefined,
17492
17807
  });
17493
17808
  const rooms = new Map();
17494
17809
  const joinsDeliveredThisConnection = new Set();
@@ -17538,10 +17853,11 @@ const createWebSocketClient = (options) => {
17538
17853
  }
17539
17854
  emit({ event: 'join-room', data: name, room: name });
17540
17855
  joinsDeliveredToClosedConnection.delete(name);
17541
- const message = signal(null, /* @ts-ignore */
17542
- ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
17856
+ const messages = new Subject();
17543
17857
  const newRoom = {
17544
- latestMessage: message,
17858
+ latestMessage: signal(null),
17859
+ messages,
17860
+ messages$: messages.asObservable(),
17545
17861
  joinCount: 1,
17546
17862
  };
17547
17863
  rooms.set(name, newRoom);
@@ -17594,6 +17910,7 @@ const createWebSocketClient = (options) => {
17594
17910
  emit({ event: 'leave-room', data: room, room });
17595
17911
  rooms.delete(room);
17596
17912
  syncDevtoolsRooms();
17913
+ joinedRoom.messages.complete();
17597
17914
  };
17598
17915
  const setupWebSocketConnectionListener = () => {
17599
17916
  socket.onAnyOutgoing((eventName, room) => {
@@ -17627,6 +17944,7 @@ const createWebSocketClient = (options) => {
17627
17944
  };
17628
17945
  const setupWebSocketListener = () => {
17629
17946
  socket.onAny((_eventName, ...args) => {
17947
+ let json;
17630
17948
  try {
17631
17949
  const data = args[0];
17632
17950
  if (typeof data !== 'string')
@@ -17637,11 +17955,7 @@ const createWebSocketClient = (options) => {
17637
17955
  typeof parsed.room !== 'string') {
17638
17956
  throw messageMalformed();
17639
17957
  }
17640
- const json = parsed;
17641
- recordDevtoolsMessage({ room: json.room, event: json.event, data: json.data, direction: 'in' });
17642
- const room = rooms.get(json.room);
17643
- if (room)
17644
- room.latestMessage.set(json);
17958
+ json = parsed;
17645
17959
  }
17646
17960
  catch (error) {
17647
17961
  if (!isDevMode())
@@ -17649,15 +17963,26 @@ const createWebSocketClient = (options) => {
17649
17963
  console.error(error);
17650
17964
  throw messageMalformed();
17651
17965
  }
17966
+ recordDevtoolsMessage({ room: json.room, event: json.event, data: json.data, direction: 'in' });
17967
+ const room = rooms.get(json.room);
17968
+ if (!room)
17969
+ return;
17970
+ room.latestMessage.set(json);
17971
+ room.messages.next(json);
17652
17972
  });
17653
17973
  };
17654
- inject(DestroyRef).onDestroy(() => socket.disconnect());
17974
+ inject(DestroyRef).onDestroy(() => {
17975
+ socket.disconnect();
17976
+ for (const room of rooms.values())
17977
+ room.messages.complete();
17978
+ });
17655
17979
  setupWebSocketConnectionListener();
17656
17980
  setupWebSocketListener();
17657
17981
  socket.connect();
17658
17982
  const client = {
17659
17983
  joinRoom,
17660
17984
  isConnected: isConnected.asReadonly(),
17985
+ send: (message) => emit({ event: message.event, data: message.data }),
17661
17986
  subtle: {
17662
17987
  leaveRoom,
17663
17988
  },
@@ -17686,5 +18011,5 @@ const createWebSocketClient = (options) => {
17686
18011
  * Generated bundle index. Do not edit.
17687
18012
  */
17688
18013
 
17689
- export { AuthBearerRefreshStrategy, BasicAuthProvider, BearerAuthFeatureType, BooleanArrayQueryField, CustomHeaderAuthProvider, DEFAULT_KEEP_UNUSED_FOR, DEFAULT_QUERY_PERSISTENCE_MAX_AGE, DEFAULT_QUERY_PERSISTENCE_MAX_ENTRIES, DEFAULT_QUERY_PERSISTENCE_VERSION, DEFAULT_QUERY_PERSISTENCE_WRITE_DELAY, DateArrayQueryField, DateQueryField, EMPTY_QUERY_DEVTOOLS_FAULT, EntityStore, IGNORED_FILTER_COUNT_FIELDS, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, LegacyQuery, LegacyQueryCreator, MAX_QUERY_BATCH_TOMBSTONES, MAX_QUERY_DEVTOOLS_TOMBSTONES, MAX_UNUSED_ENTRIES, NumberArrayQueryField, ParseHttpErrorCodeToMessageDePipe, ParseHttpErrorCodeToMessageEnPipe, ParseHttpErrorCodeToTitleDePipe, ParseHttpErrorCodeToTitleEnPipe, QUERY_CLIENT_DEVTOOLS_TOKEN, QUERY_DEVTOOLS_FAULT_STATUSES, QUERY_DEVTOOLS_TOKEN_TTL_LIMIT, QUERY_PERSISTENCE_STORE_VERSION, QUERY_SYNC_PROTOCOL_VERSION, QUERY_VERSION, QueryClientFeatureType, QueryDevtoolsComponent, QueryDirective, QueryFeatureType, QueryField, QueryForm, QueryRuntimeErrorCode, QueryShortNamePipe, QueryStateType, QueryStore, SERVER_ERROR_KIND, SERVER_VIOLATION_ERROR_KIND, SPEED_BUFFER_TIME_IN_MS, SearchQueryField, SortQueryField, StringArrayQueryField, V2BearerAuthProvider, V2Query, V2QueryClient, V2QueryCreator, addQueryContainerHandling, addQueryDevtoolsAuthAccount, applyQueryDevtoolsOverrides, applyQueryDevtoolsTokenTtl, applyQueryFeatures, armAllQueryDevtoolsMocks, armQueryDevtoolsMock, armQueryDevtoolsOverrideTransfer, authExtractTokensResponseMissingAccessToken, authExtractTokensResponseMissingRefreshToken, authExtractTokensResponseNotObject, authProviderFeatureUsedMultipleTimes, booleanArrayQueryField, buildQueryCacheKey, buildQueryString, buildRoute, buildTimestampFromSeconds, canOverrideQueryDevtoolsTokenTtl, castQueryCreatorTypes, circularQueryDependency, circularQueryDependencyChecker, clearQueryDevtoolsArmedMocks, clearQueryDevtoolsAuthCredentials, clearQueryDevtoolsAuthSessions, clearQueryDevtoolsFaults, clearQueryDevtoolsMockStore, clearQueryDevtoolsOverrideStore, clearQueryDevtoolsStore, clearQueryDevtoolsTokenTtl, clearQueryDevtoolsTombstones, clearRestoredQueryDevtoolsOverrides, collectLeafPaths, collectQueryDevtoolsSchemaComponents, collectQueryFormLinks, computeQueryAuthHeader, computeQueryBody, computeQueryHeaders, computeQueryMethod, computeQueryQueryParams, consumeSuppressQueryStackDevtools, contentfulGqlLikePaginationAdapter, countUnresolvedQueryDevtoolsOverrides, createAuthGuard, createBaseQuery, createBaseQueryCreator, createBearerAuthProvider, createDefaultRetryFn, createDeleteQuery, createExecuteFn, createGetQuery, createGqlExecuteFn, createGqlMutationViaGet, createGqlMutationViaPost, createGqlQuery, createGqlQueryCreator, createGqlQueryViaGet, createGqlQueryViaPost, createHeadQuery, createHttpRequest, createIndexedDbQueryPersistenceAdapter, createInfinityQueryConfig, createLegacyQueryCreator, createNoopQueryPersistenceAdapter, createOptionsQuery, createPagedQueryStack, createPatchQuery, createPersistentAuthFeature, createPostQuery, createPutQuery, createQuery, createQueryBatch, createQueryClient, createQueryCollection, createQueryCollectionSignal, createQueryCollectionSubject, createQueryContext, createQueryCreator, createQueryDevtoolsFormLinks, createQueryDevtoolsFormLinksRecorder, createQueryDevtoolsOverrides, createQueryDevtoolsOverridesRecorder, createQueryDevtoolsStats, createQueryDevtoolsStatsRecorder, createQueryErrorResponse, createQueryFeature, createQueryInvalidationFilter, createQueryKeyLockManager, createQueryObject, createQueryPersistenceEngine, createQueryRepository, createQuerySnapshotFn, createQueryStack, createQuerySubmission, createQuerySyncEngine, createQuerySyncTransport, createSecureDeleteQuery, createSecureExecuteFactory, createSecureExecuteFn, createSecureGetQuery, createSecureGqlExecuteFn, createSecureGqlMutationViaGet, createSecureGqlMutationViaPost, createSecureGqlQuery, createSecureGqlQueryCreator, createSecureGqlQueryViaGet, createSecureGqlQueryViaPost, createSecureHeadQuery, createSecureOptionsQuery, createSecurePatchQuery, createSecurePostQuery, createSecurePutQuery, createSecureQuery, createSecureQueryCreator, createTrackingFeature, createWebSocketClient, currentQueryDevtoolsBatch, dateArrayQueryField, dateQueryField, decryptBearer, deepFreeze, def, defineQueryForm, deleteQueryDevtoolsMock, describeQueryDevtoolsFeatures, detectContentTypeHeader, detectPaginationShape, dynLikePaginationAdapter, effectComputed, emptyQueryArgs, ethletePaginationAdapter, executeUntilSettled, extractExpiresInSeconds, extractFormViolations, extractHtmlErrorMessage, extractQuery, fakePaginationAdapter, filterFailure, filterNull, filterQueryStates, filterSuccess, forEachHeader, forgetQueryDevtoolsAuthSession, forgetQueryDevtoolsAuthSessionsFor, formatQueryDevtoolsDuration, generateQueryDevtoolsNumberPreset, generateQueryDevtoolsSampleNumber, generateQueryDevtoolsStringPreset, getBearerAuthProviderName, getDefaultHeaders, getQueryClientName, getQueryFeatureUsage, getResponseUrl, ggLikePaginationAdapter, gql, gqlDataPropertyMissingInResponse, gqlErrorsInResponse, gqlTransformerFor, hasHeader, hasQueryDevtoolsOverridesAtPath, htmlErrorPayload, htmlQueryErrorParser, ignoreAutoRefresh, initQueryDevtoolsAuthSessions, initQueryDevtoolsFaults, initQueryDevtoolsMocks, initQueryDevtoolsOverridePersistence, initQueryDevtoolsSettings, injectInfinityQueryResponseDelay, injectQueryContext, insertFrom, invalidBaseRouteError, invalidBodyError, invalidRouteError, invalidStateInsideSecureExecuteFactory, isAutoRefresh, isBasicAuthProvider, isBearerAuthProvider, isClassValidatorError, isCreateGqlQueryOptions, isCustomHeaderAuthProvider, isDateShapedLeaf, isEmptyString, isGqlQueryConfig, isHtmlErrorPayload, isLegacyQuery, isNaN$1 as isNaN, isPageOutOfRangeError, isQuery, isQueryCollection, isQueryDevtoolsEnabled, isQueryDevtoolsFaultArmed, isQueryDevtoolsRepositoryLive, isQueryDevtoolsRequestInterceptionEnabled, isQueryErrorResponse, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, isSymfonyError, isSymfonyFormViolationListError, isSymfonyListError, isSymfonyPagerfantaOutOfRangeError, isUnderInvalidatedUrl, legacyPrepareFallbackInjector, legacyPrepareWithoutInjectionContext, loadQueryDevtoolsSchema, loginQueryDevtoolsAuthAccount, logoutQueryDevtoolsAuthSession, mapToPaginated, mapViolationsToFormErrors, matchesQueryDevtoolsMockPattern, matchesQueryDevtoolsMockQuery, maybeExecute, measureQueryDevtoolsPayload, mergeHeaders, nestedEffect, noteQueryFormRead, noteQueryFormReads, numberArrayQueryField, pagedQueryStackNextPageCalledWithoutPreviousPage, pagedQueryStackPageBiggerThanTotalPages, pagedQueryStackPreviousPageCalledButAlreadyAtFirstPage, paginatedEntityValueUpdater, parseAllXhrResponseHeaders, parseHttpErrorCodeToMessageDe, parseHttpErrorCodeToMessageEn, parseHttpErrorCodeToTitleDe, parseHttpErrorCodeToTitleEn, parseQueryDevtoolsOverrideTransfer, parseQueryRoute, patchQueryDevtoolsTokenPayload, pathParamsMissingInRouteFunctionError, provideInfinityQueryResponseDelay, provideLegacyPrepareFallback, provideQueryClientForDevtools, provideQueryDevtools, queryArrayComputed, queryBatchAlreadyRunning, queryBatchWithArgsUsed, queryClientFeatureUsedMultipleTimes, queryComputed, queryComputedTillTruthy, queryDevtoolsAbout, queryDevtoolsAllowsLocalAuthSessions, queryDevtoolsApiEnvIds, queryDevtoolsApiEnvIsProduction, queryDevtoolsApiEnvScope, queryDevtoolsApiEnvValues, queryDevtoolsApiEnvs, queryDevtoolsArmedMocks, queryDevtoolsArmedMocksRestored, queryDevtoolsAuthAccountsFor, queryDevtoolsAuthActive, queryDevtoolsAuthFieldsFor, queryDevtoolsAuthOtherScopeCount, queryDevtoolsAuthProviders, queryDevtoolsAuthSessions, queryDevtoolsAuthSessionsFor, queryDevtoolsAuthTabLocal, queryDevtoolsEntries, queryDevtoolsFaults, queryDevtoolsFaultsRestored, queryDevtoolsFnDetail, queryDevtoolsMockId, queryDevtoolsMocks, queryDevtoolsOverridePersistence, queryDevtoolsRequestPath, queryDevtoolsResponseHistory, queryDevtoolsRestoredOverridesScope, queryDevtoolsSchemaNames, queryDevtoolsSchemaRoutes, queryDevtoolsSchemaState, queryDevtoolsSettings, queryDevtoolsStorage, queryDevtoolsTokenTtls, queryErrorMessage, queryErrorMessages, queryExecute, queryExecutedAfterDestroyMessage, queryFeatureUsedMultipleTimes, queryField, querySequence, querySequenceAlreadyRunning, queryStackTotalQueriesAndExpectedQueriesMismatch, queryStackWithArgsUsed, queryStackWithResponseUpdateUsed, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, queryStateErrorSignal, queryStateLoadingSignal, queryStateResponseSignal, queryStateSignal, readQueryDevtoolsAuthSeed, readQueryDevtoolsAuthSeedFor, readQueryDevtoolsStore, registerEthleteVersion, registerLiveQueryDevtoolsRepository, registerQueryDevtoolsAuthProvider, registerQueryDevtoolsEntry, registerQueryErrorParser, releaseQueryDevtoolsOverridePersistence, removeFrom, removeQueryDevtoolsAuthAccount, renameQueryDevtoolsAuthSession, request, resetExecuteState, resetPageOnError, resolveInvalidationUrl, resolveQueryDevtoolsFault, resolveQueryDevtoolsFaultForAttempt, resolveQueryDevtoolsMock, resolveQueryDevtoolsMockForAttempt, restoredQueryDevtoolsOverrides, runDefaultQueryRetry, runInQueryDevtoolsBatch, runQueryErrorParsers, saveQueryDevtoolsMock, searchQueryField, seedQueryDevtoolsSchemaBody, seedQueryDevtoolsSchemaRoute, serializeBody, serializeQueryDevtoolsOverrideTransfer, setDefaultQueryRetryFn, setQueryDevtoolsApiEnv, setQueryDevtoolsApiEnvs, setQueryDevtoolsAppInfo, setQueryDevtoolsArmedMocksScope, setQueryDevtoolsAuthCredentials, setQueryDevtoolsAuthProviderRegistrar, setQueryDevtoolsAuthSeedReader, setQueryDevtoolsAuthTabLocal, setQueryDevtoolsFault, setQueryDevtoolsFaultResolver, setQueryDevtoolsFaultsScope, setQueryDevtoolsFormLinksFactory, setQueryDevtoolsMockResolver, setQueryDevtoolsOverridePersistence, setQueryDevtoolsOverridesFactory, setQueryDevtoolsOverridesScope, setQueryDevtoolsRegistrar, setQueryDevtoolsResponseHistory, setQueryDevtoolsSchemaLoader, setQueryDevtoolsSettings, setQueryDevtoolsStatsFactory, setQueryDevtoolsTokenPayloadPatcher, setQueryDevtoolsTokenTtl, setQueryDevtoolsUiMounted, setupQueryDependencies, setupQueryExecuteState, setupQueryState, shouldAutoExecuteGqlQuery, shouldAutoExecuteQuery, shouldCacheQuery, shouldRetryRequest, silenceMissingWithArgsFeatureErrorUsedButWithArgsPresent, skipPaginationPageParamCalculator, smartDuplicateArrayItem, snapshotQueryDevtoolsHandle, sortQueryField, splitQueryConfig, stringArrayQueryField, stringifyQueryRouteParts, sumQueryDevtoolsStats, suppressNextQueryStackDevtools, switchQueryCollectionState, switchQueryDevtoolsAuthSession, switchQueryState, symfonyQueryErrorParser, takeUntilResponse, toQuerySignal, toQuerySubject, tokensNotAvailableInsideAuthAndExec, tombstoneOf, trackQueryDevtoolsAuthProvider, transformArrayResponse, transformExecStateToQueryState, transformGql, transformMethod, transformPaginatedResponse, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray, uncacheableRequestHasAllowCacheParam, uncacheableRequestHasCacheKeyParam, unwrapQuerySyncMessage, v2BuildQueryCacheKey, v2ExtractExpiresInSeconds, v2ShouldCacheQuery, v2ShouldRetryRequest, validateWithQuery, validateWithV2Query, withArgs, withArgsQueryFeatureMissingButRouteIsFunction, withAuthenticationQuery, withAutoRefresh, withAutoRefreshUsedInManualQuery, withAutoRefreshUsedOnUnsupportedHttpMethod, withBearerAuthMultiTabSync, withDefaultRetry, withErrorHandling, withEthleteApiErrors, withHtmlErrorParsing, withInactivityLogout, withLogging, withLongPolling, withLongPollingUsedOnUnsupportedHttpMethod, withLongPollingUsedWithPolling, withMultiTabSync, withPageResetOnError, withPersistentAuth, withPolling, withPollingUsedOnUnsupportedHttpMethod, withQueryDevtoolsOverridePersistence, withQueryPersistence, withRefreshQuery, withResponseUpdate, withSuccessHandling, withSymfonyErrors, withTokenExpirationWarning, withTokenRevocation, withTracking, wrapAsObservableSignal, wrapQuerySyncMessage, writeQueryDevtoolsStore };
18014
+ export { AuthBearerRefreshStrategy, BasicAuthProvider, BearerAuthFeatureType, BooleanArrayQueryField, CustomHeaderAuthProvider, DEFAULT_KEEP_UNUSED_FOR, DEFAULT_QUERY_PERSISTENCE_MAX_AGE, DEFAULT_QUERY_PERSISTENCE_MAX_ENTRIES, DEFAULT_QUERY_PERSISTENCE_VERSION, DEFAULT_QUERY_PERSISTENCE_WRITE_DELAY, DateArrayQueryField, DateQueryField, EMPTY_QUERY_DEVTOOLS_FAULT, EntityStore, IGNORED_FILTER_COUNT_FIELDS, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, LegacyQuery, LegacyQueryCreator, MAX_QUERY_BATCH_TOMBSTONES, MAX_QUERY_DEVTOOLS_TOMBSTONES, MAX_UNUSED_ENTRIES, NumberArrayQueryField, ParseHttpErrorCodeToMessageDePipe, ParseHttpErrorCodeToMessageEnPipe, ParseHttpErrorCodeToTitleDePipe, ParseHttpErrorCodeToTitleEnPipe, QUERY_CLIENT_DEVTOOLS_TOKEN, QUERY_DEVTOOLS_FAULT_STATUSES, QUERY_DEVTOOLS_TOKEN_TTL_LIMIT, QUERY_PERSISTENCE_STORE_VERSION, QUERY_SYNC_PROTOCOL_VERSION, QUERY_VERSION, QueryClientFeatureType, QueryDevtoolsComponent, QueryDirective, QueryFeatureType, QueryField, QueryForm, QueryRuntimeErrorCode, QueryShortNamePipe, QueryStateType, QueryStore, SERVER_ERROR_KIND, SERVER_VIOLATION_ERROR_KIND, SPEED_BUFFER_TIME_IN_MS, SearchQueryField, SortQueryField, StringArrayQueryField, V2BearerAuthProvider, V2Query, V2QueryClient, V2QueryCreator, addQueryContainerHandling, addQueryDevtoolsAuthAccount, applyQueryDevtoolsOverrides, applyQueryDevtoolsTokenTtl, applyQueryFeatures, armAllQueryDevtoolsMocks, armQueryDevtoolsMock, armQueryDevtoolsOverrideTransfer, authExtractTokensResponseMissingAccessToken, authExtractTokensResponseMissingRefreshToken, authExtractTokensResponseNotObject, authProviderFeatureUsedMultipleTimes, booleanArrayQueryField, buildQueryCacheKey, buildQueryString, buildRoute, buildTimestampFromSeconds, canOverrideQueryDevtoolsTokenTtl, castQueryCreatorTypes, circularQueryDependency, circularQueryDependencyChecker, clearQueryDevtoolsArmedMocks, clearQueryDevtoolsAuthCredentials, clearQueryDevtoolsAuthSessions, clearQueryDevtoolsFaults, clearQueryDevtoolsMockStore, clearQueryDevtoolsOverrideStore, clearQueryDevtoolsStore, clearQueryDevtoolsTokenTtl, clearQueryDevtoolsTombstones, clearRestoredQueryDevtoolsOverrides, collectLeafPaths, collectQueryDevtoolsSchemaComponents, collectQueryFormLinks, computeQueryAuthHeader, computeQueryBody, computeQueryHeaders, computeQueryMethod, computeQueryQueryParams, consumeSuppressQueryStackDevtools, contentfulGqlLikePaginationAdapter, countUnresolvedQueryDevtoolsOverrides, createAuthGuard, createBaseQuery, createBaseQueryCreator, createBearerAuthProvider, createDefaultRetryFn, createDeleteQuery, createExecuteFn, createGetQuery, createGqlExecuteFn, createGqlMutationViaGet, createGqlMutationViaPost, createGqlQuery, createGqlQueryCreator, createGqlQueryViaGet, createGqlQueryViaPost, createHeadQuery, createHttpRequest, createIndexedDbQueryPersistenceAdapter, createInfinityQueryConfig, createLegacyQueryCreator, createNoopQueryPersistenceAdapter, createOptionsQuery, createPagedQueryStack, createPatchQuery, createPersistentAuthFeature, createPostQuery, createPutQuery, createQuery, createQueryBatch, createQueryClient, createQueryCollection, createQueryCollectionSignal, createQueryCollectionSubject, createQueryContext, createQueryCreator, createQueryDevtoolsFormLinks, createQueryDevtoolsFormLinksRecorder, createQueryDevtoolsOverrides, createQueryDevtoolsOverridesRecorder, createQueryDevtoolsStats, createQueryDevtoolsStatsRecorder, createQueryErrorResponse, createQueryFeature, createQueryInvalidationFilter, createQueryKeyLockManager, createQueryObject, createQueryPersistenceEngine, createQueryRepository, createQuerySnapshotFn, createQueryStack, createQuerySubmission, createQuerySyncEngine, createQuerySyncTransport, createSecureDeleteQuery, createSecureExecuteFactory, createSecureExecuteFn, createSecureGetQuery, createSecureGqlExecuteFn, createSecureGqlMutationViaGet, createSecureGqlMutationViaPost, createSecureGqlQuery, createSecureGqlQueryCreator, createSecureGqlQueryViaGet, createSecureGqlQueryViaPost, createSecureHeadQuery, createSecureOptionsQuery, createSecurePatchQuery, createSecurePostQuery, createSecurePutQuery, createSecureQuery, createSecureQueryCreator, createTrackingFeature, createWebSocketClient, currentQueryDevtoolsBatch, dateArrayQueryField, dateQueryField, decryptBearer, deepFreeze, def, defineQueryForm, deleteQueryDevtoolsMock, describeQueryDevtoolsFeatures, detectContentTypeHeader, detectPaginationShape, dynLikePaginationAdapter, effectComputed, emptyQueryArgs, ethletePaginationAdapter, executeUntilSettled, extractExpiresInSeconds, extractFormViolations, extractHtmlErrorMessage, extractQuery, fakePaginationAdapter, filterFailure, filterNull, filterQueryStates, filterSuccess, forEachHeader, forgetQueryDevtoolsAuthSession, forgetQueryDevtoolsAuthSessionsFor, formatQueryDevtoolsDuration, generateQueryDevtoolsNumberPreset, generateQueryDevtoolsSampleNumber, generateQueryDevtoolsStringPreset, getBearerAuthProviderName, getDefaultHeaders, getQueryClientName, getQueryFeatureUsage, getResponseUrl, ggLikePaginationAdapter, gql, gqlDataPropertyMissingInResponse, gqlErrorsInResponse, gqlTransformerFor, hasHeader, hasQueryDevtoolsOverridesAtPath, htmlErrorPayload, htmlQueryErrorParser, ignoreAutoRefresh, initQueryDevtoolsAuthSessions, initQueryDevtoolsFaults, initQueryDevtoolsMocks, initQueryDevtoolsOverridePersistence, initQueryDevtoolsSettings, injectInfinityQueryResponseDelay, injectQueryContext, insertFrom, invalidBaseRouteError, invalidBodyError, invalidRouteError, invalidStateInsideSecureExecuteFactory, isAutoRefresh, isBasicAuthProvider, isBearerAuthProvider, isClassValidatorError, isCreateGqlQueryOptions, isCustomHeaderAuthProvider, isDateShapedLeaf, isEmptyString, isGqlQueryConfig, isHtmlErrorPayload, isIdempotentQueryMethod, isLegacyQuery, isNaN$1 as isNaN, isPageOutOfRangeError, isQuery, isQueryCollection, isQueryDevtoolsEnabled, isQueryDevtoolsFaultArmed, isQueryDevtoolsRepositoryLive, isQueryDevtoolsRequestInterceptionEnabled, isQueryErrorResponse, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, isSymfonyError, isSymfonyFormViolationListError, isSymfonyListError, isSymfonyPagerfantaOutOfRangeError, isUnderInvalidatedUrl, legacyPrepareFallbackInjector, legacyPrepareWithoutInjectionContext, loadQueryDevtoolsSchema, loginQueryDevtoolsAuthAccount, logoutQueryDevtoolsAuthSession, mapToPaginated, mapViolationsToFormErrors, matchesQueryDevtoolsMockPattern, matchesQueryDevtoolsMockQuery, maybeExecute, measureQueryDevtoolsPayload, mergeHeaders, nestedEffect, noteQueryFormRead, noteQueryFormReads, numberArrayQueryField, pagedQueryStackNextPageCalledWithoutPreviousPage, pagedQueryStackPageBiggerThanTotalPages, pagedQueryStackPreviousPageCalledButAlreadyAtFirstPage, paginatedEntityValueUpdater, parseAllXhrResponseHeaders, parseHttpErrorCodeToMessageDe, parseHttpErrorCodeToMessageEn, parseHttpErrorCodeToTitleDe, parseHttpErrorCodeToTitleEn, parseQueryDevtoolsOverrideTransfer, parseQueryRoute, patchQueryDevtoolsTokenPayload, pathParamsMissingInRouteFunctionError, provideInfinityQueryResponseDelay, provideLegacyPrepareFallback, provideQueryClientForDevtools, provideQueryDevtools, queryArrayComputed, queryBatchAlreadyRunning, queryBatchWithArgsUsed, queryClientFeatureUsedMultipleTimes, queryComputed, queryComputedTillTruthy, queryDevtoolsAbout, queryDevtoolsAllowsLocalAuthSessions, queryDevtoolsApiEnvIds, queryDevtoolsApiEnvIsProduction, queryDevtoolsApiEnvScope, queryDevtoolsApiEnvValues, queryDevtoolsApiEnvs, queryDevtoolsArmedMocks, queryDevtoolsArmedMocksRestored, queryDevtoolsAuthAccountsFor, queryDevtoolsAuthActive, queryDevtoolsAuthFieldsFor, queryDevtoolsAuthOtherScopeCount, queryDevtoolsAuthProviders, queryDevtoolsAuthSessions, queryDevtoolsAuthSessionsFor, queryDevtoolsAuthTabLocal, queryDevtoolsEntries, queryDevtoolsFaults, queryDevtoolsFaultsRestored, queryDevtoolsFnDetail, queryDevtoolsMockId, queryDevtoolsMocks, queryDevtoolsOverridePersistence, queryDevtoolsRequestPath, queryDevtoolsResponseHistory, queryDevtoolsRestoredOverridesScope, queryDevtoolsSchemaNames, queryDevtoolsSchemaRoutes, queryDevtoolsSchemaState, queryDevtoolsSettings, queryDevtoolsStorage, queryDevtoolsTokenTtls, queryErrorMessage, queryErrorMessages, queryExecute, queryExecutedAfterDestroyMessage, queryFeatureUsedMultipleTimes, queryField, querySequence, querySequenceAlreadyRunning, queryStackWithArgsUsed, queryStackWithResponseUpdateUsed, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, queryStateErrorSignal, queryStateLoadingSignal, queryStateResponseSignal, queryStateSignal, readQueryDevtoolsAuthSeed, readQueryDevtoolsAuthSeedFor, readQueryDevtoolsStore, registerEthleteVersion, registerLiveQueryDevtoolsRepository, registerQueryDevtoolsAuthProvider, registerQueryDevtoolsEntry, registerQueryErrorParser, releaseQueryDevtoolsOverridePersistence, removeFrom, removeQueryDevtoolsAuthAccount, renameQueryDevtoolsAuthSession, request, resetExecuteState, resetPageOnError, resolveInvalidationUrl, resolveQueryDevtoolsFault, resolveQueryDevtoolsFaultForAttempt, resolveQueryDevtoolsMock, resolveQueryDevtoolsMockForAttempt, resolveQueryHeaders, restoredQueryDevtoolsOverrides, runDefaultQueryRetry, runInQueryDevtoolsBatch, runQueryErrorParsers, saveQueryDevtoolsMock, searchQueryField, seedQueryDevtoolsSchemaBody, seedQueryDevtoolsSchemaRoute, serializeBody, serializeQueryDevtoolsOverrideTransfer, setDefaultQueryRetryFn, setQueryDevtoolsApiEnv, setQueryDevtoolsApiEnvs, setQueryDevtoolsAppInfo, setQueryDevtoolsArmedMocksScope, setQueryDevtoolsAuthCredentials, setQueryDevtoolsAuthProviderRegistrar, setQueryDevtoolsAuthSeedReader, setQueryDevtoolsAuthTabLocal, setQueryDevtoolsFault, setQueryDevtoolsFaultResolver, setQueryDevtoolsFaultsScope, setQueryDevtoolsFormLinksFactory, setQueryDevtoolsMockResolver, setQueryDevtoolsOverridePersistence, setQueryDevtoolsOverridesFactory, setQueryDevtoolsOverridesScope, setQueryDevtoolsRegistrar, setQueryDevtoolsResponseHistory, setQueryDevtoolsSchemaLoader, setQueryDevtoolsSettings, setQueryDevtoolsStatsFactory, setQueryDevtoolsTokenPayloadPatcher, setQueryDevtoolsTokenTtl, setQueryDevtoolsUiMounted, setupQueryDependencies, setupQueryExecuteState, setupQueryState, shouldAutoExecuteGqlQuery, shouldAutoExecuteQuery, shouldCacheQuery, shouldRetryRequest, silenceMissingWithArgsFeatureErrorUsedButWithArgsPresent, skipPaginationPageParamCalculator, smartDuplicateArrayItem, snapshotQueryDevtoolsHandle, sortQueryField, splitQueryConfig, stringArrayQueryField, stringifyQueryRouteParts, sumQueryDevtoolsStats, suppressNextQueryStackDevtools, switchQueryCollectionState, switchQueryDevtoolsAuthSession, switchQueryState, symfonyQueryErrorParser, takeUntilResponse, toQuerySignal, toQuerySubject, tokensNotAvailableInsideAuthAndExec, tombstoneOf, trackQueryDevtoolsAuthProvider, transformArrayResponse, transformExecStateToQueryState, transformGql, transformMethod, transformPaginatedResponse, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray, uncacheableRequestHasAllowCacheParam, uncacheableRequestHasCacheKeyParam, unwrapGqlResponseWithErrors, unwrapQuerySyncMessage, v2BuildQueryCacheKey, v2ExtractExpiresInSeconds, v2ShouldCacheQuery, v2ShouldRetryRequest, validateWithQuery, validateWithV2Query, withArgs, withArgsQueryFeatureMissingButRouteIsFunction, withAuthenticationQuery, withAutoRefresh, withAutoRefreshUsedInManualQuery, withAutoRefreshUsedOnUnsupportedHttpMethod, withBearerAuthMultiTabSync, withDefaultRetry, withErrorHandling, withEthleteApiErrors, withHtmlErrorParsing, withInactivityLogout, withLogging, withLongPolling, withLongPollingUsedOnUnsupportedHttpMethod, withLongPollingUsedWithPolling, withMultiTabSync, withPageResetOnError, withPersistentAuth, withPolling, withPollingUsedOnUnsupportedHttpMethod, withQueryDevtoolsOverridePersistence, withQueryPersistence, withRefreshQuery, withResponseUpdate, withSuccessHandling, withSymfonyErrors, withTokenExpirationWarning, withTokenRevocation, withTracking, wrapAsObservableSignal, wrapQuerySyncMessage, writeQueryDevtoolsStore };
17690
18015
  //# sourceMappingURL=ethlete-query.mjs.map