@electric-sql/client 0.7.2 → 0.8.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,39 +244,6 @@ 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
-
197
247
  // src/constants.ts
198
248
  var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
199
249
  var SHAPE_HANDLE_HEADER = `electric-handle`;
@@ -281,6 +331,36 @@ function createFetchWithChunkBuffer(fetchClient, prefetchOptions = ChunkPrefetch
281
331
  };
282
332
  return prefetchClient;
283
333
  }
334
+ var requiredElectricResponseHeaders = [
335
+ `electric-offset`,
336
+ `electric-handle`
337
+ ];
338
+ var requiredLiveResponseHeaders = [`electric-cursor`];
339
+ var requiredNonLiveResponseHeaders = [`electric-schema`];
340
+ function createFetchWithResponseHeadersCheck(fetchClient) {
341
+ return async (...args) => {
342
+ const response = await fetchClient(...args);
343
+ if (response.ok) {
344
+ const headers = response.headers;
345
+ const missingHeaders = [];
346
+ const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
347
+ addMissingHeaders(requiredElectricResponseHeaders);
348
+ const input = args[0];
349
+ const urlString = input.toString();
350
+ const url = new URL(urlString);
351
+ if (url.searchParams.has(LIVE_QUERY_PARAM, `true`)) {
352
+ addMissingHeaders(requiredLiveResponseHeaders);
353
+ }
354
+ if (!url.searchParams.has(LIVE_QUERY_PARAM) || url.searchParams.has(LIVE_QUERY_PARAM, `false`)) {
355
+ addMissingHeaders(requiredNonLiveResponseHeaders);
356
+ }
357
+ if (missingHeaders.length > 0) {
358
+ throw new MissingHeadersError(urlString, missingHeaders);
359
+ }
360
+ }
361
+ return response;
362
+ };
363
+ }
284
364
  var _fetchClient, _maxPrefetchedRequests, _prefetchQueue, _queueHeadUrl, _queueTailUrl, _PrefetchQueue_instances, prefetch_fn;
