@electric-sql/client 0.7.3 → 0.9.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/dist/index.mjs CHANGED
@@ -58,6 +58,91 @@ var __async = (__this, __arguments, generator) => {
58
58
  });
59
59
  };
60
60
 
61
+ // src/error.ts
62
+ var FetchError = class _FetchError extends Error {
63
+ constructor(status, text, json, headers, url, message) {
64
+ super(
65
+ message || `HTTP Error ${status} at ${url}: ${text != null ? text : JSON.stringify(json)}`
66
+ );
67
+ this.url = url;
68
+ this.name = `FetchError`;
69
+ this.status = status;
70
+ this.text = text;
71
+ this.json = json;
72
+ this.headers = headers;
73
+ }
74
+ static fromResponse(response, url) {
75
+ return __async(this, null, function* () {
76
+ const status = response.status;
77
+ const headers = Object.fromEntries([...response.headers.entries()]);
78
+ let text = void 0;
79
+ let json = void 0;
80
+ const contentType = response.headers.get(`content-type`);
81
+ if (contentType && contentType.includes(`application/json`)) {
82
+ json = yield response.json();
83
+ } else {
84
+ text = yield response.text();
85
+ }
86
+ return new _FetchError(status, text, json, headers, url);
87
+ });
88
+ }
89
+ };
90
+ var FetchBackoffAbortError = class extends Error {
91
+ constructor() {
92
+ super(`Fetch with backoff aborted`);
93
+ this.name = `FetchBackoffAbortError`;
94
+ }
95
+ };
96
+ var MissingShapeUrlError = class extends Error {
97
+ constructor() {
98
+ super(`Invalid shape options: missing required url parameter`);
99
+ this.name = `MissingShapeUrlError`;
100
+ }
101
+ };
102
+ var InvalidSignalError = class extends Error {
103
+ constructor() {
104
+ super(`Invalid signal option. It must be an instance of AbortSignal.`);
105
+ this.name = `InvalidSignalError`;
106
+ }
107
+ };
108
+ var MissingShapeHandleError = class extends Error {
109
+ constructor() {
110
+ super(
111
+ `shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`
112
+ );
113
+ this.name = `MissingShapeHandleError`;
114
+ }
115
+ };
116
+ var ReservedParamError = class extends Error {
117
+ constructor(reservedParams) {
118
+ super(
119
+ `Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
120
+ );
121
+ this.name = `ReservedParamError`;
122
+ }
123
+ };
124
+ var ParserNullValueError = class extends Error {
125
+ constructor(columnName) {
126
+ super(`Column "${columnName != null ? columnName : `unknown`}" does not allow NULL values`);
127
+ this.name = `ParserNullValueError`;
128
+ }
129
+ };
130
+ var MissingHeadersError = class extends Error {
131
+ constructor(url, missingHeaders) {
132
+ let msg = `The response for the shape request to ${url} didn't include the following required headers:
133
+ `;
134
+ missingHeaders.forEach((h) => {
135
+ msg += `- ${h}
136
+ `;
137
+ });
138
+ msg += `
139
+ This is often due to a proxy not setting CORS correctly so that all Electric headers can be read by the client.`;
140
+ msg += `
141
+ For more information visit the troubleshooting guide: /docs/guides/troubleshooting/missing-headers`;
142
+ super(msg);
143
+ }
144
+ };
145
+
61
146
  // src/parser.ts
62
147
  var parseNumber = (value) => Number(value);
63
148
  var parseBool = (value) => value === `true` || value === `t`;
@@ -159,7 +244,7 @@ function makeNullableParser(parser, columnInfo, columnName) {
159
244
  return (value) => {
160
245
  if (isPgNull(value)) {
161
246
  if (!isNullable) {
162
- throw new Error(`Column ${columnName != null ? columnName : `unknown`} is not nullable`);
247
+ throw new ParserNullValueError(columnName != null ? columnName : `unknown`);
163
248
  }
164
249
  return null;
165
250
  }
@@ -181,61 +266,12 @@ function isUpToDateMessage(message) {
181
266
  return isControlMessage(message) && message.headers.control === `up-to-date`;
182
267
  }
183
268
 
184
- // src/error.ts
185
- var FetchError = class _FetchError extends Error {
186
- constructor(status, text, json, headers, url, message) {
187
- super(
188
- message || `HTTP Error ${status} at ${url}: ${text != null ? text : JSON.stringify(json)}`
189
- );
190
- this.url = url;
191
- this.name = `FetchError`;
192
- this.status = status;
193
- this.text = text;
194
- this.json = json;
195
- this.headers = headers;
196
- }
197
- static fromResponse(response, url) {
198
- return __async(this, null, function* () {
199
- const status = response.status;
200
- const headers = Object.fromEntries([...response.headers.entries()]);
201
- let text = void 0;
202
- let json = void 0;
203
- const contentType = response.headers.get(`content-type`);
204
- if (contentType && contentType.includes(`application/json`)) {
205
- json = yield response.json();
206
- } else {
207
- text = yield response.text();
208
- }
209
- return new _FetchError(status, text, json, headers, url);
210
- });
211
- }
212
- };
213
- var FetchBackoffAbortError = class extends Error {
214
- constructor() {
215
- super(`Fetch with backoff aborted`);
216
- }
217
- };
218
- var MissingHeadersError = class extends Error {
219
- constructor(url, missingHeaders) {
220
- let msg = `The response for the shape request to ${url} didn't include the following required headers:
221
- `;
222
- missingHeaders.forEach((h) => {
223
- msg += `- ${h}
224
- `;
225
- });
226
- msg += `
227
- This is often due to a proxy not setting CORS correctly so that all Electric headers can be read by the client.`;
228
- super(msg);
229
- }
230
- };
231
-
232
269
  // src/constants.ts
233
270
  var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
234
271
  var SHAPE_HANDLE_HEADER = `electric-handle`;
235
272
  var CHUNK_LAST_OFFSET_HEADER = `electric-offset`;
236
273
  var SHAPE_SCHEMA_HEADER = `electric-schema`;
237
274
  var CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`;
238
- var DATABASE_ID_QUERY_PARAM = `database_id`;
239
275
  var COLUMNS_QUERY_PARAM = `columns`;
240
276
  var LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`;
241
277
  var SHAPE_HANDLE_QUERY_PARAM = `handle`;
@@ -328,7 +364,7 @@ function createFetchWithResponseHeadersCheck(fetchClient) {
328
364
  if (response.ok) {
329
365
  const headers = response.headers;
330
366
  const missingHeaders = [];
331
- const addMissingHeaders = (requiredHeaders) => requiredHeaders.filter((h) => !headers.has(h));
367
+ const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
332
368
  addMissingHeaders(requiredElectricResponseHeaders);
333
369
  const input = args[0];
334
370
  const urlString = input.toString();
@@ -421,6 +457,7 @@ function getNextChunkUrl(url, res) {
421
457
  if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return;
422
458
  nextUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, shapeHandle);
423
459
  nextUrl.searchParams.set(OFFSET_QUERY_PARAM, lastOffset);
460
+ nextUrl.searchParams.sort();
424
461
  return nextUrl.toString();
425
462
  }
426
463
  function chainAborter(aborter, sourceSignal) {
@@ -435,24 +472,26 @@ function chainAborter(aborter, sourceSignal) {
435
472
 
436
473
  // src/client.ts
437
474
  var RESERVED_PARAMS = /* @__PURE__ */ new Set([
438
- DATABASE_ID_QUERY_PARAM,
439
- COLUMNS_QUERY_PARAM,
440
475
  LIVE_CACHE_BUSTER_QUERY_PARAM,
441
476
  SHAPE_HANDLE_QUERY_PARAM,
442
477
  LIVE_QUERY_PARAM,
443
- OFFSET_QUERY_PARAM,
444
- TABLE_QUERY_PARAM,
445
- WHERE_QUERY_PARAM,
446
- REPLICA_PARAM
478
+ OFFSET_QUERY_PARAM
447
479
  ]);
448
- var _fetchClient2, _messageParser, _subscribers, _upToDateSubscribers, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _databaseId, _schema, _error, _replica, _ShapeStream_instances, publish_fn, sendErrorToSubscribers_fn, notifyUpToDateSubscribers_fn, sendErrorToUpToDateSubscribers_fn, reset_fn;
449
- var _ShapeStream = class _ShapeStream {
480
+ function toInternalParams(params) {
481
+ const result = {};
482
+ for (const [key, value] of Object.entries(params)) {
483
+ result[key] = Array.isArray(value) ? value.join(`,`) : value;
484
+ }
485
+ return result;
486
+ }
487
+ var _error, _fetchClient2, _messageParser, _subscribers, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _schema, _onError, _ShapeStream_instances, start_fn, publish_fn, sendErrorToSubscribers_fn, reset_fn;
488
+ var ShapeStream = class {
450
489
  constructor(options) {
451
490
  __privateAdd(this, _ShapeStream_instances);
491
+ __privateAdd(this, _error, null);
452
492
  __privateAdd(this, _fetchClient2);
453
493
  __privateAdd(this, _messageParser);
454
494
  __privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
455
- __privateAdd(this, _upToDateSubscribers, /* @__PURE__ */ new Map());
456
495
  __privateAdd(this, _lastOffset);
457
496
  __privateAdd(this, _liveCacheBuster);
458
497
  // Seconds since our Electric Epoch 😎
@@ -461,19 +500,16 @@ var _ShapeStream = class _ShapeStream {
461
500
  __privateAdd(this, _isUpToDate, false);
462
501
  __privateAdd(this, _connected, false);
463
502
  __privateAdd(this, _shapeHandle);
464
- __privateAdd(this, _databaseId);
465
503
  __privateAdd(this, _schema);
466
- __privateAdd(this, _error);
467
- __privateAdd(this, _replica);
504
+ __privateAdd(this, _onError);
468
505
  var _a, _b, _c;
469
- validateOptions(options);
470
506
  this.options = __spreadValues({ subscribe: true }, options);
507
+ validateOptions(this.options);
471
508
  __privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
472
509
  __privateSet(this, _liveCacheBuster, ``);
473
- __privateSet(this, _shapeHandle, this.options.shapeHandle);
474
- __privateSet(this, _databaseId, this.options.databaseId);
510
+ __privateSet(this, _shapeHandle, this.options.handle);
475
511
  __privateSet(this, _messageParser, new MessageParser(options.parser));
476
- __privateSet(this, _replica, this.options.replica);
512
+ __privateSet(this, _onError, this.options.onError);
477
513
  const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
478
514
  const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
479
515
  onFailedAttempt: () => {
@@ -485,131 +521,22 @@ var _ShapeStream = class _ShapeStream {
485
521
  __privateSet(this, _fetchClient2, createFetchWithResponseHeadersCheck(
486
522
  createFetchWithChunkBuffer(fetchWithBackoffClient)
487
523
  ));
488
- this.start();
524
+ __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
489
525
  }
490
526
  get shapeHandle() {
491
527
  return __privateGet(this, _shapeHandle);
492
528
  }
529
+ get error() {
530
+ return __privateGet(this, _error);
531
+ }
493
532
  get isUpToDate() {
494
533
  return __privateGet(this, _isUpToDate);
495
534
  }
496
535
  get lastOffset() {
497
536
  return __privateGet(this, _lastOffset);
498
537
  }
499
- get error() {
500
- return __privateGet(this, _error);
501
- }
502
- start() {
503
- return __async(this, null, function* () {
504
- var _a, _b;
505
- __privateSet(this, _isUpToDate, false);
506
- const { url, table, where, columns, signal } = this.options;
507
- try {
508
- while (!(signal == null ? void 0 : signal.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
509
- const fetchUrl = new URL(url);
510
- if (this.options.params) {
511
- const reservedParams = Object.keys(this.options.params).filter(
512
- (key) => RESERVED_PARAMS.has(key)
513
- );
514
- if (reservedParams.length > 0) {
515
- throw new Error(
516
- `Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
517
- );
518
- }
519
- for (const [key, value] of Object.entries(this.options.params)) {
520
- fetchUrl.searchParams.set(key, value);
521
- }
522
- }
523
- if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
524
- if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
525
- if (columns && columns.length > 0)
526
- fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
527
- fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
528
- if (__privateGet(this, _isUpToDate)) {
529
- fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
530
- fetchUrl.searchParams.set(
531
- LIVE_CACHE_BUSTER_QUERY_PARAM,
532
- __privateGet(this, _liveCacheBuster)
533
- );
534
- }
535
- if (__privateGet(this, _shapeHandle)) {
536
- fetchUrl.searchParams.set(
537
- SHAPE_HANDLE_QUERY_PARAM,
538
- __privateGet(this, _shapeHandle)
539
- );
540
- }
541
- if (__privateGet(this, _databaseId)) {
542
- fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
543
- }
544
- if (((_a = __privateGet(this, _replica)) != null ? _a : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
545
- fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
546
- }
547
- let response;
548
- try {
549
- response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
550
- signal,
551
- headers: this.options.headers
552
- });
553
- __privateSet(this, _connected, true);
554
- } catch (e) {
555
- if (e instanceof FetchBackoffAbortError) break;
556
- if (e instanceof MissingHeadersError) throw e;
557
- if (!(e instanceof FetchError)) throw e;
558
- if (e.status == 409) {
559
- const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
560
- __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
561
- yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
562
- continue;
563
- } else if (e.status >= 400 && e.status < 500) {
564
- __privateMethod(this, _ShapeStream_instances, sendErrorToUpToDateSubscribers_fn).call(this, e);
565
- __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
566
- throw e;
567
- }
568
- }
569
- const { headers, status } = response;
570
- const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
571
- if (shapeHandle) {
572
- __privateSet(this, _shapeHandle, shapeHandle);
573
- }
574
- const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
575
- if (lastOffset) {
576
- __privateSet(this, _lastOffset, lastOffset);
577
- }
578
- const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
579
- if (liveCacheBuster) {
580
- __privateSet(this, _liveCacheBuster, liveCacheBuster);
581
- }
582
- const getSchema = () => {
583
- const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
584
- return schemaHeader ? JSON.parse(schemaHeader) : {};
585
- };
586
- __privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
587
- const messages = status === 204 ? `[]` : yield response.text();
588
- if (status === 204) {
589
- __privateSet(this, _lastSyncedAt, Date.now());
590
- }
591
- const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
592
- if (batch.length > 0) {
593
- const prevUpToDate = __privateGet(this, _isUpToDate);
594
- const lastMessage = batch[batch.length - 1];
595
- if (isUpToDateMessage(lastMessage)) {
596
- __privateSet(this, _lastSyncedAt, Date.now());
597
- __privateSet(this, _isUpToDate, true);
598
- }
599
- yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
600
- if (!prevUpToDate && __privateGet(this, _isUpToDate)) {
601
- __privateMethod(this, _ShapeStream_instances, notifyUpToDateSubscribers_fn).call(this);
602
- }
603
- }
604
- }
605
- } catch (err) {
606
- __privateSet(this, _error, err);
607
- } finally {
608
- __privateSet(this, _connected, false);
609
- }
610
- });
611
- }
612
- subscribe(callback, onError) {
538
+ subscribe(callback, onError = () => {
539
+ }) {
613
540
  const subscriptionId = Math.random();
614
541
  __privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
615
542
  return () => {
@@ -619,16 +546,6 @@ var _ShapeStream = class _ShapeStream {
619
546
  unsubscribeAll() {
620
547
  __privateGet(this, _subscribers).clear();
621
548
  }
622
- subscribeOnceToUpToDate(callback, error) {
623
- const subscriptionId = Math.random();
624
- __privateGet(this, _upToDateSubscribers).set(subscriptionId, [callback, error]);
625
- return () => {
626
- __privateGet(this, _upToDateSubscribers).delete(subscriptionId);
627
- };
628
- }
629
- unsubscribeAllUpToDateSubscribers() {
630
- __privateGet(this, _upToDateSubscribers).clear();
631
- }
632
549
  /** Unix time at which we last synced. Undefined when `isLoading` is true. */
633
550
  lastSyncedAt() {
634
551
  return __privateGet(this, _lastSyncedAt);
@@ -644,24 +561,145 @@ var _ShapeStream = class _ShapeStream {
644
561
  }
645
562
  /** True during initial fetch. False afterwise. */
646
563
  isLoading() {
647
- return !this.isUpToDate;
564
+ return !__privateGet(this, _isUpToDate);
648
565
  }
649
566
  };
567
+ _error = new WeakMap();
650
568
  _fetchClient2 = new WeakMap();
651
569
  _messageParser = new WeakMap();
652
570
  _subscribers = new WeakMap();
653
- _upToDateSubscribers = new WeakMap();
654
571
  _lastOffset = new WeakMap();
655
572
  _liveCacheBuster = new WeakMap();
656
573
  _lastSyncedAt = new WeakMap();
657
574
  _isUpToDate = new WeakMap();
658
575
  _connected = new WeakMap();
659
576
  _shapeHandle = new WeakMap();
660
- _databaseId = new WeakMap();
661
577
  _schema = new WeakMap();
662
- _error = new WeakMap();
663
- _replica = new WeakMap();
578
+ _onError = new WeakMap();
664
579
  _ShapeStream_instances = new WeakSet();
580
+ start_fn = function() {
581
+ return __async(this, null, function* () {
582
+ var _a, _b;
583
+ try {
584
+ while (!((_a = this.options.signal) == null ? void 0 : _a.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
585
+ const { url, signal } = this.options;
586
+ const fetchUrl = new URL(url);
587
+ if (this.options.params) {
588
+ const reservedParams = Object.keys(this.options.params).filter(
589
+ (key) => RESERVED_PARAMS.has(key)
590
+ );
591
+ if (reservedParams.length > 0) {
592
+ throw new Error(
593
+ `Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
594
+ );
595
+ }
596
+ const params = toInternalParams(this.options.params);
597
+ if (params.table)
598
+ fetchUrl.searchParams.set(TABLE_QUERY_PARAM, params.table);
599
+ if (params.where)
600
+ fetchUrl.searchParams.set(WHERE_QUERY_PARAM, params.where);
601
+ if (params.columns)
602
+ fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, params.columns);
603
+ if (params.replica)
604
+ fetchUrl.searchParams.set(REPLICA_PARAM, params.replica);
605
+ const customParams = __spreadValues({}, params);
606
+ delete customParams.table;
607
+ delete customParams.where;
608
+ delete customParams.columns;
609
+ delete customParams.replica;
610
+ for (const [key, value] of Object.entries(customParams)) {
611
+ fetchUrl.searchParams.set(key, value);
612
+ }
613
+ }
614
+ fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
615
+ if (__privateGet(this, _isUpToDate)) {
616
+ fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
617
+ fetchUrl.searchParams.set(
618
+ LIVE_CACHE_BUSTER_QUERY_PARAM,
619
+ __privateGet(this, _liveCacheBuster)
620
+ );
621
+ }
622
+ if (__privateGet(this, _shapeHandle)) {
623
+ fetchUrl.searchParams.set(
624
+ SHAPE_HANDLE_QUERY_PARAM,
625
+ __privateGet(this, _shapeHandle)
626
+ );
627
+ }
628
+ fetchUrl.searchParams.sort();
629
+ let response;
630
+ try {
631
+ response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
632
+ signal,
633
+ headers: this.options.headers
634
+ });
635
+ __privateSet(this, _connected, true);
636
+ } catch (e) {
637
+ if (e instanceof FetchBackoffAbortError) break;
638
+ if (!(e instanceof FetchError)) throw e;
639
+ if (e.status == 409) {
640
+ const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
641
+ __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
642
+ yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
643
+ continue;
644
+ } else if (e.status >= 400 && e.status < 500) {
645
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
646
+ throw e;
647
+ }
648
+ }
649
+ const { headers, status } = response;
650
+ const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
651
+ if (shapeHandle) {
652
+ __privateSet(this, _shapeHandle, shapeHandle);
653
+ }
654
+ const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
655
+ if (lastOffset) {
656
+ __privateSet(this, _lastOffset, lastOffset);
657
+ }
658
+ const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
659
+ if (liveCacheBuster) {
660
+ __privateSet(this, _liveCacheBuster, liveCacheBuster);
661
+ }
662
+ const getSchema = () => {
663
+ const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
664
+ return schemaHeader ? JSON.parse(schemaHeader) : {};
665
+ };
666
+ __privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
667
+ const messages = status === 204 ? `[]` : yield response.text();
668
+ if (status === 204) {
669
+ __privateSet(this, _lastSyncedAt, Date.now());
670
+ }
671
+ const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
672
+ if (batch.length > 0) {
673
+ const lastMessage = batch[batch.length - 1];
674
+ if (isUpToDateMessage(lastMessage)) {
675
+ __privateSet(this, _lastSyncedAt, Date.now());
676
+ __privateSet(this, _isUpToDate, true);
677
+ }
678
+ yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
679
+ }
680
+ }
681
+ } catch (err) {
682
+ __privateSet(this, _error, err);
683
+ if (__privateGet(this, _onError)) {
684
+ const retryOpts = yield __privateGet(this, _onError).call(this, err);
685
+ if (typeof retryOpts === `object`) {
686
+ __privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
687
+ if (`params` in retryOpts) {
688
+ this.options.params = retryOpts.params;
689
+ }
690
+ if (`headers` in retryOpts) {
691
+ this.options.headers = retryOpts.headers;
692
+ }
693
+ __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
694
+ }
695
+ return;
696
+ }
697
+ throw err;
698
+ } finally {
699
+ __privateSet(this, _connected, false);
700
+ }
701
+ });
702
+ };
665
703
  publish_fn = function(messages) {
666
704
  return __async(this, null, function* () {
667
705
  yield Promise.all(
@@ -682,55 +720,38 @@ sendErrorToSubscribers_fn = function(error) {
682
720
  errorFn == null ? void 0 : errorFn(error);
683
721
  });
684
722
  };
685
- notifyUpToDateSubscribers_fn = function() {
686
- __privateGet(this, _upToDateSubscribers).forEach(([callback]) => {
687
- callback();
688
- });
689
- };
690
- sendErrorToUpToDateSubscribers_fn = function(error) {
691
- __privateGet(this, _upToDateSubscribers).forEach(
692
- ([_, errorCallback]) => errorCallback(error)
693
- );
694
- };
695
723
  /**
696
724
  * Resets the state of the stream, optionally with a provided
697
725
  * shape handle
698
726
  */
699
- reset_fn = function(shapeHandle) {
727
+ reset_fn = function(handle) {
700
728
  __privateSet(this, _lastOffset, `-1`);
701
729
  __privateSet(this, _liveCacheBuster, ``);
702
- __privateSet(this, _shapeHandle, shapeHandle);
730
+ __privateSet(this, _shapeHandle, handle);
703
731
  __privateSet(this, _isUpToDate, false);
704
732
  __privateSet(this, _connected, false);
705
733
  __privateSet(this, _schema, void 0);
706
734
  };
707
- _ShapeStream.Replica = {
735
+ ShapeStream.Replica = {
708
736
  FULL: `full`,
709
737
  DEFAULT: `default`
710
738
  };
711
- var ShapeStream = _ShapeStream;
712
739
  function validateOptions(options) {
713
740
  if (!options.url) {
714
- throw new Error(`Invalid shape options. It must provide the url`);
741
+ throw new MissingShapeUrlError();
715
742
  }
716
743
  if (options.signal && !(options.signal instanceof AbortSignal)) {
717
- throw new Error(
718
- `Invalid signal option. It must be an instance of AbortSignal.`
719
- );
744
+ throw new InvalidSignalError();
720
745
  }
721
- if (options.offset !== void 0 && options.offset !== `-1` && !options.shapeHandle) {
722
- throw new Error(
723
- `shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`
724
- );
746
+ if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
747
+ throw new MissingShapeHandleError();
725
748
  }
726
749
  if (options.params) {
727
750
  const reservedParams = Object.keys(options.params).filter(
728
751
  (key) => RESERVED_PARAMS.has(key)
729
752
  );
730
753
  if (reservedParams.length > 0) {
731
- throw new Error(
732
- `Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
733
- );
754
+ throw new ReservedParamError(reservedParams);
734
755
  }
735
756
  }
736
757
  return;
@@ -751,15 +772,6 @@ var Shape = class {
751
772
  __privateMethod(this, _Shape_instances, process_fn).bind(this),
752
773
  __privateMethod(this, _Shape_instances, handleError_fn).bind(this)
753
774
  );
754
- const unsubscribe = __privateGet(this, _stream).subscribeOnceToUpToDate(
755
- () => {
756
- unsubscribe();
757
- },
758
- (e) => {
759
- __privateMethod(this, _Shape_instances, handleError_fn).call(this, e);
760
- throw e;
761
- }
762
- );
763
775
  }
764
776
  get isUpToDate() {
765
777
  return __privateGet(this, _stream).isUpToDate;
@@ -767,6 +779,9 @@ var Shape = class {
767
779
  get lastOffset() {
768
780
  return __privateGet(this, _stream).lastOffset;
769
781
  }
782
+ get handle() {
783
+ return __privateGet(this, _stream).shapeHandle;
784
+ }
770
785
  get rows() {
771
786
  return this.value.then((v) => Array.from(v.values()));
772
787
  }