@rozenite/tanstack-query-plugin 1.7.0-rc.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/devtools/assets/{CXEL7IU7-DJlSmt0p.js → CXEL7IU7-DmbowaBr.js} +1 -1
- package/dist/devtools/assets/{tanstack-query-_leiFatc.js → tanstack-query-DRBLkSCy.js} +11 -11
- package/dist/devtools/tanstack-query.html +1 -1
- package/dist/react-native/chunks/useTanStackQueryDevTools.require.cjs +1 -1
- package/dist/react-native/chunks/useTanStackQueryDevTools.require.js +309 -268
- package/dist/rozenite.json +1 -1
- package/dist/sdk/index.cjs +1 -0
- package/dist/sdk/index.d.ts +1939 -0
- package/dist/sdk/index.js +167 -0
- package/package.json +13 -6
- package/sdk.ts +55 -0
- package/src/react-native/agent/__tests__/tanstack-query-agent.test.ts +73 -29
- package/src/react-native/agent/tanstack-query-agent.ts +33 -235
- package/src/react-native/agent/useTanStackQueryAgentTools.ts +39 -65
- package/src/react-native/devtools-actions.ts +17 -3
- package/src/react-native/useHandleDevToolsMessages.ts +14 -11
- package/src/shared/agent-tools.ts +403 -0
- package/src/shared/hydrate.ts +16 -13
- package/src/shared/messaging.ts +3 -0
- package/src/shared/query-data-sync.test.ts +93 -0
- package/src/shared/query-data-sync.ts +156 -0
- package/src/shared/types.ts +1 -0
- package/src/ui/tanstack-query.tsx +12 -0
- package/tsconfig.json +4 -1
- package/vite.config.ts +7 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Query,
|
|
3
|
+
QueryClient,
|
|
4
|
+
QueryKey,
|
|
5
|
+
QueryState,
|
|
6
|
+
} from '@tanstack/react-query';
|
|
7
|
+
|
|
8
|
+
type QuerySetState = Query['setState'];
|
|
9
|
+
type QueryClientSetQueryData = QueryClient['setQueryData'];
|
|
10
|
+
|
|
11
|
+
type SyncQueryDataPayload = {
|
|
12
|
+
queryHash: string;
|
|
13
|
+
data: unknown;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type QueryDataSyncHandler = (payload: SyncQueryDataPayload) => void;
|
|
17
|
+
|
|
18
|
+
const originalQuerySetStateMap = new WeakMap<Query, QuerySetState>();
|
|
19
|
+
const originalSetQueryDataMap = new WeakMap<
|
|
20
|
+
QueryClient,
|
|
21
|
+
QueryClientSetQueryData
|
|
22
|
+
>();
|
|
23
|
+
const instrumentedQueryClients = new WeakSet<QueryClient>();
|
|
24
|
+
const instrumentedQueries = new WeakSet<Query>();
|
|
25
|
+
const queryClientHandlers = new WeakMap<
|
|
26
|
+
QueryClient,
|
|
27
|
+
QueryDataSyncHandler | undefined
|
|
28
|
+
>();
|
|
29
|
+
const queryHandlers = new WeakMap<Query, QueryDataSyncHandler | undefined>();
|
|
30
|
+
|
|
31
|
+
const getOriginalQuerySetState = (query: Query): QuerySetState => {
|
|
32
|
+
return originalQuerySetStateMap.get(query) ?? query.setState.bind(query);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const emitIfDataChanged = (
|
|
36
|
+
query: Query,
|
|
37
|
+
previousData: unknown,
|
|
38
|
+
handler?: QueryDataSyncHandler,
|
|
39
|
+
) => {
|
|
40
|
+
if (!handler || Object.is(previousData, query.state.data)) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
handler({
|
|
45
|
+
queryHash: query.queryHash,
|
|
46
|
+
data: query.state.data,
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const isDataOnlyStateUpdate = (
|
|
51
|
+
previousState: QueryState,
|
|
52
|
+
nextState: Partial<QueryState>,
|
|
53
|
+
) => {
|
|
54
|
+
if (!('data' in nextState)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const changedKeys = Object.keys(nextState).filter((key) => {
|
|
59
|
+
const typedKey = key as keyof QueryState;
|
|
60
|
+
return !Object.is(previousState[typedKey], nextState[typedKey]);
|
|
61
|
+
}) as Array<keyof QueryState>;
|
|
62
|
+
|
|
63
|
+
return changedKeys.every((key) => key === 'data');
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const applyRemoteQueryState = (
|
|
67
|
+
query: Query,
|
|
68
|
+
state: Partial<QueryState>,
|
|
69
|
+
) => {
|
|
70
|
+
const originalSetState = getOriginalQuerySetState(query);
|
|
71
|
+
originalSetState(state);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const instrumentQuery = (
|
|
75
|
+
query: Query,
|
|
76
|
+
handler?: QueryDataSyncHandler,
|
|
77
|
+
) => {
|
|
78
|
+
queryHandlers.set(query, handler);
|
|
79
|
+
|
|
80
|
+
if (instrumentedQueries.has(query)) {
|
|
81
|
+
return query;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const originalSetState = query.setState.bind(query);
|
|
85
|
+
originalQuerySetStateMap.set(query, originalSetState);
|
|
86
|
+
|
|
87
|
+
query.setState = ((state) => {
|
|
88
|
+
const shouldEmit = isDataOnlyStateUpdate(query.state, state);
|
|
89
|
+
const previousData = query.state.data;
|
|
90
|
+
const result = originalSetState(state);
|
|
91
|
+
|
|
92
|
+
if (shouldEmit) {
|
|
93
|
+
emitIfDataChanged(query, previousData, queryHandlers.get(query));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return result;
|
|
97
|
+
}) as QuerySetState;
|
|
98
|
+
|
|
99
|
+
instrumentedQueries.add(query);
|
|
100
|
+
return query;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const resolveQueryHash = (queryClient: QueryClient, queryKey: QueryKey) => {
|
|
104
|
+
return queryClient.getQueryCache().find({ queryKey })?.queryHash;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const instrumentQueryClient = (
|
|
108
|
+
queryClient: QueryClient,
|
|
109
|
+
handler?: QueryDataSyncHandler,
|
|
110
|
+
) => {
|
|
111
|
+
queryClientHandlers.set(queryClient, handler);
|
|
112
|
+
|
|
113
|
+
if (instrumentedQueryClients.has(queryClient)) {
|
|
114
|
+
queryClient
|
|
115
|
+
.getQueryCache()
|
|
116
|
+
.getAll()
|
|
117
|
+
.forEach((query) => {
|
|
118
|
+
instrumentQuery(query, handler);
|
|
119
|
+
});
|
|
120
|
+
return queryClient;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const originalSetQueryData = queryClient.setQueryData.bind(queryClient);
|
|
124
|
+
originalSetQueryDataMap.set(queryClient, originalSetQueryData);
|
|
125
|
+
|
|
126
|
+
queryClient.setQueryData = ((queryKey, updater, options) => {
|
|
127
|
+
const result = originalSetQueryData(queryKey, updater, options);
|
|
128
|
+
const query = queryClient.getQueryCache().find({ queryKey });
|
|
129
|
+
const activeQueryClientHandler = queryClientHandlers.get(queryClient);
|
|
130
|
+
|
|
131
|
+
if (query) {
|
|
132
|
+
instrumentQuery(query, activeQueryClientHandler);
|
|
133
|
+
const queryHash = resolveQueryHash(queryClient, queryKey);
|
|
134
|
+
const activeHandler = queryHandlers.get(query);
|
|
135
|
+
|
|
136
|
+
if (activeHandler && queryHash) {
|
|
137
|
+
activeHandler({
|
|
138
|
+
queryHash,
|
|
139
|
+
data: query.state.data,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return result;
|
|
145
|
+
}) as QueryClientSetQueryData;
|
|
146
|
+
|
|
147
|
+
queryClient
|
|
148
|
+
.getQueryCache()
|
|
149
|
+
.getAll()
|
|
150
|
+
.forEach((query) => {
|
|
151
|
+
instrumentQuery(query, handler);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
instrumentedQueryClients.add(queryClient);
|
|
155
|
+
return queryClient;
|
|
156
|
+
};
|
package/src/shared/types.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
1
2
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
2
3
|
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools/production';
|
|
3
4
|
import { useRozeniteDevToolsClient } from '@rozenite/plugin-bridge';
|
|
@@ -6,12 +7,23 @@ import { useSyncInitialData } from './useSyncInitialData';
|
|
|
6
7
|
import { useSyncDevToolsEvents } from './useSyncDevToolsEvents';
|
|
7
8
|
import { useSyncOnlineStatus } from '../shared/useSyncOnlineStatus';
|
|
8
9
|
import { useHandleSyncMessages } from './useHandleSyncMessages';
|
|
10
|
+
import { instrumentQueryClient } from '../shared/query-data-sync';
|
|
9
11
|
|
|
10
12
|
const App = () => {
|
|
11
13
|
const client = useRozeniteDevToolsClient<TanStackQueryPluginEventMap>({
|
|
12
14
|
pluginId: '@rozenite/tanstack-query-plugin',
|
|
13
15
|
});
|
|
14
16
|
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
instrumentQueryClient(queryClient, ({ queryHash, data }) => {
|
|
19
|
+
client?.send('devtools-action', {
|
|
20
|
+
type: 'SET_QUERY_DATA',
|
|
21
|
+
queryHash,
|
|
22
|
+
metadata: { data },
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}, [client]);
|
|
26
|
+
|
|
15
27
|
useSyncInitialData(client);
|
|
16
28
|
|
|
17
29
|
useSyncDevToolsEvents(client);
|
package/tsconfig.json
CHANGED
|
@@ -11,12 +11,15 @@
|
|
|
11
11
|
"noFallthroughCasesInSwitch": true,
|
|
12
12
|
"module": "ESNext",
|
|
13
13
|
"moduleResolution": "bundler",
|
|
14
|
+
"paths": {
|
|
15
|
+
"@rozenite/agent-shared": ["../agent-shared/src/index.ts"]
|
|
16
|
+
},
|
|
14
17
|
"resolveJsonModule": true,
|
|
15
18
|
"isolatedModules": true,
|
|
16
19
|
"noEmit": true,
|
|
17
20
|
"jsx": "react-jsx"
|
|
18
21
|
},
|
|
19
|
-
"include": ["src/**/*", "react-native.ts", "rozenite.config.ts"],
|
|
22
|
+
"include": ["src/**/*", "react-native.ts", "sdk.ts", "rozenite.config.ts"],
|
|
20
23
|
"exclude": ["node_modules", "dist", "build"],
|
|
21
24
|
"references": [
|
|
22
25
|
{
|
package/vite.config.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
/// <reference types='vitest' />
|
|
2
|
+
import { resolve } from 'node:path';
|
|
2
3
|
import { defineConfig } from 'vite';
|
|
3
4
|
import { rozenitePlugin } from '@rozenite/vite-plugin';
|
|
4
5
|
|
|
5
6
|
export default defineConfig({
|
|
6
7
|
root: __dirname,
|
|
7
8
|
plugins: [rozenitePlugin()],
|
|
9
|
+
test: {
|
|
10
|
+
passWithNoTests: true,
|
|
11
|
+
alias: {
|
|
12
|
+
'@rozenite/agent-shared': resolve(__dirname, '../agent-shared/src/index.ts'),
|
|
13
|
+
},
|
|
14
|
+
},
|
|
8
15
|
base: './',
|
|
9
16
|
build: {
|
|
10
17
|
outDir: './dist',
|