285
365
  var PrefetchQueue = class {
286
366
  constructor(options) {
@@ -380,14 +460,14 @@ var RESERVED_PARAMS = /* @__PURE__ */ new Set([
380
460
  WHERE_QUERY_PARAM,
381
461
  REPLICA_PARAM
382
462
  ]);
383
- 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;
463
+ var _error, _fetchClient2, _messageParser, _subscribers, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _databaseId, _schema, _onError, _replica, _ShapeStream_instances, start_fn, publish_fn, sendErrorToSubscribers_fn, reset_fn;
384
464
  var _ShapeStream = class _ShapeStream {
385
465
  constructor(options) {
386
466
  __privateAdd(this, _ShapeStream_instances);
467
+ __privateAdd(this, _error, null);
387
468
  __privateAdd(this, _fetchClient2);
388
469
  __privateAdd(this, _messageParser);
389
470
  __privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
390
- __privateAdd(this, _upToDateSubscribers, /* @__PURE__ */ new Map());
391
471
  __privateAdd(this, _lastOffset);
392
472
  __privateAdd(this, _liveCacheBuster);
393
473
  // Seconds since our Electric Epoch 😎
@@ -398,17 +478,18 @@ var _ShapeStream = class _ShapeStream {
398
478
  __privateAdd(this, _shapeHandle);
399
479
  __privateAdd(this, _databaseId);
400
480
  __privateAdd(this, _schema);
401
- __privateAdd(this, _error);
481
+ __privateAdd(this, _onError);
402
482
  __privateAdd(this, _replica);
403
483
  var _a, _b, _c;
404
- validateOptions(options);
405
484
  this.options = __spreadValues({ subscribe: true }, options);
485
+ validateOptions(this.options);
406
486
  __privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
407
487
  __privateSet(this, _liveCacheBuster, ``);
408
- __privateSet(this, _shapeHandle, this.options.shapeHandle);
488
+ __privateSet(this, _shapeHandle, this.options.handle);
409
489
  __privateSet(this, _databaseId, this.options.databaseId);
410
490
  __privateSet(this, _messageParser, new MessageParser(options.parser));
411
491
  __privateSet(this, _replica, this.options.replica);
492
+ __privateSet(this, _onError, this.options.onError);
412
493
  const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
413
494
  const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
414
495
  onFailedAttempt: () => {
@@ -417,126 +498,25 @@ var _ShapeStream = class _ShapeStream {
417
498
  (_b2 = (_a2 = options.backoffOptions) == null ? void 0 : _a2.onFailedAttempt) == null ? void 0 : _b2.call(_a2);
418
499
  }
419
500
  }));
420
- __privateSet(this, _fetchClient2, createFetchWithChunkBuffer(fetchWithBackoffClient));
421
- this.start();
501
+ __privateSet(this, _fetchClient2, createFetchWithResponseHeadersCheck(
502
+ createFetchWithChunkBuffer(fetchWithBackoffClient)
503
+ ));
504
+ __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
422
505
  }
423
506
  get shapeHandle() {
424
507
  return __privateGet(this, _shapeHandle);
425
508
  }
426
- get isUpToDate() {
427
- return __privateGet(this, _isUpToDate);
428
- }
429
509
  get error() {
430
510
  return __privateGet(this, _error);
431
511
  }
432
- async start() {
433
- var _a, _b;
434
- __privateSet(this, _isUpToDate, false);
435
- const { url, table, where, columns, signal } = this.options;
436
- try {
437
- while (!(signal == null ? void 0 : signal.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
438
- const fetchUrl = new URL(url);
439
- if (this.options.params) {
440
- const reservedParams = Object.keys(this.options.params).filter(
441
- (key) => RESERVED_PARAMS.has(key)
442
- );
443
- if (reservedParams.length > 0) {
444
- throw new Error(
445
- `Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
446
- );
447
- }
448
- for (const [key, value] of Object.entries(this.options.params)) {
449
- fetchUrl.searchParams.set(key, value);
450
- }
451
- }
452
- if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
453
- if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
454
- if (columns && columns.length > 0)
455
- fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
456
- fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
457
- if (__privateGet(this, _isUpToDate)) {
458
- fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
459
- fetchUrl.searchParams.set(
460
- LIVE_CACHE_BUSTER_QUERY_PARAM,
461
- __privateGet(this, _liveCacheBuster)
462
- );
463
- }
464
- if (__privateGet(this, _shapeHandle)) {
465
- fetchUrl.searchParams.set(
466
- SHAPE_HANDLE_QUERY_PARAM,
467
- __privateGet(this, _shapeHandle)
468
- );
469
- }
470
- if (__privateGet(this, _databaseId)) {
471
- fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
472
- }
473
- if (((_a = __privateGet(this, _replica)) != null ? _a : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
474
- fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
475
- }
476
- let response;
477
- try {
478
- response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
479
- signal,
480
- headers: this.options.headers
481
- });
482
- __privateSet(this, _connected, true);
483
- } catch (e) {
484
- if (e instanceof FetchBackoffAbortError) break;
485
- if (!(e instanceof FetchError)) throw e;
486
- if (e.status == 409) {
487
- const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
488
- __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
489
- await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
490
- continue;
491
- } else if (e.status >= 400 && e.status < 500) {
492
- __privateMethod(this, _ShapeStream_instances, sendErrorToUpToDateSubscribers_fn).call(this, e);
493
- __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
494
- throw e;
495
- }
496
- }
497
- const { headers, status } = response;
498
- const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
499
- if (shapeHandle) {
500
- __privateSet(this, _shapeHandle, shapeHandle);
501
- }
502
- const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
503
- if (lastOffset) {
504
- __privateSet(this, _lastOffset, lastOffset);
505
- }
506
- const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
507
- if (liveCacheBuster) {
508
- __privateSet(this, _liveCacheBuster, liveCacheBuster);
509
- }
510
- const getSchema = () => {
511
- const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
512
- return schemaHeader ? JSON.parse(schemaHeader) : {};
513
- };
514
- __privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
515
- const messages = status === 204 ? `[]` : await response.text();
516
- if (status === 204) {
517
- __privateSet(this, _lastSyncedAt, Date.now());
518
- }
519
- const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
520
- if (batch.length > 0) {
521
- const prevUpToDate = __privateGet(this, _isUpToDate);
522
- const lastMessage = batch[batch.length - 1];
523
- if (isUpToDateMessage(lastMessage)) {
524
- __privateSet(this, _lastSyncedAt, Date.now());
525
- __privateSet(this, _isUpToDate, true);
526
- }
527
- await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
528
- if (!prevUpToDate && __privateGet(this, _isUpToDate)) {
529
- __privateMethod(this, _ShapeStream_instances, notifyUpToDateSubscribers_fn).call(this);
530
- }
531
- }
532
- }
533
- } catch (err) {
534
- __privateSet(this, _error, err);
535
- } finally {
536
- __privateSet(this, _connected, false);
537
- }
512
+ get isUpToDate() {
513
+ return __privateGet(this, _isUpToDate);
514
+ }
515
+ get lastOffset() {
516
+ return __privateGet(this, _lastOffset);
538
517
  }
539
- subscribe(callback, onError) {
518
+ subscribe(callback, onError = () => {
519
+ }) {
540
520
  const subscriptionId = Math.random();
541
521
  __privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
542
522
  return () => {
@@ -546,16 +526,6 @@ var _ShapeStream = class _ShapeStream {
546
526
  unsubscribeAll() {
547
527
  __privateGet(this, _subscribers).clear();
548
528
  }
549
- subscribeOnceToUpToDate(callback, error) {
550
- const subscriptionId = Math.random();
551
- __privateGet(this, _upToDateSubscribers).set(subscriptionId, [callback, error]);
552
- return () => {
553
- __privateGet(this, _upToDateSubscribers).delete(subscriptionId);
554
- };
555
- }
556
- unsubscribeAllUpToDateSubscribers() {
557
- __privateGet(this, _upToDateSubscribers).clear();
558
- }
559
529
  /** Unix time at which we last synced. Undefined when `isLoading` is true. */
560
530
  lastSyncedAt() {
561
531
  return __privateGet(this, _lastSyncedAt);
@@ -571,13 +541,13 @@ var _ShapeStream = class _ShapeStream {
571
541
  }
572
542
  /** True during initial fetch. False afterwise. */
573
543
  isLoading() {
574
- return !this.isUpToDate;
544
+ return !__privateGet(this, _isUpToDate);
575
545
  }
576
546
  };
547
+ _error = new WeakMap();
577
548
  _fetchClient2 = new WeakMap();
578
549
  _messageParser = new WeakMap();
579
550
  _subscribers = new WeakMap();
580
- _upToDateSubscribers = new WeakMap();
581
551
  _lastOffset = new WeakMap();
582
552
  _liveCacheBuster = new WeakMap();
583
553
  _lastSyncedAt = new WeakMap();
@@ -586,9 +556,125 @@ _connected = new WeakMap();
586
556
  _shapeHandle = new WeakMap();
587
557
  _databaseId = new WeakMap();
588
558
  _schema = new WeakMap();
589
- _error = new WeakMap();
559
+ _onError = new WeakMap();
590
560
  _replica = new WeakMap();
591
561
  _ShapeStream_instances = new WeakSet();
562
+ start_fn = async function() {
563
+ var _a, _b, _c;
564
+ try {
565
+ while (!((_a = this.options.signal) == null ? void 0 : _a.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
566
+ const { url, table, where, columns, signal } = this.options;
567
+ const fetchUrl = new URL(url);
568
+ if (this.options.params) {
569
+ const reservedParams = Object.keys(this.options.params).filter(
570
+ (key) => RESERVED_PARAMS.has(key)
571
+ );
572
+ if (reservedParams.length > 0) {
573
+ throw new Error(
574
+ `Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
575
+ );
576
+ }
577
+ for (const [key, value] of Object.entries(this.options.params)) {
578
+ fetchUrl.searchParams.set(key, value);
579
+ }
580
+ }
581
+ if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
582
+ if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
583
+ if (columns && columns.length > 0)
584
+ fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
585
+ fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
586
+ if (__privateGet(this, _isUpToDate)) {
587
+ fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
588
+ fetchUrl.searchParams.set(
589
+ LIVE_CACHE_BUSTER_QUERY_PARAM,
590
+ __privateGet(this, _liveCacheBuster)
591
+ );
592
+ }
593
+ if (__privateGet(this, _shapeHandle)) {
594
+ fetchUrl.searchParams.set(
595
+ SHAPE_HANDLE_QUERY_PARAM,
596
+ __privateGet(this, _shapeHandle)
597
+ );
598
+ }
599
+ if (__privateGet(this, _databaseId)) {
600
+ fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
601
+ }
602
+ if (((_b = __privateGet(this, _replica)) != null ? _b : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
603
+ fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
604
+ }
605
+ let response;
606
+ try {
607
+ response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
608
+ signal,
609
+ headers: this.options.headers
610
+ });
611
+ __privateSet(this, _connected, true);
612
+ } catch (e) {
613
+ if (e instanceof FetchBackoffAbortError) break;
614
+ if (!(e instanceof FetchError)) throw e;
615
+ if (e.status == 409) {
616
+ const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
617
+ __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
618
+ await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
619
+ continue;
620
+ } else if (e.status >= 400 && e.status < 500) {
621
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
622
+ throw e;
623
+ }
624
+ }
625
+ const { headers, status } = response;
626
+ const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
627
+ if (shapeHandle) {
628
+ __privateSet(this, _shapeHandle, shapeHandle);
629
+ }
630
+ const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
631
+ if (lastOffset) {
632
+ __privateSet(this, _lastOffset, lastOffset);
633
+ }
634
+ const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
635
+ if (liveCacheBuster) {
636
+ __privateSet(this, _liveCacheBuster, liveCacheBuster);
637
+ }
638
+ const getSchema = () => {
639
+ const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
640
+ return schemaHeader ? JSON.parse(schemaHeader) : {};
641
+ };
642
+ __privateSet(this, _schema, (_c = __privateGet(this, _schema)) != null ? _c : getSchema());
643
+ const messages = status === 204 ? `[]` : await response.text();
644
+ if (status === 204) {
645
+ __privateSet(this, _lastSyncedAt, Date.now());
646
+ }
647
+ const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
648
+ if (batch.length > 0) {
649
+ const lastMessage = batch[batch.length - 1];
650
+ if (isUpToDateMessage(lastMessage)) {
651
+ __privateSet(this, _lastSyncedAt, Date.now());
652
+ __privateSet(this, _isUpToDate, true);
653
+ }
654
+ await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
655
+ }
656
+ }
657
+ } catch (err) {
658
+ __privateSet(this, _error, err);
659
+ if (__privateGet(this, _onError)) {
660
+ const retryOpts = await __privateGet(this, _onError).call(this, err);
661
+ if (typeof retryOpts === `object`) {
662
+ __privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
663
+ if (`params` in retryOpts) {
664
+ this.options.params = retryOpts.params;
665
+ }
666
+ if (`headers` in retryOpts) {
667
+ this.options.headers = retryOpts.headers;
668
+ }
669
+ __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
670
+ }
671
+ return;
672
+ }
673
+ throw err;
674
+ } finally {
675
+ __privateSet(this, _connected, false);
676
+ }
677
+ };
592
678
  publish_fn = async function(messages) {
593
679
  await Promise.all(
594
680
  Array.from(__privateGet(this, _subscribers).values()).map(async ([callback, __]) => {
@@ -607,24 +693,14 @@ sendErrorToSubscribers_fn = function(error) {
607
693
  errorFn == null ? void 0 : errorFn(error);
608
694
  });
609
695
  };
610
- notifyUpToDateSubscribers_fn = function() {
611
- __privateGet(this, _upToDateSubscribers).forEach(([callback]) => {
612
- callback();
613
- });
614
- };
615
- sendErrorToUpToDateSubscribers_fn = function(error) {
616
- __privateGet(this, _upToDateSubscribers).forEach(
617
- ([_, errorCallback]) => errorCallback(error)
618
- );
619
- };
620
696
  /**
621
697
  * Resets the state of the stream, optionally with a provided
622
698
  * shape handle
623
699
  */
624
- reset_fn = function(shapeHandle) {
700
+ reset_fn = function(handle) {
625
701
  __privateSet(this, _lastOffset, `-1`);
626
702
  __privateSet(this, _liveCacheBuster, ``);
627
- __privateSet(this, _shapeHandle, shapeHandle);
703
+ __privateSet(this, _shapeHandle, handle);
628
704
  __privateSet(this, _isUpToDate, false);
629
705
  __privateSet(this, _connected, false);
630
706
  __privateSet(this, _schema, void 0);
@@ -636,17 +712,21 @@ _ShapeStream.Replica = {
636
712
  var ShapeStream = _ShapeStream;
637
713
  function validateOptions(options) {
638
714
  if (!options.url) {
639
- throw new Error(`Invalid shape options. It must provide the url`);
715
+ throw new MissingShapeUrlError();
640
716
  }
641
717
  if (options.signal && !(options.signal instanceof AbortSignal)) {
642
- throw new Error(
643
- `Invalid signal option. It must be an instance of AbortSignal.`
644
- );
718
+ throw new InvalidSignalError();
645
719
  }
646
- if (options.offset !== void 0 && options.offset !== `-1` && !options.shapeHandle) {
647
- throw new Error(
648
- `shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`
720
+ if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
721
+ throw new MissingShapeHandleError();
722
+ }
723
+ if (options.params) {
724
+ const reservedParams = Object.keys(options.params).filter(
725
+ (key) => RESERVED_PARAMS.has(key)
649
726
  );
727
+ if (reservedParams.length > 0) {
728
+ throw new ReservedParamError(reservedParams);
729
+ }
650
730
  }
651
731
  return;
652
732
  }
@@ -666,19 +746,16 @@ var Shape = class {
666
746
  __privateMethod(this, _Shape_instances, process_fn).bind(this),
667
747
  __privateMethod(this, _Shape_instances, handleError_fn).bind(this)
668
748
  );
669
- const unsubscribe = __privateGet(this, _stream).subscribeOnceToUpToDate(
670
- () => {
671
- unsubscribe();
672
- },
673
- (e) => {
674
- __privateMethod(this, _Shape_instances, handleError_fn).call(this, e);
675
- throw e;
676
- }
677
- );
678
749
  }
679
750
  get isUpToDate() {
680
751
  return __privateGet(this, _stream).isUpToDate;
681
752
  }
753
+ get lastOffset() {
754
+ return __privateGet(this, _stream).lastOffset;
755
+ }
756
+ get handle() {
757
+ return __privateGet(this, _stream).shapeHandle;
758
+ }
682
759
  get rows() {
683
760
  return this.value.then((v) => Array.from(v.values()));
684
761
  }