ngx-signal-query 1.5.0-dev.2 → 1.5.0-dev.4
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/fesm2022/ngx-signal-query.mjs +236 -99
- package/fesm2022/ngx-signal-query.mjs.map +1 -1
- package/index.d.ts +135 -23
- package/package.json +1 -1
|
@@ -232,6 +232,39 @@ function isValidTimeout(value) {
|
|
|
232
232
|
function isPromiseLike(value) {
|
|
233
233
|
return typeof value?.then === 'function';
|
|
234
234
|
}
|
|
235
|
+
// Resolves once every promise has settled, and never rejects. It resolves
|
|
236
|
+
// with nothing: what the promises brought isn't the caller's to see.
|
|
237
|
+
/** @internal */
|
|
238
|
+
function whenAllSettled(promises) {
|
|
239
|
+
return Promise.allSettled(promises).then(() => undefined);
|
|
240
|
+
}
|
|
241
|
+
/** @internal */
|
|
242
|
+
function deferred() {
|
|
243
|
+
let resolve;
|
|
244
|
+
let reject;
|
|
245
|
+
const promise = new Promise((res, rej) => {
|
|
246
|
+
resolve = res;
|
|
247
|
+
reject = rej;
|
|
248
|
+
});
|
|
249
|
+
return { promise, resolve, reject };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* The error a promise of a query's data rejects with when the fetch stops
|
|
254
|
+
* with no data to give: it was cancelled before the query had any
|
|
255
|
+
* (`cancelQueries()`, or the last component showing the query went away), or
|
|
256
|
+
* the query was removed (`removeQueries()`, e.g. on sign-out). The request
|
|
257
|
+
* didn't fail, so tell the two apart with `instanceof CancelledError`.
|
|
258
|
+
*
|
|
259
|
+
* A mutation's `mutateAsync()` rejects with one too, when the mutation cache
|
|
260
|
+
* is cleared while its run is in flight.
|
|
261
|
+
*/
|
|
262
|
+
class CancelledError extends Error {
|
|
263
|
+
constructor(message = 'The query fetch was cancelled') {
|
|
264
|
+
super(message);
|
|
265
|
+
this.name = 'CancelledError';
|
|
266
|
+
}
|
|
267
|
+
}
|
|
235
268
|
|
|
236
269
|
// Exponential backoff capped at 30s, matching TanStack's default.
|
|
237
270
|
/** @internal */
|
|
@@ -254,28 +287,6 @@ function resolveRetryDelay(retryDelay, failureCount, error) {
|
|
|
254
287
|
}
|
|
255
288
|
|
|
256
289
|
const DEFAULT_GC_TIME = 5 * 60 * 1000;
|
|
257
|
-
/**
|
|
258
|
-
* What the promise of a query's fetch rejects with when the fetch stops with
|
|
259
|
-
* no data to give: cancelled before the query had any, or thrown away with
|
|
260
|
-
* the query.
|
|
261
|
-
*
|
|
262
|
-
* @internal
|
|
263
|
-
*/
|
|
264
|
-
class CancelledError extends Error {
|
|
265
|
-
constructor() {
|
|
266
|
-
super('The query fetch was cancelled');
|
|
267
|
-
this.name = 'CancelledError';
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
function deferred() {
|
|
271
|
-
let resolve;
|
|
272
|
-
let reject;
|
|
273
|
-
const promise = new Promise((res, rej) => {
|
|
274
|
-
resolve = res;
|
|
275
|
-
reject = rej;
|
|
276
|
-
});
|
|
277
|
-
return { promise, resolve, reject };
|
|
278
|
-
}
|
|
279
290
|
/** @internal */
|
|
280
291
|
class Query {
|
|
281
292
|
key;
|
|
@@ -298,6 +309,10 @@ class Query {
|
|
|
298
309
|
// The promise of the fetch in flight, which every caller that joins the
|
|
299
310
|
// fetch shares, and how to settle it; null while nothing is in flight.
|
|
300
311
|
#outcome = null;
|
|
312
|
+
// The state the fetch in flight started from, or the last write made
|
|
313
|
+
// during it: stopping the fetch with nothing in its place goes back to it
|
|
314
|
+
// (see #stop). null while nothing is in flight.
|
|
315
|
+
#beforeFetch = null;
|
|
301
316
|
// The structuralSharing of the fetch whose response the query holds.
|
|
302
317
|
#fetchedStructuralSharing;
|
|
303
318
|
#observers = new Set();
|
|
@@ -331,15 +346,17 @@ class Query {
|
|
|
331
346
|
this.#scheduleGc();
|
|
332
347
|
}
|
|
333
348
|
}
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
|
|
349
|
+
// Refetches the query if a component shows it: the first observer that
|
|
350
|
+
// wants it fetched decides how, as in TanStack. A fetch in flight is
|
|
351
|
+
// replaced, as it may predate the change the caller is after. Returns the
|
|
352
|
+
// fetch's promise, or undefined if no observer wants the query fetched.
|
|
353
|
+
refetchIfActive() {
|
|
337
354
|
for (const observer of this.#observers) {
|
|
338
355
|
const options = observer.fetchOptions();
|
|
339
356
|
if (options)
|
|
340
|
-
return options;
|
|
357
|
+
return this.fetch(options, { cancelRefetch: true });
|
|
341
358
|
}
|
|
342
|
-
return
|
|
359
|
+
return;
|
|
343
360
|
}
|
|
344
361
|
// Refetches the query now that the user is back on the page, if one of its
|
|
345
362
|
// observers wants that: its refetchOnWindowFocus is 'always', or true while
|
|
@@ -385,8 +402,17 @@ class Query {
|
|
|
385
402
|
// Whoever waits for the fetch this one replaces gets this one's outcome.
|
|
386
403
|
running?.resolve(outcome.promise);
|
|
387
404
|
this.#outcome = outcome;
|
|
405
|
+
// A fetch that replaces one in flight keeps the state the first one
|
|
406
|
+
// started from.
|
|
407
|
+
if (!running)
|
|
408
|
+
this.#beforeFetch = this.state();
|
|
409
|
+
// A query with no data reads like a first load while it fetches, even
|
|
410
|
+
// after a failure: 'pending' with no error, so a loading state and
|
|
411
|
+
// placeholderData show rather than the old error. One with data keeps
|
|
412
|
+
// its status and error until the response, as in TanStack.
|
|
388
413
|
this.#state.update((state) => ({
|
|
389
414
|
...state,
|
|
415
|
+
...(state.data === undefined && { status: 'pending', error: null }),
|
|
390
416
|
isFetching: true,
|
|
391
417
|
failureCount: 0,
|
|
392
418
|
failureReason: null,
|
|
@@ -417,6 +443,7 @@ class Query {
|
|
|
417
443
|
.subscribe({
|
|
418
444
|
next: (data) => {
|
|
419
445
|
this.#outcome = null;
|
|
446
|
+
this.#beforeFetch = null;
|
|
420
447
|
this.#fetchedStructuralSharing = structuralSharing;
|
|
421
448
|
this.#state.set({
|
|
422
449
|
data,
|
|
@@ -432,6 +459,7 @@ class Query {
|
|
|
432
459
|
},
|
|
433
460
|
error: (err) => {
|
|
434
461
|
this.#outcome = null;
|
|
462
|
+
this.#beforeFetch = null;
|
|
435
463
|
this.#state.update((state) => ({
|
|
436
464
|
...state,
|
|
437
465
|
status: 'error',
|
|
@@ -456,6 +484,9 @@ class Query {
|
|
|
456
484
|
failureReason: null,
|
|
457
485
|
updatedAt,
|
|
458
486
|
}));
|
|
487
|
+
// A write made during a fetch is what stopping the fetch goes back to.
|
|
488
|
+
if (this.#inFlight())
|
|
489
|
+
this.#beforeFetch = this.state();
|
|
459
490
|
// Keep an orphaned query (no observers) alive for another gcTime so a
|
|
460
491
|
// setQueryData write isn't collected before anyone subscribes.
|
|
461
492
|
if (this.#observers.size === 0)
|
|
@@ -497,15 +528,27 @@ class Query {
|
|
|
497
528
|
// Stops the fetch in flight, if any, and settles its promise with settle.
|
|
498
529
|
#stop(settle) {
|
|
499
530
|
const outcome = this.#outcome;
|
|
531
|
+
const before = this.#beforeFetch;
|
|
500
532
|
this.#subscription?.unsubscribe();
|
|
501
533
|
this.#subscription = null;
|
|
502
534
|
this.#outcome = null;
|
|
535
|
+
this.#beforeFetch = null;
|
|
503
536
|
if (outcome)
|
|
504
537
|
settle(outcome);
|
|
505
|
-
// The fetch is no longer running
|
|
506
|
-
//
|
|
507
|
-
|
|
508
|
-
|
|
538
|
+
// The fetch is no longer running, so isFetching mustn't stay stuck true,
|
|
539
|
+
// and what the fetch wrote over goes back to how it was: a query that
|
|
540
|
+
// failed shows its error again, as in TanStack. Data and the stale mark
|
|
541
|
+
// aren't the fetch's to put back: they stay as written meanwhile, so an
|
|
542
|
+
// invalidation made during the fetch still holds.
|
|
543
|
+
if (before) {
|
|
544
|
+
this.#state.update((state) => ({
|
|
545
|
+
...state,
|
|
546
|
+
status: before.status,
|
|
547
|
+
error: before.error,
|
|
548
|
+
failureCount: before.failureCount,
|
|
549
|
+
failureReason: before.failureReason,
|
|
550
|
+
isFetching: false,
|
|
551
|
+
}));
|
|
509
552
|
}
|
|
510
553
|
}
|
|
511
554
|
// A fetch in flight is kept, as in TanStack, and the observers aren't even
|
|
@@ -612,6 +655,9 @@ class Mutation {
|
|
|
612
655
|
// eslint-disable-next-line @typescript-eslint/member-ordering
|
|
613
656
|
state = this.#state.asReadonly();
|
|
614
657
|
#subscription = null;
|
|
658
|
+
// The promise of the run in flight and how to settle it; null once it has
|
|
659
|
+
// settled, or before the run.
|
|
660
|
+
#run = null;
|
|
615
661
|
#observers = 0;
|
|
616
662
|
#options;
|
|
617
663
|
#cache;
|
|
@@ -638,8 +684,14 @@ class Mutation {
|
|
|
638
684
|
this.#observers--;
|
|
639
685
|
this.#disposeIfDone();
|
|
640
686
|
}
|
|
641
|
-
/**
|
|
687
|
+
/**
|
|
688
|
+
* Runs the mutation once. `variables` are passed straight to `mutationFn`.
|
|
689
|
+
* Returns a promise of the run, settled as the state is, once its last hook
|
|
690
|
+
* is done: with the data, or with the error.
|
|
691
|
+
*/
|
|
642
692
|
execute(variables) {
|
|
693
|
+
const run = deferred();
|
|
694
|
+
this.#run = run;
|
|
643
695
|
this.#state.set({
|
|
644
696
|
status: 'pending',
|
|
645
697
|
data: undefined,
|
|
@@ -668,12 +720,24 @@ class Mutation {
|
|
|
668
720
|
})), concatMap((outcome) => this.#runHooks(outcome, variables).pipe(endWith(outcome))))
|
|
669
721
|
.subscribe((outcome) => {
|
|
670
722
|
this.#state.update((state) => ({ ...state, ...outcome }));
|
|
723
|
+
this.#run = null;
|
|
671
724
|
this.#disposeIfDone();
|
|
725
|
+
if (outcome.status === 'success') {
|
|
726
|
+
run.resolve(outcome.data);
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
run.reject(outcome.error);
|
|
730
|
+
}
|
|
672
731
|
});
|
|
732
|
+
return run.promise;
|
|
673
733
|
}
|
|
734
|
+
// Whoever waits for the run hears that it was cancelled, so its promise
|
|
735
|
+
// doesn't hang.
|
|
674
736
|
cancel() {
|
|
675
737
|
this.#subscription?.unsubscribe();
|
|
676
738
|
this.#subscription = null;
|
|
739
|
+
this.#run?.reject(new CancelledError('The mutation was cancelled'));
|
|
740
|
+
this.#run = null;
|
|
677
741
|
}
|
|
678
742
|
/** Sends the write, retrying as `retry` allows, and emits its data once. */
|
|
679
743
|
#request(variables) {
|
|
@@ -865,27 +929,58 @@ class QueryClient {
|
|
|
865
929
|
/**
|
|
866
930
|
* Imperatively fetches and caches a query, unless fresh data already exists
|
|
867
931
|
* (governed by `staleTime`). Prefer {@link injectQuery} in components; use
|
|
868
|
-
* this to prefetch, from an event handler or an effect
|
|
932
|
+
* this to prefetch, from an event handler or an effect, or to wait for the
|
|
933
|
+
* data, e.g. in a route resolver.
|
|
934
|
+
*
|
|
935
|
+
* Resolves with the data as the cache holds it: at once if it's fresh,
|
|
936
|
+
* otherwise once the fetch lands. A key already fetching is waited for,
|
|
937
|
+
* not fetched again, unless `cancelRefetch` is set. Rejects with the error
|
|
938
|
+
* if the fetch fails. A cancelled fetch resolves with the data the cache
|
|
939
|
+
* holds, or rejects with a {@link CancelledError} if it holds none; a
|
|
940
|
+
* removed query always rejects with one. Nothing has to wait for the
|
|
941
|
+
* promise: a prefetch that fails only leaves the query in `'error'`.
|
|
942
|
+
*
|
|
943
|
+
* Unlike a component's query, it doesn't retry a failure unless `retry` is
|
|
944
|
+
* set here or in {@link withDefaultOptions}, so whoever waits for the data
|
|
945
|
+
* hears of the failure at once.
|
|
869
946
|
*
|
|
870
947
|
* @param key - The query key to fetch and cache under.
|
|
871
948
|
* @param queryFn - Function returning the data as an `Observable` or `Promise`.
|
|
872
949
|
* @param options - Fetch tuning (`staleTime`, `retry`, `retryDelay`,
|
|
873
950
|
* `structuralSharing`, `cancelRefetch`).
|
|
951
|
+
* @returns A promise of the data.
|
|
952
|
+
*
|
|
953
|
+
* @example
|
|
954
|
+
* ```ts
|
|
955
|
+
* export const todoResolver: ResolveFn<Todo> = (route) => {
|
|
956
|
+
* const http = inject(HttpClient)
|
|
957
|
+
* const id = Number(route.paramMap.get('id'))
|
|
958
|
+
*
|
|
959
|
+
* return injectQueryClient().fetchQuery(['todos', id], () =>
|
|
960
|
+
* http.get<Todo>(`/api/todos/${id}`),
|
|
961
|
+
* )
|
|
962
|
+
* }
|
|
963
|
+
* ```
|
|
874
964
|
*/
|
|
875
965
|
fetchQuery(key, queryFn, options = {}) {
|
|
876
|
-
untracked(() => {
|
|
877
|
-
// Resolved as a query's options are, so one set of defaults serves
|
|
878
|
-
//
|
|
966
|
+
return untracked(() => {
|
|
967
|
+
// Resolved as a query's options are, so one set of defaults serves
|
|
968
|
+
// both, except that a failure isn't retried unless asked: whoever waits
|
|
969
|
+
// for the data hears of it at once. cancelRefetch is about this call,
|
|
970
|
+
// not an option of the query.
|
|
879
971
|
const { cancelRefetch, ...queryOptions } = options;
|
|
880
972
|
const defaulted = this.defaultQueryOptions({
|
|
881
973
|
...queryOptions,
|
|
882
974
|
queryKey: key,
|
|
883
975
|
queryFn,
|
|
976
|
+
retry: queryOptions.retry ??
|
|
977
|
+
this.#config.defaultOptions?.queries?.retry ??
|
|
978
|
+
false,
|
|
884
979
|
});
|
|
885
980
|
const query = this.#cache.getOrCreate(key);
|
|
886
|
-
|
|
887
|
-
query.fetch(defaulted, { cancelRefetch })
|
|
888
|
-
|
|
981
|
+
return query.shouldFetch(defaulted.staleTime)
|
|
982
|
+
? query.fetch(defaulted, { cancelRefetch })
|
|
983
|
+
: Promise.resolve(query.state().data);
|
|
889
984
|
});
|
|
890
985
|
}
|
|
891
986
|
/**
|
|
@@ -936,11 +1031,16 @@ class QueryClient {
|
|
|
936
1031
|
* Marks matching queries as stale and refetches the actively observed ones,
|
|
937
1032
|
* once per query however many components observe it. The refetch starts
|
|
938
1033
|
* right away and replaces a fetch already in flight, which may predate the
|
|
939
|
-
* change
|
|
940
|
-
*
|
|
941
|
-
*
|
|
1034
|
+
* change. A query with no enabled observer keeps the stale mark and
|
|
1035
|
+
* refetches as soon as one appears. Commonly called after a mutation
|
|
1036
|
+
* succeeds.
|
|
1037
|
+
*
|
|
1038
|
+
* Resolves once those refetches have settled, so a mutation hook that
|
|
1039
|
+
* returns it keeps the mutation pending until the data is fresh. It never
|
|
1040
|
+
* rejects: a refetch that fails shows in its query's `error`.
|
|
942
1041
|
*
|
|
943
1042
|
* @param filters - Which queries to invalidate; omit to invalidate all.
|
|
1043
|
+
* `refetchType: 'none'` only marks them, and resolves at once.
|
|
944
1044
|
*
|
|
945
1045
|
* @example
|
|
946
1046
|
* ```ts
|
|
@@ -948,23 +1048,41 @@ class QueryClient {
|
|
|
948
1048
|
* ```
|
|
949
1049
|
*/
|
|
950
1050
|
invalidateQueries(filters) {
|
|
951
|
-
untracked(() => {
|
|
1051
|
+
return untracked(() => {
|
|
952
1052
|
const queries = this.#cache.findAll(filters);
|
|
953
1053
|
queries.forEach((query) => query.invalidate());
|
|
954
|
-
//
|
|
955
|
-
//
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
const options = query.activeFetchOptions();
|
|
960
|
-
if (options) {
|
|
961
|
-
query.fetch(options, { cancelRefetch: true });
|
|
962
|
-
}
|
|
963
|
-
});
|
|
1054
|
+
// An inactive query refetches when next observed, as shouldFetch sees
|
|
1055
|
+
// the flag.
|
|
1056
|
+
return filters?.refetchType === 'none'
|
|
1057
|
+
? Promise.resolve()
|
|
1058
|
+
: whenAllSettled(queries.map((query) => query.refetchIfActive()));
|
|
964
1059
|
});
|
|
965
1060
|
}
|
|
966
1061
|
/**
|
|
967
|
-
*
|
|
1062
|
+
* Refetches the matching queries that a component shows, whether their
|
|
1063
|
+
* data is stale or not, once per query however many components show it. A
|
|
1064
|
+
* fetch already in flight is replaced. Queries nobody shows are left as
|
|
1065
|
+
* they are, not even marked stale: to have them refetch once they're shown,
|
|
1066
|
+
* invalidate them with `invalidateQueries()`.
|
|
1067
|
+
*
|
|
1068
|
+
* Resolves once the refetches have settled. It never rejects: a refetch
|
|
1069
|
+
* that fails shows in its query's `error`.
|
|
1070
|
+
*
|
|
1071
|
+
* @param filters - Which queries to refetch; omit to refetch all.
|
|
1072
|
+
*
|
|
1073
|
+
* @example
|
|
1074
|
+
* ```ts
|
|
1075
|
+
* await client.refetchQueries({ queryKey: ['todos'] })
|
|
1076
|
+
* ```
|
|
1077
|
+
*/
|
|
1078
|
+
refetchQueries(filters) {
|
|
1079
|
+
return untracked(() => whenAllSettled(this.#cache.findAll(filters).map((query) => query.refetchIfActive())));
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Cancels any in-flight fetches for matching queries. Each query goes back
|
|
1083
|
+
* to how it was before its fetch: one that failed and was being tried
|
|
1084
|
+
* again shows its error again. Data written meanwhile stays, and so does a
|
|
1085
|
+
* stale mark from `invalidateQueries()`.
|
|
968
1086
|
*
|
|
969
1087
|
* @param filters - Which queries to cancel; omit to cancel all.
|
|
970
1088
|
*/
|
|
@@ -1379,30 +1497,38 @@ function createQueryObserver(optionsFn, injector) {
|
|
|
1379
1497
|
}
|
|
1380
1498
|
return { data: state.data, status: state.status, isPlaceholderData: false };
|
|
1381
1499
|
}, ...(ngDevMode ? [{ debugName: "resolved" }] : []));
|
|
1500
|
+
const result = {
|
|
1501
|
+
data: computed(() => resolved().data),
|
|
1502
|
+
status: computed(() => resolved().status),
|
|
1503
|
+
error: computed(() => query().state().error),
|
|
1504
|
+
isFetching: computed(() => query().state().isFetching),
|
|
1505
|
+
// A fetch in flight with no data to show: the first one, or a new attempt
|
|
1506
|
+
// after a failure. An active placeholder counts as data — the UI shows it,
|
|
1507
|
+
// not a loading state.
|
|
1508
|
+
isLoading: computed(() => query().state().isFetching && resolved().status === 'pending'),
|
|
1509
|
+
isPending: computed(() => resolved().status === 'pending'),
|
|
1510
|
+
isSuccess: computed(() => resolved().status === 'success'),
|
|
1511
|
+
isError: computed(() => resolved().status === 'error'),
|
|
1512
|
+
isPlaceholderData: computed(() => resolved().isPlaceholderData),
|
|
1513
|
+
failureCount: computed(() => query().state().failureCount),
|
|
1514
|
+
failureReason: computed(() => query().state().failureReason),
|
|
1515
|
+
// Force a fresh fetch regardless of staleTime and enabled, cancelling
|
|
1516
|
+
// any in-flight request (explicit user intent — get current data).
|
|
1517
|
+
// Untracked, as the client's methods are: called from an effect, it
|
|
1518
|
+
// mustn't make the effect depend on the options or on the query. However
|
|
1519
|
+
// the fetch ends, the signals show it, so the promise resolves with their
|
|
1520
|
+
// data and error and never rejects.
|
|
1521
|
+
refetch: () => untracked(() => {
|
|
1522
|
+
const options = defaultedOptions();
|
|
1523
|
+
return resolve(options.queryKey).fetch(options, {
|
|
1524
|
+
cancelRefetch: true,
|
|
1525
|
+
});
|
|
1526
|
+
})
|
|
1527
|
+
.catch(() => undefined)
|
|
1528
|
+
.then(() => ({ data: result.data(), error: result.error() })),
|
|
1529
|
+
};
|
|
1382
1530
|
return {
|
|
1383
|
-
result
|
|
1384
|
-
data: computed(() => resolved().data),
|
|
1385
|
-
status: computed(() => resolved().status),
|
|
1386
|
-
error: computed(() => query().state().error),
|
|
1387
|
-
isFetching: computed(() => query().state().isFetching),
|
|
1388
|
-
// First fetch in-flight: fetching with no resolved data yet. An active
|
|
1389
|
-
// placeholder counts as data — the UI shows it, not a loading state.
|
|
1390
|
-
isLoading: computed(() => query().state().isFetching && resolved().status === 'pending'),
|
|
1391
|
-
isPending: computed(() => resolved().status === 'pending'),
|
|
1392
|
-
isSuccess: computed(() => resolved().status === 'success'),
|
|
1393
|
-
isError: computed(() => resolved().status === 'error'),
|
|
1394
|
-
isPlaceholderData: computed(() => resolved().isPlaceholderData),
|
|
1395
|
-
failureCount: computed(() => query().state().failureCount),
|
|
1396
|
-
failureReason: computed(() => query().state().failureReason),
|
|
1397
|
-
// Force a fresh fetch regardless of staleTime and enabled, cancelling
|
|
1398
|
-
// any in-flight request (explicit user intent — get current data).
|
|
1399
|
-
// Untracked, as the client's methods are: called from an effect, it
|
|
1400
|
-
// mustn't make the effect depend on the options or on the query.
|
|
1401
|
-
refetch: () => untracked(() => {
|
|
1402
|
-
const options = defaultedOptions();
|
|
1403
|
-
resolve(options.queryKey).fetch(options, { cancelRefetch: true });
|
|
1404
|
-
}),
|
|
1405
|
-
},
|
|
1531
|
+
result,
|
|
1406
1532
|
destroy: () => refs.forEach((ref) => ref.destroy()),
|
|
1407
1533
|
};
|
|
1408
1534
|
}
|
|
@@ -1578,9 +1704,11 @@ function injectQueries(optionsFn, options) {
|
|
|
1578
1704
|
* until it settles. Mutations do not retry by default (a retried write is not
|
|
1579
1705
|
* idempotent); opt in via `options.retry`. Whatever of `retry`, `retryDelay`
|
|
1580
1706
|
* and the `onSuccess` / `onError` / `onSettled` hooks a mutation leaves unset
|
|
1581
|
-
* comes from {@link withDefaultOptions}. `
|
|
1582
|
-
*
|
|
1583
|
-
*
|
|
1707
|
+
* comes from {@link withDefaultOptions}. `mutateAsync(variables)` runs it
|
|
1708
|
+
* the same way and returns a promise of the run's data. `mutate()`,
|
|
1709
|
+
* `mutateAsync()` and `reset()` are safe to call from an effect: none makes
|
|
1710
|
+
* the effect depend on what it reads, such as the options or the previous
|
|
1711
|
+
* run. Bound to the current injection
|
|
1584
1712
|
* context. Destroying it doesn't cancel a run in flight: the write has most
|
|
1585
1713
|
* likely reached the server, so the run finishes and fires its hooks.
|
|
1586
1714
|
*
|
|
@@ -1592,8 +1720,8 @@ function injectQueries(optionsFn, options) {
|
|
|
1592
1720
|
* @typeParam TContext - Type returned by `onMutate`, passed to later hooks.
|
|
1593
1721
|
* @param optionsFn - Factory returning the {@link MutationOptions}.
|
|
1594
1722
|
* @param options - Optional `injector` to use outside an injection context.
|
|
1595
|
-
* @returns A {@link MutationResult}: `mutate()`, `reset()`,
|
|
1596
|
-
* (`status`, `data`, `error`, `isPending`, …).
|
|
1723
|
+
* @returns A {@link MutationResult}: `mutate()`, `mutateAsync()`, `reset()`,
|
|
1724
|
+
* and state signals (`status`, `data`, `error`, `isPending`, …).
|
|
1597
1725
|
*
|
|
1598
1726
|
* @example
|
|
1599
1727
|
* ```ts
|
|
@@ -1625,23 +1753,32 @@ function injectMutation(optionsFn, options) {
|
|
|
1625
1753
|
inject(DestroyRef).onDestroy(() => untracked(current)?.removeObserver());
|
|
1626
1754
|
const state = computed(() => current()?.state() ??
|
|
1627
1755
|
getInitialState(), ...(ngDevMode ? [{ debugName: "state" }] : []));
|
|
1756
|
+
// mutate(), mutateAsync() and reset() run untracked, as the QueryClient's
|
|
1757
|
+
// methods do. A call made from an effect mustn't make the effect depend on
|
|
1758
|
+
// what they read to do their job: the previous run's state, the options,
|
|
1759
|
+
// or what onMutate and mutationFn read. Otherwise a change to any of them
|
|
1760
|
+
// re-runs the effect, and the effect repeats the call.
|
|
1761
|
+
//
|
|
1762
|
+
// Starts a run for mutate() and mutateAsync(), makes it the run the
|
|
1763
|
+
// signals below follow, and returns its promise.
|
|
1764
|
+
const start = (variables) => untracked(() => {
|
|
1765
|
+
// Stop observing the previous run. It isn't cancelled — if it's
|
|
1766
|
+
// still in flight it finishes and fires its hooks; it just no longer
|
|
1767
|
+
// feeds the signals below.
|
|
1768
|
+
current()?.removeObserver();
|
|
1769
|
+
// Options are read per call, so a signal used in them stays live.
|
|
1770
|
+
const run = cache.build(client.defaultMutationOptions(optionsFn()));
|
|
1771
|
+
run.addObserver();
|
|
1772
|
+
current.set(run);
|
|
1773
|
+
return run.execute(variables);
|
|
1774
|
+
});
|
|
1628
1775
|
return {
|
|
1629
|
-
//
|
|
1630
|
-
//
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
// Stop observing the previous run. It isn't cancelled — if it's
|
|
1636
|
-
// still in flight it finishes and fires its hooks; it just no longer
|
|
1637
|
-
// feeds the signals below.
|
|
1638
|
-
current()?.removeObserver();
|
|
1639
|
-
// Options are read per call, so a signal used in them stays live.
|
|
1640
|
-
const run = cache.build(client.defaultMutationOptions(optionsFn()));
|
|
1641
|
-
run.addObserver();
|
|
1642
|
-
current.set(run);
|
|
1643
|
-
run.execute(variables);
|
|
1644
|
-
}),
|
|
1776
|
+
// The outcome shows in the signals; a failure mustn't surface as an
|
|
1777
|
+
// unhandled rejection too.
|
|
1778
|
+
mutate: (variables) => {
|
|
1779
|
+
start(variables).catch(() => undefined);
|
|
1780
|
+
},
|
|
1781
|
+
mutateAsync: start,
|
|
1645
1782
|
reset: () => untracked(() => {
|
|
1646
1783
|
// "Forget the result", not "stop the request". A run still in
|
|
1647
1784
|
// flight keeps going and fires its hooks, so the cache stays in step
|
|
@@ -1834,5 +1971,5 @@ function withDefaultOptions(options) {
|
|
|
1834
1971
|
* Generated bundle index. Do not edit.
|
|
1835
1972
|
*/
|
|
1836
1973
|
|
|
1837
|
-
export { QueryClient, QueryClientFeatureKind, injectIsFetching, injectIsMutating, injectMutation, injectQueries, injectQuery, injectQueryClient, keepPreviousData, mutationOptions, provideQueryClient, queryOptions, withDefaultOptions };
|
|
1974
|
+
export { CancelledError, QueryClient, QueryClientFeatureKind, injectIsFetching, injectIsMutating, injectMutation, injectQueries, injectQuery, injectQueryClient, keepPreviousData, mutationOptions, provideQueryClient, queryOptions, withDefaultOptions };
|
|
1838
1975
|
//# sourceMappingURL=ngx-signal-query.mjs.map
|