@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.
@@ -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;