@electric-sql/client 1.2.2 → 1.3.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@electric-sql/client",
3
3
  "description": "Postgres everywhere - your data, in sync, wherever you need it.",
4
- "version": "1.2.2",
4
+ "version": "1.3.0",
5
5
  "author": "ElectricSQL team and contributors.",
6
6
  "bugs": {
7
7
  "url": "https://github.com/electric-sql/electric/issues"
package/src/client.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  GetExtensions,
8
8
  ChangeMessage,
9
9
  SnapshotMetadata,
10
+ SubsetParams,
10
11
  } from './types'
11
12
  import { MessageParser, Parser, TransformFunction } from './parser'
12
13
  import {
@@ -132,14 +133,6 @@ export type ExternalParamsRecord<T extends Row<unknown> = Row> = {
132
133
  [K in string]: ParamValue | undefined
133
134
  } & Partial<PostgresParams<T>> & { [K in ReservedParamKeys]?: never }
134
135
 
135
- export type SubsetParams = {
136
- where?: string
137
- params?: Record<string, string>
138
- limit?: number
139
- offset?: number
140
- orderBy?: string
141
- }
142
-
143
136
  type ReservedParamKeys =
144
137
  | typeof LIVE_CACHE_BUSTER_QUERY_PARAM
145
138
  | typeof SHAPE_HANDLE_QUERY_PARAM
@@ -731,7 +724,6 @@ export class ShapeStream<T extends Row<unknown> = Row>
731
724
  async #requestShape(): Promise<void> {
732
725
  if (this.#state === `pause-requested`) {
733
726
  this.#state = `paused`
734
-
735
727
  return
736
728
  }
737
729
 
@@ -1259,6 +1251,13 @@ export class ShapeStream<T extends Row<unknown> = Row>
1259
1251
  this.#started &&
1260
1252
  (this.#state === `paused` || this.#state === `pause-requested`)
1261
1253
  ) {
1254
+ // Don't resume if the user's signal is already aborted
1255
+ // This can happen if the signal was aborted while we were paused
1256
+ // (e.g., TanStack DB collection was GC'd)
1257
+ if (this.options.signal?.aborted) {
1258
+ return
1259
+ }
1260
+
1262
1261
  // If we're resuming from pause-requested state, we need to set state back to active
1263
1262
  // to prevent the pause from completing
1264
1263
  if (this.#state === `pause-requested`) {
@@ -1480,6 +1479,7 @@ export class ShapeStream<T extends Row<unknown> = Row>
1480
1479
 
1481
1480
  const dataWithEndBoundary = (data as Array<Message<T>>).concat([
1482
1481
  { headers: { control: `snapshot-end`, ...metadata } },
1482
+ { headers: { control: `subset-end`, ...opts } },
1483
1483
  ])
1484
1484
 
1485
1485
  this.#snapshotTracker.addSnapshot(
package/src/fetch.ts CHANGED
@@ -221,7 +221,7 @@ export function createFetchWithChunkBuffer(
221
221
  ): typeof fetch {
222
222
  const { maxChunksToPrefetch } = prefetchOptions
223
223
 
224
- let prefetchQueue: PrefetchQueue
224
+ let prefetchQueue: PrefetchQueue | undefined
225
225
 
226
226
  const prefetchClient = async (...args: Parameters<typeof fetchClient>) => {
227
227
  const url = args[0].toString()
@@ -233,7 +233,10 @@ export function createFetchWithChunkBuffer(
233
233
  return prefetchedRequest
234
234
  }
235
235
 
236
+ // Clear the prefetch queue after aborting to prevent returning
237
+ // stale/aborted requests on future calls with the same URL
236
238
  prefetchQueue?.abort()
239
+ prefetchQueue = undefined
237
240
 
238
241
  // perform request and fire off prefetch queue if request is eligible
239
242
  const response = await fetchClient(...args)
@@ -340,16 +343,24 @@ class PrefetchQueue {
340
343
 
341
344
  abort(): void {
342
345
  this.#prefetchQueue.forEach(([_, aborter]) => aborter.abort())
346
+ this.#prefetchQueue.clear()
343
347
  }
344
348
 
345
349
  consume(...args: Parameters<typeof fetch>): Promise<Response> | void {
346
350
  const url = args[0].toString()
347
351
 
348
- const request = this.#prefetchQueue.get(url)?.[0]
352
+ const entry = this.#prefetchQueue.get(url)
349
353
  // only consume if request is in queue and is the queue "head"
350
354
  // if request is in the queue but not the head, the queue is being
351
355
  // consumed out of order and should be restarted
352
- if (!request || url !== this.#queueHeadUrl) return
356
+ if (!entry || url !== this.#queueHeadUrl) return
357
+
358
+ const [request, aborter] = entry
359
+ // Don't return aborted requests - they will reject with AbortError
360
+ if (aborter.signal.aborted) {
361
+ this.#prefetchQueue.delete(url)
362
+ return
363
+ }
353
364
  this.#prefetchQueue.delete(url)
354
365
 
355
366
  // fire off new prefetch since request has been consumed
package/src/helpers.ts CHANGED
@@ -66,11 +66,9 @@ export function isUpToDateMessage<T extends Row<unknown> = Row>(
66
66
  * If we are not in SSE mode this function will return undefined.
67
67
  */
68
68
  export function getOffset(message: ControlMessage): Offset | undefined {
69
+ if (message.headers.control != `up-to-date`) return
69
70
  const lsn = message.headers.global_last_seen_lsn
70
- if (!lsn) {
71
- return
72
- }
73
- return `${lsn}_0` as Offset
71
+ return lsn ? (`${lsn}_0` as Offset) : undefined
74
72
  }
75
73
 
76
74
  /**
package/src/types.ts CHANGED
@@ -68,6 +68,14 @@ export type MoveTag = string
68
68
  */
69
69
  export type MoveOutPattern = { pos: number; value: string }
70
70
 
71
+ export type SubsetParams = {
72
+ where?: string
73
+ params?: Record<string, string>
74
+ limit?: number
75
+ offset?: number
76
+ orderBy?: string
77
+ }
78
+
71
79
  export type ControlMessage = {
72
80
  headers:
73
81
  | (Header & {
@@ -75,6 +83,7 @@ export type ControlMessage = {
75
83
  global_last_seen_lsn?: string
76
84
  })
77
85
  | (Header & { control: `snapshot-end` } & PostgresSnapshot)
86
+ | (Header & { control: `subset-end` } & SubsetParams)
78
87
  }
79
88
 
80
89
  export type EventMessage = {