@electric-sql/client 1.0.14 → 1.1.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/src/client.ts CHANGED
@@ -44,6 +44,7 @@ import {
44
44
  FORCE_DISCONNECT_AND_REFRESH,
45
45
  PAUSE_STREAM,
46
46
  EXPERIMENTAL_LIVE_SSE_QUERY_PARAM,
47
+ LIVE_SSE_QUERY_PARAM,
47
48
  ELECTRIC_PROTOCOL_QUERY_PARAMS,
48
49
  LOG_MODE_QUERY_PARAM,
49
50
  SUBSET_PARAM_WHERE,
@@ -273,10 +274,15 @@ export interface ShapeStreamOptions<T = never> {
273
274
  subscribe?: boolean
274
275
 
275
276
  /**
276
- * Experimental support for Server-Sent Events (SSE) for live updates.
277
+ * @deprecated No longer experimental, use {@link liveSse} instead.
277
278
  */
278
279
  experimentalLiveSse?: boolean
279
280
 
281
+ /**
282
+ * Use Server-Sent Events (SSE) for live updates.
283
+ */
284
+ liveSse?: boolean
285
+
280
286
  /**
281
287
  * Initial data loading mode
282
288
  */
@@ -372,7 +378,7 @@ function canonicalShapeKey(url: URL): string {
372
378
  * ```
373
379
  * const stream = new ShapeStream({
374
380
  * url: `http://localhost:3000/v1/shape`,
375
- * experimentalLiveSse: true
381
+ * liveSse: true
376
382
  * })
377
383
  * ```
378
384
  *
@@ -598,7 +604,13 @@ export class ShapeStream<T extends Row<unknown> = Row>
598
604
  const newShapeHandle =
599
605
  e.headers[SHAPE_HANDLE_HEADER] || `${this.#shapeHandle!}-next`
600
606
  this.#reset(newShapeHandle)
601
- await this.#publish(e.json as Message<T>[])
607
+
608
+ // must refetch control message might be in a list or not depending
609
+ // on whether it came from an SSE request or long poll - handle both
610
+ // cases for safety here but worth revisiting 409 handling
611
+ await this.#publish(
612
+ (Array.isArray(e.json) ? e.json : [e.json]) as Message<T>[]
613
+ )
602
614
  return this.#requestShape()
603
615
  } else {
604
616
  // Notify subscribers
@@ -815,13 +827,15 @@ export class ShapeStream<T extends Row<unknown> = Row>
815
827
  headers: Record<string, string>
816
828
  resumingFromPause?: boolean
817
829
  }): Promise<void> {
830
+ const useSse = this.options.liveSse ?? this.options.experimentalLiveSse
818
831
  if (
819
832
  this.#isUpToDate &&
820
- this.options.experimentalLiveSse &&
833
+ useSse &&
821
834
  !this.#isRefreshing &&
822
835
  !opts.resumingFromPause
823
836
  ) {
824
837
  opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`)
838
+ opts.fetchUrl.searchParams.set(LIVE_SSE_QUERY_PARAM, `true`)
825
839
  return this.#requestShapeSSE(opts)
826
840
  }
827
841
 
package/src/constants.ts CHANGED
@@ -13,7 +13,11 @@ export const TABLE_QUERY_PARAM = `table`
13
13
  export const WHERE_QUERY_PARAM = `where`
14
14
  export const REPLICA_PARAM = `replica`
15
15
  export const WHERE_PARAMS_PARAM = `params`
16
+ /**
17
+ * @deprecated Use {@link LIVE_SSE_QUERY_PARAM} instead.
18
+ */
16
19
  export const EXPERIMENTAL_LIVE_SSE_QUERY_PARAM = `experimental_live_sse`
20
+ export const LIVE_SSE_QUERY_PARAM = `live_sse`
17
21
  export const FORCE_DISCONNECT_AND_REFRESH = `force-disconnect-and-refresh`
18
22
  export const PAUSE_STREAM = `pause-stream`
19
23
  export const LOG_MODE_QUERY_PARAM = `log`
package/src/fetch.ts CHANGED
@@ -59,13 +59,7 @@ export function createFetchWithBackoff(
59
59
  let delay = initialDelay
60
60
  let attempt = 0
61
61
 
62
- /* eslint-disable no-constant-condition -- we re-fetch the shape log
63
- * continuously until we get a non-ok response. For recoverable errors,
64
- * we retry the fetch with exponential backoff. Users can pass in an
65
- * AbortController to abort the fetching an any point.
66
- * */
67
62
  while (true) {
68
- /* eslint-enable no-constant-condition */
69
63
  try {
70
64
  const result = await fetchClient(...args)
71
65
  if (result.ok) return result
@@ -118,6 +112,10 @@ export function createFetchWithConsumedMessages(fetchClient: typeof fetch) {
118
112
  const text = await res.text()
119
113
  return new Response(text, res)
120
114
  } catch (err) {
115
+ if (args[1]?.signal?.aborted) {
116
+ throw new FetchBackoffAbortError()
117
+ }
118
+
121
119
  throw new FetchError(
122
120
  res.status,
123
121
  undefined,