@rozenite/tanstack-query-plugin 1.5.1 → 1.6.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 +12 -0
- package/README.md +22 -0
- package/dist/rozenite.json +1 -1
- package/dist/src/react-native/agent/__tests__/tanstack-query-agent.test.d.ts +1 -0
- package/dist/src/react-native/agent/tanstack-query-agent.d.ts +1000 -0
- package/dist/src/react-native/agent/useTanStackQueryAgentTools.d.ts +2 -0
- package/dist/src/react-native/devtools-actions.d.ts +103 -0
- package/dist/useTanStackQueryDevTools.cjs +1 -1
- package/dist/useTanStackQueryDevTools.js +755 -205
- package/package.json +5 -4
- package/src/react-native/agent/__tests__/tanstack-query-agent.test.ts +508 -0
- package/src/react-native/agent/tanstack-query-agent.ts +750 -0
- package/src/react-native/agent/useTanStackQueryAgentTools.ts +147 -0
- package/src/react-native/devtools-actions.ts +197 -0
- package/src/react-native/useHandleDevToolsMessages.ts +9 -104
- package/src/react-native/useTanStackQueryDevTools.ts +3 -0
- package/tsconfig.json +3 -0
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentTool,
|
|
3
|
+
JSONSchema7,
|
|
4
|
+
} from '@rozenite/agent-bridge';
|
|
5
|
+
import type {
|
|
6
|
+
Mutation,
|
|
7
|
+
MutationCacheNotifyEvent,
|
|
8
|
+
MutationObserverOptions,
|
|
9
|
+
MutationState,
|
|
10
|
+
Query,
|
|
11
|
+
QueryCacheNotifyEvent,
|
|
12
|
+
QueryClient,
|
|
13
|
+
} from '@tanstack/react-query';
|
|
14
|
+
import { onlineManager } from '@tanstack/react-query';
|
|
15
|
+
import { applyTanStackQueryDevtoolsAction } from '../devtools-actions';
|
|
16
|
+
|
|
17
|
+
const pluginId = '@rozenite/tanstack-query-plugin';
|
|
18
|
+
const DEFAULT_PAGE_LIMIT = 20;
|
|
19
|
+
const MAX_PAGE_LIMIT = 100;
|
|
20
|
+
|
|
21
|
+
type CursorKind = 'queries' | 'mutations';
|
|
22
|
+
|
|
23
|
+
type PaginationInput = {
|
|
24
|
+
limit?: number;
|
|
25
|
+
cursor?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type QueryHashInput = {
|
|
29
|
+
queryHash: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type QueryToggleInput = QueryHashInput & {
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type MutationIdInput = {
|
|
37
|
+
mutationId: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type OnlineStatusInput = {
|
|
41
|
+
online: boolean;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type CursorState = {
|
|
45
|
+
queriesGeneration: number;
|
|
46
|
+
mutationsGeneration: number;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
type AgentSafeValue =
|
|
50
|
+
| null
|
|
51
|
+
| boolean
|
|
52
|
+
| number
|
|
53
|
+
| string
|
|
54
|
+
| AgentSafeValue[]
|
|
55
|
+
| { [key: string]: AgentSafeValue };
|
|
56
|
+
|
|
57
|
+
const isPlainObject = (value: unknown): value is Record<string, unknown> => {
|
|
58
|
+
return (
|
|
59
|
+
!!value &&
|
|
60
|
+
typeof value === 'object' &&
|
|
61
|
+
Object.getPrototypeOf(value) === Object.prototype
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const serializeForAgent = (
|
|
66
|
+
value: unknown,
|
|
67
|
+
seen = new WeakSet<object>()
|
|
68
|
+
): AgentSafeValue => {
|
|
69
|
+
if (value == null) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeof value === 'string' || typeof value === 'boolean') {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (typeof value === 'number') {
|
|
78
|
+
return Number.isFinite(value) ? value : String(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof value === 'bigint') {
|
|
82
|
+
return value.toString();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (typeof value === 'function' || typeof value === 'symbol') {
|
|
86
|
+
return `[non-serializable:${typeof value}]`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (value instanceof Date) {
|
|
90
|
+
return value.toISOString();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (value instanceof Error) {
|
|
94
|
+
return {
|
|
95
|
+
name: value.name,
|
|
96
|
+
message: value.message,
|
|
97
|
+
stack: value.stack ?? null,
|
|
98
|
+
cause: serializeForAgent(
|
|
99
|
+
(value as Error & { cause?: unknown }).cause,
|
|
100
|
+
seen
|
|
101
|
+
),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (Array.isArray(value)) {
|
|
106
|
+
return value.map((item) => serializeForAgent(item, seen));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (ArrayBuffer.isView(value)) {
|
|
110
|
+
return Array.from(value as unknown as ArrayLike<number>).map((item) =>
|
|
111
|
+
typeof item === 'number' ? item : Number(item)
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (value instanceof ArrayBuffer) {
|
|
116
|
+
return Array.from(new Uint8Array(value));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (typeof value === 'object') {
|
|
120
|
+
if (seen.has(value)) {
|
|
121
|
+
return '[circular]';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
seen.add(value);
|
|
125
|
+
|
|
126
|
+
if (!isPlainObject(value)) {
|
|
127
|
+
return `[non-serializable:${value.constructor?.name ?? 'Object'}]`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const entries = Object.entries(value).map(([key, nestedValue]) => [
|
|
131
|
+
key,
|
|
132
|
+
serializeForAgent(nestedValue, seen),
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
return Object.fromEntries(entries);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return String(value);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const sanitizeLimit = (limit?: number) => {
|
|
142
|
+
if (
|
|
143
|
+
typeof limit !== 'number' ||
|
|
144
|
+
!Number.isFinite(limit) ||
|
|
145
|
+
!Number.isInteger(limit) ||
|
|
146
|
+
limit < 1
|
|
147
|
+
) {
|
|
148
|
+
return DEFAULT_PAGE_LIMIT;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return Math.min(limit, MAX_PAGE_LIMIT);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const encodeCursor = (
|
|
155
|
+
kind: CursorKind,
|
|
156
|
+
generation: number,
|
|
157
|
+
offset: number
|
|
158
|
+
): string => {
|
|
159
|
+
return `${kind}:${generation}:${offset}`;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const decodeCursor = (
|
|
163
|
+
cursor: string,
|
|
164
|
+
kind: CursorKind,
|
|
165
|
+
generation: number
|
|
166
|
+
): number => {
|
|
167
|
+
const [cursorKind, rawGeneration, rawOffset] = cursor.split(':', 3);
|
|
168
|
+
if (cursorKind !== kind || !rawGeneration || !rawOffset) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
'Cursor does not match the requested listing. Run the command again.'
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const cursorGeneration = Number(rawGeneration);
|
|
175
|
+
const offset = Number(rawOffset);
|
|
176
|
+
|
|
177
|
+
if (!Number.isInteger(cursorGeneration) || cursorGeneration !== generation) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
'Cursor does not match the requested listing. Run the command again.'
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!Number.isInteger(offset) || offset < 0) {
|
|
184
|
+
throw new Error('Cursor is invalid. Run the command again.');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return offset;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const paginate = <T>(
|
|
191
|
+
rows: T[],
|
|
192
|
+
kind: CursorKind,
|
|
193
|
+
generation: number,
|
|
194
|
+
input: PaginationInput
|
|
195
|
+
) => {
|
|
196
|
+
const limit = sanitizeLimit(input.limit);
|
|
197
|
+
const startIndex = input.cursor
|
|
198
|
+
? decodeCursor(input.cursor, kind, generation)
|
|
199
|
+
: 0;
|
|
200
|
+
const endIndex = Math.min(startIndex + limit, rows.length);
|
|
201
|
+
const hasMore = endIndex < rows.length;
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
items: rows.slice(startIndex, endIndex),
|
|
205
|
+
page: {
|
|
206
|
+
limit,
|
|
207
|
+
hasMore,
|
|
208
|
+
...(hasMore
|
|
209
|
+
? { nextCursor: encodeCursor(kind, generation, endIndex) }
|
|
210
|
+
: {}),
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const queryActionProperties = {
|
|
216
|
+
queryHash: {
|
|
217
|
+
type: 'string',
|
|
218
|
+
description: 'TanStack Query queryHash identifying the query.',
|
|
219
|
+
},
|
|
220
|
+
} satisfies Record<string, JSONSchema7>;
|
|
221
|
+
|
|
222
|
+
const mutationActionProperties = {
|
|
223
|
+
mutationId: {
|
|
224
|
+
type: 'number',
|
|
225
|
+
description: 'TanStack Query mutationId identifying the mutation.',
|
|
226
|
+
},
|
|
227
|
+
} satisfies Record<string, JSONSchema7>;
|
|
228
|
+
|
|
229
|
+
const paginationProperties = {
|
|
230
|
+
limit: {
|
|
231
|
+
type: 'number',
|
|
232
|
+
description: 'Maximum number of items to return. Defaults to 20. Maximum 100.',
|
|
233
|
+
},
|
|
234
|
+
cursor: {
|
|
235
|
+
type: 'string',
|
|
236
|
+
description: 'Opaque pagination cursor from a previous list call.',
|
|
237
|
+
},
|
|
238
|
+
} satisfies Record<string, JSONSchema7>;
|
|
239
|
+
|
|
240
|
+
const emptyInputSchema: JSONSchema7 = {
|
|
241
|
+
type: 'object',
|
|
242
|
+
properties: {},
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
export const getCacheSummaryTool: AgentTool = {
|
|
246
|
+
name: 'get-cache-summary',
|
|
247
|
+
description:
|
|
248
|
+
'Return aggregate TanStack Query cache health and count information.',
|
|
249
|
+
inputSchema: emptyInputSchema,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export const getOnlineStatusTool: AgentTool = {
|
|
253
|
+
name: 'get-online-status',
|
|
254
|
+
description: 'Return the current TanStack Query onlineManager status.',
|
|
255
|
+
inputSchema: emptyInputSchema,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export const setOnlineStatusTool: AgentTool = {
|
|
259
|
+
name: 'set-online-status',
|
|
260
|
+
description:
|
|
261
|
+
'Set the TanStack Query onlineManager status for testing offline/online behavior.',
|
|
262
|
+
inputSchema: {
|
|
263
|
+
type: 'object',
|
|
264
|
+
properties: {
|
|
265
|
+
online: {
|
|
266
|
+
type: 'boolean',
|
|
267
|
+
description: 'Whether the TanStack Query onlineManager should be online.',
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
required: ['online'],
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
export const listQueriesTool: AgentTool = {
|
|
275
|
+
name: 'list-queries',
|
|
276
|
+
description:
|
|
277
|
+
'List TanStack Query query summaries using cursor pagination.',
|
|
278
|
+
inputSchema: {
|
|
279
|
+
type: 'object',
|
|
280
|
+
properties: paginationProperties,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
export const getQueryDetailsTool: AgentTool = {
|
|
285
|
+
name: 'get-query-details',
|
|
286
|
+
description:
|
|
287
|
+
'Return a JSON-safe TanStack Query query snapshot and observer summary.',
|
|
288
|
+
inputSchema: {
|
|
289
|
+
type: 'object',
|
|
290
|
+
properties: queryActionProperties,
|
|
291
|
+
required: ['queryHash'],
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export const refetchQueryTool: AgentTool = {
|
|
296
|
+
name: 'refetch-query',
|
|
297
|
+
description: 'Refetch a TanStack Query query by queryHash.',
|
|
298
|
+
inputSchema: {
|
|
299
|
+
type: 'object',
|
|
300
|
+
properties: queryActionProperties,
|
|
301
|
+
required: ['queryHash'],
|
|
302
|
+
},
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
export const setQueryLoadingTool: AgentTool = {
|
|
306
|
+
name: 'set-query-loading',
|
|
307
|
+
description:
|
|
308
|
+
'Enable or disable TanStack Query loading-state simulation for a query by queryHash.',
|
|
309
|
+
inputSchema: {
|
|
310
|
+
type: 'object',
|
|
311
|
+
properties: queryActionProperties,
|
|
312
|
+
required: ['queryHash', 'enabled'],
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
export const setQueryErrorTool: AgentTool = {
|
|
317
|
+
name: 'set-query-error',
|
|
318
|
+
description:
|
|
319
|
+
'Enable or disable TanStack Query error-state simulation for a query by queryHash.',
|
|
320
|
+
inputSchema: {
|
|
321
|
+
type: 'object',
|
|
322
|
+
properties: queryActionProperties,
|
|
323
|
+
required: ['queryHash', 'enabled'],
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
export const invalidateQueryTool: AgentTool = {
|
|
328
|
+
name: 'invalidate-query',
|
|
329
|
+
description: 'Invalidate a TanStack Query query by queryHash.',
|
|
330
|
+
inputSchema: {
|
|
331
|
+
type: 'object',
|
|
332
|
+
properties: queryActionProperties,
|
|
333
|
+
required: ['queryHash'],
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
export const resetQueryTool: AgentTool = {
|
|
338
|
+
name: 'reset-query',
|
|
339
|
+
description: 'Reset a TanStack Query query by queryHash.',
|
|
340
|
+
inputSchema: {
|
|
341
|
+
type: 'object',
|
|
342
|
+
properties: queryActionProperties,
|
|
343
|
+
required: ['queryHash'],
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
export const removeQueryTool: AgentTool = {
|
|
348
|
+
name: 'remove-query',
|
|
349
|
+
description: 'Remove a TanStack Query query from the cache by queryHash.',
|
|
350
|
+
inputSchema: {
|
|
351
|
+
type: 'object',
|
|
352
|
+
properties: queryActionProperties,
|
|
353
|
+
required: ['queryHash'],
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
export const clearQueryCacheTool: AgentTool = {
|
|
358
|
+
name: 'clear-query-cache',
|
|
359
|
+
description: 'Clear the full TanStack Query query cache.',
|
|
360
|
+
inputSchema: emptyInputSchema,
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
export const listMutationsTool: AgentTool = {
|
|
364
|
+
name: 'list-mutations',
|
|
365
|
+
description:
|
|
366
|
+
'List TanStack Query mutation summaries using cursor pagination.',
|
|
367
|
+
inputSchema: {
|
|
368
|
+
type: 'object',
|
|
369
|
+
properties: paginationProperties,
|
|
370
|
+
},
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
export const getMutationDetailsTool: AgentTool = {
|
|
374
|
+
name: 'get-mutation-details',
|
|
375
|
+
description:
|
|
376
|
+
'Return a JSON-safe TanStack Query mutation snapshot for a mutationId.',
|
|
377
|
+
inputSchema: {
|
|
378
|
+
type: 'object',
|
|
379
|
+
properties: mutationActionProperties,
|
|
380
|
+
required: ['mutationId'],
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
export const clearMutationCacheTool: AgentTool = {
|
|
385
|
+
name: 'clear-mutation-cache',
|
|
386
|
+
description: 'Clear the full TanStack Query mutation cache.',
|
|
387
|
+
inputSchema: emptyInputSchema,
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
export const TANSTACK_QUERY_AGENT_TOOLS: AgentTool[] = [
|
|
391
|
+
getCacheSummaryTool,
|
|
392
|
+
getOnlineStatusTool,
|
|
393
|
+
setOnlineStatusTool,
|
|
394
|
+
listQueriesTool,
|
|
395
|
+
getQueryDetailsTool,
|
|
396
|
+
refetchQueryTool,
|
|
397
|
+
setQueryLoadingTool,
|
|
398
|
+
setQueryErrorTool,
|
|
399
|
+
invalidateQueryTool,
|
|
400
|
+
resetQueryTool,
|
|
401
|
+
removeQueryTool,
|
|
402
|
+
clearQueryCacheTool,
|
|
403
|
+
listMutationsTool,
|
|
404
|
+
getMutationDetailsTool,
|
|
405
|
+
clearMutationCacheTool,
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
const pickObserverOptionsSummary = (
|
|
409
|
+
options: Record<string, unknown> | undefined
|
|
410
|
+
) => {
|
|
411
|
+
if (!options) {
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return {
|
|
416
|
+
enabled: serializeForAgent(options.enabled),
|
|
417
|
+
networkMode: serializeForAgent(options.networkMode),
|
|
418
|
+
staleTime: serializeForAgent(options.staleTime),
|
|
419
|
+
gcTime: serializeForAgent(options.gcTime),
|
|
420
|
+
retry: serializeForAgent(options.retry),
|
|
421
|
+
retryDelay: serializeForAgent(options.retryDelay),
|
|
422
|
+
refetchInterval: serializeForAgent(options.refetchInterval),
|
|
423
|
+
refetchOnMount: serializeForAgent(options.refetchOnMount),
|
|
424
|
+
refetchOnReconnect: serializeForAgent(options.refetchOnReconnect),
|
|
425
|
+
refetchOnWindowFocus: serializeForAgent(options.refetchOnWindowFocus),
|
|
426
|
+
subscribed: serializeForAgent(options.subscribed),
|
|
427
|
+
meta: serializeForAgent(options.meta),
|
|
428
|
+
hasQueryFn: typeof options.queryFn === 'function',
|
|
429
|
+
hasSelect: typeof options.select === 'function',
|
|
430
|
+
hasPlaceholderData: 'placeholderData' in options,
|
|
431
|
+
hasInitialData: 'initialData' in options,
|
|
432
|
+
};
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const pickMutationOptionsSummary = (
|
|
436
|
+
options: MutationObserverOptions<unknown, Error, unknown, unknown>
|
|
437
|
+
) => {
|
|
438
|
+
return {
|
|
439
|
+
mutationKey: serializeForAgent(options.mutationKey),
|
|
440
|
+
networkMode: serializeForAgent(options.networkMode),
|
|
441
|
+
gcTime: serializeForAgent(options.gcTime),
|
|
442
|
+
retry: serializeForAgent(options.retry),
|
|
443
|
+
retryDelay: serializeForAgent(options.retryDelay),
|
|
444
|
+
meta: serializeForAgent(options.meta),
|
|
445
|
+
scope: serializeForAgent(options.scope),
|
|
446
|
+
hasMutationFn: typeof options.mutationFn === 'function',
|
|
447
|
+
hasOnMutate: typeof options.onMutate === 'function',
|
|
448
|
+
hasOnSuccess: typeof options.onSuccess === 'function',
|
|
449
|
+
hasOnError: typeof options.onError === 'function',
|
|
450
|
+
hasOnSettled: typeof options.onSettled === 'function',
|
|
451
|
+
};
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const getQuerySortTimestamp = (query: Query) => {
|
|
455
|
+
return Math.max(query.state.dataUpdatedAt, query.state.errorUpdatedAt);
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const compareQueries = (a: Query, b: Query) => {
|
|
459
|
+
return (
|
|
460
|
+
getQuerySortTimestamp(b) - getQuerySortTimestamp(a) ||
|
|
461
|
+
b.getObserversCount() - a.getObserversCount() ||
|
|
462
|
+
String(b.queryHash).localeCompare(String(a.queryHash))
|
|
463
|
+
);
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const compareMutations = (
|
|
467
|
+
a: Mutation<unknown, Error, unknown, unknown>,
|
|
468
|
+
b: Mutation<unknown, Error, unknown, unknown>
|
|
469
|
+
) => {
|
|
470
|
+
return (
|
|
471
|
+
b.state.submittedAt - a.state.submittedAt ||
|
|
472
|
+
b.mutationId - a.mutationId
|
|
473
|
+
);
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
const getQuerySummary = (query: Query) => {
|
|
477
|
+
return {
|
|
478
|
+
queryHash: query.queryHash,
|
|
479
|
+
queryKey: serializeForAgent(query.queryKey),
|
|
480
|
+
status: query.state.status,
|
|
481
|
+
fetchStatus: query.state.fetchStatus,
|
|
482
|
+
observersCount: query.getObserversCount(),
|
|
483
|
+
isInvalidated: query.state.isInvalidated,
|
|
484
|
+
dataUpdatedAt: query.state.dataUpdatedAt,
|
|
485
|
+
errorUpdatedAt: query.state.errorUpdatedAt,
|
|
486
|
+
hasData: query.state.data !== undefined,
|
|
487
|
+
hasError: query.state.error != null,
|
|
488
|
+
};
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const getMutationStatus = (
|
|
492
|
+
state: MutationState<unknown, Error, unknown, unknown>
|
|
493
|
+
) => {
|
|
494
|
+
return state.status;
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const getMutationSummary = (
|
|
498
|
+
mutation: Mutation<unknown, Error, unknown, unknown>
|
|
499
|
+
) => {
|
|
500
|
+
return {
|
|
501
|
+
mutationId: mutation.mutationId,
|
|
502
|
+
mutationKey: serializeForAgent(mutation.options.mutationKey),
|
|
503
|
+
status: getMutationStatus(mutation.state),
|
|
504
|
+
isPaused: mutation.state.isPaused,
|
|
505
|
+
submittedAt: mutation.state.submittedAt,
|
|
506
|
+
failureCount: mutation.state.failureCount,
|
|
507
|
+
hasData: mutation.state.data !== undefined,
|
|
508
|
+
hasError: mutation.state.error != null,
|
|
509
|
+
};
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
const resolveQuery = (queryClient: QueryClient, queryHash: string) => {
|
|
513
|
+
const query = queryClient.getQueryCache().get(queryHash);
|
|
514
|
+
if (!query) {
|
|
515
|
+
const available = queryClient
|
|
516
|
+
.getQueryCache()
|
|
517
|
+
.getAll()
|
|
518
|
+
.map((entry) => entry.queryHash)
|
|
519
|
+
.join(', ');
|
|
520
|
+
throw new Error(
|
|
521
|
+
`Unknown queryHash "${queryHash}". Available: ${available || '(none)'}`
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return query;
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const resolveMutation = (queryClient: QueryClient, mutationId: number) => {
|
|
529
|
+
const mutation = queryClient
|
|
530
|
+
.getMutationCache()
|
|
531
|
+
.getAll()
|
|
532
|
+
.find((entry) => entry.mutationId === mutationId);
|
|
533
|
+
|
|
534
|
+
if (!mutation) {
|
|
535
|
+
const available = queryClient
|
|
536
|
+
.getMutationCache()
|
|
537
|
+
.getAll()
|
|
538
|
+
.map((entry) => entry.mutationId)
|
|
539
|
+
.join(', ');
|
|
540
|
+
throw new Error(
|
|
541
|
+
`Unknown mutationId "${mutationId}". Available: ${available || '(none)'}`
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return mutation;
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
export const buildTanStackQueryCacheSummary = (queryClient: QueryClient) => {
|
|
549
|
+
const queries = queryClient.getQueryCache().getAll();
|
|
550
|
+
const mutations = queryClient.getMutationCache().getAll();
|
|
551
|
+
|
|
552
|
+
return {
|
|
553
|
+
online: onlineManager.isOnline(),
|
|
554
|
+
queries: {
|
|
555
|
+
total: queries.length,
|
|
556
|
+
active: queries.filter((query) => query.getObserversCount() > 0).length,
|
|
557
|
+
fetching: queries.filter(
|
|
558
|
+
(query) => query.state.fetchStatus === 'fetching'
|
|
559
|
+
).length,
|
|
560
|
+
pending: queries.filter((query) => query.state.status === 'pending')
|
|
561
|
+
.length,
|
|
562
|
+
success: queries.filter((query) => query.state.status === 'success')
|
|
563
|
+
.length,
|
|
564
|
+
error: queries.filter((query) => query.state.status === 'error').length,
|
|
565
|
+
invalidated: queries.filter((query) => query.state.isInvalidated).length,
|
|
566
|
+
},
|
|
567
|
+
mutations: {
|
|
568
|
+
total: mutations.length,
|
|
569
|
+
pending: mutations.filter(
|
|
570
|
+
(mutation) => mutation.state.status === 'pending'
|
|
571
|
+
).length,
|
|
572
|
+
success: mutations.filter(
|
|
573
|
+
(mutation) => mutation.state.status === 'success'
|
|
574
|
+
).length,
|
|
575
|
+
error: mutations.filter((mutation) => mutation.state.status === 'error')
|
|
576
|
+
.length,
|
|
577
|
+
paused: mutations.filter((mutation) => mutation.state.isPaused).length,
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
export const createTanStackQueryAgentController = (queryClient: QueryClient) => {
|
|
583
|
+
const cursorState: CursorState = {
|
|
584
|
+
queriesGeneration: 0,
|
|
585
|
+
mutationsGeneration: 0,
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
return {
|
|
589
|
+
handleQueryCacheEvent(event: QueryCacheNotifyEvent) {
|
|
590
|
+
if (event.type === 'added' || event.type === 'removed') {
|
|
591
|
+
cursorState.queriesGeneration += 1;
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
|
|
595
|
+
handleMutationCacheEvent(event: MutationCacheNotifyEvent) {
|
|
596
|
+
if (event.type === 'added' || event.type === 'removed') {
|
|
597
|
+
cursorState.mutationsGeneration += 1;
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
|
|
601
|
+
getCacheSummary() {
|
|
602
|
+
return buildTanStackQueryCacheSummary(queryClient);
|
|
603
|
+
},
|
|
604
|
+
|
|
605
|
+
getOnlineStatus() {
|
|
606
|
+
return {
|
|
607
|
+
online: onlineManager.isOnline(),
|
|
608
|
+
};
|
|
609
|
+
},
|
|
610
|
+
|
|
611
|
+
setOnlineStatus({ online }: OnlineStatusInput) {
|
|
612
|
+
onlineManager.setOnline(online);
|
|
613
|
+
return {
|
|
614
|
+
online: onlineManager.isOnline(),
|
|
615
|
+
};
|
|
616
|
+
},
|
|
617
|
+
|
|
618
|
+
listQueries(input: PaginationInput = {}) {
|
|
619
|
+
const queries = [...queryClient.getQueryCache().getAll()].sort(compareQueries);
|
|
620
|
+
return {
|
|
621
|
+
...buildTanStackQueryCacheSummary(queryClient),
|
|
622
|
+
total: queries.length,
|
|
623
|
+
...paginate(
|
|
624
|
+
queries.map(getQuerySummary),
|
|
625
|
+
'queries',
|
|
626
|
+
cursorState.queriesGeneration,
|
|
627
|
+
input
|
|
628
|
+
),
|
|
629
|
+
};
|
|
630
|
+
},
|
|
631
|
+
|
|
632
|
+
getQueryDetails({ queryHash }: QueryHashInput) {
|
|
633
|
+
const query = resolveQuery(queryClient, queryHash);
|
|
634
|
+
|
|
635
|
+
return {
|
|
636
|
+
summary: buildTanStackQueryCacheSummary(queryClient),
|
|
637
|
+
query: {
|
|
638
|
+
...getQuerySummary(query),
|
|
639
|
+
data: serializeForAgent(query.state.data),
|
|
640
|
+
error: serializeForAgent(query.state.error),
|
|
641
|
+
observers: query.observers.map((observer) => ({
|
|
642
|
+
queryHash: query.queryHash,
|
|
643
|
+
options: pickObserverOptionsSummary(
|
|
644
|
+
observer.options as unknown as Record<string, unknown>
|
|
645
|
+
),
|
|
646
|
+
})),
|
|
647
|
+
},
|
|
648
|
+
};
|
|
649
|
+
},
|
|
650
|
+
|
|
651
|
+
async refetchQuery({ queryHash }: QueryHashInput) {
|
|
652
|
+
return applyTanStackQueryDevtoolsAction(queryClient, {
|
|
653
|
+
type: 'REFETCH',
|
|
654
|
+
queryHash,
|
|
655
|
+
});
|
|
656
|
+
},
|
|
657
|
+
|
|
658
|
+
async setQueryLoading({ queryHash, enabled }: QueryToggleInput) {
|
|
659
|
+
return applyTanStackQueryDevtoolsAction(queryClient, {
|
|
660
|
+
type: enabled ? 'TRIGGER_LOADING' : 'RESTORE_LOADING',
|
|
661
|
+
queryHash,
|
|
662
|
+
});
|
|
663
|
+
},
|
|
664
|
+
|
|
665
|
+
async setQueryError({ queryHash, enabled }: QueryToggleInput) {
|
|
666
|
+
return applyTanStackQueryDevtoolsAction(queryClient, {
|
|
667
|
+
type: enabled ? 'TRIGGER_ERROR' : 'RESTORE_ERROR',
|
|
668
|
+
queryHash,
|
|
669
|
+
});
|
|
670
|
+
},
|
|
671
|
+
|
|
672
|
+
async invalidateQuery({ queryHash }: QueryHashInput) {
|
|
673
|
+
return applyTanStackQueryDevtoolsAction(queryClient, {
|
|
674
|
+
type: 'INVALIDATE',
|
|
675
|
+
queryHash,
|
|
676
|
+
});
|
|
677
|
+
},
|
|
678
|
+
|
|
679
|
+
async resetQuery({ queryHash }: QueryHashInput) {
|
|
680
|
+
return applyTanStackQueryDevtoolsAction(queryClient, {
|
|
681
|
+
type: 'RESET',
|
|
682
|
+
queryHash,
|
|
683
|
+
});
|
|
684
|
+
},
|
|
685
|
+
|
|
686
|
+
async removeQuery({ queryHash }: QueryHashInput) {
|
|
687
|
+
return applyTanStackQueryDevtoolsAction(queryClient, {
|
|
688
|
+
type: 'REMOVE',
|
|
689
|
+
queryHash,
|
|
690
|
+
});
|
|
691
|
+
},
|
|
692
|
+
|
|
693
|
+
async clearQueryCache() {
|
|
694
|
+
return applyTanStackQueryDevtoolsAction(queryClient, {
|
|
695
|
+
type: 'CLEAR_QUERY_CACHE',
|
|
696
|
+
});
|
|
697
|
+
},
|
|
698
|
+
|
|
699
|
+
listMutations(input: PaginationInput = {}) {
|
|
700
|
+
const mutations = [...queryClient.getMutationCache().getAll()].sort(
|
|
701
|
+
compareMutations
|
|
702
|
+
);
|
|
703
|
+
|
|
704
|
+
return {
|
|
705
|
+
...buildTanStackQueryCacheSummary(queryClient),
|
|
706
|
+
total: mutations.length,
|
|
707
|
+
...paginate(
|
|
708
|
+
mutations.map(getMutationSummary),
|
|
709
|
+
'mutations',
|
|
710
|
+
cursorState.mutationsGeneration,
|
|
711
|
+
input
|
|
712
|
+
),
|
|
713
|
+
};
|
|
714
|
+
},
|
|
715
|
+
|
|
716
|
+
getMutationDetails({ mutationId }: MutationIdInput) {
|
|
717
|
+
const mutation = resolveMutation(queryClient, mutationId);
|
|
718
|
+
|
|
719
|
+
return {
|
|
720
|
+
summary: buildTanStackQueryCacheSummary(queryClient),
|
|
721
|
+
mutation: {
|
|
722
|
+
...getMutationSummary(mutation),
|
|
723
|
+
variables: serializeForAgent(mutation.state.variables),
|
|
724
|
+
data: serializeForAgent(mutation.state.data),
|
|
725
|
+
error: serializeForAgent(mutation.state.error),
|
|
726
|
+
context: serializeForAgent(mutation.state.context),
|
|
727
|
+
failureReason: serializeForAgent(mutation.state.failureReason),
|
|
728
|
+
options: pickMutationOptionsSummary(mutation.options),
|
|
729
|
+
},
|
|
730
|
+
};
|
|
731
|
+
},
|
|
732
|
+
|
|
733
|
+
async clearMutationCache() {
|
|
734
|
+
return applyTanStackQueryDevtoolsAction(queryClient, {
|
|
735
|
+
type: 'CLEAR_MUTATION_CACHE',
|
|
736
|
+
});
|
|
737
|
+
},
|
|
738
|
+
};
|
|
739
|
+
};
|
|
740
|
+
|
|
741
|
+
export type TanStackQueryAgentController = ReturnType<
|
|
742
|
+
typeof createTanStackQueryAgentController
|
|
743
|
+
>;
|
|
744
|
+
|
|
745
|
+
export type TanStackQueryAgentPaginationInput = PaginationInput;
|
|
746
|
+
export type TanStackQueryAgentQueryHashInput = QueryHashInput;
|
|
747
|
+
export type TanStackQueryAgentQueryToggleInput = QueryToggleInput;
|
|
748
|
+
export type TanStackQueryAgentMutationIdInput = MutationIdInput;
|
|
749
|
+
export type TanStackQueryAgentOnlineStatusInput = OnlineStatusInput;
|
|
750
|
+
export const TANSTACK_QUERY_AGENT_PLUGIN_ID = pluginId;
|