@veams/status-quo-query 0.12.0 → 0.13.1

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.
@@ -1,14 +1,14 @@
1
1
 
2
2
  
3
- > @veams/status-quo-query@0.3.0 build
3
+ > @veams/status-quo-query@0.12.0 build
4
4
  > npm-run-all compile
5
5
 
6
6
 
7
- > @veams/status-quo-query@0.3.0 compile
7
+ > @veams/status-quo-query@0.12.0 compile
8
8
  > npm-run-all bundle:ts
9
9
 
10
10
 
11
- > @veams/status-quo-query@0.3.0 bundle:ts
11
+ > @veams/status-quo-query@0.12.0 bundle:ts
12
12
  > tsc --project tsconfig.json
13
13
 
14
14
  ⠙⠙⠙
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ - Fixed `QueryHandle.subscribe(...)` to mount the TanStack `QueryClient` lifecycle while a query handle subscription is active, restoring `refetchOnWindowFocus` and reconnect behavior for stale subscribed queries.
6
+ - Documented how `staleTime`, `refetchOnWindowFocus`, `bindSubscribable(...)`, and passive cache reads interact in the wrapper.
7
+
3
8
  ## 0.1.0
4
9
 
5
10
  - Initial package scaffold for TanStack Query and mutation services.
package/README.md CHANGED
@@ -19,11 +19,11 @@ npm install react
19
19
  Status Quo Query deliberately keeps the public surface small:
20
20
 
21
21
  - `QueryHandle<TData, TError>` is the read handle for one query.
22
- - `MutationService<TData, TError, TVariables>` is the write handle for one mutation.
22
+ - `MutationHandle<TData, TError, TVariables>` is the write handle for one mutation.
23
23
  - snapshots are passive state objects returned from `getSnapshot()` and `subscribe(...)`.
24
24
  - commands stay on the handle: `refetch()`, `invalidate()`, `mutate()`, `reset()`.
25
25
  - `QueryManager` is the broader coordination layer for cross-query work.
26
- - `@veams/status-quo-query/react` is optional and adds one React subscription hook over the same handle shape.
26
+ - `@veams/status-quo-query/react` is optional and adds React subscription hooks (`useQueryHandle`, `useMutationHandle`) over the same handle shape.
27
27
 
28
28
  That keeps the package usable in service code, query handlers, state handlers, and React components without changing the core query or mutation API.
29
29
 
@@ -49,13 +49,13 @@ Root exports:
49
49
  - `CreateUntrackedMutation`
50
50
  - `QueryHandle`
51
51
  - `QueryHandleData`
52
- - `MutationService`
52
+ - `MutationHandle`
53
53
  - `QueryHandleSnapshot`
54
- - `MutationServiceSnapshot`
54
+ - `MutationHandleSnapshot`
55
55
  - `QueryDependencyTuple`
56
56
  - `QueryHandleOptions`
57
- - `MutationServiceOptions`
58
- - `TrackedMutationServiceOptions`
57
+ - `MutationHandleOptions`
58
+ - `TrackedMutationHandleOptions`
59
59
  - `QueryInvalidateOptions`
60
60
  - `QueryMetaState`
61
61
  - `TrackedDependencyRecord`
@@ -70,7 +70,7 @@ Subpath exports:
70
70
  - `@veams/status-quo-query/provider`
71
71
  - `@veams/status-quo-query/query`
72
72
  - `@veams/status-quo-query/mutation`
73
- - `@veams/status-quo-query/react`
73
+ - `@veams/status-quo-query/react` (`useQueryHandle`, `useMutationHandle`)
74
74
 
75
75
  ## Quickstart
76
76
 
@@ -133,8 +133,9 @@ await userQuery.invalidate({ refetchType: 'none' });
133
133
 
134
134
  ## React Bindings
135
135
 
136
- The React entrypoint exposes `useQueryHandle(...)` and keeps `react` optional unless you
137
- import `@veams/status-quo-query/react`.
136
+ The React entrypoint exposes `useQueryHandle(...)` and `useMutationHandle(...)` and keeps `react` optional unless you import `@veams/status-quo-query/react`.
137
+
138
+ ### `useQueryHandle`
138
139
 
