@buoy-gg/react-query 7.0.15 → 7.0.17
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/lib/commonjs/react-query/sync/dehydrate.js +4 -2
- package/lib/commonjs/react-query/sync/snapshotProvider.js +138 -0
- package/lib/commonjs/react-query/sync/useReactQuerySyncAdapter.js +8 -0
- package/lib/module/react-query/sync/dehydrate.js +4 -2
- package/lib/module/react-query/sync/snapshotProvider.js +133 -0
- package/lib/module/react-query/sync/useReactQuerySyncAdapter.js +9 -1
- package/lib/typescript/react-query/sync/snapshotProvider.d.ts +22 -0
- package/lib/typescript/react-query/sync/snapshotProvider.d.ts.map +1 -0
- package/lib/typescript/react-query/sync/useReactQuerySyncAdapter.d.ts.map +1 -1
- package/package.json +7 -4
|
@@ -34,10 +34,12 @@ function dehydrateMutation(mutation) {
|
|
|
34
34
|
function dehydrateQuery(query) {
|
|
35
35
|
const observerStates = query.observers.map(observer => ({
|
|
36
36
|
queryHash: query.queryHash,
|
|
37
|
-
options: observer.options,
|
|
38
37
|
// Remove queryFn from observer options so the dashboard can't accidentally
|
|
39
38
|
// run device fetch functions (they aren't serializable anyway)
|
|
40
|
-
|
|
39
|
+
options: {
|
|
40
|
+
...observer.options,
|
|
41
|
+
queryFn: undefined
|
|
42
|
+
}
|
|
41
43
|
}));
|
|
42
44
|
return {
|
|
43
45
|
state: {
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.createQuerySnapshotProvider = createQuerySnapshotProvider;
|
|
7
|
+
exports.registerQuerySnapshotProvider = registerQuerySnapshotProvider;
|
|
8
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
|
+
var _dehydrate = require("./dehydrate");
|
|
10
|
+
/**
|
|
11
|
+
* Time Machine snapshot provider for React Query.
|
|
12
|
+
*
|
|
13
|
+
* Unlike the other providers this one can't register at module init — the
|
|
14
|
+
* QueryClient only exists in React context — so useReactQuerySyncAdapter
|
|
15
|
+
* (mounted app-lifetime by FloatingDevTools' auto external sync) registers
|
|
16
|
+
* it per-client via an effect.
|
|
17
|
+
*
|
|
18
|
+
* Restore is a device-safe DIFF against the live cache, NOT the desktop
|
|
19
|
+
* Hydrate: that one detaches live observers and mocks queryFns, which is
|
|
20
|
+
* fine on a mirror client and destructive in a running app. Here existing
|
|
21
|
+
* queries get `setState` (observers stay attached and are notified, keeping
|
|
22
|
+
* their real queryFns), missing ones are built into the cache, and extras
|
|
23
|
+
* are removed. Mutations aren't restorable (mutationFns can't be captured);
|
|
24
|
+
* the mutation cache is cleared instead.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/** A restored query must never claim an in-flight fetch it doesn't have. */
|
|
28
|
+
function normalizeState(state) {
|
|
29
|
+
return {
|
|
30
|
+
...state,
|
|
31
|
+
fetchStatus: "idle",
|
|
32
|
+
fetchMeta: null
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function createQuerySnapshotProvider(queryClient) {
|
|
36
|
+
return {
|
|
37
|
+
id: "query",
|
|
38
|
+
label: "React Query",
|
|
39
|
+
status: () => ({
|
|
40
|
+
canCapture: true,
|
|
41
|
+
canRestore: true
|
|
42
|
+
}),
|
|
43
|
+
async capture() {
|
|
44
|
+
const {
|
|
45
|
+
queries,
|
|
46
|
+
mutations
|
|
47
|
+
} = (0, _dehydrate.dehydrateQueryClient)(queryClient);
|
|
48
|
+
const warnings = [];
|
|
49
|
+
if (mutations.length > 0) {
|
|
50
|
+
warnings.push(`${mutations.length} mutation(s) not captured (mutation functions cannot be restored)`);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
data: {
|
|
54
|
+
queries: queries.map(({
|
|
55
|
+
observers: _observers,
|
|
56
|
+
...query
|
|
57
|
+
}) => ({
|
|
58
|
+
...query,
|
|
59
|
+
state: normalizeState(query.state)
|
|
60
|
+
}))
|
|
61
|
+
},
|
|
62
|
+
warnings
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
async restore(data) {
|
|
66
|
+
const snapshot = data;
|
|
67
|
+
const result = {
|
|
68
|
+
ok: true,
|
|
69
|
+
applied: 0,
|
|
70
|
+
skipped: [],
|
|
71
|
+
warnings: []
|
|
72
|
+
};
|
|
73
|
+
if (!snapshot?.queries) {
|
|
74
|
+
return {
|
|
75
|
+
ok: false,
|
|
76
|
+
applied: 0,
|
|
77
|
+
skipped: [],
|
|
78
|
+
warnings: ["empty query snapshot"]
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const cache = queryClient.getQueryCache();
|
|
82
|
+
const snapHashes = new Set(snapshot.queries.map(q => q.queryHash));
|
|
83
|
+
for (const snap of snapshot.queries) {
|
|
84
|
+
try {
|
|
85
|
+
const state = normalizeState(snap.state);
|
|
86
|
+
const existing = cache.get(snap.queryHash);
|
|
87
|
+
if (existing) {
|
|
88
|
+
// Observers stay attached (with their real queryFns) and get
|
|
89
|
+
// notified — the safe on-device path, same as triggerError's.
|
|
90
|
+
existing.setState(state);
|
|
91
|
+
} else {
|
|
92
|
+
cache.build(queryClient, {
|
|
93
|
+
queryKey: snap.queryKey,
|
|
94
|
+
queryHash: snap.queryHash,
|
|
95
|
+
...(snap.meta && {
|
|
96
|
+
meta: snap.meta
|
|
97
|
+
})
|
|
98
|
+
}, state);
|
|
99
|
+
}
|
|
100
|
+
result.applied += 1;
|
|
101
|
+
} catch (e) {
|
|
102
|
+
result.skipped.push({
|
|
103
|
+
id: snap.queryHash,
|
|
104
|
+
reason: String(e)
|
|
105
|
+
});
|
|
106
|
+
result.ok = false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Cache entries the snapshot doesn't know about get removed; a mounted
|
|
111
|
+
// observer will rebuild + refetch its query on next interaction.
|
|
112
|
+
for (const query of cache.getAll().slice()) {
|
|
113
|
+
if (!snapHashes.has(query.queryHash)) {
|
|
114
|
+
try {
|
|
115
|
+
cache.remove(query);
|
|
116
|
+
result.applied += 1;
|
|
117
|
+
} catch (e) {
|
|
118
|
+
result.skipped.push({
|
|
119
|
+
id: query.queryHash,
|
|
120
|
+
reason: String(e)
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const mutationCount = queryClient.getMutationCache().getAll().length;
|
|
126
|
+
if (mutationCount > 0) {
|
|
127
|
+
queryClient.getMutationCache().clear();
|
|
128
|
+
result.warnings.push(`mutation cache cleared (${mutationCount} mutation(s))`);
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Register for one client; returns the unregister fn. Last client wins. */
|
|
136
|
+
function registerQuerySnapshotProvider(queryClient) {
|
|
137
|
+
return (0, _sharedUi.registerStateSnapshotProvider)(createQuerySnapshotProvider(queryClient));
|
|
138
|
+
}
|
|
@@ -7,6 +7,7 @@ exports.useReactQuerySyncAdapter = useReactQuerySyncAdapter;
|
|
|
7
7
|
var _react = require("react");
|
|
8
8
|
var _reactQuery = require("@tanstack/react-query");
|
|
9
9
|
var _reactQuerySyncAdapter = require("./reactQuerySyncAdapter");
|
|
10
|
+
var _snapshotProvider = require("./snapshotProvider");
|
|
10
11
|
/**
|
|
11
12
|
* Context-based variant of createReactQuerySyncAdapter for zero-config
|
|
12
13
|
* wiring (FloatingDevTools' auto external sync): reads the QueryClient from
|
|
@@ -22,5 +23,12 @@ function useReactQuerySyncAdapter() {
|
|
|
22
23
|
} catch {
|
|
23
24
|
queryClient = null;
|
|
24
25
|
}
|
|
26
|
+
|
|
27
|
+
// Register the Time Machine snapshot provider for this client — this hook
|
|
28
|
+
// is the one app-lifetime spot that actually holds the QueryClient.
|
|
29
|
+
(0, _react.useEffect)(() => {
|
|
30
|
+
if (!queryClient) return;
|
|
31
|
+
return (0, _snapshotProvider.registerQuerySnapshotProvider)(queryClient);
|
|
32
|
+
}, [queryClient]);
|
|
25
33
|
return (0, _react.useMemo)(() => queryClient ? (0, _reactQuerySyncAdapter.createReactQuerySyncAdapter)(queryClient) : null, [queryClient]);
|
|
26
34
|
}
|
|
@@ -30,10 +30,12 @@ function dehydrateMutation(mutation) {
|
|
|
30
30
|
function dehydrateQuery(query) {
|
|
31
31
|
const observerStates = query.observers.map(observer => ({
|
|
32
32
|
queryHash: query.queryHash,
|
|
33
|
-
options: observer.options,
|
|
34
33
|
// Remove queryFn from observer options so the dashboard can't accidentally
|
|
35
34
|
// run device fetch functions (they aren't serializable anyway)
|
|
36
|
-
|
|
35
|
+
options: {
|
|
36
|
+
...observer.options,
|
|
37
|
+
queryFn: undefined
|
|
38
|
+
}
|
|
37
39
|
}));
|
|
38
40
|
return {
|
|
39
41
|
state: {
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Time Machine snapshot provider for React Query.
|
|
5
|
+
*
|
|
6
|
+
* Unlike the other providers this one can't register at module init — the
|
|
7
|
+
* QueryClient only exists in React context — so useReactQuerySyncAdapter
|
|
8
|
+
* (mounted app-lifetime by FloatingDevTools' auto external sync) registers
|
|
9
|
+
* it per-client via an effect.
|
|
10
|
+
*
|
|
11
|
+
* Restore is a device-safe DIFF against the live cache, NOT the desktop
|
|
12
|
+
* Hydrate: that one detaches live observers and mocks queryFns, which is
|
|
13
|
+
* fine on a mirror client and destructive in a running app. Here existing
|
|
14
|
+
* queries get `setState` (observers stay attached and are notified, keeping
|
|
15
|
+
* their real queryFns), missing ones are built into the cache, and extras
|
|
16
|
+
* are removed. Mutations aren't restorable (mutationFns can't be captured);
|
|
17
|
+
* the mutation cache is cleared instead.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { registerStateSnapshotProvider } from "@buoy-gg/shared-ui";
|
|
21
|
+
import { dehydrateQueryClient } from "./dehydrate";
|
|
22
|
+
/** A restored query must never claim an in-flight fetch it doesn't have. */
|
|
23
|
+
function normalizeState(state) {
|
|
24
|
+
return {
|
|
25
|
+
...state,
|
|
26
|
+
fetchStatus: "idle",
|
|
27
|
+
fetchMeta: null
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function createQuerySnapshotProvider(queryClient) {
|
|
31
|
+
return {
|
|
32
|
+
id: "query",
|
|
33
|
+
label: "React Query",
|
|
34
|
+
status: () => ({
|
|
35
|
+
canCapture: true,
|
|
36
|
+
canRestore: true
|
|
37
|
+
}),
|
|
38
|
+
async capture() {
|
|
39
|
+
const {
|
|
40
|
+
queries,
|
|
41
|
+
mutations
|
|
42
|
+
} = dehydrateQueryClient(queryClient);
|
|
43
|
+
const warnings = [];
|
|
44
|
+
if (mutations.length > 0) {
|
|
45
|
+
warnings.push(`${mutations.length} mutation(s) not captured (mutation functions cannot be restored)`);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
data: {
|
|
49
|
+
queries: queries.map(({
|
|
50
|
+
observers: _observers,
|
|
51
|
+
...query
|
|
52
|
+
}) => ({
|
|
53
|
+
...query,
|
|
54
|
+
state: normalizeState(query.state)
|
|
55
|
+
}))
|
|
56
|
+
},
|
|
57
|
+
warnings
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
async restore(data) {
|
|
61
|
+
const snapshot = data;
|
|
62
|
+
const result = {
|
|
63
|
+
ok: true,
|
|
64
|
+
applied: 0,
|
|
65
|
+
skipped: [],
|
|
66
|
+
warnings: []
|
|
67
|
+
};
|
|
68
|
+
if (!snapshot?.queries) {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
applied: 0,
|
|
72
|
+
skipped: [],
|
|
73
|
+
warnings: ["empty query snapshot"]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const cache = queryClient.getQueryCache();
|
|
77
|
+
const snapHashes = new Set(snapshot.queries.map(q => q.queryHash));
|
|
78
|
+
for (const snap of snapshot.queries) {
|
|
79
|
+
try {
|
|
80
|
+
const state = normalizeState(snap.state);
|
|
81
|
+
const existing = cache.get(snap.queryHash);
|
|
82
|
+
if (existing) {
|
|
83
|
+
// Observers stay attached (with their real queryFns) and get
|
|
84
|
+
// notified — the safe on-device path, same as triggerError's.
|
|
85
|
+
existing.setState(state);
|
|
86
|
+
} else {
|
|
87
|
+
cache.build(queryClient, {
|
|
88
|
+
queryKey: snap.queryKey,
|
|
89
|
+
queryHash: snap.queryHash,
|
|
90
|
+
...(snap.meta && {
|
|
91
|
+
meta: snap.meta
|
|
92
|
+
})
|
|
93
|
+
}, state);
|
|
94
|
+
}
|
|
95
|
+
result.applied += 1;
|
|
96
|
+
} catch (e) {
|
|
97
|
+
result.skipped.push({
|
|
98
|
+
id: snap.queryHash,
|
|
99
|
+
reason: String(e)
|
|
100
|
+
});
|
|
101
|
+
result.ok = false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Cache entries the snapshot doesn't know about get removed; a mounted
|
|
106
|
+
// observer will rebuild + refetch its query on next interaction.
|
|
107
|
+
for (const query of cache.getAll().slice()) {
|
|
108
|
+
if (!snapHashes.has(query.queryHash)) {
|
|
109
|
+
try {
|
|
110
|
+
cache.remove(query);
|
|
111
|
+
result.applied += 1;
|
|
112
|
+
} catch (e) {
|
|
113
|
+
result.skipped.push({
|
|
114
|
+
id: query.queryHash,
|
|
115
|
+
reason: String(e)
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const mutationCount = queryClient.getMutationCache().getAll().length;
|
|
121
|
+
if (mutationCount > 0) {
|
|
122
|
+
queryClient.getMutationCache().clear();
|
|
123
|
+
result.warnings.push(`mutation cache cleared (${mutationCount} mutation(s))`);
|
|
124
|
+
}
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Register for one client; returns the unregister fn. Last client wins. */
|
|
131
|
+
export function registerQuerySnapshotProvider(queryClient) {
|
|
132
|
+
return registerStateSnapshotProvider(createQuerySnapshotProvider(queryClient));
|
|
133
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import { useMemo } from "react";
|
|
3
|
+
import { useEffect, useMemo } from "react";
|
|
4
4
|
import { useQueryClient } from "@tanstack/react-query";
|
|
5
5
|
import { createReactQuerySyncAdapter } from "./reactQuerySyncAdapter";
|
|
6
|
+
import { registerQuerySnapshotProvider } from "./snapshotProvider";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Context-based variant of createReactQuerySyncAdapter for zero-config
|
|
@@ -19,5 +20,12 @@ export function useReactQuerySyncAdapter() {
|
|
|
19
20
|
} catch {
|
|
20
21
|
queryClient = null;
|
|
21
22
|
}
|
|
23
|
+
|
|
24
|
+
// Register the Time Machine snapshot provider for this client — this hook
|
|
25
|
+
// is the one app-lifetime spot that actually holds the QueryClient.
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!queryClient) return;
|
|
28
|
+
return registerQuerySnapshotProvider(queryClient);
|
|
29
|
+
}, [queryClient]);
|
|
22
30
|
return useMemo(() => queryClient ? createReactQuerySyncAdapter(queryClient) : null, [queryClient]);
|
|
23
31
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time Machine snapshot provider for React Query.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the other providers this one can't register at module init — the
|
|
5
|
+
* QueryClient only exists in React context — so useReactQuerySyncAdapter
|
|
6
|
+
* (mounted app-lifetime by FloatingDevTools' auto external sync) registers
|
|
7
|
+
* it per-client via an effect.
|
|
8
|
+
*
|
|
9
|
+
* Restore is a device-safe DIFF against the live cache, NOT the desktop
|
|
10
|
+
* Hydrate: that one detaches live observers and mocks queryFns, which is
|
|
11
|
+
* fine on a mirror client and destructive in a running app. Here existing
|
|
12
|
+
* queries get `setState` (observers stay attached and are notified, keeping
|
|
13
|
+
* their real queryFns), missing ones are built into the cache, and extras
|
|
14
|
+
* are removed. Mutations aren't restorable (mutationFns can't be captured);
|
|
15
|
+
* the mutation cache is cleared instead.
|
|
16
|
+
*/
|
|
17
|
+
import type { QueryClient } from "@tanstack/react-query";
|
|
18
|
+
import { type StateSnapshotProvider } from "@buoy-gg/shared-ui";
|
|
19
|
+
export declare function createQuerySnapshotProvider(queryClient: QueryClient): StateSnapshotProvider;
|
|
20
|
+
/** Register for one client; returns the unregister fn. Last client wins. */
|
|
21
|
+
export declare function registerQuerySnapshotProvider(queryClient: QueryClient): () => void;
|
|
22
|
+
//# sourceMappingURL=snapshotProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshotProvider.d.ts","sourceRoot":"","sources":["../../../../src/react-query/sync/snapshotProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAEL,KAAK,qBAAqB,EAG3B,MAAM,oBAAoB,CAAC;AAc5B,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,WAAW,GACvB,qBAAqB,CAsFvB;AAED,4EAA4E;AAC5E,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,WAAW,GACvB,MAAM,IAAI,CAEZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useReactQuerySyncAdapter.d.ts","sourceRoot":"","sources":["../../../../src/react-query/sync/useReactQuerySyncAdapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"useReactQuerySyncAdapter.d.ts","sourceRoot":"","sources":["../../../../src/react-query/sync/useReactQuerySyncAdapter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAGtE;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,UAAU,CACpD,OAAO,2BAA2B,CACnC,GAAG,IAAI,CAqBP"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/react-query",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.17",
|
|
4
4
|
"description": "TanStack Query devtools inside your React Native app — cache inspection, refetch & invalidate, offline toggle. Part of Buoy devtools.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -24,10 +24,13 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"lib"
|
|
26
26
|
],
|
|
27
|
-
"sideEffects":
|
|
27
|
+
"sideEffects": [
|
|
28
|
+
"**/snapshotProvider.js",
|
|
29
|
+
"**/snapshotProvider.ts"
|
|
30
|
+
],
|
|
28
31
|
"dependencies": {
|
|
29
|
-
"@buoy-gg/
|
|
30
|
-
"@buoy-gg/
|
|
32
|
+
"@buoy-gg/shared-ui": "^7.0.17",
|
|
33
|
+
"@buoy-gg/floating-tools-core": "^7.0.17"
|
|
31
34
|
},
|
|
32
35
|
"peerDependencies": {
|
|
33
36
|
"@tanstack/react-query": ">=5.0.0",
|