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