@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.
Files changed (38) hide show
  1. package/dist/cjs/collection.cjs +14 -6
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +10 -9
  4. package/dist/cjs/local-storage.cjs +1 -1
  5. package/dist/cjs/local-storage.cjs.map +1 -1
  6. package/dist/cjs/query/builder/index.cjs +20 -0
  7. package/dist/cjs/query/builder/index.cjs.map +1 -1
  8. package/dist/cjs/query/builder/index.d.cts +15 -0
  9. package/dist/cjs/query/compiler/index.cjs +6 -0
  10. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  11. package/dist/cjs/query/ir.cjs.map +1 -1
  12. package/dist/cjs/query/ir.d.cts +1 -0
  13. package/dist/cjs/transactions.cjs.map +1 -1
  14. package/dist/cjs/transactions.d.cts +5 -5
  15. package/dist/cjs/types.d.cts +35 -10
  16. package/dist/esm/collection.d.ts +10 -9
  17. package/dist/esm/collection.js +14 -6
  18. package/dist/esm/collection.js.map +1 -1
  19. package/dist/esm/local-storage.js +1 -1
  20. package/dist/esm/local-storage.js.map +1 -1
  21. package/dist/esm/query/builder/index.d.ts +15 -0
  22. package/dist/esm/query/builder/index.js +20 -0
  23. package/dist/esm/query/builder/index.js.map +1 -1
  24. package/dist/esm/query/compiler/index.js +7 -1
  25. package/dist/esm/query/compiler/index.js.map +1 -1
  26. package/dist/esm/query/ir.d.ts +1 -0
  27. package/dist/esm/query/ir.js.map +1 -1
  28. package/dist/esm/transactions.d.ts +5 -5
  29. package/dist/esm/transactions.js.map +1 -1
  30. package/dist/esm/types.d.ts +35 -10
  31. package/package.json +2 -2
  32. package/src/collection.ts +62 -21
  33. package/src/local-storage.ts +2 -2
  34. package/src/query/builder/index.ts +21 -0
  35. package/src/query/compiler/index.ts +11 -2
  36. package/src/query/ir.ts +1 -0
  37. package/src/transactions.ts +8 -12
  38. 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
- > extends CollectionImpl<T, TKey> {
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<ResolveType<TExplicit, TSchema, TFallback>, TKey, TUtils> {
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, any>
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, any>) {
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 = (data: T | Array<T>, config?: InsertConfig) => {
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, `insert`>> = []
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(item)
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
- changes: validatedData,
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
- ...params,
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`>> = keysArray
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
- ...params,
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
- ...params,
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
  },
@@ -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
- getKey: (item: T) => string | number,
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 future DISTINCT implementation
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
@@ -15,6 +15,7 @@ export interface QueryIR {
15
15
  orderBy?: OrderBy
16
16
  limit?: Limit
17
17
  offset?: Offset
18
+ distinct?: true
18
19
 
19
20
  // Functional variants
20
21
  fnSelect?: (row: NamespacedRow) => any
@@ -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
- TData extends object = Record<string, unknown>,
71
- >(config: TransactionConfig<TData>): Transaction<TData> {
72
- const newTransaction = new Transaction<TData>(config)
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, TOperation>>
119
- public isPersisted: Deferred<Transaction<T, TOperation>>
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, TOperation>>()
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: TOperation extends `insert`
63
- ? T
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: OperationType
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: Collection<T, any, any>
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, TOperation> & {
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<TVars = unknown>
120
- extends Omit<TransactionConfig, `mutationFn`> {
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<T, TKey>
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