@ethlete/query 6.0.0-next.52 → 6.0.0-next.53
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 +43 -0
- package/fesm2022/ethlete-query-ngrx-toolkit.mjs +96 -24
- package/fesm2022/ethlete-query-ngrx-toolkit.mjs.map +1 -1
- package/fesm2022/ethlete-query-testing.mjs +67 -25
- package/fesm2022/ethlete-query-testing.mjs.map +1 -1
- package/fesm2022/ethlete-query.mjs +7816 -7351
- package/fesm2022/ethlete-query.mjs.map +1 -1
- package/generators/migrate-from-ngrx-toolkit/consumers.js +70 -2
- package/generators/migrate-from-ngrx-toolkit/migration.js +2 -1
- package/generators/migrate-to-query-v3/cleanup-migration.js +142 -53
- package/generators/migrate-to-query-v3/migration-scope.js +2 -0
- package/generators/migrate-to-query-v3/migration.js +3 -3
- package/generators/migrate-to-query-v3/module-graph.js +39 -29
- package/generators/migrate-to-query-v3/query-client-migration.js +138 -124
- package/generators/migrate-to-query-v3/rename-symbols.js +38 -11
- package/generators/migrate-to-query-v3/report.js +40 -7
- package/generators/migrate-to-query-v3/shared.js +19 -1
- package/generators/prep-for-query-v3/migration.js +317 -56
- package/package.json +1 -1
- package/types/ethlete-query-testing.d.ts +28 -11
- package/types/ethlete-query.d.ts +679 -569
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @ethlete/query
|
|
2
2
|
|
|
3
|
+
## 6.0.0-next.53
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- Calling a query creator whose route uses `pathParams` without `withArgs` is now a type error; opt out with the literal `silenceMissingWithArgsFeatureError: true`, and let a stored `withArgs` feature's type be inferred rather than annotated `QueryFeature`.
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- Add the `reportErrors` creator option: set it to `false` to keep a query's failed requests away from Angular's `ErrorHandler`.
|
|
12
|
+
- `tableSortQueryField()` holds the `et-table` sort and keeps it in the URL, so `[(sort)]="qf.fields.sort().value"` binds it with no mapping.
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Legacy v2 queries that differ only in headers are now cached separately, `InfinityQuery` retries a failed page instead of skipping it, and `poll()` works again after `stopPolling()` ran while polling was blur-paused.
|
|
17
|
+
- An aborted execution no longer counts as a success: `createQuerySubmission` skips `onSuccess`, `querySequence` stops with `error: null`, and `executeUntilSettled` on a destroyed query resolves a cancelled snapshot instead of throwing NG0205.
|
|
18
|
+
- Auth: `createPersistentAuthFeature` and `createTrackingFeature` run in the feature context's injector, so calling them outside an injection context no longer throws NG0203.
|
|
19
|
+
- Auth: `AuthQueryConfig.retryFn` is now part of the published type declarations.
|
|
20
|
+
- Auth fixes: `redirectOnSessionEnd` fires with `withTokenRevocation`, only the tab a logout started in revokes tokens, a logout during an in-flight revocation still revokes, and multi-tab sync is inert on the server.
|
|
21
|
+
- Auth: the `withPersistentAuth` guide now says the remember-me cookie token is obfuscated with a per-origin key, not encrypted.
|
|
22
|
+
- Query batch: `retryFailed()` no longer resends a mutation that an unsubscribe aborted in flight, since the server may already have applied it.
|
|
23
|
+
- Query: `queryParams` written in a different key order now share one cache entry, in the current and the legacy client, while the URL keeps the written order.
|
|
24
|
+
- `prep-for-query-v3` rewrites `CLEAR_QUERY_ARGS` to `null` and drops its import, and warns about each `withArgs` callback that returns `null`, which now parks the query instead of keeping the previous args.
|
|
25
|
+
- A `Date` in `queryParams` is now sent as its ISO string and keys the cache, retries resolve headers again per attempt, and a root-path devtools override no longer invents a response while nothing has settled.
|
|
26
|
+
- The devtools serialize a response body once for the stats and the event log, and an event row finds its query only when it renders.
|
|
27
|
+
- Devtools mocks now match clients whose `baseUrl` has a path, batch item tombstones stay bounded, bad stored overrides and vault values are ignored, and `setupAuthTest().refresh` honours `buildRefreshArgs`.
|
|
28
|
+
- A query error's `retryState.retry` now says whether a manual retry is worth offering, even after automatic retries are exhausted or without `withDefaultRetry()`; a configured retry policy alone decides it, so `<et-query-error>` shows Retry without `alwaysAllowRetry`.
|
|
29
|
+
- Two signal query forms on one route that commit in the same tick no longer re-parse their own URL write, so no spurious commit, refetch or dropped debounce follows and a committed `Date` keeps its milliseconds.
|
|
30
|
+
- `migrate-to-query-v3` now renames only identifiers bound to the migrated client, keeps default and `type` imports, resolves `.js` and `baseUrl` imports, rewrites `.prepare()` only on legacy creators, and merges a scoped run into the existing report.
|
|
31
|
+
- `execute({ args })` without `withArgs` now records the args so a bare `execute()` re-sends them and polling does not execute twice; refreshes update `lastTimeExecutedAt()` and `triggeredBy()`, and `subtle.setResponse` no longer re-runs `transformResponse`.
|
|
32
|
+
- Legacy `[etInfinityQuery]`: `canLoadMore` stays `true` while the current page has failed or is retrying, so a trigger shown only when it is `true` can retry the first or last page.
|
|
33
|
+
- A secure interop query that waits for a token no longer reports its own request as an `auto` refresh once the token arrives; the load keeps the trigger it was started with.
|
|
34
|
+
- Auth: a negative numeric `refreshStrategy` now counts as `0`, so the proactive refresh fires at token expiry instead of after it.
|
|
35
|
+
- The ngrx-toolkit interop no longer shares a handle between `FormData`, `Blob`, `Map` or `Set` args it cannot tell apart, keys `Date` args by time value, and every `toolkitCall` sends its own headers and args.
|
|
36
|
+
- `migrate-from-ngrx-toolkit` now reports `on()` / `once()` calls on a migrated facade as `NTK-FACADE-ON`, and `NTK-UNCONVERTED-ACTION-GROUP` says the file does not typecheck against migrated facades.
|
|
37
|
+
- A response that cannot be structured-cloned is now skipped with a dev warning instead of disabling persistence writes; in `@ethlete/query/testing`, the fake `BroadcastChannel` delivers as a task (await `flushMultiTabSync()`) and fake store reads are consistent.
|
|
38
|
+
- Auth: `withPersistentAuth` no longer sends a garbled token, or deletes the shared cookie, when a sibling subdomain finds a remember-me cookie another origin wrote under `cookie.domain`.
|
|
39
|
+
- `prep-for-query-v3` now handles re-exported `CLEAR_QUERY_ARGS`, leaves shadowing local names alone, and checks `withArgs` callbacks passed by reference, listing the ones it cannot follow.
|
|
40
|
+
- Query repository: the `unbind` JSDoc now says an entry with a response is kept for `keepUnusedFor` instead of destroyed.
|
|
41
|
+
- `withTokenRevocation` no longer sends a logout revocation queued behind another one when its tokens are live again by the time its turn comes.
|
|
42
|
+
- Paged query stacks now report `isLastPageLoaded` for empty and shrunk results and allow `fetchNextPage()` during a load with `blockExecutionDuringLoading: false`; a query batch unsubscribed mid-flight settles as `cancelled` and `retryFailed()` resends it.
|
|
43
|
+
- `refresh()` and `startPolling()` on a toolkit handle a newer unhashable-args call released now do nothing and warn once, naming the release, instead of warning on every call or polling tick.
|
|
44
|
+
- The web socket client now reconnects with backoff and the current `auth` after a server disconnect or rejected handshake, resets its backoff after 10 s of uptime, and opens no connection during a server render.
|
|
45
|
+
|
|
3
46
|
## 6.0.0-next.52
|
|
4
47
|
|
|
5
48
|
### Major Changes
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ChangeDetectorRef, DestroyRef, Pipe, NgModule, signal, computed, effect, untracked, EnvironmentInjector, assertInInjectionContext } from '@angular/core';
|
|
3
|
-
import { Subscription, combineLatest, startWith, map, Subject, defer, distinctUntilChanged, interval, takeUntil, finalize, filter } from 'rxjs';
|
|
2
|
+
import { inject, ChangeDetectorRef, DestroyRef, Pipe, NgModule, signal, computed, effect, untracked, isDevMode, EnvironmentInjector, assertInInjectionContext } from '@angular/core';
|
|
3
|
+
import { Subscription, combineLatest, startWith, map, Subject, defer, of, distinctUntilChanged, interval, takeUntil, finalize, filter } from 'rxjs';
|
|
4
|
+
import { resolveQueryHeaders } from '@ethlete/query';
|
|
4
5
|
|
|
5
6
|
const createToolkitPipeTransform = (options) => {
|
|
6
7
|
const cdr = inject(ChangeDetectorRef);
|
|
@@ -149,6 +150,10 @@ const toToolkitError = (error) => ({
|
|
|
149
150
|
message: error.raw.message,
|
|
150
151
|
data: error.raw.error ?? null,
|
|
151
152
|
});
|
|
153
|
+
const handleReleasedMessage = (method) => `${method}() was called on a toolkit handle that a newer toolkitCall() released. Args that cannot be hashed ` +
|
|
154
|
+
`(FormData, Blob, Map, ...) never share a handle, so the next call for the same creator releases the previous ` +
|
|
155
|
+
`handle once its request settles. The call was ignored - call toolkitCall() again with the args instead of ` +
|
|
156
|
+
`reusing the old handle. This warning is shown once per handle.`;
|
|
152
157
|
const toQueryArgs = (args) => {
|
|
153
158
|
const actionOptions = args['actionOptions'];
|
|
154
159
|
const queryArgs = {};
|
|
@@ -162,9 +167,8 @@ const toQueryArgs = (args) => {
|
|
|
162
167
|
queryArgs['headers'] = actionOptions.headers;
|
|
163
168
|
return queryArgs;
|
|
164
169
|
};
|
|
165
|
-
const createToolkitHandle = (creator,
|
|
170
|
+
const createToolkitHandle = (creator, injector) => {
|
|
166
171
|
const query = creator({ injector, onlyManualExecution: true, silenceMissingWithArgsFeatureError: true });
|
|
167
|
-
const queryArgs = toQueryArgs(args);
|
|
168
172
|
const calledArgs = signal(null, /* @ts-ignore */
|
|
169
173
|
...(ngDevMode ? [{ debugName: "calledArgs" }] : /* istanbul ignore next */ []));
|
|
170
174
|
const toolkitError = computed(() => {
|
|
@@ -188,23 +192,37 @@ const createToolkitHandle = (creator, args, injector) => {
|
|
|
188
192
|
}, /* @ts-ignore */
|
|
189
193
|
...(ngDevMode ? [{ debugName: "snapshot" }] : /* istanbul ignore next */ []));
|
|
190
194
|
const changes = new Subject();
|
|
191
|
-
effect(() => {
|
|
195
|
+
const changesEffect = effect(() => {
|
|
192
196
|
const current = snapshot();
|
|
193
197
|
untracked(() => changes.next(current));
|
|
194
|
-
}, { injector });
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
}, { ...(ngDevMode ? { debugName: "changesEffect" } : /* istanbul ignore next */ {}), injector });
|
|
199
|
+
let releasedSnapshot = null;
|
|
200
|
+
let warnedAboutRelease = false;
|
|
201
|
+
const ignoredAfterRelease = (method) => {
|
|
202
|
+
if (!releasedSnapshot)
|
|
203
|
+
return false;
|
|
204
|
+
if (isDevMode() && !warnedAboutRelease) {
|
|
205
|
+
warnedAboutRelease = true;
|
|
206
|
+
console.warn(handleReleasedMessage(method));
|
|
207
|
+
}
|
|
208
|
+
return true;
|
|
209
|
+
};
|
|
210
|
+
const select = (pick) => defer(() => (releasedSnapshot ? of(releasedSnapshot) : changes.pipe(startWith(untracked(snapshot))))).pipe(map(pick), distinctUntilChanged());
|
|
211
|
+
const call = (args) => {
|
|
197
212
|
calledArgs.set(args);
|
|
198
|
-
query.execute({ args:
|
|
213
|
+
query.execute({ args: toQueryArgs(args) });
|
|
199
214
|
};
|
|
200
215
|
const refresh = () => {
|
|
201
|
-
if (
|
|
202
|
-
|
|
216
|
+
if (ignoredAfterRelease('refresh'))
|
|
217
|
+
return;
|
|
218
|
+
const lastArgs = untracked(calledArgs);
|
|
219
|
+
if (lastArgs)
|
|
220
|
+
call(lastArgs);
|
|
203
221
|
};
|
|
204
222
|
let pollingSubscription = Subscription.EMPTY;
|
|
205
223
|
const stopPolling = () => pollingSubscription.unsubscribe();
|
|
206
224
|
const startPolling = ({ intervalDuration, killSwitch }) => {
|
|
207
|
-
if (!pollingSubscription.closed)
|
|
225
|
+
if (ignoredAfterRelease('startPolling') || !pollingSubscription.closed)
|
|
208
226
|
return;
|
|
209
227
|
pollingSubscription = interval(intervalDuration)
|
|
210
228
|
.pipe(takeUntil(killSwitch), finalize(() => (pollingSubscription = Subscription.EMPTY)))
|
|
@@ -215,10 +233,26 @@ const createToolkitHandle = (creator, args, injector) => {
|
|
|
215
233
|
calledArgs.set(null);
|
|
216
234
|
query.reset();
|
|
217
235
|
};
|
|
218
|
-
injector.get(DestroyRef).onDestroy(() => {
|
|
236
|
+
const removeDestroyListener = injector.get(DestroyRef).onDestroy(() => {
|
|
219
237
|
stopPolling();
|
|
220
238
|
changes.complete();
|
|
221
239
|
});
|
|
240
|
+
const release = () => {
|
|
241
|
+
releasedSnapshot = untracked(snapshot);
|
|
242
|
+
stopPolling();
|
|
243
|
+
changesEffect.destroy();
|
|
244
|
+
removeDestroyListener();
|
|
245
|
+
changes.complete();
|
|
246
|
+
query.subtle.destroy();
|
|
247
|
+
};
|
|
248
|
+
const releaseWhenSettled = () => {
|
|
249
|
+
const releaseEffect = effect(() => {
|
|
250
|
+
if (snapshot().callState === CallState.LOADING)
|
|
251
|
+
return;
|
|
252
|
+
releaseEffect.destroy();
|
|
253
|
+
untracked(release);
|
|
254
|
+
}, { ...(ngDevMode ? { debugName: "releaseEffect" } : /* istanbul ignore next */ {}), injector });
|
|
255
|
+
};
|
|
222
256
|
const response$ = select((current) => current.response);
|
|
223
257
|
const handle = {
|
|
224
258
|
response$,
|
|
@@ -236,20 +270,52 @@ const createToolkitHandle = (creator, args, injector) => {
|
|
|
236
270
|
startPolling,
|
|
237
271
|
stopPolling,
|
|
238
272
|
};
|
|
239
|
-
return { handle, call };
|
|
273
|
+
return { handle, call, releaseWhenSettled };
|
|
240
274
|
};
|
|
241
275
|
|
|
242
276
|
const registries = /* @__PURE__ */ new WeakMap();
|
|
243
|
-
|
|
277
|
+
class UnhashableValueError extends Error {
|
|
278
|
+
}
|
|
279
|
+
const isPlainObject = (value) => {
|
|
280
|
+
const prototype = Object.getPrototypeOf(value);
|
|
281
|
+
return prototype === Object.prototype || prototype === null;
|
|
282
|
+
};
|
|
283
|
+
const hashableValue = (value) => {
|
|
244
284
|
if (Array.isArray(value))
|
|
245
|
-
return value.map(
|
|
285
|
+
return value.map(hashableValue);
|
|
246
286
|
if (typeof value !== 'object' || value === null)
|
|
247
287
|
return value;
|
|
288
|
+
if (value instanceof Date)
|
|
289
|
+
return { '\u0000date': value.getTime() };
|
|
290
|
+
if (!isPlainObject(value))
|
|
291
|
+
throw new UnhashableValueError();
|
|
248
292
|
return Object.fromEntries(Object.keys(value)
|
|
249
293
|
.sort()
|
|
250
|
-
.map((key) => [key,
|
|
294
|
+
.map((key) => [key, hashableValue(value[key])]));
|
|
295
|
+
};
|
|
296
|
+
const hashableHeaders = (headers) => {
|
|
297
|
+
const resolved = resolveQueryHeaders(headers);
|
|
298
|
+
return resolved
|
|
299
|
+
?.keys()
|
|
300
|
+
.map((name) => name.toLowerCase())
|
|
301
|
+
.sort()
|
|
302
|
+
.map((name) => [name, resolved.getAll(name)]);
|
|
303
|
+
};
|
|
304
|
+
/**
|
|
305
|
+
* Returns `null` for args holding a value JSON cannot represent (FormData, Blob, Map, ...). Those never share a handle,
|
|
306
|
+
* and each one is released once the next one for the same creator starts and its own call has settled.
|
|
307
|
+
*/
|
|
308
|
+
const hashArgs = (args) => {
|
|
309
|
+
const { headers, ...rest } = args;
|
|
310
|
+
try {
|
|
311
|
+
return JSON.stringify([hashableValue(rest), hashableHeaders(headers) ?? null]);
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
if (error instanceof UnhashableValueError)
|
|
315
|
+
return null;
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
251
318
|
};
|
|
252
|
-
const hashArgs = (args) => JSON.stringify(sortedValue(args ?? null));
|
|
253
319
|
const resolveEnvironmentInjector = (fn, options) => {
|
|
254
320
|
if (options?.injector)
|
|
255
321
|
return options.injector.get(EnvironmentInjector);
|
|
@@ -259,18 +325,24 @@ const resolveEnvironmentInjector = (fn, options) => {
|
|
|
259
325
|
const resolveEntry = (creator, args, injector) => {
|
|
260
326
|
let registry = registries.get(injector);
|
|
261
327
|
if (!registry) {
|
|
262
|
-
registry = new WeakMap();
|
|
328
|
+
registry = { shared: new WeakMap(), latestUnshared: new WeakMap() };
|
|
263
329
|
registries.set(injector, registry);
|
|
264
330
|
}
|
|
265
|
-
|
|
331
|
+
const key = hashArgs(toQueryArgs(args));
|
|
332
|
+
if (key === null) {
|
|
333
|
+
const entry = createToolkitHandle(creator, injector);
|
|
334
|
+
registry.latestUnshared.get(creator)?.releaseWhenSettled();
|
|
335
|
+
registry.latestUnshared.set(creator, entry);
|
|
336
|
+
return entry;
|
|
337
|
+
}
|
|
338
|
+
let entries = registry.shared.get(creator);
|
|
266
339
|
if (!entries) {
|
|
267
340
|
entries = new Map();
|
|
268
|
-
registry.set(creator, entries);
|
|
341
|
+
registry.shared.set(creator, entries);
|
|
269
342
|
}
|
|
270
|
-
const key = hashArgs(toQueryArgs(args));
|
|
271
343
|
let entry = entries.get(key);
|
|
272
344
|
if (!entry) {
|
|
273
|
-
entry = createToolkitHandle(creator,
|
|
345
|
+
entry = createToolkitHandle(creator, injector);
|
|
274
346
|
entries.set(key, entry);
|
|
275
347
|
}
|
|
276
348
|
return entry;
|
|
@@ -301,7 +373,7 @@ const toolkitSelect = (creator, args, options) => resolveEntry(creator, args, re
|
|
|
301
373
|
*/
|
|
302
374
|
const toolkitCall = (creator, args, options) => {
|
|
303
375
|
const entry = resolveEntry(creator, args, resolveEnvironmentInjector(toolkitCall, options));
|
|
304
|
-
entry.call();
|
|
376
|
+
entry.call(args);
|
|
305
377
|
return entry.handle;
|
|
306
378
|
};
|
|
307
379
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ethlete-query-ngrx-toolkit.mjs","sources":["../../../../libs/query/ngrx-toolkit/toolkit-pipe-transform.ts","../../../../libs/query/ngrx-toolkit/suspense.pipe.ts","../../../../libs/query/ngrx-toolkit/suspense-multi.pipe.ts","../../../../libs/query/ngrx-toolkit/ngrx-toolkit.module.ts","../../../../libs/query/ngrx-toolkit/toolkit-types.ts","../../../../libs/query/ngrx-toolkit/toolkit-handle.ts","../../../../libs/query/ngrx-toolkit/toolkit-call.ts","../../../../libs/query/ngrx-toolkit/toolkit-join.ts","../../../../libs/query/ngrx-toolkit/ethlete-query-ngrx-toolkit.ts"],"sourcesContent":["import { ChangeDetectorRef, DestroyRef, inject } from '@angular/core';\nimport { Observable, Subscription } from 'rxjs';\n\nexport type CreateToolkitPipeTransformOptions<TValue, TState> = {\n initialState: TState;\n disposedState: TState;\n state$: (value: TValue) => Observable<TState>;\n};\n\nexport const createToolkitPipeTransform = <TValue extends object, TState>(\n options: CreateToolkitPipeTransformOptions<TValue, TState>,\n) => {\n const cdr = inject(ChangeDetectorRef);\n let subscription = Subscription.EMPTY;\n let current: TValue | null = null;\n let state = options.initialState;\n\n const dispose = () => {\n subscription.unsubscribe();\n current = null;\n state = options.disposedState;\n };\n\n const subscribe = (value: TValue) => {\n current = value;\n subscription = options.state$(value).subscribe((next) => {\n state = next;\n cdr.markForCheck();\n });\n };\n\n inject(DestroyRef).onDestroy(dispose);\n\n return (value: TValue | null | undefined) => {\n if (!current && value) subscribe(value);\n\n if (current !== value) {\n dispose();\n if (value) subscribe(value);\n }\n\n return state;\n };\n};\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { combineLatest, map, Observable, startWith } from 'rxjs';\nimport { createToolkitPipeTransform } from './toolkit-pipe-transform';\nimport { AnyMappedEntityState, ToolkitPollingOptions } from './toolkit-types';\n\ntype ExtractFrom<T> = T extends Observable<infer U> ? U : never;\n\n/** What `suspense` and `suspenseMulti` render for a handle: every stream's current value, plus the handle's methods. */\nexport type ToolkitSuspenseState<T extends AnyMappedEntityState | null | undefined> = T extends AnyMappedEntityState\n ? {\n args: ExtractFrom<T['args$']>;\n cachedResponse: ExtractFrom<T['cachedResponse$']> | null;\n callState: ExtractFrom<T['callState$']>;\n error: ExtractFrom<T['error$']>;\n isError: ExtractFrom<T['isError$']>;\n isInit: ExtractFrom<T['isInit$']>;\n isLoading: ExtractFrom<T['isLoading$']>;\n isSuccess: ExtractFrom<T['isSuccess$']>;\n response: ExtractFrom<T['response$']>;\n timestamp: ExtractFrom<T['timestamp$']>;\n refresh: () => void;\n remove: () => void;\n startPolling: (options: ToolkitPollingOptions) => void;\n stopPolling: () => void;\n }\n : T;\n\ntype AnySuspenseState = ToolkitSuspenseState<AnyMappedEntityState>;\n\nconst noop = () => undefined;\n\nconst DEFAULT_STATE: AnySuspenseState = {\n args: null,\n cachedResponse: null,\n callState: null,\n error: null,\n isError: false,\n isInit: false,\n isLoading: false,\n isSuccess: false,\n response: null,\n timestamp: null,\n refresh: noop,\n remove: noop,\n startPolling: noop,\n stopPolling: noop,\n};\n\nexport const suspenseState$ = (handle: AnyMappedEntityState): Observable<AnySuspenseState> =>\n combineLatest({\n args: handle.args$,\n cachedResponse: handle.cachedResponse$.pipe(startWith(null)),\n callState: handle.callState$,\n error: handle.error$,\n isError: handle.isError$,\n isInit: handle.isInit$,\n isLoading: handle.isLoading$,\n isSuccess: handle.isSuccess$,\n response: handle.response$,\n timestamp: handle.timestamp$,\n }).pipe(\n map((values) => ({\n ...values,\n refresh: handle.refresh,\n remove: handle.remove,\n startPolling: handle.startPolling,\n stopPolling: handle.stopPolling,\n })),\n );\n\n/** Subscribes to a toolkit handle and renders its current state, like the `suspense` pipe of `@tomtomb/ngrx-toolkit`. */\n@Pipe({ name: 'suspense', pure: false })\nexport class SuspensePipe implements PipeTransform {\n transform = createToolkitPipeTransform<AnyMappedEntityState, AnySuspenseState | null>({\n initialState: DEFAULT_STATE,\n disposedState: null,\n state$: suspenseState$,\n }) as <T extends AnyMappedEntityState>(value: T | null | undefined) => ToolkitSuspenseState<T>;\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { combineLatest, map } from 'rxjs';\nimport { suspenseState$, ToolkitSuspenseState } from './suspense.pipe';\nimport { createToolkitPipeTransform } from './toolkit-pipe-transform';\nimport { AnyMappedEntityState } from './toolkit-types';\n\ntype AnyHandleRecord = Record<string, AnyMappedEntityState | null | undefined>;\n\nexport type ToolkitSuspenseMultiState<T extends AnyHandleRecord> = {\n [K in keyof T]: ToolkitSuspenseState<T[K]>;\n};\n\nconst suspenseMultiState$ = (handles: AnyHandleRecord) =>\n combineLatest(\n Object.entries(handles).flatMap(([key, handle]) =>\n handle ? [suspenseState$(handle).pipe(map((state) => [key, state] as const))] : [],\n ),\n ).pipe(map((states) => Object.fromEntries(states)));\n\n/**\n * Subscribes to a record of toolkit handles and renders each one's current state, like the `suspenseMulti` pipe of\n * `@tomtomb/ngrx-toolkit`. Nothing renders until every non-null handle has emitted.\n */\n@Pipe({ name: 'suspenseMulti', pure: false })\nexport class SuspenseMultiPipe implements PipeTransform {\n transform = createToolkitPipeTransform<AnyHandleRecord, Record<string, unknown>>({\n initialState: {},\n disposedState: {},\n state$: suspenseMultiState$,\n }) as <T extends AnyHandleRecord>(value: T | null | undefined) => ToolkitSuspenseMultiState<T>;\n}\n","import { NgModule } from '@angular/core';\nimport { SuspenseMultiPipe } from './suspense-multi.pipe';\nimport { SuspensePipe } from './suspense.pipe';\n\n/** Exports the `suspense` and `suspenseMulti` pipes, like the `NgRxToolkitModule` of `@tomtomb/ngrx-toolkit`. */\n@NgModule({\n imports: [SuspensePipe, SuspenseMultiPipe],\n exports: [SuspensePipe, SuspenseMultiPipe],\n})\nexport class NgRxToolkitModule {}\n","import { AnyQueryCreator, QueryArgsOf, QueryHeaders, ResponseType } from '@ethlete/query';\nimport { Observable, Subject } from 'rxjs';\n\n/** The call state of a toolkit handle, as `@tomtomb/ngrx-toolkit` names it. */\nexport const CallState = {\n INIT: 'INIT',\n LOADING: 'LOADING',\n SUCCESS: 'SUCCESS',\n ERROR: 'ERROR',\n} as const;\n\nexport type CallState = (typeof CallState)[keyof typeof CallState];\n\n/** The error shape of `@tomtomb/ngrx-toolkit`: the HTTP status, the error message and the response body. */\nexport type ToolkitError<T = unknown> = {\n status: number | string;\n message: string;\n data: T | null;\n};\n\nexport type ToolkitActionOptions = {\n headers?: QueryHeaders;\n extras?: { skipCache?: boolean };\n};\n\n/**\n * The toolkit-shaped args of a query creator: `queryParams` holds the path params, `params` the query string and\n * `body` the body, the way `@tomtomb/ngrx-toolkit` action groups named them.\n */\nexport type ActionCallArgs<TCreator extends AnyQueryCreator> = {\n [\n K in keyof QueryArgsOf<TCreator> as K extends 'pathParams'\n ? 'queryParams'\n : K extends 'queryParams'\n ? 'params'\n : K extends 'body'\n ? 'body'\n : never\n ]: QueryArgsOf<TCreator>[K];\n} & { actionOptions?: ToolkitActionOptions };\n\nexport type ToolkitPollingOptions = {\n intervalDuration: number;\n killSwitch: Subject<boolean>;\n};\n\n/** The handle `toolkitCall` returns, shaped like the `MappedEntityState` of `@tomtomb/ngrx-toolkit`. */\nexport type MappedEntityState<TCreator extends AnyQueryCreator> = {\n response$: Observable<ResponseType<QueryArgsOf<TCreator>> | null>;\n cachedResponse$: Observable<ResponseType<QueryArgsOf<TCreator>>>;\n error$: Observable<ToolkitError | null>;\n args$: Observable<ActionCallArgs<TCreator> | null>;\n isInit$: Observable<boolean | null>;\n isLoading$: Observable<boolean | null>;\n isSuccess$: Observable<boolean | null>;\n isError$: Observable<boolean | null>;\n timestamp$: Observable<number | null>;\n callState$: Observable<CallState | null>;\n refresh: () => void;\n remove: () => void;\n startPolling: (options: ToolkitPollingOptions) => void;\n stopPolling: () => void;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyMappedEntityState = MappedEntityState<any>;\n","import { computed, DestroyRef, effect, EnvironmentInjector, signal, untracked } from '@angular/core';\nimport {\n AnyQueryCreator,\n QueryArgsOf,\n QueryErrorResponse,\n QueryExecutionState,\n RequestArgs,\n ResponseType,\n} from '@ethlete/query';\nimport {\n defer,\n distinctUntilChanged,\n filter,\n finalize,\n interval,\n map,\n Observable,\n startWith,\n Subject,\n Subscription,\n takeUntil,\n} from 'rxjs';\nimport {\n ActionCallArgs,\n AnyMappedEntityState,\n CallState,\n MappedEntityState,\n ToolkitError,\n ToolkitPollingOptions,\n} from './toolkit-types';\n\ntype ToolkitHandleSnapshot = {\n args: unknown;\n callState: CallState | null;\n response: unknown;\n error: ToolkitError | null;\n timestamp: number | null;\n};\n\nexport type ToolkitHandleEntry = {\n handle: AnyMappedEntityState;\n call: () => void;\n};\n\nconst EMPTY_SNAPSHOT: ToolkitHandleSnapshot = {\n args: null,\n callState: null,\n response: null,\n error: null,\n timestamp: null,\n};\n\nconst toCallState = (state: QueryExecutionState<never> | null): CallState => {\n switch (state?.type) {\n case 'success':\n return CallState.SUCCESS;\n case 'failure':\n return CallState.ERROR;\n default:\n return CallState.LOADING;\n }\n};\n\nconst toToolkitError = (error: QueryErrorResponse): ToolkitError => ({\n status: error.raw.status,\n message: error.raw.message,\n data: error.raw.error ?? null,\n});\n\nexport const toQueryArgs = (args: Record<string, unknown>) => {\n const actionOptions = args['actionOptions'] as { headers?: unknown } | undefined;\n const queryArgs: Record<string, unknown> = {};\n\n if (args['queryParams'] !== undefined) queryArgs['pathParams'] = args['queryParams'];\n if (args['params'] !== undefined) queryArgs['queryParams'] = args['params'];\n if (args['body'] !== undefined) queryArgs['body'] = args['body'];\n if (actionOptions?.headers !== undefined) queryArgs['headers'] = actionOptions.headers;\n\n return queryArgs as RequestArgs<never>;\n};\n\nexport const createToolkitHandle = <TCreator extends AnyQueryCreator>(\n creator: TCreator,\n args: ActionCallArgs<TCreator>,\n injector: EnvironmentInjector,\n): ToolkitHandleEntry => {\n const query = creator({ injector, onlyManualExecution: true, silenceMissingWithArgsFeatureError: true });\n const queryArgs = toQueryArgs(args as Record<string, unknown>);\n\n const calledArgs = signal<ActionCallArgs<TCreator> | null>(null);\n const toolkitError = computed(() => {\n const error = query.error();\n\n return error ? toToolkitError(error) : null;\n });\n const snapshot = computed<ToolkitHandleSnapshot>(() => {\n const currentArgs = calledArgs();\n\n if (!currentArgs) return EMPTY_SNAPSHOT;\n\n const state = query.executionState();\n const callState = toCallState(state);\n\n return {\n args: currentArgs,\n callState,\n response: state?.type === 'success' ? state.response : null,\n error: callState === CallState.ERROR ? toolkitError() : null,\n timestamp: Date.now(),\n };\n });\n\n const changes = new Subject<ToolkitHandleSnapshot>();\n\n effect(\n () => {\n const current = snapshot();\n untracked(() => changes.next(current));\n },\n { injector },\n );\n\n const select = <T>(pick: (current: ToolkitHandleSnapshot) => T): Observable<T> =>\n defer(() => changes.pipe(startWith(untracked(snapshot)))).pipe(map(pick), distinctUntilChanged());\n\n const call = () => {\n calledArgs.set(args);\n query.execute({ args: queryArgs });\n };\n\n const refresh = () => {\n if (untracked(calledArgs)) call();\n };\n\n let pollingSubscription = Subscription.EMPTY;\n\n const stopPolling = () => pollingSubscription.unsubscribe();\n\n const startPolling = ({ intervalDuration, killSwitch }: ToolkitPollingOptions) => {\n if (!pollingSubscription.closed) return;\n\n pollingSubscription = interval(intervalDuration)\n .pipe(\n takeUntil(killSwitch),\n finalize(() => (pollingSubscription = Subscription.EMPTY)),\n )\n .subscribe(() => refresh());\n };\n\n const remove = () => {\n stopPolling();\n calledArgs.set(null);\n query.reset();\n };\n\n injector.get(DestroyRef).onDestroy(() => {\n stopPolling();\n changes.complete();\n });\n\n type TResponse = ResponseType<QueryArgsOf<TCreator>>;\n\n const response$ = select((current) => current.response as TResponse | null);\n\n const handle: MappedEntityState<TCreator> = {\n response$,\n cachedResponse$: response$.pipe(filter((response): response is TResponse => !!response)),\n error$: select((current) => current.error),\n args$: select((current) => current.args as ActionCallArgs<TCreator> | null),\n isInit$: select((current) => current.callState === CallState.INIT),\n isLoading$: select((current) => current.callState === CallState.LOADING),\n isSuccess$: select((current) => current.callState === CallState.SUCCESS),\n isError$: select((current) => current.callState === CallState.ERROR),\n timestamp$: select((current) => current.timestamp),\n callState$: select((current) => current.callState),\n refresh,\n remove,\n startPolling,\n stopPolling,\n };\n\n return { handle, call };\n};\n","import { assertInInjectionContext, EnvironmentInjector, inject, Injector } from '@angular/core';\nimport { AnyQueryCreator } from '@ethlete/query';\nimport { createToolkitHandle, ToolkitHandleEntry, toQueryArgs } from './toolkit-handle';\nimport { ActionCallArgs, MappedEntityState } from './toolkit-types';\n\nexport type ToolkitCallOptions = {\n /**\n * The injector the handle's query lives in. Required outside an injection context, which is where a\n * `providedIn: 'root'` facade calls from; pass the facade's `inject(Injector)`.\n */\n injector?: Injector;\n};\n\ntype ToolkitRegistry = WeakMap<AnyQueryCreator, Map<string, ToolkitHandleEntry>>;\n\nconst registries = /* @__PURE__ */ new WeakMap<EnvironmentInjector, ToolkitRegistry>();\n\nconst sortedValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(sortedValue);\n if (typeof value !== 'object' || value === null) return value;\n\n return Object.fromEntries(\n Object.keys(value)\n .sort()\n .map((key) => [key, sortedValue((value as Record<string, unknown>)[key])]),\n );\n};\n\nconst hashArgs = (args: unknown) => JSON.stringify(sortedValue(args ?? null));\n\nconst resolveEnvironmentInjector = (fn: (...args: never[]) => unknown, options?: ToolkitCallOptions) => {\n if (options?.injector) return options.injector.get(EnvironmentInjector);\n\n assertInInjectionContext(fn);\n\n return inject(EnvironmentInjector);\n};\n\nconst resolveEntry = <TCreator extends AnyQueryCreator>(\n creator: TCreator,\n args: ActionCallArgs<TCreator>,\n injector: EnvironmentInjector,\n) => {\n let registry = registries.get(injector);\n\n if (!registry) {\n registry = new WeakMap();\n registries.set(injector, registry);\n }\n\n let entries = registry.get(creator);\n\n if (!entries) {\n entries = new Map();\n registry.set(creator, entries);\n }\n\n const key = hashArgs(toQueryArgs(args as Record<string, unknown>));\n let entry = entries.get(key);\n\n if (!entry) {\n entry = createToolkitHandle(creator, args, injector);\n entries.set(key, entry);\n }\n\n return entry;\n};\n\n/**\n * Returns the handle for a creator and args without executing it - the `FacadeBase.select(group, actionId)` of\n * `@tomtomb/ngrx-toolkit`. Args that build the same request (in any key order, other keys ignored) give the same\n * handle `toolkitCall` returns, so `toolkitSelect(getTeam, args, { injector }).refresh()` re-runs a request another\n * component started.\n */\nexport const toolkitSelect = <TCreator extends AnyQueryCreator>(\n creator: TCreator,\n args: ActionCallArgs<TCreator>,\n options?: ToolkitCallOptions,\n): MappedEntityState<TCreator> =>\n resolveEntry(creator, args, resolveEnvironmentInjector(toolkitSelect, options)).handle;\n\n/**\n * Executes a query creator with toolkit-shaped args and returns its handle - the `FacadeBase.call(group, args)` of\n * `@tomtomb/ngrx-toolkit`. Every call sends the request again; equal args return the same handle, which lives as\n * long as the injector.\n *\n * @example\n * ```ts\n * @Injectable({ providedIn: 'root' })\n * export class TeamFacade {\n * private injector = inject(Injector);\n *\n * getTeam(args: ActionCallArgs<typeof getTeam>) {\n * return toolkitCall(getTeam, args, { injector: this.injector });\n * }\n * }\n * ```\n */\nexport const toolkitCall = <TCreator extends AnyQueryCreator>(\n creator: TCreator,\n args: ActionCallArgs<TCreator>,\n options?: ToolkitCallOptions,\n): MappedEntityState<TCreator> => {\n const entry = resolveEntry(creator, args, resolveEnvironmentInjector(toolkitCall, options));\n\n entry.call();\n\n return entry.handle;\n};\n","import { combineLatest, map, Observable } from 'rxjs';\nimport { ToolkitError } from './toolkit-types';\n\n/** Emits `true` while any of the given loading streams is `true`. Nullish entries are skipped. */\nexport const joinLoading = (observables: (Observable<boolean | null> | null | undefined)[]): Observable<boolean> =>\n combineLatest(observables.filter((o): o is Observable<boolean | null> => !!o)).pipe(\n map((values) => values.some((value) => !!value)),\n );\n\n/** Emits every non-null error of the given error streams, or `null` when there is none. Nullish entries are skipped. */\nexport const joinErrors = (\n observables: (Observable<ToolkitError | null> | null | undefined)[],\n): Observable<ToolkitError[] | null> =>\n combineLatest(observables.filter((o): o is Observable<ToolkitError | null> => !!o)).pipe(\n map((values) => {\n const errors = values.filter((value): value is ToolkitError => !!value);\n\n return errors.length ? errors : null;\n }),\n );\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ethlete-query-ngrx-toolkit.mjs","sources":["../../../../libs/query/ngrx-toolkit/toolkit-pipe-transform.ts","../../../../libs/query/ngrx-toolkit/suspense.pipe.ts","../../../../libs/query/ngrx-toolkit/suspense-multi.pipe.ts","../../../../libs/query/ngrx-toolkit/ngrx-toolkit.module.ts","../../../../libs/query/ngrx-toolkit/toolkit-types.ts","../../../../libs/query/ngrx-toolkit/toolkit-handle.ts","../../../../libs/query/ngrx-toolkit/toolkit-call.ts","../../../../libs/query/ngrx-toolkit/toolkit-join.ts","../../../../libs/query/ngrx-toolkit/ethlete-query-ngrx-toolkit.ts"],"sourcesContent":["import { ChangeDetectorRef, DestroyRef, inject } from '@angular/core';\nimport { Observable, Subscription } from 'rxjs';\n\nexport type CreateToolkitPipeTransformOptions<TValue, TState> = {\n initialState: TState;\n disposedState: TState;\n state$: (value: TValue) => Observable<TState>;\n};\n\nexport const createToolkitPipeTransform = <TValue extends object, TState>(\n options: CreateToolkitPipeTransformOptions<TValue, TState>,\n) => {\n const cdr = inject(ChangeDetectorRef);\n let subscription = Subscription.EMPTY;\n let current: TValue | null = null;\n let state = options.initialState;\n\n const dispose = () => {\n subscription.unsubscribe();\n current = null;\n state = options.disposedState;\n };\n\n const subscribe = (value: TValue) => {\n current = value;\n subscription = options.state$(value).subscribe((next) => {\n state = next;\n cdr.markForCheck();\n });\n };\n\n inject(DestroyRef).onDestroy(dispose);\n\n return (value: TValue | null | undefined) => {\n if (!current && value) subscribe(value);\n\n if (current !== value) {\n dispose();\n if (value) subscribe(value);\n }\n\n return state;\n };\n};\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { combineLatest, map, Observable, startWith } from 'rxjs';\nimport { createToolkitPipeTransform } from './toolkit-pipe-transform';\nimport { AnyMappedEntityState, ToolkitPollingOptions } from './toolkit-types';\n\ntype ExtractFrom<T> = T extends Observable<infer U> ? U : never;\n\n/** What `suspense` and `suspenseMulti` render for a handle: every stream's current value, plus the handle's methods. */\nexport type ToolkitSuspenseState<T extends AnyMappedEntityState | null | undefined> = T extends AnyMappedEntityState\n ? {\n args: ExtractFrom<T['args$']>;\n cachedResponse: ExtractFrom<T['cachedResponse$']> | null;\n callState: ExtractFrom<T['callState$']>;\n error: ExtractFrom<T['error$']>;\n isError: ExtractFrom<T['isError$']>;\n isInit: ExtractFrom<T['isInit$']>;\n isLoading: ExtractFrom<T['isLoading$']>;\n isSuccess: ExtractFrom<T['isSuccess$']>;\n response: ExtractFrom<T['response$']>;\n timestamp: ExtractFrom<T['timestamp$']>;\n refresh: () => void;\n remove: () => void;\n startPolling: (options: ToolkitPollingOptions) => void;\n stopPolling: () => void;\n }\n : T;\n\ntype AnySuspenseState = ToolkitSuspenseState<AnyMappedEntityState>;\n\nconst noop = () => undefined;\n\nconst DEFAULT_STATE: AnySuspenseState = {\n args: null,\n cachedResponse: null,\n callState: null,\n error: null,\n isError: false,\n isInit: false,\n isLoading: false,\n isSuccess: false,\n response: null,\n timestamp: null,\n refresh: noop,\n remove: noop,\n startPolling: noop,\n stopPolling: noop,\n};\n\nexport const suspenseState$ = (handle: AnyMappedEntityState): Observable<AnySuspenseState> =>\n combineLatest({\n args: handle.args$,\n cachedResponse: handle.cachedResponse$.pipe(startWith(null)),\n callState: handle.callState$,\n error: handle.error$,\n isError: handle.isError$,\n isInit: handle.isInit$,\n isLoading: handle.isLoading$,\n isSuccess: handle.isSuccess$,\n response: handle.response$,\n timestamp: handle.timestamp$,\n }).pipe(\n map((values) => ({\n ...values,\n refresh: handle.refresh,\n remove: handle.remove,\n startPolling: handle.startPolling,\n stopPolling: handle.stopPolling,\n })),\n );\n\n/** Subscribes to a toolkit handle and renders its current state, like the `suspense` pipe of `@tomtomb/ngrx-toolkit`. */\n@Pipe({ name: 'suspense', pure: false })\nexport class SuspensePipe implements PipeTransform {\n transform = createToolkitPipeTransform<AnyMappedEntityState, AnySuspenseState | null>({\n initialState: DEFAULT_STATE,\n disposedState: null,\n state$: suspenseState$,\n }) as <T extends AnyMappedEntityState>(value: T | null | undefined) => ToolkitSuspenseState<T>;\n}\n","import { Pipe, PipeTransform } from '@angular/core';\nimport { combineLatest, map } from 'rxjs';\nimport { suspenseState$, ToolkitSuspenseState } from './suspense.pipe';\nimport { createToolkitPipeTransform } from './toolkit-pipe-transform';\nimport { AnyMappedEntityState } from './toolkit-types';\n\ntype AnyHandleRecord = Record<string, AnyMappedEntityState | null | undefined>;\n\nexport type ToolkitSuspenseMultiState<T extends AnyHandleRecord> = {\n [K in keyof T]: ToolkitSuspenseState<T[K]>;\n};\n\nconst suspenseMultiState$ = (handles: AnyHandleRecord) =>\n combineLatest(\n Object.entries(handles).flatMap(([key, handle]) =>\n handle ? [suspenseState$(handle).pipe(map((state) => [key, state] as const))] : [],\n ),\n ).pipe(map((states) => Object.fromEntries(states)));\n\n/**\n * Subscribes to a record of toolkit handles and renders each one's current state, like the `suspenseMulti` pipe of\n * `@tomtomb/ngrx-toolkit`. Nothing renders until every non-null handle has emitted.\n */\n@Pipe({ name: 'suspenseMulti', pure: false })\nexport class SuspenseMultiPipe implements PipeTransform {\n transform = createToolkitPipeTransform<AnyHandleRecord, Record<string, unknown>>({\n initialState: {},\n disposedState: {},\n state$: suspenseMultiState$,\n }) as <T extends AnyHandleRecord>(value: T | null | undefined) => ToolkitSuspenseMultiState<T>;\n}\n","import { NgModule } from '@angular/core';\nimport { SuspenseMultiPipe } from './suspense-multi.pipe';\nimport { SuspensePipe } from './suspense.pipe';\n\n/** Exports the `suspense` and `suspenseMulti` pipes, like the `NgRxToolkitModule` of `@tomtomb/ngrx-toolkit`. */\n@NgModule({\n imports: [SuspensePipe, SuspenseMultiPipe],\n exports: [SuspensePipe, SuspenseMultiPipe],\n})\nexport class NgRxToolkitModule {}\n","import { AnyQueryCreator, QueryArgsOf, QueryHeaders, ResponseType } from '@ethlete/query';\nimport { Observable, Subject } from 'rxjs';\n\n/** The call state of a toolkit handle, as `@tomtomb/ngrx-toolkit` names it. */\nexport const CallState = {\n INIT: 'INIT',\n LOADING: 'LOADING',\n SUCCESS: 'SUCCESS',\n ERROR: 'ERROR',\n} as const;\n\nexport type CallState = (typeof CallState)[keyof typeof CallState];\n\n/** The error shape of `@tomtomb/ngrx-toolkit`: the HTTP status, the error message and the response body. */\nexport type ToolkitError<T = unknown> = {\n status: number | string;\n message: string;\n data: T | null;\n};\n\nexport type ToolkitActionOptions = {\n headers?: QueryHeaders;\n extras?: { skipCache?: boolean };\n};\n\n/**\n * The toolkit-shaped args of a query creator: `queryParams` holds the path params, `params` the query string and\n * `body` the body, the way `@tomtomb/ngrx-toolkit` action groups named them.\n */\nexport type ActionCallArgs<TCreator extends AnyQueryCreator> = {\n [\n K in keyof QueryArgsOf<TCreator> as K extends 'pathParams'\n ? 'queryParams'\n : K extends 'queryParams'\n ? 'params'\n : K extends 'body'\n ? 'body'\n : never\n ]: QueryArgsOf<TCreator>[K];\n} & { actionOptions?: ToolkitActionOptions };\n\nexport type ToolkitPollingOptions = {\n intervalDuration: number;\n killSwitch: Subject<boolean>;\n};\n\n/** The handle `toolkitCall` returns, shaped like the `MappedEntityState` of `@tomtomb/ngrx-toolkit`. */\nexport type MappedEntityState<TCreator extends AnyQueryCreator> = {\n response$: Observable<ResponseType<QueryArgsOf<TCreator>> | null>;\n cachedResponse$: Observable<ResponseType<QueryArgsOf<TCreator>>>;\n error$: Observable<ToolkitError | null>;\n args$: Observable<ActionCallArgs<TCreator> | null>;\n isInit$: Observable<boolean | null>;\n isLoading$: Observable<boolean | null>;\n isSuccess$: Observable<boolean | null>;\n isError$: Observable<boolean | null>;\n timestamp$: Observable<number | null>;\n callState$: Observable<CallState | null>;\n refresh: () => void;\n remove: () => void;\n startPolling: (options: ToolkitPollingOptions) => void;\n stopPolling: () => void;\n};\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AnyMappedEntityState = MappedEntityState<any>;\n","import { computed, DestroyRef, effect, EnvironmentInjector, isDevMode, signal, untracked } from '@angular/core';\nimport {\n AnyQueryCreator,\n QueryArgsOf,\n QueryErrorResponse,\n QueryExecutionState,\n RequestArgs,\n ResponseType,\n} from '@ethlete/query';\nimport {\n defer,\n distinctUntilChanged,\n filter,\n finalize,\n interval,\n map,\n Observable,\n of,\n startWith,\n Subject,\n Subscription,\n takeUntil,\n} from 'rxjs';\nimport {\n ActionCallArgs,\n AnyMappedEntityState,\n CallState,\n MappedEntityState,\n ToolkitError,\n ToolkitPollingOptions,\n} from './toolkit-types';\n\ntype ToolkitHandleSnapshot = {\n args: unknown;\n callState: CallState | null;\n response: unknown;\n error: ToolkitError | null;\n timestamp: number | null;\n};\n\nexport type ToolkitHandleEntry = {\n handle: AnyMappedEntityState;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n call: (args: ActionCallArgs<any>) => void;\n releaseWhenSettled: () => void;\n};\n\nconst EMPTY_SNAPSHOT: ToolkitHandleSnapshot = {\n args: null,\n callState: null,\n response: null,\n error: null,\n timestamp: null,\n};\n\nconst toCallState = (state: QueryExecutionState<never> | null): CallState => {\n switch (state?.type) {\n case 'success':\n return CallState.SUCCESS;\n case 'failure':\n return CallState.ERROR;\n default:\n return CallState.LOADING;\n }\n};\n\nconst toToolkitError = (error: QueryErrorResponse): ToolkitError => ({\n status: error.raw.status,\n message: error.raw.message,\n data: error.raw.error ?? null,\n});\n\nconst handleReleasedMessage = (method: 'refresh' | 'startPolling') =>\n `${method}() was called on a toolkit handle that a newer toolkitCall() released. Args that cannot be hashed ` +\n `(FormData, Blob, Map, ...) never share a handle, so the next call for the same creator releases the previous ` +\n `handle once its request settles. The call was ignored - call toolkitCall() again with the args instead of ` +\n `reusing the old handle. This warning is shown once per handle.`;\n\nexport const toQueryArgs = (args: Record<string, unknown>) => {\n const actionOptions = args['actionOptions'] as { headers?: unknown } | undefined;\n const queryArgs: Record<string, unknown> = {};\n\n if (args['queryParams'] !== undefined) queryArgs['pathParams'] = args['queryParams'];\n if (args['params'] !== undefined) queryArgs['queryParams'] = args['params'];\n if (args['body'] !== undefined) queryArgs['body'] = args['body'];\n if (actionOptions?.headers !== undefined) queryArgs['headers'] = actionOptions.headers;\n\n return queryArgs as RequestArgs<never>;\n};\n\nexport const createToolkitHandle = <TCreator extends AnyQueryCreator>(\n creator: TCreator,\n injector: EnvironmentInjector,\n): ToolkitHandleEntry => {\n const query = creator({ injector, onlyManualExecution: true, silenceMissingWithArgsFeatureError: true });\n\n const calledArgs = signal<ActionCallArgs<TCreator> | null>(null);\n const toolkitError = computed(() => {\n const error = query.error();\n\n return error ? toToolkitError(error) : null;\n });\n const snapshot = computed<ToolkitHandleSnapshot>(() => {\n const currentArgs = calledArgs();\n\n if (!currentArgs) return EMPTY_SNAPSHOT;\n\n const state = query.executionState();\n const callState = toCallState(state);\n\n return {\n args: currentArgs,\n callState,\n response: state?.type === 'success' ? state.response : null,\n error: callState === CallState.ERROR ? toolkitError() : null,\n timestamp: Date.now(),\n };\n });\n\n const changes = new Subject<ToolkitHandleSnapshot>();\n\n const changesEffect = effect(\n () => {\n const current = snapshot();\n untracked(() => changes.next(current));\n },\n { injector },\n );\n\n let releasedSnapshot: ToolkitHandleSnapshot | null = null;\n let warnedAboutRelease = false;\n\n const ignoredAfterRelease = (method: 'refresh' | 'startPolling') => {\n if (!releasedSnapshot) return false;\n\n if (isDevMode() && !warnedAboutRelease) {\n warnedAboutRelease = true;\n console.warn(handleReleasedMessage(method));\n }\n\n return true;\n };\n\n const select = <T>(pick: (current: ToolkitHandleSnapshot) => T): Observable<T> =>\n defer(() => (releasedSnapshot ? of(releasedSnapshot) : changes.pipe(startWith(untracked(snapshot))))).pipe(\n map(pick),\n distinctUntilChanged(),\n );\n\n const call = (args: ActionCallArgs<TCreator>) => {\n calledArgs.set(args);\n query.execute({ args: toQueryArgs(args as Record<string, unknown>) });\n };\n\n const refresh = () => {\n if (ignoredAfterRelease('refresh')) return;\n\n const lastArgs = untracked(calledArgs);\n\n if (lastArgs) call(lastArgs);\n };\n\n let pollingSubscription = Subscription.EMPTY;\n\n const stopPolling = () => pollingSubscription.unsubscribe();\n\n const startPolling = ({ intervalDuration, killSwitch }: ToolkitPollingOptions) => {\n if (ignoredAfterRelease('startPolling') || !pollingSubscription.closed) return;\n\n pollingSubscription = interval(intervalDuration)\n .pipe(\n takeUntil(killSwitch),\n finalize(() => (pollingSubscription = Subscription.EMPTY)),\n )\n .subscribe(() => refresh());\n };\n\n const remove = () => {\n stopPolling();\n calledArgs.set(null);\n query.reset();\n };\n\n const removeDestroyListener = injector.get(DestroyRef).onDestroy(() => {\n stopPolling();\n changes.complete();\n });\n\n const release = () => {\n releasedSnapshot = untracked(snapshot);\n stopPolling();\n changesEffect.destroy();\n removeDestroyListener();\n changes.complete();\n query.subtle.destroy();\n };\n\n const releaseWhenSettled = () => {\n const releaseEffect = effect(\n () => {\n if (snapshot().callState === CallState.LOADING) return;\n\n releaseEffect.destroy();\n untracked(release);\n },\n { injector },\n );\n };\n\n type TResponse = ResponseType<QueryArgsOf<TCreator>>;\n\n const response$ = select((current) => current.response as TResponse | null);\n\n const handle: MappedEntityState<TCreator> = {\n response$,\n cachedResponse$: response$.pipe(filter((response): response is TResponse => !!response)),\n error$: select((current) => current.error),\n args$: select((current) => current.args as ActionCallArgs<TCreator> | null),\n isInit$: select((current) => current.callState === CallState.INIT),\n isLoading$: select((current) => current.callState === CallState.LOADING),\n isSuccess$: select((current) => current.callState === CallState.SUCCESS),\n isError$: select((current) => current.callState === CallState.ERROR),\n timestamp$: select((current) => current.timestamp),\n callState$: select((current) => current.callState),\n refresh,\n remove,\n startPolling,\n stopPolling,\n };\n\n return { handle, call, releaseWhenSettled };\n};\n","import { assertInInjectionContext, EnvironmentInjector, inject, Injector } from '@angular/core';\nimport { AnyQueryCreator, QueryHeaders, RequestArgs, resolveQueryHeaders } from '@ethlete/query';\nimport { createToolkitHandle, ToolkitHandleEntry, toQueryArgs } from './toolkit-handle';\nimport { ActionCallArgs, MappedEntityState } from './toolkit-types';\n\nexport type ToolkitCallOptions = {\n /**\n * The injector the handle's query lives in. Required outside an injection context, which is where a\n * `providedIn: 'root'` facade calls from; pass the facade's `inject(Injector)`.\n */\n injector?: Injector;\n};\n\ntype ToolkitRegistry = {\n shared: WeakMap<AnyQueryCreator, Map<string, ToolkitHandleEntry>>;\n latestUnshared: WeakMap<AnyQueryCreator, ToolkitHandleEntry>;\n};\n\nconst registries = /* @__PURE__ */ new WeakMap<EnvironmentInjector, ToolkitRegistry>();\n\nclass UnhashableValueError extends Error {}\n\nconst isPlainObject = (value: object) => {\n const prototype = Object.getPrototypeOf(value);\n\n return prototype === Object.prototype || prototype === null;\n};\n\nconst hashableValue = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(hashableValue);\n if (typeof value !== 'object' || value === null) return value;\n if (value instanceof Date) return { '\\u0000date': value.getTime() };\n if (!isPlainObject(value)) throw new UnhashableValueError();\n\n return Object.fromEntries(\n Object.keys(value)\n .sort()\n .map((key) => [key, hashableValue((value as Record<string, unknown>)[key])]),\n );\n};\n\nconst hashableHeaders = (headers: QueryHeaders | undefined) => {\n const resolved = resolveQueryHeaders(headers);\n\n return resolved\n ?.keys()\n .map((name) => name.toLowerCase())\n .sort()\n .map((name) => [name, resolved.getAll(name)]);\n};\n\n/**\n * Returns `null` for args holding a value JSON cannot represent (FormData, Blob, Map, ...). Those never share a handle,\n * and each one is released once the next one for the same creator starts and its own call has settled.\n */\nconst hashArgs = (args: RequestArgs<never>) => {\n const { headers, ...rest } = args as { headers?: QueryHeaders };\n\n try {\n return JSON.stringify([hashableValue(rest), hashableHeaders(headers) ?? null]);\n } catch (error) {\n if (error instanceof UnhashableValueError) return null;\n\n throw error;\n }\n};\n\nconst resolveEnvironmentInjector = (fn: (...args: never[]) => unknown, options?: ToolkitCallOptions) => {\n if (options?.injector) return options.injector.get(EnvironmentInjector);\n\n assertInInjectionContext(fn);\n\n return inject(EnvironmentInjector);\n};\n\nconst resolveEntry = <TCreator extends AnyQueryCreator>(\n creator: TCreator,\n args: ActionCallArgs<TCreator>,\n injector: EnvironmentInjector,\n) => {\n let registry = registries.get(injector);\n\n if (!registry) {\n registry = { shared: new WeakMap(), latestUnshared: new WeakMap() };\n registries.set(injector, registry);\n }\n\n const key = hashArgs(toQueryArgs(args as Record<string, unknown>));\n\n if (key === null) {\n const entry = createToolkitHandle(creator, injector);\n\n registry.latestUnshared.get(creator)?.releaseWhenSettled();\n registry.latestUnshared.set(creator, entry);\n\n return entry;\n }\n\n let entries = registry.shared.get(creator);\n\n if (!entries) {\n entries = new Map();\n registry.shared.set(creator, entries);\n }\n\n let entry = entries.get(key);\n\n if (!entry) {\n entry = createToolkitHandle(creator, injector);\n entries.set(key, entry);\n }\n\n return entry;\n};\n\n/**\n * Returns the handle for a creator and args without executing it - the `FacadeBase.select(group, actionId)` of\n * `@tomtomb/ngrx-toolkit`. Args that build the same request (in any key order, other keys ignored) give the same\n * handle `toolkitCall` returns, so `toolkitSelect(getTeam, args, { injector }).refresh()` re-runs a request another\n * component started.\n */\nexport const toolkitSelect = <TCreator extends AnyQueryCreator>(\n creator: TCreator,\n args: ActionCallArgs<TCreator>,\n options?: ToolkitCallOptions,\n): MappedEntityState<TCreator> =>\n resolveEntry(creator, args, resolveEnvironmentInjector(toolkitSelect, options)).handle;\n\n/**\n * Executes a query creator with toolkit-shaped args and returns its handle - the `FacadeBase.call(group, args)` of\n * `@tomtomb/ngrx-toolkit`. Every call sends the request again; equal args return the same handle, which lives as\n * long as the injector.\n *\n * @example\n * ```ts\n * @Injectable({ providedIn: 'root' })\n * export class TeamFacade {\n * private injector = inject(Injector);\n *\n * getTeam(args: ActionCallArgs<typeof getTeam>) {\n * return toolkitCall(getTeam, args, { injector: this.injector });\n * }\n * }\n * ```\n */\nexport const toolkitCall = <TCreator extends AnyQueryCreator>(\n creator: TCreator,\n args: ActionCallArgs<TCreator>,\n options?: ToolkitCallOptions,\n): MappedEntityState<TCreator> => {\n const entry = resolveEntry(creator, args, resolveEnvironmentInjector(toolkitCall, options));\n\n entry.call(args);\n\n return entry.handle;\n};\n","import { combineLatest, map, Observable } from 'rxjs';\nimport { ToolkitError } from './toolkit-types';\n\n/** Emits `true` while any of the given loading streams is `true`. Nullish entries are skipped. */\nexport const joinLoading = (observables: (Observable<boolean | null> | null | undefined)[]): Observable<boolean> =>\n combineLatest(observables.filter((o): o is Observable<boolean | null> => !!o)).pipe(\n map((values) => values.some((value) => !!value)),\n );\n\n/** Emits every non-null error of the given error streams, or `null` when there is none. Nullish entries are skipped. */\nexport const joinErrors = (\n observables: (Observable<ToolkitError | null> | null | undefined)[],\n): Observable<ToolkitError[] | null> =>\n combineLatest(observables.filter((o): o is Observable<ToolkitError | null> => !!o)).pipe(\n map((values) => {\n const errors = values.filter((value): value is ToolkitError => !!value);\n\n return errors.length ? errors : null;\n }),\n );\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -73,7 +73,7 @@ const setupAuthTest = (config) => {
|
|
|
73
73
|
const refreshHelper = (token, response) => {
|
|
74
74
|
TestBed.runInInjectionContext(() => {
|
|
75
75
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
-
auth.queries.refresh.execute({ body: { token } });
|
|
76
|
+
auth.queries.refresh.execute((buildRefreshArgs?.(token) ?? { body: { token } }));
|
|
77
77
|
});
|
|
78
78
|
const fullUrl = `${querySetup.baseUrl}${refreshPath}`;
|
|
79
79
|
expectFlushAndWait(querySetup.httpTesting, fullUrl, response);
|
|
@@ -98,13 +98,18 @@ const setupAuthTest = (config) => {
|
|
|
98
98
|
};
|
|
99
99
|
|
|
100
100
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
101
|
+
// Captured at import, before a spec can call `vi.useFakeTimers()`: delivery must not wait on a faked clock.
|
|
102
|
+
const nodeSetImmediate = globalThis.setImmediate;
|
|
103
|
+
const scheduleTask = nodeSetImmediate
|
|
104
|
+
? nodeSetImmediate.bind(globalThis)
|
|
105
|
+
: setTimeout.bind(globalThis);
|
|
101
106
|
/**
|
|
102
107
|
* Replaces `globalThis.BroadcastChannel` with an in-memory bus.
|
|
103
108
|
*
|
|
104
109
|
* Messages are structured-cloned at post time (so a non-cloneable payload throws exactly like the
|
|
105
|
-
* real API does), delivered
|
|
106
|
-
* channel that posted them - all three matching browser behavior.
|
|
107
|
-
*
|
|
110
|
+
* real API does), delivered as a task (never synchronously, and after every pending microtask), and
|
|
111
|
+
* never delivered back to the channel that posted them - all three matching browser behavior. Delivery
|
|
112
|
+
* does not depend on fake timers; `await flushMultiTabSync()` in a spec flushes it.
|
|
108
113
|
*/
|
|
109
114
|
const installFakeBroadcastChannel = () => {
|
|
110
115
|
const original = globalThis.BroadcastChannel;
|
|
@@ -125,7 +130,7 @@ const installFakeBroadcastChannel = () => {
|
|
|
125
130
|
for (const instance of instances) {
|
|
126
131
|
if (instance === this || instance.closed || instance.name !== this.name)
|
|
127
132
|
continue;
|
|
128
|
-
|
|
133
|
+
scheduleTask(() => {
|
|
129
134
|
if (instance.closed)
|
|
130
135
|
return;
|
|
131
136
|
instance.onmessage?.({ data: structuredClone(clone) });
|
|
@@ -284,11 +289,11 @@ const installFakeWebLocks = () => {
|
|
|
284
289
|
};
|
|
285
290
|
};
|
|
286
291
|
/**
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
* tab's release needs one await per hop.
|
|
292
|
+
* Waits for the fakes' pending work - message delivery (a task) and lock grants (a microtask) - even
|
|
293
|
+
* under fake timers. Awaiting it once is enough for a single hop; a grant that has to wait for another
|
|
294
|
+
* tab's release, or a reply to a delivered message, needs one await per hop.
|
|
290
295
|
*/
|
|
291
|
-
const flushMultiTabSync = () => Promise
|
|
296
|
+
const flushMultiTabSync = () => new Promise((resolve) => scheduleTask(resolve));
|
|
292
297
|
|
|
293
298
|
const quotaError = () => new DOMException('The quota has been exceeded.', 'QuotaExceededError');
|
|
294
299
|
const createFakeQueryPersistenceStore = () => {
|
|
@@ -323,11 +328,12 @@ const createFakeQueryPersistenceStore = () => {
|
|
|
323
328
|
};
|
|
324
329
|
const read = async (key) => {
|
|
325
330
|
calls.read++;
|
|
331
|
+
const entry = store.get(key);
|
|
332
|
+
const snapshot = entry ? { body: clone(entry.body), version: entry.version } : null;
|
|
326
333
|
if (isDeferringReads) {
|
|
327
334
|
await new Promise((resolve) => deferredReads.push(resolve));
|
|
328
335
|
}
|
|
329
|
-
|
|
330
|
-
return entry ? { body: clone(entry.body), version: entry.version } : null;
|
|
336
|
+
return snapshot;
|
|
331
337
|
};
|
|
332
338
|
const write = async (entries) => {
|
|
333
339
|
calls.write++;
|
|
@@ -496,7 +502,11 @@ const createWebSocketTestDouble = () => {
|
|
|
496
502
|
const sent = [];
|
|
497
503
|
const delivered = [];
|
|
498
504
|
const buffered = [];
|
|
499
|
-
const listeners =
|
|
505
|
+
const listeners = {
|
|
506
|
+
connect: [],
|
|
507
|
+
disconnect: [],
|
|
508
|
+
connect_error: [],
|
|
509
|
+
};
|
|
500
510
|
const anyListeners = [];
|
|
501
511
|
const outgoingListeners = [];
|
|
502
512
|
let connection = null;
|
|
@@ -506,6 +516,8 @@ const createWebSocketTestDouble = () => {
|
|
|
506
516
|
let connectRequested = false;
|
|
507
517
|
let disconnected = false;
|
|
508
518
|
let connected = false;
|
|
519
|
+
let active = false;
|
|
520
|
+
let connectCalls = 0;
|
|
509
521
|
let pingExpired = false;
|
|
510
522
|
let recovered = false;
|
|
511
523
|
const deliver = (message) => {
|
|
@@ -513,9 +525,30 @@ const createWebSocketTestDouble = () => {
|
|
|
513
525
|
listener(message.event, message.data);
|
|
514
526
|
delivered.push(message);
|
|
515
527
|
};
|
|
528
|
+
const recordHandshake = () => {
|
|
529
|
+
if (auth)
|
|
530
|
+
auth((data) => handshakes.push(data));
|
|
531
|
+
else
|
|
532
|
+
handshakes.push(null);
|
|
533
|
+
};
|
|
534
|
+
const disconnect = (reason) => {
|
|
535
|
+
connected = false;
|
|
536
|
+
pingExpired = false;
|
|
537
|
+
for (const listener of listeners.disconnect)
|
|
538
|
+
listener(reason);
|
|
539
|
+
};
|
|
516
540
|
const socket = {
|
|
517
|
-
connect: () =>
|
|
518
|
-
|
|
541
|
+
connect: () => {
|
|
542
|
+
connectRequested = true;
|
|
543
|
+
active = true;
|
|
544
|
+
connectCalls++;
|
|
545
|
+
},
|
|
546
|
+
disconnect: () => {
|
|
547
|
+
disconnected = true;
|
|
548
|
+
active = false;
|
|
549
|
+
if (connected)
|
|
550
|
+
disconnect('io client disconnect');
|
|
551
|
+
},
|
|
519
552
|
emit: (event, data) => {
|
|
520
553
|
sent.push({ event, data });
|
|
521
554
|
if (connected && !pingExpired)
|
|
@@ -523,12 +556,15 @@ const createWebSocketTestDouble = () => {
|
|
|
523
556
|
else
|
|
524
557
|
buffered.push({ event, data });
|
|
525
558
|
},
|
|
526
|
-
on: (event, listener) => void listeners.
|
|
559
|
+
on: (event, listener) => void listeners[event].push(listener),
|
|
527
560
|
onAny: (listener) => void anyListeners.push(listener),
|
|
528
561
|
onAnyOutgoing: (listener) => void outgoingListeners.push(listener),
|
|
529
562
|
get recovered() {
|
|
530
563
|
return recovered;
|
|
531
564
|
},
|
|
565
|
+
get active() {
|
|
566
|
+
return active;
|
|
567
|
+
},
|
|
532
568
|
};
|
|
533
569
|
return {
|
|
534
570
|
io: (url, options) => {
|
|
@@ -542,23 +578,29 @@ const createWebSocketTestDouble = () => {
|
|
|
542
578
|
handshakes: () => [...handshakes],
|
|
543
579
|
sent: () => [...sent],
|
|
544
580
|
delivered: () => [...delivered],
|
|
545
|
-
state: () => ({ connectRequested, disconnected }),
|
|
581
|
+
state: () => ({ connectRequested, disconnected, active, connectCalls }),
|
|
546
582
|
serverConnect: (options) => {
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
else
|
|
550
|
-
handshakes.push(null);
|
|
583
|
+
recordHandshake();
|
|
584
|
+
active = true;
|
|
551
585
|
recovered = options?.recovered ?? false;
|
|
552
586
|
connected = true;
|
|
553
587
|
pingExpired = false;
|
|
554
588
|
for (const message of buffered.splice(0))
|
|
555
589
|
deliver(message);
|
|
556
|
-
listeners.
|
|
590
|
+
for (const listener of listeners.connect)
|
|
591
|
+
listener();
|
|
557
592
|
},
|
|
558
|
-
serverDisconnect: () => {
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
593
|
+
serverDisconnect: (options) => {
|
|
594
|
+
const reason = options?.reason ?? 'transport close';
|
|
595
|
+
if (reason === 'io server disconnect')
|
|
596
|
+
active = false;
|
|
597
|
+
disconnect(reason);
|
|
598
|
+
},
|
|
599
|
+
serverRejectHandshake: (message) => {
|
|
600
|
+
recordHandshake();
|
|
601
|
+
active = false;
|
|
602
|
+
for (const listener of listeners.connect_error)
|
|
603
|
+
listener(new Error(message ?? 'unauthorized'));
|
|
562
604
|
},
|
|
563
605
|
serverPingExpire: () => void (pingExpired = true),
|
|
564
606
|
serverSend: (message) => {
|