@electric-sql/client 1.5.5 → 1.5.6

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.5.5",
4
+ "version": "1.5.6",
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
@@ -1342,7 +1342,18 @@ export class ShapeStream<T extends Row<unknown> = Row>
1342
1342
  // #start handles it correctly.
1343
1343
  throw new FetchBackoffAbortError()
1344
1344
  }
1345
- throw error
1345
+ // Re-throw known Electric errors so the caller can handle them
1346
+ // (e.g., 409 shape rotation, stale cache retry, missing headers).
1347
+ // Other errors (body parsing, SSE protocol failures, null body)
1348
+ // are SSE connection failures handled by the fallback mechanism
1349
+ // in the finally block below.
1350
+ if (
1351
+ error instanceof FetchError ||
1352
+ error instanceof StaleCacheError ||
1353
+ error instanceof MissingHeadersError
1354
+ ) {
1355
+ throw error
1356
+ }
1346
1357
  } finally {
1347
1358
  // Check if the SSE connection closed too quickly
1348
1359
  // This can happen when responses are cached or when the proxy/server
package/src/fetch.ts CHANGED
@@ -226,6 +226,17 @@ export function createFetchWithChunkBuffer(
226
226
 
227
227
  const prefetchClient = async (...args: Parameters<typeof fetchClient>) => {
228
228
  const url = args[0].toString()
229
+ const method = getRequestMethod(args[0], args[1])
230
+
231
+ // Prefetch is only valid for GET requests. The prefetch queue matches
232
+ // requests by URL alone and ignores HTTP method/body, so a POST request
233
+ // with the same URL would incorrectly consume the prefetched stream
234
+ // response instead of making its own request.
235
+ if (method !== `GET`) {
236
+ prefetchQueue?.abort()
237
+ prefetchQueue = undefined
238
+ return fetchClient(...args)
239
+ }
229
240
 
230
241
  // try to consume from the prefetch queue first, and if request is
231
242
  // not present abort the prefetch queue as it must no longer be valid
@@ -494,3 +505,18 @@ function chainAborter(
494
505
  }
495
506
 
496
507
  function noop() {}
508
+
509
+ function getRequestMethod(
510
+ input: Parameters<typeof fetch>[0],
511
+ init?: Parameters<typeof fetch>[1]
512
+ ): string {
513
+ if (init?.method) {
514
+ return init.method.toUpperCase()
515
+ }
516
+
517
+ if (typeof Request !== `undefined` && input instanceof Request) {
518
+ return input.method.toUpperCase()
519
+ }
520
+
521
+ return `GET`
522
+ }
@@ -380,6 +380,19 @@ abstract class FetchingState extends ActiveState {
380
380
  if (staleResult) return staleResult
381
381
 
382
382
  const shared = this.parseResponseFields(input)
383
+
384
+ // NOTE: 204s are deprecated, the Electric server should not send these
385
+ // in latest versions but this is here for backwards compatibility.
386
+ // A 204 means "no content, you're caught up" — transition to live.
387
+ // Skip SSE detection: a 204 gives no indication SSE will work, and
388
+ // the 3-attempt fallback cycle adds unnecessary latency.
389
+ if (input.status === 204) {
390
+ return {
391
+ action: `accepted`,
392
+ state: new LiveState(shared, { sseFallbackToLongPolling: true }),
393
+ }
394
+ }
395
+
383
396
  return { action: `accepted`, state: new SyncingState(shared) }
384
397
  }
385
398