@tanstack/query-db-collection 0.2.15 → 0.2.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/dist/cjs/query.cjs +29 -5
- package/dist/cjs/query.cjs.map +1 -1
- package/dist/cjs/query.d.cts +48 -172
- package/dist/esm/query.d.ts +48 -172
- package/dist/esm/query.js +29 -5
- package/dist/esm/query.js.map +1 -1
- package/package.json +2 -2
- package/src/query.ts +151 -244
package/src/query.ts
CHANGED
|
@@ -13,14 +13,12 @@ import type {
|
|
|
13
13
|
QueryObserverOptions,
|
|
14
14
|
} from "@tanstack/query-core"
|
|
15
15
|
import type {
|
|
16
|
+
BaseCollectionConfig,
|
|
16
17
|
ChangeMessage,
|
|
17
18
|
CollectionConfig,
|
|
18
|
-
DeleteMutationFn,
|
|
19
19
|
DeleteMutationFnParams,
|
|
20
|
-
InsertMutationFn,
|
|
21
20
|
InsertMutationFnParams,
|
|
22
21
|
SyncConfig,
|
|
23
|
-
UpdateMutationFn,
|
|
24
22
|
UpdateMutationFnParams,
|
|
25
23
|
UtilsRecord,
|
|
26
24
|
} from "@tanstack/db"
|
|
@@ -36,37 +34,24 @@ type InferSchemaOutput<T> = T extends StandardSchemaV1
|
|
|
36
34
|
: Record<string, unknown>
|
|
37
35
|
: Record<string, unknown>
|
|
38
36
|
|
|
39
|
-
//
|
|
40
|
-
type
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
? TItem extends object
|
|
44
|
-
? TItem
|
|
37
|
+
// Schema input type inference helper (matches electric.ts pattern)
|
|
38
|
+
type InferSchemaInput<T> = T extends StandardSchemaV1
|
|
39
|
+
? StandardSchemaV1.InferInput<T> extends object
|
|
40
|
+
? StandardSchemaV1.InferInput<T>
|
|
45
41
|
: Record<string, unknown>
|
|
46
42
|
: Record<string, unknown>
|
|
47
43
|
|
|
48
|
-
// Type resolution system with priority order (matches electric.ts pattern)
|
|
49
|
-
type ResolveType<
|
|
50
|
-
TExplicit extends object | unknown = unknown,
|
|
51
|
-
TSchema extends StandardSchemaV1 = never,
|
|
52
|
-
TQueryFn = unknown,
|
|
53
|
-
> = unknown extends TExplicit
|
|
54
|
-
? [TSchema] extends [never]
|
|
55
|
-
? InferQueryFnOutput<TQueryFn>
|
|
56
|
-
: InferSchemaOutput<TSchema>
|
|
57
|
-
: TExplicit
|
|
58
|
-
|
|
59
44
|
/**
|
|
60
45
|
* Configuration options for creating a Query Collection
|
|
61
|
-
* @template
|
|
62
|
-
* @template
|
|
63
|
-
* @template TQueryFn - The queryFn type for inferring return type (third priority)
|
|
46
|
+
* @template T - The explicit type of items stored in the collection
|
|
47
|
+
* @template TQueryFn - The queryFn type
|
|
64
48
|
* @template TError - The type of errors that can occur during queries
|
|
65
49
|
* @template TQueryKey - The type of the query key
|
|
50
|
+
* @template TKey - The type of the item keys
|
|
51
|
+
* @template TSchema - The schema type for validation
|
|
66
52
|
*/
|
|
67
53
|
export interface QueryCollectionConfig<
|
|
68
|
-
|
|
69
|
-
TSchema extends StandardSchemaV1 = never,
|
|
54
|
+
T extends object = object,
|
|
70
55
|
TQueryFn extends (
|
|
71
56
|
context: QueryFunctionContext<any>
|
|
72
57
|
) => Promise<Array<any>> = (
|
|
@@ -74,7 +59,9 @@ export interface QueryCollectionConfig<
|
|
|
74
59
|
) => Promise<Array<any>>,
|
|
75
60
|
TError = unknown,
|
|
76
61
|
TQueryKey extends QueryKey = QueryKey,
|
|
77
|
-
|
|
62
|
+
TKey extends string | number = string | number,
|
|
63
|
+
TSchema extends StandardSchemaV1 = never,
|
|
64
|
+
> extends BaseCollectionConfig<T, TKey, TSchema> {
|
|
78
65
|
/** The query key used by TanStack Query to identify this query */
|
|
79
66
|
queryKey: TQueryKey
|
|
80
67
|
/** Function that fetches data from the server. Must return the complete collection state */
|
|
@@ -82,9 +69,7 @@ export interface QueryCollectionConfig<
|
|
|
82
69
|
context: QueryFunctionContext<TQueryKey>
|
|
83
70
|
) => Promise<Array<any>>
|
|
84
71
|
? TQueryFn
|
|
85
|
-
: (
|
|
86
|
-
context: QueryFunctionContext<TQueryKey>
|
|
87
|
-
) => Promise<Array<ResolveType<TExplicit, TSchema, TQueryFn>>>
|
|
72
|
+
: (context: QueryFunctionContext<TQueryKey>) => Promise<Array<T>>
|
|
88
73
|
|
|
89
74
|
/** The TanStack Query client instance */
|
|
90
75
|
queryClient: QueryClient
|
|
@@ -93,188 +78,34 @@ export interface QueryCollectionConfig<
|
|
|
93
78
|
/** Whether the query should automatically run (default: true) */
|
|
94
79
|
enabled?: boolean
|
|
95
80
|
refetchInterval?: QueryObserverOptions<
|
|
96
|
-
Array<
|
|
81
|
+
Array<T>,
|
|
97
82
|
TError,
|
|
98
|
-
Array<
|
|
99
|
-
Array<
|
|
83
|
+
Array<T>,
|
|
84
|
+
Array<T>,
|
|
100
85
|
TQueryKey
|
|
101
86
|
>[`refetchInterval`]
|
|
102
87
|
retry?: QueryObserverOptions<
|
|
103
|
-
Array<
|
|
88
|
+
Array<T>,
|
|
104
89
|
TError,
|
|
105
|
-
Array<
|
|
106
|
-
Array<
|
|
90
|
+
Array<T>,
|
|
91
|
+
Array<T>,
|
|
107
92
|
TQueryKey
|
|
108
93
|
>[`retry`]
|
|
109
94
|
retryDelay?: QueryObserverOptions<
|
|
110
|
-
Array<
|
|
95
|
+
Array<T>,
|
|
111
96
|
TError,
|
|
112
|
-
Array<
|
|
113
|
-
Array<
|
|
97
|
+
Array<T>,
|
|
98
|
+
Array<T>,
|
|
114
99
|
TQueryKey
|
|
115
100
|
>[`retryDelay`]
|
|
116
101
|
staleTime?: QueryObserverOptions<
|
|
117
|
-
Array<
|
|
102
|
+
Array<T>,
|
|
118
103
|
TError,
|
|
119
|
-
Array<
|
|
120
|
-
Array<
|
|
104
|
+
Array<T>,
|
|
105
|
+
Array<T>,
|
|
121
106
|
TQueryKey
|
|
122
107
|
>[`staleTime`]
|
|
123
108
|
|
|
124
|
-
// Standard Collection configuration properties
|
|
125
|
-
/** Unique identifier for the collection */
|
|
126
|
-
id?: string
|
|
127
|
-
/** Function to extract the unique key from an item */
|
|
128
|
-
getKey: CollectionConfig<ResolveType<TExplicit, TSchema, TQueryFn>>[`getKey`]
|
|
129
|
-
/** Schema for validating items */
|
|
130
|
-
schema?: TSchema
|
|
131
|
-
sync?: CollectionConfig<ResolveType<TExplicit, TSchema, TQueryFn>>[`sync`]
|
|
132
|
-
startSync?: CollectionConfig<
|
|
133
|
-
ResolveType<TExplicit, TSchema, TQueryFn>
|
|
134
|
-
>[`startSync`]
|
|
135
|
-
|
|
136
|
-
// Direct persistence handlers
|
|
137
|
-
/**
|
|
138
|
-
* Optional asynchronous handler function called before an insert operation
|
|
139
|
-
* @param params Object containing transaction and collection information
|
|
140
|
-
* @returns Promise resolving to void or { refetch?: boolean } to control refetching
|
|
141
|
-
* @example
|
|
142
|
-
* // Basic query collection insert handler
|
|
143
|
-
* onInsert: async ({ transaction }) => {
|
|
144
|
-
* const newItem = transaction.mutations[0].modified
|
|
145
|
-
* await api.createTodo(newItem)
|
|
146
|
-
* // Automatically refetches query after insert
|
|
147
|
-
* }
|
|
148
|
-
*
|
|
149
|
-
* @example
|
|
150
|
-
* // Insert handler with refetch control
|
|
151
|
-
* onInsert: async ({ transaction }) => {
|
|
152
|
-
* const newItem = transaction.mutations[0].modified
|
|
153
|
-
* await api.createTodo(newItem)
|
|
154
|
-
* return { refetch: false } // Skip automatic refetch
|
|
155
|
-
* }
|
|
156
|
-
*
|
|
157
|
-
* @example
|
|
158
|
-
* // Insert handler with multiple items
|
|
159
|
-
* onInsert: async ({ transaction }) => {
|
|
160
|
-
* const items = transaction.mutations.map(m => m.modified)
|
|
161
|
-
* await api.createTodos(items)
|
|
162
|
-
* // Will refetch query to get updated data
|
|
163
|
-
* }
|
|
164
|
-
*
|
|
165
|
-
* @example
|
|
166
|
-
* // Insert handler with error handling
|
|
167
|
-
* onInsert: async ({ transaction }) => {
|
|
168
|
-
* try {
|
|
169
|
-
* const newItem = transaction.mutations[0].modified
|
|
170
|
-
* await api.createTodo(newItem)
|
|
171
|
-
* } catch (error) {
|
|
172
|
-
* console.error('Insert failed:', error)
|
|
173
|
-
* throw error // Transaction will rollback optimistic changes
|
|
174
|
-
* }
|
|
175
|
-
* }
|
|
176
|
-
*/
|
|
177
|
-
onInsert?: InsertMutationFn<ResolveType<TExplicit, TSchema, TQueryFn>>
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Optional asynchronous handler function called before an update operation
|
|
181
|
-
* @param params Object containing transaction and collection information
|
|
182
|
-
* @returns Promise resolving to void or { refetch?: boolean } to control refetching
|
|
183
|
-
* @example
|
|
184
|
-
* // Basic query collection update handler
|
|
185
|
-
* onUpdate: async ({ transaction }) => {
|
|
186
|
-
* const mutation = transaction.mutations[0]
|
|
187
|
-
* await api.updateTodo(mutation.original.id, mutation.changes)
|
|
188
|
-
* // Automatically refetches query after update
|
|
189
|
-
* }
|
|
190
|
-
*
|
|
191
|
-
* @example
|
|
192
|
-
* // Update handler with multiple items
|
|
193
|
-
* onUpdate: async ({ transaction }) => {
|
|
194
|
-
* const updates = transaction.mutations.map(m => ({
|
|
195
|
-
* id: m.key,
|
|
196
|
-
* changes: m.changes
|
|
197
|
-
* }))
|
|
198
|
-
* await api.updateTodos(updates)
|
|
199
|
-
* // Will refetch query to get updated data
|
|
200
|
-
* }
|
|
201
|
-
*
|
|
202
|
-
* @example
|
|
203
|
-
* // Update handler with manual refetch
|
|
204
|
-
* onUpdate: async ({ transaction, collection }) => {
|
|
205
|
-
* const mutation = transaction.mutations[0]
|
|
206
|
-
* await api.updateTodo(mutation.original.id, mutation.changes)
|
|
207
|
-
*
|
|
208
|
-
* // Manually trigger refetch
|
|
209
|
-
* await collection.utils.refetch()
|
|
210
|
-
*
|
|
211
|
-
* return { refetch: false } // Skip automatic refetch
|
|
212
|
-
* }
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* // Update handler with related collection refetch
|
|
216
|
-
* onUpdate: async ({ transaction, collection }) => {
|
|
217
|
-
* const mutation = transaction.mutations[0]
|
|
218
|
-
* await api.updateTodo(mutation.original.id, mutation.changes)
|
|
219
|
-
*
|
|
220
|
-
* // Refetch related collections when this item changes
|
|
221
|
-
* await Promise.all([
|
|
222
|
-
* collection.utils.refetch(), // Refetch this collection
|
|
223
|
-
* usersCollection.utils.refetch(), // Refetch users
|
|
224
|
-
* tagsCollection.utils.refetch() // Refetch tags
|
|
225
|
-
* ])
|
|
226
|
-
*
|
|
227
|
-
* return { refetch: false } // Skip automatic refetch since we handled it manually
|
|
228
|
-
* }
|
|
229
|
-
*/
|
|
230
|
-
onUpdate?: UpdateMutationFn<ResolveType<TExplicit, TSchema, TQueryFn>>
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Optional asynchronous handler function called before a delete operation
|
|
234
|
-
* @param params Object containing transaction and collection information
|
|
235
|
-
* @returns Promise resolving to void or { refetch?: boolean } to control refetching
|
|
236
|
-
* @example
|
|
237
|
-
* // Basic query collection delete handler
|
|
238
|
-
* onDelete: async ({ transaction }) => {
|
|
239
|
-
* const mutation = transaction.mutations[0]
|
|
240
|
-
* await api.deleteTodo(mutation.original.id)
|
|
241
|
-
* // Automatically refetches query after delete
|
|
242
|
-
* }
|
|
243
|
-
*
|
|
244
|
-
* @example
|
|
245
|
-
* // Delete handler with refetch control
|
|
246
|
-
* onDelete: async ({ transaction }) => {
|
|
247
|
-
* const mutation = transaction.mutations[0]
|
|
248
|
-
* await api.deleteTodo(mutation.original.id)
|
|
249
|
-
* return { refetch: false } // Skip automatic refetch
|
|
250
|
-
* }
|
|
251
|
-
*
|
|
252
|
-
* @example
|
|
253
|
-
* // Delete handler with multiple items
|
|
254
|
-
* onDelete: async ({ transaction }) => {
|
|
255
|
-
* const keysToDelete = transaction.mutations.map(m => m.key)
|
|
256
|
-
* await api.deleteTodos(keysToDelete)
|
|
257
|
-
* // Will refetch query to get updated data
|
|
258
|
-
* }
|
|
259
|
-
*
|
|
260
|
-
* @example
|
|
261
|
-
* // Delete handler with related collection refetch
|
|
262
|
-
* onDelete: async ({ transaction, collection }) => {
|
|
263
|
-
* const mutation = transaction.mutations[0]
|
|
264
|
-
* await api.deleteTodo(mutation.original.id)
|
|
265
|
-
*
|
|
266
|
-
* // Refetch related collections when this item is deleted
|
|
267
|
-
* await Promise.all([
|
|
268
|
-
* collection.utils.refetch(), // Refetch this collection
|
|
269
|
-
* usersCollection.utils.refetch(), // Refetch users
|
|
270
|
-
* projectsCollection.utils.refetch() // Refetch projects
|
|
271
|
-
* ])
|
|
272
|
-
*
|
|
273
|
-
* return { refetch: false } // Skip automatic refetch since we handled it manually
|
|
274
|
-
* }
|
|
275
|
-
*/
|
|
276
|
-
onDelete?: DeleteMutationFn<ResolveType<TExplicit, TSchema, TQueryFn>>
|
|
277
|
-
|
|
278
109
|
/**
|
|
279
110
|
* Metadata to pass to the query.
|
|
280
111
|
* Available in queryFn via context.meta
|
|
@@ -301,7 +132,7 @@ export interface QueryCollectionConfig<
|
|
|
301
132
|
/**
|
|
302
133
|
* Type for the refetch utility function
|
|
303
134
|
*/
|
|
304
|
-
export type RefetchFn = () => Promise<void>
|
|
135
|
+
export type RefetchFn = (opts?: { throwOnError?: boolean }) => Promise<void>
|
|
305
136
|
|
|
306
137
|
/**
|
|
307
138
|
* Utility methods available on Query Collections for direct writes and manual operations.
|
|
@@ -309,11 +140,13 @@ export type RefetchFn = () => Promise<void>
|
|
|
309
140
|
* @template TItem - The type of items stored in the collection
|
|
310
141
|
* @template TKey - The type of the item keys
|
|
311
142
|
* @template TInsertInput - The type accepted for insert operations
|
|
143
|
+
* @template TError - The type of errors that can occur during queries
|
|
312
144
|
*/
|
|
313
145
|
export interface QueryCollectionUtils<
|
|
314
146
|
TItem extends object = Record<string, unknown>,
|
|
315
147
|
TKey extends string | number = string | number,
|
|
316
148
|
TInsertInput extends object = TItem,
|
|
149
|
+
TError = unknown,
|
|
317
150
|
> extends UtilsRecord {
|
|
318
151
|
/** Manually trigger a refetch of the query */
|
|
319
152
|
refetch: RefetchFn
|
|
@@ -327,6 +160,21 @@ export interface QueryCollectionUtils<
|
|
|
327
160
|
writeUpsert: (data: Partial<TItem> | Array<Partial<TItem>>) => void
|
|
328
161
|
/** Execute multiple write operations as a single atomic batch to the synced data store */
|
|
329
162
|
writeBatch: (callback: () => void) => void
|
|
163
|
+
/** Get the last error encountered by the query (if any); reset on success */
|
|
164
|
+
lastError: () => TError | undefined
|
|
165
|
+
/** Check if the collection is in an error state */
|
|
166
|
+
isError: () => boolean
|
|
167
|
+
/**
|
|
168
|
+
* Get the number of consecutive sync failures.
|
|
169
|
+
* Incremented only when query fails completely (not per retry attempt); reset on success.
|
|
170
|
+
*/
|
|
171
|
+
errorCount: () => number
|
|
172
|
+
/**
|
|
173
|
+
* Clear the error state and trigger a refetch of the query
|
|
174
|
+
* @returns Promise that resolves when the refetch completes successfully
|
|
175
|
+
* @throws Error if the refetch fails
|
|
176
|
+
*/
|
|
177
|
+
clearError: () => Promise<void>
|
|
330
178
|
}
|
|
331
179
|
|
|
332
180
|
/**
|
|
@@ -334,18 +182,13 @@ export interface QueryCollectionUtils<
|
|
|
334
182
|
* This integrates TanStack Query with TanStack DB for automatic synchronization.
|
|
335
183
|
*
|
|
336
184
|
* Supports automatic type inference following the priority order:
|
|
337
|
-
* 1.
|
|
338
|
-
* 2.
|
|
339
|
-
* 3. QueryFn return type inference (third priority)
|
|
340
|
-
* 4. Fallback to Record<string, unknown>
|
|
185
|
+
* 1. Schema inference (highest priority)
|
|
186
|
+
* 2. QueryFn return type inference (second priority)
|
|
341
187
|
*
|
|
342
|
-
* @template
|
|
343
|
-
* @template TSchema - The schema type for validation and type inference (second priority)
|
|
344
|
-
* @template TQueryFn - The queryFn type for inferring return type (third priority)
|
|
188
|
+
* @template T - Type of the schema if a schema is provided otherwise it is the type of the values returned by the queryFn
|
|
345
189
|
* @template TError - The type of errors that can occur during queries
|
|
346
190
|
* @template TQueryKey - The type of the query key
|
|
347
191
|
* @template TKey - The type of the item keys
|
|
348
|
-
* @template TInsertInput - The type accepted for insert operations
|
|
349
192
|
* @param config - Configuration options for the Query collection
|
|
350
193
|
* @returns Collection options with utilities for direct writes and manual operations
|
|
351
194
|
*
|
|
@@ -364,7 +207,7 @@ export interface QueryCollectionUtils<
|
|
|
364
207
|
* )
|
|
365
208
|
*
|
|
366
209
|
* @example
|
|
367
|
-
* // Explicit type
|
|
210
|
+
* // Explicit type
|
|
368
211
|
* const todosCollection = createCollection<Todo>(
|
|
369
212
|
* queryCollectionOptions({
|
|
370
213
|
* queryKey: ['todos'],
|
|
@@ -375,7 +218,7 @@ export interface QueryCollectionUtils<
|
|
|
375
218
|
* )
|
|
376
219
|
*
|
|
377
220
|
* @example
|
|
378
|
-
* // Schema inference
|
|
221
|
+
* // Schema inference
|
|
379
222
|
* const todosCollection = createCollection(
|
|
380
223
|
* queryCollectionOptions({
|
|
381
224
|
* queryKey: ['todos'],
|
|
@@ -406,29 +249,62 @@ export interface QueryCollectionUtils<
|
|
|
406
249
|
* })
|
|
407
250
|
* )
|
|
408
251
|
*/
|
|
252
|
+
|
|
253
|
+
// Overload for when schema is provided
|
|
409
254
|
export function queryCollectionOptions<
|
|
410
|
-
|
|
411
|
-
TSchema extends StandardSchemaV1 = never,
|
|
412
|
-
TQueryFn extends (
|
|
413
|
-
context: QueryFunctionContext<any>
|
|
414
|
-
) => Promise<Array<any>> = (
|
|
415
|
-
context: QueryFunctionContext<any>
|
|
416
|
-
) => Promise<Array<any>>,
|
|
255
|
+
T extends StandardSchemaV1,
|
|
417
256
|
TError = unknown,
|
|
418
257
|
TQueryKey extends QueryKey = QueryKey,
|
|
419
258
|
TKey extends string | number = string | number,
|
|
420
|
-
TInsertInput extends object = ResolveType<TExplicit, TSchema, TQueryFn>,
|
|
421
259
|
>(
|
|
422
|
-
config: QueryCollectionConfig<
|
|
423
|
-
|
|
260
|
+
config: QueryCollectionConfig<
|
|
261
|
+
InferSchemaOutput<T>,
|
|
262
|
+
(
|
|
263
|
+
context: QueryFunctionContext<any>
|
|
264
|
+
) => Promise<Array<InferSchemaOutput<T>>>,
|
|
265
|
+
TError,
|
|
266
|
+
TQueryKey,
|
|
267
|
+
TKey,
|
|
268
|
+
T
|
|
269
|
+
> & {
|
|
270
|
+
schema: T
|
|
271
|
+
}
|
|
272
|
+
): CollectionConfig<InferSchemaOutput<T>, TKey, T> & {
|
|
273
|
+
schema: T
|
|
424
274
|
utils: QueryCollectionUtils<
|
|
425
|
-
|
|
275
|
+
InferSchemaOutput<T>,
|
|
426
276
|
TKey,
|
|
427
|
-
|
|
277
|
+
InferSchemaInput<T>,
|
|
278
|
+
TError
|
|
428
279
|
>
|
|
429
|
-
}
|
|
430
|
-
type TItem = ResolveType<TExplicit, TSchema, TQueryFn>
|
|
280
|
+
}
|
|
431
281
|
|
|
282
|
+
// Overload for when no schema is provided
|
|
283
|
+
export function queryCollectionOptions<
|
|
284
|
+
T extends object,
|
|
285
|
+
TError = unknown,
|
|
286
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
287
|
+
TKey extends string | number = string | number,
|
|
288
|
+
>(
|
|
289
|
+
config: QueryCollectionConfig<
|
|
290
|
+
T,
|
|
291
|
+
(context: QueryFunctionContext<any>) => Promise<Array<T>>,
|
|
292
|
+
TError,
|
|
293
|
+
TQueryKey,
|
|
294
|
+
TKey
|
|
295
|
+
> & {
|
|
296
|
+
schema?: never // prohibit schema
|
|
297
|
+
}
|
|
298
|
+
): CollectionConfig<T, TKey> & {
|
|
299
|
+
schema?: never // no schema in the result
|
|
300
|
+
utils: QueryCollectionUtils<T, TKey, T, TError>
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function queryCollectionOptions(
|
|
304
|
+
config: QueryCollectionConfig<Record<string, unknown>>
|
|
305
|
+
): CollectionConfig & {
|
|
306
|
+
utils: QueryCollectionUtils
|
|
307
|
+
} {
|
|
432
308
|
const {
|
|
433
309
|
queryKey,
|
|
434
310
|
queryFn,
|
|
@@ -466,15 +342,22 @@ export function queryCollectionOptions<
|
|
|
466
342
|
throw new GetKeyRequiredError()
|
|
467
343
|
}
|
|
468
344
|
|
|
469
|
-
|
|
345
|
+
/** The last error encountered by the query */
|
|
346
|
+
let lastError: any
|
|
347
|
+
/** The number of consecutive sync failures */
|
|
348
|
+
let errorCount = 0
|
|
349
|
+
/** The timestamp for when the query most recently returned the status as "error" */
|
|
350
|
+
let lastErrorUpdatedAt = 0
|
|
351
|
+
|
|
352
|
+
const internalSync: SyncConfig<any>[`sync`] = (params) => {
|
|
470
353
|
const { begin, write, commit, markReady, collection } = params
|
|
471
354
|
|
|
472
355
|
const observerOptions: QueryObserverOptions<
|
|
473
|
-
Array<
|
|
474
|
-
|
|
475
|
-
Array<
|
|
476
|
-
Array<
|
|
477
|
-
|
|
356
|
+
Array<any>,
|
|
357
|
+
any,
|
|
358
|
+
Array<any>,
|
|
359
|
+
Array<any>,
|
|
360
|
+
any
|
|
478
361
|
> = {
|
|
479
362
|
queryKey: queryKey,
|
|
480
363
|
queryFn: queryFn,
|
|
@@ -489,16 +372,20 @@ export function queryCollectionOptions<
|
|
|
489
372
|
}
|
|
490
373
|
|
|
491
374
|
const localObserver = new QueryObserver<
|
|
492
|
-
Array<
|
|
493
|
-
|
|
494
|
-
Array<
|
|
495
|
-
Array<
|
|
496
|
-
|
|
375
|
+
Array<any>,
|
|
376
|
+
any,
|
|
377
|
+
Array<any>,
|
|
378
|
+
Array<any>,
|
|
379
|
+
any
|
|
497
380
|
>(queryClient, observerOptions)
|
|
498
381
|
|
|
499
382
|
type UpdateHandler = Parameters<typeof localObserver.subscribe>[0]
|
|
500
383
|
const handleUpdate: UpdateHandler = (result) => {
|
|
501
384
|
if (result.isSuccess) {
|
|
385
|
+
// Clear error state
|
|
386
|
+
lastError = undefined
|
|
387
|
+
errorCount = 0
|
|
388
|
+
|
|
502
389
|
const newItemsArray = result.data
|
|
503
390
|
|
|
504
391
|
if (
|
|
@@ -513,7 +400,7 @@ export function queryCollectionOptions<
|
|
|
513
400
|
}
|
|
514
401
|
|
|
515
402
|
const currentSyncedItems = new Map(collection.syncedData)
|
|
516
|
-
const newItemsMap = new Map<string | number,
|
|
403
|
+
const newItemsMap = new Map<string | number, any>()
|
|
517
404
|
newItemsArray.forEach((item) => {
|
|
518
405
|
const key = getKey(item)
|
|
519
406
|
newItemsMap.set(key, item)
|
|
@@ -567,6 +454,12 @@ export function queryCollectionOptions<
|
|
|
567
454
|
// Mark collection as ready after first successful query result
|
|
568
455
|
markReady()
|
|
569
456
|
} else if (result.isError) {
|
|
457
|
+
if (result.errorUpdatedAt !== lastErrorUpdatedAt) {
|
|
458
|
+
lastError = result.error
|
|
459
|
+
errorCount++
|
|
460
|
+
lastErrorUpdatedAt = result.errorUpdatedAt
|
|
461
|
+
}
|
|
462
|
+
|
|
570
463
|
console.error(
|
|
571
464
|
`[QueryCollection] Error observing query ${String(queryKey)}:`,
|
|
572
465
|
result.error
|
|
@@ -594,10 +487,15 @@ export function queryCollectionOptions<
|
|
|
594
487
|
* Refetch the query data
|
|
595
488
|
* @returns Promise that resolves when the refetch is complete
|
|
596
489
|
*/
|
|
597
|
-
const refetch: RefetchFn =
|
|
598
|
-
return queryClient.refetchQueries(
|
|
599
|
-
|
|
600
|
-
|
|
490
|
+
const refetch: RefetchFn = (opts) => {
|
|
491
|
+
return queryClient.refetchQueries(
|
|
492
|
+
{
|
|
493
|
+
queryKey: queryKey,
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
throwOnError: opts?.throwOnError,
|
|
497
|
+
}
|
|
498
|
+
)
|
|
601
499
|
}
|
|
602
500
|
|
|
603
501
|
// Create write context for manual write operations
|
|
@@ -605,14 +503,14 @@ export function queryCollectionOptions<
|
|
|
605
503
|
collection: any
|
|
606
504
|
queryClient: QueryClient
|
|
607
505
|
queryKey: Array<unknown>
|
|
608
|
-
getKey: (item:
|
|
506
|
+
getKey: (item: any) => string | number
|
|
609
507
|
begin: () => void
|
|
610
|
-
write: (message: Omit<ChangeMessage<
|
|
508
|
+
write: (message: Omit<ChangeMessage<any>, `key`>) => void
|
|
611
509
|
commit: () => void
|
|
612
510
|
} | null = null
|
|
613
511
|
|
|
614
512
|
// Enhanced internalSync that captures write functions for manual use
|
|
615
|
-
const enhancedInternalSync: SyncConfig<
|
|
513
|
+
const enhancedInternalSync: SyncConfig<any>[`sync`] = (params) => {
|
|
616
514
|
const { begin, write, commit, collection } = params
|
|
617
515
|
|
|
618
516
|
// Store references for manual write operations
|
|
@@ -620,7 +518,7 @@ export function queryCollectionOptions<
|
|
|
620
518
|
collection,
|
|
621
519
|
queryClient,
|
|
622
520
|
queryKey: queryKey as unknown as Array<unknown>,
|
|
623
|
-
getKey: getKey as (item:
|
|
521
|
+
getKey: getKey as (item: any) => string | number,
|
|
624
522
|
begin,
|
|
625
523
|
write,
|
|
626
524
|
commit,
|
|
@@ -631,13 +529,13 @@ export function queryCollectionOptions<
|
|
|
631
529
|
}
|
|
632
530
|
|
|
633
531
|
// Create write utils using the manual-sync module
|
|
634
|
-
const writeUtils = createWriteUtils<
|
|
532
|
+
const writeUtils = createWriteUtils<any, string | number, any>(
|
|
635
533
|
() => writeContext
|
|
636
534
|
)
|
|
637
535
|
|
|
638
536
|
// Create wrapper handlers for direct persistence operations that handle refetching
|
|
639
537
|
const wrappedOnInsert = onInsert
|
|
640
|
-
? async (params: InsertMutationFnParams<
|
|
538
|
+
? async (params: InsertMutationFnParams<any>) => {
|
|
641
539
|
const handlerResult = (await onInsert(params)) ?? {}
|
|
642
540
|
const shouldRefetch =
|
|
643
541
|
(handlerResult as { refetch?: boolean }).refetch !== false
|
|
@@ -651,7 +549,7 @@ export function queryCollectionOptions<
|
|
|
651
549
|
: undefined
|
|
652
550
|
|
|
653
551
|
const wrappedOnUpdate = onUpdate
|
|
654
|
-
? async (params: UpdateMutationFnParams<
|
|
552
|
+
? async (params: UpdateMutationFnParams<any>) => {
|
|
655
553
|
const handlerResult = (await onUpdate(params)) ?? {}
|
|
656
554
|
const shouldRefetch =
|
|
657
555
|
(handlerResult as { refetch?: boolean }).refetch !== false
|
|
@@ -665,7 +563,7 @@ export function queryCollectionOptions<
|
|
|
665
563
|
: undefined
|
|
666
564
|
|
|
667
565
|
const wrappedOnDelete = onDelete
|
|
668
|
-
? async (params: DeleteMutationFnParams<
|
|
566
|
+
? async (params: DeleteMutationFnParams<any>) => {
|
|
669
567
|
const handlerResult = (await onDelete(params)) ?? {}
|
|
670
568
|
const shouldRefetch =
|
|
671
569
|
(handlerResult as { refetch?: boolean }).refetch !== false
|
|
@@ -688,6 +586,15 @@ export function queryCollectionOptions<
|
|
|
688
586
|
utils: {
|
|
689
587
|
refetch,
|
|
690
588
|
...writeUtils,
|
|
589
|
+
lastError: () => lastError,
|
|
590
|
+
isError: () => !!lastError,
|
|
591
|
+
errorCount: () => errorCount,
|
|
592
|
+
clearError: () => {
|
|
593
|
+
lastError = undefined
|
|
594
|
+
errorCount = 0
|
|
595
|
+
lastErrorUpdatedAt = 0
|
|
596
|
+
return refetch({ throwOnError: true })
|
|
597
|
+
},
|
|
691
598
|
},
|
|
692
599
|
}
|
|
693
600
|
}
|