@tanstack/electric-db-collection 0.4.10 → 0.5.1
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/electric.cjs +146 -103
- package/dist/cjs/electric.cjs.map +1 -1
- package/dist/cjs/electric.d.cts +94 -25
- package/dist/cjs/sql-compiler.cjs +43 -92
- package/dist/cjs/sql-compiler.cjs.map +1 -1
- package/dist/esm/electric.d.ts +94 -25
- package/dist/esm/electric.js +147 -104
- package/dist/esm/electric.js.map +1 -1
- package/dist/esm/sql-compiler.js +43 -92
- package/dist/esm/sql-compiler.js.map +1 -1
- package/package.json +2 -2
- package/src/electric.ts +295 -166
- package/src/sql-compiler.ts +57 -134
package/dist/esm/electric.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseCollectionConfig, CollectionConfig, DeleteMutationFnParams, InsertMutationFnParams, SyncMode, UpdateMutationFnParams
|
|
1
|
+
import { BaseCollectionConfig, CollectionConfig, DeleteMutationFnParams, InsertMutationFnParams, SyncMode, UpdateMutationFnParams } from '@tanstack/db';
|
|
2
2
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
3
|
import { GetExtensions, Message, Row, ShapeStreamOptions } from '@electric-sql/client';
|
|
4
4
|
export { isChangeMessage, isControlMessage } from '@electric-sql/client';
|
|
@@ -28,11 +28,11 @@ export type MatchFunction<T extends Row<unknown>> = (message: Message<T>) => boo
|
|
|
28
28
|
/**
|
|
29
29
|
* Matching strategies for Electric synchronization
|
|
30
30
|
* Handlers can return:
|
|
31
|
-
* - Txid strategy: { txid: number | number[], timeout?: number } (
|
|
31
|
+
* - Txid strategy: { txid: number | number[], timeout?: number } (deprecated compatibility behavior)
|
|
32
32
|
* - Void (no return value) - mutation completes without waiting
|
|
33
33
|
*
|
|
34
34
|
* The optional timeout property specifies how long to wait for the txid(s) in milliseconds.
|
|
35
|
-
* If not specified, defaults to
|
|
35
|
+
* If not specified, defaults to 15000ms.
|
|
36
36
|
*/
|
|
37
37
|
export type MatchingStrategy = {
|
|
38
38
|
txid: Txid | Array<Txid>;
|
|
@@ -61,7 +61,7 @@ export type ElectricSyncMode = SyncMode | `progressive`;
|
|
|
61
61
|
* @template T - The type of items in the collection
|
|
62
62
|
* @template TSchema - The schema type for validation
|
|
63
63
|
*/
|
|
64
|
-
export interface ElectricCollectionConfig<T extends Row<unknown> = Row<unknown>, TSchema extends StandardSchemaV1 = never> extends Omit<BaseCollectionConfig<T, string | number, TSchema, ElectricCollectionUtils<T
|
|
64
|
+
export interface ElectricCollectionConfig<T extends Row<unknown> = Row<unknown>, TSchema extends StandardSchemaV1 = never> extends Omit<BaseCollectionConfig<T, string | number, TSchema, ElectricCollectionUtils<T>>, `onInsert` | `onUpdate` | `onDelete` | `syncMode`> {
|
|
65
65
|
/**
|
|
66
66
|
* Configuration options for the ElectricSQL ShapeStream
|
|
67
67
|
*/
|
|
@@ -74,43 +74,82 @@ export interface ElectricCollectionConfig<T extends Row<unknown> = Row<unknown>,
|
|
|
74
74
|
[ELECTRIC_TEST_HOOKS]?: ElectricTestHooks;
|
|
75
75
|
/**
|
|
76
76
|
* Optional asynchronous handler function called before an insert operation
|
|
77
|
+
*
|
|
78
|
+
* **IMPORTANT - Electric Synchronization:**
|
|
79
|
+
* This handler **must not resolve** until synchronization is confirmed.
|
|
80
|
+
* Await one of these synchronization utilities before the handler completes:
|
|
81
|
+
* 1. `await collection.utils.awaitTxId(txid)` (recommended for most cases)
|
|
82
|
+
* 2. `await collection.utils.awaitMatch(fn)` for custom matching logic
|
|
83
|
+
*
|
|
84
|
+
* Simply returning without waiting for sync will drop optimistic state too early, causing UI glitches.
|
|
85
|
+
*
|
|
77
86
|
* @param params Object containing transaction and collection information
|
|
78
|
-
* @returns Promise
|
|
87
|
+
* @returns Promise that should resolve after synchronization is complete
|
|
88
|
+
*
|
|
89
|
+
* **Deprecation notice:** Returning `{ txid }` from handlers is deprecated and will be removed in v1.0.
|
|
90
|
+
* Use `await collection.utils.awaitTxId(txid)` within the handler instead.
|
|
91
|
+
*
|
|
79
92
|
* @example
|
|
80
|
-
* //
|
|
81
|
-
* onInsert: async ({ transaction }) => {
|
|
93
|
+
* // Recommended: Wait for txid to sync
|
|
94
|
+
* onInsert: async ({ transaction, collection }) => {
|
|
82
95
|
* const newItem = transaction.mutations[0].modified
|
|
83
96
|
* const result = await api.todos.create({
|
|
84
97
|
* data: newItem
|
|
85
98
|
* })
|
|
86
|
-
*
|
|
99
|
+
* // Wait for txid to sync before handler completes
|
|
100
|
+
* await collection.utils.awaitTxId(result.txid)
|
|
87
101
|
* }
|
|
88
102
|
*
|
|
89
103
|
* @example
|
|
90
104
|
* // Insert handler with custom timeout
|
|
91
|
-
* onInsert: async ({ transaction }) => {
|
|
105
|
+
* onInsert: async ({ transaction, collection }) => {
|
|
106
|
+
* const newItem = transaction.mutations[0].modified
|
|
107
|
+
* const result = await api.todos.create({
|
|
108
|
+
* data: newItem
|
|
109
|
+
* })
|
|
110
|
+
* // Wait up to 10 seconds for txid
|
|
111
|
+
* await collection.utils.awaitTxId(result.txid, 10000)
|
|
112
|
+
* }
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* // Insert handler with timeout error handling
|
|
116
|
+
* onInsert: async ({ transaction, collection }) => {
|
|
92
117
|
* const newItem = transaction.mutations[0].modified
|
|
93
118
|
* const result = await api.todos.create({
|
|
94
119
|
* data: newItem
|
|
95
120
|
* })
|
|
96
|
-
*
|
|
121
|
+
*
|
|
122
|
+
* try {
|
|
123
|
+
* await collection.utils.awaitTxId(result.txid, 5000)
|
|
124
|
+
* } catch (error) {
|
|
125
|
+
* // Decide sync timeout policy:
|
|
126
|
+
* // - Throw to rollback optimistic state
|
|
127
|
+
* // - Catch to keep optimistic state (eventual consistency)
|
|
128
|
+
* // - Schedule background retry
|
|
129
|
+
* console.warn('Sync timeout, keeping optimistic state:', error)
|
|
130
|
+
* // Don't throw - allow optimistic state to persist
|
|
131
|
+
* }
|
|
97
132
|
* }
|
|
98
133
|
*
|
|
99
134
|
* @example
|
|
100
|
-
* // Insert handler with multiple items
|
|
101
|
-
* onInsert: async ({ transaction }) => {
|
|
135
|
+
* // Insert handler with multiple items
|
|
136
|
+
* onInsert: async ({ transaction, collection }) => {
|
|
102
137
|
* const items = transaction.mutations.map(m => m.modified)
|
|
103
138
|
* const results = await Promise.all(
|
|
104
139
|
* items.map(item => api.todos.create({ data: item }))
|
|
105
140
|
* )
|
|
106
|
-
*
|
|
141
|
+
* // Wait for all txids to sync
|
|
142
|
+
* await Promise.all(
|
|
143
|
+
* results.map(r => collection.utils.awaitTxId(r.txid))
|
|
144
|
+
* )
|
|
107
145
|
* }
|
|
108
146
|
*
|
|
109
147
|
* @example
|
|
110
|
-
* // Use awaitMatch utility for custom matching
|
|
148
|
+
* // Alternative: Use awaitMatch utility for custom matching logic
|
|
111
149
|
* onInsert: async ({ transaction, collection }) => {
|
|
112
150
|
* const newItem = transaction.mutations[0].modified
|
|
113
151
|
* await api.todos.create({ data: newItem })
|
|
152
|
+
* // Wait for specific change to appear in sync stream
|
|
114
153
|
* await collection.utils.awaitMatch(
|
|
115
154
|
* (message) => isChangeMessage(message) &&
|
|
116
155
|
* message.headers.operation === 'insert' &&
|
|
@@ -121,24 +160,39 @@ export interface ElectricCollectionConfig<T extends Row<unknown> = Row<unknown>,
|
|
|
121
160
|
onInsert?: (params: InsertMutationFnParams<T, string | number, ElectricCollectionUtils<T>>) => Promise<MatchingStrategy>;
|
|
122
161
|
/**
|
|
123
162
|
* Optional asynchronous handler function called before an update operation
|
|
163
|
+
*
|
|
164
|
+
* **IMPORTANT - Electric Synchronization:**
|
|
165
|
+
* This handler **must not resolve** until synchronization is confirmed.
|
|
166
|
+
* Await one of these synchronization utilities before the handler completes:
|
|
167
|
+
* 1. `await collection.utils.awaitTxId(txid)` (recommended for most cases)
|
|
168
|
+
* 2. `await collection.utils.awaitMatch(fn)` for custom matching logic
|
|
169
|
+
*
|
|
170
|
+
* Simply returning without waiting for sync will drop optimistic state too early, causing UI glitches.
|
|
171
|
+
*
|
|
124
172
|
* @param params Object containing transaction and collection information
|
|
125
|
-
* @returns Promise
|
|
173
|
+
* @returns Promise that should resolve after synchronization is complete
|
|
174
|
+
*
|
|
175
|
+
* **Deprecation notice:** Returning `{ txid }` from handlers is deprecated and will be removed in v1.0.
|
|
176
|
+
* Use `await collection.utils.awaitTxId(txid)` within the handler instead.
|
|
177
|
+
*
|
|
126
178
|
* @example
|
|
127
|
-
* //
|
|
128
|
-
* onUpdate: async ({ transaction }) => {
|
|
179
|
+
* // Recommended: Wait for txid to sync
|
|
180
|
+
* onUpdate: async ({ transaction, collection }) => {
|
|
129
181
|
* const { original, changes } = transaction.mutations[0]
|
|
130
182
|
* const result = await api.todos.update({
|
|
131
183
|
* where: { id: original.id },
|
|
132
184
|
* data: changes
|
|
133
185
|
* })
|
|
134
|
-
*
|
|
186
|
+
* // Wait for txid to sync before handler completes
|
|
187
|
+
* await collection.utils.awaitTxId(result.txid)
|
|
135
188
|
* }
|
|
136
189
|
*
|
|
137
190
|
* @example
|
|
138
|
-
* // Use awaitMatch utility for custom matching
|
|
191
|
+
* // Alternative: Use awaitMatch utility for custom matching logic
|
|
139
192
|
* onUpdate: async ({ transaction, collection }) => {
|
|
140
193
|
* const { original, changes } = transaction.mutations[0]
|
|
141
194
|
* await api.todos.update({ where: { id: original.id }, data: changes })
|
|
195
|
+
* // Wait for specific change to appear in sync stream
|
|
142
196
|
* await collection.utils.awaitMatch(
|
|
143
197
|
* (message) => isChangeMessage(message) &&
|
|
144
198
|
* message.headers.operation === 'update' &&
|
|
@@ -149,23 +203,38 @@ export interface ElectricCollectionConfig<T extends Row<unknown> = Row<unknown>,
|
|
|
149
203
|
onUpdate?: (params: UpdateMutationFnParams<T, string | number, ElectricCollectionUtils<T>>) => Promise<MatchingStrategy>;
|
|
150
204
|
/**
|
|
151
205
|
* Optional asynchronous handler function called before a delete operation
|
|
206
|
+
*
|
|
207
|
+
* **IMPORTANT - Electric Synchronization:**
|
|
208
|
+
* This handler **must not resolve** until synchronization is confirmed.
|
|
209
|
+
* Await one of these synchronization utilities before the handler completes:
|
|
210
|
+
* 1. `await collection.utils.awaitTxId(txid)` (recommended for most cases)
|
|
211
|
+
* 2. `await collection.utils.awaitMatch(fn)` for custom matching logic
|
|
212
|
+
*
|
|
213
|
+
* Simply returning without waiting for sync will drop optimistic state too early, causing UI glitches.
|
|
214
|
+
*
|
|
152
215
|
* @param params Object containing transaction and collection information
|
|
153
|
-
* @returns Promise
|
|
216
|
+
* @returns Promise that should resolve after synchronization is complete
|
|
217
|
+
*
|
|
218
|
+
* **Deprecation notice:** Returning `{ txid }` from handlers is deprecated and will be removed in v1.0.
|
|
219
|
+
* Use `await collection.utils.awaitTxId(txid)` within the handler instead.
|
|
220
|
+
*
|
|
154
221
|
* @example
|
|
155
|
-
* //
|
|
156
|
-
* onDelete: async ({ transaction }) => {
|
|
222
|
+
* // Recommended: Wait for txid to sync
|
|
223
|
+
* onDelete: async ({ transaction, collection }) => {
|
|
157
224
|
* const mutation = transaction.mutations[0]
|
|
158
225
|
* const result = await api.todos.delete({
|
|
159
226
|
* id: mutation.original.id
|
|
160
227
|
* })
|
|
161
|
-
*
|
|
228
|
+
* // Wait for txid to sync before handler completes
|
|
229
|
+
* await collection.utils.awaitTxId(result.txid)
|
|
162
230
|
* }
|
|
163
231
|
*
|
|
164
232
|
* @example
|
|
165
|
-
* // Use awaitMatch utility for custom matching
|
|
233
|
+
* // Alternative: Use awaitMatch utility for custom matching logic
|
|
166
234
|
* onDelete: async ({ transaction, collection }) => {
|
|
167
235
|
* const mutation = transaction.mutations[0]
|
|
168
236
|
* await api.todos.delete({ id: mutation.original.id })
|
|
237
|
+
* // Wait for specific change to appear in sync stream
|
|
169
238
|
* await collection.utils.awaitMatch(
|
|
170
239
|
* (message) => isChangeMessage(message) &&
|
|
171
240
|
* message.headers.operation === 'delete' &&
|
|
@@ -186,7 +255,7 @@ export type AwaitMatchFn<T extends Row<unknown>> = (matchFn: MatchFunction<T>, t
|
|
|
186
255
|
/**
|
|
187
256
|
* Electric collection utilities type
|
|
188
257
|
*/
|
|
189
|
-
export interface ElectricCollectionUtils<T extends Row<unknown> = Row<unknown>>
|
|
258
|
+
export interface ElectricCollectionUtils<T extends Row<unknown> = Row<unknown>> {
|
|
190
259
|
awaitTxId: AwaitTxIdFn;
|
|
191
260
|
awaitMatch: AwaitMatchFn<T>;
|
|
192
261
|
}
|