@tanstack/db 0.4.16 → 0.4.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/duplicate-instance-check.d.cts +1 -0
- package/dist/cjs/errors.cjs +38 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +6 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/optimistic-action.cjs +6 -1
- package/dist/cjs/optimistic-action.cjs.map +1 -1
- package/dist/cjs/paced-mutations.cjs.map +1 -1
- package/dist/cjs/paced-mutations.d.cts +2 -2
- package/dist/cjs/strategies/debounceStrategy.cjs.map +1 -1
- package/dist/cjs/strategies/debounceStrategy.d.cts +4 -1
- package/dist/cjs/strategies/throttleStrategy.cjs.map +1 -1
- package/dist/cjs/strategies/throttleStrategy.d.cts +8 -2
- package/dist/cjs/transactions.cjs +3 -1
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/transactions.d.cts +3 -1
- package/dist/cjs/utils/type-guards.cjs +7 -0
- package/dist/cjs/utils/type-guards.cjs.map +1 -0
- package/dist/cjs/utils/type-guards.d.cts +6 -0
- package/dist/esm/duplicate-instance-check.d.ts +1 -0
- package/dist/esm/errors.d.ts +6 -0
- package/dist/esm/errors.js +38 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/optimistic-action.js +6 -1
- package/dist/esm/optimistic-action.js.map +1 -1
- package/dist/esm/paced-mutations.d.ts +2 -2
- package/dist/esm/paced-mutations.js.map +1 -1
- package/dist/esm/strategies/debounceStrategy.d.ts +4 -1
- package/dist/esm/strategies/debounceStrategy.js.map +1 -1
- package/dist/esm/strategies/throttleStrategy.d.ts +8 -2
- package/dist/esm/strategies/throttleStrategy.js.map +1 -1
- package/dist/esm/transactions.d.ts +3 -1
- package/dist/esm/transactions.js +3 -1
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/utils/type-guards.d.ts +6 -0
- package/dist/esm/utils/type-guards.js +7 -0
- package/dist/esm/utils/type-guards.js.map +1 -0
- package/package.json +1 -1
- package/src/duplicate-instance-check.ts +32 -0
- package/src/errors.ts +35 -0
- package/src/optimistic-action.ts +7 -2
- package/src/paced-mutations.ts +2 -2
- package/src/strategies/debounceStrategy.ts +4 -1
- package/src/strategies/throttleStrategy.ts +8 -2
- package/src/transactions.ts +5 -1
- package/src/utils/type-guards.ts +12 -0
package/src/errors.ts
CHANGED
|
@@ -41,6 +41,32 @@ export class SchemaValidationError extends TanStackDBError {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Module Instance Errors
|
|
45
|
+
export class DuplicateDbInstanceError extends TanStackDBError {
|
|
46
|
+
constructor() {
|
|
47
|
+
super(
|
|
48
|
+
`Multiple instances of @tanstack/db detected!\n\n` +
|
|
49
|
+
`This causes transaction context to be lost because each instance maintains ` +
|
|
50
|
+
`its own transaction stack.\n\n` +
|
|
51
|
+
`Common causes:\n` +
|
|
52
|
+
`1. Different versions of @tanstack/db installed\n` +
|
|
53
|
+
`2. Incompatible peer dependency versions in packages\n` +
|
|
54
|
+
`3. Module resolution issues in bundler configuration\n\n` +
|
|
55
|
+
`To fix:\n` +
|
|
56
|
+
`1. Check installed versions: npm list @tanstack/db (or pnpm/yarn list)\n` +
|
|
57
|
+
`2. Force a single version using package manager overrides:\n` +
|
|
58
|
+
` - npm: "overrides" in package.json\n` +
|
|
59
|
+
` - pnpm: "pnpm.overrides" in package.json\n` +
|
|
60
|
+
` - yarn: "resolutions" in package.json\n` +
|
|
61
|
+
`3. Clear node_modules and lockfile, then reinstall\n\n` +
|
|
62
|
+
`To temporarily disable this check (not recommended):\n` +
|
|
63
|
+
`Set environment variable: TANSTACK_DB_DISABLE_DUP_CHECK=1\n\n` +
|
|
64
|
+
`See: https://tanstack.com/db/latest/docs/troubleshooting#duplicate-instances`
|
|
65
|
+
)
|
|
66
|
+
this.name = `DuplicateDbInstanceError`
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
44
70
|
// Collection Configuration Errors
|
|
45
71
|
export class CollectionConfigurationError extends TanStackDBError {
|
|
46
72
|
constructor(message: string) {
|
|
@@ -229,6 +255,15 @@ export class MissingMutationFunctionError extends TransactionError {
|
|
|
229
255
|
}
|
|
230
256
|
}
|
|
231
257
|
|
|
258
|
+
export class OnMutateMustBeSynchronousError extends TransactionError {
|
|
259
|
+
constructor() {
|
|
260
|
+
super(
|
|
261
|
+
`onMutate must be synchronous and cannot return a promise. Remove async/await or returned promises from onMutate.`
|
|
262
|
+
)
|
|
263
|
+
this.name = `OnMutateMustBeSynchronousError`
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
232
267
|
export class TransactionNotPendingMutateError extends TransactionError {
|
|
233
268
|
constructor() {
|
|
234
269
|
super(
|
package/src/optimistic-action.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { createTransaction } from "./transactions"
|
|
2
|
+
import { OnMutateMustBeSynchronousError } from "./errors"
|
|
3
|
+
import { isPromiseLike } from "./utils/type-guards"
|
|
2
4
|
import type { CreateOptimisticActionsOptions, Transaction } from "./types"
|
|
3
5
|
|
|
4
6
|
/**
|
|
@@ -67,8 +69,11 @@ export function createOptimisticAction<TVariables = unknown>(
|
|
|
67
69
|
// Execute the transaction. The mutationFn is called once mutate()
|
|
68
70
|
// is finished.
|
|
69
71
|
transaction.mutate(() => {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
const maybePromise = onMutate(variables) as unknown
|
|
73
|
+
|
|
74
|
+
if (isPromiseLike(maybePromise)) {
|
|
75
|
+
throw new OnMutateMustBeSynchronousError()
|
|
76
|
+
}
|
|
72
77
|
})
|
|
73
78
|
|
|
74
79
|
return transaction
|
package/src/paced-mutations.ts
CHANGED
|
@@ -53,7 +53,7 @@ export interface PacedMutationsConfig<
|
|
|
53
53
|
* // Apply optimistic update immediately
|
|
54
54
|
* collection.update(id, draft => { draft.text = text })
|
|
55
55
|
* },
|
|
56
|
-
* mutationFn: async (
|
|
56
|
+
* mutationFn: async ({ transaction }) => {
|
|
57
57
|
* await api.save(transaction.mutations)
|
|
58
58
|
* },
|
|
59
59
|
* strategy: debounceStrategy({ wait: 500 })
|
|
@@ -73,7 +73,7 @@ export interface PacedMutationsConfig<
|
|
|
73
73
|
* onMutate: ({ text }) => {
|
|
74
74
|
* collection.insert({ id: uuid(), text, completed: false })
|
|
75
75
|
* },
|
|
76
|
-
* mutationFn: async ({
|
|
76
|
+
* mutationFn: async ({ transaction }) => {
|
|
77
77
|
* await api.save(transaction.mutations)
|
|
78
78
|
* },
|
|
79
79
|
* strategy: queueStrategy({
|
|
@@ -14,7 +14,10 @@ import type { Transaction } from "../transactions"
|
|
|
14
14
|
*
|
|
15
15
|
* @example
|
|
16
16
|
* ```ts
|
|
17
|
-
* const mutate =
|
|
17
|
+
* const mutate = usePacedMutations({
|
|
18
|
+
* onMutate: (value) => {
|
|
19
|
+
* collection.update(id, draft => { draft.value = value })
|
|
20
|
+
* },
|
|
18
21
|
* mutationFn: async ({ transaction }) => {
|
|
19
22
|
* await api.save(transaction.mutations)
|
|
20
23
|
* },
|
|
@@ -16,7 +16,10 @@ import type { Transaction } from "../transactions"
|
|
|
16
16
|
* @example
|
|
17
17
|
* ```ts
|
|
18
18
|
* // Throttle slider updates to every 200ms
|
|
19
|
-
* const mutate =
|
|
19
|
+
* const mutate = usePacedMutations({
|
|
20
|
+
* onMutate: (volume) => {
|
|
21
|
+
* settingsCollection.update('volume', draft => { draft.value = volume })
|
|
22
|
+
* },
|
|
20
23
|
* mutationFn: async ({ transaction }) => {
|
|
21
24
|
* await api.updateVolume(transaction.mutations)
|
|
22
25
|
* },
|
|
@@ -27,7 +30,10 @@ import type { Transaction } from "../transactions"
|
|
|
27
30
|
* @example
|
|
28
31
|
* ```ts
|
|
29
32
|
* // Throttle with leading and trailing execution
|
|
30
|
-
* const mutate =
|
|
33
|
+
* const mutate = usePacedMutations({
|
|
34
|
+
* onMutate: (data) => {
|
|
35
|
+
* collection.update(id, draft => { Object.assign(draft, data) })
|
|
36
|
+
* },
|
|
31
37
|
* mutationFn: async ({ transaction }) => {
|
|
32
38
|
* await api.save(transaction.mutations)
|
|
33
39
|
* },
|
package/src/transactions.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createDeferred } from "./deferred"
|
|
2
|
+
import "./duplicate-instance-check"
|
|
2
3
|
import {
|
|
3
4
|
MissingMutationFunctionError,
|
|
4
5
|
TransactionAlreadyCompletedRollbackError,
|
|
@@ -244,7 +245,9 @@ class Transaction<T extends object = Record<string, unknown>> {
|
|
|
244
245
|
|
|
245
246
|
/**
|
|
246
247
|
* Execute collection operations within this transaction
|
|
247
|
-
* @param callback - Function containing collection operations to group together
|
|
248
|
+
* @param callback - Function containing collection operations to group together. If the
|
|
249
|
+
* callback returns a Promise, the transaction context will remain active until the promise
|
|
250
|
+
* settles, allowing optimistic writes after `await` boundaries.
|
|
248
251
|
* @returns This transaction for chaining
|
|
249
252
|
* @example
|
|
250
253
|
* // Group multiple operations
|
|
@@ -287,6 +290,7 @@ class Transaction<T extends object = Record<string, unknown>> {
|
|
|
287
290
|
}
|
|
288
291
|
|
|
289
292
|
registerTransaction(this)
|
|
293
|
+
|
|
290
294
|
try {
|
|
291
295
|
callback()
|
|
292
296
|
} finally {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard to check if a value is promise-like (has a `.then` method)
|
|
3
|
+
* @param value - The value to check
|
|
4
|
+
* @returns True if the value is promise-like, false otherwise
|
|
5
|
+
*/
|
|
6
|
+
export function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
7
|
+
return (
|
|
8
|
+
!!value &&
|
|
9
|
+
(typeof value === `object` || typeof value === `function`) &&
|
|
10
|
+
typeof (value as { then?: unknown }).then === `function`
|
|
11
|
+
)
|
|
12
|
+
}
|