@tanstack/db 0.4.15 → 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 +10 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +2 -0
- package/dist/cjs/indexes/auto-index.cjs +17 -8
- package/dist/cjs/indexes/auto-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 +52 -0
- package/dist/cjs/paced-mutations.cjs.map +1 -0
- package/dist/cjs/paced-mutations.d.cts +81 -0
- package/dist/cjs/query/optimizer.cjs +17 -2
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/cjs/strategies/debounceStrategy.cjs +21 -0
- package/dist/cjs/strategies/debounceStrategy.cjs.map +1 -0
- package/dist/cjs/strategies/debounceStrategy.d.cts +25 -0
- package/dist/cjs/strategies/index.d.cts +4 -0
- package/dist/cjs/strategies/queueStrategy.cjs +33 -0
- package/dist/cjs/strategies/queueStrategy.cjs.map +1 -0
- package/dist/cjs/strategies/queueStrategy.d.cts +43 -0
- package/dist/cjs/strategies/throttleStrategy.cjs +21 -0
- package/dist/cjs/strategies/throttleStrategy.cjs.map +1 -0
- package/dist/cjs/strategies/throttleStrategy.d.cts +45 -0
- package/dist/cjs/strategies/types.d.cts +103 -0
- 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.d.ts +2 -0
- package/dist/esm/index.js +11 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.js +17 -8
- package/dist/esm/indexes/auto-index.js.map +1 -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 +81 -0
- package/dist/esm/paced-mutations.js +52 -0
- package/dist/esm/paced-mutations.js.map +1 -0
- package/dist/esm/query/optimizer.js +17 -2
- package/dist/esm/query/optimizer.js.map +1 -1
- package/dist/esm/strategies/debounceStrategy.d.ts +25 -0
- package/dist/esm/strategies/debounceStrategy.js +21 -0
- package/dist/esm/strategies/debounceStrategy.js.map +1 -0
- package/dist/esm/strategies/index.d.ts +4 -0
- package/dist/esm/strategies/queueStrategy.d.ts +43 -0
- package/dist/esm/strategies/queueStrategy.js +33 -0
- package/dist/esm/strategies/queueStrategy.js.map +1 -0
- package/dist/esm/strategies/throttleStrategy.d.ts +45 -0
- package/dist/esm/strategies/throttleStrategy.js +21 -0
- package/dist/esm/strategies/throttleStrategy.js.map +1 -0
- package/dist/esm/strategies/types.d.ts +103 -0
- 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 +3 -1
- package/src/duplicate-instance-check.ts +32 -0
- package/src/errors.ts +35 -0
- package/src/index.ts +2 -0
- package/src/indexes/auto-index.ts +23 -10
- package/src/optimistic-action.ts +7 -2
- package/src/paced-mutations.ts +169 -0
- package/src/query/optimizer.ts +31 -4
- package/src/strategies/debounceStrategy.ts +48 -0
- package/src/strategies/index.ts +17 -0
- package/src/strategies/queueStrategy.ts +75 -0
- package/src/strategies/throttleStrategy.ts +68 -0
- package/src/strategies/types.ts +130 -0
- package/src/transactions.ts +5 -1
- package/src/utils/type-guards.ts +12 -0
package/src/query/optimizer.ts
CHANGED
|
@@ -330,9 +330,22 @@ function applySingleLevelOptimization(query: QueryIR): QueryIR {
|
|
|
330
330
|
return query
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
-
//
|
|
334
|
-
//
|
|
333
|
+
// For queries without joins, combine multiple WHERE clauses into a single clause
|
|
334
|
+
// to avoid creating multiple filter operators in the pipeline
|
|
335
335
|
if (!query.join || query.join.length === 0) {
|
|
336
|
+
// Only optimize if there are multiple WHERE clauses to combine
|
|
337
|
+
if (query.where.length > 1) {
|
|
338
|
+
// Combine multiple WHERE clauses into a single AND expression
|
|
339
|
+
const splitWhereClauses = splitAndClauses(query.where)
|
|
340
|
+
const combinedWhere = combineWithAnd(splitWhereClauses)
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
...query,
|
|
344
|
+
where: [combinedWhere],
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// For single WHERE clauses, no optimization needed
|
|
336
349
|
return query
|
|
337
350
|
}
|
|
338
351
|
|
|
@@ -674,6 +687,20 @@ function applyOptimizations(
|
|
|
674
687
|
// If optimized and no outer JOINs - don't keep (original behavior)
|
|
675
688
|
}
|
|
676
689
|
|
|
690
|
+
// Combine multiple remaining WHERE clauses into a single clause to avoid
|
|
691
|
+
// multiple filter operations in the pipeline (performance optimization)
|
|
692
|
+
// First flatten any nested AND expressions to avoid and(and(...), ...)
|
|
693
|
+
const finalWhere: Array<Where> =
|
|
694
|
+
remainingWhereClauses.length > 1
|
|
695
|
+
? [
|
|
696
|
+
combineWithAnd(
|
|
697
|
+
remainingWhereClauses.flatMap((clause) =>
|
|
698
|
+
splitAndClausesRecursive(getWhereExpression(clause))
|
|
699
|
+
)
|
|
700
|
+
),
|
|
701
|
+
]
|
|
702
|
+
: remainingWhereClauses
|
|
703
|
+
|
|
677
704
|
// Create a completely new query object to ensure immutability
|
|
678
705
|
const optimizedQuery: QueryIR = {
|
|
679
706
|
// Copy all non-optimized fields as-is
|
|
@@ -692,8 +719,8 @@ function applyOptimizations(
|
|
|
692
719
|
from: optimizedFrom,
|
|
693
720
|
join: optimizedJoins,
|
|
694
721
|
|
|
695
|
-
//
|
|
696
|
-
where:
|
|
722
|
+
// Include combined WHERE clauses
|
|
723
|
+
where: finalWhere.length > 0 ? finalWhere : [],
|
|
697
724
|
}
|
|
698
725
|
|
|
699
726
|
return optimizedQuery
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Debouncer } from "@tanstack/pacer/debouncer"
|
|
2
|
+
import type { DebounceStrategy, DebounceStrategyOptions } from "./types"
|
|
3
|
+
import type { Transaction } from "../transactions"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a debounce strategy that delays transaction execution until after
|
|
7
|
+
* a period of inactivity.
|
|
8
|
+
*
|
|
9
|
+
* Ideal for scenarios like search inputs or auto-save fields where you want
|
|
10
|
+
* to wait for the user to stop typing before persisting changes.
|
|
11
|
+
*
|
|
12
|
+
* @param options - Configuration for the debounce behavior
|
|
13
|
+
* @returns A debounce strategy instance
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const mutate = usePacedMutations({
|
|
18
|
+
* onMutate: (value) => {
|
|
19
|
+
* collection.update(id, draft => { draft.value = value })
|
|
20
|
+
* },
|
|
21
|
+
* mutationFn: async ({ transaction }) => {
|
|
22
|
+
* await api.save(transaction.mutations)
|
|
23
|
+
* },
|
|
24
|
+
* strategy: debounceStrategy({ wait: 500 })
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function debounceStrategy(
|
|
29
|
+
options: DebounceStrategyOptions
|
|
30
|
+
): DebounceStrategy {
|
|
31
|
+
const debouncer = new Debouncer(
|
|
32
|
+
(callback: () => Transaction) => callback(),
|
|
33
|
+
options
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
_type: `debounce`,
|
|
38
|
+
options,
|
|
39
|
+
execute: <T extends object = Record<string, unknown>>(
|
|
40
|
+
fn: () => Transaction<T>
|
|
41
|
+
) => {
|
|
42
|
+
debouncer.maybeExecute(fn as () => Transaction)
|
|
43
|
+
},
|
|
44
|
+
cleanup: () => {
|
|
45
|
+
debouncer.cancel()
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Export all strategy factories
|
|
2
|
+
export { debounceStrategy } from "./debounceStrategy"
|
|
3
|
+
export { queueStrategy } from "./queueStrategy"
|
|
4
|
+
export { throttleStrategy } from "./throttleStrategy"
|
|
5
|
+
|
|
6
|
+
// Export strategy types
|
|
7
|
+
export type {
|
|
8
|
+
Strategy,
|
|
9
|
+
BaseStrategy,
|
|
10
|
+
DebounceStrategy,
|
|
11
|
+
DebounceStrategyOptions,
|
|
12
|
+
QueueStrategy,
|
|
13
|
+
QueueStrategyOptions,
|
|
14
|
+
ThrottleStrategy,
|
|
15
|
+
ThrottleStrategyOptions,
|
|
16
|
+
StrategyOptions,
|
|
17
|
+
} from "./types"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { AsyncQueuer } from "@tanstack/pacer/async-queuer"
|
|
2
|
+
import type { QueueStrategy, QueueStrategyOptions } from "./types"
|
|
3
|
+
import type { Transaction } from "../transactions"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a queue strategy that processes all mutations in order with proper serialization.
|
|
7
|
+
*
|
|
8
|
+
* Unlike other strategies that may drop executions, queue ensures every
|
|
9
|
+
* mutation is processed sequentially. Each transaction commit completes before
|
|
10
|
+
* the next one starts. Useful when data consistency is critical and
|
|
11
|
+
* every operation must complete in order.
|
|
12
|
+
*
|
|
13
|
+
* @param options - Configuration for queue behavior (FIFO/LIFO, timing, size limits)
|
|
14
|
+
* @returns A queue strategy instance
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* // FIFO queue - process in order received
|
|
19
|
+
* const mutate = usePacedMutations({
|
|
20
|
+
* mutationFn: async ({ transaction }) => {
|
|
21
|
+
* await api.save(transaction.mutations)
|
|
22
|
+
* },
|
|
23
|
+
* strategy: queueStrategy({
|
|
24
|
+
* wait: 200,
|
|
25
|
+
* addItemsTo: 'back',
|
|
26
|
+
* getItemsFrom: 'front'
|
|
27
|
+
* })
|
|
28
|
+
* })
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* // LIFO queue - process most recent first
|
|
34
|
+
* const mutate = usePacedMutations({
|
|
35
|
+
* mutationFn: async ({ transaction }) => {
|
|
36
|
+
* await api.save(transaction.mutations)
|
|
37
|
+
* },
|
|
38
|
+
* strategy: queueStrategy({
|
|
39
|
+
* wait: 200,
|
|
40
|
+
* addItemsTo: 'back',
|
|
41
|
+
* getItemsFrom: 'back'
|
|
42
|
+
* })
|
|
43
|
+
* })
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function queueStrategy(options?: QueueStrategyOptions): QueueStrategy {
|
|
47
|
+
const queuer = new AsyncQueuer<void>({
|
|
48
|
+
concurrency: 1, // Process one at a time to ensure serialization
|
|
49
|
+
wait: options?.wait,
|
|
50
|
+
maxSize: options?.maxSize,
|
|
51
|
+
addItemsTo: options?.addItemsTo ?? `back`, // Default FIFO: add to back
|
|
52
|
+
getItemsFrom: options?.getItemsFrom ?? `front`, // Default FIFO: get from front
|
|
53
|
+
started: true, // Start processing immediately
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
_type: `queue`,
|
|
58
|
+
options,
|
|
59
|
+
execute: <T extends object = Record<string, unknown>>(
|
|
60
|
+
fn: () => Transaction<T>
|
|
61
|
+
) => {
|
|
62
|
+
// Wrap the callback in an async function that waits for persistence
|
|
63
|
+
queuer.addItem(async () => {
|
|
64
|
+
const transaction = fn()
|
|
65
|
+
// Wait for the transaction to be persisted before processing next item
|
|
66
|
+
// Note: fn() already calls commit(), we just wait for it to complete
|
|
67
|
+
await transaction.isPersisted.promise
|
|
68
|
+
})
|
|
69
|
+
},
|
|
70
|
+
cleanup: () => {
|
|
71
|
+
queuer.stop()
|
|
72
|
+
queuer.clear()
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Throttler } from "@tanstack/pacer/throttler"
|
|
2
|
+
import type { ThrottleStrategy, ThrottleStrategyOptions } from "./types"
|
|
3
|
+
import type { Transaction } from "../transactions"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a throttle strategy that ensures transactions are evenly spaced
|
|
7
|
+
* over time.
|
|
8
|
+
*
|
|
9
|
+
* Provides smooth, controlled execution patterns ideal for UI updates like
|
|
10
|
+
* sliders, progress bars, or scroll handlers where you want consistent
|
|
11
|
+
* execution timing.
|
|
12
|
+
*
|
|
13
|
+
* @param options - Configuration for throttle behavior
|
|
14
|
+
* @returns A throttle strategy instance
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* // Throttle slider updates to every 200ms
|
|
19
|
+
* const mutate = usePacedMutations({
|
|
20
|
+
* onMutate: (volume) => {
|
|
21
|
+
* settingsCollection.update('volume', draft => { draft.value = volume })
|
|
22
|
+
* },
|
|
23
|
+
* mutationFn: async ({ transaction }) => {
|
|
24
|
+
* await api.updateVolume(transaction.mutations)
|
|
25
|
+
* },
|
|
26
|
+
* strategy: throttleStrategy({ wait: 200 })
|
|
27
|
+
* })
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* // Throttle with leading and trailing execution
|
|
33
|
+
* const mutate = usePacedMutations({
|
|
34
|
+
* onMutate: (data) => {
|
|
35
|
+
* collection.update(id, draft => { Object.assign(draft, data) })
|
|
36
|
+
* },
|
|
37
|
+
* mutationFn: async ({ transaction }) => {
|
|
38
|
+
* await api.save(transaction.mutations)
|
|
39
|
+
* },
|
|
40
|
+
* strategy: throttleStrategy({
|
|
41
|
+
* wait: 500,
|
|
42
|
+
* leading: true,
|
|
43
|
+
* trailing: true
|
|
44
|
+
* })
|
|
45
|
+
* })
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export function throttleStrategy(
|
|
49
|
+
options: ThrottleStrategyOptions
|
|
50
|
+
): ThrottleStrategy {
|
|
51
|
+
const throttler = new Throttler(
|
|
52
|
+
(callback: () => Transaction) => callback(),
|
|
53
|
+
options
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
_type: `throttle`,
|
|
58
|
+
options,
|
|
59
|
+
execute: <T extends object = Record<string, unknown>>(
|
|
60
|
+
fn: () => Transaction<T>
|
|
61
|
+
) => {
|
|
62
|
+
throttler.maybeExecute(fn as () => Transaction)
|
|
63
|
+
},
|
|
64
|
+
cleanup: () => {
|
|
65
|
+
throttler.cancel()
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { Transaction } from "../transactions"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base strategy interface that all strategy implementations must conform to
|
|
5
|
+
*/
|
|
6
|
+
export interface BaseStrategy<TName extends string = string> {
|
|
7
|
+
/** Type discriminator for strategy identification */
|
|
8
|
+
_type: TName
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Execute a function according to the strategy's timing rules
|
|
12
|
+
* @param fn - The function to execute
|
|
13
|
+
* @returns The result of the function execution (if applicable)
|
|
14
|
+
*/
|
|
15
|
+
execute: <T extends object = Record<string, unknown>>(
|
|
16
|
+
fn: () => Transaction<T>
|
|
17
|
+
) => void | Promise<void>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Clean up any resources held by the strategy
|
|
21
|
+
* Should be called when the strategy is no longer needed
|
|
22
|
+
*/
|
|
23
|
+
cleanup: () => void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Options for debounce strategy
|
|
28
|
+
* Delays execution until after a period of inactivity
|
|
29
|
+
*/
|
|
30
|
+
export interface DebounceStrategyOptions {
|
|
31
|
+
/** Wait time in milliseconds before execution */
|
|
32
|
+
wait: number
|
|
33
|
+
/** Execute immediately on the first call */
|
|
34
|
+
leading?: boolean
|
|
35
|
+
/** Execute after the wait period on the last call */
|
|
36
|
+
trailing?: boolean
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Debounce strategy that delays execution until activity stops
|
|
41
|
+
*/
|
|
42
|
+
export interface DebounceStrategy extends BaseStrategy<`debounce`> {
|
|
43
|
+
options: DebounceStrategyOptions
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Options for queue strategy
|
|
48
|
+
* Processes all executions in order (FIFO/LIFO)
|
|
49
|
+
*/
|
|
50
|
+
export interface QueueStrategyOptions {
|
|
51
|
+
/** Wait time between processing queue items (milliseconds) */
|
|
52
|
+
wait?: number
|
|
53
|
+
/** Maximum queue size (items are dropped if exceeded) */
|
|
54
|
+
maxSize?: number
|
|
55
|
+
/** Where to add new items in the queue */
|
|
56
|
+
addItemsTo?: `front` | `back`
|
|
57
|
+
/** Where to get items from when processing */
|
|
58
|
+
getItemsFrom?: `front` | `back`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Queue strategy that processes all executions in order
|
|
63
|
+
* FIFO: { addItemsTo: 'back', getItemsFrom: 'front' }
|
|
64
|
+
* LIFO: { addItemsTo: 'back', getItemsFrom: 'back' }
|
|
65
|
+
*/
|
|
66
|
+
export interface QueueStrategy extends BaseStrategy<`queue`> {
|
|
67
|
+
options?: QueueStrategyOptions
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Options for throttle strategy
|
|
72
|
+
* Ensures executions are evenly spaced over time
|
|
73
|
+
*/
|
|
74
|
+
export interface ThrottleStrategyOptions {
|
|
75
|
+
/** Minimum wait time between executions (milliseconds) */
|
|
76
|
+
wait: number
|
|
77
|
+
/** Execute immediately on the first call */
|
|
78
|
+
leading?: boolean
|
|
79
|
+
/** Execute on the last call after wait period */
|
|
80
|
+
trailing?: boolean
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Throttle strategy that spaces executions evenly over time
|
|
85
|
+
*/
|
|
86
|
+
export interface ThrottleStrategy extends BaseStrategy<`throttle`> {
|
|
87
|
+
options: ThrottleStrategyOptions
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Options for batch strategy
|
|
92
|
+
* Groups multiple executions together
|
|
93
|
+
*/
|
|
94
|
+
export interface BatchStrategyOptions {
|
|
95
|
+
/** Maximum items per batch */
|
|
96
|
+
maxSize?: number
|
|
97
|
+
/** Maximum wait time before processing batch (milliseconds) */
|
|
98
|
+
wait?: number
|
|
99
|
+
/** Custom logic to determine when to execute batch */
|
|
100
|
+
getShouldExecute?: (items: Array<any>) => boolean
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Batch strategy that groups multiple executions together
|
|
105
|
+
*/
|
|
106
|
+
export interface BatchStrategy extends BaseStrategy<`batch`> {
|
|
107
|
+
options?: BatchStrategyOptions
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Union type of all available strategies
|
|
112
|
+
*/
|
|
113
|
+
export type Strategy =
|
|
114
|
+
| DebounceStrategy
|
|
115
|
+
| QueueStrategy
|
|
116
|
+
| ThrottleStrategy
|
|
117
|
+
| BatchStrategy
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Extract the options type from a strategy
|
|
121
|
+
*/
|
|
122
|
+
export type StrategyOptions<T extends Strategy> = T extends DebounceStrategy
|
|
123
|
+
? DebounceStrategyOptions
|
|
124
|
+
: T extends QueueStrategy
|
|
125
|
+
? QueueStrategyOptions
|
|
126
|
+
: T extends ThrottleStrategy
|
|
127
|
+
? ThrottleStrategyOptions
|
|
128
|
+
: T extends BatchStrategy
|
|
129
|
+
? BatchStrategyOptions
|
|
130
|
+
: never
|
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
|
+
}
|