@electric-sql/client 0.6.1 → 0.6.3

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
@@ -17,6 +17,9 @@ import {
17
17
  } from './fetch'
18
18
  import {
19
19
  CHUNK_LAST_OFFSET_HEADER,
20
+ LIVE_CACHE_BUSTER_HEADER,
21
+ LIVE_CACHE_BUSTER_QUERY_PARAM,
22
+ COLUMNS_QUERY_PARAM,
20
23
  LIVE_QUERY_PARAM,
21
24
  OFFSET_QUERY_PARAM,
22
25
  SHAPE_ID_HEADER,
@@ -35,9 +38,16 @@ export interface ShapeStreamOptions<T = never> {
35
38
  */
36
39
  url: string
37
40
  /**
38
- * where clauses for the shape.
41
+ * The where clauses for the shape.
39
42
  */
40
43
  where?: string
44
+
45
+ /**
46
+ * The columns to include in the shape.
47
+ * Must include primary keys, and can only inlude valid columns.
48
+ */
49
+ columns?: string[]
50
+
41
51
  /**
42
52
  * The "offset" on the shape log. This is typically not set as the ShapeStream
43
53
  * will handle this automatically. A common scenario where you might pass an offset
@@ -143,6 +153,7 @@ export class ShapeStream<T extends Row<unknown> = Row>
143
153
  >()
144
154
 
145
155
  #lastOffset: Offset
156
+ #liveCacheBuster: string // Seconds since our Electric Epoch 😎
146
157
  #lastSyncedAt?: number // unix time
147
158
  #isUpToDate: boolean = false
148
159
  #connected: boolean = false
@@ -153,6 +164,7 @@ export class ShapeStream<T extends Row<unknown> = Row>
153
164
  validateOptions(options)
154
165
  this.options = { subscribe: true, ...options }
155
166
  this.#lastOffset = this.options.offset ?? `-1`
167
+ this.#liveCacheBuster = ``
156
168
  this.#shapeId = this.options.shapeId
157
169
  this.#messageParser = new MessageParser<T>(options.parser)
158
170
 
@@ -184,7 +196,7 @@ export class ShapeStream<T extends Row<unknown> = Row>
184
196
  async start() {
185
197
  this.#isUpToDate = false
186
198
 
187
- const { url, where, signal } = this.options
199
+ const { url, where, columns, signal } = this.options
188
200
 
189
201
  try {
190
202
  while (
@@ -193,10 +205,16 @@ export class ShapeStream<T extends Row<unknown> = Row>
193
205
  ) {
194
206
  const fetchUrl = new URL(url)
195
207
  if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where)
208
+ if (columns && columns.length > 0)
209
+ fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`))
196
210
  fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, this.#lastOffset)
197
211
 
198
212
  if (this.#isUpToDate) {
199
213
  fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`)
214
+ fetchUrl.searchParams.set(
215
+ LIVE_CACHE_BUSTER_QUERY_PARAM,
216
+ this.#liveCacheBuster
217
+ )
200
218
  }
201
219
 
202
220
  if (this.#shapeId) {
@@ -248,6 +266,11 @@ export class ShapeStream<T extends Row<unknown> = Row>
248
266
  this.#lastOffset = lastOffset as Offset
249
267
  }
250
268
 
269
+ const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER)
270
+ if (liveCacheBuster) {
271
+ this.#liveCacheBuster = liveCacheBuster
272
+ }
273
+
251
274
  const getSchema = (): Schema => {
252
275
  const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER)
253
276
  return schemaHeader ? JSON.parse(schemaHeader) : {}
@@ -376,6 +399,7 @@ export class ShapeStream<T extends Row<unknown> = Row>
376
399
  */
377
400
  #reset(shapeId?: string) {
378
401
  this.#lastOffset = `-1`
402
+ this.#liveCacheBuster = ``
379
403
  this.#shapeId = shapeId
380
404
  this.#isUpToDate = false
381
405
  this.#connected = false
package/src/constants.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  export const SHAPE_ID_HEADER = `electric-shape-id`
2
+ export const LIVE_CACHE_BUSTER_HEADER = `electric-next-cursor`
3
+ export const LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`
2
4
  export const CHUNK_LAST_OFFSET_HEADER = `electric-chunk-last-offset`
3
5
  export const CHUNK_UP_TO_DATE_HEADER = `electric-chunk-up-to-date`
4
6
  export const SHAPE_SCHEMA_HEADER = `electric-schema`
5
7
  export const SHAPE_ID_QUERY_PARAM = `shape_id`
6
8
  export const OFFSET_QUERY_PARAM = `offset`
7
9
  export const WHERE_QUERY_PARAM = `where`
10
+ export const COLUMNS_QUERY_PARAM = `columns`
8
11
  export const LIVE_QUERY_PARAM = `live`
package/src/fetch.ts CHANGED
@@ -8,6 +8,10 @@ import {
8
8
  } from './constants'
9
9
  import { FetchError, FetchBackoffAbortError } from './error'
10
10
 
11
+ // Some specific 4xx and 5xx HTTP status codes that we definitely
12
+ // want to retry
13
+ const HTTP_RETRY_STATUS_CODES = [429]
14
+
11
15
  export interface BackoffOptions {
12
16
  /**
13
17
  * Initial delay before retrying in milliseconds
@@ -63,6 +67,7 @@ export function createFetchWithBackoff(
63
67
  throw new FetchBackoffAbortError()
64
68
  } else if (
65
69
  e instanceof FetchError &&
70
+ !HTTP_RETRY_STATUS_CODES.includes(e.status) &&
66
71
  e.status >= 400 &&
67
72
  e.status < 500
68
73
  ) {