@ethlete/query 6.0.0-next.23 → 6.0.0-next.24
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.
- package/CHANGELOG.md +45 -0
- package/fesm2022/ethlete-query.mjs +1333 -117
- package/fesm2022/ethlete-query.mjs.map +1 -1
- package/generators/deprecate-legacy-queries/migration.js +77 -0
- package/generators/deprecate-legacy-queries/schema.json +26 -0
- package/generators/generators.json +5 -0
- package/generators/migrate-to-query-v3/legacy-prepare-migration.js +111 -18
- package/generators/migrate-to-query-v3/legacy-query-creator-migration.js +2 -2
- package/generators/migrate-to-query-v3/shared.js +43 -0
- package/package.json +1 -1
- package/types/ethlete-query.d.ts +1187 -15
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { DestroyRef, InjectionToken, inject, Injector, EnvironmentInjector, ElementRef, ErrorHandler, createEnvironmentInjector, effect, untracked, signal, linkedSignal, computed, runInInjectionContext, isDevMode, PLATFORM_ID, resource, DOCUMENT, makeEnvironmentProviders, assertInInjectionContext, ChangeDetectorRef, ViewContainerRef, TemplateRef, Input, Directive, input, Pipe, ViewEncapsulation, Component, NgZone } from '@angular/core';
|
|
2
|
+
import { DestroyRef, InjectionToken, inject, Injector, EnvironmentInjector, ElementRef, ErrorHandler, createEnvironmentInjector, effect, untracked, signal, linkedSignal, computed, runInInjectionContext, isDevMode, PLATFORM_ID, resource, DOCUMENT, makeEnvironmentProviders, assertInInjectionContext, provideEnvironmentInitializer, ChangeDetectorRef, ViewContainerRef, TemplateRef, Input, Directive, input, Pipe, ViewEncapsulation, Component, NgZone } from '@angular/core';
|
|
3
3
|
import { defineStaticProvider, RuntimeError, clamp, randomId, defineRootProvider, isObject, injectUnsavedChangesCoordinator, injectRoute, getCookie, getDomain, setCookie, deleteCookie, createDestroy, computedTillTruthy, defineProvider, toProvideFn, toInjectFn, nextFrame, injectQueryParams, injectQueryParamChanges, clone, equal, ET_PROPERTY_REMOVED, previousSignalValue } from '@ethlete/core';
|
|
4
|
-
import { filter, Subject, Subscription,
|
|
5
|
-
import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
4
|
+
import { filter, Subject, Subscription, defer, throwError, timer, switchMap, tap, retry, catchError, take, of, firstValueFrom, merge, fromEvent, throttleTime, interval, combineLatest, map, Observable, takeUntil, startWith, pairwise, BehaviorSubject, ReplaySubject, shareReplay, skip, takeWhile, EMPTY, withLatestFrom, debounceTime } from 'rxjs';
|
|
6
5
|
import { HttpClient, HttpEventType, HttpErrorResponse, HttpHeaders, HttpStatusCode } from '@angular/common/http';
|
|
6
|
+
import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
7
7
|
import { getActiveConsumer, setActiveConsumer } from '@angular/core/primitives/signals';
|
|
8
8
|
import { isPlatformBrowser, NgClass, AsyncPipe, JsonPipe } from '@angular/common';
|
|
9
9
|
import { validateAsync, form } from '@angular/forms/signals';
|
|
@@ -12,6 +12,45 @@ import { FormControl, ReactiveFormsModule, FormGroup } from '@angular/forms';
|
|
|
12
12
|
import { Router, ActivatedRoute } from '@angular/router';
|
|
13
13
|
import { io } from 'socket.io-client';
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Turns the features applied at creation into their devtools representation.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
const describeQueryDevtoolsFeatures = (features) => features?.map((feature) => ({ type: feature.type, details: feature.devtools?.() ?? [] })) ?? [];
|
|
20
|
+
/**
|
|
21
|
+
* Renders a millisecond duration the way feature details spell one out - `500ms`, `1.5s`, `15m`.
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
const formatQueryDevtoolsDuration = (ms) => {
|
|
25
|
+
if (!Number.isFinite(ms))
|
|
26
|
+
return String(ms);
|
|
27
|
+
if (ms < 1000)
|
|
28
|
+
return `${ms}ms`;
|
|
29
|
+
const round = (value, unit) => `${Number(value.toFixed(1))}${unit}`;
|
|
30
|
+
if (ms < 60_000)
|
|
31
|
+
return round(ms / 1000, 's');
|
|
32
|
+
if (ms < 3_600_000)
|
|
33
|
+
return round(ms / 60_000, 'm');
|
|
34
|
+
return round(ms / 3_600_000, 'h');
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The name of a function passed as `options.<key>`, or `null` when it is an inline lambda. An inline
|
|
38
|
+
* `{ handler: (e) => … }` inherits the name `handler` from the key it is assigned to, which tells the
|
|
39
|
+
* reader nothing - a reference to a declared function does.
|
|
40
|
+
*/
|
|
41
|
+
const queryDevtoolsFnName = (fn, key) => {
|
|
42
|
+
const name = typeof fn === 'function' ? fn.name.replace(/^bound /, '') : '';
|
|
43
|
+
return !name || name === key ? null : name;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* A detail for the function passed as `options.<key>`, omitted when it is an inline lambda.
|
|
47
|
+
* @internal
|
|
48
|
+
*/
|
|
49
|
+
const queryDevtoolsFnDetail = (fn, key) => {
|
|
50
|
+
const name = queryDevtoolsFnName(fn, key);
|
|
51
|
+
return name ? [{ label: key, value: name }] : [];
|
|
52
|
+
};
|
|
53
|
+
|
|
15
54
|
const noop$2 = () => undefined;
|
|
16
55
|
let registrar = null;
|
|
17
56
|
/**
|
|
@@ -33,6 +72,85 @@ const isQueryDevtoolsEnabled = () => registrar !== null;
|
|
|
33
72
|
* @internal
|
|
34
73
|
*/
|
|
35
74
|
const registerQueryDevtoolsEntry = (entry) => registrar?.(entry) ?? noop$2;
|
|
75
|
+
let statsFactory = null;
|
|
76
|
+
/**
|
|
77
|
+
* Installs the stats recorder factory. Called by `provideQueryDevtools()`; nothing else may call it.
|
|
78
|
+
* @internal
|
|
79
|
+
*/
|
|
80
|
+
const setQueryDevtoolsStatsFactory = (fn) => {
|
|
81
|
+
statsFactory = fn;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* A stats recorder for one entry, or `null` unless {@link provideQueryDevtools} has been called.
|
|
85
|
+
*
|
|
86
|
+
* Instrumentation sites must go through this instead of importing `createQueryDevtoolsStats`: a static
|
|
87
|
+
* import pins the whole stats module (~1.4 kB) into every bundle that touches a query, and a runtime
|
|
88
|
+
* `isQueryDevtoolsEnabled()` guard at the call site does not undo that. Reached only from the provider,
|
|
89
|
+
* it is dead code in an app without devtools.
|
|
90
|
+
* @internal
|
|
91
|
+
*/
|
|
92
|
+
const createQueryDevtoolsStatsRecorder = () => statsFactory?.() ?? null;
|
|
93
|
+
let formLinksFactory = null;
|
|
94
|
+
/**
|
|
95
|
+
* Installs the form-links recorder factory. Called by `provideQueryDevtools()`; nothing else may call it.
|
|
96
|
+
* @internal
|
|
97
|
+
*/
|
|
98
|
+
const setQueryDevtoolsFormLinksFactory = (fn) => {
|
|
99
|
+
formLinksFactory = fn;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* A form-links recorder for one query, or `null` unless {@link provideQueryDevtools} has been called.
|
|
103
|
+
* Reached only from the provider for the same reason {@link createQueryDevtoolsStatsRecorder} is.
|
|
104
|
+
* @internal
|
|
105
|
+
*/
|
|
106
|
+
const createQueryDevtoolsFormLinksRecorder = () => formLinksFactory?.() ?? null;
|
|
107
|
+
/** The ids of the query forms read by the args build currently running, or `null` outside of one. */
|
|
108
|
+
let formLinkSink = null;
|
|
109
|
+
/**
|
|
110
|
+
* Runs `build` and reports which query forms it read. Nested builds are supported - the outer one
|
|
111
|
+
* resumes collecting once the inner one is done.
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
const collectQueryFormLinks = (build) => {
|
|
115
|
+
const outer = formLinkSink;
|
|
116
|
+
const sink = new Set();
|
|
117
|
+
formLinkSink = sink;
|
|
118
|
+
try {
|
|
119
|
+
return { value: build(), ids: [...sink] };
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
formLinkSink = outer;
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Records that a query form's committed value was read. The form calls this from its `value` signal, so
|
|
127
|
+
* a read that happens while a query builds its args is what links the two - no naming convention, and
|
|
128
|
+
* nothing to keep in sync when the args change.
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
131
|
+
const noteQueryFormRead = (id) => {
|
|
132
|
+
formLinkSink?.add(id);
|
|
133
|
+
};
|
|
134
|
+
let faultResolver = null;
|
|
135
|
+
/**
|
|
136
|
+
* Installs the fault resolver. Called by `provideQueryDevtools()`; nothing else may call it.
|
|
137
|
+
* @internal
|
|
138
|
+
*/
|
|
139
|
+
const setQueryDevtoolsFaultResolver = (fn) => {
|
|
140
|
+
faultResolver = fn;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* Whether fault injection can do anything at all. Checked before building the wrapper that resolves a
|
|
144
|
+
* fault per attempt, so a request in an app without devtools keeps the pipeline it always had.
|
|
145
|
+
* @internal
|
|
146
|
+
*/
|
|
147
|
+
const isQueryDevtoolsFaultInjectionEnabled = () => faultResolver !== null;
|
|
148
|
+
/**
|
|
149
|
+
* Resolves the fault to apply to one upcoming attempt, or `null` when nothing is armed. Consumes a
|
|
150
|
+
* `failNext` budget, so call it exactly once per attempt.
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
const resolveQueryDevtoolsFault = (target) => faultResolver?.(target) ?? null;
|
|
36
154
|
let suppressStackRegistration = false;
|
|
37
155
|
/**
|
|
38
156
|
* Suppresses devtools registration for the next {@link createQueryStack} call. Used by
|
|
@@ -230,12 +348,15 @@ const invalidStateInsideSecureExecuteFactory = () => {
|
|
|
230
348
|
const circularQueryDependency = () => {
|
|
231
349
|
return new RuntimeError(QueryRuntimeErrorCode.CIRCULAR_QUERY_DEPENDENCY, `Query was executed more than 5 times in less than 100ms. This is usually a sign of a circular dependency.`);
|
|
232
350
|
};
|
|
233
|
-
const legacyPrepareWithoutInjectionContext = (creatorName) => {
|
|
351
|
+
const legacyPrepareWithoutInjectionContext = (creatorName, method = 'prepare') => {
|
|
234
352
|
const label = creatorName ? `"${creatorName}"` : 'A legacy query creator';
|
|
235
|
-
|
|
353
|
+
const example = method === 'prepare'
|
|
354
|
+
? ` search = (term: string) => myQuery.prepare({ queryParams: { term }, injector: this.injector });`
|
|
355
|
+
: ` container = myQuery.${method}(null, { injector: this.injector });`;
|
|
356
|
+
return new RuntimeError(QueryRuntimeErrorCode.LEGACY_PREPARE_WITHOUT_INJECTION_CONTEXT, `${label}.${method}() was called outside of an injection context. Unlike the v2 client, the legacy interop layer needs an injector to build the underlying query.\n\n` +
|
|
236
357
|
`Capture one where a context does exist (field initializer, constructor) and pass it along:\n\n` +
|
|
237
358
|
` private injector = inject(Injector);\n` +
|
|
238
|
-
|
|
359
|
+
`${example}\n\n` +
|
|
239
360
|
`Prefer an injector that outlives the call site. A component injector passed from a callback that can still fire after the component is gone leaves prepare() with a destroyed injector, which returns an inert query instead of executing.`);
|
|
240
361
|
};
|
|
241
362
|
const querySequenceAlreadyRunning = () => {
|
|
@@ -322,7 +443,7 @@ const withArgs = (args) => {
|
|
|
322
443
|
// auto-refresh) always sees the latest value with no pending-effect window. Sentinels are
|
|
323
444
|
// resolved here: CLEAR -> null, `null` ("don't change") -> keep the previous value.
|
|
324
445
|
let previous = null;
|
|
325
|
-
|
|
446
|
+
const resolve = () => {
|
|
326
447
|
const value = args();
|
|
327
448
|
if (value === CLEAR_QUERY_ARGS) {
|
|
328
449
|
previous = null;
|
|
@@ -332,7 +453,12 @@ const withArgs = (args) => {
|
|
|
332
453
|
return previous;
|
|
333
454
|
previous = value;
|
|
334
455
|
return value;
|
|
335
|
-
}
|
|
456
|
+
};
|
|
457
|
+
// Only ever non-null while the devtools are installed. Wrapping the source (rather than reading
|
|
458
|
+
// `state.args` from the outside) is what puts the tracking window around the *evaluation* of the
|
|
459
|
+
// args - a cached read of the computed runs nothing and would record no form at all.
|
|
460
|
+
const formLinks = context.state.subtle.devtoolsFormLinks;
|
|
461
|
+
context.state.subtle.setArgsSource(formLinks ? () => formLinks.track(resolve) : resolve);
|
|
336
462
|
// Trigger (auto-)execution whenever the resolved args change.
|
|
337
463
|
nestedEffect(() => {
|
|
338
464
|
const currArgsNow = context.state.args();
|
|
@@ -362,6 +488,10 @@ const withArgs = (args) => {
|
|
|
362
488
|
const withPolling = (options) => {
|
|
363
489
|
return createQueryFeature({
|
|
364
490
|
type: QueryFeatureType.WITH_POLLING,
|
|
491
|
+
devtools: () => [
|
|
492
|
+
{ label: 'interval', value: formatQueryDevtoolsDuration(options.interval) },
|
|
493
|
+
{ label: 'execute initially', value: options.executeInitially ? 'yes' : 'no' },
|
|
494
|
+
],
|
|
365
495
|
fn: (context) => {
|
|
366
496
|
if (!context.flags.shouldAutoExecuteMethod) {
|
|
367
497
|
throw withPollingUsedOnUnsupportedHttpMethod(context.flags.method);
|
|
@@ -428,6 +558,7 @@ const withPolling = (options) => {
|
|
|
428
558
|
const withLogging = (options) => {
|
|
429
559
|
return createQueryFeature({
|
|
430
560
|
type: QueryFeatureType.WITH_LOGGING,
|
|
561
|
+
devtools: () => queryDevtoolsFnDetail(options.logFn, 'logFn'),
|
|
431
562
|
fn: (context) => {
|
|
432
563
|
context.state.events$.pipe(takeUntilDestroyed(context.deps.destroyRef)).subscribe((event) => {
|
|
433
564
|
options.logFn(event);
|
|
@@ -439,6 +570,7 @@ const withLogging = (options) => {
|
|
|
439
570
|
const withErrorHandling = (options) => {
|
|
440
571
|
return createQueryFeature({
|
|
441
572
|
type: QueryFeatureType.WITH_ERROR_HANDLING,
|
|
573
|
+
devtools: () => queryDevtoolsFnDetail(options.handler, 'handler'),
|
|
442
574
|
fn: (context) => {
|
|
443
575
|
context.state.events$
|
|
444
576
|
.pipe(filter((event) => event.type === 'error'), takeUntilDestroyed(context.deps.destroyRef))
|
|
@@ -452,6 +584,7 @@ const withErrorHandling = (options) => {
|
|
|
452
584
|
const withSuccessHandling = (options) => {
|
|
453
585
|
return createQueryFeature({
|
|
454
586
|
type: QueryFeatureType.WITH_SUCCESS_HANDLING,
|
|
587
|
+
devtools: () => queryDevtoolsFnDetail(options.handler, 'handler'),
|
|
455
588
|
fn: (context) => {
|
|
456
589
|
context.state.events$
|
|
457
590
|
.pipe(filter((event) => event.type === HttpEventType.Response), takeUntilDestroyed(context.deps.destroyRef))
|
|
@@ -469,6 +602,10 @@ const withSuccessHandling = (options) => {
|
|
|
469
602
|
const withAutoRefresh = (options) => {
|
|
470
603
|
return createQueryFeature({
|
|
471
604
|
type: QueryFeatureType.WITH_AUTO_REFRESH,
|
|
605
|
+
devtools: () => [
|
|
606
|
+
{ label: 'signals', value: String(options.onSignalChanges.length) },
|
|
607
|
+
...(options.ignoreOnlyManualExecution ? [{ label: 'ignores manual only', value: 'yes' }] : []),
|
|
608
|
+
],
|
|
472
609
|
fn: (context) => {
|
|
473
610
|
if (!context.flags.shouldAutoExecuteMethod) {
|
|
474
611
|
throw withAutoRefreshUsedOnUnsupportedHttpMethod(context.flags.method);
|
|
@@ -495,6 +632,7 @@ const withAutoRefresh = (options) => {
|
|
|
495
632
|
const withResponseUpdate = (options) => {
|
|
496
633
|
return createQueryFeature({
|
|
497
634
|
type: QueryFeatureType.WITH_RESPONSE_UPDATE,
|
|
635
|
+
devtools: () => queryDevtoolsFnDetail(options.updater, 'updater'),
|
|
498
636
|
fn: (context) => {
|
|
499
637
|
nestedEffect(() => {
|
|
500
638
|
const currentResponse = untracked(() => context.state.response());
|
|
@@ -545,6 +683,11 @@ const withPageResetOnError = (options) => {
|
|
|
545
683
|
const matches = options.when ?? isPageOutOfRangeError;
|
|
546
684
|
return createQueryFeature({
|
|
547
685
|
type: QueryFeatureType.WITH_PAGE_RESET_ON_ERROR,
|
|
686
|
+
devtools: () => [
|
|
687
|
+
{ label: 'resets', value: options.reset ? 'custom callback' : 'page signal' },
|
|
688
|
+
...(options.reset ? [] : [{ label: 'reset to', value: String(options.resetTo ?? 1) }]),
|
|
689
|
+
{ label: 'when', value: options.when ? 'custom' : 'page out of range' },
|
|
690
|
+
],
|
|
548
691
|
fn: (context) => {
|
|
549
692
|
context.state.events$
|
|
550
693
|
.pipe(filter((event) => event.type === 'error'), takeUntilDestroyed(context.deps.destroyRef))
|
|
@@ -624,7 +767,10 @@ const setupQueryState = (options) => {
|
|
|
624
767
|
error: currentError,
|
|
625
768
|
};
|
|
626
769
|
}
|
|
627
|
-
else if (currentResponse) {
|
|
770
|
+
else if (currentResponse !== null || latestHttpEvent()?.type === HttpEventType.Response) {
|
|
771
|
+
// A response event having arrived is what makes the execution a success, not a truthy body: a 204 (or any
|
|
772
|
+
// other empty payload) sets `response` to `null`, which would otherwise be indistinguishable from a query
|
|
773
|
+
// that never ran - leaving `filterSuccess`, `onSuccess` and the success handlers waiting forever.
|
|
628
774
|
return {
|
|
629
775
|
type: 'success',
|
|
630
776
|
response: currentResponse,
|
|
@@ -651,6 +797,8 @@ const setupQueryState = (options) => {
|
|
|
651
797
|
rawResponse,
|
|
652
798
|
bindRequestEvents,
|
|
653
799
|
setArgsSource,
|
|
800
|
+
devtoolsStats: options.devtoolsStats ?? null,
|
|
801
|
+
devtoolsFormLinks: options.devtoolsFormLinks ?? null,
|
|
654
802
|
},
|
|
655
803
|
};
|
|
656
804
|
return state;
|
|
@@ -824,8 +972,12 @@ const createBaseQuery = (options) => {
|
|
|
824
972
|
queryConfig: options.queryConfig,
|
|
825
973
|
});
|
|
826
974
|
return runInInjectionContext(deps.injector, () => {
|
|
975
|
+
const devtoolsStats = createQueryDevtoolsStatsRecorder();
|
|
976
|
+
const devtoolsFormLinks = createQueryDevtoolsFormLinksRecorder();
|
|
827
977
|
const state = setupQueryState({
|
|
828
978
|
transformResponse: options.creator?.transformResponse,
|
|
979
|
+
devtoolsStats,
|
|
980
|
+
devtoolsFormLinks,
|
|
829
981
|
});
|
|
830
982
|
const flags = getQueryFeatureUsage(options);
|
|
831
983
|
const execute = options.executeFactory({
|
|
@@ -844,18 +996,40 @@ const createBaseQuery = (options) => {
|
|
|
844
996
|
applyQueryFeatures(options.features, featureFnContext);
|
|
845
997
|
maybeExecute({ execute, flags });
|
|
846
998
|
const query = createQueryObject({ state, execute, deps });
|
|
847
|
-
|
|
999
|
+
// Only ever non-null while the devtools are installed, so this whole block - the entry and the
|
|
1000
|
+
// per-response payload measuring behind it - is dead code in an app without them.
|
|
1001
|
+
if (devtoolsStats) {
|
|
1002
|
+
const statsSubscription = state.events$.subscribe((event) => {
|
|
1003
|
+
if (event.type === 'error') {
|
|
1004
|
+
devtoolsStats.recordError();
|
|
1005
|
+
}
|
|
1006
|
+
else if (event.type === HttpEventType.Response) {
|
|
1007
|
+
devtoolsStats.recordResponse({ headers: event.headers, body: event.body });
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
deps.destroyRef.onDestroy(() => statsSubscription.unsubscribe());
|
|
1011
|
+
// A retry happens inside the request, which is shared by every query hitting the same cache key -
|
|
1012
|
+
// so the recorder reads it off the request instead of being handed down into one. `recordRetry` is
|
|
1013
|
+
// idempotent per attempt, which is what makes reading a signal a safe way to count them.
|
|
1014
|
+
effect(() => {
|
|
1015
|
+
const retry = state.subtle.request()?.subtle.retryState();
|
|
1016
|
+
if (retry)
|
|
1017
|
+
devtoolsStats.recordRetry({ attempt: retry.attempt });
|
|
1018
|
+
});
|
|
848
1019
|
const unregister = registerQueryDevtoolsEntry({
|
|
849
1020
|
kind: 'query',
|
|
850
1021
|
handle: query,
|
|
851
1022
|
clientRef: client,
|
|
1023
|
+
stats: devtoolsStats,
|
|
1024
|
+
formLinks: devtoolsFormLinks ?? undefined,
|
|
852
1025
|
route: options.creatorInternals.route ??
|
|
853
1026
|
options.creator?.route ??
|
|
854
1027
|
null,
|
|
1028
|
+
authProviderRef: options.creatorInternals.authProvider,
|
|
855
1029
|
meta: {
|
|
856
1030
|
clientBaseUrl: deps.client.baseUrl,
|
|
857
1031
|
method: flags.method,
|
|
858
|
-
|
|
1032
|
+
features: describeQueryDevtoolsFeatures(options.features),
|
|
859
1033
|
queryConfig: options.queryConfig,
|
|
860
1034
|
creator: options.creator,
|
|
861
1035
|
repository: deps.client.repository,
|
|
@@ -890,7 +1064,7 @@ const setupQueryExecuteState = () => {
|
|
|
890
1064
|
const queryExecute = (options) => {
|
|
891
1065
|
const { executeOptions, args, executeState, options: runQueryOptions, isSecure } = options;
|
|
892
1066
|
const { deps, state, creator, creatorInternals, queryConfig } = executeOptions;
|
|
893
|
-
const { key, request } = deps.client.repository.request({
|
|
1067
|
+
const { key, request, executed } = deps.client.repository.request({
|
|
894
1068
|
route: creatorInternals.route,
|
|
895
1069
|
method: creatorInternals.method,
|
|
896
1070
|
args,
|
|
@@ -908,6 +1082,7 @@ const queryExecute = (options) => {
|
|
|
908
1082
|
state.lastTriggeredBy.set(runQueryOptions?.triggeredBy ?? null);
|
|
909
1083
|
state.subtle.request.set(request);
|
|
910
1084
|
state.subtle.bindRequestEvents(request);
|
|
1085
|
+
state.subtle.devtoolsStats?.recordExecution({ didRequest: executed, body: args?.body, url: request.url });
|
|
911
1086
|
};
|
|
912
1087
|
const circularQueryDependencyChecker = () => {
|
|
913
1088
|
let lastTriggerTs = 0;
|
|
@@ -1009,6 +1184,10 @@ const createBaseQueryCreator = (config) => {
|
|
|
1009
1184
|
options: { ...config.options, ...additionalOptions },
|
|
1010
1185
|
});
|
|
1011
1186
|
};
|
|
1187
|
+
queryCreator.subtle = {
|
|
1188
|
+
creatorInternals: config.internals,
|
|
1189
|
+
creatorOptions: config.options,
|
|
1190
|
+
};
|
|
1012
1191
|
return queryCreator;
|
|
1013
1192
|
};
|
|
1014
1193
|
|
|
@@ -1341,6 +1520,10 @@ const createHttpRequest = (options) => {
|
|
|
1341
1520
|
...(ngDevMode ? [{ debugName: "lastExecuteTime" }] : /* istanbul ignore next */ []));
|
|
1342
1521
|
const expiresIn = signal(null, /* @ts-ignore */
|
|
1343
1522
|
...(ngDevMode ? [{ debugName: "expiresIn" }] : /* istanbul ignore next */ []));
|
|
1523
|
+
const attempts = signal(1, /* @ts-ignore */
|
|
1524
|
+
...(ngDevMode ? [{ debugName: "attempts" }] : /* istanbul ignore next */ []));
|
|
1525
|
+
const retryState = signal(null, /* @ts-ignore */
|
|
1526
|
+
...(ngDevMode ? [{ debugName: "retryState" }] : /* istanbul ignore next */ []));
|
|
1344
1527
|
// NOTE: This must be a plain function, not a `computed`. The freshness check compares against
|
|
1345
1528
|
// `Date.now()`, which is not reactive, so a memoized computed would only ever recompute when
|
|
1346
1529
|
// `expiresIn` changes - once it evaluated to `false` (fresh) it would stay `false` forever, even
|
|
@@ -1362,26 +1545,68 @@ const createHttpRequest = (options) => {
|
|
|
1362
1545
|
// client-level one instead of being appended next to it.
|
|
1363
1546
|
return argHeaders.keys().reduce((merged, key) => merged.set(key, argHeaders.getAll(key) ?? []), clientHeaders);
|
|
1364
1547
|
};
|
|
1548
|
+
const send = (headers) => dependencies.httpClient.request(options.method, options.fullPath, {
|
|
1549
|
+
observe: 'events',
|
|
1550
|
+
body: args?.body,
|
|
1551
|
+
reportProgress: clientOptions?.reportProgress,
|
|
1552
|
+
withCredentials: clientOptions?.withCredentials,
|
|
1553
|
+
transferCache: clientOptions?.transferCache,
|
|
1554
|
+
responseType: clientOptions?.responseType || 'json',
|
|
1555
|
+
headers,
|
|
1556
|
+
});
|
|
1557
|
+
/**
|
|
1558
|
+
* Wraps the request in the fault the devtools have armed for this client, if any.
|
|
1559
|
+
*
|
|
1560
|
+
* Inside a `defer` so a fault is resolved per subscription rather than per execution - which is what
|
|
1561
|
+
* makes `retry` re-roll it, so an armed "fail the next two attempts" lets the third one through like a
|
|
1562
|
+
* flaky server would. Injected latency delays the attempt rather than its response, so the loading
|
|
1563
|
+
* state genuinely lasts that long and a retry waits it out too.
|
|
1564
|
+
*
|
|
1565
|
+
* A faulted attempt always goes through `timer`, even at zero latency: no real request can fail in the
|
|
1566
|
+
* same tick it was started in, and settling synchronously inside `execute()` would let an injected
|
|
1567
|
+
* failure land before the caller has finished wiring the request up.
|
|
1568
|
+
*/
|
|
1569
|
+
const sendWithFaults = (headers) => defer(() => {
|
|
1570
|
+
const fault = resolveQueryDevtoolsFault({
|
|
1571
|
+
clientName: options.clientName ?? '',
|
|
1572
|
+
method: options.method,
|
|
1573
|
+
url: options.fullPath,
|
|
1574
|
+
});
|
|
1575
|
+
if (!fault)
|
|
1576
|
+
return send(headers);
|
|
1577
|
+
const { status } = fault;
|
|
1578
|
+
const attempt$ = status === null
|
|
1579
|
+
? send(headers)
|
|
1580
|
+
: throwError(() => new HttpErrorResponse({
|
|
1581
|
+
status,
|
|
1582
|
+
statusText: 'Injected by the query devtools',
|
|
1583
|
+
url: options.fullPath,
|
|
1584
|
+
error: { message: `Injected ${status} (query devtools)` },
|
|
1585
|
+
}));
|
|
1586
|
+
return timer(fault.latencyMs).pipe(switchMap(() => attempt$));
|
|
1587
|
+
});
|
|
1365
1588
|
const createStream = () => {
|
|
1366
1589
|
const headers = resolveHeaders();
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
observe: 'events',
|
|
1370
|
-
body: args?.body,
|
|
1371
|
-
reportProgress: clientOptions?.reportProgress,
|
|
1372
|
-
withCredentials: clientOptions?.withCredentials,
|
|
1373
|
-
transferCache: clientOptions?.transferCache,
|
|
1374
|
-
responseType: clientOptions?.responseType || 'json',
|
|
1375
|
-
headers,
|
|
1376
|
-
})
|
|
1377
|
-
.pipe(tap((event) => updateState(event)), retry({
|
|
1590
|
+
const source$ = isQueryDevtoolsFaultInjectionEnabled() ? sendWithFaults(headers) : send(headers);
|
|
1591
|
+
return source$.pipe(tap((event) => updateState(event)), retry({
|
|
1378
1592
|
delay: (error, retryCount) => {
|
|
1379
1593
|
const retryOptions = { error, retryCount };
|
|
1380
1594
|
const retryResult = options.retryFn?.(retryOptions) || runDefaultQueryRetry(retryOptions);
|
|
1381
1595
|
if (!retryResult.retry) {
|
|
1382
1596
|
return throwError(() => error);
|
|
1383
1597
|
}
|
|
1384
|
-
|
|
1598
|
+
// rxjs counts retries from 1, so the attempt this delay leads up to is one further along.
|
|
1599
|
+
const attempt = retryCount + 1;
|
|
1600
|
+
retryState.set({
|
|
1601
|
+
attempt,
|
|
1602
|
+
delayMs: retryResult.delay,
|
|
1603
|
+
startsAt: Date.now() + retryResult.delay,
|
|
1604
|
+
status: error instanceof HttpErrorResponse ? error.status : 0,
|
|
1605
|
+
});
|
|
1606
|
+
return timer(retryResult.delay).pipe(tap(() => {
|
|
1607
|
+
retryState.set(null);
|
|
1608
|
+
attempts.set(attempt);
|
|
1609
|
+
}));
|
|
1385
1610
|
},
|
|
1386
1611
|
}), catchError((e) => {
|
|
1387
1612
|
updateErrorState(e);
|
|
@@ -1402,6 +1627,8 @@ const createHttpRequest = (options) => {
|
|
|
1402
1627
|
});
|
|
1403
1628
|
error.set(null);
|
|
1404
1629
|
expiresIn.set(null);
|
|
1630
|
+
attempts.set(1);
|
|
1631
|
+
retryState.set(null);
|
|
1405
1632
|
const stream = createStream();
|
|
1406
1633
|
currentStreamSubscription = stream.subscribe({
|
|
1407
1634
|
error: () => {
|
|
@@ -1414,6 +1641,9 @@ const createHttpRequest = (options) => {
|
|
|
1414
1641
|
const destroy = () => {
|
|
1415
1642
|
const wasActive = !currentStreamSubscription.closed;
|
|
1416
1643
|
currentStreamSubscription.unsubscribe();
|
|
1644
|
+
// A request destroyed mid-backoff never runs the attempt it was waiting for, so leaving the state
|
|
1645
|
+
// set would keep a "retrying in …" readout counting down for a request that is gone.
|
|
1646
|
+
retryState.set(null);
|
|
1417
1647
|
event$.complete();
|
|
1418
1648
|
return wasActive;
|
|
1419
1649
|
};
|
|
@@ -1504,6 +1734,7 @@ const createHttpRequest = (options) => {
|
|
|
1504
1734
|
const httpRequest = {
|
|
1505
1735
|
method: options.method,
|
|
1506
1736
|
url: options.fullPath,
|
|
1737
|
+
args: args ?? null,
|
|
1507
1738
|
execute,
|
|
1508
1739
|
destroy,
|
|
1509
1740
|
loading: loading.asReadonly(),
|
|
@@ -1518,6 +1749,9 @@ const createHttpRequest = (options) => {
|
|
|
1518
1749
|
lastExternalResponseAt: lastExternalResponseAt.asReadonly(),
|
|
1519
1750
|
applyPersistedResponse,
|
|
1520
1751
|
lastPersistedResponseAt: lastPersistedResponseAt.asReadonly(),
|
|
1752
|
+
resolveHeaders,
|
|
1753
|
+
attempts: attempts.asReadonly(),
|
|
1754
|
+
retryState: retryState.asReadonly(),
|
|
1521
1755
|
},
|
|
1522
1756
|
};
|
|
1523
1757
|
return httpRequest;
|
|
@@ -1685,7 +1919,7 @@ const createQueryStack = (options) => {
|
|
|
1685
1919
|
kind: 'query-stack',
|
|
1686
1920
|
handle: stack,
|
|
1687
1921
|
meta: {
|
|
1688
|
-
|
|
1922
|
+
features: describeQueryDevtoolsFeatures(features),
|
|
1689
1923
|
},
|
|
1690
1924
|
});
|
|
1691
1925
|
inject(DestroyRef).onDestroy(unregister);
|
|
@@ -1974,7 +2208,7 @@ const createPagedQueryStack = (options) => {
|
|
|
1974
2208
|
kind: 'paged-query-stack',
|
|
1975
2209
|
handle: pagedQuery,
|
|
1976
2210
|
meta: {
|
|
1977
|
-
|
|
2211
|
+
features: describeQueryDevtoolsFeatures(features),
|
|
1978
2212
|
},
|
|
1979
2213
|
});
|
|
1980
2214
|
inject(DestroyRef).onDestroy(unregister);
|
|
@@ -2732,13 +2966,21 @@ const createQuerySyncEngine = (options) => {
|
|
|
2732
2966
|
// An invalidation is something the other tab's app code asked for explicitly, so unlike the
|
|
2733
2967
|
// mutation heuristic it is not something `refreshOnMutation: false` opts out of.
|
|
2734
2968
|
if (message.type === 'invalidate') {
|
|
2735
|
-
repository.refreshInUse(createQueryInvalidationFilter({ url: message.url })
|
|
2969
|
+
repository.refreshInUse(createQueryInvalidationFilter({ url: message.url }), {
|
|
2970
|
+
type: 'invalidation',
|
|
2971
|
+
url: message.url,
|
|
2972
|
+
otherTab: true,
|
|
2973
|
+
});
|
|
2736
2974
|
return;
|
|
2737
2975
|
}
|
|
2738
2976
|
if (!refreshOnMutation)
|
|
2739
2977
|
return;
|
|
2740
2978
|
const mutation = { method: message.method, url: message.url };
|
|
2741
|
-
repository.refreshInUse(mutationFilter ? (request) => mutationFilter(mutation, request) : undefined
|
|
2979
|
+
repository.refreshInUse(mutationFilter ? (request) => mutationFilter(mutation, request) : undefined, {
|
|
2980
|
+
type: 'mutation',
|
|
2981
|
+
url: message.url,
|
|
2982
|
+
otherTab: true,
|
|
2983
|
+
});
|
|
2742
2984
|
};
|
|
2743
2985
|
const postInvalidation = (url) => transport.post({ type: 'invalidate', url });
|
|
2744
2986
|
const eventSubscription = repository.events$.subscribe(broadcast);
|
|
@@ -2853,6 +3095,13 @@ const QueryClientFeatureType = {
|
|
|
2853
3095
|
DEFAULT_RETRY: 'DEFAULT_RETRY',
|
|
2854
3096
|
ETHLETE_API_ERRORS: 'ETHLETE_API_ERRORS',
|
|
2855
3097
|
};
|
|
3098
|
+
const describeRefreshOnMutation = (config) => {
|
|
3099
|
+
if (config === false)
|
|
3100
|
+
return 'no';
|
|
3101
|
+
if (config === true || config === undefined)
|
|
3102
|
+
return 'every query in use';
|
|
3103
|
+
return 'filtered';
|
|
3104
|
+
};
|
|
2856
3105
|
/**
|
|
2857
3106
|
* Coordinates a query client with its own instances in the user's **other tabs**, over a
|
|
2858
3107
|
* `BroadcastChannel` and the Web Locks API. Three things happen once it is on:
|
|
@@ -2893,7 +3142,16 @@ const withMultiTabSync = (config) => ({ clientName, repository, destroyRef, isBr
|
|
|
2893
3142
|
lockManager: createQueryKeyLockManager(`et-query-poll:${channelName}`),
|
|
2894
3143
|
});
|
|
2895
3144
|
destroyRef.onDestroy(sync.destroy);
|
|
2896
|
-
return {
|
|
3145
|
+
return {
|
|
3146
|
+
type: QueryClientFeatureType.MULTI_TAB_SYNC,
|
|
3147
|
+
instance: sync,
|
|
3148
|
+
devtools: () => [
|
|
3149
|
+
{ label: 'channel', value: channelName },
|
|
3150
|
+
{ label: 'share responses', value: syncConfig.syncResponses === false ? 'no' : 'yes' },
|
|
3151
|
+
{ label: 'dedupe polling', value: syncConfig.dedupePolling === false ? 'no' : 'yes' },
|
|
3152
|
+
{ label: 'refresh on mutation', value: describeRefreshOnMutation(syncConfig.refreshOnMutation) },
|
|
3153
|
+
],
|
|
3154
|
+
};
|
|
2897
3155
|
};
|
|
2898
3156
|
/**
|
|
2899
3157
|
* Keeps a query client's successful reads on disk (IndexedDB), so a reload renders the last known
|
|
@@ -2947,7 +3205,19 @@ const withQueryPersistence = (config) => ({ clientName, repository, destroyRef,
|
|
|
2947
3205
|
window.removeEventListener('pagehide', flush);
|
|
2948
3206
|
engine.destroy();
|
|
2949
3207
|
});
|
|
2950
|
-
return {
|
|
3208
|
+
return {
|
|
3209
|
+
type: QueryClientFeatureType.PERSISTENCE,
|
|
3210
|
+
instance: engine,
|
|
3211
|
+
devtools: () => [
|
|
3212
|
+
{ label: 'store', value: persistenceConfig.storageName ?? `et-query-persistence-${clientName}` },
|
|
3213
|
+
{ label: 'version', value: String(persistenceConfig.version ?? 1) },
|
|
3214
|
+
{ label: 'max age', value: formatQueryDevtoolsDuration(persistenceConfig.maxAge ?? 86_400_000) },
|
|
3215
|
+
{ label: 'max entries', value: String(persistenceConfig.maxEntries ?? 50) },
|
|
3216
|
+
{ label: 'write delay', value: formatQueryDevtoolsDuration(persistenceConfig.writeDelay ?? 1000) },
|
|
3217
|
+
...(persistenceConfig.adapter ? [{ label: 'adapter', value: 'custom' }] : []),
|
|
3218
|
+
...queryDevtoolsFnDetail(persistenceConfig.filter, 'filter'),
|
|
3219
|
+
],
|
|
3220
|
+
};
|
|
2951
3221
|
};
|
|
2952
3222
|
/**
|
|
2953
3223
|
* Teaches the error pipeline to read an **HTML error page** - the 502 a proxy answers with, a
|
|
@@ -3104,10 +3374,10 @@ const createQueryRepository = (config) => {
|
|
|
3104
3374
|
isPersistEnabled,
|
|
3105
3375
|
keepUnusedFor,
|
|
3106
3376
|
});
|
|
3107
|
-
|
|
3108
|
-
cacheEntry.request.execute({ allowCache: runQueryOptions?.allowCache })
|
|
3109
|
-
|
|
3110
|
-
return { key: cacheKey, request: cacheEntry.request };
|
|
3377
|
+
const executed = !runQueryOptions?.allowCache || cacheEntry.request.isStale()
|
|
3378
|
+
? cacheEntry.request.execute({ allowCache: runQueryOptions?.allowCache })
|
|
3379
|
+
: false;
|
|
3380
|
+
return { key: cacheKey, request: cacheEntry.request, executed };
|
|
3111
3381
|
}
|
|
3112
3382
|
}
|
|
3113
3383
|
const request = createHttpRequest({
|
|
@@ -3116,11 +3386,12 @@ const createQueryRepository = (config) => {
|
|
|
3116
3386
|
method: options.method,
|
|
3117
3387
|
dependencies: config.dependencies,
|
|
3118
3388
|
clientOptions: creatorOptions,
|
|
3389
|
+
clientName: config.name,
|
|
3119
3390
|
clientHeaders: config.headers,
|
|
3120
3391
|
cacheAdapter: config.cacheAdapter,
|
|
3121
3392
|
retryFn: options.retryFn ?? config.retryFn,
|
|
3122
3393
|
});
|
|
3123
|
-
request.execute();
|
|
3394
|
+
const executed = request.execute();
|
|
3124
3395
|
bind({
|
|
3125
3396
|
key: trackingKey,
|
|
3126
3397
|
consumerDestroyRef: options.consumerDestroyRef,
|
|
@@ -3135,7 +3406,7 @@ const createQueryRepository = (config) => {
|
|
|
3135
3406
|
// on it - hydration from the persisted store - is only ever allowed to fill a gap this request has
|
|
3136
3407
|
// not filled itself.
|
|
3137
3408
|
eventsSubject.next({ type: 'entry-created', key: trackingKey, isCached: shouldCache, isPersistEnabled });
|
|
3138
|
-
return { key: trackingKey, request };
|
|
3409
|
+
return { key: trackingKey, request, executed };
|
|
3139
3410
|
};
|
|
3140
3411
|
/** Tears an entry down for good, whether it currently has consumers or not. */
|
|
3141
3412
|
const destroyEntry = (key, cacheEntry) => {
|
|
@@ -3198,7 +3469,8 @@ const createQueryRepository = (config) => {
|
|
|
3198
3469
|
bumpCacheVersion();
|
|
3199
3470
|
eventsSubject.next({ type: 'unbind-all-secure' });
|
|
3200
3471
|
};
|
|
3201
|
-
const refreshInUse = (filter) => {
|
|
3472
|
+
const refreshInUse = (filter, cause) => {
|
|
3473
|
+
const refreshed = [];
|
|
3202
3474
|
for (const cacheEntry of cache.values()) {
|
|
3203
3475
|
if (cacheEntry.consumers.size === 0)
|
|
3204
3476
|
continue;
|
|
@@ -3209,8 +3481,14 @@ const createQueryRepository = (config) => {
|
|
|
3209
3481
|
continue;
|
|
3210
3482
|
if (filter && !filter(cacheEntry.request))
|
|
3211
3483
|
continue;
|
|
3484
|
+
refreshed.push(cacheEntry.request);
|
|
3212
3485
|
cacheEntry.request.execute({ force: true });
|
|
3213
3486
|
}
|
|
3487
|
+
eventsSubject.next({
|
|
3488
|
+
type: 'queries-refreshed',
|
|
3489
|
+
cause: cause ?? { type: 'refresh', url: null, otherTab: false },
|
|
3490
|
+
requests: refreshed,
|
|
3491
|
+
});
|
|
3214
3492
|
};
|
|
3215
3493
|
const applyExternalResponse = (options) => {
|
|
3216
3494
|
const cacheEntry = cache.get(options.key);
|
|
@@ -3322,6 +3600,7 @@ const createQueryClient = (options) => defineRootProvider(() => {
|
|
|
3322
3600
|
const destroyRef = inject(DestroyRef);
|
|
3323
3601
|
let sync = null;
|
|
3324
3602
|
let persistenceEngine = null;
|
|
3603
|
+
const appliedFeatures = [];
|
|
3325
3604
|
if (options.features?.length) {
|
|
3326
3605
|
const seen = new Set();
|
|
3327
3606
|
for (const featureFn of options.features) {
|
|
@@ -3329,6 +3608,7 @@ const createQueryClient = (options) => defineRootProvider(() => {
|
|
|
3329
3608
|
if (seen.has(feature.type))
|
|
3330
3609
|
throw queryClientFeatureUsedMultipleTimes(feature.type);
|
|
3331
3610
|
seen.add(feature.type);
|
|
3611
|
+
appliedFeatures.push(feature);
|
|
3332
3612
|
if (feature.type === QueryClientFeatureType.MULTI_TAB_SYNC) {
|
|
3333
3613
|
sync = feature.instance;
|
|
3334
3614
|
}
|
|
@@ -3343,7 +3623,11 @@ const createQueryClient = (options) => defineRootProvider(() => {
|
|
|
3343
3623
|
refreshQueriesInUse: () => repository.refreshInUse(),
|
|
3344
3624
|
invalidateQueries: (invalidation) => {
|
|
3345
3625
|
const url = invalidation?.url ? resolveInvalidationUrl(options.baseUrl, invalidation.url) : null;
|
|
3346
|
-
repository.refreshInUse(createQueryInvalidationFilter({ url, filter: invalidation?.filter })
|
|
3626
|
+
repository.refreshInUse(createQueryInvalidationFilter({ url, filter: invalidation?.filter }), {
|
|
3627
|
+
type: 'invalidation',
|
|
3628
|
+
url,
|
|
3629
|
+
otherTab: false,
|
|
3630
|
+
});
|
|
3347
3631
|
// The resolved URL is what travels: the other tabs are the same client, so they would
|
|
3348
3632
|
// resolve it identically, and a message that needs no interpretation cannot drift.
|
|
3349
3633
|
if (invalidation?.otherTabs ?? true)
|
|
@@ -3351,7 +3635,11 @@ const createQueryClient = (options) => defineRootProvider(() => {
|
|
|
3351
3635
|
},
|
|
3352
3636
|
clearPersistedQueries: () => persistenceEngine?.clear() ?? Promise.resolve(),
|
|
3353
3637
|
whenPersistenceReady: persistenceEngine?.whenReady ?? Promise.resolve(),
|
|
3354
|
-
subtle: {
|
|
3638
|
+
subtle: {
|
|
3639
|
+
sync,
|
|
3640
|
+
persistence: persistenceEngine,
|
|
3641
|
+
devtoolsFeatures: isQueryDevtoolsEnabled() ? describeQueryDevtoolsFeatures(appliedFeatures) : [],
|
|
3642
|
+
},
|
|
3355
3643
|
};
|
|
3356
3644
|
return client;
|
|
3357
3645
|
}, {
|
|
@@ -3998,8 +4286,10 @@ const setupBearerQueryRegistry = (builders, injector, latestExecutedQuery, lates
|
|
|
3998
4286
|
};
|
|
3999
4287
|
const setupFeatures = (featureBuilders, context) => {
|
|
4000
4288
|
const features = {};
|
|
4289
|
+
/** The features as applied, in order - what the devtools describe. */
|
|
4290
|
+
const applied = [];
|
|
4001
4291
|
if (!featureBuilders?.length) {
|
|
4002
|
-
return features;
|
|
4292
|
+
return { features, applied };
|
|
4003
4293
|
}
|
|
4004
4294
|
const featureTypes = new Set();
|
|
4005
4295
|
for (const featureSetup of featureBuilders) {
|
|
@@ -4014,9 +4304,25 @@ const setupFeatures = (featureBuilders, context) => {
|
|
|
4014
4304
|
.map((word, index) => (index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1)))
|
|
4015
4305
|
.join('');
|
|
4016
4306
|
features[featureName] = feature.instance;
|
|
4307
|
+
applied.push({ type: feature.type, devtools: feature.devtools });
|
|
4017
4308
|
}
|
|
4018
|
-
return features;
|
|
4309
|
+
return { features, applied };
|
|
4019
4310
|
};
|
|
4311
|
+
/**
|
|
4312
|
+
* Describes the provider's queries for the devtools from what their creators were built with, so a
|
|
4313
|
+
* refresh request can be exported before the refresh query has ever run.
|
|
4314
|
+
*/
|
|
4315
|
+
const describeAuthQueries = (builders) => builders.map((builder) => {
|
|
4316
|
+
const internals = builder.config.queryCreator.subtle.creatorInternals;
|
|
4317
|
+
const isRefresh = builder._type === 'tokenRefreshQuery';
|
|
4318
|
+
return {
|
|
4319
|
+
key: builder.key,
|
|
4320
|
+
kind: (isRefresh ? 'token-refresh' : 'auth'),
|
|
4321
|
+
method: internals.method ?? 'POST',
|
|
4322
|
+
route: internals.route,
|
|
4323
|
+
buildArgs: isRefresh ? builder.buildArgs : undefined,
|
|
4324
|
+
};
|
|
4325
|
+
});
|
|
4020
4326
|
const alwaysLeader = () => true;
|
|
4021
4327
|
const runEarlyFeatureSetup = (featureBuilders, context) => {
|
|
4022
4328
|
let isLeaderFn = alwaysLeader;
|
|
@@ -4110,7 +4416,7 @@ const createBearerAuthProviderImpl = (config) => {
|
|
|
4110
4416
|
queries: queries,
|
|
4111
4417
|
executionState,
|
|
4112
4418
|
};
|
|
4113
|
-
const features = setupFeatures(config.features, featureSetupContext);
|
|
4419
|
+
const { features, applied: appliedFeatures } = setupFeatures(config.features, featureSetupContext);
|
|
4114
4420
|
const provider = {
|
|
4115
4421
|
queries,
|
|
4116
4422
|
features: features,
|
|
@@ -4130,11 +4436,13 @@ const createBearerAuthProviderImpl = (config) => {
|
|
|
4130
4436
|
kind: 'auth-provider',
|
|
4131
4437
|
handle: provider,
|
|
4132
4438
|
clientRef: config.queryClientRef,
|
|
4439
|
+
authQueries: describeAuthQueries(config.queries),
|
|
4133
4440
|
meta: {
|
|
4134
4441
|
name: config.name,
|
|
4135
4442
|
clientBaseUrl: queryClient.baseUrl,
|
|
4136
4443
|
repository: queryClient.repository,
|
|
4137
4444
|
client: queryClient,
|
|
4445
|
+
features: describeQueryDevtoolsFeatures(appliedFeatures),
|
|
4138
4446
|
},
|
|
4139
4447
|
});
|
|
4140
4448
|
destroyRef.onDestroy(unregister);
|
|
@@ -4177,6 +4485,7 @@ const withRefreshQuery = (key, config) => {
|
|
|
4177
4485
|
const delay = Math.min(exponentialDelay, maxRetryDelayMs);
|
|
4178
4486
|
return { retry: true, delay };
|
|
4179
4487
|
};
|
|
4488
|
+
const buildArgs = (refreshToken) => ({ body: { token: refreshToken } });
|
|
4180
4489
|
const setup = (context) => {
|
|
4181
4490
|
const expiresInPropertyName = config.expiresInPropertyName ?? 'exp';
|
|
4182
4491
|
const minRefreshInterval = config.minRefreshInterval ?? 30000; // 30 seconds default
|
|
@@ -4192,9 +4501,8 @@ const withRefreshQuery = (key, config) => {
|
|
|
4192
4501
|
if (timeSinceLastRefresh < minRefreshInterval)
|
|
4193
4502
|
return;
|
|
4194
4503
|
lastRefreshTime = now;
|
|
4195
|
-
const refreshArgs = { body: { token: currentRefreshToken } };
|
|
4196
4504
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
4197
|
-
context.queries[key].execute(
|
|
4505
|
+
context.queries[key].execute(buildArgs(currentRefreshToken), {
|
|
4198
4506
|
triggeredBy: triggeredInternally ? 'token-refresh' : undefined,
|
|
4199
4507
|
});
|
|
4200
4508
|
};
|
|
@@ -4277,6 +4585,7 @@ const withRefreshQuery = (key, config) => {
|
|
|
4277
4585
|
queryCreator: config.queryCreator.clone({ retryFn: refreshRetryFn, subtle: { useQueryRepositoryCache: true } }),
|
|
4278
4586
|
},
|
|
4279
4587
|
setup,
|
|
4588
|
+
buildArgs,
|
|
4280
4589
|
};
|
|
4281
4590
|
};
|
|
4282
4591
|
|
|
@@ -4343,6 +4652,11 @@ const withInactivityLogout = (config = {}) => {
|
|
|
4343
4652
|
return {
|
|
4344
4653
|
type: BearerAuthFeatureType.INACTIVITY_LOGOUT,
|
|
4345
4654
|
instance,
|
|
4655
|
+
devtools: () => [
|
|
4656
|
+
{ label: 'timeout', value: formatQueryDevtoolsDuration(inactivityTimeout) },
|
|
4657
|
+
{ label: 'activity events', value: activityEvents.join(', ') },
|
|
4658
|
+
...(config.customActivityCheck ? [{ label: 'custom activity check', value: 'yes' }] : []),
|
|
4659
|
+
],
|
|
4346
4660
|
};
|
|
4347
4661
|
};
|
|
4348
4662
|
};
|
|
@@ -4619,6 +4933,15 @@ const withBearerAuthMultiTabSync = (config = {}) => {
|
|
|
4619
4933
|
const setup = (_context) => ({
|
|
4620
4934
|
type: BearerAuthFeatureType.MULTI_TAB_SYNC,
|
|
4621
4935
|
instance,
|
|
4936
|
+
devtools: () => [
|
|
4937
|
+
{ label: 'channel', value: config.channelName ?? 'ethlete-auth-sync' },
|
|
4938
|
+
{ label: 'tokens', value: config.syncTokens === false ? 'tab local' : 'synced' },
|
|
4939
|
+
{ label: 'logout', value: config.syncLogout === false ? 'tab local' : 'synced' },
|
|
4940
|
+
{
|
|
4941
|
+
label: 'leader election',
|
|
4942
|
+
value: config.leaderElection === false ? 'every tab refreshes' : 'one tab refreshes',
|
|
4943
|
+
},
|
|
4944
|
+
],
|
|
4622
4945
|
});
|
|
4623
4946
|
// The elected leader gates the auth queries' automatic refresh, which is wired up before the
|
|
4624
4947
|
// regular feature setup runs - so this half has to happen earlier than `setup`.
|
|
@@ -4639,6 +4962,16 @@ const withPersistentAuth = (config) => {
|
|
|
4639
4962
|
return {
|
|
4640
4963
|
type: BearerAuthFeatureType.PERSISTENT_AUTH,
|
|
4641
4964
|
instance,
|
|
4965
|
+
devtools: () => [
|
|
4966
|
+
{ label: 'cookie', value: config.cookie?.name ?? 'etAuth' },
|
|
4967
|
+
{ label: 'remembered for', value: `${config.cookie?.expiresInDays ?? 30}d` },
|
|
4968
|
+
{ label: 'same site', value: config.cookie?.sameSite ?? 'lax' },
|
|
4969
|
+
{ label: 'remember me default', value: config.defaultRememberMe ? 'yes' : 'no' },
|
|
4970
|
+
{ label: 'auto login', value: config.autoLogin.queryKey },
|
|
4971
|
+
...(config.autoLogin.excludeRoutes?.length
|
|
4972
|
+
? [{ label: 'excluded routes', value: config.autoLogin.excludeRoutes.join(', ') }]
|
|
4973
|
+
: []),
|
|
4974
|
+
],
|
|
4642
4975
|
};
|
|
4643
4976
|
};
|
|
4644
4977
|
};
|
|
@@ -4745,6 +5078,10 @@ const withTokenExpirationWarning = (config = {}) => {
|
|
|
4745
5078
|
return {
|
|
4746
5079
|
type: BearerAuthFeatureType.TOKEN_EXPIRATION_WARNING,
|
|
4747
5080
|
instance,
|
|
5081
|
+
devtools: () => [
|
|
5082
|
+
{ label: 'warn before', value: formatQueryDevtoolsDuration(warningThreshold) },
|
|
5083
|
+
{ label: 'check every', value: formatQueryDevtoolsDuration(checkInterval) },
|
|
5084
|
+
],
|
|
4748
5085
|
};
|
|
4749
5086
|
};
|
|
4750
5087
|
};
|
|
@@ -4799,6 +5136,10 @@ const withTokenRevocation = (config) => {
|
|
|
4799
5136
|
return {
|
|
4800
5137
|
type: BearerAuthFeatureType.TOKEN_REVOCATION,
|
|
4801
5138
|
instance,
|
|
5139
|
+
devtools: () => [
|
|
5140
|
+
{ label: 'query', value: config.queryKey },
|
|
5141
|
+
{ label: 'on logout', value: revokeOnLogout ? 'yes' : 'no' },
|
|
5142
|
+
],
|
|
4802
5143
|
};
|
|
4803
5144
|
};
|
|
4804
5145
|
};
|
|
@@ -4806,9 +5147,14 @@ const withTokenRevocation = (config) => {
|
|
|
4806
5147
|
const withTracking = (config) => {
|
|
4807
5148
|
return (context) => {
|
|
4808
5149
|
const instance = createTrackingFeature(context, config);
|
|
5150
|
+
const handledEvents = Object.keys(config?.on ?? {});
|
|
4809
5151
|
return {
|
|
4810
5152
|
type: BearerAuthFeatureType.TRACKING,
|
|
4811
5153
|
instance,
|
|
5154
|
+
devtools: () => [
|
|
5155
|
+
{ label: 'internal events', value: config?.trackInternalEvents === false ? 'ignored' : 'tracked' },
|
|
5156
|
+
...(handledEvents.length ? [{ label: 'handlers', value: handledEvents.join(', ') }] : []),
|
|
5157
|
+
],
|
|
4812
5158
|
};
|
|
4813
5159
|
};
|
|
4814
5160
|
};
|
|
@@ -4945,6 +5291,347 @@ const createTrackingFeature = (context, config) => {
|
|
|
4945
5291
|
};
|
|
4946
5292
|
};
|
|
4947
5293
|
|
|
5294
|
+
/** The neutral fault: nothing armed. */
|
|
5295
|
+
const EMPTY_QUERY_DEVTOOLS_FAULT = {
|
|
5296
|
+
latencyMs: 0,
|
|
5297
|
+
failNext: 0,
|
|
5298
|
+
failRate: 0,
|
|
5299
|
+
status: 503,
|
|
5300
|
+
};
|
|
5301
|
+
/**
|
|
5302
|
+
* Statuses worth injecting, and whether the SDK's default retry policy retries them. A picker built from
|
|
5303
|
+
* this cannot arm a 500 and then leave you wondering why no retry fired.
|
|
5304
|
+
* @see runDefaultQueryRetry
|
|
5305
|
+
*/
|
|
5306
|
+
const QUERY_DEVTOOLS_FAULT_STATUSES = [
|
|
5307
|
+
{ status: 0, label: 'Network error', retryable: true },
|
|
5308
|
+
{ status: 400, label: 'Bad Request', retryable: false },
|
|
5309
|
+
{ status: 401, label: 'Unauthorized', retryable: false },
|
|
5310
|
+
{ status: 403, label: 'Forbidden', retryable: false },
|
|
5311
|
+
{ status: 404, label: 'Not Found', retryable: false },
|
|
5312
|
+
{ status: 408, label: 'Request Timeout', retryable: true },
|
|
5313
|
+
{ status: 429, label: 'Too Many Requests', retryable: true },
|
|
5314
|
+
{ status: 500, label: 'Internal Server Error', retryable: false },
|
|
5315
|
+
{ status: 502, label: 'Bad Gateway', retryable: true },
|
|
5316
|
+
{ status: 503, label: 'Service Unavailable', retryable: true },
|
|
5317
|
+
];
|
|
5318
|
+
const faults = /* @__PURE__ */ signal({}, /* @ts-ignore */
|
|
5319
|
+
...(ngDevMode ? [{ debugName: "faults" }] : /* istanbul ignore next */ []));
|
|
5320
|
+
/**
|
|
5321
|
+
* The faults armed per client name, consumed by the `<et-query-devtools>` UI. Clients with nothing armed
|
|
5322
|
+
* are absent rather than present with an {@link EMPTY_QUERY_DEVTOOLS_FAULT}.
|
|
5323
|
+
*/
|
|
5324
|
+
const queryDevtoolsFaults = /* @__PURE__ */ faults.asReadonly();
|
|
5325
|
+
/** Whether anything at all is armed on a fault. */
|
|
5326
|
+
const isQueryDevtoolsFaultArmed = (fault) => fault.latencyMs > 0 || fault.failNext > 0 || fault.failRate > 0;
|
|
5327
|
+
/**
|
|
5328
|
+
* Arms (or updates) the fault of one client. Only the given fields change; the rest keep their current
|
|
5329
|
+
* value. A client whose resulting fault has nothing armed is dropped from {@link queryDevtoolsFaults}.
|
|
5330
|
+
*
|
|
5331
|
+
* Part of the devtools contract consumed by `<et-query-devtools>`; a fault is a debugging tool, not
|
|
5332
|
+
* something an application should arm on itself.
|
|
5333
|
+
*/
|
|
5334
|
+
const setQueryDevtoolsFault = (options) => {
|
|
5335
|
+
const { clientName, patch } = options;
|
|
5336
|
+
faults.update((current) => {
|
|
5337
|
+
const next = { ...(current[clientName] ?? EMPTY_QUERY_DEVTOOLS_FAULT), ...patch };
|
|
5338
|
+
if (!isQueryDevtoolsFaultArmed(next)) {
|
|
5339
|
+
const { [clientName]: _removed, ...rest } = current;
|
|
5340
|
+
return rest;
|
|
5341
|
+
}
|
|
5342
|
+
return { ...current, [clientName]: next };
|
|
5343
|
+
});
|
|
5344
|
+
};
|
|
5345
|
+
/**
|
|
5346
|
+
* Disarms every fault of one client, or of all clients when no name is given.
|
|
5347
|
+
*
|
|
5348
|
+
* @see setQueryDevtoolsFault
|
|
5349
|
+
*/
|
|
5350
|
+
const clearQueryDevtoolsFaults = (clientName) => faults.update((current) => {
|
|
5351
|
+
if (clientName === undefined)
|
|
5352
|
+
return {};
|
|
5353
|
+
const { [clientName]: _removed, ...rest } = current;
|
|
5354
|
+
return rest;
|
|
5355
|
+
});
|
|
5356
|
+
/**
|
|
5357
|
+
* Resolves what to do with one upcoming attempt, consuming a `failNext` budget in the process - so this
|
|
5358
|
+
* must be called once per attempt and never speculatively.
|
|
5359
|
+
*
|
|
5360
|
+
* Installed as the fault resolver by `provideQueryDevtools()`; nothing else may call it.
|
|
5361
|
+
* @internal
|
|
5362
|
+
*/
|
|
5363
|
+
const resolveQueryDevtoolsFaultForAttempt = (target) => {
|
|
5364
|
+
const fault = faults()[target.clientName];
|
|
5365
|
+
if (!fault || !isQueryDevtoolsFaultArmed(fault))
|
|
5366
|
+
return null;
|
|
5367
|
+
let shouldFail = false;
|
|
5368
|
+
if (fault.failNext > 0) {
|
|
5369
|
+
shouldFail = true;
|
|
5370
|
+
setQueryDevtoolsFault({ clientName: target.clientName, patch: { failNext: fault.failNext - 1 } });
|
|
5371
|
+
}
|
|
5372
|
+
else if (fault.failRate > 0) {
|
|
5373
|
+
shouldFail = Math.random() * 100 < fault.failRate;
|
|
5374
|
+
}
|
|
5375
|
+
return {
|
|
5376
|
+
latencyMs: fault.latencyMs,
|
|
5377
|
+
status: shouldFail ? fault.status : null,
|
|
5378
|
+
};
|
|
5379
|
+
};
|
|
5380
|
+
|
|
5381
|
+
/**
|
|
5382
|
+
* Records which query forms feed one query's args, so the devtools can answer "which query does this
|
|
5383
|
+
* form drive?" from the reads themselves rather than from a naming convention.
|
|
5384
|
+
* @internal
|
|
5385
|
+
*/
|
|
5386
|
+
const createQueryDevtoolsFormLinks = () => {
|
|
5387
|
+
const ids = signal([], /* @ts-ignore */
|
|
5388
|
+
...(ngDevMode ? [{ debugName: "ids" }] : /* istanbul ignore next */ []));
|
|
5389
|
+
return {
|
|
5390
|
+
ids: ids.asReadonly(),
|
|
5391
|
+
track: (build) => {
|
|
5392
|
+
const collected = collectQueryFormLinks(build);
|
|
5393
|
+
// Args are built inside a reactive computation, which may not write to a signal - `untracked`
|
|
5394
|
+
// lifts that restriction, and skipping the write unless something is new keeps the panel from
|
|
5395
|
+
// being notified on every evaluation.
|
|
5396
|
+
untracked(() => {
|
|
5397
|
+
const known = ids();
|
|
5398
|
+
const added = collected.ids.filter((id) => !known.includes(id));
|
|
5399
|
+
if (added.length)
|
|
5400
|
+
ids.set([...known, ...added]);
|
|
5401
|
+
});
|
|
5402
|
+
return collected.value;
|
|
5403
|
+
},
|
|
5404
|
+
};
|
|
5405
|
+
};
|
|
5406
|
+
|
|
5407
|
+
const EMPTY_STATS = {
|
|
5408
|
+
executions: 0,
|
|
5409
|
+
requests: 0,
|
|
5410
|
+
responses: 0,
|
|
5411
|
+
errors: 0,
|
|
5412
|
+
retries: 0,
|
|
5413
|
+
receivedBytes: 0,
|
|
5414
|
+
sentBytes: 0,
|
|
5415
|
+
hasEstimatedBytes: false,
|
|
5416
|
+
firstExecutedAt: null,
|
|
5417
|
+
lastResponseAt: null,
|
|
5418
|
+
lastDurationMs: null,
|
|
5419
|
+
totalDurationMs: 0,
|
|
5420
|
+
};
|
|
5421
|
+
const textEncoder = typeof TextEncoder === 'function' ? /* @__PURE__ */ new TextEncoder() : null;
|
|
5422
|
+
const byteLengthOf = (text) => textEncoder?.encode(text).length ?? text.length;
|
|
5423
|
+
const estimateBodySize = (body) => {
|
|
5424
|
+
if (body === null || body === undefined)
|
|
5425
|
+
return 0;
|
|
5426
|
+
if (typeof body === 'string')
|
|
5427
|
+
return byteLengthOf(body);
|
|
5428
|
+
if (typeof Blob !== 'undefined' && body instanceof Blob)
|
|
5429
|
+
return body.size;
|
|
5430
|
+
if (body instanceof ArrayBuffer)
|
|
5431
|
+
return body.byteLength;
|
|
5432
|
+
if (ArrayBuffer.isView(body))
|
|
5433
|
+
return body.byteLength;
|
|
5434
|
+
if (typeof FormData !== 'undefined' && body instanceof FormData)
|
|
5435
|
+
return 0;
|
|
5436
|
+
try {
|
|
5437
|
+
return byteLengthOf(JSON.stringify(body) ?? '');
|
|
5438
|
+
}
|
|
5439
|
+
catch {
|
|
5440
|
+
// A body holding a circular reference or a BigInt has no serialized size to report.
|
|
5441
|
+
return 0;
|
|
5442
|
+
}
|
|
5443
|
+
};
|
|
5444
|
+
/** How many runs a query keeps. Enough to see a stampede or a chain without growing unbounded. */
|
|
5445
|
+
const RUN_HISTORY = 25;
|
|
5446
|
+
/**
|
|
5447
|
+
* How many of those runs keep their response body. Bodies dominate what the buffer retains, and a diff
|
|
5448
|
+
* only ever looks a couple of runs back.
|
|
5449
|
+
*/
|
|
5450
|
+
const RESPONSE_HISTORY = 5;
|
|
5451
|
+
/** The index of the newest run still in flight, or -1 when none is. */
|
|
5452
|
+
const lastPendingRunIndex = (runs) => {
|
|
5453
|
+
for (let index = runs.length - 1; index >= 0; index--) {
|
|
5454
|
+
if (runs[index]?.status === 'pending')
|
|
5455
|
+
return index;
|
|
5456
|
+
}
|
|
5457
|
+
return -1;
|
|
5458
|
+
};
|
|
5459
|
+
/**
|
|
5460
|
+
* Drops the response body of every run past the newest {@link RESPONSE_HISTORY} that hold one. Mutates
|
|
5461
|
+
* the array it is given, which is always one this module just built.
|
|
5462
|
+
*/
|
|
5463
|
+
const trimRetainedResponses = (runs) => {
|
|
5464
|
+
let kept = 0;
|
|
5465
|
+
for (let index = runs.length - 1; index >= 0; index--) {
|
|
5466
|
+
const run = runs[index];
|
|
5467
|
+
if (!run?.hasResponse)
|
|
5468
|
+
continue;
|
|
5469
|
+
if (++kept > RESPONSE_HISTORY)
|
|
5470
|
+
runs[index] = { ...run, response: null, hasResponse: false };
|
|
5471
|
+
}
|
|
5472
|
+
return runs;
|
|
5473
|
+
};
|
|
5474
|
+
/**
|
|
5475
|
+
* The size of a payload in bytes, and whether that size is the one that went over the wire. Only a
|
|
5476
|
+
* `content-length` header is exact; everything else is measured by serializing the body.
|
|
5477
|
+
* @internal
|
|
5478
|
+
*/
|
|
5479
|
+
const measureQueryDevtoolsPayload = (payload) => {
|
|
5480
|
+
const contentLength = Number(payload.headers?.get('content-length'));
|
|
5481
|
+
if (Number.isFinite(contentLength) && contentLength > 0)
|
|
5482
|
+
return { bytes: contentLength, isExact: true };
|
|
5483
|
+
return { bytes: estimateBodySize(payload.body), isExact: false };
|
|
5484
|
+
};
|
|
5485
|
+
/**
|
|
5486
|
+
* Accumulates what a query does, for the devtools panel. Only ever created while
|
|
5487
|
+
* {@link provideQueryDevtools} is installed - measuring a body's size serializes it, which no app
|
|
5488
|
+
* without devtools should pay for.
|
|
5489
|
+
* @internal
|
|
5490
|
+
*/
|
|
5491
|
+
const createQueryDevtoolsStats = () => {
|
|
5492
|
+
const stats = signal(EMPTY_STATS, /* @ts-ignore */
|
|
5493
|
+
...(ngDevMode ? [{ debugName: "stats" }] : /* istanbul ignore next */ []));
|
|
5494
|
+
const runs = signal([], /* @ts-ignore */
|
|
5495
|
+
...(ngDevMode ? [{ debugName: "runs" }] : /* istanbul ignore next */ []));
|
|
5496
|
+
// The baseline the next response's duration is measured against. Deliberately not part of the stats:
|
|
5497
|
+
// a duration is only ever reported once the response it belongs to has arrived.
|
|
5498
|
+
let lastExecutionAt = null;
|
|
5499
|
+
// The URL of the last execution, whether or not it requested - so a response that arrives without a run
|
|
5500
|
+
// of the query's own is still attributed to the URL the query was pointed at.
|
|
5501
|
+
let lastUrl = null;
|
|
5502
|
+
let runCounter = 0;
|
|
5503
|
+
const appendRun = (run, previous) => {
|
|
5504
|
+
const appended = { ...run, index: ++runCounter };
|
|
5505
|
+
runs.set(trimRetainedResponses([...previous, appended].slice(-RUN_HISTORY)));
|
|
5506
|
+
};
|
|
5507
|
+
/**
|
|
5508
|
+
* Ends the newest run still in flight. A response with no run of the query's own waiting for it came
|
|
5509
|
+
* from elsewhere (see {@link QueryDevtoolsRun.didRequest}), so it is recorded as an instant instead.
|
|
5510
|
+
*/
|
|
5511
|
+
const endRun = (end) => {
|
|
5512
|
+
const current = runs();
|
|
5513
|
+
const pending = lastPendingRunIndex(current);
|
|
5514
|
+
if (pending === -1) {
|
|
5515
|
+
appendRun({ ...end, startedAt: end.endedAt, didRequest: false, url: lastUrl, sentBytes: 0, attempts: 0 }, current);
|
|
5516
|
+
return;
|
|
5517
|
+
}
|
|
5518
|
+
runs.set(trimRetainedResponses(current.map((run, index) => (index === pending ? { ...run, ...end } : run))));
|
|
5519
|
+
};
|
|
5520
|
+
const recordExecution = ({ didRequest, body, url }) => {
|
|
5521
|
+
const now = Date.now();
|
|
5522
|
+
const sent = didRequest ? measureQueryDevtoolsPayload({ body }) : null;
|
|
5523
|
+
lastExecutionAt = now;
|
|
5524
|
+
lastUrl = url ?? null;
|
|
5525
|
+
stats.update((current) => ({
|
|
5526
|
+
...current,
|
|
5527
|
+
executions: current.executions + 1,
|
|
5528
|
+
requests: current.requests + (didRequest ? 1 : 0),
|
|
5529
|
+
sentBytes: current.sentBytes + (sent?.bytes ?? 0),
|
|
5530
|
+
hasEstimatedBytes: current.hasEstimatedBytes || (!!sent?.bytes && !sent.isExact),
|
|
5531
|
+
firstExecutedAt: current.firstExecutedAt ?? now,
|
|
5532
|
+
}));
|
|
5533
|
+
// An execution answered from the cache, or one that joined a request already in flight, starts no
|
|
5534
|
+
// run of its own - the response it eventually gets (if any) is what records one.
|
|
5535
|
+
if (!didRequest)
|
|
5536
|
+
return;
|
|
5537
|
+
appendRun({
|
|
5538
|
+
startedAt: now,
|
|
5539
|
+
endedAt: null,
|
|
5540
|
+
status: 'pending',
|
|
5541
|
+
didRequest: true,
|
|
5542
|
+
url: lastUrl,
|
|
5543
|
+
attempts: 1,
|
|
5544
|
+
sentBytes: sent?.bytes ?? 0,
|
|
5545
|
+
receivedBytes: 0,
|
|
5546
|
+
response: null,
|
|
5547
|
+
hasResponse: false,
|
|
5548
|
+
}, runs().map((run) => (run.status === 'pending' ? { ...run, status: 'aborted', endedAt: now } : run)));
|
|
5549
|
+
};
|
|
5550
|
+
const recordResponse = (payload) => {
|
|
5551
|
+
const now = Date.now();
|
|
5552
|
+
const received = measureQueryDevtoolsPayload(payload);
|
|
5553
|
+
const duration = lastExecutionAt === null ? null : now - lastExecutionAt;
|
|
5554
|
+
stats.update((current) => ({
|
|
5555
|
+
...current,
|
|
5556
|
+
responses: current.responses + 1,
|
|
5557
|
+
receivedBytes: current.receivedBytes + received.bytes,
|
|
5558
|
+
hasEstimatedBytes: current.hasEstimatedBytes || (received.bytes > 0 && !received.isExact),
|
|
5559
|
+
lastResponseAt: now,
|
|
5560
|
+
lastDurationMs: duration,
|
|
5561
|
+
totalDurationMs: current.totalDurationMs + (duration ?? 0),
|
|
5562
|
+
}));
|
|
5563
|
+
endRun({
|
|
5564
|
+
endedAt: now,
|
|
5565
|
+
status: 'success',
|
|
5566
|
+
receivedBytes: received.bytes,
|
|
5567
|
+
response: payload.body,
|
|
5568
|
+
hasResponse: true,
|
|
5569
|
+
});
|
|
5570
|
+
};
|
|
5571
|
+
const recordError = () => {
|
|
5572
|
+
stats.update((current) => ({ ...current, errors: current.errors + 1 }));
|
|
5573
|
+
endRun({ endedAt: Date.now(), status: 'error', receivedBytes: 0, response: null, hasResponse: false });
|
|
5574
|
+
};
|
|
5575
|
+
const recordRetry = ({ attempt }) => {
|
|
5576
|
+
const current = runs();
|
|
5577
|
+
const pending = lastPendingRunIndex(current);
|
|
5578
|
+
const run = current[pending];
|
|
5579
|
+
// Nothing in flight means the retry belongs to a request this query is not the one waiting on, and an
|
|
5580
|
+
// attempt already accounted for is the caller reporting the same one twice.
|
|
5581
|
+
if (!run || attempt <= run.attempts)
|
|
5582
|
+
return;
|
|
5583
|
+
stats.update((state) => ({ ...state, retries: state.retries + (attempt - run.attempts) }));
|
|
5584
|
+
runs.set(current.map((entry, index) => (index === pending ? { ...entry, attempts: attempt } : entry)));
|
|
5585
|
+
};
|
|
5586
|
+
const reset = () => {
|
|
5587
|
+
lastExecutionAt = null;
|
|
5588
|
+
lastUrl = null;
|
|
5589
|
+
runCounter = 0;
|
|
5590
|
+
stats.set(EMPTY_STATS);
|
|
5591
|
+
runs.set([]);
|
|
5592
|
+
};
|
|
5593
|
+
return {
|
|
5594
|
+
current: stats.asReadonly(),
|
|
5595
|
+
runs: runs.asReadonly(),
|
|
5596
|
+
reset,
|
|
5597
|
+
recordExecution,
|
|
5598
|
+
recordResponse,
|
|
5599
|
+
recordError,
|
|
5600
|
+
recordRetry,
|
|
5601
|
+
};
|
|
5602
|
+
};
|
|
5603
|
+
/**
|
|
5604
|
+
* Adds up the stats of several entries, for a stack's or a client's total. `lastDurationMs` is taken from
|
|
5605
|
+
* whichever entry responded most recently rather than summed.
|
|
5606
|
+
*/
|
|
5607
|
+
const sumQueryDevtoolsStats = (handles) => {
|
|
5608
|
+
const total = { ...EMPTY_STATS };
|
|
5609
|
+
for (const handle of handles) {
|
|
5610
|
+
if (!handle)
|
|
5611
|
+
continue;
|
|
5612
|
+
const stats = handle.current();
|
|
5613
|
+
total.executions += stats.executions;
|
|
5614
|
+
total.requests += stats.requests;
|
|
5615
|
+
total.responses += stats.responses;
|
|
5616
|
+
total.errors += stats.errors;
|
|
5617
|
+
total.retries += stats.retries;
|
|
5618
|
+
total.receivedBytes += stats.receivedBytes;
|
|
5619
|
+
total.sentBytes += stats.sentBytes;
|
|
5620
|
+
total.totalDurationMs += stats.totalDurationMs;
|
|
5621
|
+
total.hasEstimatedBytes = total.hasEstimatedBytes || stats.hasEstimatedBytes;
|
|
5622
|
+
if (stats.firstExecutedAt !== null &&
|
|
5623
|
+
(total.firstExecutedAt === null || stats.firstExecutedAt < total.firstExecutedAt)) {
|
|
5624
|
+
total.firstExecutedAt = stats.firstExecutedAt;
|
|
5625
|
+
}
|
|
5626
|
+
if (stats.lastResponseAt !== null &&
|
|
5627
|
+
(total.lastResponseAt === null || stats.lastResponseAt > total.lastResponseAt)) {
|
|
5628
|
+
total.lastResponseAt = stats.lastResponseAt;
|
|
5629
|
+
total.lastDurationMs = stats.lastDurationMs;
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
return total;
|
|
5633
|
+
};
|
|
5634
|
+
|
|
4948
5635
|
const entries = /* @__PURE__ */ signal([], /* @ts-ignore */
|
|
4949
5636
|
...(ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []));
|
|
4950
5637
|
/**
|
|
@@ -4963,26 +5650,49 @@ const descriptorOf = (entry) => {
|
|
|
4963
5650
|
return `auth-provider|${meta.name ?? ''}`;
|
|
4964
5651
|
if (kind === 'ws-client')
|
|
4965
5652
|
return `ws-client|${meta.name ?? ''}`;
|
|
5653
|
+
if (kind === 'query-form')
|
|
5654
|
+
return `query-form|${meta.name ?? ''}`;
|
|
4966
5655
|
return kind;
|
|
4967
5656
|
};
|
|
4968
5657
|
/**
|
|
4969
|
-
*
|
|
4970
|
-
*
|
|
5658
|
+
* Wraps a path param name where a route function interpolated it. A NUL cannot occur in a route, so
|
|
5659
|
+
* a marked-up route stays unambiguously parseable.
|
|
5660
|
+
*/
|
|
5661
|
+
const PARAM_MARKER = '\u0000';
|
|
5662
|
+
/**
|
|
5663
|
+
* Stands in for the path params object a route function expects, recording every param it reads as
|
|
5664
|
+
* `\0<name>\0` in the returned route. A route that transforms what it reads (`p.id.slice(2)`) still
|
|
5665
|
+
* yields a usable route - the recorded name is transformed along with it.
|
|
5666
|
+
*/
|
|
5667
|
+
const paramRecorder = () => new Proxy({}, {
|
|
5668
|
+
get: (_, prop) => (typeof prop === 'string' ? `${PARAM_MARKER}${prop}${PARAM_MARKER}` : undefined),
|
|
5669
|
+
});
|
|
5670
|
+
/** Splits a recorded route into its literal chunks and the path params interleaved between them. */
|
|
5671
|
+
const parseRecordedRoute = (recorded) => recorded
|
|
5672
|
+
.split(PARAM_MARKER)
|
|
5673
|
+
// Markers are balanced, so every odd chunk is a param name and every even one is literal text.
|
|
5674
|
+
.map((text, index) => ({ text, param: index % 2 === 1 ? text : null }))
|
|
5675
|
+
.filter((part) => part.text !== '');
|
|
5676
|
+
/**
|
|
5677
|
+
* Parses a query creator route into its literal and path-param parts, so the devtools can show which
|
|
5678
|
+
* segments of a route are dynamic and fill them in with the values a query actually used.
|
|
4971
5679
|
* @internal
|
|
4972
5680
|
*/
|
|
4973
|
-
const
|
|
5681
|
+
const parseQueryRoute = (route) => {
|
|
4974
5682
|
if (route === undefined || route === null)
|
|
4975
|
-
return
|
|
5683
|
+
return [];
|
|
4976
5684
|
try {
|
|
4977
5685
|
if (typeof route === 'function') {
|
|
4978
|
-
return route(
|
|
5686
|
+
return parseRecordedRoute(route(paramRecorder()));
|
|
4979
5687
|
}
|
|
4980
|
-
return String(route);
|
|
5688
|
+
return [{ text: String(route), param: null }];
|
|
4981
5689
|
}
|
|
4982
5690
|
catch {
|
|
4983
|
-
return typeof route === 'function' ? '(dynamic route)' : String(route);
|
|
5691
|
+
return [{ text: typeof route === 'function' ? '(dynamic route)' : String(route), param: null }];
|
|
4984
5692
|
}
|
|
4985
5693
|
};
|
|
5694
|
+
/** Renders parsed route parts as a template, e.g. `/team/:teamId/players`. */
|
|
5695
|
+
const stringifyQueryRouteParts = (parts) => parts.map((part) => (part.param ? `:${part.param}` : part.text)).join('');
|
|
4986
5696
|
/**
|
|
4987
5697
|
* Extracts the human readable client name from a query client tuple. The client's DI token is named
|
|
4988
5698
|
* `QueryClient_<name>`, so we strip the prefix to recover the original name.
|
|
@@ -4993,16 +5703,37 @@ const getQueryClientName = (client) => {
|
|
|
4993
5703
|
const desc = token?.toString?.() ?? '';
|
|
4994
5704
|
return desc.replace('InjectionToken ', '').replace('QueryClient_', '') || 'unknown';
|
|
4995
5705
|
};
|
|
5706
|
+
/**
|
|
5707
|
+
* The provider name behind a bearer auth provider definition, recovered from its DI token the way
|
|
5708
|
+
* {@link getQueryClientName} recovers a client's.
|
|
5709
|
+
* @internal
|
|
5710
|
+
*/
|
|
5711
|
+
const getBearerAuthProviderName = (ref) => {
|
|
5712
|
+
const desc = String(ref.token ?? '');
|
|
5713
|
+
return desc.replace('InjectionToken ', '').replace('BearerAuthProvider_', '') || 'unknown';
|
|
5714
|
+
};
|
|
4996
5715
|
/**
|
|
4997
5716
|
* Ids are derived deterministically from a stable descriptor + a per-descriptor sequence number, so
|
|
4998
5717
|
* they survive a page reload (letting the UI restore the selected entry) instead of being random.
|
|
4999
5718
|
*/
|
|
5000
5719
|
const registerEntry = (registration) => {
|
|
5001
5720
|
const meta = { ...registration.meta };
|
|
5002
|
-
if (registration.route !== undefined)
|
|
5003
|
-
meta.
|
|
5721
|
+
if (registration.route !== undefined) {
|
|
5722
|
+
meta.routeParts = parseQueryRoute(registration.route);
|
|
5723
|
+
meta.route = stringifyQueryRouteParts(meta.routeParts);
|
|
5724
|
+
}
|
|
5004
5725
|
if (registration.clientRef)
|
|
5005
5726
|
meta.clientName = getQueryClientName(registration.clientRef);
|
|
5727
|
+
if (registration.authProviderRef) {
|
|
5728
|
+
meta.isSecure = true;
|
|
5729
|
+
meta.authProviderName = getBearerAuthProviderName(registration.authProviderRef);
|
|
5730
|
+
}
|
|
5731
|
+
if (registration.authQueries) {
|
|
5732
|
+
meta.authQueries = registration.authQueries.map((authQuery) => ({
|
|
5733
|
+
...authQuery,
|
|
5734
|
+
route: stringifyQueryRouteParts(parseQueryRoute(authQuery.route)),
|
|
5735
|
+
}));
|
|
5736
|
+
}
|
|
5006
5737
|
let id = registration.id;
|
|
5007
5738
|
if (!id) {
|
|
5008
5739
|
const descriptor = descriptorOf({ kind: registration.kind, meta });
|
|
@@ -5016,6 +5747,8 @@ const registerEntry = (registration) => {
|
|
|
5016
5747
|
handle: registration.handle,
|
|
5017
5748
|
meta,
|
|
5018
5749
|
createdAt: Date.now(),
|
|
5750
|
+
stats: registration.stats,
|
|
5751
|
+
formLinks: registration.formLinks,
|
|
5019
5752
|
};
|
|
5020
5753
|
entries.update((list) => [...list, fullEntry]);
|
|
5021
5754
|
return () => entries.update((list) => list.filter((e) => e.id !== fullEntry.id));
|
|
@@ -5037,6 +5770,9 @@ const registerEntry = (registration) => {
|
|
|
5037
5770
|
*/
|
|
5038
5771
|
const provideQueryDevtools = () => {
|
|
5039
5772
|
setQueryDevtoolsRegistrar(registerEntry);
|
|
5773
|
+
setQueryDevtoolsStatsFactory(createQueryDevtoolsStats);
|
|
5774
|
+
setQueryDevtoolsFormLinksFactory(createQueryDevtoolsFormLinks);
|
|
5775
|
+
setQueryDevtoolsFaultResolver(resolveQueryDevtoolsFaultForAttempt);
|
|
5040
5776
|
if (!isDevMode()) {
|
|
5041
5777
|
console.warn('You are using the Query Devtools in production mode. This increases the size of your bundle and should only be used for development purposes.');
|
|
5042
5778
|
}
|
|
@@ -5246,6 +5982,9 @@ const createGqlMutationViaPost = /* @__PURE__ */ createGqlCreatorTemplate('MUTAT
|
|
|
5246
5982
|
/** A query creator that creates a secure GQL mutation where the payload is sent via POST (body) */
|
|
5247
5983
|
const createSecureGqlMutationViaPost = /* @__PURE__ */ createSecureGqlCreatorTemplate('MUTATE', 'POST');
|
|
5248
5984
|
|
|
5985
|
+
/**
|
|
5986
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
5987
|
+
*/
|
|
5249
5988
|
class CustomHeaderAuthProvider {
|
|
5250
5989
|
constructor(_config) {
|
|
5251
5990
|
this._config = _config;
|
|
@@ -5258,6 +5997,9 @@ class CustomHeaderAuthProvider {
|
|
|
5258
5997
|
}
|
|
5259
5998
|
}
|
|
5260
5999
|
|
|
6000
|
+
/**
|
|
6001
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6002
|
+
*/
|
|
5261
6003
|
class BasicAuthProvider {
|
|
5262
6004
|
constructor(_config) {
|
|
5263
6005
|
this._config = _config;
|
|
@@ -5273,7 +6015,13 @@ class BasicAuthProvider {
|
|
|
5273
6015
|
}
|
|
5274
6016
|
}
|
|
5275
6017
|
|
|
6018
|
+
/**
|
|
6019
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6020
|
+
*/
|
|
5276
6021
|
const isRequestError = (error) => error instanceof Object && 'status' in error && 'statusText' in error && 'url' in error;
|
|
6022
|
+
/**
|
|
6023
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6024
|
+
*/
|
|
5277
6025
|
const v2ExtractExpiresInSeconds = (headers) => {
|
|
5278
6026
|
const cacheControl = headers['cache-control'];
|
|
5279
6027
|
const age = headers['age'];
|
|
@@ -5319,6 +6067,9 @@ const v2ExtractExpiresInSeconds = (headers) => {
|
|
|
5319
6067
|
}
|
|
5320
6068
|
return expiresIn;
|
|
5321
6069
|
};
|
|
6070
|
+
/**
|
|
6071
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6072
|
+
*/
|
|
5322
6073
|
const serializeBody = (body) => {
|
|
5323
6074
|
if (body === null || body === undefined) {
|
|
5324
6075
|
return null;
|
|
@@ -5336,6 +6087,9 @@ const serializeBody = (body) => {
|
|
|
5336
6087
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5337
6088
|
return body.toString();
|
|
5338
6089
|
};
|
|
6090
|
+
/**
|
|
6091
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6092
|
+
*/
|
|
5339
6093
|
const transformMethod = (config) => {
|
|
5340
6094
|
if (config.method === 'GQL_QUERY' || config.method === 'GQL_MUTATE') {
|
|
5341
6095
|
if (!config.transferVia) {
|
|
@@ -5345,6 +6099,9 @@ const transformMethod = (config) => {
|
|
|
5345
6099
|
}
|
|
5346
6100
|
return config.method;
|
|
5347
6101
|
};
|
|
6102
|
+
/**
|
|
6103
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6104
|
+
*/
|
|
5348
6105
|
const detectContentTypeHeader = (body) => {
|
|
5349
6106
|
// An empty body has no content type.
|
|
5350
6107
|
if (body === null) {
|
|
@@ -5375,14 +6132,23 @@ const detectContentTypeHeader = (body) => {
|
|
|
5375
6132
|
// No type could be inferred.
|
|
5376
6133
|
return null;
|
|
5377
6134
|
};
|
|
6135
|
+
/**
|
|
6136
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6137
|
+
*/
|
|
5378
6138
|
const hasHeader = (headers, header) => {
|
|
5379
6139
|
return Object.keys(headers).some((key) => key.toLowerCase() === header.toLowerCase());
|
|
5380
6140
|
};
|
|
6141
|
+
/**
|
|
6142
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6143
|
+
*/
|
|
5381
6144
|
const forEachHeader = (headers, callback) => {
|
|
5382
6145
|
Object.entries(headers).forEach(([key, value]) => {
|
|
5383
6146
|
callback(key, value);
|
|
5384
6147
|
});
|
|
5385
6148
|
};
|
|
6149
|
+
/**
|
|
6150
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6151
|
+
*/
|
|
5386
6152
|
const parseAllXhrResponseHeaders = (xhr) => {
|
|
5387
6153
|
const headers = xhr.getAllResponseHeaders();
|
|
5388
6154
|
const parsedHeaders = {};
|
|
@@ -5397,6 +6163,9 @@ const parseAllXhrResponseHeaders = (xhr) => {
|
|
|
5397
6163
|
}
|
|
5398
6164
|
return parsedHeaders;
|
|
5399
6165
|
};
|
|
6166
|
+
/**
|
|
6167
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6168
|
+
*/
|
|
5400
6169
|
const getResponseUrl = (xhr) => {
|
|
5401
6170
|
if ('responseURL' in xhr && xhr.responseURL) {
|
|
5402
6171
|
return xhr.responseURL;
|
|
@@ -5406,6 +6175,9 @@ const getResponseUrl = (xhr) => {
|
|
|
5406
6175
|
}
|
|
5407
6176
|
return null;
|
|
5408
6177
|
};
|
|
6178
|
+
/**
|
|
6179
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6180
|
+
*/
|
|
5409
6181
|
const v2ShouldRetryRequest = (config) => {
|
|
5410
6182
|
const defaultRetryDelay = 1000 + 1000 * config.currentRetryCount;
|
|
5411
6183
|
if (config.currentRetryCount > 3) {
|
|
@@ -5448,6 +6220,9 @@ const v2ShouldRetryRequest = (config) => {
|
|
|
5448
6220
|
};
|
|
5449
6221
|
|
|
5450
6222
|
const XSSI_PREFIX = /^\)\]\}',?\n/;
|
|
6223
|
+
/**
|
|
6224
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6225
|
+
*/
|
|
5451
6226
|
const request = (config) => {
|
|
5452
6227
|
const headers = config.headers || {};
|
|
5453
6228
|
const responseType = config.responseType || 'json';
|
|
@@ -5653,11 +6428,16 @@ const request = (config) => {
|
|
|
5653
6428
|
});
|
|
5654
6429
|
};
|
|
5655
6430
|
|
|
6431
|
+
/**
|
|
6432
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6433
|
+
*/
|
|
5656
6434
|
const addQueryContainerHandling = (obs, valueFn, config) => {
|
|
5657
|
-
|
|
6435
|
+
if (!config?.injector) {
|
|
6436
|
+
assertInInjectionContext(addQueryContainerHandling);
|
|
6437
|
+
}
|
|
5658
6438
|
const { abortPrevious, abortOnDestroy, stopPreviousPolling } = config ?? {};
|
|
5659
|
-
const injector = inject(Injector);
|
|
5660
|
-
const destroy$ = createDestroy();
|
|
6439
|
+
const injector = config?.injector ?? inject(Injector);
|
|
6440
|
+
const destroy$ = runInInjectionContext(injector, () => createDestroy());
|
|
5661
6441
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5662
6442
|
const tNode = injector._tNode;
|
|
5663
6443
|
const componentId = tNode?.index ?? -1;
|
|
@@ -5722,12 +6502,18 @@ const addQueryContainerHandling = (obs, valueFn, config) => {
|
|
|
5722
6502
|
}
|
|
5723
6503
|
});
|
|
5724
6504
|
};
|
|
6505
|
+
/**
|
|
6506
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6507
|
+
*/
|
|
5725
6508
|
function toQuerySignal(source, options) {
|
|
5726
6509
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
5727
6510
|
const s = toSignal(source, options);
|
|
5728
6511
|
addQueryContainerHandling(source, () => s(), options);
|
|
5729
6512
|
return s;
|
|
5730
6513
|
}
|
|
6514
|
+
/**
|
|
6515
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6516
|
+
*/
|
|
5731
6517
|
function effectComputed(computation, injector) {
|
|
5732
6518
|
let initialData = null;
|
|
5733
6519
|
try {
|
|
@@ -5746,11 +6532,14 @@ function effectComputed(computation, injector) {
|
|
|
5746
6532
|
return;
|
|
5747
6533
|
lastResult.set(data);
|
|
5748
6534
|
});
|
|
5749
|
-
});
|
|
6535
|
+
}, { injector });
|
|
5750
6536
|
return lastResult.asReadonly();
|
|
5751
6537
|
}
|
|
6538
|
+
/**
|
|
6539
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6540
|
+
*/
|
|
5752
6541
|
function queryComputed(computation, options) {
|
|
5753
|
-
const injector = inject(Injector);
|
|
6542
|
+
const injector = options?.injector ?? inject(Injector);
|
|
5754
6543
|
const c = effectComputed(computation, injector);
|
|
5755
6544
|
const obs = toObservable(c, options);
|
|
5756
6545
|
addQueryContainerHandling(obs, () => c(), options);
|
|
@@ -5759,26 +6548,37 @@ function queryComputed(computation, options) {
|
|
|
5759
6548
|
/**
|
|
5760
6549
|
* Creates a signal that will only be reactive until the first query is created.
|
|
5761
6550
|
* All subsequent changes inside the computation will be ignored.
|
|
6551
|
+
*
|
|
6552
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
5762
6553
|
*/
|
|
5763
6554
|
function queryComputedTillTruthy(computation, options) {
|
|
5764
|
-
const injector = inject(Injector);
|
|
6555
|
+
const injector = options?.injector ?? inject(Injector);
|
|
5765
6556
|
const c = computedTillTruthy(effectComputed(computation, injector));
|
|
5766
6557
|
const obs = toObservable(c, options);
|
|
5767
6558
|
addQueryContainerHandling(obs, () => c(), options);
|
|
5768
6559
|
return c;
|
|
5769
6560
|
}
|
|
6561
|
+
/**
|
|
6562
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6563
|
+
*/
|
|
5770
6564
|
function queryArrayComputed(computation, options) {
|
|
5771
|
-
const injector = inject(Injector);
|
|
6565
|
+
const injector = options?.injector ?? inject(Injector);
|
|
5772
6566
|
const c = effectComputed(computation, injector);
|
|
5773
6567
|
const obs = toObservable(c, options);
|
|
5774
6568
|
addQueryContainerHandling(obs, () => c(), options);
|
|
5775
6569
|
return c;
|
|
5776
6570
|
}
|
|
6571
|
+
/**
|
|
6572
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6573
|
+
*/
|
|
5777
6574
|
function toQuerySubject(source, options) {
|
|
5778
6575
|
const obs = toObservable(source, options);
|
|
5779
6576
|
addQueryContainerHandling(obs, () => source(), options);
|
|
5780
6577
|
return obs;
|
|
5781
6578
|
}
|
|
6579
|
+
/**
|
|
6580
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6581
|
+
*/
|
|
5782
6582
|
function queryStateSignal(source, options) {
|
|
5783
6583
|
const { cacheResponse } = options ?? {};
|
|
5784
6584
|
const roSignal = toSignal(toObservable(source).pipe(switchMap((q) => extractQuery(q)?.state$ ?? of(null)), switchMap((state) => {
|
|
@@ -5793,6 +6593,9 @@ function queryStateSignal(source, options) {
|
|
|
5793
6593
|
...(ngDevMode ? [{ debugName: "rwSignal" }] : /* istanbul ignore next */ []));
|
|
5794
6594
|
return rwSignal;
|
|
5795
6595
|
}
|
|
6596
|
+
/**
|
|
6597
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6598
|
+
*/
|
|
5796
6599
|
function queryStateResponseSignal(source, options) {
|
|
5797
6600
|
const s = queryStateSignal(source, options);
|
|
5798
6601
|
const dataSignal = computed(() => {
|
|
@@ -5805,6 +6608,9 @@ function queryStateResponseSignal(source, options) {
|
|
|
5805
6608
|
};
|
|
5806
6609
|
return dataSignal;
|
|
5807
6610
|
}
|
|
6611
|
+
/**
|
|
6612
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6613
|
+
*/
|
|
5808
6614
|
function queryStateErrorSignal(source) {
|
|
5809
6615
|
const s = queryStateSignal(source);
|
|
5810
6616
|
return computed(() => {
|
|
@@ -5812,6 +6618,9 @@ function queryStateErrorSignal(source) {
|
|
|
5812
6618
|
return isQueryStateFailure(state) ? state.error : null;
|
|
5813
6619
|
});
|
|
5814
6620
|
}
|
|
6621
|
+
/**
|
|
6622
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6623
|
+
*/
|
|
5815
6624
|
function queryStateLoadingSignal(source) {
|
|
5816
6625
|
const s = queryStateSignal(source);
|
|
5817
6626
|
return computed(() => {
|
|
@@ -5820,6 +6629,9 @@ function queryStateLoadingSignal(source) {
|
|
|
5820
6629
|
});
|
|
5821
6630
|
}
|
|
5822
6631
|
|
|
6632
|
+
/**
|
|
6633
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6634
|
+
*/
|
|
5823
6635
|
const deepFreeze = (object) => {
|
|
5824
6636
|
// check if object is actually an object or frozen
|
|
5825
6637
|
if (!object || typeof object !== 'object' || Object.isFrozen(object)) {
|
|
@@ -5842,9 +6654,14 @@ const deepFreeze = (object) => {
|
|
|
5842
6654
|
* **This function always returns null.**
|
|
5843
6655
|
* @see https://github.com/microsoft/TypeScript/issues/26242
|
|
5844
6656
|
* @returns null
|
|
6657
|
+
*
|
|
6658
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
5845
6659
|
*/
|
|
5846
6660
|
const def = () => null;
|
|
5847
6661
|
|
|
6662
|
+
/**
|
|
6663
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6664
|
+
*/
|
|
5848
6665
|
class V2QueryCreator {
|
|
5849
6666
|
constructor(_queryConfig, _client, _store) {
|
|
5850
6667
|
this._queryConfig = _queryConfig;
|
|
@@ -5891,6 +6708,9 @@ class V2QueryCreator {
|
|
|
5891
6708
|
}
|
|
5892
6709
|
}
|
|
5893
6710
|
|
|
6711
|
+
/**
|
|
6712
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6713
|
+
*/
|
|
5894
6714
|
class QueryStore {
|
|
5895
6715
|
constructor(_config) {
|
|
5896
6716
|
this._config = _config;
|
|
@@ -6034,6 +6854,9 @@ class QueryStore {
|
|
|
6034
6854
|
}
|
|
6035
6855
|
}
|
|
6036
6856
|
|
|
6857
|
+
/**
|
|
6858
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6859
|
+
*/
|
|
6037
6860
|
class V2QueryClient {
|
|
6038
6861
|
constructor(_clientConfig) {
|
|
6039
6862
|
this._clientConfig = _clientConfig;
|
|
@@ -6111,9 +6934,15 @@ class V2QueryClient {
|
|
|
6111
6934
|
}
|
|
6112
6935
|
}
|
|
6113
6936
|
|
|
6937
|
+
/**
|
|
6938
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6939
|
+
*/
|
|
6114
6940
|
const v2ShouldCacheQuery = (method) => {
|
|
6115
6941
|
return method === 'GET' || method === 'OPTIONS' || method === 'HEAD' || method === 'GQL_QUERY';
|
|
6116
6942
|
};
|
|
6943
|
+
/**
|
|
6944
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6945
|
+
*/
|
|
6117
6946
|
const v2BuildQueryCacheKey = (route, args) => {
|
|
6118
6947
|
const variables = JSON.stringify(args?.variables || {})
|
|
6119
6948
|
// replace all curly braces with empty string
|
|
@@ -6131,6 +6960,64 @@ const v2BuildQueryCacheKey = (route, args) => {
|
|
|
6131
6960
|
return hash.toString();
|
|
6132
6961
|
};
|
|
6133
6962
|
|
|
6963
|
+
let fallbackInjector = null;
|
|
6964
|
+
/**
|
|
6965
|
+
* The stashed root injector, or `null` when the fallback was never provided (or was provided on the
|
|
6966
|
+
* server, where stashing is refused).
|
|
6967
|
+
*
|
|
6968
|
+
* @internal
|
|
6969
|
+
*/
|
|
6970
|
+
const legacyPrepareFallbackInjector = () => fallbackInjector;
|
|
6971
|
+
/**
|
|
6972
|
+
* Lets `prepare()` fall back to the application's root injector when it is called outside any injection
|
|
6973
|
+
* context, instead of throwing `ET950`.
|
|
6974
|
+
*
|
|
6975
|
+
* Opt-in, and **browser only**. Two things degrade when the fallback is used, both matching v2, which had
|
|
6976
|
+
* no injector to scope to at all:
|
|
6977
|
+
*
|
|
6978
|
+
* - **The query's lifetime becomes the application's.** Nothing tears it down when a component dies, so
|
|
6979
|
+
* cleanup rests entirely on a query container or `config: { destroyOnResponse: true }`.
|
|
6980
|
+
* - **The devtools lose the host element**, so "inspect" cannot jump to the component. Already the case
|
|
6981
|
+
* for any query created from a root or environment injector.
|
|
6982
|
+
*
|
|
6983
|
+
* It refuses to stash anything on the server: a module-global injector would be shared across concurrent
|
|
6984
|
+
* requests, which is data bleed rather than a leak. Server-side renders keep throwing `ET950`, so a call
|
|
6985
|
+
* site that only works in the browser fails loudly where it matters.
|
|
6986
|
+
*
|
|
6987
|
+
* Prefer passing `injector` at the call site - it keeps the query scoped to whatever created it. Reach for
|
|
6988
|
+
* this when a migration has too many call sites to thread by hand.
|
|
6989
|
+
*
|
|
6990
|
+
* @example
|
|
6991
|
+
* ```ts
|
|
6992
|
+
* bootstrapApplication(AppComponent, {
|
|
6993
|
+
* providers: [provideLegacyPrepareFallback()],
|
|
6994
|
+
* });
|
|
6995
|
+
* ```
|
|
6996
|
+
*
|
|
6997
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6998
|
+
*/
|
|
6999
|
+
const provideLegacyPrepareFallback = () => makeEnvironmentProviders([
|
|
7000
|
+
provideEnvironmentInitializer(() => {
|
|
7001
|
+
if (!isPlatformBrowser(inject(PLATFORM_ID))) {
|
|
7002
|
+
if (isDevMode()) {
|
|
7003
|
+
console.warn('provideLegacyPrepareFallback() does nothing on the server: a module-global injector would be shared ' +
|
|
7004
|
+
'between concurrent requests. prepare() calls without an injection context keep throwing ET950 there.');
|
|
7005
|
+
}
|
|
7006
|
+
return;
|
|
7007
|
+
}
|
|
7008
|
+
const injector = inject(EnvironmentInjector);
|
|
7009
|
+
fallbackInjector = injector;
|
|
7010
|
+
inject(DestroyRef).onDestroy(() => {
|
|
7011
|
+
if (fallbackInjector === injector) {
|
|
7012
|
+
fallbackInjector = null;
|
|
7013
|
+
}
|
|
7014
|
+
});
|
|
7015
|
+
}),
|
|
7016
|
+
]);
|
|
7017
|
+
|
|
7018
|
+
/**
|
|
7019
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7020
|
+
*/
|
|
6134
7021
|
const transformExecStateToQueryState = (execState) => {
|
|
6135
7022
|
const meta = { id: -1, triggeredVia: 'program' };
|
|
6136
7023
|
switch (execState?.type) {
|
|
@@ -6182,6 +7069,9 @@ const transformExecStateToQueryState = (execState) => {
|
|
|
6182
7069
|
}
|
|
6183
7070
|
}
|
|
6184
7071
|
};
|
|
7072
|
+
/**
|
|
7073
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7074
|
+
*/
|
|
6185
7075
|
const isLegacyQuery = (query) => {
|
|
6186
7076
|
if (!query || typeof query !== 'object' || Array.isArray(query)) {
|
|
6187
7077
|
return false;
|
|
@@ -6191,6 +7081,9 @@ const isLegacyQuery = (query) => {
|
|
|
6191
7081
|
}
|
|
6192
7082
|
return true;
|
|
6193
7083
|
};
|
|
7084
|
+
/**
|
|
7085
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7086
|
+
*/
|
|
6194
7087
|
class LegacyQuery {
|
|
6195
7088
|
constructor(newQuery, _arguments, entity,
|
|
6196
7089
|
/**
|
|
@@ -6199,11 +7092,19 @@ class LegacyQuery {
|
|
|
6199
7092
|
*
|
|
6200
7093
|
* @internal
|
|
6201
7094
|
*/
|
|
6202
|
-
_isInert = false
|
|
7095
|
+
_isInert = false,
|
|
7096
|
+
/**
|
|
7097
|
+
* Whether the underlying request is cacheable. Resolved by the creator, which is the only place that
|
|
7098
|
+
* knows the method before the first execution.
|
|
7099
|
+
*
|
|
7100
|
+
* @internal
|
|
7101
|
+
*/
|
|
7102
|
+
_canBeCached = false) {
|
|
6203
7103
|
this.newQuery = newQuery;
|
|
6204
7104
|
this._arguments = _arguments;
|
|
6205
7105
|
this.entity = entity;
|
|
6206
7106
|
this._isInert = _isInert;
|
|
7107
|
+
this._canBeCached = _canBeCached;
|
|
6207
7108
|
/**
|
|
6208
7109
|
* @internal
|
|
6209
7110
|
*/
|
|
@@ -6238,17 +7139,22 @@ class LegacyQuery {
|
|
|
6238
7139
|
}
|
|
6239
7140
|
this.state$ = toObservable(this.newQuery.executionState, { injector: this.newQuery.subtle.injector }).pipe(map((execState) => transformExecStateToQueryState(execState)), switchMap((s) => this._transformState(s)), shareReplay({ bufferSize: 1, refCount: true }));
|
|
6240
7141
|
this.storeSyncEffect = effect(() => {
|
|
6241
|
-
|
|
7142
|
+
// Gated on the execution state, not on the response being non-null: the effect runs once on
|
|
7143
|
+
// creation, and `response()` is null until the first one arrives - `id`/`set` are typed
|
|
7144
|
+
// non-nullable, so an unguarded write feeds `null` through them on every `prepare()`. A plain
|
|
7145
|
+
// null-check would be wrong the other way: a 204 is a success whose body is legitimately null.
|
|
7146
|
+
const execState = this.newQuery.executionState();
|
|
6242
7147
|
untracked(() => {
|
|
6243
|
-
if (this.entity?.set)
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
7148
|
+
if (execState?.type !== 'success' || !this.entity?.set)
|
|
7149
|
+
return;
|
|
7150
|
+
const res = execState.response;
|
|
7151
|
+
const id = this.entity.id({ args: this._arguments, response: res });
|
|
7152
|
+
this.entity.set({
|
|
7153
|
+
args: this._arguments,
|
|
7154
|
+
response: res,
|
|
7155
|
+
id,
|
|
7156
|
+
store: this.entity.store,
|
|
7157
|
+
});
|
|
6252
7158
|
});
|
|
6253
7159
|
}, /* @ts-ignore */
|
|
6254
7160
|
...(ngDevMode ? [{ debugName: "storeSyncEffect" }] : /* istanbul ignore next */ []));
|
|
@@ -6292,7 +7198,7 @@ class LegacyQuery {
|
|
|
6292
7198
|
return null;
|
|
6293
7199
|
}
|
|
6294
7200
|
get canBeCached() {
|
|
6295
|
-
return
|
|
7201
|
+
return this._canBeCached;
|
|
6296
7202
|
}
|
|
6297
7203
|
get _isInMockMode() {
|
|
6298
7204
|
return false;
|
|
@@ -6484,32 +7390,61 @@ const isInjectorUsable = (injector) => {
|
|
|
6484
7390
|
return false;
|
|
6485
7391
|
}
|
|
6486
7392
|
};
|
|
7393
|
+
/**
|
|
7394
|
+
* Angular's NG0203, thrown by `inject()` outside an injection context. Matched on the code rather than
|
|
7395
|
+
* the message: production builds strip the message, and NG0205 ("Injector has already been destroyed")
|
|
7396
|
+
* comes out of the same `inject()` call for a different reason entirely.
|
|
7397
|
+
*/
|
|
7398
|
+
const isMissingInjectionContextError = (error) => typeof error === 'object' && error !== null && Math.abs(Number(error.code)) === 203;
|
|
7399
|
+
/**
|
|
7400
|
+
* What the creator was built with, for diagnostics - `GET /person`. Lets an unnamed wrapper still point
|
|
7401
|
+
* at an endpoint instead of "a legacy query creator". A route function cannot be described this way, but
|
|
7402
|
+
* those wrappers come from the generator, which always emits a `name`.
|
|
7403
|
+
*/
|
|
7404
|
+
const describeCreator = (creator) => {
|
|
7405
|
+
const { method, route } = creator.subtle.creatorInternals;
|
|
7406
|
+
if (typeof route !== 'string')
|
|
7407
|
+
return undefined;
|
|
7408
|
+
return typeof method === 'string' ? `${method} ${route}` : route;
|
|
7409
|
+
};
|
|
7410
|
+
/**
|
|
7411
|
+
* Mirrors the cacheability decision the query repository makes per request: cacheable HTTP methods and
|
|
7412
|
+
* GraphQL queries, unless the creator opted in or out explicitly. The legacy container helpers branch on
|
|
7413
|
+
* `canBeCached` for their default cleanup, so a wrapper has to answer before the first execution.
|
|
7414
|
+
*/
|
|
7415
|
+
const canCreatorBeCached = (creator) => {
|
|
7416
|
+
const { method } = creator.subtle.creatorInternals;
|
|
7417
|
+
const explicit = creator.subtle.creatorOptions?.subtle
|
|
7418
|
+
?.useQueryRepositoryCache;
|
|
7419
|
+
if (explicit !== undefined)
|
|
7420
|
+
return explicit;
|
|
7421
|
+
if (method === 'QUERY')
|
|
7422
|
+
return true;
|
|
7423
|
+
if (method === 'MUTATE')
|
|
7424
|
+
return false;
|
|
7425
|
+
return typeof method === 'string' && shouldCacheQuery(method);
|
|
7426
|
+
};
|
|
7427
|
+
/**
|
|
7428
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7429
|
+
*/
|
|
6487
7430
|
class LegacyQueryCreator {
|
|
6488
7431
|
constructor(options) {
|
|
6489
7432
|
this.options = options;
|
|
6490
7433
|
this.prepare = (args) => {
|
|
6491
|
-
|
|
6492
|
-
// Angular's raw NG0203 names neither the query nor the call site, which makes those a bisection
|
|
6493
|
-
// exercise - replace it with an error that does.
|
|
6494
|
-
const injector = args?.injector ??
|
|
6495
|
-
(() => {
|
|
6496
|
-
try {
|
|
6497
|
-
return inject(Injector);
|
|
6498
|
-
}
|
|
6499
|
-
catch {
|
|
6500
|
-
throw legacyPrepareWithoutInjectionContext(this.options.name);
|
|
6501
|
-
}
|
|
6502
|
-
})();
|
|
7434
|
+
const injector = args?.injector ?? this.resolveAmbientInjector('prepare');
|
|
6503
7435
|
let headers = new HttpHeaders();
|
|
6504
7436
|
if (args?.headers) {
|
|
6505
|
-
Object.entries(args.headers)
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
|
|
7437
|
+
for (const [key, value] of Object.entries(args.headers)) {
|
|
7438
|
+
// Only an absent value is skipped - an empty string is a header the caller asked to send.
|
|
7439
|
+
if (value === undefined || value === null)
|
|
7440
|
+
continue;
|
|
7441
|
+
headers = headers.set(key, value);
|
|
7442
|
+
}
|
|
6510
7443
|
}
|
|
6511
7444
|
const queryArgs = {
|
|
6512
|
-
|
|
7445
|
+
// Presence, not truthiness: `0`, `''` and `false` are bodies, and dropping them silently sends a
|
|
7446
|
+
// different request than the call site asked for.
|
|
7447
|
+
...(args?.body !== undefined ? { body: args.body } : {}),
|
|
6513
7448
|
...(args?.pathParams ? { pathParams: args.pathParams } : {}),
|
|
6514
7449
|
...(args?.queryParams ? { queryParams: args.queryParams } : {}),
|
|
6515
7450
|
headers,
|
|
@@ -6520,10 +7455,10 @@ class LegacyQueryCreator {
|
|
|
6520
7455
|
// yields a query that does nothing instead of an NG0205 raised from the view's cleanup phase.
|
|
6521
7456
|
if (!isInjectorUsable(injector)) {
|
|
6522
7457
|
if (isDevMode()) {
|
|
6523
|
-
console.warn(`${this.
|
|
7458
|
+
console.warn(`${this.label ? `"${this.label}"` : 'A legacy query'}.prepare() was called with a destroyed injector, so an inert query was returned. ` +
|
|
6524
7459
|
`This usually means a callback outlived the component whose injector it captured - guard the call site with DestroyRef.onDestroy, or pass an injector that outlives it.`);
|
|
6525
7460
|
}
|
|
6526
|
-
return new LegacyQuery(createInertQuery(), queryArgs, this.options.entity, true);
|
|
7461
|
+
return new LegacyQuery(createInertQuery(), queryArgs, this.options.entity, true, this.canBeCached);
|
|
6527
7462
|
}
|
|
6528
7463
|
return runInInjectionContext(injector, () => {
|
|
6529
7464
|
return untracked(() => {
|
|
@@ -6534,33 +7469,71 @@ class LegacyQueryCreator {
|
|
|
6534
7469
|
// `LegacyQuery.execute()` forwards v2's `skipCache` as `allowCache` for every method, cacheable or not.
|
|
6535
7470
|
silenceUncacheableAllowCacheError: true,
|
|
6536
7471
|
});
|
|
6537
|
-
const legacyQuery = new LegacyQuery(newQuery, queryArgs, this.options.entity);
|
|
7472
|
+
const legacyQuery = new LegacyQuery(newQuery, queryArgs, this.options.entity, false, this.canBeCached);
|
|
6538
7473
|
if (args?.config?.destroyOnResponse) {
|
|
6539
|
-
|
|
6540
|
-
|
|
7474
|
+
// Owned by the query's own injector, not the caller's. Call sites are told to pass an injector
|
|
7475
|
+
// that outlives them, so they hand over a root or environment one - and an effect that only
|
|
7476
|
+
// tears itself down on a terminal state then outlives an aborted or never-executed query for
|
|
7477
|
+
// the lifetime of the app. Tied to the query, it dies with the thing it exists to destroy.
|
|
7478
|
+
effect(() => {
|
|
7479
|
+
const type = newQuery.executionState()?.type;
|
|
7480
|
+
if (type === 'success' || type === 'failure') {
|
|
6541
7481
|
legacyQuery.destroy();
|
|
6542
|
-
destroyEffect.destroy();
|
|
6543
7482
|
}
|
|
6544
|
-
},
|
|
6545
|
-
...(ngDevMode ? [{ debugName: "destroyEffect" }] : /* istanbul ignore next */ []));
|
|
7483
|
+
}, { injector: newQuery.subtle.injector });
|
|
6546
7484
|
}
|
|
6547
7485
|
return legacyQuery;
|
|
6548
7486
|
});
|
|
6549
7487
|
});
|
|
6550
7488
|
};
|
|
6551
7489
|
this.createSubject = (initialValue, config) => {
|
|
7490
|
+
const injector = config?.injector ?? this.resolveAmbientInjector('createSubject');
|
|
6552
7491
|
const subject = new BehaviorSubject(initialValue ?? null);
|
|
6553
|
-
addQueryContainerHandling(subject, () => subject.getValue(), config);
|
|
7492
|
+
addQueryContainerHandling(subject, () => subject.getValue(), { ...config, injector });
|
|
6554
7493
|
return subject;
|
|
6555
7494
|
};
|
|
6556
7495
|
this.createSignal = (initialValue, config) => {
|
|
7496
|
+
const injector = config?.injector ?? this.resolveAmbientInjector('createSignal');
|
|
6557
7497
|
const _signal = signal(initialValue ?? null, /* @ts-ignore */
|
|
6558
7498
|
...(ngDevMode ? [{ debugName: "_signal" }] : /* istanbul ignore next */ []));
|
|
6559
|
-
addQueryContainerHandling(toObservable(_signal), () => _signal(), config);
|
|
7499
|
+
addQueryContainerHandling(toObservable(_signal, { injector }), () => _signal(), { ...config, injector });
|
|
6560
7500
|
return _signal;
|
|
6561
7501
|
};
|
|
6562
7502
|
this.behaviorSubject = this.createSubject;
|
|
6563
7503
|
}
|
|
7504
|
+
/**
|
|
7505
|
+
* Whether the underlying request is cacheable, which is what the legacy container helpers use to decide
|
|
7506
|
+
* their default cleanup. Resolved from the creator, so it answers before the first execution.
|
|
7507
|
+
*/
|
|
7508
|
+
get canBeCached() {
|
|
7509
|
+
return canCreatorBeCached(this.options.creator);
|
|
7510
|
+
}
|
|
7511
|
+
get label() {
|
|
7512
|
+
return this.options.name ?? describeCreator(this.options.creator);
|
|
7513
|
+
}
|
|
7514
|
+
/**
|
|
7515
|
+
* v2 needed no injection context, so plenty of call sites are plain callbacks. Angular's raw NG0203
|
|
7516
|
+
* names neither the query nor the call site, which makes those a bisection exercise - replace it with
|
|
7517
|
+
* an error that does, and let every other DI failure (NG0205 from an injector mid-teardown, a missing
|
|
7518
|
+
* provider) through untouched.
|
|
7519
|
+
*/
|
|
7520
|
+
resolveAmbientInjector(method) {
|
|
7521
|
+
try {
|
|
7522
|
+
return inject(Injector);
|
|
7523
|
+
}
|
|
7524
|
+
catch (error) {
|
|
7525
|
+
if (!isMissingInjectionContextError(error)) {
|
|
7526
|
+
throw error;
|
|
7527
|
+
}
|
|
7528
|
+
const fallback = legacyPrepareFallbackInjector();
|
|
7529
|
+
// A stale stash (the app injector is gone) is no injector at all - handing it over would build a
|
|
7530
|
+
// query that silently never runs.
|
|
7531
|
+
if (fallback && isInjectorUsable(fallback)) {
|
|
7532
|
+
return fallback;
|
|
7533
|
+
}
|
|
7534
|
+
throw legacyPrepareWithoutInjectionContext(this.label, method);
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
6564
7537
|
}
|
|
6565
7538
|
/**
|
|
6566
7539
|
* Creates a legacy query creator.
|
|
@@ -6568,32 +7541,49 @@ class LegacyQueryCreator {
|
|
|
6568
7541
|
* **NOTE**: This is a temporary solution to support legacy queries. It will be removed in the future.
|
|
6569
7542
|
*
|
|
6570
7543
|
* **DO NOT TURN NEW QUERY CREATORS INTO LEGACY ONES MANUALLY. THIS IS ONLY NEEDED FOR MIGRATION PURPOSES.**
|
|
7544
|
+
*
|
|
7545
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
6571
7546
|
*/
|
|
6572
7547
|
const createLegacyQueryCreator = (options) => {
|
|
6573
7548
|
return new LegacyQueryCreator(options);
|
|
6574
7549
|
};
|
|
6575
7550
|
|
|
6576
7551
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7552
|
+
/**
|
|
7553
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7554
|
+
*/
|
|
6577
7555
|
function filterSuccess() {
|
|
6578
7556
|
return function (source) {
|
|
6579
7557
|
return source.pipe(filter((value) => isQueryStateSuccess(value)));
|
|
6580
7558
|
};
|
|
6581
7559
|
}
|
|
7560
|
+
/**
|
|
7561
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7562
|
+
*/
|
|
6582
7563
|
function ignoreAutoRefresh() {
|
|
6583
7564
|
return function (source) {
|
|
6584
7565
|
return source.pipe(filter((value) => !isAutoRefresh(value)));
|
|
6585
7566
|
};
|
|
6586
7567
|
}
|
|
7568
|
+
/**
|
|
7569
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7570
|
+
*/
|
|
6587
7571
|
function filterFailure() {
|
|
6588
7572
|
return function (source) {
|
|
6589
7573
|
return source.pipe(filter((value) => isQueryStateFailure(value)));
|
|
6590
7574
|
};
|
|
6591
7575
|
}
|
|
7576
|
+
/**
|
|
7577
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7578
|
+
*/
|
|
6592
7579
|
function filterQueryStates(allowedStates) {
|
|
6593
7580
|
return function (source) {
|
|
6594
7581
|
return source.pipe(filter((value) => value && allowedStates.includes(value.type)));
|
|
6595
7582
|
};
|
|
6596
7583
|
}
|
|
7584
|
+
/**
|
|
7585
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7586
|
+
*/
|
|
6597
7587
|
function takeUntilResponse(config) {
|
|
6598
7588
|
return function (source) {
|
|
6599
7589
|
return source.pipe(takeWhile((value) => {
|
|
@@ -6604,21 +7594,33 @@ function takeUntilResponse(config) {
|
|
|
6604
7594
|
}, true));
|
|
6605
7595
|
};
|
|
6606
7596
|
}
|
|
7597
|
+
/**
|
|
7598
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7599
|
+
*/
|
|
6607
7600
|
function filterNull() {
|
|
6608
7601
|
return function (source) {
|
|
6609
7602
|
return source.pipe(filter((value) => value !== null));
|
|
6610
7603
|
};
|
|
6611
7604
|
}
|
|
7605
|
+
/**
|
|
7606
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7607
|
+
*/
|
|
6612
7608
|
function switchQueryState() {
|
|
6613
7609
|
return function (source) {
|
|
6614
7610
|
return source.pipe(switchMap((value) => value?.state$ ?? of(null)));
|
|
6615
7611
|
};
|
|
6616
7612
|
}
|
|
7613
|
+
/**
|
|
7614
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7615
|
+
*/
|
|
6617
7616
|
function switchQueryCollectionState() {
|
|
6618
7617
|
return function (source) {
|
|
6619
7618
|
return source.pipe(switchMap((value) => value?.query.state$ ?? of(null)));
|
|
6620
7619
|
};
|
|
6621
7620
|
}
|
|
7621
|
+
/**
|
|
7622
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7623
|
+
*/
|
|
6622
7624
|
const resetPageOnError = (config) => (source) => {
|
|
6623
7625
|
const { queryForm, pageControlKey = 'page' } = config;
|
|
6624
7626
|
if (isDevMode() && !queryForm.form.controls[pageControlKey]) {
|
|
@@ -6647,12 +7649,33 @@ const resetPageOnError = (config) => (source) => {
|
|
|
6647
7649
|
}), takeUntilResponse());
|
|
6648
7650
|
}));
|
|
6649
7651
|
};
|
|
7652
|
+
/**
|
|
7653
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7654
|
+
*/
|
|
6650
7655
|
const isQueryStateLoading = (state) => state?.type === "LOADING" /* QueryStateType.Loading */;
|
|
7656
|
+
/**
|
|
7657
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7658
|
+
*/
|
|
6651
7659
|
const isQueryStateSuccess = (state) => state?.type === "SUCCESS" /* QueryStateType.Success */;
|
|
7660
|
+
/**
|
|
7661
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7662
|
+
*/
|
|
6652
7663
|
const isQueryStateFailure = (state) => state?.type === "FAILURE" /* QueryStateType.Failure */;
|
|
7664
|
+
/**
|
|
7665
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7666
|
+
*/
|
|
6653
7667
|
const isQueryStateCancelled = (state) => state?.type === "CANCELLED" /* QueryStateType.Cancelled */;
|
|
7668
|
+
/**
|
|
7669
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7670
|
+
*/
|
|
6654
7671
|
const isQueryStatePrepared = (state) => state?.type === "PREPARED" /* QueryStateType.Prepared */;
|
|
7672
|
+
/**
|
|
7673
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7674
|
+
*/
|
|
6655
7675
|
const isAutoRefresh = (state) => state?.meta.triggeredVia === 'auto';
|
|
7676
|
+
/**
|
|
7677
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7678
|
+
*/
|
|
6656
7679
|
const mergeHeaders = (...headers) => {
|
|
6657
7680
|
return headers.reduce((acc, headers) => {
|
|
6658
7681
|
if (!headers) {
|
|
@@ -6664,6 +7687,9 @@ const mergeHeaders = (...headers) => {
|
|
|
6664
7687
|
};
|
|
6665
7688
|
}, {});
|
|
6666
7689
|
};
|
|
7690
|
+
/**
|
|
7691
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7692
|
+
*/
|
|
6667
7693
|
const isGqlQueryConfig = (config) => {
|
|
6668
7694
|
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
6669
7695
|
return false;
|
|
@@ -6673,6 +7699,9 @@ const isGqlQueryConfig = (config) => {
|
|
|
6673
7699
|
}
|
|
6674
7700
|
return true;
|
|
6675
7701
|
};
|
|
7702
|
+
/**
|
|
7703
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7704
|
+
*/
|
|
6676
7705
|
const isQuery = (query) => {
|
|
6677
7706
|
if (!query || typeof query !== 'object' || Array.isArray(query)) {
|
|
6678
7707
|
return false;
|
|
@@ -6682,6 +7711,9 @@ const isQuery = (query) => {
|
|
|
6682
7711
|
}
|
|
6683
7712
|
return true;
|
|
6684
7713
|
};
|
|
7714
|
+
/**
|
|
7715
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7716
|
+
*/
|
|
6685
7717
|
const isQueryCollection = (query) => {
|
|
6686
7718
|
if (!query || typeof query !== 'object' || Array.isArray(query)) {
|
|
6687
7719
|
return false;
|
|
@@ -6691,9 +7723,15 @@ const isQueryCollection = (query) => {
|
|
|
6691
7723
|
}
|
|
6692
7724
|
return true;
|
|
6693
7725
|
};
|
|
7726
|
+
/**
|
|
7727
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7728
|
+
*/
|
|
6694
7729
|
const createQueryCollection = (
|
|
6695
7730
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
6696
7731
|
queryMap) => new BehaviorSubject(null);
|
|
7732
|
+
/**
|
|
7733
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7734
|
+
*/
|
|
6697
7735
|
const createQueryCollectionSubject = (
|
|
6698
7736
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
6699
7737
|
queryMap, config) => {
|
|
@@ -6702,6 +7740,9 @@ queryMap, config) => {
|
|
|
6702
7740
|
addQueryContainerHandling(subject.pipe(map((s) => s?.query ?? null)), () => subject.getValue()?.query ?? null, config);
|
|
6703
7741
|
return subject;
|
|
6704
7742
|
};
|
|
7743
|
+
/**
|
|
7744
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7745
|
+
*/
|
|
6705
7746
|
const createQueryCollectionSignal = (
|
|
6706
7747
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
6707
7748
|
queryMap, config) => {
|
|
@@ -6711,7 +7752,13 @@ queryMap, config) => {
|
|
|
6711
7752
|
addQueryContainerHandling(toObservable(_signal).pipe(map((s) => s?.query ?? null)), () => _signal()?.query ?? null, config);
|
|
6712
7753
|
return _signal;
|
|
6713
7754
|
};
|
|
7755
|
+
/**
|
|
7756
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7757
|
+
*/
|
|
6714
7758
|
const extractQuery = (v) => (isQuery(v) ? v : isLegacyQuery(v) ? v : v?.query) ?? null;
|
|
7759
|
+
/**
|
|
7760
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7761
|
+
*/
|
|
6715
7762
|
const getDefaultHeaders = (headers, method) => {
|
|
6716
7763
|
if (!headers) {
|
|
6717
7764
|
return {};
|
|
@@ -6721,6 +7768,9 @@ const getDefaultHeaders = (headers, method) => {
|
|
|
6721
7768
|
}
|
|
6722
7769
|
return headers;
|
|
6723
7770
|
};
|
|
7771
|
+
/**
|
|
7772
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7773
|
+
*/
|
|
6724
7774
|
const castQueryCreatorTypes = (config) => {
|
|
6725
7775
|
if (config.args?.pathParams) {
|
|
6726
7776
|
console.error(config);
|
|
@@ -6728,6 +7778,9 @@ const castQueryCreatorTypes = (config) => {
|
|
|
6728
7778
|
}
|
|
6729
7779
|
return config.creator;
|
|
6730
7780
|
};
|
|
7781
|
+
/**
|
|
7782
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7783
|
+
*/
|
|
6731
7784
|
const computeQueryMethod = (config) => {
|
|
6732
7785
|
let method;
|
|
6733
7786
|
if (isGqlQueryConfig(config.config)) {
|
|
@@ -6739,6 +7792,9 @@ const computeQueryMethod = (config) => {
|
|
|
6739
7792
|
}
|
|
6740
7793
|
return method;
|
|
6741
7794
|
};
|
|
7795
|
+
/**
|
|
7796
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7797
|
+
*/
|
|
6742
7798
|
const computeQueryBody = (config) => {
|
|
6743
7799
|
let body;
|
|
6744
7800
|
if (config.method === 'GET' ||
|
|
@@ -6759,6 +7815,9 @@ const computeQueryBody = (config) => {
|
|
|
6759
7815
|
}
|
|
6760
7816
|
return body;
|
|
6761
7817
|
};
|
|
7818
|
+
/**
|
|
7819
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7820
|
+
*/
|
|
6762
7821
|
const computeQueryAuthHeader = (config) => {
|
|
6763
7822
|
let authHeader = null;
|
|
6764
7823
|
if (config.config.secure) {
|
|
@@ -6774,11 +7833,17 @@ const computeQueryAuthHeader = (config) => {
|
|
|
6774
7833
|
}
|
|
6775
7834
|
return authHeader;
|
|
6776
7835
|
};
|
|
7836
|
+
/**
|
|
7837
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7838
|
+
*/
|
|
6777
7839
|
const computeQueryHeaders = (config) => {
|
|
6778
7840
|
const authHeader = computeQueryAuthHeader(config);
|
|
6779
7841
|
const mergedHeaders = mergeHeaders(getDefaultHeaders(config.client.config.request?.headers, config.config.method), authHeader, config.args?.headers) || undefined;
|
|
6780
7842
|
return mergedHeaders;
|
|
6781
7843
|
};
|
|
7844
|
+
/**
|
|
7845
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7846
|
+
*/
|
|
6782
7847
|
const computeQueryQueryParams = (config) => {
|
|
6783
7848
|
if (isGqlQueryConfig(config.config) &&
|
|
6784
7849
|
(config.config.transferVia === 'GET' ||
|
|
@@ -6793,6 +7858,9 @@ const computeQueryQueryParams = (config) => {
|
|
|
6793
7858
|
}
|
|
6794
7859
|
return config.args?.queryParams;
|
|
6795
7860
|
};
|
|
7861
|
+
/**
|
|
7862
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7863
|
+
*/
|
|
6796
7864
|
const paginatedEntityValueUpdater = (findFn) => ({ response, rawResponse, entity }) => {
|
|
6797
7865
|
const indexOfEntityRaw = rawResponse.items.findIndex((item) => findFn(item, entity));
|
|
6798
7866
|
const indexOfEntity = response.items.findIndex((item) => findFn(item, entity));
|
|
@@ -6818,6 +7886,9 @@ const paginatedEntityValueUpdater = (findFn) => ({ response, rawResponse, entity
|
|
|
6818
7886
|
};
|
|
6819
7887
|
|
|
6820
7888
|
let _nextQueryId = 0;
|
|
7889
|
+
/**
|
|
7890
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
7891
|
+
*/
|
|
6821
7892
|
class V2Query {
|
|
6822
7893
|
constructor(_client,
|
|
6823
7894
|
/**
|
|
@@ -7249,10 +8320,22 @@ class V2Query {
|
|
|
7249
8320
|
}
|
|
7250
8321
|
}
|
|
7251
8322
|
|
|
8323
|
+
/**
|
|
8324
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8325
|
+
*/
|
|
7252
8326
|
const isBasicAuthProvider = (authProvider) => authProvider instanceof BasicAuthProvider;
|
|
8327
|
+
/**
|
|
8328
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8329
|
+
*/
|
|
7253
8330
|
const isBearerAuthProvider = (authProvider) => authProvider instanceof V2BearerAuthProvider;
|
|
8331
|
+
/**
|
|
8332
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8333
|
+
*/
|
|
7254
8334
|
const isCustomHeaderAuthProvider = (authProvider) => authProvider instanceof CustomHeaderAuthProvider;
|
|
7255
8335
|
|
|
8336
|
+
/**
|
|
8337
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8338
|
+
*/
|
|
7256
8339
|
class V2BearerAuthProvider {
|
|
7257
8340
|
constructor(_config) {
|
|
7258
8341
|
this._config = _config;
|
|
@@ -7431,9 +8514,18 @@ const INFINITY_QUERY_RESPONSE_DELAY_DEF = /* @__PURE__ */ defineProvider(() => {
|
|
|
7431
8514
|
enabled$,
|
|
7432
8515
|
};
|
|
7433
8516
|
}, { name: 'Infinity Query Response Delay' });
|
|
8517
|
+
/**
|
|
8518
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8519
|
+
*/
|
|
7434
8520
|
const provideInfinityQueryResponseDelay = /* @__PURE__ */ toProvideFn(INFINITY_QUERY_RESPONSE_DELAY_DEF);
|
|
8521
|
+
/**
|
|
8522
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8523
|
+
*/
|
|
7435
8524
|
const injectInfinityQueryResponseDelay = /* @__PURE__ */ toInjectFn(INFINITY_QUERY_RESPONSE_DELAY_DEF);
|
|
7436
8525
|
|
|
8526
|
+
/**
|
|
8527
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8528
|
+
*/
|
|
7437
8529
|
class InfinityQuery {
|
|
7438
8530
|
constructor(_config, _destroy$) {
|
|
7439
8531
|
this._config = _config;
|
|
@@ -7604,11 +8696,23 @@ class InfinityQuery {
|
|
|
7604
8696
|
}
|
|
7605
8697
|
}
|
|
7606
8698
|
|
|
8699
|
+
/**
|
|
8700
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8701
|
+
*/
|
|
7607
8702
|
const createInfinityQueryConfig = (config) => config;
|
|
8703
|
+
/**
|
|
8704
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8705
|
+
*/
|
|
7608
8706
|
const skipPaginationPageParamCalculator = ({ page, itemsPerPage }) => itemsPerPage * (page - 1);
|
|
7609
8707
|
|
|
7610
8708
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
8709
|
+
/**
|
|
8710
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8711
|
+
*/
|
|
7611
8712
|
const INFINITY_QUERY_TOKEN = new InjectionToken('INFINITY_QUERY_TOKEN');
|
|
8713
|
+
/**
|
|
8714
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8715
|
+
*/
|
|
7612
8716
|
class InfinityQueryDirective {
|
|
7613
8717
|
// TODO: Skipped for migration because:
|
|
7614
8718
|
// Accessor inputs cannot be migrated as they are too complex.
|
|
@@ -7757,6 +8861,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
7757
8861
|
args: ['etInfinityQuery']
|
|
7758
8862
|
}] } });
|
|
7759
8863
|
|
|
8864
|
+
/**
|
|
8865
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8866
|
+
*/
|
|
7760
8867
|
class InfinityQueryTriggerDirective {
|
|
7761
8868
|
constructor() {
|
|
7762
8869
|
this.elementRef = inject(ElementRef);
|
|
@@ -7812,6 +8919,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
7812
8919
|
}]
|
|
7813
8920
|
}], propDecorators: { scrollContainerSelector: [{ type: i0.Input, args: [{ isSignal: true, alias: "scrollContainerSelector", required: false }] }], infinityQueryOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "infinityQueryOverride", required: false }] }] } });
|
|
7814
8921
|
|
|
8922
|
+
/**
|
|
8923
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8924
|
+
*/
|
|
7815
8925
|
class QueryDirective {
|
|
7816
8926
|
static ngTemplateContextGuard(_dir, _ctx) {
|
|
7817
8927
|
return true;
|
|
@@ -7901,6 +9011,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
7901
9011
|
}]
|
|
7902
9012
|
}], ctorParameters: () => [], propDecorators: { query: [{ type: i0.Input, args: [{ isSignal: true, alias: "etQuery", required: true }] }], cache: [{ type: i0.Input, args: [{ isSignal: true, alias: "etQueryCache", required: false }] }] } });
|
|
7903
9013
|
|
|
9014
|
+
/**
|
|
9015
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9016
|
+
*/
|
|
7904
9017
|
class QueryShortNamePipe {
|
|
7905
9018
|
transform(value) {
|
|
7906
9019
|
const route = value._routeWithParams;
|
|
@@ -7923,7 +9036,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
7923
9036
|
args: [{ name: 'queryShortName' }]
|
|
7924
9037
|
}] });
|
|
7925
9038
|
|
|
9039
|
+
/**
|
|
9040
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9041
|
+
*/
|
|
7926
9042
|
const QUERY_CLIENT_DEVTOOLS_TOKEN = new InjectionToken('QUERY_CLIENT_DEVTOOLS_TOKEN');
|
|
9043
|
+
/**
|
|
9044
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9045
|
+
*/
|
|
7927
9046
|
const provideQueryClientForDevtools = (queryClient) => {
|
|
7928
9047
|
if (!isDevMode()) {
|
|
7929
9048
|
console.warn('You are using the Query Devtools in production mode. This increases the size of your bundle and should only be used for development purposes.');
|
|
@@ -7935,6 +9054,9 @@ const provideQueryClientForDevtools = (queryClient) => {
|
|
|
7935
9054
|
};
|
|
7936
9055
|
};
|
|
7937
9056
|
|
|
9057
|
+
/**
|
|
9058
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9059
|
+
*/
|
|
7938
9060
|
class QueryDevtoolsComponent {
|
|
7939
9061
|
constructor() {
|
|
7940
9062
|
this.cdr = inject(ChangeDetectorRef);
|
|
@@ -8244,6 +9366,9 @@ class EntityStoreError extends Error {
|
|
|
8244
9366
|
function flatten(arr) {
|
|
8245
9367
|
return arr.reduce((acc, val) => acc.concat(val), []);
|
|
8246
9368
|
}
|
|
9369
|
+
/**
|
|
9370
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9371
|
+
*/
|
|
8247
9372
|
class EntityStore {
|
|
8248
9373
|
constructor(_config) {
|
|
8249
9374
|
this._config = _config;
|
|
@@ -8360,12 +9485,17 @@ class EntityStore {
|
|
|
8360
9485
|
}
|
|
8361
9486
|
}
|
|
8362
9487
|
|
|
9488
|
+
/**
|
|
9489
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9490
|
+
*/
|
|
8363
9491
|
function mapToPaginated(response) {
|
|
8364
9492
|
return map((items) => ({ ...response, items }));
|
|
8365
9493
|
}
|
|
8366
9494
|
// FIXME: This breaks if SubStoreType is an array
|
|
8367
9495
|
/**
|
|
8368
9496
|
* Use an other store to represent a property of the current entity.
|
|
9497
|
+
*
|
|
9498
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8369
9499
|
*/
|
|
8370
9500
|
const insertFrom = (store, { for: key, id }) => switchMap((value) => {
|
|
8371
9501
|
if (!value)
|
|
@@ -8380,6 +9510,8 @@ const insertFrom = (store, { for: key, id }) => switchMap((value) => {
|
|
|
8380
9510
|
});
|
|
8381
9511
|
/**
|
|
8382
9512
|
* Remove entities from a store based on a where function.
|
|
9513
|
+
*
|
|
9514
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8383
9515
|
*/
|
|
8384
9516
|
const removeFrom = (store, { where, id }) => {
|
|
8385
9517
|
store
|
|
@@ -8388,9 +9520,21 @@ const removeFrom = (store, { where, id }) => {
|
|
|
8388
9520
|
.subscribe();
|
|
8389
9521
|
};
|
|
8390
9522
|
|
|
9523
|
+
/**
|
|
9524
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9525
|
+
*/
|
|
8391
9526
|
const queryStateAlreadyHasKeyError = (data) => new QueryError('004', 'The query state already contains the provided key', data);
|
|
9527
|
+
/**
|
|
9528
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9529
|
+
*/
|
|
8392
9530
|
const queryStateDoesNotContainKeyError = (data) => new QueryError('005', 'The query state does not contain the provided key', data);
|
|
9531
|
+
/**
|
|
9532
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9533
|
+
*/
|
|
8393
9534
|
const queryStateCannotTransformError = (data) => new QueryError('006', 'The query state cannot be transformed because it is not loading', data);
|
|
9535
|
+
/**
|
|
9536
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
9537
|
+
*/
|
|
8394
9538
|
const invalidBodyError = (data) => new QueryError('007', 'The body is not valid or upsupported', data);
|
|
8395
9539
|
|
|
8396
9540
|
/**
|
|
@@ -8415,6 +9559,8 @@ const invalidBodyError = (data) => new QueryError('007', 'The body is not valid
|
|
|
8415
9559
|
* });
|
|
8416
9560
|
* });
|
|
8417
9561
|
* ```
|
|
9562
|
+
*
|
|
9563
|
+
* @deprecated Part of the legacy (v2) query system. Migrate to the current query API - see https://ethlete-sdk-docs.web.app/query/migrating-from-v2, and run `nx g @ethlete/query:migrate-to-query-v3` to rewrite the mechanical parts. Intent to remove in v7.
|
|
8418
9564
|
*/
|
|
8419
9565
|
const validateWithV2Query = (path, config) => {
|
|
8420
9566
|
applyQueryAsyncValidator(path, {
|
|
@@ -9556,6 +10702,16 @@ const createBranch = (fields, initial, injector) => {
|
|
|
9556
10702
|
resetAllFieldsToDefault: () => model.set(clone(defaults)),
|
|
9557
10703
|
};
|
|
9558
10704
|
};
|
|
10705
|
+
/**
|
|
10706
|
+
* Per-name sequence behind the devtools id of a form, mirroring the registry's own scheme so the
|
|
10707
|
+
* panel can restore the selected form after a reload.
|
|
10708
|
+
*/
|
|
10709
|
+
const devtoolsIdCounters = /* @__PURE__ */ new Map();
|
|
10710
|
+
const nextQueryFormDevtoolsId = (name) => {
|
|
10711
|
+
const seq = devtoolsIdCounters.get(name) ?? 0;
|
|
10712
|
+
devtoolsIdCounters.set(name, seq + 1);
|
|
10713
|
+
return `query-form|${name}#${seq}`;
|
|
10714
|
+
};
|
|
9559
10715
|
/**
|
|
9560
10716
|
* A signals-first query form: binds on-screen controls to typed fields, keeps a
|
|
9561
10717
|
* debounced committed value, syncs to the URL query params, and resets dependent
|
|
@@ -9582,6 +10738,8 @@ class QueryFormSignals {
|
|
|
9582
10738
|
this.observing = signal(false, /* @ts-ignore */
|
|
9583
10739
|
...(ngDevMode ? [{ debugName: "observing" }] : /* istanbul ignore next */ []));
|
|
9584
10740
|
this.pendingTimer = null;
|
|
10741
|
+
this.commitPending = signal(false, /* @ts-ignore */
|
|
10742
|
+
...(ngDevMode ? [{ debugName: "commitPending" }] : /* istanbul ignore next */ []));
|
|
9585
10743
|
this.skipNextResets = false;
|
|
9586
10744
|
assertInInjectionContext(QueryFormSignals);
|
|
9587
10745
|
this._fields = config.fields;
|
|
@@ -9594,7 +10752,16 @@ class QueryFormSignals {
|
|
|
9594
10752
|
this.committed = signal(clone(initial), /* @ts-ignore */
|
|
9595
10753
|
...(ngDevMode ? [{ debugName: "committed" }] : /* istanbul ignore next */ []));
|
|
9596
10754
|
this.fields = form(this.model);
|
|
9597
|
-
this.
|
|
10755
|
+
const devtoolsName = config.name ?? (typeof this.prefix === 'string' ? this.prefix : 'form');
|
|
10756
|
+
const devtoolsId = isQueryDevtoolsEnabled() ? nextQueryFormDevtoolsId(devtoolsName) : null;
|
|
10757
|
+
// Reading `value` is how a query's args pick the form up, so the devtools learn which query a form
|
|
10758
|
+
// drives by noting the read - see `QueryDevtoolsFormLinksHandle`.
|
|
10759
|
+
this.value = devtoolsId
|
|
10760
|
+
? computed(() => {
|
|
10761
|
+
noteQueryFormRead(devtoolsId);
|
|
10762
|
+
return this.committed();
|
|
10763
|
+
})
|
|
10764
|
+
: this.committed.asReadonly();
|
|
9598
10765
|
this.previousValue = this.previous.asReadonly();
|
|
9599
10766
|
this.changes = computed(() => ({ previousValue: this.previous(), currentValue: this.committed() }), /* @ts-ignore */
|
|
9600
10767
|
...(ngDevMode ? [{ debugName: "changes" }] : /* istanbul ignore next */ []));
|
|
@@ -9614,7 +10781,16 @@ class QueryFormSignals {
|
|
|
9614
10781
|
this.applyFromUrl(changes);
|
|
9615
10782
|
});
|
|
9616
10783
|
});
|
|
9617
|
-
inject(DestroyRef)
|
|
10784
|
+
const destroyRef = inject(DestroyRef);
|
|
10785
|
+
destroyRef.onDestroy(() => this.cleanup());
|
|
10786
|
+
if (devtoolsId) {
|
|
10787
|
+
destroyRef.onDestroy(registerQueryDevtoolsEntry({
|
|
10788
|
+
id: devtoolsId,
|
|
10789
|
+
kind: 'query-form',
|
|
10790
|
+
handle: this.devtoolsHandle(),
|
|
10791
|
+
meta: { name: devtoolsName },
|
|
10792
|
+
}));
|
|
10793
|
+
}
|
|
9618
10794
|
}
|
|
9619
10795
|
observe(options) {
|
|
9620
10796
|
if (this.observing()) {
|
|
@@ -9670,6 +10846,46 @@ class QueryFormSignals {
|
|
|
9670
10846
|
branch() {
|
|
9671
10847
|
return createBranch(this._fields, clone(this.committed()), this.injector);
|
|
9672
10848
|
}
|
|
10849
|
+
/**
|
|
10850
|
+
* The live view `<et-query-devtools>` renders. Only built while the devtools are installed - it walks
|
|
10851
|
+
* every field on every change, which an app without them should not pay for.
|
|
10852
|
+
*/
|
|
10853
|
+
devtoolsHandle() {
|
|
10854
|
+
const fields = computed(() => {
|
|
10855
|
+
const committed = this.committed();
|
|
10856
|
+
const live = this.model();
|
|
10857
|
+
return Object.entries(this._fields).map(([key, def]) => {
|
|
10858
|
+
const value = committed[key];
|
|
10859
|
+
return {
|
|
10860
|
+
key,
|
|
10861
|
+
paramKey: this.paramKey(key),
|
|
10862
|
+
value,
|
|
10863
|
+
liveValue: live[key],
|
|
10864
|
+
defaultValue: this.defaults[key],
|
|
10865
|
+
isDefault: equal(value, this.defaults[key]),
|
|
10866
|
+
queryParam: this.queryParamFor(key, def, value),
|
|
10867
|
+
debounceMs: def.debounce ?? null,
|
|
10868
|
+
isResetBy: def.isResetBy ?? [],
|
|
10869
|
+
countsAsFilter: !IGNORED_FILTER_COUNT_FIELDS.includes(key) && !def.skipInFilterCount,
|
|
10870
|
+
};
|
|
10871
|
+
});
|
|
10872
|
+
}, /* @ts-ignore */
|
|
10873
|
+
...(ngDevMode ? [{ debugName: "fields" }] : /* istanbul ignore next */ []));
|
|
10874
|
+
return {
|
|
10875
|
+
fields,
|
|
10876
|
+
// Not `this.value` - reading that from the panel would link the form to whichever query happens
|
|
10877
|
+
// to be building its args at the time.
|
|
10878
|
+
value: computed(() => this.committed()),
|
|
10879
|
+
previousValue: computed(() => this.previous() ?? null),
|
|
10880
|
+
defaultValue: this.defaults,
|
|
10881
|
+
activeFilterCount: this.activeFilterCount,
|
|
10882
|
+
isAtDefaults: computed(() => equal(this.committed(), this.defaults)),
|
|
10883
|
+
isCommitPending: this.commitPending.asReadonly(),
|
|
10884
|
+
isObserving: this.observing.asReadonly(),
|
|
10885
|
+
resetField: (key) => this.resetFieldToDefault(key),
|
|
10886
|
+
resetAll: () => this.resetAllFieldsToDefault(),
|
|
10887
|
+
};
|
|
10888
|
+
}
|
|
9673
10889
|
onLiveChange(live) {
|
|
9674
10890
|
if (!this.observing())
|
|
9675
10891
|
return;
|
|
@@ -9682,6 +10898,7 @@ class QueryFormSignals {
|
|
|
9682
10898
|
this.flush();
|
|
9683
10899
|
}
|
|
9684
10900
|
else {
|
|
10901
|
+
this.commitPending.set(true);
|
|
9685
10902
|
this.pendingTimer = setTimeout(() => {
|
|
9686
10903
|
this.pendingTimer = null;
|
|
9687
10904
|
this.flush();
|
|
@@ -9782,23 +10999,21 @@ class QueryFormSignals {
|
|
|
9782
10999
|
return raw;
|
|
9783
11000
|
return autoCoerce(raw, resolveDefault(def));
|
|
9784
11001
|
}
|
|
11002
|
+
/** What a field puts in the URL for a given value, or `undefined` when it writes nothing. */
|
|
11003
|
+
queryParamFor(key, def, value) {
|
|
11004
|
+
const isDefault = equal(value, this.defaults[key]);
|
|
11005
|
+
const writeToUrl = def.appendToUrl !== false;
|
|
11006
|
+
const writeDefault = def.appendDefaultValueToUrl === true;
|
|
11007
|
+
if (!writeToUrl || (isDefault && !writeDefault))
|
|
11008
|
+
return undefined;
|
|
11009
|
+
if (def.valueToQueryParam)
|
|
11010
|
+
return def.valueToQueryParam(value);
|
|
11011
|
+
return value === null ? ET_NULL_VALUE : value;
|
|
11012
|
+
}
|
|
9785
11013
|
writeToUrl(value) {
|
|
9786
11014
|
const queryParams = {};
|
|
9787
11015
|
for (const [key, def] of Object.entries(this._fields)) {
|
|
9788
|
-
|
|
9789
|
-
const fieldValue = value[key];
|
|
9790
|
-
const isDefault = equal(fieldValue, this.defaults[key]);
|
|
9791
|
-
const writeToUrl = def.appendToUrl !== false;
|
|
9792
|
-
const writeDefault = def.appendDefaultValueToUrl === true;
|
|
9793
|
-
if (!writeToUrl || (isDefault && !writeDefault)) {
|
|
9794
|
-
queryParams[paramKey] = undefined;
|
|
9795
|
-
}
|
|
9796
|
-
else if (def.valueToQueryParam) {
|
|
9797
|
-
queryParams[paramKey] = def.valueToQueryParam(fieldValue);
|
|
9798
|
-
}
|
|
9799
|
-
else {
|
|
9800
|
-
queryParams[paramKey] = fieldValue === null ? ET_NULL_VALUE : fieldValue;
|
|
9801
|
-
}
|
|
11016
|
+
queryParams[this.paramKey(key)] = this.queryParamFor(key, def, value[key]);
|
|
9802
11017
|
}
|
|
9803
11018
|
queueMicrotask(() => {
|
|
9804
11019
|
this.router.navigate([], {
|
|
@@ -9819,6 +11034,7 @@ class QueryFormSignals {
|
|
|
9819
11034
|
clearTimeout(this.pendingTimer);
|
|
9820
11035
|
this.pendingTimer = null;
|
|
9821
11036
|
}
|
|
11037
|
+
this.commitPending.set(false);
|
|
9822
11038
|
}
|
|
9823
11039
|
cleanup() {
|
|
9824
11040
|
if (!this.observing())
|
|
@@ -10071,5 +11287,5 @@ const createWebSocketClient = (options) => {
|
|
|
10071
11287
|
* Generated bundle index. Do not edit.
|
|
10072
11288
|
*/
|
|
10073
11289
|
|
|
10074
|
-
export { BasicAuthProvider, BearerAuthFeatureType, BooleanArrayQueryField, CLEAR_QUERY_ARGS, 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, EntityStore, IGNORED_FILTER_COUNT_FIELDS, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, LegacyQuery, LegacyQueryCreator, MAX_UNUSED_ENTRIES, NumberArrayQueryField, ParseHttpErrorCodeToMessageDePipe, ParseHttpErrorCodeToMessageEnPipe, ParseHttpErrorCodeToTitleDePipe, ParseHttpErrorCodeToTitleEnPipe, QUERY_CLIENT_DEVTOOLS_TOKEN, QUERY_PERSISTENCE_STORE_VERSION, QUERY_SYNC_PROTOCOL_VERSION, QueryClientFeatureType, QueryDevtoolsComponent, QueryDirective, QueryError, QueryFeatureType, QueryField, QueryForm, QueryFormSignals, QueryRuntimeErrorCode, QueryShortNamePipe, QueryStore, SERVER_ERROR_KIND, SERVER_VIOLATION_ERROR_KIND, SPEED_BUFFER_TIME_IN_MS, SearchQueryField, SortQueryField, StringArrayQueryField, V2BearerAuthProvider, V2Query, V2QueryClient, V2QueryCreator, addQueryContainerHandling, applyQueryFeatures, authExtractTokensResponseMissingAccessToken, authExtractTokensResponseMissingRefreshToken, authExtractTokensResponseNotObject, authProviderFeatureUsedMultipleTimes, booleanArrayQueryField, buildErrorMessage, buildQueryCacheKey, buildQueryString, buildRoute, buildTimestampFromSeconds, castQueryCreatorTypes, circularQueryDependency, circularQueryDependencyChecker, computeQueryAuthHeader, computeQueryBody, computeQueryHeaders, computeQueryMethod, computeQueryQueryParams, consumeSuppressQueryStackDevtools, contentfulGqlLikePaginationAdapter, createBaseQuery, createBaseQueryCreator, createBearerAuthProvider, createDeleteQuery, createExecuteFn, createGetQuery, createGqlExecuteFn, createGqlMutationViaGet, createGqlMutationViaPost, createGqlQuery, createGqlQueryCreator, createGqlQueryViaGet, createGqlQueryViaPost, createHttpRequest, createIndexedDbQueryPersistenceAdapter, createInfinityQueryConfig, createLegacyQueryCreator, createNoopQueryPersistenceAdapter, createPagedQueryStack, createPatchQuery, createPersistentAuthFeature, createPostQuery, createPutQuery, createQuery, createQueryClient, createQueryCollection, createQueryCollectionSignal, createQueryCollectionSubject, createQueryContext, createQueryCreator, createQueryErrorResponse, createQueryFeature, createQueryForm, createQueryInvalidationFilter, createQueryKeyLockManager, createQueryObject, createQueryPersistenceEngine, createQueryRepository, createQuerySnapshotFn, createQueryStack, createQuerySyncEngine, createQuerySyncTransport, createSecureDeleteQuery, createSecureExecuteFactory, createSecureExecuteFn, createSecureGetQuery, createSecureGqlExecuteFn, createSecureGqlMutationViaGet, createSecureGqlMutationViaPost, createSecureGqlQuery, createSecureGqlQueryCreator, createSecureGqlQueryViaGet, createSecureGqlQueryViaPost, createSecurePatchQuery, createSecurePostQuery, createSecurePutQuery, createSecureQuery, createSecureQueryCreator, createTrackingFeature, createWebSocketClient, dateArrayQueryField, dateQueryField, decryptBearer, deepFreeze, def, detectContentTypeHeader, dynLikePaginationAdapter, effectComputed, emptyQueryArgs, ethletePaginationAdapter, executeUntilSettled, extractExpiresInSeconds, extractFormViolations, extractHtmlErrorMessage, extractQuery, fakePaginationAdapter, filterFailure, filterNull, filterQueryStates, filterSuccess, forEachHeader, getDefaultHeaders, getQueryClientName, getQueryFeatureUsage, getResponseUrl, ggLikePaginationAdapter, gql, gqlDataPropertyMissingInResponse, hasHeader, htmlErrorPayload, htmlQueryErrorParser, ignoreAutoRefresh, injectInfinityQueryResponseDelay, injectQueryContext, insertFrom, invalidBaseRouteError, invalidBodyError, invalidRouteError, invalidStateInsideSecureExecuteFactory, isAutoRefresh, isBasicAuthProvider, isBearerAuthProvider, isClassValidatorError, isCreateGqlQueryOptions, isCustomHeaderAuthProvider, isEmptyString, isGqlQueryConfig, isHtmlErrorPayload, isLegacyQuery, isNaN$1 as isNaN, isPageOutOfRangeError, isQuery, isQueryCollection, isQueryDevtoolsEnabled, isQueryErrorResponse, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, isSymfonyError, isSymfonyFormViolationListError, isSymfonyListError, isSymfonyPagerfantaOutOfRangeError, isUnderInvalidatedUrl, legacyPrepareWithoutInjectionContext, mapToPaginated, mapViolationsToFormErrors, maybeExecute, mergeHeaders, nestedEffect, numberArrayQueryField, pagedQueryStackNextPageCalledWithoutPreviousPage, pagedQueryStackPageBiggerThanTotalPages, pagedQueryStackPreviousPageCalledButAlreadyAtFirstPage, paginatedEntityValueUpdater, parseAllXhrResponseHeaders, parseHttpErrorCodeToMessageDe, parseHttpErrorCodeToMessageEn, parseHttpErrorCodeToTitleDe, parseHttpErrorCodeToTitleEn, pathParamsMissingInRouteFunctionError, provideInfinityQueryResponseDelay, provideQueryClientForDevtools, provideQueryDevtools, queryArrayComputed, queryClientFeatureUsedMultipleTimes, queryComputed, queryComputedTillTruthy, queryDevtoolsEntries, queryErrorMessage, queryErrorMessages, queryExecute, queryFeatureUsedMultipleTimes, queryField, querySequence, querySequenceAlreadyRunning, queryStackTotalQueriesAndExpectedQueriesMismatch, queryStackWithArgsUsed, queryStackWithResponseUpdateUsed, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, queryStateErrorSignal, queryStateLoadingSignal, queryStateResponseSignal, queryStateSignal, registerQueryDevtoolsEntry, registerQueryErrorParser, removeFrom, request, resetExecuteState, resetPageOnError, resolveInvalidationUrl, runDefaultQueryRetry, runQueryErrorParsers, searchQueryField, serializeBody, setDefaultQueryRetryFn, setQueryDevtoolsRegistrar, setupQueryDependencies, setupQueryExecuteState, setupQueryState, shouldAutoExecuteGqlQuery, shouldAutoExecuteQuery, shouldCacheQuery, shouldRetryRequest, silenceMissingWithArgsFeatureErrorUsedButWithArgsPresent, skipPaginationPageParamCalculator, sortQueryField, splitQueryConfig, stringArrayQueryField,
|
|
11290
|
+
export { BasicAuthProvider, BearerAuthFeatureType, BooleanArrayQueryField, CLEAR_QUERY_ARGS, 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_UNUSED_ENTRIES, NumberArrayQueryField, ParseHttpErrorCodeToMessageDePipe, ParseHttpErrorCodeToMessageEnPipe, ParseHttpErrorCodeToTitleDePipe, ParseHttpErrorCodeToTitleEnPipe, QUERY_CLIENT_DEVTOOLS_TOKEN, QUERY_DEVTOOLS_FAULT_STATUSES, QUERY_PERSISTENCE_STORE_VERSION, QUERY_SYNC_PROTOCOL_VERSION, QueryClientFeatureType, QueryDevtoolsComponent, QueryDirective, QueryError, QueryFeatureType, QueryField, QueryForm, QueryFormSignals, QueryRuntimeErrorCode, QueryShortNamePipe, QueryStore, SERVER_ERROR_KIND, SERVER_VIOLATION_ERROR_KIND, SPEED_BUFFER_TIME_IN_MS, SearchQueryField, SortQueryField, StringArrayQueryField, V2BearerAuthProvider, V2Query, V2QueryClient, V2QueryCreator, addQueryContainerHandling, applyQueryFeatures, authExtractTokensResponseMissingAccessToken, authExtractTokensResponseMissingRefreshToken, authExtractTokensResponseNotObject, authProviderFeatureUsedMultipleTimes, booleanArrayQueryField, buildErrorMessage, buildQueryCacheKey, buildQueryString, buildRoute, buildTimestampFromSeconds, castQueryCreatorTypes, circularQueryDependency, circularQueryDependencyChecker, clearQueryDevtoolsFaults, collectQueryFormLinks, computeQueryAuthHeader, computeQueryBody, computeQueryHeaders, computeQueryMethod, computeQueryQueryParams, consumeSuppressQueryStackDevtools, contentfulGqlLikePaginationAdapter, createBaseQuery, createBaseQueryCreator, createBearerAuthProvider, createDeleteQuery, createExecuteFn, createGetQuery, createGqlExecuteFn, createGqlMutationViaGet, createGqlMutationViaPost, createGqlQuery, createGqlQueryCreator, createGqlQueryViaGet, createGqlQueryViaPost, createHttpRequest, createIndexedDbQueryPersistenceAdapter, createInfinityQueryConfig, createLegacyQueryCreator, createNoopQueryPersistenceAdapter, createPagedQueryStack, createPatchQuery, createPersistentAuthFeature, createPostQuery, createPutQuery, createQuery, createQueryClient, createQueryCollection, createQueryCollectionSignal, createQueryCollectionSubject, createQueryContext, createQueryCreator, createQueryDevtoolsFormLinks, createQueryDevtoolsFormLinksRecorder, createQueryDevtoolsStats, createQueryDevtoolsStatsRecorder, createQueryErrorResponse, createQueryFeature, createQueryForm, createQueryInvalidationFilter, createQueryKeyLockManager, createQueryObject, createQueryPersistenceEngine, createQueryRepository, createQuerySnapshotFn, createQueryStack, createQuerySyncEngine, createQuerySyncTransport, createSecureDeleteQuery, createSecureExecuteFactory, createSecureExecuteFn, createSecureGetQuery, createSecureGqlExecuteFn, createSecureGqlMutationViaGet, createSecureGqlMutationViaPost, createSecureGqlQuery, createSecureGqlQueryCreator, createSecureGqlQueryViaGet, createSecureGqlQueryViaPost, createSecurePatchQuery, createSecurePostQuery, createSecurePutQuery, createSecureQuery, createSecureQueryCreator, createTrackingFeature, createWebSocketClient, dateArrayQueryField, dateQueryField, decryptBearer, deepFreeze, def, describeQueryDevtoolsFeatures, detectContentTypeHeader, dynLikePaginationAdapter, effectComputed, emptyQueryArgs, ethletePaginationAdapter, executeUntilSettled, extractExpiresInSeconds, extractFormViolations, extractHtmlErrorMessage, extractQuery, fakePaginationAdapter, filterFailure, filterNull, filterQueryStates, filterSuccess, forEachHeader, formatQueryDevtoolsDuration, getBearerAuthProviderName, getDefaultHeaders, getQueryClientName, getQueryFeatureUsage, getResponseUrl, ggLikePaginationAdapter, gql, gqlDataPropertyMissingInResponse, hasHeader, htmlErrorPayload, htmlQueryErrorParser, ignoreAutoRefresh, injectInfinityQueryResponseDelay, injectQueryContext, insertFrom, invalidBaseRouteError, invalidBodyError, invalidRouteError, invalidStateInsideSecureExecuteFactory, isAutoRefresh, isBasicAuthProvider, isBearerAuthProvider, isClassValidatorError, isCreateGqlQueryOptions, isCustomHeaderAuthProvider, isEmptyString, isGqlQueryConfig, isHtmlErrorPayload, isLegacyQuery, isNaN$1 as isNaN, isPageOutOfRangeError, isQuery, isQueryCollection, isQueryDevtoolsEnabled, isQueryDevtoolsFaultArmed, isQueryDevtoolsFaultInjectionEnabled, isQueryErrorResponse, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, isSymfonyError, isSymfonyFormViolationListError, isSymfonyListError, isSymfonyPagerfantaOutOfRangeError, isUnderInvalidatedUrl, legacyPrepareFallbackInjector, legacyPrepareWithoutInjectionContext, mapToPaginated, mapViolationsToFormErrors, maybeExecute, measureQueryDevtoolsPayload, mergeHeaders, nestedEffect, noteQueryFormRead, numberArrayQueryField, pagedQueryStackNextPageCalledWithoutPreviousPage, pagedQueryStackPageBiggerThanTotalPages, pagedQueryStackPreviousPageCalledButAlreadyAtFirstPage, paginatedEntityValueUpdater, parseAllXhrResponseHeaders, parseHttpErrorCodeToMessageDe, parseHttpErrorCodeToMessageEn, parseHttpErrorCodeToTitleDe, parseHttpErrorCodeToTitleEn, parseQueryRoute, pathParamsMissingInRouteFunctionError, provideInfinityQueryResponseDelay, provideLegacyPrepareFallback, provideQueryClientForDevtools, provideQueryDevtools, queryArrayComputed, queryClientFeatureUsedMultipleTimes, queryComputed, queryComputedTillTruthy, queryDevtoolsEntries, queryDevtoolsFaults, queryDevtoolsFnDetail, queryErrorMessage, queryErrorMessages, queryExecute, queryFeatureUsedMultipleTimes, queryField, querySequence, querySequenceAlreadyRunning, queryStackTotalQueriesAndExpectedQueriesMismatch, queryStackWithArgsUsed, queryStackWithResponseUpdateUsed, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, queryStateErrorSignal, queryStateLoadingSignal, queryStateResponseSignal, queryStateSignal, registerQueryDevtoolsEntry, registerQueryErrorParser, removeFrom, request, resetExecuteState, resetPageOnError, resolveInvalidationUrl, resolveQueryDevtoolsFault, resolveQueryDevtoolsFaultForAttempt, runDefaultQueryRetry, runQueryErrorParsers, searchQueryField, serializeBody, setDefaultQueryRetryFn, setQueryDevtoolsFault, setQueryDevtoolsFaultResolver, setQueryDevtoolsFormLinksFactory, setQueryDevtoolsRegistrar, setQueryDevtoolsStatsFactory, setupQueryDependencies, setupQueryExecuteState, setupQueryState, shouldAutoExecuteGqlQuery, shouldAutoExecuteQuery, shouldCacheQuery, shouldRetryRequest, silenceMissingWithArgsFeatureErrorUsedButWithArgsPresent, skipPaginationPageParamCalculator, sortQueryField, splitQueryConfig, stringArrayQueryField, stringifyQueryRouteParts, sumQueryDevtoolsStats, suppressNextQueryStackDevtools, switchQueryCollectionState, switchQueryState, symfonyQueryErrorParser, takeUntilResponse, toQuerySignal, toQuerySubject, tokensNotAvailableInsideAuthAndExec, 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, withMultiTabSync, withPageResetOnError, withPersistentAuth, withPolling, withPollingUsedOnUnsupportedHttpMethod, withQueryPersistence, withRefreshQuery, withResponseUpdate, withSuccessHandling, withSymfonyErrors, withTokenExpirationWarning, withTokenRevocation, withTracking, wrapAsObservableSignal, wrapQuerySyncMessage };
|
|
10075
11291
|
//# sourceMappingURL=ethlete-query.mjs.map
|