@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/README.md +2 -0
- package/dist/cjs/index.cjs +436 -240
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.browser.mjs +1 -1
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +57 -50
- package/dist/index.legacy-esm.js +434 -236
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +436 -240
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +179 -422
- package/src/constants.ts +8 -0
- package/src/error.ts +50 -0
- package/src/fetch.ts +272 -0
- package/src/helpers.ts +6 -0
- package/src/index.ts +4 -1
- package/src/shape.ts +197 -0
- package/src/types.ts +2 -0
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/
|
|
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
|
|
230
|
-
this
|
|
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
|
|
233
|
-
this
|
|
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
|
-
|
|
400
|
+
validateOptions(options);
|
|
236
401
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
237
|
-
this
|
|
238
|
-
this
|
|
239
|
-
this
|
|
240
|
-
|
|
241
|
-
|
|
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
|
|
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
|
|
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(
|
|
253
|
-
fetchUrl.searchParams.set(
|
|
254
|
-
if (this
|
|
255
|
-
fetchUrl.searchParams.set(
|
|
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
|
|
258
|
-
fetchUrl.searchParams.set(
|
|
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
|
-
|
|
263
|
-
|
|
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.
|
|
269
|
-
this.
|
|
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[
|
|
273
|
-
this.
|
|
274
|
-
this.
|
|
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.
|
|
278
|
-
this.
|
|
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(
|
|
461
|
+
const shapeId = headers.get(SHAPE_ID_HEADER);
|
|
284
462
|
if (shapeId) {
|
|
285
|
-
this
|
|
463
|
+
__privateSet(this, _shapeId, shapeId);
|
|
286
464
|
}
|
|
287
|
-
const lastOffset = headers.get(
|
|
465
|
+
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
288
466
|
if (lastOffset) {
|
|
289
|
-
this
|
|
467
|
+
__privateSet(this, _lastOffset, lastOffset);
|
|
290
468
|
}
|
|
291
469
|
const getSchema = () => {
|
|
292
|
-
const schemaHeader = headers.get(
|
|
470
|
+
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
293
471
|
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
294
472
|
};
|
|
295
|
-
this
|
|
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
|
|
476
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
299
477
|
}
|
|
300
|
-
const batch = this.
|
|
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 (
|
|
304
|
-
this
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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
|
|
493
|
+
__privateSet(this, _connected, false);
|
|
315
494
|
}
|
|
316
495
|
});
|
|
317
496
|
}
|
|
318
497
|
subscribe(callback, onError) {
|
|
319
498
|
const subscriptionId = Math.random();
|
|
320
|
-
|
|
321
|
-
this.subscribers.set(subscriptionId, [subscriber, onError]);
|
|
499
|
+
__privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
|
|
322
500
|
return () => {
|
|
323
|
-
this.
|
|
501
|
+
__privateGet(this, _subscribers).delete(subscriptionId);
|
|
324
502
|
};
|
|
325
503
|
}
|
|
326
504
|
unsubscribeAll() {
|
|
327
|
-
this.
|
|
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.
|
|
509
|
+
__privateGet(this, _upToDateSubscribers).set(subscriptionId, [callback, error]);
|
|
342
510
|
return () => {
|
|
343
|
-
this.
|
|
511
|
+
__privateGet(this, _upToDateSubscribers).delete(subscriptionId);
|
|
344
512
|
};
|
|
345
513
|
}
|
|
346
514
|
unsubscribeAllUpToDateSubscribers() {
|
|
347
|
-
this.
|
|
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
|
|
352
|
-
return Date.now() - this
|
|
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
|
|
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
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
|
|
373
|
-
|
|
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
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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
|
|
432
|
-
this
|
|
433
|
-
this
|
|
434
|
-
this
|
|
435
|
-
this
|
|
436
|
-
|
|
437
|
-
|
|
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.
|
|
624
|
+
__privateMethod(this, _Shape_instances, handleError_fn).call(this, e);
|
|
443
625
|
throw e;
|
|
444
626
|
}
|
|
445
627
|
);
|
|
446
628
|
}
|
|
447
|
-
|
|
448
|
-
return this.
|
|
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.
|
|
633
|
+
return new Promise((resolve, reject) => {
|
|
634
|
+
if (__privateGet(this, _stream).isUpToDate) {
|
|
460
635
|
resolve(this.valueSync);
|
|
461
636
|
} else {
|
|
462
|
-
const unsubscribe = this.
|
|
463
|
-
()
|
|
464
|
-
|
|
465
|
-
|
|
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
|
|
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.
|
|
669
|
+
__privateGet(this, _subscribers2).set(subscriptionId, callback);
|
|
480
670
|
return () => {
|
|
481
|
-
this.
|
|
671
|
+
__privateGet(this, _subscribers2).delete(subscriptionId);
|
|
482
672
|
};
|
|
483
673
|
}
|
|
484
674
|
unsubscribeAll() {
|
|
485
|
-
this.
|
|
675
|
+
__privateGet(this, _subscribers2).clear();
|
|
486
676
|
}
|
|
487
677
|
get numSubscribers() {
|
|
488
|
-
return this.
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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,
|