@ethlete/query 6.0.0-next.48 → 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);
@@ -696,6 +784,7 @@ const withArgs = (args) => {
696
784
  },
697
785
  });
698
786
  };
787
+ const readPollingInterval = (interval) => typeof interval === 'function' ? interval() : interval;
699
788
  /**
700
789
  * A query feature that will automatically execute the query at a given interval.
701
790
  * The interval will be cleared when the query is destroyed.
@@ -713,14 +802,21 @@ const withPolling = (options) => {
713
802
  return createQueryFeature({
714
803
  type: QueryFeatureType.WITH_POLLING,
715
804
  devtools: () => [
716
- { label: 'interval', value: formatQueryDevtoolsDuration(options.interval) },
805
+ { label: 'interval', value: formatQueryDevtoolsDuration(untracked(() => readPollingInterval(options.interval))) },
717
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' }] : []),
718
810
  ],
719
811
  fn: (context) => {
720
812
  if (!context.flags.shouldAutoExecuteMethod) {
721
813
  throw withPollingUsedOnUnsupportedHttpMethod(context.flags.method);
722
814
  }
723
815
  let intervalId = null;
816
+ let timeoutId = null;
817
+ let active = false;
818
+ let currentArgs = null;
819
+ let lastTickAt = 0;
724
820
  const sync = context.deps.client.subtle.sync;
725
821
  const lockManager = sync?.isPollingDedupeEnabled && context.flags.isMultiTabSyncEnabled ? sync.lockManager : null;
726
822
  // Only ever set while dedup is active. A tick runs unless we know another tab is taking care of
@@ -753,28 +849,98 @@ const withPolling = (options) => {
753
849
  hold?.release();
754
850
  });
755
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();
756
886
  nestedEffect(() => {
757
887
  const args = context.state.args();
758
888
  untracked(() => {
759
- if (intervalId !== null)
760
- clearInterval(intervalId);
889
+ stop();
890
+ currentArgs = args;
761
891
  // Don't start polling if the query doesn't have args.
762
892
  // It should have args because a withArgs feature is present.
763
- if (args === null && context.flags.hasWithArgsFeature)
893
+ active = !(args === null && context.flags.hasWithArgsFeature);
894
+ if (!active)
764
895
  return;
765
- if (options.executeInitially && (!hold || hold.isHolder())) {
766
- 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 });
767
902
  }
768
- intervalId = window.setInterval(() => {
769
- // The interval keeps running while another tab is the holder, so leadership arriving
770
- // mid-flight needs no restart and `executeInitially`/interval semantics stay identical.
771
- if (hold && !hold.isHolder())
772
- return;
773
- context.execute({ args });
774
- }, options.interval);
903
+ schedule(currentInterval());
775
904
  });
776
905
  }, { injector: context.deps.injector });
777
- 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);
778
944
  },
779
945
  });
780
946
  };
@@ -1064,6 +1230,74 @@ const setDefaultQueryRetryFn = (fn) => {
1064
1230
  /** @internal */
1065
1231
  const runDefaultQueryRetry = (options) => defaultRetryFn$1?.(options) ?? NO_RETRY;
1066
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
+
1067
1301
  const isQueryErrorResponse = (error) => {
1068
1302
  return typeof error === 'object' && error !== null && 'raw' in error && 'retryState' in error && 'isList' in error;
1069
1303
  };
@@ -1106,7 +1340,13 @@ const createQueryErrorResponse = (error, retry) => {
1106
1340
  statusText: 'Unknown Error',
1107
1341
  });
1108
1342
  }
1109
- 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
+ };
1110
1350
  const retryState = typeof retry?.retryFn === 'function' ? retry.retryFn(retryOptions) : runDefaultQueryRetry(retryOptions);
1111
1351
  const detail = err.error;
1112
1352
  const errorList = [];
@@ -1570,6 +1810,14 @@ const createBaseQuery = (options) => {
1570
1810
  });
1571
1811
  };
