@electric-sql/client 0.5.0 → 0.6.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
@@ -1,7 +1,12 @@
1
1
  var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
2
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
4
6
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __typeError = (msg) => {
8
+ throw TypeError(msg);
9
+ };
5
10
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
11
  var __spreadValues = (a, b) => {
7
12
  for (var prop in b || (b = {}))
@@ -14,6 +19,7 @@ var __spreadValues = (a, b) => {
14
19
  }
15
20
  return a;
16
21
  };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
17
23
  var __objRest = (source, exclude) => {
18
24
  var target = {};
19
25
  for (var prop in source)
@@ -26,6 +32,11 @@ var __objRest = (source, exclude) => {
26
32
  }
27
33
  return target;
28
34
  };
35
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
36
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
37
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
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
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
29
40
  var __async = (__this, __arguments, generator) => {
30
41
  return new Promise((resolve, reject) => {
31
42
  var fulfilled = (value) => {
@@ -166,36 +177,11 @@ function isChangeMessage(message) {
166
177
  function isControlMessage(message) {
167
178
  return !isChangeMessage(message);
168
179
  }
180
+ function isUpToDateMessage(message) {
181
+ return isControlMessage(message) && message.headers.control === `up-to-date`;
182
+ }
169
183
 
170
- // src/client.ts
171
- var BackoffDefaults = {
172
- initialDelay: 100,
173
- maxDelay: 1e4,
174
- multiplier: 1.3
175
- };
176
- var MessageProcessor = class {
177
- constructor(callback) {
178
- this.messageQueue = [];
179
- this.isProcessing = false;
180
- this.callback = callback;
181
- }
182
- process(messages) {
183
- this.messageQueue.push(messages);
184
- if (!this.isProcessing) {
185
- this.processQueue();
186
- }
187
- }
188
- processQueue() {
189
- return __async(this, null, function* () {
190
- this.isProcessing = true;
191
- while (this.messageQueue.length > 0) {
192
- const messages = this.messageQueue.shift();
193
- yield this.callback(messages);
194
- }
195
- this.isProcessing = false;
196
- });
197
- }
198
- };
184
+ // src/error.ts
199
185
  var FetchError = class _FetchError extends Error {
200
186
  constructor(status, text, json, headers, url, message) {
201
187
  super(
@@ -224,324 +210,534 @@ var FetchError = class _FetchError extends Error {
224
210
  });
225
211
  }
226
212
  };
213
+ var FetchBackoffAbortError = class extends Error {
214
+ constructor() {
215
+ super(`Fetch with backoff aborted`);
216
+ }
217
+ };
218
+
219
+ // src/constants.ts
220
+ var SHAPE_ID_HEADER = `electric-shape-id`;
221
+ var CHUNK_LAST_OFFSET_HEADER = `electric-chunk-last-offset`;
222
+ var CHUNK_UP_TO_DATE_HEADER = `electric-chunk-up-to-date`;
223
+ var SHAPE_SCHEMA_HEADER = `electric-schema`;
224
+ var SHAPE_ID_QUERY_PARAM = `shape_id`;
225
+ var OFFSET_QUERY_PARAM = `offset`;
226
+ var WHERE_QUERY_PARAM = `where`;
227
+ var LIVE_QUERY_PARAM = `live`;
228
+
229
+ // src/fetch.ts
230
+ var BackoffDefaults = {
231
+ initialDelay: 100,
232
+ maxDelay: 1e4,
233
+ multiplier: 1.3
234
+ };
235
+ function createFetchWithBackoff(fetchClient, backoffOptions = BackoffDefaults) {
236
+ const {
237
+ initialDelay,
238
+ maxDelay,
239
+ multiplier,
240
+ debug = false,
241
+ onFailedAttempt
242
+ } = backoffOptions;
243
+ return (...args) => __async(this, null, function* () {
244
+ var _a;
245
+ const url = args[0];
246
+ const options = args[1];
247
+ let delay = initialDelay;
248
+ let attempt = 0;
249
+ while (true) {
250
+ try {
251
+ const result = yield fetchClient(...args);
252
+ if (result.ok) return result;
253
+ else throw yield FetchError.fromResponse(result, url.toString());
254
+ } catch (e) {
255
+ onFailedAttempt == null ? void 0 : onFailedAttempt();
256
+ if ((_a = options == null ? void 0 : options.signal) == null ? void 0 : _a.aborted) {
257
+ throw new FetchBackoffAbortError();
258
+ } else if (e instanceof FetchError && e.status >= 400 && e.status < 500) {
259
+ throw e;
260
+ } else {
261
+ yield new Promise((resolve) => setTimeout(resolve, delay));
262
+ delay = Math.min(delay * multiplier, maxDelay);
263
+ if (debug) {
264
+ attempt++;
265
+ console.log(`Retry attempt #${attempt} after ${delay}ms`);
266
+ }
267
+ }
268
+ }
269
+ }
270
+ });
271
+ }
272
+ var ChunkPrefetchDefaults = {
273
+ maxChunksToPrefetch: 2
274
+ };
275
+ function createFetchWithChunkBuffer(fetchClient, prefetchOptions = ChunkPrefetchDefaults) {
276
+ const { maxChunksToPrefetch } = prefetchOptions;
277
+ let prefetchQueue;
278
+ const prefetchClient = (...args) => __async(this, null, function* () {
279
+ const url = args[0].toString();
280
+ const prefetchedRequest = prefetchQueue == null ? void 0 : prefetchQueue.consume(...args);
281
+ if (prefetchedRequest) {
282
+ return prefetchedRequest;
283
+ }
284
+ prefetchQueue == null ? void 0 : prefetchQueue.abort();
285
+ const response = yield fetchClient(...args);
286
+ const nextUrl = getNextChunkUrl(url, response);
287
+ if (nextUrl) {
288
+ prefetchQueue = new PrefetchQueue({
289
+ fetchClient,
290
+ maxPrefetchedRequests: maxChunksToPrefetch,
291
+ url: nextUrl,
292
+ requestInit: args[1]
293
+ });
294
+ }
295
+ return response;
296
+ });
297
+ return prefetchClient;
298
+ }
299
+ var _fetchClient, _maxPrefetchedRequests, _prefetchQueue, _queueHeadUrl, _queueTailUrl, _PrefetchQueue_instances, prefetch_fn;
300
+ var PrefetchQueue = class {
301
+ constructor(options) {
302
+ __privateAdd(this, _PrefetchQueue_instances);
303
+ __privateAdd(this, _fetchClient);
304
+ __privateAdd(this, _maxPrefetchedRequests);
305
+ __privateAdd(this, _prefetchQueue, /* @__PURE__ */ new Map());
306
+ __privateAdd(this, _queueHeadUrl);
307
+ __privateAdd(this, _queueTailUrl);
308
+ var _a;
309
+ __privateSet(this, _fetchClient, (_a = options.fetchClient) != null ? _a : (...args) => fetch(...args));
310
+ __privateSet(this, _maxPrefetchedRequests, options.maxPrefetchedRequests);
311
+ __privateSet(this, _queueHeadUrl, options.url.toString());
312
+ __privateSet(this, _queueTailUrl, __privateGet(this, _queueHeadUrl));
313
+ __privateMethod(this, _PrefetchQueue_instances, prefetch_fn).call(this, options.url, options.requestInit);
314
+ }
315
+ abort() {
316
+ __privateGet(this, _prefetchQueue).forEach(([_, aborter]) => aborter.abort());
317
+ }
318
+ consume(...args) {
319
+ var _a;
320
+ const url = args[0].toString();
321
+ const request = (_a = __privateGet(this, _prefetchQueue).get(url)) == null ? void 0 : _a[0];
322
+ if (!request || url !== __privateGet(this, _queueHeadUrl)) return;
323
+ __privateGet(this, _prefetchQueue).delete(url);
324
+ request.then((response) => {
325
+ const nextUrl = getNextChunkUrl(url, response);
326
+ __privateSet(this, _queueHeadUrl, nextUrl);
327
+ if (!__privateGet(this, _prefetchQueue).has(__privateGet(this, _queueTailUrl))) {
328
+ __privateMethod(this, _PrefetchQueue_instances, prefetch_fn).call(this, __privateGet(this, _queueTailUrl), args[1]);
329
+ }
330
+ }).catch(() => {
331
+ });
332
+ return request;
333
+ }
334
+ };
335
+ _fetchClient = new WeakMap();
336
+ _maxPrefetchedRequests = new WeakMap();
337
+ _prefetchQueue = new WeakMap();
338
+ _queueHeadUrl = new WeakMap();
339
+ _queueTailUrl = new WeakMap();
340
+ _PrefetchQueue_instances = new WeakSet();
341
+ prefetch_fn = function(...args) {
342
+ var _a, _b;
343
+ const url = args[0].toString();
344
+ if (__privateGet(this, _prefetchQueue).size >= __privateGet(this, _maxPrefetchedRequests)) return;
345
+ const aborter = new AbortController();
346
+ try {
347
+ const request = __privateGet(this, _fetchClient).call(this, url, __spreadProps(__spreadValues({}, (_a = args[1]) != null ? _a : {}), {
348
+ signal: chainAborter(aborter, (_b = args[1]) == null ? void 0 : _b.signal)
349
+ }));
350
+ __privateGet(this, _prefetchQueue).set(url, [request, aborter]);
351
+ request.then((response) => {
352
+ if (!response.ok || aborter.signal.aborted) return;
353
+ const nextUrl = getNextChunkUrl(url, response);
354
+ if (!nextUrl || nextUrl === url) return;
355
+ __privateSet(this, _queueTailUrl, nextUrl);
356
+ return __privateMethod(this, _PrefetchQueue_instances, prefetch_fn).call(this, nextUrl, args[1]);
357
+ }).catch(() => {
358
+ });
359
+ } catch (_) {
360
+ }
361
+ };
362
+ function getNextChunkUrl(url, res) {
363
+ const shapeId = res.headers.get(SHAPE_ID_HEADER);
364
+ const lastOffset = res.headers.get(CHUNK_LAST_OFFSET_HEADER);
365
+ const isUpToDate = res.headers.has(CHUNK_UP_TO_DATE_HEADER);
366
+ if (!shapeId || !lastOffset || isUpToDate) return;
367
+ const nextUrl = new URL(url);
368
+ if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return;
369
+ nextUrl.searchParams.set(SHAPE_ID_QUERY_PARAM, shapeId);
370
+ nextUrl.searchParams.set(OFFSET_QUERY_PARAM, lastOffset);
371
+ return nextUrl.toString();
372
+ }
373
+ function chainAborter(aborter, sourceSignal) {
374
+ if (!sourceSignal) return aborter.signal;
375
+ if (sourceSignal.aborted) aborter.abort();
376
+ else
377
+ sourceSignal.addEventListener(`abort`, () => aborter.abort(), {
378
+ once: true
379
+ });
380
+ return aborter.signal;
381
+ }
382
+
383
+ // src/client.ts
384
+ var _fetchClient2, _messageParser, _subscribers, _upToDateSubscribers, _lastOffset, _lastSyncedAt, _isUpToDate, _connected, _shapeId, _schema, _ShapeStream_instances, publish_fn, sendErrorToSubscribers_fn, notifyUpToDateSubscribers_fn, sendErrorToUpToDateSubscribers_fn, reset_fn;
227
385
  var ShapeStream = class {
228
386
  constructor(options) {
229
- this.subscribers = /* @__PURE__ */ new Map();
230
- this.upToDateSubscribers = /* @__PURE__ */ new Map();
387
+ __privateAdd(this, _ShapeStream_instances);
388
+ __privateAdd(this, _fetchClient2);
389
+ __privateAdd(this, _messageParser);
390
+ __privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
391
+ __privateAdd(this, _upToDateSubscribers, /* @__PURE__ */ new Map());
392
+ __privateAdd(this, _lastOffset);
393
+ __privateAdd(this, _lastSyncedAt);
231
394
  // unix time
232
- this.isUpToDate = false;
233
- this.connected = false;
395
+ __privateAdd(this, _isUpToDate, false);
396
+ __privateAdd(this, _connected, false);
397
+ __privateAdd(this, _shapeId);
398
+ __privateAdd(this, _schema);
234
399
  var _a, _b, _c;
235
- this.validateOptions(options);
400
+ validateOptions(options);
236
401
  this.options = __spreadValues({ subscribe: true }, options);
237
- this.lastOffset = (_a = this.options.offset) != null ? _a : `-1`;
238
- this.shapeId = this.options.shapeId;
239
- this.messageParser = new MessageParser(options.parser);
240
- this.backoffOptions = (_b = options.backoffOptions) != null ? _b : BackoffDefaults;
241
- this.fetchClient = (_c = options.fetchClient) != null ? _c : (...args) => fetch(...args);
402
+ __privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
403
+ __privateSet(this, _shapeId, this.options.shapeId);
404
+ __privateSet(this, _messageParser, new MessageParser(options.parser));
405
+ const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
406
+ const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
407
+ onFailedAttempt: () => {
408
+ var _a2, _b2;
409
+ __privateSet(this, _connected, false);
410
+ (_b2 = (_a2 = options.backoffOptions) == null ? void 0 : _a2.onFailedAttempt) == null ? void 0 : _b2.call(_a2);
411
+ }
412
+ }));
413
+ __privateSet(this, _fetchClient2, createFetchWithChunkBuffer(fetchWithBackoffClient));
242
414
  this.start();
243
415
  }
416
+ get shapeId() {
417
+ return __privateGet(this, _shapeId);
418
+ }
419
+ get isUpToDate() {
420
+ return __privateGet(this, _isUpToDate);
421
+ }
244
422
  start() {
245
423
  return __async(this, null, function* () {
246
424
  var _a;
247
- this.isUpToDate = false;
425
+ __privateSet(this, _isUpToDate, false);
248
426
  const { url, where, signal } = this.options;
249
427
  try {
250
- while (!(signal == null ? void 0 : signal.aborted) && !this.isUpToDate || this.options.subscribe) {
428
+ while (!(signal == null ? void 0 : signal.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
251
429
  const fetchUrl = new URL(url);
252
- if (where) fetchUrl.searchParams.set(`where`, where);
253
- fetchUrl.searchParams.set(`offset`, this.lastOffset);
254
- if (this.isUpToDate) {
255
- fetchUrl.searchParams.set(`live`, `true`);
430
+ if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
431
+ fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
432
+ if (__privateGet(this, _isUpToDate)) {
433
+ fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
256
434
  }
257
- if (this.shapeId) {
258
- fetchUrl.searchParams.set(`shape_id`, this.shapeId);
435
+ if (__privateGet(this, _shapeId)) {
436
+ fetchUrl.searchParams.set(SHAPE_ID_QUERY_PARAM, __privateGet(this, _shapeId));
259
437
  }
260
438
  let response;
261
439
  try {
262
- const maybeResponse = yield this.fetchWithBackoff(fetchUrl);
263
- if (maybeResponse) response = maybeResponse;
264
- else break;
440
+ response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), { signal });
441
+ __privateSet(this, _connected, true);
265
442
  } catch (e) {
443
+ if (e instanceof FetchBackoffAbortError) break;
266
444
  if (!(e instanceof FetchError)) throw e;
267
445
  if (e.status == 400) {
268
- this.reset();
269
- this.publish(e.json);
446
+ __privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
447
+ yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
270
448
  continue;
271
449
  } else if (e.status == 409) {
272
- const newShapeId = e.headers[`x-electric-shape-id`];
273
- this.reset(newShapeId);
274
- this.publish(e.json);
450
+ const newShapeId = e.headers[SHAPE_ID_HEADER];
451
+ __privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeId);
452
+ yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
275
453
  continue;
276
454
  } else if (e.status >= 400 && e.status < 500) {
277
- this.sendErrorToUpToDateSubscribers(e);
278
- this.sendErrorToSubscribers(e);
455
+ __privateMethod(this, _ShapeStream_instances, sendErrorToUpToDateSubscribers_fn).call(this, e);
456
+ __privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
279
457
  throw e;
280
458
  }
281
459
  }
282
460
  const { headers, status } = response;
283
- const shapeId = headers.get(`X-Electric-Shape-Id`);
461
+ const shapeId = headers.get(SHAPE_ID_HEADER);
284
462
  if (shapeId) {
285
- this.shapeId = shapeId;
463
+ __privateSet(this, _shapeId, shapeId);
286
464
  }
287
- const lastOffset = headers.get(`X-Electric-Chunk-Last-Offset`);
465
+ const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
288
466
  if (lastOffset) {
289
- this.lastOffset = lastOffset;
467
+ __privateSet(this, _lastOffset, lastOffset);
290
468
  }
291
469
  const getSchema = () => {
292
- const schemaHeader = headers.get(`X-Electric-Schema`);
470
+ const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
293
471
  return schemaHeader ? JSON.parse(schemaHeader) : {};
294
472
  };
295
- this.schema = (_a = this.schema) != null ? _a : getSchema();
473
+ __privateSet(this, _schema, (_a = __privateGet(this, _schema)) != null ? _a : getSchema());
296
474
  const messages = status === 204 ? `[]` : yield response.text();
297
475
  if (status === 204) {
298
- this.lastSyncedAt = Date.now();
476
+ __privateSet(this, _lastSyncedAt, Date.now());
299
477
  }
300
- const batch = this.messageParser.parse(messages, this.schema);
478
+ const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
301
479
  if (batch.length > 0) {
480
+ const prevUpToDate = __privateGet(this, _isUpToDate);
302
481
  const lastMessage = batch[batch.length - 1];
303
- if (isControlMessage(lastMessage) && lastMessage.headers.control === `up-to-date`) {
304
- this.lastSyncedAt = Date.now();
305
- if (!this.isUpToDate) {
306
- this.isUpToDate = true;
307
- this.notifyUpToDateSubscribers();
308
- }
482
+ if (isUpToDateMessage(lastMessage)) {
483
+ __privateSet(this, _lastSyncedAt, Date.now());
484
+ __privateSet(this, _isUpToDate, true);
485
+ }
486
+ yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
487
+ if (!prevUpToDate && __privateGet(this, _isUpToDate)) {
488
+ __privateMethod(this, _ShapeStream_instances, notifyUpToDateSubscribers_fn).call(this);
309
489
  }
310
- this.publish(batch);
311
490
  }
312
491
  }
313
492
  } finally {
314
- this.connected = false;
493
+ __privateSet(this, _connected, false);
315
494
  }
316
495
  });
317
496
  }
318
497
  subscribe(callback, onError) {
319
498
  const subscriptionId = Math.random();
320
- const subscriber = new MessageProcessor(callback);
321
- this.subscribers.set(subscriptionId, [subscriber, onError]);
499
+ __privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
322
500
  return () => {
323
- this.subscribers.delete(subscriptionId);
501
+ __privateGet(this, _subscribers).delete(subscriptionId);
324
502
  };
325
503
  }
326
504
  unsubscribeAll() {
327
- this.subscribers.clear();
328
- }
329
- publish(messages) {
330
- this.subscribers.forEach(([subscriber, _]) => {
331
- subscriber.process(messages);
332
- });
333
- }
334
- sendErrorToSubscribers(error) {
335
- this.subscribers.forEach(([_, errorFn]) => {
336
- errorFn == null ? void 0 : errorFn(error);
337
- });
505
+ __privateGet(this, _subscribers).clear();
338
506
  }
339
507
  subscribeOnceToUpToDate(callback, error) {
340
508
  const subscriptionId = Math.random();
341
- this.upToDateSubscribers.set(subscriptionId, [callback, error]);
509
+ __privateGet(this, _upToDateSubscribers).set(subscriptionId, [callback, error]);
342
510
  return () => {
343
- this.upToDateSubscribers.delete(subscriptionId);
511
+ __privateGet(this, _upToDateSubscribers).delete(subscriptionId);
344
512
  };
345
513
  }
346
514
  unsubscribeAllUpToDateSubscribers() {
347
- this.upToDateSubscribers.clear();
515
+ __privateGet(this, _upToDateSubscribers).clear();
516
+ }
517
+ /** Unix time at which we last synced. Undefined when `isLoading` is true. */
518
+ lastSyncedAt() {
519
+ return __privateGet(this, _lastSyncedAt);
348
520
  }
349
521
  /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */
350
522
  lastSynced() {
351
- if (this.lastSyncedAt === void 0) return Infinity;
352
- return Date.now() - this.lastSyncedAt;
523
+ if (__privateGet(this, _lastSyncedAt) === void 0) return Infinity;
524
+ return Date.now() - __privateGet(this, _lastSyncedAt);
353
525
  }
526
+ /** Indicates if we are connected to the Electric sync service. */
354
527
  isConnected() {
355
- return this.connected;
528
+ return __privateGet(this, _connected);
356
529
  }
357
530
  /** True during initial fetch. False afterwise. */
358
531
  isLoading() {
359
532
  return !this.isUpToDate;
360
533
  }
361
- notifyUpToDateSubscribers() {
362
- this.upToDateSubscribers.forEach(([callback]) => {
363
- callback();
364
- });
365
- }
366
- sendErrorToUpToDateSubscribers(error) {
367
- this.upToDateSubscribers.forEach(
368
- ([_, errorCallback]) => errorCallback(error)
534
+ };
535
+ _fetchClient2 = new WeakMap();
536
+ _messageParser = new WeakMap();
537
+ _subscribers = new WeakMap();
538
+ _upToDateSubscribers = new WeakMap();
539
+ _lastOffset = new WeakMap();
540
+ _lastSyncedAt = new WeakMap();
541
+ _isUpToDate = new WeakMap();
542
+ _connected = new WeakMap();
543
+ _shapeId = new WeakMap();
544
+ _schema = new WeakMap();
545
+ _ShapeStream_instances = new WeakSet();
546
+ publish_fn = function(messages) {
547
+ return __async(this, null, function* () {
548
+ yield Promise.all(
549
+ Array.from(__privateGet(this, _subscribers).values()).map((_0) => __async(this, [_0], function* ([callback, __]) {
550
+ try {
551
+ yield callback(messages);
552
+ } catch (err) {
553
+ queueMicrotask(() => {
554
+ throw err;
555
+ });
556
+ }
557
+ }))
369
558
  );
559
+ });
560
+ };
561
+ sendErrorToSubscribers_fn = function(error) {
562
+ __privateGet(this, _subscribers).forEach(([_, errorFn]) => {
563
+ errorFn == null ? void 0 : errorFn(error);
564
+ });
565
+ };
566
+ notifyUpToDateSubscribers_fn = function() {
567
+ __privateGet(this, _upToDateSubscribers).forEach(([callback]) => {
568
+ callback();
569
+ });
570
+ };
571
+ sendErrorToUpToDateSubscribers_fn = function(error) {
572
+ __privateGet(this, _upToDateSubscribers).forEach(
573
+ ([_, errorCallback]) => errorCallback(error)
574
+ );
575
+ };
576
+ /**
577
+ * Resets the state of the stream, optionally with a provided
578
+ * shape ID
579
+ */
580
+ reset_fn = function(shapeId) {
581
+ __privateSet(this, _lastOffset, `-1`);
582
+ __privateSet(this, _shapeId, shapeId);
583
+ __privateSet(this, _isUpToDate, false);
584
+ __privateSet(this, _connected, false);
585
+ __privateSet(this, _schema, void 0);
586
+ };
587
+ function validateOptions(options) {
588
+ if (!options.url) {
589
+ throw new Error(`Invalid shape option. It must provide the url`);
370
590
  }
371
- /**
372
- * Resets the state of the stream, optionally with a provided
373
- * shape ID
374
- */
375
- reset(shapeId) {
376
- this.lastOffset = `-1`;
377
- this.shapeId = shapeId;
378
- this.isUpToDate = false;
379
- this.connected = false;
380
- this.schema = void 0;
381
- }
382
- validateOptions(options) {
383
- if (!options.url) {
384
- throw new Error(`Invalid shape option. It must provide the url`);
385
- }
386
- if (options.signal && !(options.signal instanceof AbortSignal)) {
387
- throw new Error(
388
- `Invalid signal option. It must be an instance of AbortSignal.`
389
- );
390
- }
391
- if (options.offset !== void 0 && options.offset !== `-1` && !options.shapeId) {
392
- throw new Error(
393
- `shapeId is required if this isn't an initial fetch (i.e. offset > -1)`
394
- );
395
- }
591
+ if (options.signal && !(options.signal instanceof AbortSignal)) {
592
+ throw new Error(
593
+ `Invalid signal option. It must be an instance of AbortSignal.`
594
+ );
396
595
  }
397
- fetchWithBackoff(url) {
398
- return __async(this, null, function* () {
399
- const { initialDelay, maxDelay, multiplier } = this.backoffOptions;
400
- const signal = this.options.signal;
401
- let delay = initialDelay;
402
- let attempt = 0;
403
- while (true) {
404
- try {
405
- const result = yield this.fetchClient(url.toString(), { signal });
406
- if (result.ok) {
407
- if (this.options.subscribe) {
408
- this.connected = true;
409
- }
410
- return result;
411
- } else throw yield FetchError.fromResponse(result, url.toString());
412
- } catch (e) {
413
- this.connected = false;
414
- if (signal == null ? void 0 : signal.aborted) {
415
- return void 0;
416
- } else if (e instanceof FetchError && e.status >= 400 && e.status < 500) {
417
- throw e;
418
- } else {
419
- yield new Promise((resolve) => setTimeout(resolve, delay));
420
- delay = Math.min(delay * multiplier, maxDelay);
421
- attempt++;
422
- console.log(`Retry attempt #${attempt} after ${delay}ms`);
423
- }
424
- }
425
- }
426
- });
596
+ if (options.offset !== void 0 && options.offset !== `-1` && !options.shapeId) {
597
+ throw new Error(
598
+ `shapeId is required if this isn't an initial fetch (i.e. offset > -1)`
599
+ );
427
600
  }
428
- };
601
+ return;
602
+ }
603
+
604
+ // src/shape.ts
605
+ var _stream, _data, _subscribers2, _hasNotifiedSubscribersUpToDate, _error, _Shape_instances, process_fn, handleError_fn, notify_fn;
429
606
  var Shape = class {
430
607
  constructor(stream) {
431
- this.data = /* @__PURE__ */ new Map();
432
- this.subscribers = /* @__PURE__ */ new Map();
433
- this.error = false;
434
- this.hasNotifiedSubscribersUpToDate = false;
435
- this.stream = stream;
436
- this.stream.subscribe(this.process.bind(this), this.handleError.bind(this));
437
- const unsubscribe = this.stream.subscribeOnceToUpToDate(
608
+ __privateAdd(this, _Shape_instances);
609
+ __privateAdd(this, _stream);
610
+ __privateAdd(this, _data, /* @__PURE__ */ new Map());
611
+ __privateAdd(this, _subscribers2, /* @__PURE__ */ new Map());
612
+ __privateAdd(this, _hasNotifiedSubscribersUpToDate, false);
613
+ __privateAdd(this, _error, false);
614
+ __privateSet(this, _stream, stream);
615
+ __privateGet(this, _stream).subscribe(
616
+ __privateMethod(this, _Shape_instances, process_fn).bind(this),
617
+ __privateMethod(this, _Shape_instances, handleError_fn).bind(this)
618
+ );
619
+ const unsubscribe = __privateGet(this, _stream).subscribeOnceToUpToDate(
438
620
  () => {
439
621
  unsubscribe();
440
622
  },
441
623
  (e) => {
442
- this.handleError(e);
624
+ __privateMethod(this, _Shape_instances, handleError_fn).call(this, e);
443
625
  throw e;
444
626
  }
445
627
  );
446
628
  }
447
- lastSynced() {
448
- return this.stream.lastSynced();
449
- }
450
- isConnected() {
451
- return this.stream.isConnected();
452
- }
453
- /** True during initial fetch. False afterwise. */
454
- isLoading() {
455
- return this.stream.isLoading();
629
+ get isUpToDate() {
630
+ return __privateGet(this, _stream).isUpToDate;
456
631
  }
457
632
  get value() {
458
- return new Promise((resolve) => {
459
- if (this.stream.isUpToDate) {
633
+ return new Promise((resolve, reject) => {
634
+ if (__privateGet(this, _stream).isUpToDate) {
460
635
  resolve(this.valueSync);
461
636
  } else {
462
- const unsubscribe = this.stream.subscribeOnceToUpToDate(
463
- () => {
464
- unsubscribe();
465
- resolve(this.valueSync);
466
- },
467
- (e) => {
468
- throw e;
469
- }
470
- );
637
+ const unsubscribe = this.subscribe((shapeData) => {
638
+ unsubscribe();
639
+ if (__privateGet(this, _error)) reject(__privateGet(this, _error));
640
+ resolve(shapeData);
641
+ });
471
642
  }
472
643
  });
473
644
  }
474
645
  get valueSync() {
475
- return this.data;
646
+ return __privateGet(this, _data);
647
+ }
648
+ get error() {
649
+ return __privateGet(this, _error);
650
+ }
651
+ /** Unix time at which we last synced. Undefined when `isLoading` is true. */
652
+ lastSyncedAt() {
653
+ return __privateGet(this, _stream).lastSyncedAt();
654
+ }
655
+ /** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */
656
+ lastSynced() {
657
+ return __privateGet(this, _stream).lastSynced();
658
+ }
659
+ /** True during initial fetch. False afterwise. */
660
+ isLoading() {
661
+ return __privateGet(this, _stream).isLoading();
662
+ }
663
+ /** Indicates if we are connected to the Electric sync service. */
664
+ isConnected() {
665
+ return __privateGet(this, _stream).isConnected();
476
666
  }
477
667
  subscribe(callback) {
478
668
  const subscriptionId = Math.random();
479
- this.subscribers.set(subscriptionId, callback);
669
+ __privateGet(this, _subscribers2).set(subscriptionId, callback);
480
670
  return () => {
481
- this.subscribers.delete(subscriptionId);
671
+ __privateGet(this, _subscribers2).delete(subscriptionId);
482
672
  };
483
673
  }
484
674
  unsubscribeAll() {
485
- this.subscribers.clear();
675
+ __privateGet(this, _subscribers2).clear();
486
676
  }
487
677
  get numSubscribers() {
488
- return this.subscribers.size;
489
- }
490
- process(messages) {
491
- let dataMayHaveChanged = false;
492
- let isUpToDate = false;
493
- let newlyUpToDate = false;
494
- messages.forEach((message) => {
495
- if (isChangeMessage(message)) {
496
- dataMayHaveChanged = [`insert`, `update`, `delete`].includes(
497
- message.headers.operation
498
- );
499
- switch (message.headers.operation) {
500
- case `insert`:
501
- this.data.set(message.key, message.value);
502
- break;
503
- case `update`:
504
- this.data.set(message.key, __spreadValues(__spreadValues({}, this.data.get(message.key)), message.value));
505
- break;
506
- case `delete`:
507
- this.data.delete(message.key);
508
- break;
509
- }
510
- }
511
- if (isControlMessage(message)) {
512
- switch (message.headers.control) {
513
- case `up-to-date`:
514
- isUpToDate = true;
515
- if (!this.hasNotifiedSubscribersUpToDate) {
516
- newlyUpToDate = true;
517
- }
518
- break;
519
- case `must-refetch`:
520
- this.data.clear();
521
- this.error = false;
522
- isUpToDate = false;
523
- newlyUpToDate = false;
524
- break;
525
- }
678
+ return __privateGet(this, _subscribers2).size;
679
+ }
680
+ };
681
+ _stream = new WeakMap();
682
+ _data = new WeakMap();
683
+ _subscribers2 = new WeakMap();
684
+ _hasNotifiedSubscribersUpToDate = new WeakMap();
685
+ _error = new WeakMap();
686
+ _Shape_instances = new WeakSet();
687
+ process_fn = function(messages) {
688
+ let dataMayHaveChanged = false;
689
+ let isUpToDate = false;
690
+ let newlyUpToDate = false;
691
+ messages.forEach((message) => {
692
+ if (isChangeMessage(message)) {
693
+ dataMayHaveChanged = [`insert`, `update`, `delete`].includes(
694
+ message.headers.operation
695
+ );
696
+ switch (message.headers.operation) {
697
+ case `insert`:
698
+ __privateGet(this, _data).set(message.key, message.value);
699
+ break;
700
+ case `update`:
701
+ __privateGet(this, _data).set(message.key, __spreadValues(__spreadValues({}, __privateGet(this, _data).get(message.key)), message.value));
702
+ break;
703
+ case `delete`:
704
+ __privateGet(this, _data).delete(message.key);
705
+ break;
526
706
  }
527
- });
528
- if (newlyUpToDate || isUpToDate && dataMayHaveChanged) {
529
- this.hasNotifiedSubscribersUpToDate = true;
530
- this.notify();
531
707
  }
532
- }
533
- handleError(e) {
534
- if (e instanceof FetchError) {
535
- this.error = e;
536
- this.notify();
708
+ if (isControlMessage(message)) {
709
+ switch (message.headers.control) {
710
+ case `up-to-date`:
711
+ isUpToDate = true;
712
+ if (!__privateGet(this, _hasNotifiedSubscribersUpToDate)) {
713
+ newlyUpToDate = true;
714
+ }
715
+ break;
716
+ case `must-refetch`:
717
+ __privateGet(this, _data).clear();
718
+ __privateSet(this, _error, false);
719
+ isUpToDate = false;
720
+ newlyUpToDate = false;
721
+ break;
722
+ }
537
723
  }
724
+ });
725
+ if (newlyUpToDate || isUpToDate && dataMayHaveChanged) {
726
+ __privateSet(this, _hasNotifiedSubscribersUpToDate, true);
727
+ __privateMethod(this, _Shape_instances, notify_fn).call(this);
538
728
  }
539
- notify() {
540
- this.subscribers.forEach((callback) => {
541
- callback(this.valueSync);
542
- });
729
+ };
730
+ handleError_fn = function(e) {
731
+ if (e instanceof FetchError) {
732
+ __privateSet(this, _error, e);
733
+ __privateMethod(this, _Shape_instances, notify_fn).call(this);
543
734
  }
544
735
  };
736
+ notify_fn = function() {
737
+ __privateGet(this, _subscribers2).forEach((callback) => {
738
+ callback(this.valueSync);
739
+ });
740
+ };
545
741
  export {
546
742
  BackoffDefaults,
547
743
  FetchError,