@ethlete/query 6.0.0-next.40 → 6.0.0-next.41
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 +10 -0
- package/fesm2022/ethlete-query.mjs +1013 -18
- package/fesm2022/ethlete-query.mjs.map +1 -1
- package/package.json +1 -1
- package/types/ethlete-query.d.ts +314 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, Injector, effect, DestroyRef, InjectionToken, EnvironmentInjector, ElementRef, ErrorHandler, createEnvironmentInjector, untracked, signal, linkedSignal, computed, runInInjectionContext, isDevMode, PLATFORM_ID, resource, DOCUMENT, VERSION, makeEnvironmentProviders, assertInInjectionContext, provideEnvironmentInitializer, ChangeDetectorRef, ViewContainerRef, TemplateRef, Input, Directive, input, Pipe, ViewEncapsulation, Component, NgZone } from '@angular/core';
|
|
3
3
|
import { UrlTree, Router, RedirectCommand, ActivatedRoute } from '@angular/router';
|
|
4
|
-
import { Observable, defer, from, filter, Subject, Subscription, throwError, of, timer, switchMap, tap, retry, catchError, EMPTY, take, takeUntil, finalize, mergeMap, toArray, map, merge, takeWhile, firstValueFrom, race, concatMap, fromEvent, throttleTime, interval, startWith, pairwise, BehaviorSubject, combineLatest, ReplaySubject, shareReplay, skip, withLatestFrom, debounceTime } from 'rxjs';
|
|
4
|
+
import { Observable, defer, from, filter, Subject, Subscription, throwError, of, timer, switchMap, tap, retry, catchError, EMPTY as EMPTY$1, take, takeUntil, finalize, mergeMap, toArray, map, merge, takeWhile, firstValueFrom, race, concatMap, fromEvent, throttleTime, interval, startWith, pairwise, BehaviorSubject, combineLatest, ReplaySubject, shareReplay, skip, withLatestFrom, debounceTime } from 'rxjs';
|
|
5
5
|
import { defineStaticProvider, RuntimeError, clamp, randomId, defineRootProvider, isObject, injectUnsavedChangesCoordinator, injectRoute, getCookie, deleteCookie, setCookie, CORE_VERSION, createDestroy, computedTillTruthy, defineProvider, toProvideFn, toInjectFn, nextFrame, injectQueryParams, injectQueryParamChanges, clone, equal, ET_PROPERTY_REMOVED, previousSignalValue } from '@ethlete/core';
|
|
6
6
|
import { HttpClient, HttpEventType, HttpErrorResponse, HttpResponse, HttpHeaders, HttpStatusCode } from '@angular/common/http';
|
|
7
7
|
import { toObservable, takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
|
@@ -330,6 +330,36 @@ const consumeSuppressQueryStackDevtools = () => {
|
|
|
330
330
|
suppressStackRegistration = false;
|
|
331
331
|
return value;
|
|
332
332
|
};
|
|
333
|
+
let authProviderRegistrar = null;
|
|
334
|
+
/**
|
|
335
|
+
* Installs the session vault's provider registrar. Called by `provideQueryDevtools()`; nothing else may
|
|
336
|
+
* call it.
|
|
337
|
+
* @internal
|
|
338
|
+
*/
|
|
339
|
+
const setQueryDevtoolsAuthProviderRegistrar = (fn) => {
|
|
340
|
+
authProviderRegistrar = fn;
|
|
341
|
+
};
|
|
342
|
+
/**
|
|
343
|
+
* Hands one auth provider to the devtools' session vault, which then keeps the live session's tokens in
|
|
344
|
+
* step with the stored one. A no-op without `provideQueryDevtools()`.
|
|
345
|
+
* @internal
|
|
346
|
+
*/
|
|
347
|
+
const registerQueryDevtoolsAuthProvider = (registration) => authProviderRegistrar?.(registration) ?? (() => undefined);
|
|
348
|
+
let authSeedReader = null;
|
|
349
|
+
/**
|
|
350
|
+
* Installs the tab-local session reader. Called by `provideQueryDevtools()`; nothing else may call it.
|
|
351
|
+
* @internal
|
|
352
|
+
*/
|
|
353
|
+
const setQueryDevtoolsAuthSeedReader = (fn) => {
|
|
354
|
+
authSeedReader = fn;
|
|
355
|
+
};
|
|
356
|
+
/**
|
|
357
|
+
* The session the panel put in this tab alone, or `null` for a tab on the shared one. Read once while a
|
|
358
|
+
* provider is built, before its features run: the tokens have to be in place before `withPersistentAuth`
|
|
359
|
+
* decides whether to spend its cookie.
|
|
360
|
+
* @internal
|
|
361
|
+
*/
|
|
362
|
+
const readQueryDevtoolsAuthSeed = (providerName) => authSeedReader?.(providerName) ?? null;
|
|
333
363
|
|
|
334
364
|
/**
|
|
335
365
|
* Whether a create-query options object came from the gql layer - only gql's `creatorInternals` carries
|
|
@@ -3102,12 +3132,12 @@ const createQueryBatch = (options) => {
|
|
|
3102
3132
|
const runEntry = (entry) => defer(() => {
|
|
3103
3133
|
if (cancelRequested) {
|
|
3104
3134
|
record({ status: 'cancelled', index: entry.index, item: entry.item });
|
|
3105
|
-
return EMPTY;
|
|
3135
|
+
return EMPTY$1;
|
|
3106
3136
|
}
|
|
3107
3137
|
const args = mapArgs(entry.item, entry.index);
|
|
3108
3138
|
if (args === null) {
|
|
3109
3139
|
record({ status: 'skipped', index: entry.index, item: entry.item });
|
|
3110
|
-
return EMPTY;
|
|
3140
|
+
return EMPTY$1;
|
|
3111
3141
|
}
|
|
3112
3142
|
// The args reach the query through `execute()`, never `withArgs()`, so a function route has to
|
|
3113
3143
|
// be told the missing feature is intentional or creating the query throws ET100.
|
|
@@ -4511,7 +4541,7 @@ const createSecureExecuteFactory = (options) => {
|
|
|
4511
4541
|
injector: options.deps.injector,
|
|
4512
4542
|
}).pipe(switchMap((latestQuery) => {
|
|
4513
4543
|
if (!latestQuery)
|
|
4514
|
-
return EMPTY;
|
|
4544
|
+
return EMPTY$1;
|
|
4515
4545
|
const query = latestQuery.snapshot;
|
|
4516
4546
|
return toObservable(query.isAlive, { injector: options.deps.injector }).pipe(filter((isAlive) => !isAlive), filter(() => !!query.error() || !!query.response()), map(() => query));
|
|
4517
4547
|
}), tap((query) => {
|
|
@@ -5276,6 +5306,16 @@ const createBearerAuthProviderImpl = (config) => {
|
|
|
5276
5306
|
// leaves the tab locked against closing. See `injectUnsavedChangesCoordinator`.
|
|
5277
5307
|
unsavedChanges.abandonAll('logout');
|
|
5278
5308
|
};
|
|
5309
|
+
const isTabLocalSession = signal(false, /* @ts-ignore */
|
|
5310
|
+
...(ngDevMode ? [{ debugName: "isTabLocalSession" }] : /* istanbul ignore next */ []));
|
|
5311
|
+
// Before the features: `withPersistentAuth` starts its cookie auto-login during setup, and a tab that
|
|
5312
|
+
// was handed a session of its own must already hold it by then - both so the auto-login stands down,
|
|
5313
|
+
// and so the sync and the cookie see the flag before they decide whether to run at all.
|
|
5314
|
+
const devtoolsSeed = isQueryDevtoolsEnabled() ? readQueryDevtoolsAuthSeed(config.name) : null;
|
|
5315
|
+
if (devtoolsSeed) {
|
|
5316
|
+
isTabLocalSession.set(true);
|
|
5317
|
+
setTokens(devtoolsSeed.accessToken, devtoolsSeed.refreshToken);
|
|
5318
|
+
}
|
|
5279
5319
|
const isLeader = runEarlyFeatureSetup(config.features, {
|
|
5280
5320
|
accessToken,
|
|
5281
5321
|
refreshToken,
|
|
@@ -5285,6 +5325,7 @@ const createBearerAuthProviderImpl = (config) => {
|
|
|
5285
5325
|
setTokens,
|
|
5286
5326
|
logout,
|
|
5287
5327
|
sessionEndCause: sessionEndCause.asReadonly(),
|
|
5328
|
+
isTabLocalSession: isTabLocalSession.asReadonly(),
|
|
5288
5329
|
});
|
|
5289
5330
|
const querySetupContext = {
|
|
5290
5331
|
name: config.name,
|
|
@@ -5314,6 +5355,7 @@ const createBearerAuthProviderImpl = (config) => {
|
|
|
5314
5355
|
destroyRef,
|
|
5315
5356
|
setTokens,
|
|
5316
5357
|
isLeader: isLeader.isLeaderFn,
|
|
5358
|
+
isTabLocalSession: isTabLocalSession.asReadonly(),
|
|
5317
5359
|
leaderElection: isLeader.leaderElectionContext,
|
|
5318
5360
|
sessionAdoption: isLeader.sessionAdoption,
|
|
5319
5361
|
activityCoordination: isLeader.activityCoordination,
|
|
@@ -5373,6 +5415,13 @@ const createBearerAuthProviderImpl = (config) => {
|
|
|
5373
5415
|
},
|
|
5374
5416
|
});
|
|
5375
5417
|
destroyRef.onDestroy(unregister);
|
|
5418
|
+
destroyRef.onDestroy(registerQueryDevtoolsAuthProvider({
|
|
5419
|
+
name: config.name,
|
|
5420
|
+
handle: provider,
|
|
5421
|
+
client: queryClient,
|
|
5422
|
+
isTabLocalSession,
|
|
5423
|
+
injector,
|
|
5424
|
+
}));
|
|
5376
5425
|
}
|
|
5377
5426
|
return provider;
|
|
5378
5427
|
};
|
|
@@ -5706,7 +5755,7 @@ const withRefreshQuery = (key, config) => {
|
|
|
5706
5755
|
const attempt = executeRefresh('scheduled');
|
|
5707
5756
|
const retryInMs = retryScheduledIn(attempt);
|
|
5708
5757
|
if (retryInMs === null)
|
|
5709
|
-
return EMPTY;
|
|
5758
|
+
return EMPTY$1;
|
|
5710
5759
|
// Waiting on another tab does not spend the budget. Both outcomes come back at a deadline
|
|
5711
5760
|
// rather than on a fixed interval, and both end the moment a token pair arrives, because a
|
|
5712
5761
|
// new token restarts the schedule. Spending the budget on them leaves a follower whose
|
|
@@ -5714,7 +5763,7 @@ const withRefreshQuery = (key, config) => {
|
|
|
5714
5763
|
// from: the tab then holds a dead token and cannot even ask for a new one.
|
|
5715
5764
|
if (attempt === 'notLeader' || attempt === 'delegated')
|
|
5716
5765
|
return scheduledRefresh(retryInMs, attemptsLeft);
|
|
5717
|
-
return attemptsLeft <= 0 ? EMPTY : scheduledRefresh(retryInMs, attemptsLeft - 1);
|
|
5766
|
+
return attemptsLeft <= 0 ? EMPTY$1 : scheduledRefresh(retryInMs, attemptsLeft - 1);
|
|
5718
5767
|
}));
|
|
5719
5768
|
// A backgrounded tab's `timer()` does not fire on time - the browser throttles it, and a frozen
|
|
5720
5769
|
// page stops running it altogether while keeping the Web Lock that makes it the leader. So the
|
|
@@ -5742,7 +5791,7 @@ const withRefreshQuery = (key, config) => {
|
|
|
5742
5791
|
}, /* @ts-ignore */
|
|
5743
5792
|
...(ngDevMode ? [{ debugName: "nextScheduledRefresh" }] : /* istanbul ignore next */ []));
|
|
5744
5793
|
toObservable(nextScheduledRefresh)
|
|
5745
|
-
.pipe(switchMap(({ dueInMs }) => (dueInMs === null ? EMPTY : scheduledRefresh(dueInMs, maxRescheduleAttempts))), takeUntilDestroyed())
|
|
5794
|
+
.pipe(switchMap(({ dueInMs }) => (dueInMs === null ? EMPTY$1 : scheduledRefresh(dueInMs, maxRescheduleAttempts))), takeUntilDestroyed())
|
|
5746
5795
|
.subscribe();
|
|
5747
5796
|
context.refreshCoordination?.requests$.pipe(takeUntilDestroyed()).subscribe(() => executeRefresh('unauthorized'));
|
|
5748
5797
|
context.refreshCoordination?.starts$.pipe(takeUntilDestroyed()).subscribe(() => {
|
|
@@ -6543,7 +6592,10 @@ const withBearerAuthMultiTabSync = (config = {}) => {
|
|
|
6543
6592
|
type: BearerAuthFeatureType.MULTI_TAB_SYNC,
|
|
6544
6593
|
instance: state.instance,
|
|
6545
6594
|
devtools: () => [
|
|
6546
|
-
{
|
|
6595
|
+
{
|
|
6596
|
+
label: 'channel',
|
|
6597
|
+
value: context.isTabLocalSession() ? "off (session is this tab's own)" : state.channelName,
|
|
6598
|
+
},
|
|
6547
6599
|
{ label: 'tokens', value: config.syncTokens === false ? 'tab local' : 'synced' },
|
|
6548
6600
|
{ label: 'logout', value: config.syncLogout === false ? 'tab local' : 'synced' },
|
|
6549
6601
|
{
|
|
@@ -6557,6 +6609,13 @@ const withBearerAuthMultiTabSync = (config = {}) => {
|
|
|
6557
6609
|
// regular feature setup runs - so this half has to happen earlier than `setup`.
|
|
6558
6610
|
const earlySetup = (context) => {
|
|
6559
6611
|
const channelName = config.channelName ?? defaultSyncChannelName(context.name);
|
|
6612
|
+
// A tab the devtools handed a session of its own is not part of this app's shared session: it must
|
|
6613
|
+
// not push its tokens at its siblings, adopt theirs, or log them out - and it refreshes for itself,
|
|
6614
|
+
// because a leader that speaks for a session it is not on would refresh the wrong one.
|
|
6615
|
+
if (context.isTabLocalSession()) {
|
|
6616
|
+
pendingSetups.push({ instance: SINGLE_TAB, channelName });
|
|
6617
|
+
return {};
|
|
6618
|
+
}
|
|
6560
6619
|
// Filled in below once the election exists. Until then this tab answers as its own leader, which
|
|
6561
6620
|
// is what it is: the sync is set up first because the auth queries read `isLeader` while being
|
|
6562
6621
|
// wired, and a `state-request` cannot arrive before this function returns anyway.
|
|
@@ -6643,6 +6702,11 @@ const createPersistentAuthFeature = (config, context) => {
|
|
|
6643
6702
|
effect(() => {
|
|
6644
6703
|
const token = refreshToken();
|
|
6645
6704
|
const rememberMe = rememberMeSignal();
|
|
6705
|
+
// The cookie is the whole browser's, so a tab holding a session of its own would hand its user to
|
|
6706
|
+
// every other tab on the next load. Such a tab is restored from `sessionStorage` by the devtools
|
|
6707
|
+
// instead - see `setQueryDevtoolsAuthTabLocal`.
|
|
6708
|
+
if (context.isTabLocalSession())
|
|
6709
|
+
return;
|
|
6646
6710
|
// A missing token is not a reason to drop the cookie. It is missing on every startup - `tryLogin`
|
|
6647
6711
|
// reads the cookie synchronously and the auto-login only resolves a tick later - so deleting here
|
|
6648
6712
|
// would throw away a 30-day refresh token before it was ever used. Deletion is driven by the events
|
|
@@ -6654,7 +6718,7 @@ const createPersistentAuthFeature = (config, context) => {
|
|
|
6654
6718
|
});
|
|
6655
6719
|
effect(() => {
|
|
6656
6720
|
const endCause = context.sessionEndCause();
|
|
6657
|
-
if (endCause)
|
|
6721
|
+
if (endCause && !context.isTabLocalSession())
|
|
6658
6722
|
removeCookie();
|
|
6659
6723
|
});
|
|
6660
6724
|
effect(() => {
|
|
@@ -6666,7 +6730,7 @@ const createPersistentAuthFeature = (config, context) => {
|
|
|
6666
6730
|
const wasRejected = state.state === 'error' &&
|
|
6667
6731
|
(state.type === 'tokenRefresh' || state.type === 'autoLogin') &&
|
|
6668
6732
|
(state.error.code === 401 || state.error.code === 403);
|
|
6669
|
-
if (wasRejected)
|
|
6733
|
+
if (wasRejected && !context.isTabLocalSession())
|
|
6670
6734
|
removeCookie();
|
|
6671
6735
|
});
|
|
6672
6736
|
const exchangeCookie = () => {
|
|
@@ -7034,6 +7098,437 @@ const queryDevtoolsAbout = () => ({
|
|
|
7034
7098
|
app,
|
|
7035
7099
|
});
|
|
7036
7100
|
|
|
7101
|
+
const HOST_ID = 'et-query-devtools-pill';
|
|
7102
|
+
/** What the picker writes for "let the application decide", which is the key removed. */
|
|
7103
|
+
const DEFAULT_VALUE = '';
|
|
7104
|
+
/** Prefixes a production env's name, so the open dropdown says which option is the real backend. */
|
|
7105
|
+
const PRODUCTION_MARK = '⚠ ';
|
|
7106
|
+
const STYLE = `
|
|
7107
|
+
:host {
|
|
7108
|
+
position: fixed;
|
|
7109
|
+
display: flex;
|
|
7110
|
+
flex-direction: column;
|
|
7111
|
+
align-items: flex-end;
|
|
7112
|
+
gap: 8px;
|
|
7113
|
+
inset-block-end: 64px;
|
|
7114
|
+
inset-inline-end: 16px;
|
|
7115
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
7116
|
+
font-size: 13px;
|
|
7117
|
+
/* One below the panel and the toggle button (2147483010), so an open panel covers the pill - the
|
|
7118
|
+
panel offers the same picker on its Settings tab. Keep the three in step. */
|
|
7119
|
+
z-index: 2147483009;
|
|
7120
|
+
}
|
|
7121
|
+
|
|
7122
|
+
label {
|
|
7123
|
+
--_accent: var(--et-theme-color-primary-solid, #60a5fa);
|
|
7124
|
+
|
|
7125
|
+
display: inline-flex;
|
|
7126
|
+
align-items: center;
|
|
7127
|
+
gap: 8px;
|
|
7128
|
+
padding: 8px 10px 8px 12px;
|
|
7129
|
+
border-radius: 999px;
|
|
7130
|
+
background: linear-gradient(135deg, #1f1f23, #2a2a30);
|
|
7131
|
+
color: #fafafa;
|
|
7132
|
+
box-shadow:
|
|
7133
|
+
0 6px 20px rgb(0 0 0 / 0.35),
|
|
7134
|
+
inset 0 0 0 1px rgb(255 255 255 / 0.08);
|
|
7135
|
+
}
|
|
7136
|
+
|
|
7137
|
+
.name {
|
|
7138
|
+
color: #a1a1aa;
|
|
7139
|
+
font-size: 11px;
|
|
7140
|
+
font-weight: 600;
|
|
7141
|
+
letter-spacing: 0.04em;
|
|
7142
|
+
line-height: 1;
|
|
7143
|
+
text-transform: uppercase;
|
|
7144
|
+
}
|
|
7145
|
+
|
|
7146
|
+
select {
|
|
7147
|
+
max-inline-size: 220px;
|
|
7148
|
+
padding: 3px 6px;
|
|
7149
|
+
border: none;
|
|
7150
|
+
border-radius: 5px;
|
|
7151
|
+
background: rgb(255 255 255 / 0.07);
|
|
7152
|
+
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.12);
|
|
7153
|
+
color: #fafafa;
|
|
7154
|
+
font: inherit;
|
|
7155
|
+
font-size: 12px;
|
|
7156
|
+
font-weight: 600;
|
|
7157
|
+
line-height: 1;
|
|
7158
|
+
cursor: pointer;
|
|
7159
|
+
}
|
|
7160
|
+
|
|
7161
|
+
select:hover {
|
|
7162
|
+
box-shadow: inset 0 0 0 1px var(--_accent);
|
|
7163
|
+
}
|
|
7164
|
+
|
|
7165
|
+
/* The popup is painted by the browser, which does not inherit the pill's dark background. */
|
|
7166
|
+
option {
|
|
7167
|
+
background: #1f1f23;
|
|
7168
|
+
color: #fafafa;
|
|
7169
|
+
}
|
|
7170
|
+
|
|
7171
|
+
label[data-production] {
|
|
7172
|
+
background: linear-gradient(135deg, #7f1d1d, #b91c1c);
|
|
7173
|
+
color: #fff;
|
|
7174
|
+
box-shadow:
|
|
7175
|
+
0 6px 22px rgb(239 68 68 / 0.5),
|
|
7176
|
+
inset 0 0 0 1px rgb(254 202 202 / 0.7);
|
|
7177
|
+
}
|
|
7178
|
+
|
|
7179
|
+
label[data-production] .name {
|
|
7180
|
+
color: #fee2e2;
|
|
7181
|
+
}
|
|
7182
|
+
|
|
7183
|
+
label[data-production] .mark {
|
|
7184
|
+
font-size: 15px;
|
|
7185
|
+
line-height: 1;
|
|
7186
|
+
}
|
|
7187
|
+
|
|
7188
|
+
label[data-production] select {
|
|
7189
|
+
background: rgb(0 0 0 / 0.35);
|
|
7190
|
+
box-shadow: inset 0 0 0 1px rgb(254 202 202 / 0.6);
|
|
7191
|
+
text-transform: uppercase;
|
|
7192
|
+
}
|
|
7193
|
+
|
|
7194
|
+
label[data-production] option {
|
|
7195
|
+
background: #450a0a;
|
|
7196
|
+
}
|
|
7197
|
+
|
|
7198
|
+
label[data-auth] {
|
|
7199
|
+
--_accent: var(--et-theme-color-success-solid, #34d399);
|
|
7200
|
+
}
|
|
7201
|
+
|
|
7202
|
+
button {
|
|
7203
|
+
padding: 3px 7px;
|
|
7204
|
+
border: none;
|
|
7205
|
+
border-radius: 5px;
|
|
7206
|
+
background: rgb(255 255 255 / 0.07);
|
|
7207
|
+
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.12);
|
|
7208
|
+
color: #fafafa;
|
|
7209
|
+
font: inherit;
|
|
7210
|
+
font-size: 11px;
|
|
7211
|
+
font-weight: 600;
|
|
7212
|
+
line-height: 1.4;
|
|
7213
|
+
cursor: pointer;
|
|
7214
|
+
}
|
|
7215
|
+
|
|
7216
|
+
button:hover {
|
|
7217
|
+
box-shadow: inset 0 0 0 1px var(--_accent);
|
|
7218
|
+
}
|
|
7219
|
+
|
|
7220
|
+
button[data-on] {
|
|
7221
|
+
background: rgb(52 211 153 / 0.18);
|
|
7222
|
+
box-shadow: inset 0 0 0 1px rgb(52 211 153 / 0.7);
|
|
7223
|
+
}
|
|
7224
|
+
|
|
7225
|
+
/* The whole viewport, so the warning is on screen wherever the reader is looking. Outside the pill's
|
|
7226
|
+
flex flow: a fixed child of a fixed host still lays out against the viewport. */
|
|
7227
|
+
.frame {
|
|
7228
|
+
position: fixed;
|
|
7229
|
+
inset: 0;
|
|
7230
|
+
box-shadow:
|
|
7231
|
+
inset 0 0 0 3px rgb(239 68 68 / 0.85),
|
|
7232
|
+
inset 0 0 22px rgb(239 68 68 / 0.25);
|
|
7233
|
+
opacity: 0.85;
|
|
7234
|
+
pointer-events: none;
|
|
7235
|
+
}
|
|
7236
|
+
|
|
7237
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
7238
|
+
label[data-production] {
|
|
7239
|
+
animation: et-qd-api-env-pulse 1.6s ease-in-out infinite;
|
|
7240
|
+
}
|
|
7241
|
+
|
|
7242
|
+
.frame {
|
|
7243
|
+
animation: et-qd-api-env-frame 1.6s ease-in-out infinite;
|
|
7244
|
+
}
|
|
7245
|
+
}
|
|
7246
|
+
|
|
7247
|
+
@keyframes et-qd-api-env-pulse {
|
|
7248
|
+
0%,
|
|
7249
|
+
100% {
|
|
7250
|
+
box-shadow:
|
|
7251
|
+
0 6px 20px rgb(239 68 68 / 0.35),
|
|
7252
|
+
inset 0 0 0 1px rgb(254 202 202 / 0.45);
|
|
7253
|
+
}
|
|
7254
|
+
|
|
7255
|
+
50% {
|
|
7256
|
+
box-shadow:
|
|
7257
|
+
0 6px 30px rgb(239 68 68 / 0.85),
|
|
7258
|
+
inset 0 0 0 2px rgb(254 226 226 / 0.95);
|
|
7259
|
+
}
|
|
7260
|
+
}
|
|
7261
|
+
|
|
7262
|
+
@keyframes et-qd-api-env-frame {
|
|
7263
|
+
0%,
|
|
7264
|
+
100% {
|
|
7265
|
+
opacity: 0.35;
|
|
7266
|
+
}
|
|
7267
|
+
|
|
7268
|
+
50% {
|
|
7269
|
+
opacity: 1;
|
|
7270
|
+
}
|
|
7271
|
+
}
|
|
7272
|
+
`;
|
|
7273
|
+
/** The env a stored value lands on, which is the switch's own fallback while nothing is stored. */
|
|
7274
|
+
const resolvedEnvOf = (apiSwitch, stored) => apiSwitch.envs.find((env) => env.id === (stored || apiSwitch.fallback));
|
|
7275
|
+
const nameOf = (env) => `${env.production ? PRODUCTION_MARK : ''}${env.label ?? env.id}`;
|
|
7276
|
+
const optionsOf = (apiSwitch, stored) => {
|
|
7277
|
+
const fallback = apiSwitch.envs.find((env) => env.id === apiSwitch.fallback);
|
|
7278
|
+
const options = [
|
|
7279
|
+
{
|
|
7280
|
+
value: DEFAULT_VALUE,
|
|
7281
|
+
label: fallback ? `default (${nameOf(fallback)})` : 'default',
|
|
7282
|
+
title: `Removes ${apiSwitch.storageKey}, so the app picks for itself`,
|
|
7283
|
+
},
|
|
7284
|
+
];
|
|
7285
|
+
for (const env of apiSwitch.envs) {
|
|
7286
|
+
options.push({ value: env.id, label: nameOf(env), title: env.url ?? env.id });
|
|
7287
|
+
}
|
|
7288
|
+
// A URL typed into the Settings tab is a value no env carries, and a select cannot show one.
|
|
7289
|
+
if (stored !== DEFAULT_VALUE && !apiSwitch.envs.some((env) => env.id === stored)) {
|
|
7290
|
+
options.push({ value: stored, label: stored, title: `Typed in: ${stored}` });
|
|
7291
|
+
}
|
|
7292
|
+
return options;
|
|
7293
|
+
};
|
|
7294
|
+
const buildSwitch = (doc, apiSwitch, stored, pick) => {
|
|
7295
|
+
const production = resolvedEnvOf(apiSwitch, stored)?.production === true;
|
|
7296
|
+
const label = doc.createElement('label');
|
|
7297
|
+
label.title = production
|
|
7298
|
+
? `${apiSwitch.name} points at production. The data is real. Picking reloads the page.`
|
|
7299
|
+
: `${apiSwitch.name} — picking reloads the page`;
|
|
7300
|
+
if (production) {
|
|
7301
|
+
label.setAttribute('data-production', '');
|
|
7302
|
+
const mark = doc.createElement('span');
|
|
7303
|
+
mark.className = 'mark';
|
|
7304
|
+
mark.textContent = '⚠';
|
|
7305
|
+
mark.setAttribute('aria-hidden', 'true');
|
|
7306
|
+
label.append(mark);
|
|
7307
|
+
}
|
|
7308
|
+
const name = doc.createElement('span');
|
|
7309
|
+
name.className = 'name';
|
|
7310
|
+
name.textContent = production ? `${apiSwitch.name} · live` : apiSwitch.name;
|
|
7311
|
+
const select = doc.createElement('select');
|
|
7312
|
+
select.setAttribute('aria-label', apiSwitch.name);
|
|
7313
|
+
for (const option of optionsOf(apiSwitch, stored)) {
|
|
7314
|
+
const element = doc.createElement('option');
|
|
7315
|
+
element.value = option.value;
|
|
7316
|
+
element.textContent = option.label;
|
|
7317
|
+
element.title = option.title;
|
|
7318
|
+
element.selected = option.value === stored;
|
|
7319
|
+
select.append(element);
|
|
7320
|
+
}
|
|
7321
|
+
select.addEventListener('change', () => pick(apiSwitch.storageKey, select.value === DEFAULT_VALUE ? null : select.value));
|
|
7322
|
+
label.append(name, select);
|
|
7323
|
+
return label;
|
|
7324
|
+
};
|
|
7325
|
+
let envState = null;
|
|
7326
|
+
let authState = null;
|
|
7327
|
+
const buildAuthRow = (doc, row) => {
|
|
7328
|
+
const label = doc.createElement('label');
|
|
7329
|
+
label.setAttribute('data-auth', '');
|
|
7330
|
+
label.title = row.tabLocal
|
|
7331
|
+
? `${row.name} holds this tab's own session. The other tabs are on theirs.`
|
|
7332
|
+
: `${row.name} - the session every tab of this app shares`;
|
|
7333
|
+
const name = doc.createElement('span');
|
|
7334
|
+
name.className = 'name';
|
|
7335
|
+
name.textContent = row.tabLocal ? `${row.name} · this tab` : row.name;
|
|
7336
|
+
const select = doc.createElement('select');
|
|
7337
|
+
select.setAttribute('aria-label', `${row.name} session`);
|
|
7338
|
+
const current = doc.createElement('option');
|
|
7339
|
+
current.value = '';
|
|
7340
|
+
current.textContent = row.current;
|
|
7341
|
+
current.selected = !row.options.some((option) => option.selected);
|
|
7342
|
+
select.append(current);
|
|
7343
|
+
for (const option of row.options) {
|
|
7344
|
+
const element = doc.createElement('option');
|
|
7345
|
+
element.value = option.value;
|
|
7346
|
+
element.textContent = option.label;
|
|
7347
|
+
element.title = option.title;
|
|
7348
|
+
element.selected = option.selected;
|
|
7349
|
+
element.disabled = option.disabled;
|
|
7350
|
+
select.append(element);
|
|
7351
|
+
}
|
|
7352
|
+
select.addEventListener('change', () => {
|
|
7353
|
+
if (select.value)
|
|
7354
|
+
row.pick(select.value);
|
|
7355
|
+
});
|
|
7356
|
+
const isolate = doc.createElement('button');
|
|
7357
|
+
isolate.type = 'button';
|
|
7358
|
+
isolate.textContent = row.tabLocal ? 'rejoin' : 'own tab';
|
|
7359
|
+
isolate.title = row.tabLocal
|
|
7360
|
+
? 'Hands this tab back to the session every other tab shares. Reloads the page.'
|
|
7361
|
+
: 'Keeps this session in this tab alone, so another tab can be somebody else. Reloads the page.';
|
|
7362
|
+
if (row.tabLocal)
|
|
7363
|
+
isolate.setAttribute('data-on', '');
|
|
7364
|
+
isolate.addEventListener('click', row.toggleTabLocal);
|
|
7365
|
+
label.append(name, select, isolate);
|
|
7366
|
+
return label;
|
|
7367
|
+
};
|
|
7368
|
+
const render = (doc) => {
|
|
7369
|
+
doc.getElementById(HOST_ID)?.remove();
|
|
7370
|
+
const switches = envState?.switches ?? [];
|
|
7371
|
+
const rows = authState?.rows ?? [];
|
|
7372
|
+
if (!switches.length && !rows.length)
|
|
7373
|
+
return;
|
|
7374
|
+
const host = doc.createElement('div');
|
|
7375
|
+
host.id = HOST_ID;
|
|
7376
|
+
// Shadow DOM is intentional, for the same reason the toggle button uses it: this paints over the
|
|
7377
|
+
// host application's page and must be as isolated from its global CSS (resets, Tailwind) as possible.
|
|
7378
|
+
const shadow = host.attachShadow({ mode: 'open' });
|
|
7379
|
+
const style = doc.createElement('style');
|
|
7380
|
+
style.textContent = STYLE;
|
|
7381
|
+
shadow.append(style);
|
|
7382
|
+
let production = false;
|
|
7383
|
+
for (const row of rows)
|
|
7384
|
+
shadow.append(buildAuthRow(doc, row));
|
|
7385
|
+
for (const apiSwitch of switches) {
|
|
7386
|
+
const stored = envState?.read(apiSwitch.storageKey) ?? DEFAULT_VALUE;
|
|
7387
|
+
production ||= resolvedEnvOf(apiSwitch, stored)?.production === true;
|
|
7388
|
+
shadow.append(buildSwitch(doc, apiSwitch, stored, (storageKey, value) => envState?.pick(storageKey, value)));
|
|
7389
|
+
}
|
|
7390
|
+
if (production) {
|
|
7391
|
+
const frame = doc.createElement('div');
|
|
7392
|
+
frame.className = 'frame';
|
|
7393
|
+
frame.setAttribute('aria-hidden', 'true');
|
|
7394
|
+
shadow.append(frame);
|
|
7395
|
+
}
|
|
7396
|
+
doc.body.append(host);
|
|
7397
|
+
};
|
|
7398
|
+
/**
|
|
7399
|
+
* Paints the devtools' pills straight onto `document.body`, outside any Angular component tree.
|
|
7400
|
+
*
|
|
7401
|
+
* They cannot be components: an application whose boot is stuck on the backend it was just pointed at -
|
|
7402
|
+
* a blocking initial navigation waiting out an auth call that never answers - renders no component at
|
|
7403
|
+
* all, and that is exactly when somebody needs to pick a different backend or a different user. Mounted
|
|
7404
|
+
* from `setQueryDevtoolsApiEnvs()`, which runs while `provideQueryDevtools()` is being called, the env
|
|
7405
|
+
* part of this is on the page before `bootstrapApplication` starts.
|
|
7406
|
+
*/
|
|
7407
|
+
const renderPills = () => {
|
|
7408
|
+
const doc = globalThis.document;
|
|
7409
|
+
if (!doc)
|
|
7410
|
+
return;
|
|
7411
|
+
if (doc.body)
|
|
7412
|
+
render(doc);
|
|
7413
|
+
else
|
|
7414
|
+
doc.addEventListener('DOMContentLoaded', () => render(doc), { once: true });
|
|
7415
|
+
};
|
|
7416
|
+
/**
|
|
7417
|
+
* Declares the API env part of the pill. Called by `setQueryDevtoolsApiEnvs()`.
|
|
7418
|
+
* @internal
|
|
7419
|
+
*/
|
|
7420
|
+
const setQueryDevtoolsEnvPills = (state) => {
|
|
7421
|
+
envState = state.switches.length ? state : null;
|
|
7422
|
+
renderPills();
|
|
7423
|
+
};
|
|
7424
|
+
/**
|
|
7425
|
+
* Declares the session part of the pill, which is repainted on every switch, login and logout.
|
|
7426
|
+
* @internal
|
|
7427
|
+
*/
|
|
7428
|
+
const setQueryDevtoolsAuthPill = (state) => {
|
|
7429
|
+
authState = state.rows.length ? state : null;
|
|
7430
|
+
renderPills();
|
|
7431
|
+
};
|
|
7432
|
+
|
|
7433
|
+
const switches = /* @__PURE__ */ signal([], /* @ts-ignore */
|
|
7434
|
+
...(ngDevMode ? [{ debugName: "switches" }] : /* istanbul ignore next */ []));
|
|
7435
|
+
const values = /* @__PURE__ */ signal({}, /* @ts-ignore */
|
|
7436
|
+
...(ngDevMode ? [{ debugName: "values" }] : /* istanbul ignore next */ []));
|
|
7437
|
+
/**
|
|
7438
|
+
* The API env switches the application declared, or an empty list. Both the floating toggle and the
|
|
7439
|
+
* panel's Settings tab render nothing at all until an application declares one.
|
|
7440
|
+
*
|
|
7441
|
+
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7442
|
+
*/
|
|
7443
|
+
const queryDevtoolsApiEnvs = /* @__PURE__ */ switches.asReadonly();
|
|
7444
|
+
/**
|
|
7445
|
+
* What each switch's key holds now, keyed by that key. `null` for a key nothing has written, which is
|
|
7446
|
+
* the switch's own fallback.
|
|
7447
|
+
*
|
|
7448
|
+
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7449
|
+
*/
|
|
7450
|
+
const queryDevtoolsApiEnvValues = /* @__PURE__ */ values.asReadonly();
|
|
7451
|
+
const read$1 = (key) => {
|
|
7452
|
+
try {
|
|
7453
|
+
return window.localStorage.getItem(key);
|
|
7454
|
+
}
|
|
7455
|
+
catch {
|
|
7456
|
+
return null;
|
|
7457
|
+
}
|
|
7458
|
+
};
|
|
7459
|
+
/**
|
|
7460
|
+
* Declares the switches and reads what each key holds. Called by `provideQueryDevtools()` and by
|
|
7461
|
+
* nothing else.
|
|
7462
|
+
* @internal
|
|
7463
|
+
*/
|
|
7464
|
+
const setQueryDevtoolsApiEnvs = (list) => {
|
|
7465
|
+
switches.set(list ?? []);
|
|
7466
|
+
values.set(Object.fromEntries((list ?? []).map((entry) => [entry.storageKey, read$1(entry.storageKey)])));
|
|
7467
|
+
setQueryDevtoolsEnvPills({ switches: list ?? [], read: read$1, pick: pickAndReload });
|
|
7468
|
+
};
|
|
7469
|
+
/**
|
|
7470
|
+
* Writes one switch's key, or removes it when `value` is `null` so the application falls back. The
|
|
7471
|
+
* application reads the key before Angular boots, so this takes effect on the next page load only -
|
|
7472
|
+
* callers reload.
|
|
7473
|
+
*
|
|
7474
|
+
* The value is written as the plain string the application reads. Do not route it through
|
|
7475
|
+
* `writeQueryDevtoolsStore`: that JSON-encodes, and the quotes would reach the application.
|
|
7476
|
+
*
|
|
7477
|
+
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7478
|
+
*/
|
|
7479
|
+
const setQueryDevtoolsApiEnv = (storageKey, value) => {
|
|
7480
|
+
try {
|
|
7481
|
+
if (value === null)
|
|
7482
|
+
window.localStorage.removeItem(storageKey);
|
|
7483
|
+
else
|
|
7484
|
+
window.localStorage.setItem(storageKey, value);
|
|
7485
|
+
}
|
|
7486
|
+
catch {
|
|
7487
|
+
// ignore (private mode / disabled storage / quota)
|
|
7488
|
+
}
|
|
7489
|
+
values.update((current) => ({ ...current, [storageKey]: read$1(storageKey) }));
|
|
7490
|
+
};
|
|
7491
|
+
/** The env in force for one switch, which is its own fallback while nothing is stored. */
|
|
7492
|
+
const resolvedIdOf = (entry) => values()[entry.storageKey] ?? entry.fallback ?? '';
|
|
7493
|
+
/**
|
|
7494
|
+
* Which backends the application is pointed at right now, as one string. Every declared switch is in it,
|
|
7495
|
+
* so a session captured against staging is never offered on production.
|
|
7496
|
+
*
|
|
7497
|
+
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7498
|
+
*/
|
|
7499
|
+
const queryDevtoolsApiEnvScope = () => {
|
|
7500
|
+
const list = switches();
|
|
7501
|
+
if (!list.length)
|
|
7502
|
+
return 'default';
|
|
7503
|
+
return list
|
|
7504
|
+
.map((entry) => `${entry.storageKey}=${resolvedIdOf(entry)}`)
|
|
7505
|
+
.sort()
|
|
7506
|
+
.join('&');
|
|
7507
|
+
};
|
|
7508
|
+
/**
|
|
7509
|
+
* The env id in force per switch, which is what an account's declared `envs` is matched against.
|
|
7510
|
+
*
|
|
7511
|
+
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7512
|
+
*/
|
|
7513
|
+
const queryDevtoolsApiEnvIds = () => switches()
|
|
7514
|
+
.map(resolvedIdOf)
|
|
7515
|
+
.filter((id) => id !== '');
|
|
7516
|
+
/**
|
|
7517
|
+
* Whether any switch's pick is an env the application declared as `production`. Everything that would
|
|
7518
|
+
* keep a real user's tokens on this machine refuses while this reads `true`.
|
|
7519
|
+
*
|
|
7520
|
+
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7521
|
+
*/
|
|
7522
|
+
const queryDevtoolsApiEnvIsProduction = () => switches().some((entry) => entry.envs.find((env) => env.id === resolvedIdOf(entry))?.production === true);
|
|
7523
|
+
/**
|
|
7524
|
+
* What the floating pill does on a pick. The application reads the key before Angular boots, so a
|
|
7525
|
+
* reload is the only way to apply one.
|
|
7526
|
+
*/
|
|
7527
|
+
const pickAndReload = (storageKey, value) => {
|
|
7528
|
+
setQueryDevtoolsApiEnv(storageKey, value);
|
|
7529
|
+
window.location.reload();
|
|
7530
|
+
};
|
|
7531
|
+
|
|
7037
7532
|
const DEFAULTS = {
|
|
7038
7533
|
viewState: 'session',
|
|
7039
7534
|
pins: 'local',
|
|
@@ -7041,9 +7536,11 @@ const DEFAULTS = {
|
|
|
7041
7536
|
mocks: 'local',
|
|
7042
7537
|
armedMocks: 'none',
|
|
7043
7538
|
armedFaults: 'none',
|
|
7539
|
+
authSessions: 'local',
|
|
7044
7540
|
maxEvents: 100,
|
|
7045
7541
|
maxDroppedCacheEntries: 20,
|
|
7046
7542
|
responseHistory: null,
|
|
7543
|
+
reloadOnAuthSwitch: true,
|
|
7047
7544
|
};
|
|
7048
7545
|
/**
|
|
7049
7546
|
* `localStorage`, whatever the scopes below say - a scope of `none` still has to be remembered, or the
|
|
@@ -7138,16 +7635,18 @@ const asOptionalCount = (value, limits) => {
|
|
|
7138
7635
|
return Number.isFinite(count) ? Math.min(Math.max(count, limits.min), limits.max) : null;
|
|
7139
7636
|
};
|
|
7140
7637
|
/** Both a hand-edited store and a number typed into the panel come through here. */
|
|
7141
|
-
const sanitize = (value) => ({
|
|
7638
|
+
const sanitize$1 = (value) => ({
|
|
7142
7639
|
viewState: asScope(value?.viewState, DEFAULTS.viewState),
|
|
7143
7640
|
pins: asScope(value?.pins, DEFAULTS.pins),
|
|
7144
7641
|
overrides: asScope(value?.overrides, DEFAULTS.overrides),
|
|
7145
7642
|
mocks: asScope(value?.mocks, DEFAULTS.mocks),
|
|
7146
7643
|
armedMocks: asScope(value?.armedMocks, DEFAULTS.armedMocks),
|
|
7147
7644
|
armedFaults: asScope(value?.armedFaults, DEFAULTS.armedFaults),
|
|
7645
|
+
authSessions: asScope(value?.authSessions, DEFAULTS.authSessions),
|
|
7148
7646
|
maxEvents: asCount(value?.maxEvents, LIMITS.maxEvents, DEFAULTS.maxEvents),
|
|
7149
7647
|
maxDroppedCacheEntries: asCount(value?.maxDroppedCacheEntries, LIMITS.maxDroppedCacheEntries, DEFAULTS.maxDroppedCacheEntries),
|
|
7150
7648
|
responseHistory: asOptionalCount(value?.responseHistory, LIMITS.responseHistory),
|
|
7649
|
+
reloadOnAuthSwitch: typeof value?.reloadOnAuthSwitch === 'boolean' ? value.reloadOnAuthSwitch : DEFAULTS.reloadOnAuthSwitch,
|
|
7151
7650
|
});
|
|
7152
7651
|
/**
|
|
7153
7652
|
* Reads the stored settings. Called by `provideQueryDevtools()` **before** the override store is read
|
|
@@ -7156,7 +7655,7 @@ const sanitize = (value) => ({
|
|
|
7156
7655
|
* @internal
|
|
7157
7656
|
*/
|
|
7158
7657
|
const initQueryDevtoolsSettings = () => {
|
|
7159
|
-
settings.set(sanitize(readQueryDevtoolsStore('local', SETTINGS_KEY)));
|
|
7658
|
+
settings.set(sanitize$1(readQueryDevtoolsStore('local', SETTINGS_KEY)));
|
|
7160
7659
|
};
|
|
7161
7660
|
/**
|
|
7162
7661
|
* Changes part of the devtools configuration and stores it. Values outside the range a field allows are
|
|
@@ -7168,11 +7667,503 @@ const initQueryDevtoolsSettings = () => {
|
|
|
7168
7667
|
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7169
7668
|
*/
|
|
7170
7669
|
const setQueryDevtoolsSettings = (patch) => {
|
|
7171
|
-
const next = sanitize({ ...settings(), ...patch });
|
|
7670
|
+
const next = sanitize$1({ ...settings(), ...patch });
|
|
7172
7671
|
settings.set(next);
|
|
7173
7672
|
writeQueryDevtoolsStore('local', SETTINGS_KEY, next);
|
|
7174
7673
|
};
|
|
7175
7674
|
|
|
7675
|
+
const STORE_KEY = 'ethlete:query:devtools:auth:v1';
|
|
7676
|
+
/**
|
|
7677
|
+
* `sessionStorage`: which stored session a provider's live tokens belong to is the one part of this that
|
|
7678
|
+
* is the tab's own. Two tabs holding two users would otherwise overwrite each other's answer.
|
|
7679
|
+
*/
|
|
7680
|
+
const ACTIVE_KEY = 'ethlete:query:devtools:auth-active:v1';
|
|
7681
|
+
/** `sessionStorage`, always: a session that belongs to one tab cannot be kept where every tab reads it. */
|
|
7682
|
+
const SEED_KEY = 'ethlete:query:devtools:auth-tab-session:v1';
|
|
7683
|
+
const DEFAULT_FIELDS = [
|
|
7684
|
+
{ name: 'email', type: 'email' },
|
|
7685
|
+
{ name: 'password', type: 'password' },
|
|
7686
|
+
];
|
|
7687
|
+
const EMPTY = { sessions: [], credentials: {}, accounts: [] };
|
|
7688
|
+
const store = /* @__PURE__ */ signal(EMPTY, /* @ts-ignore */
|
|
7689
|
+
...(ngDevMode ? [{ debugName: "store" }] : /* istanbul ignore next */ []));
|
|
7690
|
+
const active = /* @__PURE__ */ signal({}, /* @ts-ignore */
|
|
7691
|
+
...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
|
|
7692
|
+
const declared = /* @__PURE__ */ signal([], /* @ts-ignore */
|
|
7693
|
+
...(ngDevMode ? [{ debugName: "declared" }] : /* istanbul ignore next */ []));
|
|
7694
|
+
const providers = /* @__PURE__ */ signal([], /* @ts-ignore */
|
|
7695
|
+
...(ngDevMode ? [{ debugName: "providers" }] : /* istanbul ignore next */ []));
|
|
7696
|
+
const tabLocal = /* @__PURE__ */ signal({}, /* @ts-ignore */
|
|
7697
|
+
...(ngDevMode ? [{ debugName: "tabLocal" }] : /* istanbul ignore next */ []));
|
|
7698
|
+
const live = /* @__PURE__ */ new Map();
|
|
7699
|
+
let idCounter = 0;
|
|
7700
|
+
const nextId = (prefix) => `${prefix}-${Date.now().toString(36)}-${++idCounter}`;
|
|
7701
|
+
/**
|
|
7702
|
+
* Every session the vault holds, whatever env or provider it belongs to. Read
|
|
7703
|
+
* {@link queryDevtoolsAuthSessionsFor} instead where a provider is in hand.
|
|
7704
|
+
*
|
|
7705
|
+
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7706
|
+
*/
|
|
7707
|
+
const queryDevtoolsAuthSessions = /* @__PURE__ */ computed(() => store().sessions, /* @ts-ignore */
|
|
7708
|
+
...(ngDevMode ? [{ debugName: "queryDevtoolsAuthSessions" }] : /* istanbul ignore next */ []));
|
|
7709
|
+
/**
|
|
7710
|
+
* Which stored session each provider's live tokens belong to, keyed by provider name. This tab's own
|
|
7711
|
+
* answer: another tab on another user has its own.
|
|
7712
|
+
*
|
|
7713
|
+
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7714
|
+
*/
|
|
7715
|
+
const queryDevtoolsAuthActive = /* @__PURE__ */ active.asReadonly();
|
|
7716
|
+
/**
|
|
7717
|
+
* Which providers hold a session of their own rather than the one every tab shares, keyed by provider
|
|
7718
|
+
* name.
|
|
7719
|
+
*
|
|
7720
|
+
* Part of the devtools contract. **Not part of the general public contract.**
|
|
7721
|
+
*/
|
|
7722
|
+
const queryDevtoolsAuthTabLocal = /* @__PURE__ */ tabLocal.asReadonly();
|
|
7723
|
+
/** The auth providers the vault has been handed, in registration order. @internal */
|
|
7724
|
+
const queryDevtoolsAuthProviders = /* @__PURE__ */ providers.asReadonly();
|
|
7725
|
+
const scopeOf = () => queryDevtoolsApiEnvScope();
|
|
7726
|
+
const persist = () => {
|
|
7727
|
+
const scope = queryDevtoolsSettings().authSessions;
|
|
7728
|
+
writeQueryDevtoolsStore(scope, STORE_KEY, store());
|
|
7729
|
+
writeQueryDevtoolsStore(scope === 'none' ? 'none' : 'session', ACTIVE_KEY, active());
|
|
7730
|
+
};
|
|
7731
|
+
const setActive = (provider, sessionId) => {
|
|
7732
|
+
active.update((current) => ({ ...current, [provider]: sessionId }));
|
|
7733
|
+
};
|
|
7734
|
+
const readSeeds = () => readQueryDevtoolsStore('session', SEED_KEY) ?? {};
|
|
7735
|
+
const writeSeeds = (seeds) => {
|
|
7736
|
+
if (Object.keys(seeds).length)
|
|
7737
|
+
writeQueryDevtoolsStore('session', SEED_KEY, seeds);
|
|
7738
|
+
else
|
|
7739
|
+
queryDevtoolsStorage('session')?.removeItem(SEED_KEY);
|
|
7740
|
+
};
|
|
7741
|
+
/** The claims a session is named and recognised by, or nothing for a token that is not a JWT. */
|
|
7742
|
+
const identityOf = (accessToken) => {
|
|
7743
|
+
try {
|
|
7744
|
+
const payload = decryptBearer(accessToken);
|
|
7745
|
+
if (!payload)
|
|
7746
|
+
return { label: null, subject: null, expiresAt: null };
|
|
7747
|
+
const named = ['name', 'preferred_username', 'username', 'email', 'sub'].find((claim) => typeof payload[claim] === 'string' && payload[claim] !== '');
|
|
7748
|
+
return {
|
|
7749
|
+
label: named ? String(payload[named]) : null,
|
|
7750
|
+
subject: typeof payload['sub'] === 'string' ? payload['sub'] : null,
|
|
7751
|
+
expiresAt: typeof payload['exp'] === 'number' ? payload['exp'] : null,
|
|
7752
|
+
};
|
|
7753
|
+
}
|
|
7754
|
+
catch {
|
|
7755
|
+
return { label: null, subject: null, expiresAt: null };
|
|
7756
|
+
}
|
|
7757
|
+
};
|
|
7758
|
+
const sanitizeSession = (value) => {
|
|
7759
|
+
if (typeof value !== 'object' || value === null)
|
|
7760
|
+
return null;
|
|
7761
|
+
const session = value;
|
|
7762
|
+
if (typeof session.id !== 'string' ||
|
|
7763
|
+
typeof session.provider !== 'string' ||
|
|
7764
|
+
typeof session.accessToken !== 'string' ||
|
|
7765
|
+
typeof session.refreshToken !== 'string') {
|
|
7766
|
+
return null;
|
|
7767
|
+
}
|
|
7768
|
+
return {
|
|
7769
|
+
id: session.id,
|
|
7770
|
+
provider: session.provider,
|
|
7771
|
+
label: typeof session.label === 'string' ? session.label : session.id,
|
|
7772
|
+
scope: typeof session.scope === 'string' ? session.scope : 'default',
|
|
7773
|
+
accessToken: session.accessToken,
|
|
7774
|
+
refreshToken: session.refreshToken,
|
|
7775
|
+
subject: typeof session.subject === 'string' ? session.subject : null,
|
|
7776
|
+
expiresAt: typeof session.expiresAt === 'number' ? session.expiresAt : null,
|
|
7777
|
+
savedAt: typeof session.savedAt === 'number' ? session.savedAt : 0,
|
|
7778
|
+
};
|
|
7779
|
+
};
|
|
7780
|
+
const sanitize = (value) => ({
|
|
7781
|
+
sessions: Array.isArray(value?.sessions)
|
|
7782
|
+
? value.sessions.map(sanitizeSession).filter((session) => session !== null)
|
|
7783
|
+
: [],
|
|
7784
|
+
credentials: typeof value?.credentials === 'object' && value.credentials !== null ? value.credentials : {},
|
|
7785
|
+
accounts: Array.isArray(value?.accounts) ? value.accounts : [],
|
|
7786
|
+
});
|
|
7787
|
+
/**
|
|
7788
|
+
* The vault is one key every tab writes, so a tab holding a stale copy of it would drop whatever another
|
|
7789
|
+
* tab stored since. The event fires in the other tabs only, which is exactly the ones that need it.
|
|
7790
|
+
*/
|
|
7791
|
+
const listenForOtherTabs = () => {
|
|
7792
|
+
if (typeof window === 'undefined' || listening)
|
|
7793
|
+
return;
|
|
7794
|
+
listening = true;
|
|
7795
|
+
window.addEventListener('storage', (event) => {
|
|
7796
|
+
if (event.key !== STORE_KEY)
|
|
7797
|
+
return;
|
|
7798
|
+
store.set(sanitize(event.newValue ? JSON.parse(event.newValue) : null));
|
|
7799
|
+
syncPill();
|
|
7800
|
+
});
|
|
7801
|
+
};
|
|
7802
|
+
let listening = false;
|
|
7803
|
+
/**
|
|
7804
|
+
* Declares the accounts the panel may log in as, and reads back what a previous page stored. Called by
|
|
7805
|
+
* `provideQueryDevtools()` and by nothing else.
|
|
7806
|
+
* @internal
|
|
7807
|
+
*/
|
|
7808
|
+
const initQueryDevtoolsAuthSessions = (accounts) => {
|
|
7809
|
+
const scope = queryDevtoolsSettings().authSessions;
|
|
7810
|
+
declared.set(accounts ?? []);
|
|
7811
|
+
store.set(sanitize(readQueryDevtoolsStore(scope, STORE_KEY)));
|
|
7812
|
+
active.set(readQueryDevtoolsStore(scope === 'none' ? 'none' : 'session', ACTIVE_KEY) ?? {});
|
|
7813
|
+
listenForOtherTabs();
|
|
7814
|
+
syncPill();
|
|
7815
|
+
};
|
|
7816
|
+
/**
|
|
7817
|
+
* The session the panel put in this tab alone, or `null`. A seed from another backend is dropped rather
|
|
7818
|
+
* than applied: its tokens were issued by an API this page is not pointed at any more.
|
|
7819
|
+
* @internal
|
|
7820
|
+
*/
|
|
7821
|
+
const readQueryDevtoolsAuthSeedFor = (providerName) => {
|
|
7822
|
+
const seeds = readSeeds();
|
|
7823
|
+
const seed = seeds[providerName];
|
|
7824
|
+
if (!seed)
|
|
7825
|
+
return null;
|
|
7826
|
+
if (seed.scope !== scopeOf()) {
|
|
7827
|
+
const { [providerName]: _dropped, ...rest } = seeds;
|
|
7828
|
+
writeSeeds(rest);
|
|
7829
|
+
return null;
|
|
7830
|
+
}
|
|
7831
|
+
tabLocal.update((current) => ({ ...current, [providerName]: true }));
|
|
7832
|
+
setActive(providerName, seed.sessionId);
|
|
7833
|
+
return { accessToken: seed.accessToken, refreshToken: seed.refreshToken };
|
|
7834
|
+
};
|
|
7835
|
+
/**
|
|
7836
|
+
* Keeps the stored session in step with the live one: every rotation of a refresh token is written back,
|
|
7837
|
+
* so switching away and back later does not present a token the server has already spent.
|
|
7838
|
+
* @internal
|
|
7839
|
+
*/
|
|
7840
|
+
const trackQueryDevtoolsAuthProvider = (registration) => {
|
|
7841
|
+
const { name, handle, injector, isTabLocalSession } = registration;
|
|
7842
|
+
live.set(name, registration);
|
|
7843
|
+
providers.update((current) => (current.includes(name) ? current : [...current, name]));
|
|
7844
|
+
isTabLocalSession.set(tabLocal()[name] === true);
|
|
7845
|
+
const stop = effect(() => {
|
|
7846
|
+
const accessToken = handle.accessToken();
|
|
7847
|
+
const refreshToken = handle.refreshToken();
|
|
7848
|
+
untracked(() => {
|
|
7849
|
+
if (accessToken && refreshToken)
|
|
7850
|
+
remember({ provider: name, accessToken, refreshToken });
|
|
7851
|
+
else
|
|
7852
|
+
forgetActive(name);
|
|
7853
|
+
});
|
|
7854
|
+
}, { ...(ngDevMode ? { debugName: "stop" } : /* istanbul ignore next */ {}), injector });
|
|
7855
|
+
syncPill();
|
|
7856
|
+
return () => {
|
|
7857
|
+
stop.destroy();
|
|
7858
|
+
live.delete(name);
|
|
7859
|
+
providers.update((current) => current.filter((entry) => entry !== name));
|
|
7860
|
+
syncPill();
|
|
7861
|
+
};
|
|
7862
|
+
};
|
|
7863
|
+
/**
|
|
7864
|
+
* Writes a live token pair into the vault. A pair whose subject is already stored updates that session
|
|
7865
|
+
* rather than adding another, which is what lets a plain login in the application fill the picker.
|
|
7866
|
+
*/
|
|
7867
|
+
const remember = (options) => {
|
|
7868
|
+
const { provider, accessToken, refreshToken } = options;
|
|
7869
|
+
// Nobody's real credentials or tokens are kept on this machine. See the Auth tab's own warning.
|
|
7870
|
+
if (queryDevtoolsApiEnvIsProduction())
|
|
7871
|
+
return;
|
|
7872
|
+
const scope = scopeOf();
|
|
7873
|
+
const identity = identityOf(accessToken);
|
|
7874
|
+
const activeId = active()[provider] ?? null;
|
|
7875
|
+
const activeSession = store().sessions.find((session) => session.id === activeId);
|
|
7876
|
+
const bySubject = identity.subject
|
|
7877
|
+
? store().sessions.find((session) => session.provider === provider && session.scope === scope && session.subject === identity.subject)
|
|
7878
|
+
: undefined;
|
|
7879
|
+
// The session on record is only a rotation of the live one while both name the same user. Logging in as
|
|
7880
|
+
// somebody else without logging out first would otherwise overwrite the tokens of the user this tab is
|
|
7881
|
+
// switching away from - which is the one pair the vault exists to keep.
|
|
7882
|
+
const isRotation = !activeSession || !identity.subject || !activeSession.subject || activeSession.subject === identity.subject;
|
|
7883
|
+
const target = isRotation ? (activeSession ?? bySubject) : bySubject;
|
|
7884
|
+
const session = {
|
|
7885
|
+
id: target?.id ?? nextId('session'),
|
|
7886
|
+
provider,
|
|
7887
|
+
label: target?.label ?? identity.label ?? 'session',
|
|
7888
|
+
scope,
|
|
7889
|
+
accessToken,
|
|
7890
|
+
refreshToken,
|
|
7891
|
+
subject: identity.subject,
|
|
7892
|
+
expiresAt: identity.expiresAt,
|
|
7893
|
+
savedAt: Date.now(),
|
|
7894
|
+
};
|
|
7895
|
+
store.update((current) => ({
|
|
7896
|
+
...current,
|
|
7897
|
+
sessions: target
|
|
7898
|
+
? current.sessions.map((entry) => (entry.id === target.id ? session : entry))
|
|
7899
|
+
: [...current.sessions, session],
|
|
7900
|
+
}));
|
|
7901
|
+
setActive(provider, session.id);
|
|
7902
|
+
if (tabLocal()[provider]) {
|
|
7903
|
+
writeSeeds({ ...readSeeds(), [provider]: { sessionId: session.id, scope, accessToken, refreshToken } });
|
|
7904
|
+
}
|
|
7905
|
+
persist();
|
|
7906
|
+
syncPill();
|
|
7907
|
+
};
|
|
7908
|
+
/** Lets go of the session a provider was on, without forgetting what the vault holds of it. */
|
|
7909
|
+
const forgetActive = (provider) => {
|
|
7910
|
+
if (!active()[provider])
|
|
7911
|
+
return;
|
|
7912
|
+
setActive(provider, null);
|
|
7913
|
+
persist();
|
|
7914
|
+
syncPill();
|
|
7915
|
+
};
|
|
7916
|
+
/** The sessions offered for one provider, which are the ones this backend issued. */
|
|
7917
|
+
const queryDevtoolsAuthSessionsFor = (provider) => {
|
|
7918
|
+
const scope = scopeOf();
|
|
7919
|
+
return store().sessions.filter((session) => session.provider === provider && session.scope === scope);
|
|
7920
|
+
};
|
|
7921
|
+
/** How many sessions this provider has under a different backend, which is what a switch left behind. */
|
|
7922
|
+
const queryDevtoolsAuthOtherScopeCount = (provider) => {
|
|
7923
|
+
const scope = scopeOf();
|
|
7924
|
+
return store().sessions.filter((session) => session.provider === provider && session.scope !== scope).length;
|
|
7925
|
+
};
|
|
7926
|
+
const declaredIdOf = (account) => `declared:${account.provider}|${account.label}`;
|
|
7927
|
+
const viewOf = (options) => {
|
|
7928
|
+
const stored = store().credentials[options.id] ?? {};
|
|
7929
|
+
const values = Object.fromEntries(options.fields.map((field) => [field.name, stored[field.name] ?? field.default ?? '']));
|
|
7930
|
+
return {
|
|
7931
|
+
...options,
|
|
7932
|
+
values,
|
|
7933
|
+
ready: options.fields.every((field) => values[field.name] !== ''),
|
|
7934
|
+
};
|
|
7935
|
+
};
|
|
7936
|
+
/**
|
|
7937
|
+
* The accounts on offer for one provider: the ones the application declared for the env in force, plus
|
|
7938
|
+
* the ones added in the panel. Empty while a production env is the pick - nothing keeps a real user's
|
|
7939
|
+
* password on a developer's machine.
|
|
7940
|
+
*/
|
|
7941
|
+
const queryDevtoolsAuthAccountsFor = (provider) => {
|
|
7942
|
+
if (queryDevtoolsApiEnvIsProduction())
|
|
7943
|
+
return [];
|
|
7944
|
+
const scope = scopeOf();
|
|
7945
|
+
const envIds = queryDevtoolsApiEnvIds();
|
|
7946
|
+
const fromApp = declared()
|
|
7947
|
+
.filter((account) => account.provider === provider)
|
|
7948
|
+
.filter((account) => !account.envs?.length || account.envs.some((id) => envIds.includes(id)))
|
|
7949
|
+
.map((account) => viewOf({
|
|
7950
|
+
id: declaredIdOf(account),
|
|
7951
|
+
provider,
|
|
7952
|
+
label: account.label,
|
|
7953
|
+
loginQuery: account.loginQuery,
|
|
7954
|
+
fields: account.fields ?? DEFAULT_FIELDS,
|
|
7955
|
+
declared: true,
|
|
7956
|
+
buildArgs: account.buildArgs,
|
|
7957
|
+
note: account.note,
|
|
7958
|
+
}));
|
|
7959
|
+
const fromPanel = store()
|
|
7960
|
+
.accounts.filter((account) => account.provider === provider && account.scope === scope)
|
|
7961
|
+
.map((account) => viewOf({
|
|
7962
|
+
id: `local:${account.id}`,
|
|
7963
|
+
provider,
|
|
7964
|
+
label: account.label,
|
|
7965
|
+
loginQuery: account.loginQuery,
|
|
7966
|
+
fields: DEFAULT_FIELDS,
|
|
7967
|
+
declared: false,
|
|
7968
|
+
}));
|
|
7969
|
+
return [...fromApp, ...fromPanel];
|
|
7970
|
+
};
|
|
7971
|
+
/** Keeps one account's credentials on this machine. Refused while a production env is the pick. */
|
|
7972
|
+
const setQueryDevtoolsAuthCredentials = (options) => {
|
|
7973
|
+
if (queryDevtoolsApiEnvIsProduction())
|
|
7974
|
+
return;
|
|
7975
|
+
store.update((current) => ({
|
|
7976
|
+
...current,
|
|
7977
|
+
credentials: { ...current.credentials, [options.accountId]: options.values },
|
|
7978
|
+
}));
|
|
7979
|
+
persist();
|
|
7980
|
+
syncPill();
|
|
7981
|
+
};
|
|
7982
|
+
/** Forgets what was typed in for one account, leaving the account itself on offer. */
|
|
7983
|
+
const clearQueryDevtoolsAuthCredentials = (accountId) => {
|
|
7984
|
+
store.update((current) => {
|
|
7985
|
+
const { [accountId]: _removed, ...rest } = current.credentials;
|
|
7986
|
+
return { ...current, credentials: rest };
|
|
7987
|
+
});
|
|
7988
|
+
persist();
|
|
7989
|
+
syncPill();
|
|
7990
|
+
};
|
|
7991
|
+
/** Adds an account the application does not declare, for the backend in force. */
|
|
7992
|
+
const addQueryDevtoolsAuthAccount = (options) => {
|
|
7993
|
+
const account = { ...options, id: nextId('account'), scope: scopeOf() };
|
|
7994
|
+
store.update((current) => ({ ...current, accounts: [...current.accounts, account] }));
|
|
7995
|
+
persist();
|
|
7996
|
+
syncPill();
|
|
7997
|
+
return `local:${account.id}`;
|
|
7998
|
+
};
|
|
7999
|
+
/** Removes an account added in the panel, and whatever was typed in for it. */
|
|
8000
|
+
const removeQueryDevtoolsAuthAccount = (accountId) => {
|
|
8001
|
+
const id = accountId.replace(/^local:/, '');
|
|
8002
|
+
store.update((current) => {
|
|
8003
|
+
const { [accountId]: _removed, ...credentials } = current.credentials;
|
|
8004
|
+
return { ...current, credentials, accounts: current.accounts.filter((account) => account.id !== id) };
|
|
8005
|
+
});
|
|
8006
|
+
persist();
|
|
8007
|
+
syncPill();
|
|
8008
|
+
};
|
|
8009
|
+
const renameQueryDevtoolsAuthSession = (options) => {
|
|
8010
|
+
store.update((current) => ({
|
|
8011
|
+
...current,
|
|
8012
|
+
sessions: current.sessions.map((session) => session.id === options.sessionId ? { ...session, label: options.label } : session),
|
|
8013
|
+
}));
|
|
8014
|
+
persist();
|
|
8015
|
+
syncPill();
|
|
8016
|
+
};
|
|
8017
|
+
/** Drops one stored session. The live one it describes is left alone. */
|
|
8018
|
+
const forgetQueryDevtoolsAuthSession = (sessionId) => {
|
|
8019
|
+
store.update((current) => ({
|
|
8020
|
+
...current,
|
|
8021
|
+
sessions: current.sessions.filter((session) => session.id !== sessionId),
|
|
8022
|
+
}));
|
|
8023
|
+
active.update((current) => Object.fromEntries(Object.entries(current).map(([provider, id]) => [provider, id === sessionId ? null : id])));
|
|
8024
|
+
persist();
|
|
8025
|
+
syncPill();
|
|
8026
|
+
};
|
|
8027
|
+
/** Empties the vault: every session, every account added in the panel, every credential. */
|
|
8028
|
+
const clearQueryDevtoolsAuthSessions = () => {
|
|
8029
|
+
store.set(EMPTY);
|
|
8030
|
+
active.set({});
|
|
8031
|
+
writeSeeds({});
|
|
8032
|
+
persist();
|
|
8033
|
+
syncPill();
|
|
8034
|
+
};
|
|
8035
|
+
const reload = () => globalThis.location?.reload();
|
|
8036
|
+
/**
|
|
8037
|
+
* Drops what the previous user left behind. Their responses are cached under keys the next user's
|
|
8038
|
+
* requests hit, and a bound secure query would go on rendering a body that was fetched for somebody else.
|
|
8039
|
+
*/
|
|
8040
|
+
const clearClientData = (entry) => {
|
|
8041
|
+
const { repository } = entry.client;
|
|
8042
|
+
repository.unbindAllSecure();
|
|
8043
|
+
for (const cacheEntry of repository.subtle.cacheEntries())
|
|
8044
|
+
repository.subtle.evict(cacheEntry.key);
|
|
8045
|
+
void entry.client.clearPersistedQueries();
|
|
8046
|
+
};
|
|
8047
|
+
/**
|
|
8048
|
+
* Puts one stored session's tokens in force. The reload is the default because a switch only replaces
|
|
8049
|
+
* what the query layer holds: an application that keeps the user anywhere else - a profile service, the
|
|
8050
|
+
* router, an open form - is still showing the last one.
|
|
8051
|
+
*/
|
|
8052
|
+
const switchQueryDevtoolsAuthSession = (options) => {
|
|
8053
|
+
const session = store().sessions.find((entry) => entry.id === options.sessionId);
|
|
8054
|
+
if (!session)
|
|
8055
|
+
return;
|
|
8056
|
+
const entry = live.get(session.provider);
|
|
8057
|
+
if (!entry || session.scope !== scopeOf())
|
|
8058
|
+
return;
|
|
8059
|
+
clearClientData(entry);
|
|
8060
|
+
setActive(session.provider, session.id);
|
|
8061
|
+
if (tabLocal()[session.provider]) {
|
|
8062
|
+
writeSeeds({
|
|
8063
|
+
...readSeeds(),
|
|
8064
|
+
[session.provider]: {
|
|
8065
|
+
sessionId: session.id,
|
|
8066
|
+
scope: session.scope,
|
|
8067
|
+
accessToken: session.accessToken,
|
|
8068
|
+
refreshToken: session.refreshToken,
|
|
8069
|
+
},
|
|
8070
|
+
});
|
|
8071
|
+
}
|
|
8072
|
+
persist();
|
|
8073
|
+
entry.handle.setTokens(session.accessToken, session.refreshToken);
|
|
8074
|
+
syncPill();
|
|
8075
|
+
if (options.reload ?? queryDevtoolsSettings().reloadOnAuthSwitch)
|
|
8076
|
+
reload();
|
|
8077
|
+
};
|
|
8078
|
+
/**
|
|
8079
|
+
* Logs in as one account, through the provider's own login query - so the tokens are issued the way the
|
|
8080
|
+
* application issues them, and the vault picks the session up like any other login.
|
|
8081
|
+
*/
|
|
8082
|
+
const loginQueryDevtoolsAuthAccount = (accountId) => {
|
|
8083
|
+
const account = queryDevtoolsAuthProviders()
|
|
8084
|
+
.flatMap((provider) => queryDevtoolsAuthAccountsFor(provider))
|
|
8085
|
+
.find((entry) => entry.id === accountId);
|
|
8086
|
+
if (!account?.ready)
|
|
8087
|
+
return;
|
|
8088
|
+
const entry = live.get(account.provider);
|
|
8089
|
+
const query = entry?.handle.queries?.[account.loginQuery];
|
|
8090
|
+
if (!entry || !query)
|
|
8091
|
+
return;
|
|
8092
|
+
clearClientData(entry);
|
|
8093
|
+
setActive(account.provider, null);
|
|
8094
|
+
persist();
|
|
8095
|
+
query.execute(account.buildArgs?.(account.values) ?? { body: account.values });
|
|
8096
|
+
};
|
|
8097
|
+
/** Ends the live session without forgetting it, so it can be switched back to. */
|
|
8098
|
+
const logoutQueryDevtoolsAuthSession = (provider) => {
|
|
8099
|
+
live.get(provider)?.handle.logout();
|
|
8100
|
+
};
|
|
8101
|
+
/**
|
|
8102
|
+
* Makes this tab's session its own, or hands it back to its siblings. Both reload: a tab that keeps its
|
|
8103
|
+
* own session runs no `BroadcastChannel` sync and writes no persistence cookie, and both are decided
|
|
8104
|
+
* while the provider is built.
|
|
8105
|
+
*/
|
|
8106
|
+
const setQueryDevtoolsAuthTabLocal = (options) => {
|
|
8107
|
+
const { provider } = options;
|
|
8108
|
+
const seeds = readSeeds();
|
|
8109
|
+
if (!options.tabLocal) {
|
|
8110
|
+
const { [provider]: _dropped, ...rest } = seeds;
|
|
8111
|
+
writeSeeds(rest);
|
|
8112
|
+
reload();
|
|
8113
|
+
return;
|
|
8114
|
+
}
|
|
8115
|
+
const entry = live.get(provider);
|
|
8116
|
+
const accessToken = entry?.handle.accessToken();
|
|
8117
|
+
const refreshToken = entry?.handle.refreshToken();
|
|
8118
|
+
if (!accessToken || !refreshToken)
|
|
8119
|
+
return;
|
|
8120
|
+
const sessionId = active()[provider] ?? null;
|
|
8121
|
+
if (!sessionId)
|
|
8122
|
+
return;
|
|
8123
|
+
writeSeeds({ ...seeds, [provider]: { sessionId, scope: scopeOf(), accessToken, refreshToken } });
|
|
8124
|
+
reload();
|
|
8125
|
+
};
|
|
8126
|
+
/** What the floating pill offers, rebuilt whenever any of it changes. */
|
|
8127
|
+
const syncPill = () => {
|
|
8128
|
+
const rows = untracked(() => providers().map((provider) => {
|
|
8129
|
+
const sessions = queryDevtoolsAuthSessionsFor(provider);
|
|
8130
|
+
const accounts = queryDevtoolsAuthAccountsFor(provider);
|
|
8131
|
+
const activeId = active()[provider] ?? null;
|
|
8132
|
+
const current = sessions.find((session) => session.id === activeId);
|
|
8133
|
+
return {
|
|
8134
|
+
name: provider,
|
|
8135
|
+
current: current?.label ?? (live.get(provider)?.handle.accessToken() ? 'unnamed session' : 'anonymous'),
|
|
8136
|
+
tabLocal: tabLocal()[provider] === true,
|
|
8137
|
+
options: [
|
|
8138
|
+
...sessions.map((session) => ({
|
|
8139
|
+
value: `session:${session.id}`,
|
|
8140
|
+
label: session.label,
|
|
8141
|
+
title: `Switch to ${session.label}`,
|
|
8142
|
+
selected: session.id === activeId,
|
|
8143
|
+
disabled: false,
|
|
8144
|
+
})),
|
|
8145
|
+
...accounts.map((account) => ({
|
|
8146
|
+
value: `account:${account.id}`,
|
|
8147
|
+
label: `log in as ${account.label}`,
|
|
8148
|
+
title: account.ready
|
|
8149
|
+
? `Logs in through ${account.loginQuery}`
|
|
8150
|
+
: 'Needs its credentials typed into the panel first',
|
|
8151
|
+
selected: false,
|
|
8152
|
+
disabled: !account.ready,
|
|
8153
|
+
})),
|
|
8154
|
+
],
|
|
8155
|
+
pick: (value) => {
|
|
8156
|
+
if (value.startsWith('session:'))
|
|
8157
|
+
switchQueryDevtoolsAuthSession({ sessionId: value.slice(8) });
|
|
8158
|
+
else if (value.startsWith('account:'))
|
|
8159
|
+
loginQueryDevtoolsAuthAccount(value.slice(8));
|
|
8160
|
+
},
|
|
8161
|
+
toggleTabLocal: () => setQueryDevtoolsAuthTabLocal({ provider, tabLocal: !tabLocal()[provider] }),
|
|
8162
|
+
};
|
|
8163
|
+
}));
|
|
8164
|
+
setQueryDevtoolsAuthPill({ rows: rows.filter((row) => row.options.length > 0) });
|
|
8165
|
+
};
|
|
8166
|
+
|
|
7176
8167
|
/** The neutral fault: nothing armed. */
|
|
7177
8168
|
const EMPTY_QUERY_DEVTOOLS_FAULT = {
|
|
7178
8169
|
latencyMs: 0,
|
|
@@ -8268,7 +9259,7 @@ const armQueryDevtoolsOverrideTransfer = (recorder, ops) => {
|
|
|
8268
9259
|
|
|
8269
9260
|
// Generated from package.json by tools/scripts/generate-version.js - do not edit.
|
|
8270
9261
|
/** The version of `@ethlete/query` this build was cut from. */
|
|
8271
|
-
const QUERY_VERSION = '6.0.0-next.
|
|
9262
|
+
const QUERY_VERSION = '6.0.0-next.41';
|
|
8272
9263
|
|
|
8273
9264
|
/** How deep a schema is expanded before a nested branch is given up on. */
|
|
8274
9265
|
const MAX_DEPTH = 12;
|
|
@@ -9561,6 +10552,10 @@ const provideQueryDevtools = (options) => {
|
|
|
9561
10552
|
initQueryDevtoolsMocks();
|
|
9562
10553
|
initQueryDevtoolsFaults();
|
|
9563
10554
|
setQueryDevtoolsSchemaLoader(options?.schema);
|
|
10555
|
+
setQueryDevtoolsApiEnvs(options?.apiEnvs);
|
|
10556
|
+
initQueryDevtoolsAuthSessions(options?.authAccounts);
|
|
10557
|
+
setQueryDevtoolsAuthSeedReader(readQueryDevtoolsAuthSeedFor);
|
|
10558
|
+
setQueryDevtoolsAuthProviderRegistrar(trackQueryDevtoolsAuthProvider);
|
|
9564
10559
|
setQueryDevtoolsRegistrar(registerEntry);
|
|
9565
10560
|
setQueryDevtoolsStatsFactory(createQueryDevtoolsStats);
|
|
9566
10561
|
setQueryDevtoolsFormLinksFactory(createQueryDevtoolsFormLinks);
|
|
@@ -11861,14 +12856,14 @@ class V2Query {
|
|
|
11861
12856
|
authProvider.tokens$.pipe(take(1)),
|
|
11862
12857
|
]).pipe(takeUntil(this.onAbort$), switchMap(([state, tokens]) => {
|
|
11863
12858
|
if (state && isQueryStateLoading(state)) {
|
|
11864
|
-
return EMPTY;
|
|
12859
|
+
return EMPTY$1;
|
|
11865
12860
|
}
|
|
11866
12861
|
if (isQueryStateFailure(state)) {
|
|
11867
12862
|
this.updateEntityState({ type: 'failure', error: state.error, headers: {} }, meta, options);
|
|
11868
|
-
return EMPTY;
|
|
12863
|
+
return EMPTY$1;
|
|
11869
12864
|
}
|
|
11870
12865
|
if (!state && !(tokens?.token || tokens?.refreshToken)) {
|
|
11871
|
-
return EMPTY;
|
|
12866
|
+
return EMPTY$1;
|
|
11872
12867
|
}
|
|
11873
12868
|
return of(true);
|
|
11874
12869
|
}), take(1));
|
|
@@ -15258,5 +16253,5 @@ const createWebSocketClient = (options) => {
|
|
|
15258
16253
|
* Generated bundle index. Do not edit.
|
|
15259
16254
|
*/
|
|
15260
16255
|
|
|
15261
|
-
export { AuthBearerRefreshStrategy, BasicAuthProvider, BearerAuthFeatureType, BooleanArrayQueryField, CustomHeaderAuthProvider, DEFAULT_KEEP_UNUSED_FOR, DEFAULT_QUERY_PERSISTENCE_MAX_AGE, DEFAULT_QUERY_PERSISTENCE_MAX_ENTRIES, DEFAULT_QUERY_PERSISTENCE_VERSION, DEFAULT_QUERY_PERSISTENCE_WRITE_DELAY, DateArrayQueryField, DateQueryField, EMPTY_QUERY_DEVTOOLS_FAULT, EntityStore, IGNORED_FILTER_COUNT_FIELDS, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, LegacyQuery, LegacyQueryCreator, MAX_QUERY_BATCH_TOMBSTONES, MAX_QUERY_DEVTOOLS_TOMBSTONES, MAX_UNUSED_ENTRIES, NumberArrayQueryField, ParseHttpErrorCodeToMessageDePipe, ParseHttpErrorCodeToMessageEnPipe, ParseHttpErrorCodeToTitleDePipe, ParseHttpErrorCodeToTitleEnPipe, QUERY_CLIENT_DEVTOOLS_TOKEN, QUERY_DEVTOOLS_FAULT_STATUSES, QUERY_DEVTOOLS_TOKEN_TTL_LIMIT, QUERY_PERSISTENCE_STORE_VERSION, QUERY_SYNC_PROTOCOL_VERSION, QUERY_VERSION, QueryClientFeatureType, QueryDevtoolsComponent, QueryDirective, QueryFeatureType, QueryField, QueryForm, QueryRuntimeErrorCode, QueryShortNamePipe, QueryStateType, QueryStore, SERVER_ERROR_KIND, SERVER_VIOLATION_ERROR_KIND, SPEED_BUFFER_TIME_IN_MS, SearchQueryField, SortQueryField, StringArrayQueryField, V2BearerAuthProvider, V2Query, V2QueryClient, V2QueryCreator, addQueryContainerHandling, applyQueryDevtoolsOverrides, applyQueryDevtoolsTokenTtl, applyQueryFeatures, armAllQueryDevtoolsMocks, armQueryDevtoolsMock, armQueryDevtoolsOverrideTransfer, authExtractTokensResponseMissingAccessToken, authExtractTokensResponseMissingRefreshToken, authExtractTokensResponseNotObject, authProviderFeatureUsedMultipleTimes, booleanArrayQueryField, buildQueryCacheKey, buildQueryString, buildRoute, buildTimestampFromSeconds, canOverrideQueryDevtoolsTokenTtl, castQueryCreatorTypes, circularQueryDependency, circularQueryDependencyChecker, clearQueryDevtoolsArmedMocks, clearQueryDevtoolsFaults, clearQueryDevtoolsMockStore, clearQueryDevtoolsOverrideStore, clearQueryDevtoolsStore, clearQueryDevtoolsTokenTtl, clearQueryDevtoolsTombstones, clearRestoredQueryDevtoolsOverrides, collectLeafPaths, collectQueryDevtoolsSchemaComponents, collectQueryFormLinks, computeQueryAuthHeader, computeQueryBody, computeQueryHeaders, computeQueryMethod, computeQueryQueryParams, consumeSuppressQueryStackDevtools, contentfulGqlLikePaginationAdapter, countUnresolvedQueryDevtoolsOverrides, createAuthGuard, createBaseQuery, createBaseQueryCreator, createBearerAuthProvider, createDefaultRetryFn, createDeleteQuery, createExecuteFn, createGetQuery, createGqlExecuteFn, createGqlMutationViaGet, createGqlMutationViaPost, createGqlQuery, createGqlQueryCreator, createGqlQueryViaGet, createGqlQueryViaPost, createHttpRequest, createIndexedDbQueryPersistenceAdapter, createInfinityQueryConfig, createLegacyQueryCreator, createNoopQueryPersistenceAdapter, createPagedQueryStack, createPatchQuery, createPersistentAuthFeature, createPostQuery, createPutQuery, createQuery, createQueryBatch, createQueryClient, createQueryCollection, createQueryCollectionSignal, createQueryCollectionSubject, createQueryContext, createQueryCreator, createQueryDevtoolsFormLinks, createQueryDevtoolsFormLinksRecorder, createQueryDevtoolsOverrides, createQueryDevtoolsOverridesRecorder, createQueryDevtoolsStats, createQueryDevtoolsStatsRecorder, createQueryErrorResponse, createQueryFeature, createQueryInvalidationFilter, createQueryKeyLockManager, createQueryObject, createQueryPersistenceEngine, createQueryRepository, createQuerySnapshotFn, createQueryStack, createQuerySubmission, createQuerySyncEngine, createQuerySyncTransport, createSecureDeleteQuery, createSecureExecuteFactory, createSecureExecuteFn, createSecureGetQuery, createSecureGqlExecuteFn, createSecureGqlMutationViaGet, createSecureGqlMutationViaPost, createSecureGqlQuery, createSecureGqlQueryCreator, createSecureGqlQueryViaGet, createSecureGqlQueryViaPost, createSecurePatchQuery, createSecurePostQuery, createSecurePutQuery, createSecureQuery, createSecureQueryCreator, createTrackingFeature, createWebSocketClient, currentQueryDevtoolsBatch, dateArrayQueryField, dateQueryField, decryptBearer, deepFreeze, def, defineQueryForm, deleteQueryDevtoolsMock, describeQueryDevtoolsFeatures, detectContentTypeHeader, detectPaginationShape, dynLikePaginationAdapter, effectComputed, emptyQueryArgs, ethletePaginationAdapter, executeUntilSettled, extractExpiresInSeconds, extractFormViolations, extractHtmlErrorMessage, extractQuery, fakePaginationAdapter, filterFailure, filterNull, filterQueryStates, filterSuccess, forEachHeader, formatQueryDevtoolsDuration, generateQueryDevtoolsNumberPreset, generateQueryDevtoolsSampleNumber, generateQueryDevtoolsStringPreset, getBearerAuthProviderName, getDefaultHeaders, getQueryClientName, getQueryFeatureUsage, getResponseUrl, ggLikePaginationAdapter, gql, gqlDataPropertyMissingInResponse, hasHeader, hasQueryDevtoolsOverridesAtPath, htmlErrorPayload, htmlQueryErrorParser, ignoreAutoRefresh, initQueryDevtoolsFaults, initQueryDevtoolsMocks, initQueryDevtoolsOverridePersistence, initQueryDevtoolsSettings, injectInfinityQueryResponseDelay, injectQueryContext, insertFrom, invalidBaseRouteError, invalidBodyError, invalidRouteError, invalidStateInsideSecureExecuteFactory, isAutoRefresh, isBasicAuthProvider, isBearerAuthProvider, isClassValidatorError, isCreateGqlQueryOptions, isCustomHeaderAuthProvider, isDateShapedLeaf, isEmptyString, isGqlQueryConfig, isHtmlErrorPayload, isLegacyQuery, isNaN$1 as isNaN, isPageOutOfRangeError, isQuery, isQueryCollection, isQueryDevtoolsEnabled, isQueryDevtoolsFaultArmed, isQueryDevtoolsRequestInterceptionEnabled, isQueryErrorResponse, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, isSymfonyError, isSymfonyFormViolationListError, isSymfonyListError, isSymfonyPagerfantaOutOfRangeError, isUnderInvalidatedUrl, legacyPrepareFallbackInjector, legacyPrepareWithoutInjectionContext, loadQueryDevtoolsSchema, mapToPaginated, mapViolationsToFormErrors, matchesQueryDevtoolsMockPattern, matchesQueryDevtoolsMockQuery, maybeExecute, measureQueryDevtoolsPayload, mergeHeaders, nestedEffect, noteQueryFormRead, numberArrayQueryField, pagedQueryStackNextPageCalledWithoutPreviousPage, pagedQueryStackPageBiggerThanTotalPages, pagedQueryStackPreviousPageCalledButAlreadyAtFirstPage, paginatedEntityValueUpdater, parseAllXhrResponseHeaders, parseHttpErrorCodeToMessageDe, parseHttpErrorCodeToMessageEn, parseHttpErrorCodeToTitleDe, parseHttpErrorCodeToTitleEn, parseQueryDevtoolsOverrideTransfer, parseQueryRoute, patchQueryDevtoolsTokenPayload, pathParamsMissingInRouteFunctionError, provideInfinityQueryResponseDelay, provideLegacyPrepareFallback, provideQueryClientForDevtools, provideQueryDevtools, queryArrayComputed, queryBatchAlreadyRunning, queryBatchWithArgsUsed, queryClientFeatureUsedMultipleTimes, queryComputed, queryComputedTillTruthy, queryDevtoolsAbout, queryDevtoolsArmedMocks, queryDevtoolsArmedMocksRestored, queryDevtoolsEntries, queryDevtoolsFaults, queryDevtoolsFaultsRestored, queryDevtoolsFnDetail, queryDevtoolsMockId, queryDevtoolsMocks, queryDevtoolsOverridePersistence, queryDevtoolsRequestPath, queryDevtoolsResponseHistory, queryDevtoolsRestoredOverridesScope, queryDevtoolsSchemaNames, queryDevtoolsSchemaRoutes, queryDevtoolsSchemaState, queryDevtoolsSettings, queryDevtoolsStorage, queryDevtoolsTokenTtls, queryErrorMessage, queryErrorMessages, queryExecute, queryFeatureUsedMultipleTimes, queryField, querySequence, querySequenceAlreadyRunning, queryStackTotalQueriesAndExpectedQueriesMismatch, queryStackWithArgsUsed, queryStackWithResponseUpdateUsed, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, queryStateErrorSignal, queryStateLoadingSignal, queryStateResponseSignal, queryStateSignal, readQueryDevtoolsStore, registerEthleteVersion, registerQueryDevtoolsEntry, registerQueryErrorParser, removeFrom, request, resetExecuteState, resetPageOnError, resolveInvalidationUrl, resolveQueryDevtoolsFault, resolveQueryDevtoolsFaultForAttempt, resolveQueryDevtoolsMock, resolveQueryDevtoolsMockForAttempt, restoredQueryDevtoolsOverrides, runDefaultQueryRetry, runInQueryDevtoolsBatch, runQueryErrorParsers, saveQueryDevtoolsMock, searchQueryField, seedQueryDevtoolsSchemaBody, seedQueryDevtoolsSchemaRoute, serializeBody, serializeQueryDevtoolsOverrideTransfer, setDefaultQueryRetryFn, setQueryDevtoolsAppInfo, setQueryDevtoolsArmedMocksScope, setQueryDevtoolsFault, setQueryDevtoolsFaultResolver, setQueryDevtoolsFaultsScope, setQueryDevtoolsFormLinksFactory, setQueryDevtoolsMockResolver, setQueryDevtoolsOverridePersistence, setQueryDevtoolsOverridesFactory, setQueryDevtoolsOverridesScope, setQueryDevtoolsRegistrar, setQueryDevtoolsResponseHistory, setQueryDevtoolsSchemaLoader, setQueryDevtoolsSettings, setQueryDevtoolsStatsFactory, setQueryDevtoolsTokenPayloadPatcher, setQueryDevtoolsTokenTtl, setupQueryDependencies, setupQueryExecuteState, setupQueryState, shouldAutoExecuteGqlQuery, shouldAutoExecuteQuery, shouldCacheQuery, shouldRetryRequest, silenceMissingWithArgsFeatureErrorUsedButWithArgsPresent, skipPaginationPageParamCalculator, smartDuplicateArrayItem, snapshotQueryDevtoolsHandle, sortQueryField, splitQueryConfig, stringArrayQueryField, stringifyQueryRouteParts, sumQueryDevtoolsStats, suppressNextQueryStackDevtools, switchQueryCollectionState, switchQueryState, symfonyQueryErrorParser, takeUntilResponse, toQuerySignal, toQuerySubject, tokensNotAvailableInsideAuthAndExec, tombstoneOf, transformArrayResponse, transformExecStateToQueryState, transformGql, transformMethod, transformPaginatedResponse, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray, uncacheableRequestHasAllowCacheParam, uncacheableRequestHasCacheKeyParam, unwrapQuerySyncMessage, v2BuildQueryCacheKey, v2ExtractExpiresInSeconds, v2ShouldCacheQuery, v2ShouldRetryRequest, validateWithQuery, validateWithV2Query, withArgs, withArgsQueryFeatureMissingButRouteIsFunction, withAuthenticationQuery, withAutoRefresh, withAutoRefreshUsedInManualQuery, withAutoRefreshUsedOnUnsupportedHttpMethod, withBearerAuthMultiTabSync, withDefaultRetry, withErrorHandling, withEthleteApiErrors, withHtmlErrorParsing, withInactivityLogout, withLogging, withLongPolling, withLongPollingUsedOnUnsupportedHttpMethod, withLongPollingUsedWithPolling, withMultiTabSync, withPageResetOnError, withPersistentAuth, withPolling, withPollingUsedOnUnsupportedHttpMethod, withQueryDevtoolsOverridePersistence, withQueryPersistence, withRefreshQuery, withResponseUpdate, withSuccessHandling, withSymfonyErrors, withTokenExpirationWarning, withTokenRevocation, withTracking, wrapAsObservableSignal, wrapQuerySyncMessage, writeQueryDevtoolsStore };
|
|
16256
|
+
export { AuthBearerRefreshStrategy, BasicAuthProvider, BearerAuthFeatureType, BooleanArrayQueryField, CustomHeaderAuthProvider, DEFAULT_KEEP_UNUSED_FOR, DEFAULT_QUERY_PERSISTENCE_MAX_AGE, DEFAULT_QUERY_PERSISTENCE_MAX_ENTRIES, DEFAULT_QUERY_PERSISTENCE_VERSION, DEFAULT_QUERY_PERSISTENCE_WRITE_DELAY, DateArrayQueryField, DateQueryField, EMPTY_QUERY_DEVTOOLS_FAULT, EntityStore, IGNORED_FILTER_COUNT_FIELDS, INFINITY_QUERY_TOKEN, InfinityQuery, InfinityQueryDirective, InfinityQueryTriggerDirective, LegacyQuery, LegacyQueryCreator, MAX_QUERY_BATCH_TOMBSTONES, MAX_QUERY_DEVTOOLS_TOMBSTONES, MAX_UNUSED_ENTRIES, NumberArrayQueryField, ParseHttpErrorCodeToMessageDePipe, ParseHttpErrorCodeToMessageEnPipe, ParseHttpErrorCodeToTitleDePipe, ParseHttpErrorCodeToTitleEnPipe, QUERY_CLIENT_DEVTOOLS_TOKEN, QUERY_DEVTOOLS_FAULT_STATUSES, QUERY_DEVTOOLS_TOKEN_TTL_LIMIT, QUERY_PERSISTENCE_STORE_VERSION, QUERY_SYNC_PROTOCOL_VERSION, QUERY_VERSION, QueryClientFeatureType, QueryDevtoolsComponent, QueryDirective, QueryFeatureType, QueryField, QueryForm, QueryRuntimeErrorCode, QueryShortNamePipe, QueryStateType, QueryStore, SERVER_ERROR_KIND, SERVER_VIOLATION_ERROR_KIND, SPEED_BUFFER_TIME_IN_MS, SearchQueryField, SortQueryField, StringArrayQueryField, V2BearerAuthProvider, V2Query, V2QueryClient, V2QueryCreator, addQueryContainerHandling, addQueryDevtoolsAuthAccount, applyQueryDevtoolsOverrides, applyQueryDevtoolsTokenTtl, applyQueryFeatures, armAllQueryDevtoolsMocks, armQueryDevtoolsMock, armQueryDevtoolsOverrideTransfer, authExtractTokensResponseMissingAccessToken, authExtractTokensResponseMissingRefreshToken, authExtractTokensResponseNotObject, authProviderFeatureUsedMultipleTimes, booleanArrayQueryField, buildQueryCacheKey, buildQueryString, buildRoute, buildTimestampFromSeconds, canOverrideQueryDevtoolsTokenTtl, castQueryCreatorTypes, circularQueryDependency, circularQueryDependencyChecker, clearQueryDevtoolsArmedMocks, clearQueryDevtoolsAuthCredentials, clearQueryDevtoolsAuthSessions, clearQueryDevtoolsFaults, clearQueryDevtoolsMockStore, clearQueryDevtoolsOverrideStore, clearQueryDevtoolsStore, clearQueryDevtoolsTokenTtl, clearQueryDevtoolsTombstones, clearRestoredQueryDevtoolsOverrides, collectLeafPaths, collectQueryDevtoolsSchemaComponents, collectQueryFormLinks, computeQueryAuthHeader, computeQueryBody, computeQueryHeaders, computeQueryMethod, computeQueryQueryParams, consumeSuppressQueryStackDevtools, contentfulGqlLikePaginationAdapter, countUnresolvedQueryDevtoolsOverrides, createAuthGuard, createBaseQuery, createBaseQueryCreator, createBearerAuthProvider, createDefaultRetryFn, createDeleteQuery, createExecuteFn, createGetQuery, createGqlExecuteFn, createGqlMutationViaGet, createGqlMutationViaPost, createGqlQuery, createGqlQueryCreator, createGqlQueryViaGet, createGqlQueryViaPost, createHttpRequest, createIndexedDbQueryPersistenceAdapter, createInfinityQueryConfig, createLegacyQueryCreator, createNoopQueryPersistenceAdapter, createPagedQueryStack, createPatchQuery, createPersistentAuthFeature, createPostQuery, createPutQuery, createQuery, createQueryBatch, createQueryClient, createQueryCollection, createQueryCollectionSignal, createQueryCollectionSubject, createQueryContext, createQueryCreator, createQueryDevtoolsFormLinks, createQueryDevtoolsFormLinksRecorder, createQueryDevtoolsOverrides, createQueryDevtoolsOverridesRecorder, createQueryDevtoolsStats, createQueryDevtoolsStatsRecorder, createQueryErrorResponse, createQueryFeature, createQueryInvalidationFilter, createQueryKeyLockManager, createQueryObject, createQueryPersistenceEngine, createQueryRepository, createQuerySnapshotFn, createQueryStack, createQuerySubmission, createQuerySyncEngine, createQuerySyncTransport, createSecureDeleteQuery, createSecureExecuteFactory, createSecureExecuteFn, createSecureGetQuery, createSecureGqlExecuteFn, createSecureGqlMutationViaGet, createSecureGqlMutationViaPost, createSecureGqlQuery, createSecureGqlQueryCreator, createSecureGqlQueryViaGet, createSecureGqlQueryViaPost, createSecurePatchQuery, createSecurePostQuery, createSecurePutQuery, createSecureQuery, createSecureQueryCreator, createTrackingFeature, createWebSocketClient, currentQueryDevtoolsBatch, dateArrayQueryField, dateQueryField, decryptBearer, deepFreeze, def, defineQueryForm, deleteQueryDevtoolsMock, describeQueryDevtoolsFeatures, detectContentTypeHeader, detectPaginationShape, dynLikePaginationAdapter, effectComputed, emptyQueryArgs, ethletePaginationAdapter, executeUntilSettled, extractExpiresInSeconds, extractFormViolations, extractHtmlErrorMessage, extractQuery, fakePaginationAdapter, filterFailure, filterNull, filterQueryStates, filterSuccess, forEachHeader, forgetQueryDevtoolsAuthSession, formatQueryDevtoolsDuration, generateQueryDevtoolsNumberPreset, generateQueryDevtoolsSampleNumber, generateQueryDevtoolsStringPreset, getBearerAuthProviderName, getDefaultHeaders, getQueryClientName, getQueryFeatureUsage, getResponseUrl, ggLikePaginationAdapter, gql, gqlDataPropertyMissingInResponse, hasHeader, hasQueryDevtoolsOverridesAtPath, htmlErrorPayload, htmlQueryErrorParser, ignoreAutoRefresh, initQueryDevtoolsAuthSessions, initQueryDevtoolsFaults, initQueryDevtoolsMocks, initQueryDevtoolsOverridePersistence, initQueryDevtoolsSettings, injectInfinityQueryResponseDelay, injectQueryContext, insertFrom, invalidBaseRouteError, invalidBodyError, invalidRouteError, invalidStateInsideSecureExecuteFactory, isAutoRefresh, isBasicAuthProvider, isBearerAuthProvider, isClassValidatorError, isCreateGqlQueryOptions, isCustomHeaderAuthProvider, isDateShapedLeaf, isEmptyString, isGqlQueryConfig, isHtmlErrorPayload, isLegacyQuery, isNaN$1 as isNaN, isPageOutOfRangeError, isQuery, isQueryCollection, isQueryDevtoolsEnabled, isQueryDevtoolsFaultArmed, isQueryDevtoolsRequestInterceptionEnabled, isQueryErrorResponse, isQueryStateCancelled, isQueryStateFailure, isQueryStateLoading, isQueryStatePrepared, isQueryStateSuccess, isRequestError, isSymfonyError, isSymfonyFormViolationListError, isSymfonyListError, isSymfonyPagerfantaOutOfRangeError, isUnderInvalidatedUrl, legacyPrepareFallbackInjector, legacyPrepareWithoutInjectionContext, loadQueryDevtoolsSchema, loginQueryDevtoolsAuthAccount, logoutQueryDevtoolsAuthSession, mapToPaginated, mapViolationsToFormErrors, matchesQueryDevtoolsMockPattern, matchesQueryDevtoolsMockQuery, maybeExecute, measureQueryDevtoolsPayload, mergeHeaders, nestedEffect, noteQueryFormRead, numberArrayQueryField, pagedQueryStackNextPageCalledWithoutPreviousPage, pagedQueryStackPageBiggerThanTotalPages, pagedQueryStackPreviousPageCalledButAlreadyAtFirstPage, paginatedEntityValueUpdater, parseAllXhrResponseHeaders, parseHttpErrorCodeToMessageDe, parseHttpErrorCodeToMessageEn, parseHttpErrorCodeToTitleDe, parseHttpErrorCodeToTitleEn, parseQueryDevtoolsOverrideTransfer, parseQueryRoute, patchQueryDevtoolsTokenPayload, pathParamsMissingInRouteFunctionError, provideInfinityQueryResponseDelay, provideLegacyPrepareFallback, provideQueryClientForDevtools, provideQueryDevtools, queryArrayComputed, queryBatchAlreadyRunning, queryBatchWithArgsUsed, queryClientFeatureUsedMultipleTimes, queryComputed, queryComputedTillTruthy, queryDevtoolsAbout, queryDevtoolsApiEnvIds, queryDevtoolsApiEnvIsProduction, queryDevtoolsApiEnvScope, queryDevtoolsApiEnvValues, queryDevtoolsApiEnvs, queryDevtoolsArmedMocks, queryDevtoolsArmedMocksRestored, queryDevtoolsAuthAccountsFor, queryDevtoolsAuthActive, queryDevtoolsAuthOtherScopeCount, queryDevtoolsAuthProviders, queryDevtoolsAuthSessions, queryDevtoolsAuthSessionsFor, queryDevtoolsAuthTabLocal, queryDevtoolsEntries, queryDevtoolsFaults, queryDevtoolsFaultsRestored, queryDevtoolsFnDetail, queryDevtoolsMockId, queryDevtoolsMocks, queryDevtoolsOverridePersistence, queryDevtoolsRequestPath, queryDevtoolsResponseHistory, queryDevtoolsRestoredOverridesScope, queryDevtoolsSchemaNames, queryDevtoolsSchemaRoutes, queryDevtoolsSchemaState, queryDevtoolsSettings, queryDevtoolsStorage, queryDevtoolsTokenTtls, queryErrorMessage, queryErrorMessages, queryExecute, queryFeatureUsedMultipleTimes, queryField, querySequence, querySequenceAlreadyRunning, queryStackTotalQueriesAndExpectedQueriesMismatch, queryStackWithArgsUsed, queryStackWithResponseUpdateUsed, queryStateAlreadyHasKeyError, queryStateCannotTransformError, queryStateDoesNotContainKeyError, queryStateErrorSignal, queryStateLoadingSignal, queryStateResponseSignal, queryStateSignal, readQueryDevtoolsAuthSeed, readQueryDevtoolsAuthSeedFor, readQueryDevtoolsStore, registerEthleteVersion, registerQueryDevtoolsAuthProvider, registerQueryDevtoolsEntry, registerQueryErrorParser, removeFrom, removeQueryDevtoolsAuthAccount, renameQueryDevtoolsAuthSession, request, resetExecuteState, resetPageOnError, resolveInvalidationUrl, resolveQueryDevtoolsFault, resolveQueryDevtoolsFaultForAttempt, resolveQueryDevtoolsMock, resolveQueryDevtoolsMockForAttempt, restoredQueryDevtoolsOverrides, runDefaultQueryRetry, runInQueryDevtoolsBatch, runQueryErrorParsers, saveQueryDevtoolsMock, searchQueryField, seedQueryDevtoolsSchemaBody, seedQueryDevtoolsSchemaRoute, serializeBody, serializeQueryDevtoolsOverrideTransfer, setDefaultQueryRetryFn, setQueryDevtoolsApiEnv, setQueryDevtoolsApiEnvs, setQueryDevtoolsAppInfo, setQueryDevtoolsArmedMocksScope, setQueryDevtoolsAuthCredentials, setQueryDevtoolsAuthProviderRegistrar, setQueryDevtoolsAuthSeedReader, setQueryDevtoolsAuthTabLocal, setQueryDevtoolsFault, setQueryDevtoolsFaultResolver, setQueryDevtoolsFaultsScope, setQueryDevtoolsFormLinksFactory, setQueryDevtoolsMockResolver, setQueryDevtoolsOverridePersistence, setQueryDevtoolsOverridesFactory, setQueryDevtoolsOverridesScope, setQueryDevtoolsRegistrar, setQueryDevtoolsResponseHistory, setQueryDevtoolsSchemaLoader, setQueryDevtoolsSettings, setQueryDevtoolsStatsFactory, setQueryDevtoolsTokenPayloadPatcher, setQueryDevtoolsTokenTtl, setupQueryDependencies, setupQueryExecuteState, setupQueryState, shouldAutoExecuteGqlQuery, shouldAutoExecuteQuery, shouldCacheQuery, shouldRetryRequest, silenceMissingWithArgsFeatureErrorUsedButWithArgsPresent, skipPaginationPageParamCalculator, smartDuplicateArrayItem, snapshotQueryDevtoolsHandle, sortQueryField, splitQueryConfig, stringArrayQueryField, stringifyQueryRouteParts, sumQueryDevtoolsStats, suppressNextQueryStackDevtools, switchQueryCollectionState, switchQueryDevtoolsAuthSession, switchQueryState, symfonyQueryErrorParser, takeUntilResponse, toQuerySignal, toQuerySubject, tokensNotAvailableInsideAuthAndExec, tombstoneOf, trackQueryDevtoolsAuthProvider, transformArrayResponse, transformExecStateToQueryState, transformGql, transformMethod, transformPaginatedResponse, transformToBoolean, transformToBooleanArray, transformToDate, transformToDateArray, transformToNumber, transformToNumberArray, transformToSort, transformToSortQueryParam, transformToString, transformToStringArray, uncacheableRequestHasAllowCacheParam, uncacheableRequestHasCacheKeyParam, unwrapQuerySyncMessage, v2BuildQueryCacheKey, v2ExtractExpiresInSeconds, v2ShouldCacheQuery, v2ShouldRetryRequest, validateWithQuery, validateWithV2Query, withArgs, withArgsQueryFeatureMissingButRouteIsFunction, withAuthenticationQuery, withAutoRefresh, withAutoRefreshUsedInManualQuery, withAutoRefreshUsedOnUnsupportedHttpMethod, withBearerAuthMultiTabSync, withDefaultRetry, withErrorHandling, withEthleteApiErrors, withHtmlErrorParsing, withInactivityLogout, withLogging, withLongPolling, withLongPollingUsedOnUnsupportedHttpMethod, withLongPollingUsedWithPolling, withMultiTabSync, withPageResetOnError, withPersistentAuth, withPolling, withPollingUsedOnUnsupportedHttpMethod, withQueryDevtoolsOverridePersistence, withQueryPersistence, withRefreshQuery, withResponseUpdate, withSuccessHandling, withSymfonyErrors, withTokenExpirationWarning, withTokenRevocation, withTracking, wrapAsObservableSignal, wrapQuerySyncMessage, writeQueryDevtoolsStore };
|
|
15262
16257
|
//# sourceMappingURL=ethlete-query.mjs.map
|