@electric-sql/client 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -1
- package/dist/cjs/index.cjs +273 -196
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.browser.mjs +5 -1
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +37 -10
- package/dist/index.legacy-esm.js +269 -192
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +273 -196
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +106 -71
- package/src/error.ts +66 -0
- package/src/fetch.ts +52 -1
- package/src/parser.ts +2 -1
- package/src/shape.ts +9 -10
package/dist/index.legacy-esm.js
CHANGED
|
@@ -38,6 +38,89 @@ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot
|
|
|
38
38
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
39
39
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
40
40
|
|
|
41
|
+
// src/error.ts
|
|
42
|
+
var FetchError = class _FetchError extends Error {
|
|
43
|
+
constructor(status, text, json, headers, url, message) {
|
|
44
|
+
super(
|
|
45
|
+
message || `HTTP Error ${status} at ${url}: ${text != null ? text : JSON.stringify(json)}`
|
|
46
|
+
);
|
|
47
|
+
this.url = url;
|
|
48
|
+
this.name = `FetchError`;
|
|
49
|
+
this.status = status;
|
|
50
|
+
this.text = text;
|
|
51
|
+
this.json = json;
|
|
52
|
+
this.headers = headers;
|
|
53
|
+
}
|
|
54
|
+
static async fromResponse(response, url) {
|
|
55
|
+
const status = response.status;
|
|
56
|
+
const headers = Object.fromEntries([...response.headers.entries()]);
|
|
57
|
+
let text = void 0;
|
|
58
|
+
let json = void 0;
|
|
59
|
+
const contentType = response.headers.get(`content-type`);
|
|
60
|
+
if (contentType && contentType.includes(`application/json`)) {
|
|
61
|
+
json = await response.json();
|
|
62
|
+
} else {
|
|
63
|
+
text = await response.text();
|
|
64
|
+
}
|
|
65
|
+
return new _FetchError(status, text, json, headers, url);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var FetchBackoffAbortError = class extends Error {
|
|
69
|
+
constructor() {
|
|
70
|
+
super(`Fetch with backoff aborted`);
|
|
71
|
+
this.name = `FetchBackoffAbortError`;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var MissingShapeUrlError = class extends Error {
|
|
75
|
+
constructor() {
|
|
76
|
+
super(`Invalid shape options: missing required url parameter`);
|
|
77
|
+
this.name = `MissingShapeUrlError`;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var InvalidSignalError = class extends Error {
|
|
81
|
+
constructor() {
|
|
82
|
+
super(`Invalid signal option. It must be an instance of AbortSignal.`);
|
|
83
|
+
this.name = `InvalidSignalError`;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var MissingShapeHandleError = class extends Error {
|
|
87
|
+
constructor() {
|
|
88
|
+
super(
|
|
89
|
+
`shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`
|
|
90
|
+
);
|
|
91
|
+
this.name = `MissingShapeHandleError`;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var ReservedParamError = class extends Error {
|
|
95
|
+
constructor(reservedParams) {
|
|
96
|
+
super(
|
|
97
|
+
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
98
|
+
);
|
|
99
|
+
this.name = `ReservedParamError`;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var ParserNullValueError = class extends Error {
|
|
103
|
+
constructor(columnName) {
|
|
104
|
+
super(`Column "${columnName != null ? columnName : `unknown`}" does not allow NULL values`);
|
|
105
|
+
this.name = `ParserNullValueError`;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var MissingHeadersError = class extends Error {
|
|
109
|
+
constructor(url, missingHeaders) {
|
|
110
|
+
let msg = `The response for the shape request to ${url} didn't include the following required headers:
|
|
111
|
+
`;
|
|
112
|
+
missingHeaders.forEach((h) => {
|
|
113
|
+
msg += `- ${h}
|
|
114
|
+
`;
|
|
115
|
+
});
|
|
116
|
+
msg += `
|
|
117
|
+
This is often due to a proxy not setting CORS correctly so that all Electric headers can be read by the client.`;
|
|
118
|
+
msg += `
|
|
119
|
+
For more information visit the troubleshooting guide: /docs/guides/troubleshooting/missing-headers`;
|
|
120
|
+
super(msg);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
41
124
|
// src/parser.ts
|
|
42
125
|
var parseNumber = (value) => Number(value);
|
|
43
126
|
var parseBool = (value) => value === `true` || value === `t`;
|
|
@@ -139,7 +222,7 @@ function makeNullableParser(parser, columnInfo, columnName) {
|
|
|
139
222
|
return (value) => {
|
|
140
223
|
if (isPgNull(value)) {
|
|
141
224
|
if (!isNullable) {
|
|
142
|
-
throw new
|
|
225
|
+
throw new ParserNullValueError(columnName != null ? columnName : `unknown`);
|
|
143
226
|
}
|
|
144
227
|
return null;
|
|
145
228
|
}
|
|
@@ -161,39 +244,6 @@ function isUpToDateMessage(message) {
|
|
|
161
244
|
return isControlMessage(message) && message.headers.control === `up-to-date`;
|
|
162
245
|
}
|
|
163
246
|
|
|
164
|
-
// src/error.ts
|
|
165
|
-
var FetchError = class _FetchError extends Error {
|
|
166
|
-
constructor(status, text, json, headers, url, message) {
|
|
167
|
-
super(
|
|
168
|
-
message || `HTTP Error ${status} at ${url}: ${text != null ? text : JSON.stringify(json)}`
|
|
169
|
-
);
|
|
170
|
-
this.url = url;
|
|
171
|
-
this.name = `FetchError`;
|
|
172
|
-
this.status = status;
|
|
173
|
-
this.text = text;
|
|
174
|
-
this.json = json;
|
|
175
|
-
this.headers = headers;
|
|
176
|
-
}
|
|
177
|
-
static async fromResponse(response, url) {
|
|
178
|
-
const status = response.status;
|
|
179
|
-
const headers = Object.fromEntries([...response.headers.entries()]);
|
|
180
|
-
let text = void 0;
|
|
181
|
-
let json = void 0;
|
|
182
|
-
const contentType = response.headers.get(`content-type`);
|
|
183
|
-
if (contentType && contentType.includes(`application/json`)) {
|
|
184
|
-
json = await response.json();
|
|
185
|
-
} else {
|
|
186
|
-
text = await response.text();
|
|
187
|
-
}
|
|
188
|
-
return new _FetchError(status, text, json, headers, url);
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
var FetchBackoffAbortError = class extends Error {
|
|
192
|
-
constructor() {
|
|
193
|
-
super(`Fetch with backoff aborted`);
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
|
|
197
247
|
// src/constants.ts
|
|
198
248
|
var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
|
|
199
249
|
var SHAPE_HANDLE_HEADER = `electric-handle`;
|
|
@@ -281,6 +331,36 @@ function createFetchWithChunkBuffer(fetchClient, prefetchOptions = ChunkPrefetch
|
|
|
281
331
|
};
|
|
282
332
|
return prefetchClient;
|
|
283
333
|
}
|
|
334
|
+
var requiredElectricResponseHeaders = [
|
|
335
|
+
`electric-offset`,
|
|
336
|
+
`electric-handle`
|
|
337
|
+
];
|
|
338
|
+
var requiredLiveResponseHeaders = [`electric-cursor`];
|
|
339
|
+
var requiredNonLiveResponseHeaders = [`electric-schema`];
|
|
340
|
+
function createFetchWithResponseHeadersCheck(fetchClient) {
|
|
341
|
+
return async (...args) => {
|
|
342
|
+
const response = await fetchClient(...args);
|
|
343
|
+
if (response.ok) {
|
|
344
|
+
const headers = response.headers;
|
|
345
|
+
const missingHeaders = [];
|
|
346
|
+
const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
|
|
347
|
+
addMissingHeaders(requiredElectricResponseHeaders);
|
|
348
|
+
const input = args[0];
|
|
349
|
+
const urlString = input.toString();
|
|
350
|
+
const url = new URL(urlString);
|
|
351
|
+
if (url.searchParams.has(LIVE_QUERY_PARAM, `true`)) {
|
|
352
|
+
addMissingHeaders(requiredLiveResponseHeaders);
|
|
353
|
+
}
|
|
354
|
+
if (!url.searchParams.has(LIVE_QUERY_PARAM) || url.searchParams.has(LIVE_QUERY_PARAM, `false`)) {
|
|
355
|
+
addMissingHeaders(requiredNonLiveResponseHeaders);
|
|
356
|
+
}
|
|
357
|
+
if (missingHeaders.length > 0) {
|
|
358
|
+
throw new MissingHeadersError(urlString, missingHeaders);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
return response;
|
|
362
|
+
};
|
|
363
|
+
}
|
|
284
364
|
var _fetchClient, _maxPrefetchedRequests, _prefetchQueue, _queueHeadUrl, _queueTailUrl, _PrefetchQueue_instances, prefetch_fn;
|
|
285
365
|
var PrefetchQueue = class {
|
|
286
366
|
constructor(options) {
|
|
@@ -380,14 +460,14 @@ var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
380
460
|
WHERE_QUERY_PARAM,
|
|
381
461
|
REPLICA_PARAM
|
|
382
462
|
]);
|
|
383
|
-
var _fetchClient2, _messageParser, _subscribers,
|
|
463
|
+
var _error, _fetchClient2, _messageParser, _subscribers, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _databaseId, _schema, _onError, _replica, _ShapeStream_instances, start_fn, publish_fn, sendErrorToSubscribers_fn, reset_fn;
|
|
384
464
|
var _ShapeStream = class _ShapeStream {
|
|
385
465
|
constructor(options) {
|
|
386
466
|
__privateAdd(this, _ShapeStream_instances);
|
|
467
|
+
__privateAdd(this, _error, null);
|
|
387
468
|
__privateAdd(this, _fetchClient2);
|
|
388
469
|
__privateAdd(this, _messageParser);
|
|
389
470
|
__privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
|
|
390
|
-
__privateAdd(this, _upToDateSubscribers, /* @__PURE__ */ new Map());
|
|
391
471
|
__privateAdd(this, _lastOffset);
|
|
392
472
|
__privateAdd(this, _liveCacheBuster);
|
|
393
473
|
// Seconds since our Electric Epoch 😎
|
|
@@ -398,17 +478,18 @@ var _ShapeStream = class _ShapeStream {
|
|
|
398
478
|
__privateAdd(this, _shapeHandle);
|
|
399
479
|
__privateAdd(this, _databaseId);
|
|
400
480
|
__privateAdd(this, _schema);
|
|
401
|
-
__privateAdd(this,
|
|
481
|
+
__privateAdd(this, _onError);
|
|
402
482
|
__privateAdd(this, _replica);
|
|
403
483
|
var _a, _b, _c;
|
|
404
|
-
validateOptions(options);
|
|
405
484
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
485
|
+
validateOptions(this.options);
|
|
406
486
|
__privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
|
|
407
487
|
__privateSet(this, _liveCacheBuster, ``);
|
|
408
|
-
__privateSet(this, _shapeHandle, this.options.
|
|
488
|
+
__privateSet(this, _shapeHandle, this.options.handle);
|
|
409
489
|
__privateSet(this, _databaseId, this.options.databaseId);
|
|
410
490
|
__privateSet(this, _messageParser, new MessageParser(options.parser));
|
|
411
491
|
__privateSet(this, _replica, this.options.replica);
|
|
492
|
+
__privateSet(this, _onError, this.options.onError);
|
|
412
493
|
const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
|
|
413
494
|
const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
|
|
414
495
|
onFailedAttempt: () => {
|
|
@@ -417,126 +498,25 @@ var _ShapeStream = class _ShapeStream {
|
|
|
417
498
|
(_b2 = (_a2 = options.backoffOptions) == null ? void 0 : _a2.onFailedAttempt) == null ? void 0 : _b2.call(_a2);
|
|
418
499
|
}
|
|
419
500
|
}));
|
|
420
|
-
__privateSet(this, _fetchClient2,
|
|
421
|
-
|
|
501
|
+
__privateSet(this, _fetchClient2, createFetchWithResponseHeadersCheck(
|
|
502
|
+
createFetchWithChunkBuffer(fetchWithBackoffClient)
|
|
503
|
+
));
|
|
504
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
422
505
|
}
|
|
423
506
|
get shapeHandle() {
|
|
424
507
|
return __privateGet(this, _shapeHandle);
|
|
425
508
|
}
|
|
426
|
-
get isUpToDate() {
|
|
427
|
-
return __privateGet(this, _isUpToDate);
|
|
428
|
-
}
|
|
429
509
|
get error() {
|
|
430
510
|
return __privateGet(this, _error);
|
|
431
511
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
while (!(signal == null ? void 0 : signal.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
438
|
-
const fetchUrl = new URL(url);
|
|
439
|
-
if (this.options.params) {
|
|
440
|
-
const reservedParams = Object.keys(this.options.params).filter(
|
|
441
|
-
(key) => RESERVED_PARAMS.has(key)
|
|
442
|
-
);
|
|
443
|
-
if (reservedParams.length > 0) {
|
|
444
|
-
throw new Error(
|
|
445
|
-
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
for (const [key, value] of Object.entries(this.options.params)) {
|
|
449
|
-
fetchUrl.searchParams.set(key, value);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
|
|
453
|
-
if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
|
|
454
|
-
if (columns && columns.length > 0)
|
|
455
|
-
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
|
|
456
|
-
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
457
|
-
if (__privateGet(this, _isUpToDate)) {
|
|
458
|
-
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
459
|
-
fetchUrl.searchParams.set(
|
|
460
|
-
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
461
|
-
__privateGet(this, _liveCacheBuster)
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
if (__privateGet(this, _shapeHandle)) {
|
|
465
|
-
fetchUrl.searchParams.set(
|
|
466
|
-
SHAPE_HANDLE_QUERY_PARAM,
|
|
467
|
-
__privateGet(this, _shapeHandle)
|
|
468
|
-
);
|
|
469
|
-
}
|
|
470
|
-
if (__privateGet(this, _databaseId)) {
|
|
471
|
-
fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
|
|
472
|
-
}
|
|
473
|
-
if (((_a = __privateGet(this, _replica)) != null ? _a : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
|
|
474
|
-
fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
|
|
475
|
-
}
|
|
476
|
-
let response;
|
|
477
|
-
try {
|
|
478
|
-
response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
479
|
-
signal,
|
|
480
|
-
headers: this.options.headers
|
|
481
|
-
});
|
|
482
|
-
__privateSet(this, _connected, true);
|
|
483
|
-
} catch (e) {
|
|
484
|
-
if (e instanceof FetchBackoffAbortError) break;
|
|
485
|
-
if (!(e instanceof FetchError)) throw e;
|
|
486
|
-
if (e.status == 409) {
|
|
487
|
-
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
488
|
-
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
489
|
-
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
490
|
-
continue;
|
|
491
|
-
} else if (e.status >= 400 && e.status < 500) {
|
|
492
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToUpToDateSubscribers_fn).call(this, e);
|
|
493
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
494
|
-
throw e;
|
|
495
|
-
}
|
|
496
|
-
}
|
|
497
|
-
const { headers, status } = response;
|
|
498
|
-
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
499
|
-
if (shapeHandle) {
|
|
500
|
-
__privateSet(this, _shapeHandle, shapeHandle);
|
|
501
|
-
}
|
|
502
|
-
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
503
|
-
if (lastOffset) {
|
|
504
|
-
__privateSet(this, _lastOffset, lastOffset);
|
|
505
|
-
}
|
|
506
|
-
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
507
|
-
if (liveCacheBuster) {
|
|
508
|
-
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
509
|
-
}
|
|
510
|
-
const getSchema = () => {
|
|
511
|
-
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
512
|
-
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
513
|
-
};
|
|
514
|
-
__privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
|
|
515
|
-
const messages = status === 204 ? `[]` : await response.text();
|
|
516
|
-
if (status === 204) {
|
|
517
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
518
|
-
}
|
|
519
|
-
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
520
|
-
if (batch.length > 0) {
|
|
521
|
-
const prevUpToDate = __privateGet(this, _isUpToDate);
|
|
522
|
-
const lastMessage = batch[batch.length - 1];
|
|
523
|
-
if (isUpToDateMessage(lastMessage)) {
|
|
524
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
525
|
-
__privateSet(this, _isUpToDate, true);
|
|
526
|
-
}
|
|
527
|
-
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
528
|
-
if (!prevUpToDate && __privateGet(this, _isUpToDate)) {
|
|
529
|
-
__privateMethod(this, _ShapeStream_instances, notifyUpToDateSubscribers_fn).call(this);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
} catch (err) {
|
|
534
|
-
__privateSet(this, _error, err);
|
|
535
|
-
} finally {
|
|
536
|
-
__privateSet(this, _connected, false);
|
|
537
|
-
}
|
|
512
|
+
get isUpToDate() {
|
|
513
|
+
return __privateGet(this, _isUpToDate);
|
|
514
|
+
}
|
|
515
|
+
get lastOffset() {
|
|
516
|
+
return __privateGet(this, _lastOffset);
|
|
538
517
|
}
|
|
539
|
-
subscribe(callback, onError) {
|
|
518
|
+
subscribe(callback, onError = () => {
|
|
519
|
+
}) {
|
|
540
520
|
const subscriptionId = Math.random();
|
|
541
521
|
__privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
|
|
542
522
|
return () => {
|
|
@@ -546,16 +526,6 @@ var _ShapeStream = class _ShapeStream {
|
|
|
546
526
|
unsubscribeAll() {
|
|
547
527
|
__privateGet(this, _subscribers).clear();
|
|
548
528
|
}
|
|
549
|
-
subscribeOnceToUpToDate(callback, error) {
|
|
550
|
-
const subscriptionId = Math.random();
|
|
551
|
-
__privateGet(this, _upToDateSubscribers).set(subscriptionId, [callback, error]);
|
|
552
|
-
return () => {
|
|
553
|
-
__privateGet(this, _upToDateSubscribers).delete(subscriptionId);
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
unsubscribeAllUpToDateSubscribers() {
|
|
557
|
-
__privateGet(this, _upToDateSubscribers).clear();
|
|
558
|
-
}
|
|
559
529
|
/** Unix time at which we last synced. Undefined when `isLoading` is true. */
|
|
560
530
|
lastSyncedAt() {
|
|
561
531
|
return __privateGet(this, _lastSyncedAt);
|
|
@@ -571,13 +541,13 @@ var _ShapeStream = class _ShapeStream {
|
|
|
571
541
|
}
|
|
572
542
|
/** True during initial fetch. False afterwise. */
|
|
573
543
|
isLoading() {
|
|
574
|
-
return !this
|
|
544
|
+
return !__privateGet(this, _isUpToDate);
|
|
575
545
|
}
|
|
576
546
|
};
|
|
547
|
+
_error = new WeakMap();
|
|
577
548
|
_fetchClient2 = new WeakMap();
|
|
578
549
|
_messageParser = new WeakMap();
|
|
579
550
|
_subscribers = new WeakMap();
|
|
580
|
-
_upToDateSubscribers = new WeakMap();
|
|
581
551
|
_lastOffset = new WeakMap();
|
|
582
552
|
_liveCacheBuster = new WeakMap();
|
|
583
553
|
_lastSyncedAt = new WeakMap();
|
|
@@ -586,9 +556,125 @@ _connected = new WeakMap();
|
|
|
586
556
|
_shapeHandle = new WeakMap();
|
|
587
557
|
_databaseId = new WeakMap();
|
|
588
558
|
_schema = new WeakMap();
|
|
589
|
-
|
|
559
|
+
_onError = new WeakMap();
|
|
590
560
|
_replica = new WeakMap();
|
|
591
561
|
_ShapeStream_instances = new WeakSet();
|
|
562
|
+
start_fn = async function() {
|
|
563
|
+
var _a, _b, _c;
|
|
564
|
+
try {
|
|
565
|
+
while (!((_a = this.options.signal) == null ? void 0 : _a.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
566
|
+
const { url, table, where, columns, signal } = this.options;
|
|
567
|
+
const fetchUrl = new URL(url);
|
|
568
|
+
if (this.options.params) {
|
|
569
|
+
const reservedParams = Object.keys(this.options.params).filter(
|
|
570
|
+
(key) => RESERVED_PARAMS.has(key)
|
|
571
|
+
);
|
|
572
|
+
if (reservedParams.length > 0) {
|
|
573
|
+
throw new Error(
|
|
574
|
+
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
for (const [key, value] of Object.entries(this.options.params)) {
|
|
578
|
+
fetchUrl.searchParams.set(key, value);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
|
|
582
|
+
if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
|
|
583
|
+
if (columns && columns.length > 0)
|
|
584
|
+
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
|
|
585
|
+
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
586
|
+
if (__privateGet(this, _isUpToDate)) {
|
|
587
|
+
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
588
|
+
fetchUrl.searchParams.set(
|
|
589
|
+
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
590
|
+
__privateGet(this, _liveCacheBuster)
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
if (__privateGet(this, _shapeHandle)) {
|
|
594
|
+
fetchUrl.searchParams.set(
|
|
595
|
+
SHAPE_HANDLE_QUERY_PARAM,
|
|
596
|
+
__privateGet(this, _shapeHandle)
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
if (__privateGet(this, _databaseId)) {
|
|
600
|
+
fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
|
|
601
|
+
}
|
|
602
|
+
if (((_b = __privateGet(this, _replica)) != null ? _b : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
|
|
603
|
+
fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
|
|
604
|
+
}
|
|
605
|
+
let response;
|
|
606
|
+
try {
|
|
607
|
+
response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
608
|
+
signal,
|
|
609
|
+
headers: this.options.headers
|
|
610
|
+
});
|
|
611
|
+
__privateSet(this, _connected, true);
|
|
612
|
+
} catch (e) {
|
|
613
|
+
if (e instanceof FetchBackoffAbortError) break;
|
|
614
|
+
if (!(e instanceof FetchError)) throw e;
|
|
615
|
+
if (e.status == 409) {
|
|
616
|
+
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
617
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
618
|
+
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
619
|
+
continue;
|
|
620
|
+
} else if (e.status >= 400 && e.status < 500) {
|
|
621
|
+
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
622
|
+
throw e;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const { headers, status } = response;
|
|
626
|
+
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
627
|
+
if (shapeHandle) {
|
|
628
|
+
__privateSet(this, _shapeHandle, shapeHandle);
|
|
629
|
+
}
|
|
630
|
+
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
631
|
+
if (lastOffset) {
|
|
632
|
+
__privateSet(this, _lastOffset, lastOffset);
|
|
633
|
+
}
|
|
634
|
+
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
635
|
+
if (liveCacheBuster) {
|
|
636
|
+
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
637
|
+
}
|
|
638
|
+
const getSchema = () => {
|
|
639
|
+
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
640
|
+
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
641
|
+
};
|
|
642
|
+
__privateSet(this, _schema, (_c = __privateGet(this, _schema)) != null ? _c : getSchema());
|
|
643
|
+
const messages = status === 204 ? `[]` : await response.text();
|
|
644
|
+
if (status === 204) {
|
|
645
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
646
|
+
}
|
|
647
|
+
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
648
|
+
if (batch.length > 0) {
|
|
649
|
+
const lastMessage = batch[batch.length - 1];
|
|
650
|
+
if (isUpToDateMessage(lastMessage)) {
|
|
651
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
652
|
+
__privateSet(this, _isUpToDate, true);
|
|
653
|
+
}
|
|
654
|
+
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
} catch (err) {
|
|
658
|
+
__privateSet(this, _error, err);
|
|
659
|
+
if (__privateGet(this, _onError)) {
|
|
660
|
+
const retryOpts = await __privateGet(this, _onError).call(this, err);
|
|
661
|
+
if (typeof retryOpts === `object`) {
|
|
662
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
|
|
663
|
+
if (`params` in retryOpts) {
|
|
664
|
+
this.options.params = retryOpts.params;
|
|
665
|
+
}
|
|
666
|
+
if (`headers` in retryOpts) {
|
|
667
|
+
this.options.headers = retryOpts.headers;
|
|
668
|
+
}
|
|
669
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
670
|
+
}
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
throw err;
|
|
674
|
+
} finally {
|
|
675
|
+
__privateSet(this, _connected, false);
|
|
676
|
+
}
|
|
677
|
+
};
|
|
592
678
|
publish_fn = async function(messages) {
|
|
593
679
|
await Promise.all(
|
|
594
680
|
Array.from(__privateGet(this, _subscribers).values()).map(async ([callback, __]) => {
|
|
@@ -607,24 +693,14 @@ sendErrorToSubscribers_fn = function(error) {
|
|
|
607
693
|
errorFn == null ? void 0 : errorFn(error);
|
|
608
694
|
});
|
|
609
695
|
};
|
|
610
|
-
notifyUpToDateSubscribers_fn = function() {
|
|
611
|
-
__privateGet(this, _upToDateSubscribers).forEach(([callback]) => {
|
|
612
|
-
callback();
|
|
613
|
-
});
|
|
614
|
-
};
|
|
615
|
-
sendErrorToUpToDateSubscribers_fn = function(error) {
|
|
616
|
-
__privateGet(this, _upToDateSubscribers).forEach(
|
|
617
|
-
([_, errorCallback]) => errorCallback(error)
|
|
618
|
-
);
|
|
619
|
-
};
|
|
620
696
|
/**
|
|
621
697
|
* Resets the state of the stream, optionally with a provided
|
|
622
698
|
* shape handle
|
|
623
699
|
*/
|
|
624
|
-
reset_fn = function(
|
|
700
|
+
reset_fn = function(handle) {
|
|
625
701
|
__privateSet(this, _lastOffset, `-1`);
|
|
626
702
|
__privateSet(this, _liveCacheBuster, ``);
|
|
627
|
-
__privateSet(this, _shapeHandle,
|
|
703
|
+
__privateSet(this, _shapeHandle, handle);
|
|
628
704
|
__privateSet(this, _isUpToDate, false);
|
|
629
705
|
__privateSet(this, _connected, false);
|
|
630
706
|
__privateSet(this, _schema, void 0);
|
|
@@ -636,17 +712,21 @@ _ShapeStream.Replica = {
|
|
|
636
712
|
var ShapeStream = _ShapeStream;
|
|
637
713
|
function validateOptions(options) {
|
|
638
714
|
if (!options.url) {
|
|
639
|
-
throw new
|
|
715
|
+
throw new MissingShapeUrlError();
|
|
640
716
|
}
|
|
641
717
|
if (options.signal && !(options.signal instanceof AbortSignal)) {
|
|
642
|
-
throw new
|
|
643
|
-
`Invalid signal option. It must be an instance of AbortSignal.`
|
|
644
|
-
);
|
|
718
|
+
throw new InvalidSignalError();
|
|
645
719
|
}
|
|
646
|
-
if (options.offset !== void 0 && options.offset !== `-1` && !options.
|
|
647
|
-
throw new
|
|
648
|
-
|
|
720
|
+
if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
|
|
721
|
+
throw new MissingShapeHandleError();
|
|
722
|
+
}
|
|
723
|
+
if (options.params) {
|
|
724
|
+
const reservedParams = Object.keys(options.params).filter(
|
|
725
|
+
(key) => RESERVED_PARAMS.has(key)
|
|
649
726
|
);
|
|
727
|
+
if (reservedParams.length > 0) {
|
|
728
|
+
throw new ReservedParamError(reservedParams);
|
|
729
|
+
}
|
|
650
730
|
}
|
|
651
731
|
return;
|
|
652
732
|
}
|
|
@@ -666,19 +746,16 @@ var Shape = class {
|
|
|
666
746
|
__privateMethod(this, _Shape_instances, process_fn).bind(this),
|
|
667
747
|
__privateMethod(this, _Shape_instances, handleError_fn).bind(this)
|
|
668
748
|
);
|
|
669
|
-
const unsubscribe = __privateGet(this, _stream).subscribeOnceToUpToDate(
|
|
670
|
-
() => {
|
|
671
|
-
unsubscribe();
|
|
672
|
-
},
|
|
673
|
-
(e) => {
|
|
674
|
-
__privateMethod(this, _Shape_instances, handleError_fn).call(this, e);
|
|
675
|
-
throw e;
|
|
676
|
-
}
|
|
677
|
-
);
|
|
678
749
|
}
|
|
679
750
|
get isUpToDate() {
|
|
680
751
|
return __privateGet(this, _stream).isUpToDate;
|
|
681
752
|
}
|
|
753
|
+
get lastOffset() {
|
|
754
|
+
return __privateGet(this, _stream).lastOffset;
|
|
755
|
+
}
|
|
756
|
+
get handle() {
|
|
757
|
+
return __privateGet(this, _stream).shapeHandle;
|
|
758
|
+
}
|
|
682
759
|
get rows() {
|
|
683
760
|
return this.value.then((v) => Array.from(v.values()));
|
|
684
761
|
}
|