@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.mjs
CHANGED
|
@@ -58,6 +58,91 @@ var __async = (__this, __arguments, generator) => {
|
|
|
58
58
|
});
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
// src/error.ts
|
|
62
|
+
var FetchError = class _FetchError extends Error {
|
|
63
|
+
constructor(status, text, json, headers, url, message) {
|
|
64
|
+
super(
|
|
65
|
+
message || `HTTP Error ${status} at ${url}: ${text != null ? text : JSON.stringify(json)}`
|
|
66
|
+
);
|
|
67
|
+
this.url = url;
|
|
68
|
+
this.name = `FetchError`;
|
|
69
|
+
this.status = status;
|
|
70
|
+
this.text = text;
|
|
71
|
+
this.json = json;
|
|
72
|
+
this.headers = headers;
|
|
73
|
+
}
|
|
74
|
+
static fromResponse(response, url) {
|
|
75
|
+
return __async(this, null, function* () {
|
|
76
|
+
const status = response.status;
|
|
77
|
+
const headers = Object.fromEntries([...response.headers.entries()]);
|
|
78
|
+
let text = void 0;
|
|
79
|
+
let json = void 0;
|
|
80
|
+
const contentType = response.headers.get(`content-type`);
|
|
81
|
+
if (contentType && contentType.includes(`application/json`)) {
|
|
82
|
+
json = yield response.json();
|
|
83
|
+
} else {
|
|
84
|
+
text = yield response.text();
|
|
85
|
+
}
|
|
86
|
+
return new _FetchError(status, text, json, headers, url);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var FetchBackoffAbortError = class extends Error {
|
|
91
|
+
constructor() {
|
|
92
|
+
super(`Fetch with backoff aborted`);
|
|
93
|
+
this.name = `FetchBackoffAbortError`;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var MissingShapeUrlError = class extends Error {
|
|
97
|
+
constructor() {
|
|
98
|
+
super(`Invalid shape options: missing required url parameter`);
|
|
99
|
+
this.name = `MissingShapeUrlError`;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var InvalidSignalError = class extends Error {
|
|
103
|
+
constructor() {
|
|
104
|
+
super(`Invalid signal option. It must be an instance of AbortSignal.`);
|
|
105
|
+
this.name = `InvalidSignalError`;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var MissingShapeHandleError = class extends Error {
|
|
109
|
+
constructor() {
|
|
110
|
+
super(
|
|
111
|
+
`shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`
|
|
112
|
+
);
|
|
113
|
+
this.name = `MissingShapeHandleError`;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
var ReservedParamError = class extends Error {
|
|
117
|
+
constructor(reservedParams) {
|
|
118
|
+
super(
|
|
119
|
+
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
120
|
+
);
|
|
121
|
+
this.name = `ReservedParamError`;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var ParserNullValueError = class extends Error {
|
|
125
|
+
constructor(columnName) {
|
|
126
|
+
super(`Column "${columnName != null ? columnName : `unknown`}" does not allow NULL values`);
|
|
127
|
+
this.name = `ParserNullValueError`;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
var MissingHeadersError = class extends Error {
|
|
131
|
+
constructor(url, missingHeaders) {
|
|
132
|
+
let msg = `The response for the shape request to ${url} didn't include the following required headers:
|
|
133
|
+
`;
|
|
134
|
+
missingHeaders.forEach((h) => {
|
|
135
|
+
msg += `- ${h}
|
|
136
|
+
`;
|
|
137
|
+
});
|
|
138
|
+
msg += `
|
|
139
|
+
This is often due to a proxy not setting CORS correctly so that all Electric headers can be read by the client.`;
|
|
140
|
+
msg += `
|
|
141
|
+
For more information visit the troubleshooting guide: /docs/guides/troubleshooting/missing-headers`;
|
|
142
|
+
super(msg);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
61
146
|
// src/parser.ts
|
|
62
147
|
var parseNumber = (value) => Number(value);
|
|
63
148
|
var parseBool = (value) => value === `true` || value === `t`;
|
|
@@ -159,7 +244,7 @@ function makeNullableParser(parser, columnInfo, columnName) {
|
|
|
159
244
|
return (value) => {
|
|
160
245
|
if (isPgNull(value)) {
|
|
161
246
|
if (!isNullable) {
|
|
162
|
-
throw new
|
|
247
|
+
throw new ParserNullValueError(columnName != null ? columnName : `unknown`);
|
|
163
248
|
}
|
|
164
249
|
return null;
|
|
165
250
|
}
|
|
@@ -181,41 +266,6 @@ function isUpToDateMessage(message) {
|
|
|
181
266
|
return isControlMessage(message) && message.headers.control === `up-to-date`;
|
|
182
267
|
}
|
|
183
268
|
|
|
184
|
-
// src/error.ts
|
|
185
|
-
var FetchError = class _FetchError extends Error {
|
|
186
|
-
constructor(status, text, json, headers, url, message) {
|
|
187
|
-
super(
|
|
188
|
-
message || `HTTP Error ${status} at ${url}: ${text != null ? text : JSON.stringify(json)}`
|
|
189
|
-
);
|
|
190
|
-
this.url = url;
|
|
191
|
-
this.name = `FetchError`;
|
|
192
|
-
this.status = status;
|
|
193
|
-
this.text = text;
|
|
194
|
-
this.json = json;
|
|
195
|
-
this.headers = headers;
|
|
196
|
-
}
|
|
197
|
-
static fromResponse(response, url) {
|
|
198
|
-
return __async(this, null, function* () {
|
|
199
|
-
const status = response.status;
|
|
200
|
-
const headers = Object.fromEntries([...response.headers.entries()]);
|
|
201
|
-
let text = void 0;
|
|
202
|
-
let json = void 0;
|
|
203
|
-
const contentType = response.headers.get(`content-type`);
|
|
204
|
-
if (contentType && contentType.includes(`application/json`)) {
|
|
205
|
-
json = yield response.json();
|
|
206
|
-
} else {
|
|
207
|
-
text = yield response.text();
|
|
208
|
-
}
|
|
209
|
-
return new _FetchError(status, text, json, headers, url);
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
var FetchBackoffAbortError = class extends Error {
|
|
214
|
-
constructor() {
|
|
215
|
-
super(`Fetch with backoff aborted`);
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
|
|
219
269
|
// src/constants.ts
|
|
220
270
|
var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
|
|
221
271
|
var SHAPE_HANDLE_HEADER = `electric-handle`;
|
|
@@ -303,6 +353,36 @@ function createFetchWithChunkBuffer(fetchClient, prefetchOptions = ChunkPrefetch
|
|
|
303
353
|
});
|
|
304
354
|
return prefetchClient;
|
|
305
355
|
}
|
|
356
|
+
var requiredElectricResponseHeaders = [
|
|
357
|
+
`electric-offset`,
|
|
358
|
+
`electric-handle`
|
|
359
|
+
];
|
|
360
|
+
var requiredLiveResponseHeaders = [`electric-cursor`];
|
|
361
|
+
var requiredNonLiveResponseHeaders = [`electric-schema`];
|
|
362
|
+
function createFetchWithResponseHeadersCheck(fetchClient) {
|
|
363
|
+
return (...args) => __async(this, null, function* () {
|
|
364
|
+
const response = yield fetchClient(...args);
|
|
365
|
+
if (response.ok) {
|
|
366
|
+
const headers = response.headers;
|
|
367
|
+
const missingHeaders = [];
|
|
368
|
+
const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
|
|
369
|
+
addMissingHeaders(requiredElectricResponseHeaders);
|
|
370
|
+
const input = args[0];
|
|
371
|
+
const urlString = input.toString();
|
|
372
|
+
const url = new URL(urlString);
|
|
373
|
+
if (url.searchParams.has(LIVE_QUERY_PARAM, `true`)) {
|
|
374
|
+
addMissingHeaders(requiredLiveResponseHeaders);
|
|
375
|
+
}
|
|
376
|
+
if (!url.searchParams.has(LIVE_QUERY_PARAM) || url.searchParams.has(LIVE_QUERY_PARAM, `false`)) {
|
|
377
|
+
addMissingHeaders(requiredNonLiveResponseHeaders);
|
|
378
|
+
}
|
|
379
|
+
if (missingHeaders.length > 0) {
|
|
380
|
+
throw new MissingHeadersError(urlString, missingHeaders);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return response;
|
|
384
|
+
});
|
|
385
|
+
}
|
|
306
386
|
var _fetchClient, _maxPrefetchedRequests, _prefetchQueue, _queueHeadUrl, _queueTailUrl, _PrefetchQueue_instances, prefetch_fn;
|
|
307
387
|
var PrefetchQueue = class {
|
|
308
388
|
constructor(options) {
|
|
@@ -402,14 +482,14 @@ var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
|
402
482
|
WHERE_QUERY_PARAM,
|
|
403
483
|
REPLICA_PARAM
|
|
404
484
|
]);
|
|
405
|
-
var _fetchClient2, _messageParser, _subscribers,
|
|
485
|
+
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;
|
|
406
486
|
var _ShapeStream = class _ShapeStream {
|
|
407
487
|
constructor(options) {
|
|
408
488
|
__privateAdd(this, _ShapeStream_instances);
|
|
489
|
+
__privateAdd(this, _error, null);
|
|
409
490
|
__privateAdd(this, _fetchClient2);
|
|
410
491
|
__privateAdd(this, _messageParser);
|
|
411
492
|
__privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
|
|
412
|
-
__privateAdd(this, _upToDateSubscribers, /* @__PURE__ */ new Map());
|
|
413
493
|
__privateAdd(this, _lastOffset);
|
|
414
494
|
__privateAdd(this, _liveCacheBuster);
|
|
415
495
|
// Seconds since our Electric Epoch 😎
|
|
@@ -420,17 +500,18 @@ var _ShapeStream = class _ShapeStream {
|
|
|
420
500
|
__privateAdd(this, _shapeHandle);
|
|
421
501
|
__privateAdd(this, _databaseId);
|
|
422
502
|
__privateAdd(this, _schema);
|
|
423
|
-
__privateAdd(this,
|
|
503
|
+
__privateAdd(this, _onError);
|
|
424
504
|
__privateAdd(this, _replica);
|
|
425
505
|
var _a, _b, _c;
|
|
426
|
-
validateOptions(options);
|
|
427
506
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
507
|
+
validateOptions(this.options);
|
|
428
508
|
__privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
|
|
429
509
|
__privateSet(this, _liveCacheBuster, ``);
|
|
430
|
-
__privateSet(this, _shapeHandle, this.options.
|
|
510
|
+
__privateSet(this, _shapeHandle, this.options.handle);
|
|
431
511
|
__privateSet(this, _databaseId, this.options.databaseId);
|
|
432
512
|
__privateSet(this, _messageParser, new MessageParser(options.parser));
|
|
433
513
|
__privateSet(this, _replica, this.options.replica);
|
|
514
|
+
__privateSet(this, _onError, this.options.onError);
|
|
434
515
|
const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
|
|
435
516
|
const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
|
|
436
517
|
onFailedAttempt: () => {
|
|
@@ -439,128 +520,25 @@ var _ShapeStream = class _ShapeStream {
|
|
|
439
520
|
(_b2 = (_a2 = options.backoffOptions) == null ? void 0 : _a2.onFailedAttempt) == null ? void 0 : _b2.call(_a2);
|
|
440
521
|
}
|
|
441
522
|
}));
|
|
442
|
-
__privateSet(this, _fetchClient2,
|
|
443
|
-
|
|
523
|
+
__privateSet(this, _fetchClient2, createFetchWithResponseHeadersCheck(
|
|
524
|
+
createFetchWithChunkBuffer(fetchWithBackoffClient)
|
|
525
|
+
));
|
|
526
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
444
527
|
}
|
|
445
528
|
get shapeHandle() {
|
|
446
529
|
return __privateGet(this, _shapeHandle);
|
|
447
530
|
}
|
|
448
|
-
get isUpToDate() {
|
|
449
|
-
return __privateGet(this, _isUpToDate);
|
|
450
|
-
}
|
|
451
531
|
get error() {
|
|
452
532
|
return __privateGet(this, _error);
|
|
453
533
|
}
|
|
454
|
-
|
|
455
|
-
return
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
try {
|
|
460
|
-
while (!(signal == null ? void 0 : signal.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
461
|
-
const fetchUrl = new URL(url);
|
|
462
|
-
if (this.options.params) {
|
|
463
|
-
const reservedParams = Object.keys(this.options.params).filter(
|
|
464
|
-
(key) => RESERVED_PARAMS.has(key)
|
|
465
|
-
);
|
|
466
|
-
if (reservedParams.length > 0) {
|
|
467
|
-
throw new Error(
|
|
468
|
-
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
for (const [key, value] of Object.entries(this.options.params)) {
|
|
472
|
-
fetchUrl.searchParams.set(key, value);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
|
|
476
|
-
if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
|
|
477
|
-
if (columns && columns.length > 0)
|
|
478
|
-
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
|
|
479
|
-
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
480
|
-
if (__privateGet(this, _isUpToDate)) {
|
|
481
|
-
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
482
|
-
fetchUrl.searchParams.set(
|
|
483
|
-
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
484
|
-
__privateGet(this, _liveCacheBuster)
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
if (__privateGet(this, _shapeHandle)) {
|
|
488
|
-
fetchUrl.searchParams.set(
|
|
489
|
-
SHAPE_HANDLE_QUERY_PARAM,
|
|
490
|
-
__privateGet(this, _shapeHandle)
|
|
491
|
-
);
|
|
492
|
-
}
|
|
493
|
-
if (__privateGet(this, _databaseId)) {
|
|
494
|
-
fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
|
|
495
|
-
}
|
|
496
|
-
if (((_a = __privateGet(this, _replica)) != null ? _a : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
|
|
497
|
-
fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
|
|
498
|
-
}
|
|
499
|
-
let response;
|
|
500
|
-
try {
|
|
501
|
-
response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
502
|
-
signal,
|
|
503
|
-
headers: this.options.headers
|
|
504
|
-
});
|
|
505
|
-
__privateSet(this, _connected, true);
|
|
506
|
-
} catch (e) {
|
|
507
|
-
if (e instanceof FetchBackoffAbortError) break;
|
|
508
|
-
if (!(e instanceof FetchError)) throw e;
|
|
509
|
-
if (e.status == 409) {
|
|
510
|
-
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
511
|
-
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
512
|
-
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
513
|
-
continue;
|
|
514
|
-
} else if (e.status >= 400 && e.status < 500) {
|
|
515
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToUpToDateSubscribers_fn).call(this, e);
|
|
516
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
517
|
-
throw e;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
const { headers, status } = response;
|
|
521
|
-
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
522
|
-
if (shapeHandle) {
|
|
523
|
-
__privateSet(this, _shapeHandle, shapeHandle);
|
|
524
|
-
}
|
|
525
|
-
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
526
|
-
if (lastOffset) {
|
|
527
|
-
__privateSet(this, _lastOffset, lastOffset);
|
|
528
|
-
}
|
|
529
|
-
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
530
|
-
if (liveCacheBuster) {
|
|
531
|
-
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
532
|
-
}
|
|
533
|
-
const getSchema = () => {
|
|
534
|
-
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
535
|
-
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
536
|
-
};
|
|
537
|
-
__privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
|
|
538
|
-
const messages = status === 204 ? `[]` : yield response.text();
|
|
539
|
-
if (status === 204) {
|
|
540
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
541
|
-
}
|
|
542
|
-
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
543
|
-
if (batch.length > 0) {
|
|
544
|
-
const prevUpToDate = __privateGet(this, _isUpToDate);
|
|
545
|
-
const lastMessage = batch[batch.length - 1];
|
|
546
|
-
if (isUpToDateMessage(lastMessage)) {
|
|
547
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
548
|
-
__privateSet(this, _isUpToDate, true);
|
|
549
|
-
}
|
|
550
|
-
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
551
|
-
if (!prevUpToDate && __privateGet(this, _isUpToDate)) {
|
|
552
|
-
__privateMethod(this, _ShapeStream_instances, notifyUpToDateSubscribers_fn).call(this);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
} catch (err) {
|
|
557
|
-
__privateSet(this, _error, err);
|
|
558
|
-
} finally {
|
|
559
|
-
__privateSet(this, _connected, false);
|
|
560
|
-
}
|
|
561
|
-
});
|
|
534
|
+
get isUpToDate() {
|
|
535
|
+
return __privateGet(this, _isUpToDate);
|
|
536
|
+
}
|
|
537
|
+
get lastOffset() {
|
|
538
|
+
return __privateGet(this, _lastOffset);
|
|
562
539
|
}
|
|
563
|
-
subscribe(callback, onError) {
|
|
540
|
+
subscribe(callback, onError = () => {
|
|
541
|
+
}) {
|
|
564
542
|
const subscriptionId = Math.random();
|
|
565
543
|
__privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
|
|
566
544
|
return () => {
|
|
@@ -570,16 +548,6 @@ var _ShapeStream = class _ShapeStream {
|
|
|
570
548
|
unsubscribeAll() {
|
|
571
549
|
__privateGet(this, _subscribers).clear();
|
|
572
550
|
}
|
|
573
|
-
subscribeOnceToUpToDate(callback, error) {
|
|
574
|
-
const subscriptionId = Math.random();
|
|
575
|
-
__privateGet(this, _upToDateSubscribers).set(subscriptionId, [callback, error]);
|
|
576
|
-
return () => {
|
|
577
|
-
__privateGet(this, _upToDateSubscribers).delete(subscriptionId);
|
|
578
|
-
};
|
|
579
|
-
}
|
|
580
|
-
unsubscribeAllUpToDateSubscribers() {
|
|
581
|
-
__privateGet(this, _upToDateSubscribers).clear();
|
|
582
|
-
}
|
|
583
551
|
/** Unix time at which we last synced. Undefined when `isLoading` is true. */
|
|
584
552
|
lastSyncedAt() {
|
|
585
553
|
return __privateGet(this, _lastSyncedAt);
|
|
@@ -595,13 +563,13 @@ var _ShapeStream = class _ShapeStream {
|
|
|
595
563
|
}
|
|
596
564
|
/** True during initial fetch. False afterwise. */
|
|
597
565
|
isLoading() {
|
|
598
|
-
return !this
|
|
566
|
+
return !__privateGet(this, _isUpToDate);
|
|
599
567
|
}
|
|
600
568
|
};
|
|
569
|
+
_error = new WeakMap();
|
|
601
570
|
_fetchClient2 = new WeakMap();
|
|
602
571
|
_messageParser = new WeakMap();
|
|
603
572
|
_subscribers = new WeakMap();
|
|
604
|
-
_upToDateSubscribers = new WeakMap();
|
|
605
573
|
_lastOffset = new WeakMap();
|
|
606
574
|
_liveCacheBuster = new WeakMap();
|
|
607
575
|
_lastSyncedAt = new WeakMap();
|
|
@@ -610,9 +578,127 @@ _connected = new WeakMap();
|
|
|
610
578
|
_shapeHandle = new WeakMap();
|
|
611
579
|
_databaseId = new WeakMap();
|
|
612
580
|
_schema = new WeakMap();
|
|
613
|
-
|
|
581
|
+
_onError = new WeakMap();
|
|
614
582
|
_replica = new WeakMap();
|
|
615
583
|
_ShapeStream_instances = new WeakSet();
|
|
584
|
+
start_fn = function() {
|
|
585
|
+
return __async(this, null, function* () {
|
|
586
|
+
var _a, _b, _c;
|
|
587
|
+
try {
|
|
588
|
+
while (!((_a = this.options.signal) == null ? void 0 : _a.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
589
|
+
const { url, table, where, columns, signal } = this.options;
|
|
590
|
+
const fetchUrl = new URL(url);
|
|
591
|
+
if (this.options.params) {
|
|
592
|
+
const reservedParams = Object.keys(this.options.params).filter(
|
|
593
|
+
(key) => RESERVED_PARAMS.has(key)
|
|
594
|
+
);
|
|
595
|
+
if (reservedParams.length > 0) {
|
|
596
|
+
throw new Error(
|
|
597
|
+
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
for (const [key, value] of Object.entries(this.options.params)) {
|
|
601
|
+
fetchUrl.searchParams.set(key, value);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
|
|
605
|
+
if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
|
|
606
|
+
if (columns && columns.length > 0)
|
|
607
|
+
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
|
|
608
|
+
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
609
|
+
if (__privateGet(this, _isUpToDate)) {
|
|
610
|
+
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
611
|
+
fetchUrl.searchParams.set(
|
|
612
|
+
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
613
|
+
__privateGet(this, _liveCacheBuster)
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
if (__privateGet(this, _shapeHandle)) {
|
|
617
|
+
fetchUrl.searchParams.set(
|
|
618
|
+
SHAPE_HANDLE_QUERY_PARAM,
|
|
619
|
+
__privateGet(this, _shapeHandle)
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
if (__privateGet(this, _databaseId)) {
|
|
623
|
+
fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
|
|
624
|
+
}
|
|
625
|
+
if (((_b = __privateGet(this, _replica)) != null ? _b : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
|
|
626
|
+
fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
|
|
627
|
+
}
|
|
628
|
+
let response;
|
|
629
|
+
try {
|
|
630
|
+
response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
631
|
+
signal,
|
|
632
|
+
headers: this.options.headers
|
|
633
|
+
});
|
|
634
|
+
__privateSet(this, _connected, true);
|
|
635
|
+
} catch (e) {
|
|
636
|
+
if (e instanceof FetchBackoffAbortError) break;
|
|
637
|
+
if (!(e instanceof FetchError)) throw e;
|
|
638
|
+
if (e.status == 409) {
|
|
639
|
+
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
640
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
641
|
+
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
642
|
+
continue;
|
|
643
|
+
} else if (e.status >= 400 && e.status < 500) {
|
|
644
|
+
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
645
|
+
throw e;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
const { headers, status } = response;
|
|
649
|
+
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
650
|
+
if (shapeHandle) {
|
|
651
|
+
__privateSet(this, _shapeHandle, shapeHandle);
|
|
652
|
+
}
|
|
653
|
+
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
654
|
+
if (lastOffset) {
|
|
655
|
+
__privateSet(this, _lastOffset, lastOffset);
|
|
656
|
+
}
|
|
657
|
+
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
658
|
+
if (liveCacheBuster) {
|
|
659
|
+
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
660
|
+
}
|
|
661
|
+
const getSchema = () => {
|
|
662
|
+
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
663
|
+
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
664
|
+
};
|
|
665
|
+
__privateSet(this, _schema, (_c = __privateGet(this, _schema)) != null ? _c : getSchema());
|
|
666
|
+
const messages = status === 204 ? `[]` : yield response.text();
|
|
667
|
+
if (status === 204) {
|
|
668
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
669
|
+
}
|
|
670
|
+
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
671
|
+
if (batch.length > 0) {
|
|
672
|
+
const lastMessage = batch[batch.length - 1];
|
|
673
|
+
if (isUpToDateMessage(lastMessage)) {
|
|
674
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
675
|
+
__privateSet(this, _isUpToDate, true);
|
|
676
|
+
}
|
|
677
|
+
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
} catch (err) {
|
|
681
|
+
__privateSet(this, _error, err);
|
|
682
|
+
if (__privateGet(this, _onError)) {
|
|
683
|
+
const retryOpts = yield __privateGet(this, _onError).call(this, err);
|
|
684
|
+
if (typeof retryOpts === `object`) {
|
|
685
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
|
|
686
|
+
if (`params` in retryOpts) {
|
|
687
|
+
this.options.params = retryOpts.params;
|
|
688
|
+
}
|
|
689
|
+
if (`headers` in retryOpts) {
|
|
690
|
+
this.options.headers = retryOpts.headers;
|
|
691
|
+
}
|
|
692
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
693
|
+
}
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
throw err;
|
|
697
|
+
} finally {
|
|
698
|
+
__privateSet(this, _connected, false);
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
};
|
|
616
702
|
publish_fn = function(messages) {
|
|
617
703
|
return __async(this, null, function* () {
|
|
618
704
|
yield Promise.all(
|
|
@@ -633,24 +719,14 @@ sendErrorToSubscribers_fn = function(error) {
|
|
|
633
719
|
errorFn == null ? void 0 : errorFn(error);
|
|
634
720
|
});
|
|
635
721
|
};
|
|
636
|
-
notifyUpToDateSubscribers_fn = function() {
|
|
637
|
-
__privateGet(this, _upToDateSubscribers).forEach(([callback]) => {
|
|
638
|
-
callback();
|
|
639
|
-
});
|
|
640
|
-
};
|
|
641
|
-
sendErrorToUpToDateSubscribers_fn = function(error) {
|
|
642
|
-
__privateGet(this, _upToDateSubscribers).forEach(
|
|
643
|
-
([_, errorCallback]) => errorCallback(error)
|
|
644
|
-
);
|
|
645
|
-
};
|
|
646
722
|
/**
|
|
647
723
|
* Resets the state of the stream, optionally with a provided
|
|
648
724
|
* shape handle
|
|
649
725
|
*/
|
|
650
|
-
reset_fn = function(
|
|
726
|
+
reset_fn = function(handle) {
|
|
651
727
|
__privateSet(this, _lastOffset, `-1`);
|
|
652
728
|
__privateSet(this, _liveCacheBuster, ``);
|
|
653
|
-
__privateSet(this, _shapeHandle,
|
|
729
|
+
__privateSet(this, _shapeHandle, handle);
|
|
654
730
|
__privateSet(this, _isUpToDate, false);
|
|
655
731
|
__privateSet(this, _connected, false);
|
|
656
732
|
__privateSet(this, _schema, void 0);
|
|
@@ -662,17 +738,21 @@ _ShapeStream.Replica = {
|
|
|
662
738
|
var ShapeStream = _ShapeStream;
|
|
663
739
|
function validateOptions(options) {
|
|
664
740
|
if (!options.url) {
|
|
665
|
-
throw new
|
|
741
|
+
throw new MissingShapeUrlError();
|
|
666
742
|
}
|
|
667
743
|
if (options.signal && !(options.signal instanceof AbortSignal)) {
|
|
668
|
-
throw new
|
|
669
|
-
`Invalid signal option. It must be an instance of AbortSignal.`
|
|
670
|
-
);
|
|
744
|
+
throw new InvalidSignalError();
|
|
671
745
|
}
|
|
672
|
-
if (options.offset !== void 0 && options.offset !== `-1` && !options.
|
|
673
|
-
throw new
|
|
674
|
-
|
|
746
|
+
if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
|
|
747
|
+
throw new MissingShapeHandleError();
|
|
748
|
+
}
|
|
749
|
+
if (options.params) {
|
|
750
|
+
const reservedParams = Object.keys(options.params).filter(
|
|
751
|
+
(key) => RESERVED_PARAMS.has(key)
|
|
675
752
|
);
|
|
753
|
+
if (reservedParams.length > 0) {
|
|
754
|
+
throw new ReservedParamError(reservedParams);
|
|
755
|
+
}
|
|
676
756
|
}
|
|
677
757
|
return;
|
|
678
758
|
}
|
|
@@ -692,19 +772,16 @@ var Shape = class {
|
|
|
692
772
|
__privateMethod(this, _Shape_instances, process_fn).bind(this),
|
|
693
773
|
__privateMethod(this, _Shape_instances, handleError_fn).bind(this)
|
|
694
774
|
);
|
|
695
|
-
const unsubscribe = __privateGet(this, _stream).subscribeOnceToUpToDate(
|
|
696
|
-
() => {
|
|
697
|
-
unsubscribe();
|
|
698
|
-
},
|
|
699
|
-
(e) => {
|
|
700
|
-
__privateMethod(this, _Shape_instances, handleError_fn).call(this, e);
|
|
701
|
-
throw e;
|
|
702
|
-
}
|
|
703
|
-
);
|
|
704
775
|
}
|
|
705
776
|
get isUpToDate() {
|
|
706
777
|
return __privateGet(this, _stream).isUpToDate;
|
|
707
778
|
}
|
|
779
|
+
get lastOffset() {
|
|
780
|
+
return __privateGet(this, _stream).lastOffset;
|
|
781
|
+
}
|
|
782
|
+
get handle() {
|
|
783
|
+
return __privateGet(this, _stream).shapeHandle;
|
|
784
|
+
}
|
|
708
785
|
get rows() {
|
|
709
786
|
return this.value.then((v) => Array.from(v.values()));
|
|
710
787
|
}
|