@tanstack/db 0.0.22 → 0.0.24
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/collection.cjs +14 -6
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +10 -9
- package/dist/cjs/local-storage.cjs +1 -1
- package/dist/cjs/local-storage.cjs.map +1 -1
- package/dist/cjs/proxy.cjs +21 -0
- package/dist/cjs/proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/index.cjs +72 -0
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +64 -0
- package/dist/cjs/query/compiler/index.cjs +44 -8
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +4 -7
- package/dist/cjs/query/compiler/joins.cjs +14 -6
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.d.cts +4 -8
- package/dist/cjs/query/compiler/types.d.cts +10 -0
- package/dist/cjs/query/optimizer.cjs +283 -0
- package/dist/cjs/query/optimizer.cjs.map +1 -0
- package/dist/cjs/query/optimizer.d.cts +42 -0
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/transactions.d.cts +5 -5
- package/dist/cjs/types.d.cts +35 -10
- package/dist/cjs/utils.cjs +42 -0
- package/dist/cjs/utils.cjs.map +1 -0
- package/dist/cjs/utils.d.cts +18 -0
- package/dist/esm/collection.d.ts +10 -9
- package/dist/esm/collection.js +14 -6
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/local-storage.js +1 -1
- package/dist/esm/local-storage.js.map +1 -1
- package/dist/esm/proxy.js +21 -0
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query/builder/index.d.ts +64 -0
- package/dist/esm/query/builder/index.js +72 -0
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +4 -7
- package/dist/esm/query/compiler/index.js +44 -8
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.d.ts +4 -8
- package/dist/esm/query/compiler/joins.js +14 -6
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/types.d.ts +10 -0
- package/dist/esm/query/optimizer.d.ts +42 -0
- package/dist/esm/query/optimizer.js +283 -0
- package/dist/esm/query/optimizer.js.map +1 -0
- package/dist/esm/transactions.d.ts +5 -5
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +35 -10
- package/dist/esm/utils.d.ts +18 -0
- package/dist/esm/utils.js +42 -0
- package/dist/esm/utils.js.map +1 -0
- package/package.json +1 -1
- package/src/collection.ts +62 -21
- package/src/local-storage.ts +2 -2
- package/src/proxy.ts +24 -0
- package/src/query/builder/index.ts +104 -0
- package/src/query/compiler/index.ts +85 -18
- package/src/query/compiler/joins.ts +21 -13
- package/src/query/compiler/types.ts +12 -0
- package/src/query/optimizer.ts +738 -0
- package/src/transactions.ts +8 -12
- package/src/types.ts +69 -14
- package/src/utils.ts +86 -0
package/src/transactions.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { createDeferred } from "./deferred"
|
|
|
2
2
|
import type { Deferred } from "./deferred"
|
|
3
3
|
import type {
|
|
4
4
|
MutationFn,
|
|
5
|
-
OperationType,
|
|
6
5
|
PendingMutation,
|
|
7
6
|
TransactionConfig,
|
|
8
7
|
TransactionState,
|
|
@@ -66,10 +65,10 @@ let sequenceNumber = 0
|
|
|
66
65
|
* // Commit later
|
|
67
66
|
* await tx.commit()
|
|
68
67
|
*/
|
|
69
|
-
export function createTransaction<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const newTransaction = new Transaction<
|
|
68
|
+
export function createTransaction<T extends object = Record<string, unknown>>(
|
|
69
|
+
config: TransactionConfig<T>
|
|
70
|
+
): Transaction<T> {
|
|
71
|
+
const newTransaction = new Transaction<T>(config)
|
|
73
72
|
transactions.push(newTransaction)
|
|
74
73
|
return newTransaction
|
|
75
74
|
}
|
|
@@ -108,15 +107,12 @@ function removeFromPendingList(tx: Transaction<any>) {
|
|
|
108
107
|
}
|
|
109
108
|
}
|
|
110
109
|
|
|
111
|
-
class Transaction<
|
|
112
|
-
T extends object = Record<string, unknown>,
|
|
113
|
-
TOperation extends OperationType = OperationType,
|
|
114
|
-
> {
|
|
110
|
+
class Transaction<T extends object = Record<string, unknown>> {
|
|
115
111
|
public id: string
|
|
116
112
|
public state: TransactionState
|
|
117
113
|
public mutationFn: MutationFn<T>
|
|
118
|
-
public mutations: Array<PendingMutation<T
|
|
119
|
-
public isPersisted: Deferred<Transaction<T
|
|
114
|
+
public mutations: Array<PendingMutation<T>>
|
|
115
|
+
public isPersisted: Deferred<Transaction<T>>
|
|
120
116
|
public autoCommit: boolean
|
|
121
117
|
public createdAt: Date
|
|
122
118
|
public sequenceNumber: number
|
|
@@ -134,7 +130,7 @@ class Transaction<
|
|
|
134
130
|
this.mutationFn = config.mutationFn
|
|
135
131
|
this.state = `pending`
|
|
136
132
|
this.mutations = []
|
|
137
|
-
this.isPersisted = createDeferred<Transaction<T
|
|
133
|
+
this.isPersisted = createDeferred<Transaction<T>>()
|
|
138
134
|
this.autoCommit = config.autoCommit ?? true
|
|
139
135
|
this.createdAt = new Date()
|
|
140
136
|
this.sequenceNumber = sequenceNumber++
|
package/src/types.ts
CHANGED
|
@@ -14,6 +14,40 @@ export type InferSchemaOutput<T> = T extends StandardSchemaV1
|
|
|
14
14
|
: Record<string, unknown>
|
|
15
15
|
: Record<string, unknown>
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Helper type to extract the input type from a standard schema
|
|
19
|
+
*
|
|
20
|
+
* @internal This is used for collection insert type inference
|
|
21
|
+
*/
|
|
22
|
+
export type InferSchemaInput<T> = T extends StandardSchemaV1
|
|
23
|
+
? StandardSchemaV1.InferInput<T> extends object
|
|
24
|
+
? StandardSchemaV1.InferInput<T>
|
|
25
|
+
: Record<string, unknown>
|
|
26
|
+
: Record<string, unknown>
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Helper type to determine the insert input type
|
|
30
|
+
* This takes the raw generics (TExplicit, TSchema, TFallback) instead of the resolved T.
|
|
31
|
+
*
|
|
32
|
+
* Priority:
|
|
33
|
+
* 1. Explicit generic TExplicit (if not 'unknown')
|
|
34
|
+
* 2. Schema input type (if schema provided)
|
|
35
|
+
* 3. Fallback type TFallback
|
|
36
|
+
*
|
|
37
|
+
* @internal This is used for collection insert type inference
|
|
38
|
+
*/
|
|
39
|
+
export type ResolveInsertInput<
|
|
40
|
+
TExplicit = unknown,
|
|
41
|
+
TSchema extends StandardSchemaV1 = never,
|
|
42
|
+
TFallback extends object = Record<string, unknown>,
|
|
43
|
+
> = unknown extends TExplicit
|
|
44
|
+
? [TSchema] extends [never]
|
|
45
|
+
? TFallback
|
|
46
|
+
: InferSchemaInput<TSchema>
|
|
47
|
+
: TExplicit extends object
|
|
48
|
+
? TExplicit
|
|
49
|
+
: Record<string, unknown>
|
|
50
|
+
|
|
17
51
|
/**
|
|
18
52
|
* Helper type to determine the final type based on priority:
|
|
19
53
|
* 1. Explicit generic TExplicit (if not 'unknown')
|
|
@@ -48,6 +82,17 @@ export type Fn = (...args: Array<any>) => any
|
|
|
48
82
|
*/
|
|
49
83
|
export type UtilsRecord = Record<string, Fn>
|
|
50
84
|
|
|
85
|
+
/**
|
|
86
|
+
*
|
|
87
|
+
* @remarks `update` and `insert` are both represented as `Partial<T>`, but changes for `insert` could me made more precise by inferring the schema input type. In practice, this has almost 0 real world impact so it's not worth the added type complexity.
|
|
88
|
+
*
|
|
89
|
+
* @see https://github.com/TanStack/db/pull/209#issuecomment-3053001206
|
|
90
|
+
*/
|
|
91
|
+
export type ResolveTransactionChanges<
|
|
92
|
+
T extends object = Record<string, unknown>,
|
|
93
|
+
TOperation extends OperationType = OperationType,
|
|
94
|
+
> = TOperation extends `delete` ? T : Partial<T>
|
|
95
|
+
|
|
51
96
|
/**
|
|
52
97
|
* Represents a pending mutation within a transaction
|
|
53
98
|
* Contains information about the original and modified data, as well as metadata
|
|
@@ -55,25 +100,32 @@ export type UtilsRecord = Record<string, Fn>
|
|
|
55
100
|
export interface PendingMutation<
|
|
56
101
|
T extends object = Record<string, unknown>,
|
|
57
102
|
TOperation extends OperationType = OperationType,
|
|
103
|
+
TCollection extends Collection<T, any, any, any, any> = Collection<
|
|
104
|
+
T,
|
|
105
|
+
any,
|
|
106
|
+
any,
|
|
107
|
+
any,
|
|
108
|
+
any
|
|
109
|
+
>,
|
|
58
110
|
> {
|
|
59
111
|
mutationId: string
|
|
112
|
+
// The state of the object before the mutation.
|
|
60
113
|
original: TOperation extends `insert` ? {} : T
|
|
114
|
+
// The result state of the object after all mutations.
|
|
61
115
|
modified: T
|
|
62
|
-
changes
|
|
63
|
-
|
|
64
|
-
: TOperation extends `delete`
|
|
65
|
-
? T
|
|
66
|
-
: Partial<T>
|
|
116
|
+
// Only the actual changes to the object by the mutation.
|
|
117
|
+
changes: ResolveTransactionChanges<T, TOperation>
|
|
67
118
|
globalKey: string
|
|
119
|
+
|
|
68
120
|
key: any
|
|
69
|
-
type:
|
|
121
|
+
type: TOperation
|
|
70
122
|
metadata: unknown
|
|
71
123
|
syncMetadata: Record<string, unknown>
|
|
72
124
|
/** Whether this mutation should be applied optimistically (defaults to true) */
|
|
73
125
|
optimistic: boolean
|
|
74
126
|
createdAt: Date
|
|
75
127
|
updatedAt: Date
|
|
76
|
-
collection:
|
|
128
|
+
collection: TCollection
|
|
77
129
|
}
|
|
78
130
|
|
|
79
131
|
/**
|
|
@@ -99,7 +151,7 @@ export type NonEmptyArray<T> = [T, ...Array<T>]
|
|
|
99
151
|
export type TransactionWithMutations<
|
|
100
152
|
T extends object = Record<string, unknown>,
|
|
101
153
|
TOperation extends OperationType = OperationType,
|
|
102
|
-
> = Transaction<T
|
|
154
|
+
> = Transaction<T> & {
|
|
103
155
|
mutations: NonEmptyArray<PendingMutation<T, TOperation>>
|
|
104
156
|
}
|
|
105
157
|
|
|
@@ -116,12 +168,14 @@ export interface TransactionConfig<T extends object = Record<string, unknown>> {
|
|
|
116
168
|
/**
|
|
117
169
|
* Options for the createOptimisticAction helper
|
|
118
170
|
*/
|
|
119
|
-
export interface CreateOptimisticActionsOptions<
|
|
120
|
-
|
|
171
|
+
export interface CreateOptimisticActionsOptions<
|
|
172
|
+
TVars = unknown,
|
|
173
|
+
T extends object = Record<string, unknown>,
|
|
174
|
+
> extends Omit<TransactionConfig<T>, `mutationFn`> {
|
|
121
175
|
/** Function to apply optimistic updates locally before the mutation completes */
|
|
122
176
|
onMutate: (vars: TVars) => void
|
|
123
177
|
/** Function to execute the mutation on the server */
|
|
124
|
-
mutationFn: (vars: TVars, params: MutationFnParams) => Promise<any>
|
|
178
|
+
mutationFn: (vars: TVars, params: MutationFnParams<T>) => Promise<any>
|
|
125
179
|
}
|
|
126
180
|
|
|
127
181
|
export type { Transaction }
|
|
@@ -145,7 +199,7 @@ export interface SyncConfig<
|
|
|
145
199
|
TKey extends string | number = string | number,
|
|
146
200
|
> {
|
|
147
201
|
sync: (params: {
|
|
148
|
-
collection: Collection<T, TKey>
|
|
202
|
+
collection: Collection<T, TKey, any, any, any>
|
|
149
203
|
begin: () => void
|
|
150
204
|
write: (message: Omit<ChangeMessage<T>, `key`>) => void
|
|
151
205
|
commit: () => void
|
|
@@ -232,7 +286,6 @@ export type InsertMutationFnParams<
|
|
|
232
286
|
transaction: TransactionWithMutations<T, `insert`>
|
|
233
287
|
collection: Collection<T, TKey, TUtils>
|
|
234
288
|
}
|
|
235
|
-
|
|
236
289
|
export type DeleteMutationFnParams<
|
|
237
290
|
T extends object = Record<string, unknown>,
|
|
238
291
|
TKey extends string | number = string | number,
|
|
@@ -293,6 +346,7 @@ export interface CollectionConfig<
|
|
|
293
346
|
T extends object = Record<string, unknown>,
|
|
294
347
|
TKey extends string | number = string | number,
|
|
295
348
|
TSchema extends StandardSchemaV1 = StandardSchemaV1,
|
|
349
|
+
TInsertInput extends object = T,
|
|
296
350
|
> {
|
|
297
351
|
// If an id isn't passed in, a UUID will be
|
|
298
352
|
// generated for it.
|
|
@@ -371,7 +425,8 @@ export interface CollectionConfig<
|
|
|
371
425
|
* })
|
|
372
426
|
* }
|
|
373
427
|
*/
|
|
374
|
-
onInsert?: InsertMutationFn<
|
|
428
|
+
onInsert?: InsertMutationFn<TInsertInput, TKey>
|
|
429
|
+
|
|
375
430
|
/**
|
|
376
431
|
* Optional asynchronous handler function called before an update operation
|
|
377
432
|
* @param params Object containing transaction and collection information
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic utility functions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Deep equality function that compares two values recursively
|
|
7
|
+
*
|
|
8
|
+
* @param a - First value to compare
|
|
9
|
+
* @param b - Second value to compare
|
|
10
|
+
* @returns True if the values are deeply equal, false otherwise
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* deepEquals({ a: 1, b: 2 }, { b: 2, a: 1 }) // true (property order doesn't matter)
|
|
15
|
+
* deepEquals([1, { x: 2 }], [1, { x: 2 }]) // true
|
|
16
|
+
* deepEquals({ a: 1 }, { a: 2 }) // false
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function deepEquals(a: any, b: any): boolean {
|
|
20
|
+
return deepEqualsInternal(a, b, new Map())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Internal implementation with cycle detection to prevent infinite recursion
|
|
25
|
+
*/
|
|
26
|
+
function deepEqualsInternal(
|
|
27
|
+
a: any,
|
|
28
|
+
b: any,
|
|
29
|
+
visited: Map<object, object>
|
|
30
|
+
): boolean {
|
|
31
|
+
// Handle strict equality (primitives, same reference)
|
|
32
|
+
if (a === b) return true
|
|
33
|
+
|
|
34
|
+
// Handle null/undefined
|
|
35
|
+
if (a == null || b == null) return false
|
|
36
|
+
|
|
37
|
+
// Handle different types
|
|
38
|
+
if (typeof a !== typeof b) return false
|
|
39
|
+
|
|
40
|
+
// Handle arrays
|
|
41
|
+
if (Array.isArray(a)) {
|
|
42
|
+
if (!Array.isArray(b) || a.length !== b.length) return false
|
|
43
|
+
|
|
44
|
+
// Check for circular references
|
|
45
|
+
if (visited.has(a)) {
|
|
46
|
+
return visited.get(a) === b
|
|
47
|
+
}
|
|
48
|
+
visited.set(a, b)
|
|
49
|
+
|
|
50
|
+
const result = a.every((item, index) =>
|
|
51
|
+
deepEqualsInternal(item, b[index], visited)
|
|
52
|
+
)
|
|
53
|
+
visited.delete(a)
|
|
54
|
+
return result
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle objects
|
|
58
|
+
if (typeof a === `object`) {
|
|
59
|
+
// Check for circular references
|
|
60
|
+
if (visited.has(a)) {
|
|
61
|
+
return visited.get(a) === b
|
|
62
|
+
}
|
|
63
|
+
visited.set(a, b)
|
|
64
|
+
|
|
65
|
+
// Get all keys from both objects
|
|
66
|
+
const keysA = Object.keys(a)
|
|
67
|
+
const keysB = Object.keys(b)
|
|
68
|
+
|
|
69
|
+
// Check if they have the same number of keys
|
|
70
|
+
if (keysA.length !== keysB.length) {
|
|
71
|
+
visited.delete(a)
|
|
72
|
+
return false
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check if all keys exist in both objects and their values are equal
|
|
76
|
+
const result = keysA.every(
|
|
77
|
+
(key) => key in b && deepEqualsInternal(a[key], b[key], visited)
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
visited.delete(a)
|
|
81
|
+
return result
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// For primitives that aren't strictly equal
|
|
85
|
+
return false
|
|
86
|
+
}
|