@tanstack/db 0.0.21 → 0.0.23
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/query/builder/index.cjs +20 -0
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/index.d.cts +15 -0
- package/dist/cjs/query/compiler/index.cjs +6 -0
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +1 -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/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/query/builder/index.d.ts +15 -0
- package/dist/esm/query/builder/index.js +20 -0
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/compiler/index.js +7 -1
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/ir.d.ts +1 -0
- package/dist/esm/query/ir.js.map +1 -1
- 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/package.json +2 -2
- package/src/collection.ts +62 -21
- package/src/local-storage.ts +2 -2
- package/src/query/builder/index.ts +21 -0
- package/src/query/compiler/index.ts +11 -2
- package/src/query/ir.ts +1 -0
- package/src/transactions.ts +8 -12
- package/src/types.ts +69 -14
package/src/collection.ts
CHANGED
|
@@ -12,15 +12,17 @@ import type {
|
|
|
12
12
|
OperationConfig,
|
|
13
13
|
OptimisticChangeMessage,
|
|
14
14
|
PendingMutation,
|
|
15
|
+
ResolveInsertInput,
|
|
15
16
|
ResolveType,
|
|
16
17
|
StandardSchema,
|
|
17
18
|
Transaction as TransactionType,
|
|
19
|
+
TransactionWithMutations,
|
|
18
20
|
UtilsRecord,
|
|
19
21
|
} from "./types"
|
|
20
22
|
import type { StandardSchemaV1 } from "@standard-schema/spec"
|
|
21
23
|
|
|
22
24
|
// Store collections in memory
|
|
23
|
-
export const collectionsStore = new Map<string, CollectionImpl<any, any>>()
|
|
25
|
+
export const collectionsStore = new Map<string, CollectionImpl<any, any, any>>()
|
|
24
26
|
|
|
25
27
|
interface PendingSyncedTransaction<T extends object = Record<string, unknown>> {
|
|
26
28
|
committed: boolean
|
|
@@ -32,12 +34,15 @@ interface PendingSyncedTransaction<T extends object = Record<string, unknown>> {
|
|
|
32
34
|
* @template T - The type of items in the collection
|
|
33
35
|
* @template TKey - The type of the key for the collection
|
|
34
36
|
* @template TUtils - The utilities record type
|
|
37
|
+
* @template TInsertInput - The type for insert operations (can be different from T for schemas with defaults)
|
|
35
38
|
*/
|
|
36
39
|
export interface Collection<
|
|
37
40
|
T extends object = Record<string, unknown>,
|
|
38
41
|
TKey extends string | number = string | number,
|
|
39
42
|
TUtils extends UtilsRecord = {},
|
|
40
|
-
|
|
43
|
+
TSchema extends StandardSchemaV1 = StandardSchemaV1,
|
|
44
|
+
TInsertInput extends object = T,
|
|
45
|
+
> extends CollectionImpl<T, TKey, TUtils, TSchema, TInsertInput> {
|
|
41
46
|
readonly utils: TUtils
|
|
42
47
|
}
|
|
43
48
|
|
|
@@ -124,12 +129,22 @@ export function createCollection<
|
|
|
124
129
|
options: CollectionConfig<
|
|
125
130
|
ResolveType<TExplicit, TSchema, TFallback>,
|
|
126
131
|
TKey,
|
|
127
|
-
TSchema
|
|
132
|
+
TSchema,
|
|
133
|
+
ResolveInsertInput<TExplicit, TSchema, TFallback>
|
|
128
134
|
> & { utils?: TUtils }
|
|
129
|
-
): Collection<
|
|
135
|
+
): Collection<
|
|
136
|
+
ResolveType<TExplicit, TSchema, TFallback>,
|
|
137
|
+
TKey,
|
|
138
|
+
TUtils,
|
|
139
|
+
TSchema,
|
|
140
|
+
ResolveInsertInput<TExplicit, TSchema, TFallback>
|
|
141
|
+
> {
|
|
130
142
|
const collection = new CollectionImpl<
|
|
131
143
|
ResolveType<TExplicit, TSchema, TFallback>,
|
|
132
|
-
TKey
|
|
144
|
+
TKey,
|
|
145
|
+
TUtils,
|
|
146
|
+
TSchema,
|
|
147
|
+
ResolveInsertInput<TExplicit, TSchema, TFallback>
|
|
133
148
|
>(options)
|
|
134
149
|
|
|
135
150
|
// Copy utils to both top level and .utils namespace
|
|
@@ -142,7 +157,9 @@ export function createCollection<
|
|
|
142
157
|
return collection as Collection<
|
|
143
158
|
ResolveType<TExplicit, TSchema, TFallback>,
|
|
144
159
|
TKey,
|
|
145
|
-
TUtils
|
|
160
|
+
TUtils,
|
|
161
|
+
TSchema,
|
|
162
|
+
ResolveInsertInput<TExplicit, TSchema, TFallback>
|
|
146
163
|
>
|
|
147
164
|
}
|
|
148
165
|
|
|
@@ -179,8 +196,10 @@ export class CollectionImpl<
|
|
|
179
196
|
T extends object = Record<string, unknown>,
|
|
180
197
|
TKey extends string | number = string | number,
|
|
181
198
|
TUtils extends UtilsRecord = {},
|
|
199
|
+
TSchema extends StandardSchemaV1 = StandardSchemaV1,
|
|
200
|
+
TInsertInput extends object = T,
|
|
182
201
|
> {
|
|
183
|
-
public config: CollectionConfig<T, TKey,
|
|
202
|
+
public config: CollectionConfig<T, TKey, TSchema, TInsertInput>
|
|
184
203
|
|
|
185
204
|
// Core state - make public for testing
|
|
186
205
|
public transactions: SortedMap<string, Transaction<any>>
|
|
@@ -312,7 +331,7 @@ export class CollectionImpl<
|
|
|
312
331
|
* @param config - Configuration object for the collection
|
|
313
332
|
* @throws Error if sync config is missing
|
|
314
333
|
*/
|
|
315
|
-
constructor(config: CollectionConfig<T, TKey,
|
|
334
|
+
constructor(config: CollectionConfig<T, TKey, TSchema, TInsertInput>) {
|
|
316
335
|
// eslint-disable-next-line
|
|
317
336
|
if (!config) {
|
|
318
337
|
throw new Error(`Collection requires a config`)
|
|
@@ -1322,9 +1341,11 @@ export class CollectionImpl<
|
|
|
1322
1341
|
* console.log('Insert failed:', error)
|
|
1323
1342
|
* }
|
|
1324
1343
|
*/
|
|
1325
|
-
insert = (
|
|
1344
|
+
insert = (
|
|
1345
|
+
data: TInsertInput | Array<TInsertInput>,
|
|
1346
|
+
config?: InsertConfig
|
|
1347
|
+
) => {
|
|
1326
1348
|
this.validateCollectionUsable(`insert`)
|
|
1327
|
-
|
|
1328
1349
|
const ambientTransaction = getActiveTransaction()
|
|
1329
1350
|
|
|
1330
1351
|
// If no ambient transaction exists, check for an onInsert handler early
|
|
@@ -1335,7 +1356,7 @@ export class CollectionImpl<
|
|
|
1335
1356
|
}
|
|
1336
1357
|
|
|
1337
1358
|
const items = Array.isArray(data) ? data : [data]
|
|
1338
|
-
const mutations: Array<PendingMutation<T
|
|
1359
|
+
const mutations: Array<PendingMutation<T>> = []
|
|
1339
1360
|
|
|
1340
1361
|
// Create mutations for each item
|
|
1341
1362
|
items.forEach((item) => {
|
|
@@ -1343,7 +1364,7 @@ export class CollectionImpl<
|
|
|
1343
1364
|
const validatedData = this.validateData(item, `insert`)
|
|
1344
1365
|
|
|
1345
1366
|
// Check if an item with this ID already exists in the collection
|
|
1346
|
-
const key = this.getKeyFromItem(
|
|
1367
|
+
const key = this.getKeyFromItem(validatedData)
|
|
1347
1368
|
if (this.has(key)) {
|
|
1348
1369
|
throw `Cannot insert document with ID "${key}" because it already exists in the collection`
|
|
1349
1370
|
}
|
|
@@ -1353,7 +1374,15 @@ export class CollectionImpl<
|
|
|
1353
1374
|
mutationId: crypto.randomUUID(),
|
|
1354
1375
|
original: {},
|
|
1355
1376
|
modified: validatedData,
|
|
1356
|
-
|
|
1377
|
+
// Pick the values from validatedData based on what's passed in - this is for cases
|
|
1378
|
+
// where a schema has default values. The validated data has the extra default
|
|
1379
|
+
// values but for changes, we just want to show the data that was actually passed in.
|
|
1380
|
+
changes: Object.fromEntries(
|
|
1381
|
+
Object.keys(item).map((k) => [
|
|
1382
|
+
k,
|
|
1383
|
+
validatedData[k as keyof typeof validatedData],
|
|
1384
|
+
])
|
|
1385
|
+
) as TInsertInput,
|
|
1357
1386
|
globalKey,
|
|
1358
1387
|
key,
|
|
1359
1388
|
metadata: config?.metadata as unknown,
|
|
@@ -1381,8 +1410,12 @@ export class CollectionImpl<
|
|
|
1381
1410
|
const directOpTransaction = createTransaction<T>({
|
|
1382
1411
|
mutationFn: async (params) => {
|
|
1383
1412
|
// Call the onInsert handler with the transaction and collection
|
|
1384
|
-
return this.config.onInsert!({
|
|
1385
|
-
|
|
1413
|
+
return await this.config.onInsert!({
|
|
1414
|
+
transaction:
|
|
1415
|
+
params.transaction as unknown as TransactionWithMutations<
|
|
1416
|
+
TInsertInput,
|
|
1417
|
+
`insert`
|
|
1418
|
+
>,
|
|
1386
1419
|
collection: this as unknown as Collection<T, TKey, TUtils>,
|
|
1387
1420
|
})
|
|
1388
1421
|
},
|
|
@@ -1526,7 +1559,7 @@ export class CollectionImpl<
|
|
|
1526
1559
|
}
|
|
1527
1560
|
|
|
1528
1561
|
// Create mutations for each object that has changes
|
|
1529
|
-
const mutations: Array<PendingMutation<T, `update
|
|
1562
|
+
const mutations: Array<PendingMutation<T, `update`, this>> = keysArray
|
|
1530
1563
|
.map((key, index) => {
|
|
1531
1564
|
const itemChanges = changesArray[index] // User-provided changes for this specific item
|
|
1532
1565
|
|
|
@@ -1581,7 +1614,7 @@ export class CollectionImpl<
|
|
|
1581
1614
|
collection: this,
|
|
1582
1615
|
}
|
|
1583
1616
|
})
|
|
1584
|
-
.filter(Boolean) as Array<PendingMutation<T, `update
|
|
1617
|
+
.filter(Boolean) as Array<PendingMutation<T, `update`, this>>
|
|
1585
1618
|
|
|
1586
1619
|
// If no changes were made, return an empty transaction early
|
|
1587
1620
|
if (mutations.length === 0) {
|
|
@@ -1609,7 +1642,11 @@ export class CollectionImpl<
|
|
|
1609
1642
|
mutationFn: async (params) => {
|
|
1610
1643
|
// Call the onUpdate handler with the transaction and collection
|
|
1611
1644
|
return this.config.onUpdate!({
|
|
1612
|
-
|
|
1645
|
+
transaction:
|
|
1646
|
+
params.transaction as unknown as TransactionWithMutations<
|
|
1647
|
+
T,
|
|
1648
|
+
`update`
|
|
1649
|
+
>,
|
|
1613
1650
|
collection: this as unknown as Collection<T, TKey, TUtils>,
|
|
1614
1651
|
})
|
|
1615
1652
|
},
|
|
@@ -1677,7 +1714,7 @@ export class CollectionImpl<
|
|
|
1677
1714
|
}
|
|
1678
1715
|
|
|
1679
1716
|
const keysArray = Array.isArray(keys) ? keys : [keys]
|
|
1680
|
-
const mutations: Array<PendingMutation<T, `delete
|
|
1717
|
+
const mutations: Array<PendingMutation<T, `delete`, this>> = []
|
|
1681
1718
|
|
|
1682
1719
|
for (const key of keysArray) {
|
|
1683
1720
|
if (!this.has(key)) {
|
|
@@ -1686,7 +1723,7 @@ export class CollectionImpl<
|
|
|
1686
1723
|
)
|
|
1687
1724
|
}
|
|
1688
1725
|
const globalKey = this.generateGlobalKey(key, this.get(key)!)
|
|
1689
|
-
const mutation: PendingMutation<T, `delete
|
|
1726
|
+
const mutation: PendingMutation<T, `delete`, this> = {
|
|
1690
1727
|
mutationId: crypto.randomUUID(),
|
|
1691
1728
|
original: this.get(key)!,
|
|
1692
1729
|
modified: this.get(key)!,
|
|
@@ -1724,7 +1761,11 @@ export class CollectionImpl<
|
|
|
1724
1761
|
mutationFn: async (params) => {
|
|
1725
1762
|
// Call the onDelete handler with the transaction and collection
|
|
1726
1763
|
return this.config.onDelete!({
|
|
1727
|
-
|
|
1764
|
+
transaction:
|
|
1765
|
+
params.transaction as unknown as TransactionWithMutations<
|
|
1766
|
+
T,
|
|
1767
|
+
`delete`
|
|
1768
|
+
>,
|
|
1728
1769
|
collection: this as unknown as Collection<T, TKey, TUtils>,
|
|
1729
1770
|
})
|
|
1730
1771
|
},
|
package/src/local-storage.ts
CHANGED
|
@@ -393,7 +393,7 @@ export function localStorageCollectionOptions<
|
|
|
393
393
|
// Remove items
|
|
394
394
|
params.transaction.mutations.forEach((mutation) => {
|
|
395
395
|
// For delete operations, mutation.original contains the full object
|
|
396
|
-
const key = config.getKey(mutation.original)
|
|
396
|
+
const key = config.getKey(mutation.original as ResolvedType)
|
|
397
397
|
currentData.delete(key)
|
|
398
398
|
})
|
|
399
399
|
|
|
@@ -506,7 +506,7 @@ function createLocalStorageSync<T extends object>(
|
|
|
506
506
|
storageKey: string,
|
|
507
507
|
storage: StorageApi,
|
|
508
508
|
storageEventApi: StorageEventApi,
|
|
509
|
-
|
|
509
|
+
_getKey: (item: T) => string | number,
|
|
510
510
|
lastKnownData: Map<string | number, StoredItem<T>>
|
|
511
511
|
): SyncConfig<T> & { manualTrigger?: () => void } {
|
|
512
512
|
let syncParams: Parameters<SyncConfig<T>[`sync`]>[0] | null = null
|
|
@@ -480,6 +480,27 @@ export class BaseQueryBuilder<TContext extends Context = Context> {
|
|
|
480
480
|
}) as any
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Specify that the query should return distinct rows.
|
|
485
|
+
* Deduplicates rows based on the selected columns.
|
|
486
|
+
* @returns A QueryBuilder with distinct enabled
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```ts
|
|
490
|
+
* // Get countries our users are from
|
|
491
|
+
* query
|
|
492
|
+
* .from({ users: usersCollection })
|
|
493
|
+
* .select(({users}) => users.country)
|
|
494
|
+
* .distinct()
|
|
495
|
+
* ```
|
|
496
|
+
*/
|
|
497
|
+
distinct(): QueryBuilder<TContext> {
|
|
498
|
+
return new BaseQueryBuilder({
|
|
499
|
+
...this.query,
|
|
500
|
+
distinct: true,
|
|
501
|
+
}) as any
|
|
502
|
+
}
|
|
503
|
+
|
|
483
504
|
// Helper methods
|
|
484
505
|
private _getCurrentAliases(): Array<string> {
|
|
485
506
|
const aliases: Array<string> = []
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { filter, map } from "@electric-sql/d2mini"
|
|
1
|
+
import { distinct, filter, map } from "@electric-sql/d2mini"
|
|
2
2
|
import { compileExpression } from "./evaluators.js"
|
|
3
3
|
import { processJoins } from "./joins.js"
|
|
4
4
|
import { processGroupBy } from "./group-by.js"
|
|
@@ -98,8 +98,12 @@ export function compileQuery(
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
if (query.distinct && !query.fnSelect && !query.select) {
|
|
102
|
+
throw new Error(`DISTINCT requires a SELECT clause.`)
|
|
103
|
+
}
|
|
104
|
+
|
|
101
105
|
// Process the SELECT clause early - always create __select_results
|
|
102
|
-
// This eliminates duplication and allows for
|
|
106
|
+
// This eliminates duplication and allows for DISTINCT implementation
|
|
103
107
|
if (query.fnSelect) {
|
|
104
108
|
// Handle functional select - apply the function to transform the row
|
|
105
109
|
pipeline = pipeline.pipe(
|
|
@@ -190,6 +194,11 @@ export function compileQuery(
|
|
|
190
194
|
}
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
// Process the DISTINCT clause if it exists
|
|
198
|
+
if (query.distinct) {
|
|
199
|
+
pipeline = pipeline.pipe(distinct(([_key, row]) => row.__select_results))
|
|
200
|
+
}
|
|
201
|
+
|
|
193
202
|
// Process orderBy parameter if it exists
|
|
194
203
|
if (query.orderBy && query.orderBy.length > 0) {
|
|
195
204
|
const orderedPipeline = processOrderBy(
|
package/src/query/ir.ts
CHANGED
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
|