Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .changeset/add-timeout-support-electric.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
"@tanstack/electric-db-collection": minor
---

Add timeout support to electricCollectionOptions matching strategies. You can now specify a custom timeout when returning txids from mutation handlers (onInsert, onUpdate, onDelete).

Previously, users could only customize timeouts when manually calling `collection.utils.awaitTxId()`, but not when using the automatic txid matching strategy.

**Example:**

```ts
const collection = createCollection(
electricCollectionOptions({
// ... other config
onInsert: async ({ transaction }) => {
const newItem = transaction.mutations[0].modified
const result = await api.todos.create({ data: newItem })
// Specify custom timeout (in milliseconds)
return { txid: result.txid, timeout: 10000 }
},
})
)
```

The timeout parameter is optional and defaults to 5000ms when not specified. It works with both single txids and arrays of txids.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: ElectricDBCollectionError

# Class: ElectricDBCollectionError

Defined in: [packages/electric-db-collection/src/errors.ts:4](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L4)
Defined in: packages/electric-db-collection/src/errors.ts:4

## Extends

Expand All @@ -26,7 +26,7 @@ Defined in: [packages/electric-db-collection/src/errors.ts:4](https://github.com
new ElectricDBCollectionError(message, collectionId?): ElectricDBCollectionError;
```

Defined in: [packages/electric-db-collection/src/errors.ts:5](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L5)
Defined in: packages/electric-db-collection/src/errors.ts:5

#### Parameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: ExpectedNumberInAwaitTxIdError

# Class: ExpectedNumberInAwaitTxIdError

Defined in: [packages/electric-db-collection/src/errors.ts:11](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L11)
Defined in: packages/electric-db-collection/src/errors.ts:11

## Extends

Expand All @@ -19,7 +19,7 @@ Defined in: [packages/electric-db-collection/src/errors.ts:11](https://github.co
new ExpectedNumberInAwaitTxIdError(txIdType, collectionId?): ExpectedNumberInAwaitTxIdError;
```

Defined in: [packages/electric-db-collection/src/errors.ts:12](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L12)
Defined in: packages/electric-db-collection/src/errors.ts:12

#### Parameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: StreamAbortedError

# Class: StreamAbortedError

Defined in: [packages/electric-db-collection/src/errors.ts:32](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L32)
Defined in: packages/electric-db-collection/src/errors.ts:32

## Extends

Expand All @@ -19,7 +19,7 @@ Defined in: [packages/electric-db-collection/src/errors.ts:32](https://github.co
new StreamAbortedError(collectionId?): StreamAbortedError;
```

Defined in: [packages/electric-db-collection/src/errors.ts:33](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L33)
Defined in: packages/electric-db-collection/src/errors.ts:33

#### Parameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: TimeoutWaitingForMatchError

# Class: TimeoutWaitingForMatchError

Defined in: [packages/electric-db-collection/src/errors.ts:25](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L25)
Defined in: packages/electric-db-collection/src/errors.ts:25

## Extends

Expand All @@ -19,7 +19,7 @@ Defined in: [packages/electric-db-collection/src/errors.ts:25](https://github.co
new TimeoutWaitingForMatchError(collectionId?): TimeoutWaitingForMatchError;
```

Defined in: [packages/electric-db-collection/src/errors.ts:26](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L26)
Defined in: packages/electric-db-collection/src/errors.ts:26

#### Parameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: TimeoutWaitingForTxIdError

# Class: TimeoutWaitingForTxIdError

Defined in: [packages/electric-db-collection/src/errors.ts:18](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L18)
Defined in: packages/electric-db-collection/src/errors.ts:18

## Extends

Expand All @@ -19,7 +19,7 @@ Defined in: [packages/electric-db-collection/src/errors.ts:18](https://github.co
new TimeoutWaitingForTxIdError(txId, collectionId?): TimeoutWaitingForTxIdError;
```

Defined in: [packages/electric-db-collection/src/errors.ts:19](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/errors.ts#L19)
Defined in: packages/electric-db-collection/src/errors.ts:19

#### Parameters

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ title: electricCollectionOptions
function electricCollectionOptions<T>(config): CollectionConfig<InferSchemaOutput<T>, string | number, T, UtilsRecord> & object;
```

Defined in: [packages/electric-db-collection/src/electric.ts:277](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L277)
Defined in: packages/electric-db-collection/src/electric.ts:293

Creates Electric collection options for use with a standard Collection

Expand Down Expand Up @@ -43,7 +43,7 @@ Collection options with utilities
function electricCollectionOptions<T>(config): CollectionConfig<T, string | number, never, UtilsRecord> & object;
```

Defined in: [packages/electric-db-collection/src/electric.ts:288](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L288)
Defined in: packages/electric-db-collection/src/electric.ts:304

Creates Electric collection options for use with a standard Collection

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: ElectricCollectionConfig

# Interface: ElectricCollectionConfig\<T, TSchema\>

Defined in: [packages/electric-db-collection/src/electric.ts:102](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L102)
Defined in: packages/electric-db-collection/src/electric.ts:108

Configuration interface for Electric collection options

Expand Down Expand Up @@ -35,7 +35,7 @@ The schema type for validation
optional onDelete: (params) => Promise<MatchingStrategy>;
```

Defined in: [packages/electric-db-collection/src/electric.ts:208](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L208)
Defined in: packages/electric-db-collection/src/electric.ts:224

Optional asynchronous handler function called before a delete operation

Expand All @@ -51,7 +51,7 @@ Object containing transaction and collection information

`Promise`\<`MatchingStrategy`\>

Promise resolving to { txid } or void
Promise resolving to { txid, timeout? } or void

#### Examples

Expand Down Expand Up @@ -87,7 +87,7 @@ onDelete: async ({ transaction, collection }) => {
optional onInsert: (params) => Promise<MatchingStrategy>;
```

Defined in: [packages/electric-db-collection/src/electric.ts:151](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L151)
Defined in: packages/electric-db-collection/src/electric.ts:167

Optional asynchronous handler function called before an insert operation

Expand All @@ -103,7 +103,7 @@ Object containing transaction and collection information

`Promise`\<`MatchingStrategy`\>

Promise resolving to { txid } or void
Promise resolving to { txid, timeout? } or void

#### Examples

Expand All @@ -118,6 +118,17 @@ onInsert: async ({ transaction }) => {
}
```

```ts
// Insert handler with custom timeout
onInsert: async ({ transaction }) => {
const newItem = transaction.mutations[0].modified
const result = await api.todos.create({
data: newItem
})
return { txid: result.txid, timeout: 10000 } // Wait up to 10 seconds
}
```

```ts
// Insert handler with multiple items - return array of txids
onInsert: async ({ transaction }) => {
Expand Down Expand Up @@ -150,7 +161,7 @@ onInsert: async ({ transaction, collection }) => {
optional onUpdate: (params) => Promise<MatchingStrategy>;
```

Defined in: [packages/electric-db-collection/src/electric.ts:180](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L180)
Defined in: packages/electric-db-collection/src/electric.ts:196

Optional asynchronous handler function called before an update operation

Expand All @@ -166,7 +177,7 @@ Object containing transaction and collection information

`Promise`\<`MatchingStrategy`\>

Promise resolving to { txid } or void
Promise resolving to { txid, timeout? } or void

#### Examples

Expand Down Expand Up @@ -203,7 +214,7 @@ onUpdate: async ({ transaction, collection }) => {
shapeOptions: ShapeStreamOptions<GetExtensions<T>>;
```

Defined in: [packages/electric-db-collection/src/electric.ts:112](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L112)
Defined in: packages/electric-db-collection/src/electric.ts:118

Configuration options for the ElectricSQL ShapeStream

Expand All @@ -215,4 +226,4 @@ Configuration options for the ElectricSQL ShapeStream
optional syncMode: ElectricSyncMode;
```

Defined in: [packages/electric-db-collection/src/electric.ts:113](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L113)
Defined in: packages/electric-db-collection/src/electric.ts:119
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: ElectricCollectionUtils

# Interface: ElectricCollectionUtils\<T\>

Defined in: [packages/electric-db-collection/src/electric.ts:260](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L260)
Defined in: packages/electric-db-collection/src/electric.ts:276

Electric collection utilities type

Expand Down Expand Up @@ -33,7 +33,7 @@ Electric collection utilities type
awaitMatch: AwaitMatchFn<T>;
```

Defined in: [packages/electric-db-collection/src/electric.ts:263](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L263)
Defined in: packages/electric-db-collection/src/electric.ts:279

***

Expand All @@ -43,4 +43,4 @@ Defined in: [packages/electric-db-collection/src/electric.ts:263](https://github
awaitTxId: AwaitTxIdFn;
```

Defined in: [packages/electric-db-collection/src/electric.ts:262](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L262)
Defined in: packages/electric-db-collection/src/electric.ts:278
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ title: AwaitTxIdFn
type AwaitTxIdFn = (txId, timeout?) => Promise<boolean>;
```

Defined in: [packages/electric-db-collection/src/electric.ts:247](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L247)
Defined in: packages/electric-db-collection/src/electric.ts:263

Type for the awaitTxId utility function

Expand Down
2 changes: 1 addition & 1 deletion docs/reference/electric-db-collection/type-aliases/Txid.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ title: Txid
type Txid = number;
```

Defined in: [packages/electric-db-collection/src/electric.ts:46](https://github.com/TanStack/db/blob/main/packages/electric-db-collection/src/electric.ts#L46)
Defined in: packages/electric-db-collection/src/electric.ts:46

Type representing a transaction ID in ElectricSQL
31 changes: 24 additions & 7 deletions packages/electric-db-collection/src/electric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,16 @@ export type MatchFunction<T extends Row<unknown>> = (
/**
* Matching strategies for Electric synchronization
* Handlers can return:
* - Txid strategy: { txid: number | number[] } (recommended)
* - Txid strategy: { txid: number | number[], timeout?: number } (recommended)
* - Void (no return value) - mutation completes without waiting
*
* The optional timeout property specifies how long to wait for the txid(s) in milliseconds.
* If not specified, defaults to 5000ms.
*/
export type MatchingStrategy = { txid: Txid | Array<Txid> } | void
export type MatchingStrategy = {
txid: Txid | Array<Txid>
timeout?: number
} | void

/**
* Type representing a snapshot end message
Expand Down Expand Up @@ -115,7 +121,7 @@ export interface ElectricCollectionConfig<
/**
* Optional asynchronous handler function called before an insert operation
* @param params Object containing transaction and collection information
* @returns Promise resolving to { txid } or void
* @returns Promise resolving to { txid, timeout? } or void
* @example
* // Basic Electric insert handler with txid (recommended)
* onInsert: async ({ transaction }) => {
Expand All @@ -127,6 +133,16 @@ export interface ElectricCollectionConfig<
* }
*
* @example
* // Insert handler with custom timeout
* onInsert: async ({ transaction }) => {
* const newItem = transaction.mutations[0].modified
* const result = await api.todos.create({
* data: newItem
* })
* return { txid: result.txid, timeout: 10000 } // Wait up to 10 seconds
* }
*
* @example
* // Insert handler with multiple items - return array of txids
* onInsert: async ({ transaction }) => {
* const items = transaction.mutations.map(m => m.modified)
Expand All @@ -153,7 +169,7 @@ export interface ElectricCollectionConfig<
/**
* Optional asynchronous handler function called before an update operation
* @param params Object containing transaction and collection information
* @returns Promise resolving to { txid } or void
* @returns Promise resolving to { txid, timeout? } or void
* @example
* // Basic Electric update handler with txid (recommended)
* onUpdate: async ({ transaction }) => {
Expand Down Expand Up @@ -182,7 +198,7 @@ export interface ElectricCollectionConfig<
/**
* Optional asynchronous handler function called before a delete operation
* @param params Object containing transaction and collection information
* @returns Promise resolving to { txid } or void
* @returns Promise resolving to { txid, timeout? } or void
* @example
* // Basic Electric delete handler with txid (recommended)
* onDelete: async ({ transaction }) => {
Expand Down Expand Up @@ -531,11 +547,12 @@ export function electricCollectionOptions(
): Promise<void> => {
// Only wait if result contains txid
if (result && `txid` in result) {
const timeout = result.timeout
// Handle both single txid and array of txids
if (Array.isArray(result.txid)) {
await Promise.all(result.txid.map((txid) => awaitTxId(txid)))
await Promise.all(result.txid.map((txid) => awaitTxId(txid, timeout)))
} else {
await awaitTxId(result.txid)
await awaitTxId(result.txid, timeout)
}
}
// If result is void/undefined, don't wait - mutation completes immediately
Expand Down
31 changes: 31 additions & 0 deletions packages/electric-db-collection/tests/electric.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,37 @@ describe(`Electric Integration`, () => {
expect(onInsert).toHaveBeenCalled()
})

it(`should support custom timeout in matching strategy`, async () => {
const onInsert = vi.fn(async () => {
// Return a txid that will never arrive with a very short timeout
return { txid: 999999, timeout: 100 }
})

const config = {
id: `test-custom-timeout`,
shapeOptions: {
url: `http://test-url`,
params: { table: `test_table` },
},
startSync: true,
getKey: (item: Row) => item.id as number,
onInsert,
}

const testCollection = createCollection(electricCollectionOptions(config))

// Insert data - should timeout after 100ms
const tx = testCollection.insert({ id: 1, name: `Timeout Test` })

// Verify that our onInsert handler was called
expect(onInsert).toHaveBeenCalled()

// The transaction should reject due to timeout
await expect(tx.isPersisted.promise).rejects.toThrow(
`Timeout waiting for txId: 999999`
)
})

it(`should handle array of txids returned from handler`, async () => {
// Create a fake backend that returns multiple txids
const fakeBackend = {
Expand Down
Loading