@ttt-productions/query-core 0.14.18 → 0.14.19
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/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/pending-mutations.d.ts +29 -0
- package/dist/react/pending-mutations.d.ts.map +1 -0
- package/dist/react/pending-mutations.js +85 -0
- package/dist/react/pending-mutations.js.map +1 -0
- package/package.json +1 -1
package/dist/react/index.d.ts
CHANGED
|
@@ -20,4 +20,5 @@ export { ABSENT_RETRY_DELAYS_MS } from './firestore/absence-scheduler.js';
|
|
|
20
20
|
export { useFirestoreSearch } from './search/use-firestore-search.js';
|
|
21
21
|
export { useReadRetryState, useReadRetryStates } from './use-read-retry-state.js';
|
|
22
22
|
export type { ReadRetryEntry, ReadRetrySource, ReadRetryState } from './use-read-retry-state.js';
|
|
23
|
+
export { usePendingMutationVariables, pendingMutationVariables, usePendingMutationTargets, hasPendingMutation, duplicateSubmissionError, useDuplicateGuardedMutation, } from './pending-mutations.js';
|
|
23
24
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAGnE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGxD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC3E,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAGrE,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAC7G,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,YAAY,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AACtF,YAAY,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AAG9F,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wCAAwC,CAAC;AAGhD,OAAO,EACL,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACV,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAG1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAGtE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAClF,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,YAAY,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAGnE,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGxD,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC3E,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAGrE,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAC7G,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAC/E,YAAY,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAC1F,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AACtF,YAAY,EAAE,2BAA2B,EAAE,MAAM,4CAA4C,CAAC;AAG9F,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wCAAwC,CAAC;AAGhD,OAAO,EACL,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACV,yBAAyB,EACzB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAG1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAGtE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAClF,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIjG,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,kBAAkB,EAClB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,wBAAwB,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -21,4 +21,7 @@ export { ABSENT_RETRY_DELAYS_MS } from './firestore/absence-scheduler.js';
|
|
|
21
21
|
export { useFirestoreSearch } from './search/use-firestore-search.js';
|
|
22
22
|
// Read retry state
|
|
23
23
|
export { useReadRetryState, useReadRetryStates } from './use-read-retry-state.js';
|
|
24
|
+
// Authoritative pending for interactive mutations: in-flight state and duplicate guards read from
|
|
25
|
+
// the shared MutationCache under the app's stable mutation keys.
|
|
26
|
+
export { usePendingMutationVariables, pendingMutationVariables, usePendingMutationTargets, hasPendingMutation, duplicateSubmissionError, useDuplicateGuardedMutation, } from './pending-mutations.js';
|
|
24
27
|
//# sourceMappingURL=index.js.map
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,qEAAqE;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGvD,WAAW;AACX,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,qBAAqB;AACrB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG3E,wBAAwB;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAEjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAE/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAGtF,2BAA2B;AAC3B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wCAAwC,CAAC;AAEhD,uBAAuB;AACvB,OAAO,EACL,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AAM9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAE1E,cAAc;AACd,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEtE,mBAAmB;AACnB,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC","sourcesContent":["\"use client\";\n\n// Query Client factory (client-only; requires @tanstack/react-query)\nexport { createQueryClient } from '../query-client.js';\nexport type { CreateQueryClientOptions } from '../query-client.js';\n\n// Provider\nexport { QueryProvider } from './provider.js';\nexport type { QueryProviderProps } from './provider.js';\n\n// Firestore Provider\nexport { FirestoreProvider, useFirestoreDb } from './firestore/context.js';\nexport type { FirestoreProviderProps } from './firestore/context.js';\n\n// Firestore Query Hooks\nexport { useFirestoreDoc } from './firestore/use-firestore-doc.js';\nexport { useFirestoreCollection } from './firestore/use-firestore-collection.js';\nexport type { FirestoreSourceState, WithSourceState, BatchFirestoreDocOutcome } from '../firestore/types.js';\nexport { useFirestoreInfinite } from './firestore/use-firestore-infinite.js';\nexport { useFirestorePaginated } from './firestore/use-firestore-paginated.js';\nexport type { UseFirestorePaginatedResult } from './firestore/use-firestore-paginated.js';\nexport { useFirestoreCount } from './firestore/use-firestore-count.js';\nexport { useFirestoreLiveInfinite } from './firestore/use-firestore-live-infinite.js';\nexport type { FirestoreLiveInfiniteResult } from './firestore/use-firestore-live-infinite.js';\n\n// Firestore Mutation Hooks\nexport {\n useFirestoreSet,\n useFirestoreUpdate,\n useFirestoreDelete,\n useFirestoreBatch,\n} from './firestore/use-firestore-mutations.js';\n\n// Batch Firestore Docs\nexport {\n useBatchFirestoreDocs,\n} from './firestore/useBatchFirestoreDocs.js';\nexport type {\n BatchFirestoreDocsOptions,\n BatchFirestoreDocsResult,\n BatchFirestoreDocsTransport,\n} from './firestore/useBatchFirestoreDocs.js';\nexport { ABSENT_RETRY_DELAYS_MS } from './firestore/absence-scheduler.js';\n\n// Search Hook\nexport { useFirestoreSearch } from './search/use-firestore-search.js';\n\n// Read retry state\nexport { useReadRetryState, useReadRetryStates } from './use-read-retry-state.js';\nexport type { ReadRetryEntry, ReadRetrySource, ReadRetryState } from './use-read-retry-state.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,qEAAqE;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGvD,WAAW;AACX,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG9C,qBAAqB;AACrB,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAG3E,wBAAwB;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAEjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAE/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAGtF,2BAA2B;AAC3B,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wCAAwC,CAAC;AAEhD,uBAAuB;AACvB,OAAO,EACL,qBAAqB,GACtB,MAAM,sCAAsC,CAAC;AAM9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAE1E,cAAc;AACd,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEtE,mBAAmB;AACnB,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAGlF,kGAAkG;AAClG,iEAAiE;AACjE,OAAO,EACL,2BAA2B,EAC3B,wBAAwB,EACxB,yBAAyB,EACzB,kBAAkB,EAClB,wBAAwB,EACxB,2BAA2B,GAC5B,MAAM,wBAAwB,CAAC","sourcesContent":["\"use client\";\n\n// Query Client factory (client-only; requires @tanstack/react-query)\nexport { createQueryClient } from '../query-client.js';\nexport type { CreateQueryClientOptions } from '../query-client.js';\n\n// Provider\nexport { QueryProvider } from './provider.js';\nexport type { QueryProviderProps } from './provider.js';\n\n// Firestore Provider\nexport { FirestoreProvider, useFirestoreDb } from './firestore/context.js';\nexport type { FirestoreProviderProps } from './firestore/context.js';\n\n// Firestore Query Hooks\nexport { useFirestoreDoc } from './firestore/use-firestore-doc.js';\nexport { useFirestoreCollection } from './firestore/use-firestore-collection.js';\nexport type { FirestoreSourceState, WithSourceState, BatchFirestoreDocOutcome } from '../firestore/types.js';\nexport { useFirestoreInfinite } from './firestore/use-firestore-infinite.js';\nexport { useFirestorePaginated } from './firestore/use-firestore-paginated.js';\nexport type { UseFirestorePaginatedResult } from './firestore/use-firestore-paginated.js';\nexport { useFirestoreCount } from './firestore/use-firestore-count.js';\nexport { useFirestoreLiveInfinite } from './firestore/use-firestore-live-infinite.js';\nexport type { FirestoreLiveInfiniteResult } from './firestore/use-firestore-live-infinite.js';\n\n// Firestore Mutation Hooks\nexport {\n useFirestoreSet,\n useFirestoreUpdate,\n useFirestoreDelete,\n useFirestoreBatch,\n} from './firestore/use-firestore-mutations.js';\n\n// Batch Firestore Docs\nexport {\n useBatchFirestoreDocs,\n} from './firestore/useBatchFirestoreDocs.js';\nexport type {\n BatchFirestoreDocsOptions,\n BatchFirestoreDocsResult,\n BatchFirestoreDocsTransport,\n} from './firestore/useBatchFirestoreDocs.js';\nexport { ABSENT_RETRY_DELAYS_MS } from './firestore/absence-scheduler.js';\n\n// Search Hook\nexport { useFirestoreSearch } from './search/use-firestore-search.js';\n\n// Read retry state\nexport { useReadRetryState, useReadRetryStates } from './use-read-retry-state.js';\nexport type { ReadRetryEntry, ReadRetrySource, ReadRetryState } from './use-read-retry-state.js';\n\n// Authoritative pending for interactive mutations: in-flight state and duplicate guards read from\n// the shared MutationCache under the app's stable mutation keys.\nexport {\n usePendingMutationVariables,\n pendingMutationVariables,\n usePendingMutationTargets,\n hasPendingMutation,\n duplicateSubmissionError,\n useDuplicateGuardedMutation,\n} from './pending-mutations.js';\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type MutationKey, type QueryClient, type UseMutationResult } from "@tanstack/react-query";
|
|
2
|
+
/** The variables of every pending mutation under `mutationKey` (prefix-matched), live. */
|
|
3
|
+
export declare function usePendingMutationVariables<V>(mutationKey: MutationKey): ReadonlyArray<V | undefined>;
|
|
4
|
+
/** The variables of every pending mutation under `mutationKey`, read once — for a duplicate guard. */
|
|
5
|
+
export declare function pendingMutationVariables<V>(queryClient: QueryClient, mutationKey: MutationKey): Array<V | undefined>;
|
|
6
|
+
/**
|
|
7
|
+
* The targets of every pending mutation under `mutationKey`, live — for a list whose rows each
|
|
8
|
+
* ask "is mine pending?" through one hook call. `targetOf` names a variables' target, or returns
|
|
9
|
+
* nothing for variables without one.
|
|
10
|
+
*/
|
|
11
|
+
export declare function usePendingMutationTargets<V>(mutationKey: MutationKey, targetOf: (variables: V) => string | null | undefined): ReadonlySet<string>;
|
|
12
|
+
/**
|
|
13
|
+
* True when a mutation under `mutationKey` is pending — any one, or with `matches`, one whose
|
|
14
|
+
* variables are for the same target.
|
|
15
|
+
*/
|
|
16
|
+
export declare function hasPendingMutation<V>(queryClient: QueryClient, mutationKey: MutationKey, matches?: (variables: V) => boolean): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The rejection a guarded `mutateAsync` throws for a duplicate submission: an `AbortError`, the
|
|
19
|
+
* shape a caller already treats as "nothing happened" rather than a failure to report.
|
|
20
|
+
*/
|
|
21
|
+
export declare function duplicateSubmissionError(what: string): Error;
|
|
22
|
+
/**
|
|
23
|
+
* Wrap a keyed mutation's `mutate` / `mutateAsync` in the cache-consulting duplicate guard:
|
|
24
|
+
* `mutate` drops a duplicate silently, and `mutateAsync` rejects it with
|
|
25
|
+
* {@link duplicateSubmissionError}. `isDuplicate` must have a stable identity (declare it at module
|
|
26
|
+
* level) so the guarded callbacks keep theirs.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useDuplicateGuardedMutation<TData, TError, TVariables, TContext>(mutation: UseMutationResult<TData, TError, TVariables, TContext>, isDuplicate: (queryClient: QueryClient, variables: TVariables) => boolean, what: string): UseMutationResult<TData, TError, TVariables, TContext>;
|
|
29
|
+
//# sourceMappingURL=pending-mutations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-mutations.d.ts","sourceRoot":"","sources":["../../src/react/pending-mutations.ts"],"names":[],"mappings":"AAeA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACvB,MAAM,uBAAuB,CAAC;AAE/B,0FAA0F;AAC1F,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,WAAW,EAAE,WAAW,GAAG,aAAa,CAAC,CAAC,GAAG,SAAS,CAAC,CAKrG;AAED,sGAAsG;AACtG,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,GACvB,KAAK,CAAC,CAAC,GAAG,SAAS,CAAC,CAKtB;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,EACzC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,GACpD,WAAW,CAAC,MAAM,CAAC,CAQrB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAClC,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,EACxB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,OAAO,GAClC,OAAO,CAIT;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAI5D;AAED;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAC7E,QAAQ,EAAE,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,EAChE,WAAW,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,KAAK,OAAO,EACzE,IAAI,EAAE,MAAM,GACX,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,CAAC,CAkBxD"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// Authoritative pending for interactive mutations. A mutation registers in React Query's shared
|
|
3
|
+
// MutationCache under a stable `mutationKey` the app declares (one key per action), and its target
|
|
4
|
+
// travels in the mutation's variables. Pending is read from that cache, never from one mounted
|
|
5
|
+
// control's `isPending`, which gives three guarantees:
|
|
6
|
+
//
|
|
7
|
+
// 1. an in-flight request survives its originating control unmounting;
|
|
8
|
+
// 2. a remounted control still renders pending for that target;
|
|
9
|
+
// 3. a second submission for the same target is refused while the first is pending.
|
|
10
|
+
//
|
|
11
|
+
// The app owns its keys, its variable shapes and what counts as the same target; this module owns
|
|
12
|
+
// only the mechanics.
|
|
13
|
+
import { useCallback } from "react";
|
|
14
|
+
import { useMutationState, useQueryClient, } from "@tanstack/react-query";
|
|
15
|
+
/** The variables of every pending mutation under `mutationKey` (prefix-matched), live. */
|
|
16
|
+
export function usePendingMutationVariables(mutationKey) {
|
|
17
|
+
return useMutationState({
|
|
18
|
+
filters: { mutationKey, status: "pending" },
|
|
19
|
+
select: (mutation) => mutation.state.variables,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/** The variables of every pending mutation under `mutationKey`, read once — for a duplicate guard. */
|
|
23
|
+
export function pendingMutationVariables(queryClient, mutationKey) {
|
|
24
|
+
return queryClient
|
|
25
|
+
.getMutationCache()
|
|
26
|
+
.findAll({ mutationKey, status: "pending" })
|
|
27
|
+
.map((mutation) => mutation.state.variables);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The targets of every pending mutation under `mutationKey`, live — for a list whose rows each
|
|
31
|
+
* ask "is mine pending?" through one hook call. `targetOf` names a variables' target, or returns
|
|
32
|
+
* nothing for variables without one.
|
|
33
|
+
*/
|
|
34
|
+
export function usePendingMutationTargets(mutationKey, targetOf) {
|
|
35
|
+
const targets = new Set();
|
|
36
|
+
for (const variables of usePendingMutationVariables(mutationKey)) {
|
|
37
|
+
if (variables === undefined)
|
|
38
|
+
continue;
|
|
39
|
+
const target = targetOf(variables);
|
|
40
|
+
if (target)
|
|
41
|
+
targets.add(target);
|
|
42
|
+
}
|
|
43
|
+
return targets;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* True when a mutation under `mutationKey` is pending — any one, or with `matches`, one whose
|
|
47
|
+
* variables are for the same target.
|
|
48
|
+
*/
|
|
49
|
+
export function hasPendingMutation(queryClient, mutationKey, matches) {
|
|
50
|
+
const pending = pendingMutationVariables(queryClient, mutationKey);
|
|
51
|
+
if (!matches)
|
|
52
|
+
return pending.length > 0;
|
|
53
|
+
return pending.some((variables) => variables !== undefined && matches(variables));
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The rejection a guarded `mutateAsync` throws for a duplicate submission: an `AbortError`, the
|
|
57
|
+
* shape a caller already treats as "nothing happened" rather than a failure to report.
|
|
58
|
+
*/
|
|
59
|
+
export function duplicateSubmissionError(what) {
|
|
60
|
+
const duplicate = new Error(`${what} is already in flight.`);
|
|
61
|
+
duplicate.name = "AbortError";
|
|
62
|
+
return duplicate;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Wrap a keyed mutation's `mutate` / `mutateAsync` in the cache-consulting duplicate guard:
|
|
66
|
+
* `mutate` drops a duplicate silently, and `mutateAsync` rejects it with
|
|
67
|
+
* {@link duplicateSubmissionError}. `isDuplicate` must have a stable identity (declare it at module
|
|
68
|
+
* level) so the guarded callbacks keep theirs.
|
|
69
|
+
*/
|
|
70
|
+
export function useDuplicateGuardedMutation(mutation, isDuplicate, what) {
|
|
71
|
+
const queryClient = useQueryClient();
|
|
72
|
+
const { mutate: rawMutate, mutateAsync: rawMutateAsync } = mutation;
|
|
73
|
+
const mutate = useCallback((variables, options) => {
|
|
74
|
+
if (isDuplicate(queryClient, variables))
|
|
75
|
+
return;
|
|
76
|
+
rawMutate(variables, options);
|
|
77
|
+
}, [queryClient, rawMutate, isDuplicate]);
|
|
78
|
+
const mutateAsync = useCallback(async (variables, options) => {
|
|
79
|
+
if (isDuplicate(queryClient, variables))
|
|
80
|
+
throw duplicateSubmissionError(what);
|
|
81
|
+
return rawMutateAsync(variables, options);
|
|
82
|
+
}, [queryClient, rawMutateAsync, isDuplicate, what]);
|
|
83
|
+
return { ...mutation, mutate, mutateAsync };
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=pending-mutations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-mutations.js","sourceRoot":"","sources":["../../src/react/pending-mutations.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\n// Authoritative pending for interactive mutations. A mutation registers in React Query's shared\n// MutationCache under a stable `mutationKey` the app declares (one key per action), and its target\n// travels in the mutation's variables. Pending is read from that cache, never from one mounted\n// control's `isPending`, which gives three guarantees:\n//\n// 1. an in-flight request survives its originating control unmounting;\n// 2. a remounted control still renders pending for that target;\n// 3. a second submission for the same target is refused while the first is pending.\n//\n// The app owns its keys, its variable shapes and what counts as the same target; this module owns\n// only the mechanics.\n\nimport { useCallback } from \"react\";\nimport {\n useMutationState,\n useQueryClient,\n type MutateOptions,\n type MutationKey,\n type QueryClient,\n type UseMutationResult,\n} from \"@tanstack/react-query\";\n\n/** The variables of every pending mutation under `mutationKey` (prefix-matched), live. */\nexport function usePendingMutationVariables<V>(mutationKey: MutationKey): ReadonlyArray<V | undefined> {\n return useMutationState({\n filters: { mutationKey, status: \"pending\" },\n select: (mutation) => mutation.state.variables as V | undefined,\n });\n}\n\n/** The variables of every pending mutation under `mutationKey`, read once — for a duplicate guard. */\nexport function pendingMutationVariables<V>(\n queryClient: QueryClient,\n mutationKey: MutationKey,\n): Array<V | undefined> {\n return queryClient\n .getMutationCache()\n .findAll({ mutationKey, status: \"pending\" })\n .map((mutation) => mutation.state.variables as V | undefined);\n}\n\n/**\n * The targets of every pending mutation under `mutationKey`, live — for a list whose rows each\n * ask \"is mine pending?\" through one hook call. `targetOf` names a variables' target, or returns\n * nothing for variables without one.\n */\nexport function usePendingMutationTargets<V>(\n mutationKey: MutationKey,\n targetOf: (variables: V) => string | null | undefined,\n): ReadonlySet<string> {\n const targets = new Set<string>();\n for (const variables of usePendingMutationVariables<V>(mutationKey)) {\n if (variables === undefined) continue;\n const target = targetOf(variables);\n if (target) targets.add(target);\n }\n return targets;\n}\n\n/**\n * True when a mutation under `mutationKey` is pending — any one, or with `matches`, one whose\n * variables are for the same target.\n */\nexport function hasPendingMutation<V>(\n queryClient: QueryClient,\n mutationKey: MutationKey,\n matches?: (variables: V) => boolean,\n): boolean {\n const pending = pendingMutationVariables<V>(queryClient, mutationKey);\n if (!matches) return pending.length > 0;\n return pending.some((variables) => variables !== undefined && matches(variables));\n}\n\n/**\n * The rejection a guarded `mutateAsync` throws for a duplicate submission: an `AbortError`, the\n * shape a caller already treats as \"nothing happened\" rather than a failure to report.\n */\nexport function duplicateSubmissionError(what: string): Error {\n const duplicate = new Error(`${what} is already in flight.`);\n duplicate.name = \"AbortError\";\n return duplicate;\n}\n\n/**\n * Wrap a keyed mutation's `mutate` / `mutateAsync` in the cache-consulting duplicate guard:\n * `mutate` drops a duplicate silently, and `mutateAsync` rejects it with\n * {@link duplicateSubmissionError}. `isDuplicate` must have a stable identity (declare it at module\n * level) so the guarded callbacks keep theirs.\n */\nexport function useDuplicateGuardedMutation<TData, TError, TVariables, TContext>(\n mutation: UseMutationResult<TData, TError, TVariables, TContext>,\n isDuplicate: (queryClient: QueryClient, variables: TVariables) => boolean,\n what: string,\n): UseMutationResult<TData, TError, TVariables, TContext> {\n const queryClient = useQueryClient();\n const { mutate: rawMutate, mutateAsync: rawMutateAsync } = mutation;\n const mutate = useCallback(\n (variables: TVariables, options?: MutateOptions<TData, TError, TVariables, TContext>) => {\n if (isDuplicate(queryClient, variables)) return;\n rawMutate(variables, options);\n },\n [queryClient, rawMutate, isDuplicate],\n );\n const mutateAsync = useCallback(\n async (variables: TVariables, options?: MutateOptions<TData, TError, TVariables, TContext>) => {\n if (isDuplicate(queryClient, variables)) throw duplicateSubmissionError(what);\n return rawMutateAsync(variables, options);\n },\n [queryClient, rawMutateAsync, isDuplicate, what],\n );\n return { ...mutation, mutate, mutateAsync } as UseMutationResult<TData, TError, TVariables, TContext>;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttt-productions/query-core",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.19",
|
|
4
4
|
"description": "Shared TanStack Query (React Query) client defaults, query key helpers, and Firestore integration hooks for TTT Productions apps",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|