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