1572
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
+
1573
1821
  const extractExpiresInSeconds = (headers) => {
1574
1822
  const cacheControl = headers.get('cache-control');
1575
1823
  const age = headers.get('age');
@@ -1625,7 +1873,7 @@ const sortObjectKeys = (_key, value) => {
1625
1873
  * entries written by an older version are stored under.
1626
1874
  */
1627
1875
  const buildQueryCacheKey = (route, args, method) => {
1628
- const headers = typeof args?.headers === 'function' ? args.headers() : args?.headers;
1876
+ const headers = resolveQueryHeaders(args?.headers);
1629
1877
  const serializedHeaders = headers
1630
1878
  ?.keys()
1631
1879
  .map((name) => name.toLowerCase())
@@ -1966,6 +2214,7 @@ const createHttpRequest = (options) => {
1966
2214
  ...(ngDevMode ? [{ debugName: "attempts" }] : /* istanbul ignore next */ []));
1967
2215
  const retryState = signal(null, /* @ts-ignore */
1968
2216
  ...(ngDevMode ? [{ debugName: "retryState" }] : /* istanbul ignore next */ []));
2217
+ const idempotent = options.idempotent ?? isIdempotentQueryMethod(options.method);
1969
2218
  const lastDurationMs = signal(null, /* @ts-ignore */
1970
2219
  ...(ngDevMode ? [{ debugName: "lastDurationMs" }] : /* istanbul ignore next */ []));
1971
2220
  // Set at the top of every `sendWithFaults` attempt (including unfaulted ones), so it always reflects
@@ -1982,8 +2231,8 @@ const createHttpRequest = (options) => {
1982
2231
  // Resolved per execution rather than once at creation, so a client whose headers are a function
1983
2232
  // reading a signal (a preview token, a tenant id) sees the current value on every re-run.
1984
2233
  const resolveHeaders = () => {
1985
- const clientHeaders = typeof options.clientHeaders === 'function' ? options.clientHeaders() : options.clientHeaders;
1986
- const argHeaders = typeof args?.headers === 'function' ? args.headers() : args?.headers;
2234
+ const clientHeaders = resolveQueryHeaders(options.clientHeaders);
2235
+ const argHeaders = resolveQueryHeaders(args?.headers);
1987
2236
  if (!clientHeaders)
1988
2237
  return argHeaders;
1989
2238
  if (!argHeaders)
@@ -2069,7 +2318,7 @@ const createHttpRequest = (options) => {
2069
2318
  const source$ = isQueryDevtoolsRequestInterceptionEnabled() ? sendWithFaults(headers) : send(headers);
2070
2319
  return source$.pipe(tap((event) => updateState(event)), retry({
2071
2320
  delay: (error, retryCount) => {
2072
- const retryOptions = { error, retryCount };
2321
+ const retryOptions = { error, retryCount, method: options.method, idempotent };
2073
2322
  const retryResult = options.retryFn?.(retryOptions) || runDefaultQueryRetry(retryOptions);
2074
2323
  if (!retryResult.retry) {
2075
2324
  return throwError(() => error);
@@ -2174,6 +2423,8 @@ const createHttpRequest = (options) => {
2174
2423
  const errorRes = createQueryErrorResponse(errorResponse, {
2175
2424
  retryCount: attempts(),
2176
2425
  retryFn: options.retryFn,
2426
+ method: options.method,
2427
+ idempotent,
2177
2428
  });
2178
2429
  error.set(errorRes);
2179
2430
  loading.set(null);
@@ -3766,69 +4017,6 @@ const symfonyQueryErrorParser = (detail) => {
3766
4017
  return null;
3767
4018
  };
3768
4019
 
3769
- const DEFAULT_MAX_ATTEMPTS = 3;
3770
- const DEFAULT_BASE_DELAY_MS = 1000;
3771
- const DEFAULT_MAX_DELAY_MS = 30_000;
3772
- const DEFAULT_JITTER = 0.25;
3773
- // 500 is left out on purpose: an internal server error is a bug in the backend far more often than a
3774
- // blip, and repeating the request that triggered it does not make it go away.
3775
- const isRetryableByDefault = (status) => status === 0 || status === 408 || status === 425 || status === 429 || status >= 501;
3776
- /**
3777
- * Builds a retry policy: an exponentially backing off, jittered {@link ShouldRetryRequestFn} bounded by
3778
- * an attempt ceiling. What {@link withDefaultRetry} installs - reach for this directly to hand a single
3779
- * client or creator its own `retryFn`.
3780
- *
3781
- * @example
3782
- * const flakyEndpointRetry = createDefaultRetryFn({ maxAttempts: 8, maxDelayMs: 10_000 });
3783
- *
3784
- * const getReport = myApiGet<GetReportArgs>('/report').clone({ retryFn: flakyEndpointRetry });
3785
- */
3786
- const createDefaultRetryFn = (options = {}) => {
3787
- const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
3788
- const baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS;
3789
- const maxDelayMs = options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
3790
- const jitter = clamp(options.jitter ?? DEFAULT_JITTER, 0, 1);
3791
- const retryableStatusCodes = options.retryableStatusCodes;
3792
- const isRetryableStatus = (status) => retryableStatusCodes ? retryableStatusCodes.includes(status) : isRetryableByDefault(status);
3793
- const backoff = (retryCount) => {
3794
- const exponential = baseDelayMs * Math.pow(2, retryCount);
3795
- const spread = jitter === 0 ? 1 : 1 + (Math.random() * 2 - 1) * jitter;
3796
- return Math.round(clamp(exponential * spread, 0, maxDelayMs));
3797
- };
3798
- return ({ retryCount, error }) => {
3799
- // Not an HTTP failure at all, so nothing about it says a second attempt would go any better.
3800
- if (!(error instanceof HttpErrorResponse))
3801
- return { retry: false };
3802
- if (maxAttempts > 0 && retryCount > maxAttempts)
3803
- return { retry: false };
3804
- const { status, error: detail, headers } = error;
3805
- if (!isRetryableStatus(status))
3806
- return { retry: false };
3807
- // A page past the end of a Pagerfanta collection answers 5xx, and asking for it again keeps
3808
- // answering 5xx - the request is wrong, not the server.
3809
- if (status >= 501 && isSymfonyPagerfantaOutOfRangeError(detail))
3810
- return { retry: false };
3811
- if (status === 429) {
3812
- const retryAfter = headers.get('retry-after') || headers.get('x-retry-after');
3813
- const delay = retryAfter ? parseInt(retryAfter) * 1000 : NaN;
3814
- // The server named a time, so it is used as named rather than jittered - only capped, since a
3815
- // `retry-after` of an hour would otherwise arm a timer nobody is still around for.
3816
- if (!Number.isNaN(delay))
3817
- return { retry: true, delay: Math.min(delay, maxDelayMs) };
3818
- }
3819
- return { retry: true, delay: backoff(retryCount) };
3820
- };
3821
- };
3822
- const defaultRetryFn = /* @__PURE__ */ createDefaultRetryFn();
3823
- /**
3824
- * The SDK's default retry policy, installed by {@link withDefaultRetry}: three retries with an
3825
- * exponentially backing off, jittered delay, for a connection failure, a `408`, a `425`, a `429`
3826
- * (honouring `retry-after`) and a 5xx above `500`.
3827
- *
3828
- * Pass {@link createDefaultRetryFn} options to {@link withDefaultRetry} to configure it.
3829
- */
3830
- const shouldRetryRequest = (options) => defaultRetryFn(options instanceof HttpErrorResponse ? { retryCount: 0, error: options } : options);
3831
-
3832
4020
  const noop$2 = () => undefined;
3833
4021
  /** Shared because it never changes: without Web Locks, every hold is granted and never lost. */
3834
4022
  const alwaysHolder = { isHolder: /* @__PURE__ */ signal(true).asReadonly(), release: noop$2 };
@@ -3915,6 +4103,7 @@ const createQueryKeyLockManager = (namespace) => {
3915
4103
  return { hold, isSupported: true, keyStates: keyStates.asReadonly() };
3916
4104
  };
3917
4105
 
4106
+ /** @internal */
3918
4107
  const createQuerySyncEngine = (options) => {
3919
4108
  const { config, repository, transport, lockManager } = options;
3920
4109
  const syncResponses = config.syncResponses ?? true;
@@ -4248,7 +4437,8 @@ const withSymfonyErrors = () => () => {
4248
4437
  /**
4249
4438
  * Gives this client the SDK's default retry policy for every request that does not bring its own `retryFn`: a
4250
4439
  * connection failure, a 5xx above 500, a 408/425 and a 429 (honouring `retry-after`) are retried three
4251
- * 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`.
4252
4442
  *
4253
4443
  * The policy belongs to the client the feature is on, so another client of the same app is not
4254
4444
  * affected. Without it nothing is retried automatically and `error.retryState` always reads
@@ -4386,6 +4576,7 @@ const createQueryRepository = (config) => {
4386
4576
  clientHeaders: config.headers,
4387
4577
  cacheAdapter: config.cacheAdapter,
4388
4578
  retryFn: options.retryFn ?? config.retryFn,
4579
+ idempotent: options.isRefreshable ?? isIdempotentQueryMethod(options.method),
4389
4580
  });
4390
4581
  const executed = request.execute();
4391
4582
  bind({
@@ -4738,7 +4929,7 @@ const createQueryClient = (options) => defineRootProvider(() => {
4738
4929
  name: `QueryClient_${options.name}`,
4739
4930
  });
4740
4931
 
4741
- const AUTH_HEADER = 'Authorization';
4932
+ const AUTH_HEADER$1 = 'Authorization';
4742
4933
  /**
4743
4934
  * How long a request waits for the refresh of an expired access token before it sends the expired one
4744
4935
  * anyway. Nothing guarantees the refresh arrives - the refresh token can be spent, and the request can
@@ -4808,12 +4999,12 @@ const createSecureExecuteFactory = (options) => {
4808
4999
  const args = executeArgs.args;
4809
5000
  const headerProvider = () => {
4810
5001
  const accessToken = options.authProvider.accessToken();
4811
- const baseHeaders = typeof args?.headers === 'function' ? args.headers() : args?.headers || new HttpHeaders();
5002
+ const baseHeaders = resolveQueryHeaders(args?.headers) ?? new HttpHeaders();
4812
5003
  if (!accessToken) {
4813
5004
  throw tokensNotAvailableInsideAuthAndExec();
4814
5005
  }
4815
- if (!baseHeaders.has(AUTH_HEADER)) {
4816
- return baseHeaders.set(AUTH_HEADER, `Bearer ${accessToken}`);
5006
+ if (!baseHeaders.has(AUTH_HEADER$1)) {
5007
+ return baseHeaders.set(AUTH_HEADER$1, `Bearer ${accessToken}`);
4817
5008
  }
4818
5009
  return baseHeaders;
4819
5010
  };
@@ -5798,6 +5989,7 @@ const createBearerAuthProviderImpl = (config) => {
5798
5989
  activityCoordination: isLeader.activityCoordination,
5799
5990
  afterTokenRefresh$,
5800
5991
  queries: queries,
5992
+ queryBuilders: config.queries,
5801
5993
  latestExecutedQuery: latestExecutedQuery.asReadonly(),
5802
5994
  executionState,
5803
5995
  sessionStatus: sessionStatus.asReadonly(),
@@ -5834,6 +6026,11 @@ const createBearerAuthProviderImpl = (config) => {
5834
6026
  executionState: executionState.asReadonly(),
5835
6027
  sessionStatus: sessionStatus.asReadonly(),
5836
6028
  sessionEndCause: sessionEndCause.asReadonly(),
6029
+ session: computed(() => ({
6030
+ status: sessionStatus(),
6031
+ endCause: sessionEndCause(),
6032
+ executionState: executionState(),
6033
+ })),
5837
6034
  setTokens,
5838
6035
  logout,
5839
6036
  afterTokenRefresh$,
@@ -5935,7 +6132,7 @@ const withAuthenticationQuery = (key, config, setup) => ({
5935
6132
  const withRefreshQuery = (key, config) => {
5936
6133
  const retryableStatusCodes = config.retryConfig?.retryableStatusCodes ?? [0, 408, 425, 429, 500, 502, 503, 504];
5937
6134
  const maxRetryDelayMs = config.retryConfig?.maxRetryDelayMs ?? 30000; // 30 seconds
5938
- const maxAttempts = config.retryConfig?.maxAttempts ?? 0; // 0 = unlimited
6135
+ const maxAttempts = config.retryConfig?.maxAttempts ?? 8;
5939
6136
  const refreshRetryFn = ({ error, retryCount }) => {
5940
6137
  const { status } = error;
5941
6138
  if (maxAttempts > 0 && retryCount > maxAttempts) {
@@ -7316,12 +7513,20 @@ const createPersistentAuthFeature = (config, context) => {
7316
7513
  if (wasRejected && !context.isTabLocalSession())
7317
7514
  removeCookie();
7318
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
+ };
7319
7524
  const exchangeCookie = () => {
7320
7525
  const storedToken = getCookie(cookieName);
7321
7526
  if (!storedToken)
7322
7527
  return;
7323
7528
  const decryptedToken = decryptToken(storedToken);
7324
- const args = config.autoLogin.buildArgs(decryptedToken);
7529
+ const args = buildAutoLoginArgs(decryptedToken);
7325
7530
  context.queries[config.autoLogin.queryKey].execute(args, { triggeredBy: 'persistent-auth' });
7326
7531
  };
7327
7532
  const tryLogin = () => {
@@ -7369,7 +7574,10 @@ const withTokenExpirationWarning = (config = {}) => {
7369
7574
  return (context) => {
7370
7575
  const warningThreshold = config.warningThreshold ?? 5 * 60 * 1000;
7371
7576
  const checkInterval = config.checkInterval ?? 1000;
7372
- 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';
7373
7581
  const expiresAt = computed(() => {
7374
7582
  const decoded = context.bearerData();
7375
7583
  if (!decoded)
@@ -7413,9 +7621,21 @@ const withTokenExpirationWarning = (config = {}) => {
7413
7621
  };
7414
7622
  };
7415
7623
 
7624
+ const AUTH_HEADER = 'Authorization';
7416
7625
  const withTokenRevocation = (config) => {
7417
7626
  return (context) => {
7418
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));
7419
7639
  const enabled = signal(true, /* @ts-ignore */
7420
7640
  ...(ngDevMode ? [{ debugName: "enabled" }] : /* istanbul ignore next */ []));
7421
7641
  let previousAccessToken = null;
@@ -7427,7 +7647,7 @@ const withTokenRevocation = (config) => {
7427
7647
  if (!accessToken && !refreshToken) {
7428
7648
  return null;
7429
7649
  }
7430
- const args = config.buildArgs({ accessToken, refreshToken });
7650
+ const args = withBearerHeader(config.buildArgs?.({ accessToken, refreshToken }) ?? {}, accessToken);
7431
7651
  context.executionState.set({ type: 'revocation', state: 'loading' });
7432
7652
  currentRevocationSnapshot = context.queries[config.queryKey].execute(args, {
7433
7653
  triggeredBy: 'token-revocation',
@@ -7447,7 +7667,10 @@ const withTokenRevocation = (config) => {
7447
7667
  effect(() => {
7448
7668
  const currentToken = context.accessToken();
7449
7669
  const currentRefreshToken = context.refreshToken();
7450
- if (previousAccessToken && !currentToken && enabled()) {
7670
+ if (previousAccessToken &&
7671
+ !currentToken &&
7672
+ enabled() &&
7673
+ shouldRevokeFor(untracked(context.sessionEndCause))) {
7451
7674
  untracked(() => revokeWithTokens(previousAccessToken, previousRefreshToken));
7452
7675
  }
7453
7676
  previousAccessToken = currentToken;
@@ -7465,7 +7688,8 @@ const withTokenRevocation = (config) => {
7465
7688
  instance,
7466
7689
  devtools: () => [
7467
7690
  { label: 'query', value: config.queryKey },
7468
- { 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' },
7469
7693
  ],
7470
7694
  };
7471
7695
  };
@@ -8474,12 +8698,13 @@ const asScope = (value, fallback) => value === 'none' || value === 'session' ||
8474
8698
  * Whether the session vault may be kept in `localStorage`. Only a development build may: the vault
8475
8699
  * holds live access and refresh tokens plus the credentials somebody typed in, and a deployed
8476
8700
  * application that mounts the panel for its debugging value must not leave those on the machine of
8477
- * 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.
8478
8703
  *
8479
8704
  * Part of the devtools contract. **Not part of the general public contract.**
8480
8705
  */
8481
8706
  const queryDevtoolsAllowsLocalAuthSessions = () => isDevMode();
8482
- const clampAuthSessions = (scope) => scope === 'local' && !queryDevtoolsAllowsLocalAuthSessions() ? 'session' : scope;
8707
+ const clampAuthSessions = (scope) => scope === 'local' && !queryDevtoolsAllowsLocalAuthSessions() ? 'none' : scope;
8483
8708
  const asCount = (value, limits, fallback) => {
8484
8709
  const count = Math.floor(Number(value));
8485
8710
  if (!Number.isFinite(count))
@@ -8684,11 +8909,15 @@ let listening = false;
8684
8909
  * @internal
8685
8910
  */
8686
8911
  const initQueryDevtoolsAuthSessions = (accounts) => {
8687
- // Before the read below, which never looks at `localStorage` once the scope is clamped: a vault an
8688
- // earlier version left there would otherwise sit on a deployed origin with nothing left to remove it.
8689
- if (!queryDevtoolsAllowsLocalAuthSessions())
8690
- queryDevtoolsStorage('local')?.removeItem(STORE_KEY);
8691
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);
8692
8921
  declared.set(accounts ?? []);
8693
8922
  store.set(sanitize(readQueryDevtoolsStore(scope, STORE_KEY)));
8694
8923
  active.set(readQueryDevtoolsStore(scope === 'none' ? 'none' : 'session', ACTIVE_KEY) ?? {});
@@ -10283,7 +10512,7 @@ const armQueryDevtoolsOverrideTransfer = (recorder, ops) => {
10283
10512
 
10284
10513
  // Generated from package.json by tools/scripts/generate-version.js - do not edit.
10285
10514
  /** The version of `@ethlete/query` this build was cut from. */
10286
- const QUERY_VERSION = '6.0.0-next.48';
10515
+ const QUERY_VERSION = '6.0.0-next.49';
10287
10516
 
10288
10517
  /** How deep a schema is expanded before a nested branch is given up on. */
10289
10518
  const MAX_DEPTH = 12;
@@ -11680,6 +11909,7 @@ const gql = (strings, ...values) => {
11680
11909
  return str;
11681
11910
  };
11682
11911
 
11912
+ /** @internal */
11683
11913
  const createGqlExecuteFn = (executeOptions) => {
11684
11914
  const executeState = setupQueryExecuteState();
11685
11915
  const circularChecker = circularQueryDependencyChecker();
@@ -11737,7 +11967,7 @@ const createGqlQuery = (options) => createBaseQuery({
11737
11967
  executeFactory: createGqlExecuteFn,
11738
11968
  });
11739
11969
 
11740
- const unwrapGqlResponse = (rawResponse) => {
11970
+ const readGqlEnvelope = (rawResponse) => {
11741
11971
  if (!rawResponse || typeof rawResponse !== 'object')
11742
11972
  throw gqlDataPropertyMissingInResponse();
11743
11973
  const data = 'data' in rawResponse ? rawResponse.data : undefined;
@@ -11746,8 +11976,14 @@ const unwrapGqlResponse = (rawResponse) => {
11746
11976
  throw gqlErrorsInResponse(errors);
11747
11977
  if (!('data' in rawResponse))
11748
11978
  throw gqlDataPropertyMissingInResponse();
11749
- return data;
11979
+ return { data, errors: Array.isArray(errors) ? errors : [] };
11750
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);
11751
11987
 
11752
11988
  const createGqlQueryCreator = (options, internals) => createBaseQueryCreator({
11753
11989
  options: {
@@ -11758,6 +11994,7 @@ const createGqlQueryCreator = (options, internals) => createBaseQueryCreator({
11758
11994
  queryFactory: createGqlQuery,
11759
11995
  });
11760
11996
 
11997
+ /** @internal */
11761
11998
  const createSecureGqlExecuteFn = (executeOptions) => {
11762
11999
  const authProvider = executeOptions.creatorInternals.authProvider.inject();
11763
12000
  return createSecureExecuteFactory({
@@ -16876,6 +17113,36 @@ const normalizeLive = (fieldDefs, rawLive, defaults) => {
16876
17113
  }
16877
17114
  return live;
16878
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
+ };
16879
17146
  const computeFilterCount = (fields, value, defaults) => {
16880
17147
  let count = 0;
16881
17148
  for (const [key, def] of Object.entries(fields)) {
@@ -16973,6 +17240,11 @@ const createBranch = (fields, initial, injector) => {
16973
17240
  model.set(clone(next));
16974
17241
  }
16975
17242
  };
17243
+ const write = (next, options) => {
17244
+ model.set(next);
17245
+ if (!options?.debounce)
17246
+ flush();
17247
+ };
16976
17248
  effect(() => {
16977
17249
  const live = normalizedLive();
16978
17250
  untracked(() => {
@@ -16995,13 +17267,13 @@ const createBranch = (fields, initial, injector) => {
16995
17267
  value: committed.asReadonly(),
16996
17268
  liveValue: model.asReadonly(),
16997
17269
  activeFilterCount: computed(() => computeFilterCount(fields, committed(), defaults)),
16998
- setValue: (value) => model.set(clone(value)),
16999
- patchValue: (value) => model.update((cur) => ({ ...cur, ...value })),
17000
- resetFieldToDefault: (key) => model.update((cur) => ({ ...cur, [key]: defaultFor(key) })),
17001
- 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) => {
17002
17274
  for (const key of Object.keys(fields))
17003
17275
  defaultFor(key);
17004
- model.set(clone(defaults));
17276
+ write(clone(defaults), options);
17005
17277
  },
17006
17278
  };
17007
17279
  };
@@ -17063,6 +17335,7 @@ const defineQueryForm = (config) => {
17063
17335
  const skipResetsFor = new Set();
17064
17336
  let skipNextResetsFor;
17065
17337
  let urlWriteVersion = 0;
17338
+ let changesSeenAtObserve;
17066
17339
  const urlNavigationMarker = {};
17067
17340
  const pathOf = (tree) => router.serializeUrl(tree).split(/[?#]/)[0];
17068
17341
  const fields = form(model);
@@ -17080,6 +17353,9 @@ const defineQueryForm = (config) => {
17080
17353
  const writeDefault = def.appendDefaultValueToUrl === true;
17081
17354
  if (!writeToUrl || (isDefault && !writeDefault))
17082
17355
  return undefined;
17356
+ return serialize(key, def, value);
17357
+ };
17358
+ const serialize = (key, def, value) => {
17083
17359
  if (def.valueToQueryParam)
17084
17360
  return def.valueToQueryParam(value);
17085
17361
  if (value === '' && defaults[key] === null)
@@ -17088,16 +17364,47 @@ const defineQueryForm = (config) => {
17088
17364
  return ET_EMPTY_ARRAY_VALUE;
17089
17365
  return value === null ? ET_NULL_VALUE : value;
17090
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
+ };
17091
17398
  const deserialize = (def, raw) => {
17092
17399
  if (!def.skipAutoTransform && raw === ET_NULL_VALUE)
17093
17400
  return null;
17094
17401
  if (!def.skipAutoTransform && raw === ET_EMPTY_ARRAY_VALUE)
17095
17402
  return [];
17096
17403
  if (def.queryParamToValue)
17097
- return def.queryParamToValue(raw);
17404
+ return def.queryParamToValue(raw) ?? resolveDefault(def);
17098
17405
  if (def.skipAutoTransform)
17099
17406
  return raw;
17100
- return autoCoerce(raw, resolveDefault(def));
17407
+ return autoCoerce(raw, resolveDefault(def)) ?? resolveDefault(def);
17101
17408
  };
17102
17409
  const clearTimer = () => {
17103
17410
  if (pendingTimer !== null) {
@@ -17173,6 +17480,7 @@ const defineQueryForm = (config) => {
17173
17480
  if (observing() && observeOptions?.writeToQueryParams !== false) {
17174
17481
  writeToUrl(next);
17175
17482
  }
17483
+ persist();
17176
17484
  };
17177
17485
  const onLiveChange = (live) => {
17178
17486
  if (equal(live, committed())) {
@@ -17203,6 +17511,7 @@ const defineQueryForm = (config) => {
17203
17511
  committed.set(clone(next));
17204
17512
  // Feed the URL value into the live model & bound controls; the effect no-ops (model === committed).
17205
17513
  model.set(clone(next));
17514
+ persist();
17206
17515
  };
17207
17516
  const applyFromUrl = (params, options) => {
17208
17517
  const current = { ...(options?.base ?? committed()) };
@@ -17248,6 +17557,12 @@ const defineQueryForm = (config) => {
17248
17557
  }
17249
17558
  }
17250
17559
  model.set(next);
17560
+ if (options?.debounce) {
17561
+ onLiveChange(next);
17562
+ }
17563
+ else {
17564
+ flush();
17565
+ }
17251
17566
  };
17252
17567
  const setValue = (value, options) => writeModel(clone(value), options);
17253
17568
  const patchValue = (value, options) => writeModel({ ...model(), ...value }, options);
@@ -17278,6 +17593,10 @@ const defineQueryForm = (config) => {
17278
17593
  }
17279
17594
  observeOptions = options;
17280
17595
  observing.set(true);
17596
+ changesSeenAtObserve = untracked(queryParamChanges);
17597
+ if (options?.persistence) {
17598
+ restoreFromStorage(options.persistence, route.snapshot.queryParams);
17599
+ }
17281
17600
  if (options?.syncOnNavigation !== false) {
17282
17601
  // A value written before `observe()` sits in the model only, so the URL merges onto the model
17283
17602
  // here. Against `committed()` any single URL param would drop that value.
@@ -17293,6 +17612,7 @@ const defineQueryForm = (config) => {
17293
17612
  if (options?.writeToQueryParams !== false && writesAnyParam) {
17294
17613
  writeToUrl(committed());
17295
17614
  }
17615
+ persist();
17296
17616
  return queryForm;
17297
17617
  },
17298
17618
  unobserve: () => {
@@ -17325,6 +17645,10 @@ const defineQueryForm = (config) => {
17325
17645
  untracked(() => {
17326
17646
  if (!observing() || observeOptions?.syncOnNavigation === false)
17327
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;
17328
17652
  const info = router.lastSuccessfulNavigation()?.extras.info;
17329
17653
  // Including the newest write: re-parsing the form's own output would coerce a committed
17330
17654
  // string back to a number and drop the milliseconds of a committed Date.
@@ -17474,10 +17798,12 @@ const messageMalformed = () => {
17474
17798
  const MAX_DEVTOOLS_MESSAGES = 100;
17475
17799
  const createWebSocketClient = (options) => {
17476
17800
  return defineRootProvider(() => {
17801
+ const auth = options.auth;
17477
17802
  const socket = options.io(options.url, {
17478
- withCredentials: true,
17803
+ withCredentials: options.withCredentials ?? true,
17479
17804
  autoConnect: false,
17480
17805
  transports: options.transports,
17806
+ auth: auth ? (cb) => cb(typeof auth === 'function' ? auth() : auth) : undefined,
17481
17807
  });
17482
17808
  const rooms = new Map();
17483
17809
  const joinsDeliveredThisConnection = new Set();
@@ -17527,10 +17853,11 @@ const createWebSocketClient = (options) => {
17527
17853
  }
17528
17854
  emit({ event: 'join-room', data: name, room: name });
17529
17855
  joinsDeliveredToClosedConnection.delete(name);
17530
- const message = signal(null, /* @ts-ignore */
17531
- ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
17856
+ const messages = new Subject();
17532
17857
  const newRoom = {
17533
- latestMessage: message,
17858
+ latestMessage: signal(null),
17859
+ messages,
17860
+ messages$: messages.asObservable(),
17534
17861
  joinCount: 1,
17535
17862
  };
17536
17863
  rooms.set(name, newRoom);
@@ -17583,6 +17910,7 @@ const createWebSocketClient = (options) => {
17583
17910
  emit({ event: 'leave-room', data: room, room });
17584
17911
  rooms.delete(room);
17585
17912
  syncDevtoolsRooms();
17913
+ joinedRoom.messages.complete();
17586
17914
  };
17587
17915
  const setupWebSocketConnectionListener = () => {
17588
17916
  socket.onAnyOutgoing((eventName, room) => {
@@ -17616,6 +17944,7 @@ const createWebSocketClient = (options) => {
17616
17944
  };
17617
17945
  const setupWebSocketListener = () => {
17618
17946
  socket.onAny((_eventName, ...args) => {
17947
+ let json;
17619
17948
  try {
17620
17949
  const data = args[0];
17621
17950
  if (typeof data !== 'string')
@@ -17626,11 +17955,7 @@ const createWebSocketClient = (options) => {
17626
17955
  typeof parsed.room !== 'string') {
17627
17956
  throw messageMalformed();
17628
17957
  }
17629
- const json = parsed;
17630
- recordDevtoolsMessage({ room: json.room, event: json.event, data: json.data, direction: 'in' });
17631
- const room = rooms.get(json.room);
17632
- if (room)
17633
- room.latestMessage.set(json);
17958
+ json = parsed;
17634
17959
  }
17635
17960
  catch (error) {
17636
17961
  if (!isDevMode())
@@ -17638,15 +17963,26 @@ const createWebSocketClient = (options) => {
17638
17963
  console.error(error);
17639
17964
  throw messageMalformed();
17640
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);
17641
17972
  });
17642
17973
  };
17643
- inject(DestroyRef).onDestroy(() => socket.disconnect());
17974
+ inject(DestroyRef).onDestroy(() => {
17975
+ socket.disconnect();
17976
+ for (const room of rooms.values())
17977
+ room.messages.complete();
17978
+ });
17644
17979
  setupWebSocketConnectionListener();
17645
17980
  setupWebSocketListener();
17646
17981
  socket.connect();
17647
17982
  const client = {
17648
17983
  joinRoom,
17649
17984
  isConnected: isConnected.asReadonly(),
17985
+ send: (message) => emit({ event: message.event, data: message.data }),
17650
17986
  subtle: {
17651
17987
  leaveRoom,
17652
17988
  },
@@ -17675,5 +18011,5 @@ const createWebSocketClient = (options) => {
17675
18011
  * Generated bundle index. Do not edit.
17676
18012
  */
17677
18013
 
17678
- 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, 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 };
17679
18015
  //# sourceMappingURL=ethlete-query.mjs.map