139
140
  ```tsx
140
141
  import { useQueryHandle } from '@veams/status-quo-query/react';
@@ -153,6 +154,35 @@ Use the hook when a component should subscribe directly to a query handle and re
153
154
  - call `query.refetch()` or `query.invalidate()` on the handle itself
154
155
  - derive view-specific values in the component instead of adding selector logic to the hook
155
156
 
157
+ ### `useMutationHandle`
158
+
159
+ ```tsx
160
+ import { useMutationHandle } from '@veams/status-quo-query/react';
161
+ import type { MutationHandle } from '@veams/status-quo-query';
162
+
163
+ function SaveButton({
164
+ mutation,
165
+ payload,
166
+ }: {
167
+ mutation: MutationHandle<{ ok: boolean }, Error, { name: string }>;
168
+ payload: { name: string };
169
+ }) {
170
+ const snapshot = useMutationHandle(mutation);
171
+
172
+ return (
173
+ <button onClick={() => mutation.mutate(payload)} disabled={snapshot.isPending}>
174
+ {snapshot.isPending ? 'Saving…' : 'Save'}
175
+ </button>
176
+ );
177
+ }
178
+ ```
179
+
180
+ Use `useMutationHandle` when a component should react to mutation state (pending, success, error). Keep imperative calls on the handle itself:
181
+
182
+ - read `status`, `isPending`, `isError`, `isSuccess`, `data`, `error`, `variables` from the snapshot
183
+ - call `mutation.mutate(variables)` or `mutation.reset()` on the handle itself
184
+ - the hook does not trigger the mutation — it only subscribes to its state
185
+
156
186
  ## Status Quo Integration
157
187
 
158
188
  The same query handle can also feed a `status-quo` handler through `bindSubscribable(...)`.
@@ -763,7 +793,7 @@ Only `deps` participates in automatic invalidation tracking. `view` is optional
763
793
 
764
794
  `createQuery(queryKey, queryFn, options?)` returns the same `QueryHandle<TData, TError>` shape as `createUntrackedQuery(...)`, but it registers the query hash under every `deps` entry, re-registers on `refetch()` or the first `subscribe(...)` if TanStack has removed the cache entry in the meantime, and keeps the registry in sync when `dependsOn` derives a new tracked key at runtime.
765
795
 
766
- `createMutation(mutationFn, options?)` returns the same `MutationService<TData, TError, TVariables, TOnMutateResult>` shape as `createUntrackedMutation(...)`, but adds:
796
+ `createMutation(mutationFn, options?)` returns the same `MutationHandle<TData, TError, TVariables, TOnMutateResult>` shape as `createUntrackedMutation(...)`, but adds:
767
797
 
768
798
  - `dependencyKeys?`
769
799
  - `resolveDependencies?`
@@ -786,7 +816,7 @@ Captures dependency names once and returns:
786
816
  - the tracked query factory
787
817
  - a tracked mutation factory whose default resolver reads `variables[dependencyKey]`
788
818
 
789
- The tracked query factory still expects a query key with a final `{ deps, view? }` segment. The tracked mutation factory keeps the same `MutationService` shape as `createMutation(...)`, but no longer needs `dependencyKeys` repeated in each call.
819
+ The tracked query factory still expects a query key with a final `{ deps, view? }` segment. The tracked mutation factory keeps the same `MutationHandle` shape as `createMutation(...)`, but no longer needs `dependencyKeys` repeated in each call.
790
820
 
791
821
  Reach for standalone `createMutation(...)` when:
792
822
 
@@ -840,15 +870,106 @@ It also adds:
840
870
 
841
871
  `unsafe_getResult()` returns the raw TanStack `QueryObserverResult`.
842
872
 
873
+ ### Window focus and stale queries
874
+
875
+ `QueryHandle.subscribe(...)` activates the same TanStack `QueryClient` lifecycle that
876
+ `QueryClientProvider` activates for `useQuery`.
877
+
878
+ TanStack's `refetchOnWindowFocus` support does not live on the individual
879
+ `QueryObserver` alone. A `QueryClient` has to be mounted so TanStack can subscribe to
880
+ the global `focusManager` and forward browser focus changes to the query cache. Native
881
+ React Query users usually get this through:
882
+
883
+ ```tsx
884
+ <QueryClientProvider client={queryClient}>
885
+ <App />
886
+ </QueryClientProvider>
887
+ ```
888
+
889
+ Status Quo Query does not require that provider because query handles can be used in
890
+ service code, state handlers, and React components. Instead, a subscribed query handle
891
+ mounts the provided `QueryClient` for as long as that handle subscription is active:
892
+
893
+ ```ts
894
+ const query = createUntrackedQuery(['clock'], fetchClock, {
895
+ staleTime: 5_000,
896
+ refetchOnWindowFocus: true,
897
+ });
898
+
899
+ const unsubscribe = query.subscribe((snapshot) => {
900
+ console.log(snapshot.data);
901
+ });
902
+
903
+ // Later, when the state handler or component is destroyed:
904
+ unsubscribe();
905
+ ```
906
+
907
+ With an active subscription, the flow is:
908
+
909
+ 1. The first `subscribe(...)` mounts the `QueryClient`.
910
+ 2. TanStack installs its focus and online listeners through `focusManager` and
911
+ `onlineManager`.
912
+ 3. The `QueryObserver` performs its normal mount behavior and starts stale timers.
913
+ 4. Once `staleTime` has elapsed, the observer marks the current result stale.
914
+ 5. When the browser window becomes focused again, TanStack calls `queryCache.onFocus()`.
915
+ 6. Active stale queries with `refetchOnWindowFocus: true` refetch.
916
+ 7. Unsubscribing removes the observer and unmounts the client lifecycle for that
917
+ subscription.
918
+
919
+ This means `staleTime` and `refetchOnWindowFocus` behave the same way through a
920
+ `QueryHandle` subscription as they do through `useQuery`:
921
+
922
+ ```ts
923
+ const query = createUntrackedQuery(
924
+ ['staleTime'],
925
+ () => Promise.resolve(new Date()),
926
+ {
927
+ staleTime: 5_000,
928
+ }
929
+ );
930
+ ```
931
+
932
+ The default TanStack behavior is preserved. `refetchOnWindowFocus` defaults to `true`,
933
+ so the query above refetches on focus only after it has become stale. Use
934
+ `refetchOnWindowFocus: 'always'` when focus should refetch even while the cached data is
935
+ still fresh, or `refetchOnWindowFocus: false` when focus should never refetch that
936
+ query.
937
+
938
+ `bindSubscribable(...)` integrations also participate automatically because they call
939
+ `QueryHandle.subscribe(...)` internally:
940
+
941
+ ```ts
942
+ this.bindSubscribable(productQuery, (snapshot) => {
943
+ this.setState({
944
+ product: snapshot.data,
945
+ productStatus: snapshot.status,
946
+ });
947
+ });
948
+ ```
949
+
950
+ The state handler receives:
951
+
952
+ - an initial snapshot, either synchronously from the observer or through
953
+ `getSnapshot()`
954
+ - the stale-result update after `staleTime` expires
955
+ - a fresh snapshot after the window regains focus and the stale query refetches
956
+
957
+ One-off reads do not install focus listeners. `getSnapshot()`, `getQueryData(...)`,
958
+ `getQueryState(...)`, and `fetchQuery(...)` are passive cache reads or explicit fetches;
959
+ they are not live observers. Use `subscribe(...)`, `useQueryHandle(...)`, or
960
+ `bindSubscribable(...)` when the query should respond to TanStack observer lifecycle
961
+ events such as `refetchOnWindowFocus`, `refetchOnReconnect`, stale timers, and refetch
962
+ intervals.
963
+
843
964
  ### `setupMutation(queryClient)`
844
965
 
845
966
  Creates a `createUntrackedMutation` factory bound to a `QueryClient`.
846
967
 
847
- `createUntrackedMutation(mutationFn, options?)` returns `MutationService<TData, TError, TVariables, TOnMutateResult>`.
968
+ `createUntrackedMutation(mutationFn, options?)` returns `MutationHandle<TData, TError, TVariables, TOnMutateResult>`.
848
969
 
849
- `MutationServiceOptions` is based on TanStack `MutationObserverOptions`, without `mutationFn` because it is provided directly to `createUntrackedMutation`.
970
+ `MutationHandleOptions` is based on TanStack `MutationObserverOptions`, without `mutationFn` because it is provided directly to `createUntrackedMutation`.
850
971
 
851
- `MutationService` methods:
972
+ `MutationHandle` methods:
852
973
 
853
974
  - `getSnapshot()`
854
975
  - `subscribe(listener)`
@@ -856,7 +977,7 @@ Creates a `createUntrackedMutation` factory bound to a `QueryClient`.
856
977
  - `reset()`
857
978
  - `unsafe_getResult()`
858
979
 
859
- `MutationServiceSnapshot<TData, TError, TVariables>` fields:
980
+ `MutationHandleSnapshot<TData, TError, TVariables>` fields:
860
981
 
861
982
  - `data`
862
983
  - `error`
@@ -4,7 +4,7 @@ export type MutationStatus = TanstackMutationStatus;
4
4
  /**
5
5
  * Represents a stable snapshot of the mutation service's state.
6
6
  */
7
- export interface MutationServiceSnapshot<TData = unknown, TError = Error, TVariables = void> {
7
+ export interface MutationHandleSnapshot<TData = unknown, TError = Error, TVariables = void> {
8
8
  data: TData | undefined;
9
9
  error: TError | null;
10
10
  status: MutationStatus;
@@ -17,9 +17,9 @@ export interface MutationServiceSnapshot<TData = unknown, TError = Error, TVaria
17
17
  /**
18
18
  * Defines the public API for a mutation service.
19
19
  */
20
- export interface MutationService<TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown> {
21
- getSnapshot: () => MutationServiceSnapshot<TData, TError, TVariables>;
22
- subscribe: (listener: (snapshot: MutationServiceSnapshot<TData, TError, TVariables>) => void) => () => void;
20
+ export interface MutationHandle<TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown> {
21
+ getSnapshot: () => MutationHandleSnapshot<TData, TError, TVariables>;
22
+ subscribe: (listener: (snapshot: MutationHandleSnapshot<TData, TError, TVariables>) => void) => () => void;
23
23
  mutate: (variables: TVariables, options?: MutateOptions<TData, TError, TVariables, TOnMutateResult>) => Promise<TData>;
24
24
  reset: () => void;
25
25
  unsafe_getResult: () => MutationObserverResult<TData, TError, TVariables, TOnMutateResult>;
@@ -27,12 +27,12 @@ export interface MutationService<TData = unknown, TError = Error, TVariables = v
27
27
  /**
28
28
  * Configuration options for creating a mutation service, excluding the mutation function itself.
29
29
  */
30
- export type MutationServiceOptions<TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown> = Omit<MutationObserverOptions<TData, TError, TVariables, TOnMutateResult>, 'mutationFn'>;
30
+ export type MutationHandleOptions<TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown> = Omit<MutationObserverOptions<TData, TError, TVariables, TOnMutateResult>, 'mutationFn'>;
31
31
  /**
32
32
  * Function signature for the untracked mutation factory.
33
33
  */
34
34
  export interface CreateUntrackedMutation {
35
- <TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(mutationFn: MutationFunction<TData, TVariables>, options?: MutationServiceOptions<TData, TError, TVariables, TOnMutateResult>): MutationService<TData, TError, TVariables, TOnMutateResult>;
35
+ <TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(mutationFn: MutationFunction<TData, TVariables>, options?: MutationHandleOptions<TData, TError, TVariables, TOnMutateResult>): MutationHandle<TData, TError, TVariables, TOnMutateResult>;
36
36
  }
37
37
  /**
38
38
  * Additional options for tracked mutations that invalidate queries automatically.
@@ -41,7 +41,7 @@ export interface CreateUntrackedMutation {
41
41
  * options only describe how the facade should derive dependency values and when it should
42
42
  * invalidate matching tracked queries after the mutation lifecycle settles.
43
43
  */
44
- export interface TrackedMutationServiceOptions<TDeps extends TrackedDependencyRecord = TrackedDependencyRecord, TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown> extends MutationServiceOptions<TData, TError, TVariables, TOnMutateResult> {
44
+ export interface TrackedMutationHandleOptions<TDeps extends TrackedDependencyRecord = TrackedDependencyRecord, TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown> extends MutationHandleOptions<TData, TError, TVariables, TOnMutateResult> {
45
45
  dependencyKeys?: readonly (keyof TDeps & string)[];
46
46
  resolveDependencies?: (variables: TVariables) => Partial<TDeps>;
47
47
  invalidateOn?: TrackedInvalidateOn;
@@ -51,7 +51,7 @@ export interface TrackedMutationServiceOptions<TDeps extends TrackedDependencyRe
51
51
  * Function signature for the default mutation factory with automatic invalidation.
52
52
  */
53
53
  export interface CreateMutation {
54
- <TDeps extends TrackedDependencyRecord = TrackedDependencyRecord, TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(mutationFn: MutationFunction<TData, TVariables>, options?: TrackedMutationServiceOptions<TDeps, TData, TError, TVariables, TOnMutateResult>): MutationService<TData, TError, TVariables, TOnMutateResult>;
54
+ <TDeps extends TrackedDependencyRecord = TrackedDependencyRecord, TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(mutationFn: MutationFunction<TData, TVariables>, options?: TrackedMutationHandleOptions<TDeps, TData, TError, TVariables, TOnMutateResult>): MutationHandle<TData, TError, TVariables, TOnMutateResult>;
55
55
  }
56
56
  /**
57
57
  * Prepares the mutation factory by binding it to a specific QueryClient instance.
package/dist/mutation.js CHANGED
@@ -8,7 +8,7 @@ import { pickTrackedDependencies, resolveTrackedQueries, toTrackedDependencyEntr
8
8
  export function setupMutation(queryClient) {
9
9
  // Returns the actual factory function for creating individual mutation services.
10
10
  return function createMutation(mutationFn, options) {
11
- return createMutationService(queryClient, mutationFn, options);
11
+ return createMutationHandle(queryClient, mutationFn, options);
12
12
  };
13
13
  }
14
14
  /**
@@ -22,8 +22,8 @@ export function setupTrackedMutation(queryClient, trackingRegistry, defaultDepen
22
22
  return function createMutation(mutationFn, options) {
23
23
  // Split tracked-only options from the underlying TanStack mutation observer options.
24
24
  const { dependencyKeys, invalidateOn = 'success', matchMode = 'intersection', resolveDependencies, ...mutationOptions } = options ?? {};
25
- // Reuse the normal mutation service so snapshots and subscription behavior stay identical.
26
- const service = createMutationService(queryClient, mutationFn, mutationOptions);
25
+ // Reuse the normal mutation handle so snapshots and subscription behavior stay identical.
26
+ const handle = createMutationHandle(queryClient, mutationFn, mutationOptions);
27
27
  // The paired helper injects dependency keys here, while standalone tracked mutations can
28
28
  // still provide them directly or bypass them with a custom resolver.
29
29
  const resolvedDependencyKeys = (dependencyKeys ?? defaultDependencyKeys);
@@ -41,12 +41,12 @@ export function setupTrackedMutation(queryClient, trackingRegistry, defaultDepen
41
41
  })));
42
42
  };
43
43
  return {
44
- ...service,
44
+ ...handle,
45
45
  mutate: async (variables, mutateOptions) => {
46
46
  try {
47
47
  // Let TanStack finish the mutation first so its own callbacks and state machine remain
48
48
  // authoritative. The facade only coordinates the follow-up invalidation.
49
- const result = await service.mutate(variables, mutateOptions);
49
+ const result = await handle.mutate(variables, mutateOptions);
50
50
  if (invalidateOn === 'success' || invalidateOn === 'settled') {
51
51
  await invalidateTrackedQueries(variables);
52
52
  }
@@ -71,7 +71,7 @@ export function setupTrackedMutation(queryClient, trackingRegistry, defaultDepen
71
71
  /**
72
72
  * Internal helper to transform a raw Tanstack mutation result into our public snapshot format.
73
73
  */
74
- function toMutationServiceSnapshot(result) {
74
+ function toMutationHandleSnapshot(result) {
75
75
  // Extract and return the relevant fields for the UI or other services.
76
76
  return {
77
77
  data: result.data,
@@ -84,7 +84,7 @@ function toMutationServiceSnapshot(result) {
84
84
  isSuccess: result.isSuccess,
85
85
  };
86
86
  }
87
- function createMutationService(queryClient, mutationFn, options) {
87
+ function createMutationHandle(queryClient, mutationFn, options) {
88
88
  // Keep the original mutation implementation in one place so tracked and untracked mutations
89
89
  // always expose the same observer-backed runtime behavior.
90
90
  const observer = new MutationObserver(queryClient, {
@@ -92,9 +92,9 @@ function createMutationService(queryClient, mutationFn, options) {
92
92
  mutationFn,
93
93
  });
94
94
  return {
95
- getSnapshot: () => toMutationServiceSnapshot(observer.getCurrentResult()),
95
+ getSnapshot: () => toMutationHandleSnapshot(observer.getCurrentResult()),
96
96
  subscribe: (listener) => observer.subscribe((result) => {
97
- listener(toMutationServiceSnapshot(result));
97
+ listener(toMutationHandleSnapshot(result));
98
98
  }),
99
99
  mutate: (variables, mutateOptions) => observer.mutate(variables, mutateOptions),
100
100
  reset: () => observer.reset(),
@@ -1 +1 @@
1
- {"version":3,"file":"mutation.js","sourceRoot":"","sources":["../src/mutation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mutation.js","sourceRoot":"","sources":["../src/mutation.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { type QueryClient, type MutationFunction } from '@tanstack/query-core';
2
- import { type CreateMutation, type CreateUntrackedMutation, type MutationService, type TrackedMutationServiceOptions } from './mutation.js';
2
+ import { type CreateMutation, type CreateUntrackedMutation, type MutationHandle, type TrackedMutationHandleOptions } from './mutation.js';
3
3
  import { type CreateQuery, type CreateUntrackedQuery } from './query.js';
4
4
  import { type TrackedDependencyValue } from './tracking.js';
5
5
  /**
@@ -9,7 +9,7 @@ import { type TrackedDependencyValue } from './tracking.js';
9
9
  * once at setup time and injects them automatically for each tracked mutation it creates.
10
10
  */
11
11
  export interface CreateMutationWithDefaults<TDependencyKey extends string> {
12
- <TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(mutationFn: MutationFunction<TData, TVariables>, options?: Omit<TrackedMutationServiceOptions<Record<TDependencyKey, TrackedDependencyValue>, TData, TError, TVariables, TOnMutateResult>, 'dependencyKeys'>): MutationService<TData, TError, TVariables, TOnMutateResult>;
12
+ <TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(mutationFn: MutationFunction<TData, TVariables>, options?: Omit<TrackedMutationHandleOptions<Record<TDependencyKey, TrackedDependencyValue>, TData, TError, TVariables, TOnMutateResult>, 'dependencyKeys'>): MutationHandle<TData, TError, TVariables, TOnMutateResult>;
13
13
  }
14
14
  /**
15
15
  * Paired tracked helper that captures dependency keys once for default mutation resolution.
package/dist/query.js CHANGED
@@ -152,9 +152,13 @@ function createQueryHandle(queryClient, queryKey, queryFn, options) {
152
152
  setDerivedState,
153
153
  handle: {
154
154
  getSnapshot: () => toQueryHandleSnapshot(observer.getCurrentResult()),
155
- subscribe: (listener) => observer.subscribe((result) => {
155
+ subscribe: (listener) =>
156
+ // QueryObserver subscriptions alone are not enough for browser lifecycle events.
157
+ // TanStack wires focus/reconnect refetching through QueryClient.mount(), so every
158
+ // live QueryHandle subscription must also activate the client lifecycle.
159
+ subscribeToMountedQueryClient(queryClient, () => observer.subscribe((result) => {
156
160
  listener(toQueryHandleSnapshot(result));
157
- }),
161
+ })),
158
162
  refetch: async (refetchOptions) => toQueryHandleSnapshot(await observer.refetch(refetchOptions)),
159
163
  invalidate: (invalidateOptions) => queryClient.invalidateQueries({
160
164
  exact: true,
@@ -167,6 +171,49 @@ function createQueryHandle(queryClient, queryKey, queryFn, options) {
167
171
  },
168
172
  };
169
173
  }
174
+ /**
175
+ * Subscribes to a TanStack observer while the owning QueryClient is mounted.
176
+ *
177
+ * Native `useQuery` users normally get this lifecycle from `QueryClientProvider`, which calls
178
+ * `queryClient.mount()` and lets TanStack subscribe to `focusManager` and `onlineManager`.
179
+ * Status Quo Query can be used without a React provider, so the wrapper has to mount the client
180
+ * when a query handle becomes live.
181
+ *
182
+ * Mounting is intentionally scoped to `subscribe(...)` rather than `setupQuery(...)`:
183
+ *
184
+ * - passive reads such as `getSnapshot()` and `getQueryData(...)` should not install global
185
+ * focus/online listeners
186
+ * - short-lived factories should not leave a permanently mounted client behind
187
+ * - TanStack reference-counts `mount()` / `unmount()`, so multiple active handles can safely
188
+ * mount the same client at the same time
189
+ *
190
+ * This is the piece that makes `staleTime` + `refetchOnWindowFocus` behave like native
191
+ * `useQuery`: the observer marks itself stale after the stale timer expires, then the mounted
192
+ * client forwards the next focus event to the query cache so active stale queries refetch.
193
+ */
194
+ function subscribeToMountedQueryClient(queryClient, subscribe) {
195
+ queryClient.mount();
196
+ let isSubscribed = true;
197
+ let unsubscribe;
198
+ try {
199
+ unsubscribe = subscribe();
200
+ }
201
+ catch (error) {
202
+ // Keep mount/unmount balanced if the observer subscription throws before returning cleanup.
203
+ queryClient.unmount();
204
+ throw error;
205
+ }
206
+ return () => {
207
+ if (!isSubscribed) {
208
+ return;
209
+ }
210
+ isSubscribed = false;
211
+ unsubscribe();
212
+ // Balance the mount for this specific subscription. TanStack keeps the client mounted while
213
+ // any other active subscription has also called mount().
214
+ queryClient.unmount();
215
+ };
216
+ }
170
217
  function bindQueryDependencies(queryHandle, queryKey, dependsOn) {
171
218
  const dependencyController = createDependencyController(queryKey, queryHandle.setDerivedState, dependsOn);
172
219
  let subscriberCount = 0;
package/dist/query.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
@@ -1 +1,2 @@
1
1
  export { useQueryHandle } from './use-query-handle.js';
2
+ export { useMutationHandle } from './use-mutation-handle.js';
@@ -1,2 +1,3 @@
1
1
  export { useQueryHandle } from './use-query-handle.js';
2
+ export { useMutationHandle } from './use-mutation-handle.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/react/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { MutationHandle, MutationHandleSnapshot } from '../../mutation.js';
2
+ export declare function useMutationHandle<TData, TError = Error, TVariables = void>(mutationHandle: MutationHandle<TData, TError, TVariables>): MutationHandleSnapshot<TData, TError, TVariables>;
@@ -0,0 +1,71 @@
1
+ // Import the React hooks needed to memoize callbacks, hold mutable cache state,
2
+ // and connect an external store to React rendering.
3
+ import { useCallback, useRef, useSyncExternalStore } from 'react';
4
+ // Subscribe a React component to a MutationHandle and return its latest snapshot.
5
+ export function useMutationHandle(
6
+ // Receive the external mutation handle instance to subscribe to.
7
+ mutationHandle) {
8
+ // Count store notifications so we can tell when our cached snapshot is stale.
9
+ const snapshotVersionRef = useRef(0);
10
+ // Cache one client-side snapshot per observed version to keep getSnapshot stable.
11
+ const snapshotCacheRef = useRef(null);
12
+ // Cache the server snapshot separately for the useSyncExternalStore SSR fallback.
13
+ const serverSnapshotCacheRef = useRef(null);
14
+ // Create the subscribe function expected by useSyncExternalStore.
15
+ const subscribe = useCallback(
16
+ // React passes a listener that must run whenever the external store changes.
17
+ (listener) =>
18
+ // Forward the subscription to the mutation handle.
19
+ mutationHandle.subscribe(() => {
20
+ // Bump the version so later reads know the previous cache is outdated.
21
+ snapshotVersionRef.current += 1;
22
+ // Drop the cached client snapshot because the store just changed.
23
+ snapshotCacheRef.current = null;
24
+ // Drop the cached server snapshot for the same reason.
25
+ serverSnapshotCacheRef.current = null;
26
+ // Notify React that it should read a fresh snapshot.
27
+ listener();
28
+ }),
29
+ // Recreate the subscription function only when the handle instance changes.
30
+ [mutationHandle]);
31
+ // Read the current client snapshot in a referentially stable way for React.
32
+ const getSnapshot = useCallback(() => {
33
+ // Read the latest store version number.
34
+ const version = snapshotVersionRef.current;
35
+ // Read the last cached client snapshot, if there is one.
36
+ const cachedSnapshot = snapshotCacheRef.current;
37
+ // Reuse the cached snapshot when it was produced for the current version.
38
+ if (cachedSnapshot && cachedSnapshot.version === version) {
39
+ // Return the cached snapshot so repeated reads in the same render stay stable.
40
+ return cachedSnapshot.snapshot;
41
+ }
42
+ // Ask the mutation handle for the latest snapshot because the cache is empty or stale.
43
+ const snapshot = mutationHandle.getSnapshot();
44
+ // Store the new snapshot together with the version it belongs to.
45
+ snapshotCacheRef.current = {
46
+ snapshot,
47
+ version,
48
+ };
49
+ // Return the freshly read snapshot to React.
50
+ return snapshot;
51
+ }, [mutationHandle]);
52
+ // Read the server snapshot used by React during SSR or hydration fallback paths.
53
+ const getServerSnapshot = useCallback(() => {
54
+ // Read the cached server snapshot, if one was stored earlier.
55
+ const cachedSnapshot = serverSnapshotCacheRef.current;
56
+ // Reuse the cached server snapshot to keep server reads stable.
57
+ if (cachedSnapshot) {
58
+ // Return the cached server snapshot directly.
59
+ return cachedSnapshot;
60
+ }
61
+ // Ask the mutation handle for a snapshot because no server cache exists yet.
62
+ const snapshot = mutationHandle.getSnapshot();
63
+ // Cache that snapshot for the next server read.
64
+ serverSnapshotCacheRef.current = snapshot;
65
+ // Return the freshly read server snapshot.
66
+ return snapshot;
67
+ }, [mutationHandle]);
68
+ // Let React subscribe to the external store and read snapshots through the callbacks above.
69
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
70
+ }
71
+ //# sourceMappingURL=use-mutation-handle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-mutation-handle.js","sourceRoot":"","sources":["../../../src/react/hooks/use-mutation-handle.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,oDAAoD;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAelE,kFAAkF;AAClF,MAAM,UAAU,iBAAiB;AAC/B,iEAAiE;AACjE,cAAyD;IAEzD,8EAA8E;IAC9E,MAAM,kBAAkB,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACrC,kFAAkF;IAClF,MAAM,gBAAgB,GAAG,MAAM,CAAuD,IAAI,CAAC,CAAC;IAC5F,kFAAkF;IAClF,MAAM,sBAAsB,GAAG,MAAM,CACnC,IAAI,CACL,CAAC;IAEF,kEAAkE;IAClE,MAAM,SAAS,GAAG,WAAW;IAC3B,6EAA6E;IAC7E,CAAC,QAAkB,EAAE,EAAE;IACrB,mDAAmD;IACnD,cAAc,CAAC,SAAS,CAAC,GAAG,EAAE;QAC5B,uEAAuE;QACvE,kBAAkB,CAAC,OAAO,IAAI,CAAC,CAAC;QAChC,kEAAkE;QAClE,gBAAgB,CAAC,OAAO,GAAG,IAAI,CAAC;QAChC,uDAAuD;QACvD,sBAAsB,CAAC,OAAO,GAAG,IAAI,CAAC;QACtC,qDAAqD;QACrD,QAAQ,EAAE,CAAC;IACb,CAAC,CAAC;IACJ,4EAA4E;IAC5E,CAAC,cAAc,CAAC,CACjB,CAAC;IAEF,4EAA4E;IAC5E,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,wCAAwC;QACxC,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC;QAC3C,yDAAyD;QACzD,MAAM,cAAc,GAAG,gBAAgB,CAAC,OAAO,CAAC;QAEhD,0EAA0E;QAC1E,IAAI,cAAc,IAAI,cAAc,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;YACzD,+EAA+E;YAC/E,OAAO,cAAc,CAAC,QAAQ,CAAC;QACjC,CAAC;QAED,uFAAuF;QACvF,MAAM,QAAQ,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;QAC9C,kEAAkE;QAClE,gBAAgB,CAAC,OAAO,GAAG;YACzB,QAAQ;YACR,OAAO;SACR,CAAC;QAEF,6CAA6C;QAC7C,OAAO,QAAQ,CAAC;IAClB,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAErB,iFAAiF;IACjF,MAAM,iBAAiB,GAAG,WAAW,CAAC,GAAG,EAAE;QACzC,8DAA8D;QAC9D,MAAM,cAAc,GAAG,sBAAsB,CAAC,OAAO,CAAC;QAEtD,gEAAgE;QAChE,IAAI,cAAc,EAAE,CAAC;YACnB,8CAA8C;YAC9C,OAAO,cAAc,CAAC;QACxB,CAAC;QAED,6EAA6E;QAC7E,MAAM,QAAQ,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;QAC9C,gDAAgD;QAChD,sBAAsB,CAAC,OAAO,GAAG,QAAQ,CAAC;QAE1C,2CAA2C;QAC3C,OAAO,QAAQ,CAAC;IAClB,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAErB,4FAA4F;IAC5F,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AACzE,CAAC"}
@@ -1 +1 @@
1
- export { useQueryHandle } from './hooks/index.js';
1
+ export { useQueryHandle, useMutationHandle } from './hooks/index.js';
@@ -1,2 +1,2 @@
1
- export { useQueryHandle } from './hooks/index.js';
1
+ export { useQueryHandle, useMutationHandle } from './hooks/index.js';
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@veams/status-quo-query",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "TanStack Query service layer for the VEAMS StatusQuo ecosystem.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -1,4 +1,4 @@
1
- import { QueryClient } from '@tanstack/query-core';
1
+ import { focusManager, QueryClient } from '@tanstack/query-core';
2
2
 
3
3
  import { isQueryLoading, setupQuery, toQueryMetaState } from '../query';
4
4
 
@@ -43,6 +43,40 @@ describe('Query Service', () => {
43
43
  expect(statuses).toContain('success');
44
44
  });
45
45
 
46
+ it('refetches stale subscribed queries when window focus returns', async () => {
47
+ jest.useFakeTimers();
48
+
49
+ const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 0 } } });
50
+ const createQuery = setupQuery(queryClient);
51
+ const queryFn = jest
52
+ .fn()
53
+ .mockResolvedValueOnce('initial')
54
+ .mockResolvedValueOnce('focused');
55
+ const service = createQuery(['demo', 'focus'], queryFn, { staleTime: 5_000 });
56
+ const unsubscribe = service.subscribe(() => undefined);
57
+
58
+ try {
59
+ await flushTasks();
60
+
61
+ expect(queryFn).toHaveBeenCalledTimes(1);
62
+ expect(service.getSnapshot().data).toBe('initial');
63
+
64
+ jest.advanceTimersByTime(5_001);
65
+ await flushTasks();
66
+
67
+ focusManager.setFocused(false);
68
+ focusManager.setFocused(true);
69
+ await flushTasks();
70
+
71
+ expect(queryFn).toHaveBeenCalledTimes(2);
72
+ expect(service.getSnapshot().data).toBe('focused');
73
+ } finally {
74
+ unsubscribe();
75
+ focusManager.setFocused(undefined);
76
+ jest.useRealTimers();
77
+ }
78
+ });
79
+
46
80
  it('invalidates its own query key exactly', async () => {
47
81
  const queryClient = new QueryClient({ defaultOptions: { queries: { retry: 0 } } });
48
82
  const invalidateQueriesSpy = jest.spyOn(queryClient, 'invalidateQueries');
package/src/mutation.ts CHANGED
@@ -32,7 +32,7 @@ export type MutationStatus = TanstackMutationStatus;
32
32
  /**
33
33
  * Represents a stable snapshot of the mutation service's state.
34
34
  */
35
- export interface MutationServiceSnapshot<TData = unknown, TError = Error, TVariables = void> {
35
+ export interface MutationHandleSnapshot<TData = unknown, TError = Error, TVariables = void> {
36
36
  // The data returned from a successful mutation.
37
37
  data: TData | undefined;
38
38
  // The error object if the mutation failed.
@@ -54,17 +54,17 @@ export interface MutationServiceSnapshot<TData = unknown, TError = Error, TVaria
54
54
  /**
55
55
  * Defines the public API for a mutation service.
56
56
  */
57
- export interface MutationService<
57
+ export interface MutationHandle<
58
58
  TData = unknown,
59
59
  TError = Error,
60
60
  TVariables = void,
61
61
  TOnMutateResult = unknown,
62
62
  > {
63
63
  // Returns the current state snapshot of the mutation.
64
- getSnapshot: () => MutationServiceSnapshot<TData, TError, TVariables>;
64
+ getSnapshot: () => MutationHandleSnapshot<TData, TError, TVariables>;
65
65
  // Subscribes a listener to state changes; returns an unsubscribe function.
66
66
  subscribe: (
67
- listener: (snapshot: MutationServiceSnapshot<TData, TError, TVariables>) => void
67
+ listener: (snapshot: MutationHandleSnapshot<TData, TError, TVariables>) => void
68
68
  ) => () => void;
69
69
  // Triggers the mutation with the given variables and optional lifecycle callbacks.
70
70
  mutate: (
@@ -80,7 +80,7 @@ export interface MutationService<
80
80
  /**
81
81
  * Configuration options for creating a mutation service, excluding the mutation function itself.
82
82
  */
83
- export type MutationServiceOptions<
83
+ export type MutationHandleOptions<
84
84
  TData = unknown,
85
85
  TError = Error,
86
86
  TVariables = void,
@@ -95,8 +95,8 @@ export interface CreateUntrackedMutation {
95
95
  // The asynchronous function that performs the mutation.
96
96
  mutationFn: MutationFunction<TData, TVariables>,
97
97
  // Optional configuration for behavior like retry or lifecycle hooks.
98
- options?: MutationServiceOptions<TData, TError, TVariables, TOnMutateResult>
99
- ): MutationService<TData, TError, TVariables, TOnMutateResult>;
98
+ options?: MutationHandleOptions<TData, TError, TVariables, TOnMutateResult>
99
+ ): MutationHandle<TData, TError, TVariables, TOnMutateResult>;
100
100
  }
101
101
 
102
102
  /**
@@ -106,13 +106,13 @@ export interface CreateUntrackedMutation {
106
106
  * options only describe how the facade should derive dependency values and when it should
107
107
  * invalidate matching tracked queries after the mutation lifecycle settles.
108
108
  */
109
- export interface TrackedMutationServiceOptions<
109
+ export interface TrackedMutationHandleOptions<
110
110
  TDeps extends TrackedDependencyRecord = TrackedDependencyRecord,
111
111
  TData = unknown,
112
112
  TError = Error,
113
113
  TVariables = void,
114
114
  TOnMutateResult = unknown,
115
- > extends MutationServiceOptions<TData, TError, TVariables, TOnMutateResult> {
115
+ > extends MutationHandleOptions<TData, TError, TVariables, TOnMutateResult> {
116
116
  // Optional dependency keys used by the default variable reader.
117
117
  dependencyKeys?: readonly (keyof TDeps & string)[];
118
118
  // Optional custom resolver when mutation variables do not expose dependency fields directly.
@@ -135,8 +135,8 @@ export interface CreateMutation {
135
135
  TOnMutateResult = unknown,
136
136
  >(
137
137
  mutationFn: MutationFunction<TData, TVariables>,
138
- options?: TrackedMutationServiceOptions<TDeps, TData, TError, TVariables, TOnMutateResult>
139
- ): MutationService<TData, TError, TVariables, TOnMutateResult>;
138
+ options?: TrackedMutationHandleOptions<TDeps, TData, TError, TVariables, TOnMutateResult>
139
+ ): MutationHandle<TData, TError, TVariables, TOnMutateResult>;
140
140
  }
141
141
 
142
142
  /**
@@ -151,9 +151,9 @@ export function setupMutation(queryClient: QueryClient): CreateUntrackedMutation
151
151
  TOnMutateResult = unknown,
152
152
  >(
153
153
  mutationFn: MutationFunction<TData, TVariables>,
154
- options?: MutationServiceOptions<TData, TError, TVariables, TOnMutateResult>
155
- ): MutationService<TData, TError, TVariables, TOnMutateResult> {
156
- return createMutationService(queryClient, mutationFn, options);
154
+ options?: MutationHandleOptions<TData, TError, TVariables, TOnMutateResult>
155
+ ): MutationHandle<TData, TError, TVariables, TOnMutateResult> {
156
+ return createMutationHandle(queryClient, mutationFn, options);
157
157
  };
158
158
  }
159
159
 
@@ -177,8 +177,8 @@ export function setupTrackedMutation(
177
177
  TOnMutateResult = unknown,
178
178
  >(
179
179
  mutationFn: MutationFunction<TData, TVariables>,
180
- options?: TrackedMutationServiceOptions<TDeps, TData, TError, TVariables, TOnMutateResult>
181
- ): MutationService<TData, TError, TVariables, TOnMutateResult> {
180
+ options?: TrackedMutationHandleOptions<TDeps, TData, TError, TVariables, TOnMutateResult>
181
+ ): MutationHandle<TData, TError, TVariables, TOnMutateResult> {
182
182
  // Split tracked-only options from the underlying TanStack mutation observer options.
183
183
  const {
184
184
  dependencyKeys,
@@ -187,8 +187,8 @@ export function setupTrackedMutation(
187
187
  resolveDependencies,
188
188
  ...mutationOptions
189
189
  } = options ?? {};
190
- // Reuse the normal mutation service so snapshots and subscription behavior stay identical.
191
- const service = createMutationService(queryClient, mutationFn, mutationOptions);
190
+ // Reuse the normal mutation handle so snapshots and subscription behavior stay identical.
191
+ const handle = createMutationHandle(queryClient, mutationFn, mutationOptions);
192
192
  // The paired helper injects dependency keys here, while standalone tracked mutations can
193
193
  // still provide them directly or bypass them with a custom resolver.
194
194
  const resolvedDependencyKeys = (dependencyKeys ?? defaultDependencyKeys);
@@ -220,12 +220,12 @@ export function setupTrackedMutation(
220
220
  };
221
221
 
222
222
  return {
223
- ...service,
223
+ ...handle,
224
224
  mutate: async (variables, mutateOptions) => {
225
225
  try {
226
226
  // Let TanStack finish the mutation first so its own callbacks and state machine remain
227
227
  // authoritative. The facade only coordinates the follow-up invalidation.
228
- const result = await service.mutate(variables, mutateOptions);
228
+ const result = await handle.mutate(variables, mutateOptions);
229
229
 
230
230
  if (invalidateOn === 'success' || invalidateOn === 'settled') {
231
231
  await invalidateTrackedQueries(variables);
@@ -252,9 +252,9 @@ export function setupTrackedMutation(
252
252
  /**
253
253
  * Internal helper to transform a raw Tanstack mutation result into our public snapshot format.
254
254
  */
255
- function toMutationServiceSnapshot<TData, TError, TVariables, TOnMutateResult>(
255
+ function toMutationHandleSnapshot<TData, TError, TVariables, TOnMutateResult>(
256
256
  result: MutationObserverResult<TData, TError, TVariables, TOnMutateResult>
257
- ): MutationServiceSnapshot<TData, TError, TVariables> {
257
+ ): MutationHandleSnapshot<TData, TError, TVariables> {
258
258
  // Extract and return the relevant fields for the UI or other services.
259
259
  return {
260
260
  data: result.data,
@@ -268,7 +268,7 @@ function toMutationServiceSnapshot<TData, TError, TVariables, TOnMutateResult>(
268
268
  };
269
269
  }
270
270
 
271
- function createMutationService<
271
+ function createMutationHandle<
272
272
  TData = unknown,
273
273
  TError = Error,
274
274
  TVariables = void,
@@ -276,8 +276,8 @@ function createMutationService<
276
276
  >(
277
277
  queryClient: QueryClient,
278
278
  mutationFn: MutationFunction<TData, TVariables>,
279
- options?: MutationServiceOptions<TData, TError, TVariables, TOnMutateResult>
280
- ): MutationService<TData, TError, TVariables, TOnMutateResult> {
279
+ options?: MutationHandleOptions<TData, TError, TVariables, TOnMutateResult>
280
+ ): MutationHandle<TData, TError, TVariables, TOnMutateResult> {
281
281
  // Keep the original mutation implementation in one place so tracked and untracked mutations
282
282
  // always expose the same observer-backed runtime behavior.
283
283
  const observer = new MutationObserver<TData, TError, TVariables, TOnMutateResult>(queryClient, {
@@ -286,10 +286,10 @@ function createMutationService<
286
286
  });
287
287
 
288
288
  return {
289
- getSnapshot: () => toMutationServiceSnapshot(observer.getCurrentResult()),
289
+ getSnapshot: () => toMutationHandleSnapshot(observer.getCurrentResult()),
290
290
  subscribe: (listener) =>
291
291
  observer.subscribe((result) => {
292
- listener(toMutationServiceSnapshot(result));
292
+ listener(toMutationHandleSnapshot(result));
293
293
  }),
294
294
  mutate: (variables, mutateOptions) => observer.mutate(variables, mutateOptions),
295
295
  reset: () => observer.reset(),
package/src/provider.ts CHANGED
@@ -8,8 +8,8 @@ import {
8
8
  import {
9
9
  type CreateMutation,
10
10
  type CreateUntrackedMutation,
11
- type MutationService,
12
- type TrackedMutationServiceOptions,
11
+ type MutationHandle,
12
+ type TrackedMutationHandleOptions,
13
13
  setupMutation,
14
14
  setupTrackedMutation,
15
15
  } from './mutation.js';
@@ -29,7 +29,7 @@ export interface CreateMutationWithDefaults<TDependencyKey extends string> {
29
29
  <TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(
30
30
  mutationFn: MutationFunction<TData, TVariables>,
31
31
  options?: Omit<
32
- TrackedMutationServiceOptions<
32
+ TrackedMutationHandleOptions<
33
33
  Record<TDependencyKey, TrackedDependencyValue>,
34
34
  TData,
35
35
  TError,
@@ -38,7 +38,7 @@ export interface CreateMutationWithDefaults<TDependencyKey extends string> {
38
38
  >,
39
39
  'dependencyKeys'
40
40
  >
41
- ): MutationService<TData, TError, TVariables, TOnMutateResult>;
41
+ ): MutationHandle<TData, TError, TVariables, TOnMutateResult>;
42
42
  }
43
43
 
44
44
  /**
@@ -54,13 +54,13 @@ export interface CreateQueryAndMutation {
54
54
  * Defines the public API for the query manager facade.
55
55
  */
56
56
  export interface QueryManager {
57
- // Factory for creating a dependency-tracked mutation service within the context of this provider.
57
+ // Factory for creating a dependency-tracked mutation handle within the context of this provider.
58
58
  createMutation: CreateMutation;
59
59
  // Factory for creating a dependency-tracked query handle within the context of this provider.
60
60
  createQuery: CreateQuery;
61
61
  // Factory for creating an untracked query handle within the context of this provider.
62
62
  createUntrackedQuery: CreateUntrackedQuery;
63
- // Factory for creating an untracked mutation service within the context of this provider.
63
+ // Factory for creating an untracked mutation handle within the context of this provider.
64
64
  createUntrackedMutation: CreateUntrackedMutation;
65
65
  // Convenience helper that shares dependency keys between tracked queries and mutations.
66
66
  createQueryAndMutation: CreateQueryAndMutation;
@@ -126,7 +126,7 @@ export function setupQueryManager(queryClient: QueryClient): QueryManager {
126
126
  > = <TData = unknown, TError = Error, TVariables = void, TOnMutateResult = unknown>(
127
127
  mutationFn: MutationFunction<TData, TVariables>,
128
128
  options?: Omit<
129
- TrackedMutationServiceOptions<
129
+ TrackedMutationHandleOptions<
130
130
  Record<TDependencyKeys[number], TrackedDependencyValue>,
131
131
  TData,
132
132
  TError,
package/src/query.ts CHANGED
@@ -424,9 +424,14 @@ function createQueryHandle<
424
424
  handle: {
425
425
  getSnapshot: () => toQueryHandleSnapshot(observer.getCurrentResult()),
426
426
  subscribe: (listener) =>
427
- observer.subscribe((result) => {
428
- listener(toQueryHandleSnapshot(result));
429
- }),
427
+ // QueryObserver subscriptions alone are not enough for browser lifecycle events.
428
+ // TanStack wires focus/reconnect refetching through QueryClient.mount(), so every
429
+ // live QueryHandle subscription must also activate the client lifecycle.
430
+ subscribeToMountedQueryClient(queryClient, () =>
431
+ observer.subscribe((result) => {
432
+ listener(toQueryHandleSnapshot(result));
433
+ })
434
+ ),
430
435
  refetch: async (refetchOptions) =>
431
436
  toQueryHandleSnapshot(await observer.refetch(refetchOptions)),
432
437
  invalidate: (invalidateOptions) =>
@@ -445,6 +450,56 @@ function createQueryHandle<
445
450
  };
446
451
  }
447
452
 
453
+ /**
454
+ * Subscribes to a TanStack observer while the owning QueryClient is mounted.
455
+ *
456
+ * Native `useQuery` users normally get this lifecycle from `QueryClientProvider`, which calls
457
+ * `queryClient.mount()` and lets TanStack subscribe to `focusManager` and `onlineManager`.
458
+ * Status Quo Query can be used without a React provider, so the wrapper has to mount the client
459
+ * when a query handle becomes live.
460
+ *
461
+ * Mounting is intentionally scoped to `subscribe(...)` rather than `setupQuery(...)`:
462
+ *
463
+ * - passive reads such as `getSnapshot()` and `getQueryData(...)` should not install global
464
+ * focus/online listeners
465
+ * - short-lived factories should not leave a permanently mounted client behind
466
+ * - TanStack reference-counts `mount()` / `unmount()`, so multiple active handles can safely
467
+ * mount the same client at the same time
468
+ *
469
+ * This is the piece that makes `staleTime` + `refetchOnWindowFocus` behave like native
470
+ * `useQuery`: the observer marks itself stale after the stale timer expires, then the mounted
471
+ * client forwards the next focus event to the query cache so active stale queries refetch.
472
+ */
473
+ function subscribeToMountedQueryClient(
474
+ queryClient: QueryClient,
475
+ subscribe: () => () => void
476
+ ): () => void {
477
+ queryClient.mount();
478
+
479
+ let isSubscribed = true;
480
+ let unsubscribe: () => void;
481
+
482
+ try {
483
+ unsubscribe = subscribe();
484
+ } catch (error) {
485
+ // Keep mount/unmount balanced if the observer subscription throws before returning cleanup.
486
+ queryClient.unmount();
487
+ throw error;
488
+ }
489
+
490
+ return () => {
491
+ if (!isSubscribed) {
492
+ return;
493
+ }
494
+
495
+ isSubscribed = false;
496
+ unsubscribe();
497
+ // Balance the mount for this specific subscription. TanStack keeps the client mounted while
498
+ // any other active subscription has also called mount().
499
+ queryClient.unmount();
500
+ };
501
+ }
502
+
448
503
  function bindQueryDependencies<
449
504
  TSources extends readonly unknown[] = [],
450
505
  TQueryFnData = unknown,
@@ -0,0 +1,107 @@
1
+ import React, { act } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import { QueryClient } from '@tanstack/query-core';
4
+
5
+ import { setupMutation } from '../../mutation.js';
6
+ import { useMutationHandle } from '../hooks/use-mutation-handle.js';
7
+
8
+ import type { MutationHandle, MutationHandleSnapshot } from '../../mutation.js';
9
+
10
+ declare global {
11
+ var IS_REACT_ACT_ENVIRONMENT: boolean;
12
+ }
13
+
14
+ describe('useMutationHandle', () => {
15
+ let container: HTMLDivElement;
16
+
17
+ beforeAll(() => {
18
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
19
+ });
20
+
21
+ beforeEach(() => {
22
+ container = document.createElement('div');
23
+ document.body.appendChild(container);
24
+ });
25
+
26
+ afterEach(() => {
27
+ container.remove();
28
+ });
29
+
30
+ it('renders the latest mutation snapshot', async () => {
31
+ const queryClient = new QueryClient({ defaultOptions: { mutations: { retry: 0 } } });
32
+ const createMutation = setupMutation(queryClient);
33
+ const mutationFn = jest.fn((_payload: { name: string }) =>
34
+ Promise.resolve({ ok: true as const })
35
+ );
36
+ const handle = createMutation(mutationFn);
37
+ const statuses: string[] = [];
38
+
39
+ const Consumer = () => {
40
+ const snapshot = useMutationHandle(handle);
41
+ statuses.push(snapshot.status);
42
+
43
+ return <span>{snapshot.status}</span>;
44
+ };
45
+
46
+ const root = createRoot(container);
47
+
48
+ await act(async () => {
49
+ root.render(<Consumer />);
50
+ });
51
+
52
+ expect(container.textContent).toBe('idle');
53
+
54
+ await act(async () => {
55
+ await handle.mutate({ name: 'Ada' });
56
+ });
57
+
58
+ expect(container.textContent).toBe('success');
59
+ expect(statuses).toContain('idle');
60
+ expect(statuses).toContain('pending');
61
+ expect(statuses).toContain('success');
62
+
63
+ await act(async () => {
64
+ root.unmount();
65
+ });
66
+ });
67
+
68
+ it('cleans up the store subscription on unmount', async () => {
69
+ const snapshot: MutationHandleSnapshot<{ ok: true }, Error, void> = {
70
+ data: { ok: true },
71
+ error: null,
72
+ status: 'success',
73
+ variables: undefined,
74
+ isError: false,
75
+ isIdle: false,
76
+ isPending: false,
77
+ isSuccess: true,
78
+ };
79
+ const unsubscribe = jest.fn();
80
+ const handle: MutationHandle<{ ok: true }, Error, void> = {
81
+ getSnapshot: () => snapshot,
82
+ subscribe: jest.fn(() => unsubscribe),
83
+ mutate: jest.fn(async () => ({ ok: true as const })),
84
+ reset: jest.fn(),
85
+ unsafe_getResult: jest.fn(),
86
+ };
87
+
88
+ const root = createRoot(container);
89
+
90
+ const Consumer = () => {
91
+ useMutationHandle(handle);
92
+ return <span>ready</span>;
93
+ };
94
+
95
+ await act(async () => {
96
+ root.render(<Consumer />);
97
+ });
98
+
99
+ expect(handle.subscribe).toHaveBeenCalledTimes(1);
100
+
101
+ await act(async () => {
102
+ root.unmount();
103
+ });
104
+
105
+ expect(unsubscribe).toHaveBeenCalledTimes(1);
106
+ });
107
+ });
@@ -1 +1,2 @@
1
1
  export { useQueryHandle } from './use-query-handle.js';
2
+ export { useMutationHandle } from './use-mutation-handle.js';
@@ -0,0 +1,98 @@
1
+ // Import the React hooks needed to memoize callbacks, hold mutable cache state,
2
+ // and connect an external store to React rendering.
3
+ import { useCallback, useRef, useSyncExternalStore } from 'react';
4
+
5
+ // Import the mutation-handle contract and the stable snapshot shape the hook returns.
6
+ import type { MutationHandle, MutationHandleSnapshot } from '../../mutation.js';
7
+ // Describe the no-argument listener shape expected by useSyncExternalStore.
8
+ type Listener = () => void;
9
+
10
+ // Store one cached snapshot together with the store version it belongs to.
11
+ type SnapshotCacheEntry<TData, TError, TVariables> = {
12
+ // Hold the snapshot returned by the mutation handle for this version.
13
+ snapshot: MutationHandleSnapshot<TData, TError, TVariables>;
14
+ // Track which subscription version produced the cached snapshot.
15
+ version: number;
16
+ };
17
+
18
+ // Subscribe a React component to a MutationHandle and return its latest snapshot.
19
+ export function useMutationHandle<TData, TError = Error, TVariables = void>(
20
+ // Receive the external mutation handle instance to subscribe to.
21
+ mutationHandle: MutationHandle<TData, TError, TVariables>
22
+ ) {
23
+ // Count store notifications so we can tell when our cached snapshot is stale.
24
+ const snapshotVersionRef = useRef(0);
25
+ // Cache one client-side snapshot per observed version to keep getSnapshot stable.
26
+ const snapshotCacheRef = useRef<SnapshotCacheEntry<TData, TError, TVariables> | null>(null);
27
+ // Cache the server snapshot separately for the useSyncExternalStore SSR fallback.
28
+ const serverSnapshotCacheRef = useRef<MutationHandleSnapshot<TData, TError, TVariables> | null>(
29
+ null
30
+ );
31
+
32
+ // Create the subscribe function expected by useSyncExternalStore.
33
+ const subscribe = useCallback(
34
+ // React passes a listener that must run whenever the external store changes.
35
+ (listener: Listener) =>
36
+ // Forward the subscription to the mutation handle.
37
+ mutationHandle.subscribe(() => {
38
+ // Bump the version so later reads know the previous cache is outdated.
39
+ snapshotVersionRef.current += 1;
40
+ // Drop the cached client snapshot because the store just changed.
41
+ snapshotCacheRef.current = null;
42
+ // Drop the cached server snapshot for the same reason.
43
+ serverSnapshotCacheRef.current = null;
44
+ // Notify React that it should read a fresh snapshot.
45
+ listener();
46
+ }),
47
+ // Recreate the subscription function only when the handle instance changes.
48
+ [mutationHandle]
49
+ );
50
+
51
+ // Read the current client snapshot in a referentially stable way for React.
52
+ const getSnapshot = useCallback(() => {
53
+ // Read the latest store version number.
54
+ const version = snapshotVersionRef.current;
55
+ // Read the last cached client snapshot, if there is one.
56
+ const cachedSnapshot = snapshotCacheRef.current;
57
+
58
+ // Reuse the cached snapshot when it was produced for the current version.
59
+ if (cachedSnapshot && cachedSnapshot.version === version) {
60
+ // Return the cached snapshot so repeated reads in the same render stay stable.
61
+ return cachedSnapshot.snapshot;
62
+ }
63
+
64
+ // Ask the mutation handle for the latest snapshot because the cache is empty or stale.
65
+ const snapshot = mutationHandle.getSnapshot();
66
+ // Store the new snapshot together with the version it belongs to.
67
+ snapshotCacheRef.current = {
68
+ snapshot,
69
+ version,
70
+ };
71
+
72
+ // Return the freshly read snapshot to React.
73
+ return snapshot;
74
+ }, [mutationHandle]);
75
+
76
+ // Read the server snapshot used by React during SSR or hydration fallback paths.
77
+ const getServerSnapshot = useCallback(() => {
78
+ // Read the cached server snapshot, if one was stored earlier.
79
+ const cachedSnapshot = serverSnapshotCacheRef.current;
80
+
81
+ // Reuse the cached server snapshot to keep server reads stable.
82
+ if (cachedSnapshot) {
83
+ // Return the cached server snapshot directly.
84
+ return cachedSnapshot;
85
+ }
86
+
87
+ // Ask the mutation handle for a snapshot because no server cache exists yet.
88
+ const snapshot = mutationHandle.getSnapshot();
89
+ // Cache that snapshot for the next server read.
90
+ serverSnapshotCacheRef.current = snapshot;
91
+
92
+ // Return the freshly read server snapshot.
93
+ return snapshot;
94
+ }, [mutationHandle]);
95
+
96
+ // Let React subscribe to the external store and read snapshots through the callbacks above.
97
+ return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
98
+ }
@@ -1 +1 @@
1
- export { useQueryHandle } from './hooks/index.js';
1
+ export { useQueryHandle, useMutationHandle } from './hooks/index.js';