@electric-sql/client 0.7.3 → 0.9.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 +85 -4
- package/dist/cjs/index.cjs +246 -231
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/index.browser.mjs +5 -4
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +56 -37
- package/dist/index.legacy-esm.js +242 -227
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +246 -231
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +153 -139
- package/src/constants.ts +0 -1
- package/src/error.ts +55 -0
- package/src/fetch.ts +2 -1
- package/src/parser.ts +2 -1
- package/src/shape.ts +10 -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,59 +244,12 @@ 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
|
-
var MissingHeadersError = class extends Error {
|
|
197
|
-
constructor(url, missingHeaders) {
|
|
198
|
-
let msg = `The response for the shape request to ${url} didn't include the following required headers:
|
|
199
|
-
`;
|
|
200
|
-
missingHeaders.forEach((h) => {
|
|
201
|
-
msg += `- ${h}
|
|
202
|
-
`;
|
|
203
|
-
});
|
|
204
|
-
msg += `
|
|
205
|
-
This is often due to a proxy not setting CORS correctly so that all Electric headers can be read by the client.`;
|
|
206
|
-
super(msg);
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
|
|
210
247
|
// src/constants.ts
|
|
211
248
|
var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
|
|
212
249
|
var SHAPE_HANDLE_HEADER = `electric-handle`;
|
|
213
250
|
var CHUNK_LAST_OFFSET_HEADER = `electric-offset`;
|
|
214
251
|
var SHAPE_SCHEMA_HEADER = `electric-schema`;
|
|
215
252
|
var CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`;
|
|
216
|
-
var DATABASE_ID_QUERY_PARAM = `database_id`;
|
|
217
253
|
var COLUMNS_QUERY_PARAM = `columns`;
|
|
218
254
|
var LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`;
|
|
219
255
|
var SHAPE_HANDLE_QUERY_PARAM = `handle`;
|
|
@@ -306,7 +342,7 @@ function createFetchWithResponseHeadersCheck(fetchClient) {
|
|
|
306
342
|
if (response.ok) {
|
|
307
343
|
const headers = response.headers;
|
|
308
344
|
const missingHeaders = [];
|
|
309
|
-
const addMissingHeaders = (requiredHeaders) => requiredHeaders.filter((h) => !headers.has(h));
|
|
345
|
+
const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
|
|
310
346
|
addMissingHeaders(requiredElectricResponseHeaders);
|
|
311
347
|
const input = args[0];
|
|
312
348
|
const urlString = input.toString();
|
|
@@ -399,6 +435,7 @@ function getNextChunkUrl(url, res) {
|
|
|
399
435
|
if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return;
|
|
400
436
|
nextUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, shapeHandle);
|
|
401
437
|
nextUrl.searchParams.set(OFFSET_QUERY_PARAM, lastOffset);
|
|
438
|
+
nextUrl.searchParams.sort();
|
|
402
439
|
return nextUrl.toString();
|
|
403
440
|
}
|
|
404
441
|
function chainAborter(aborter, sourceSignal) {
|
|
@@ -413,24 +450,26 @@ function chainAborter(aborter, sourceSignal) {
|
|
|
413
450
|
|
|
414
451
|
// src/client.ts
|
|
415
452
|
var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
416
|
-
DATABASE_ID_QUERY_PARAM,
|
|
417
|
-
COLUMNS_QUERY_PARAM,
|
|
418
453
|
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
419
454
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
420
455
|
LIVE_QUERY_PARAM,
|
|
421
|
-
OFFSET_QUERY_PARAM
|
|
422
|
-
TABLE_QUERY_PARAM,
|
|
423
|
-
WHERE_QUERY_PARAM,
|
|
424
|
-
REPLICA_PARAM
|
|
456
|
+
OFFSET_QUERY_PARAM
|
|
425
457
|
]);
|
|
426
|
-
|
|
427
|
-
|
|
458
|
+
function toInternalParams(params) {
|
|
459
|
+
const result = {};
|
|
460
|
+
for (const [key, value] of Object.entries(params)) {
|
|
461
|
+
result[key] = Array.isArray(value) ? value.join(`,`) : value;
|
|
462
|
+
}
|
|
463
|
+
return result;
|
|
464
|
+
}
|
|
465
|
+
var _error, _fetchClient2, _messageParser, _subscribers, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _schema, _onError, _ShapeStream_instances, start_fn, publish_fn, sendErrorToSubscribers_fn, reset_fn;
|
|
466
|
+
var ShapeStream = class {
|
|
428
467
|
constructor(options) {
|
|
429
468
|
__privateAdd(this, _ShapeStream_instances);
|
|
469
|
+
__privateAdd(this, _error, null);
|
|
430
470
|
__privateAdd(this, _fetchClient2);
|
|
431
471
|
__privateAdd(this, _messageParser);
|
|
432
472
|
__privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
|
|
433
|
-
__privateAdd(this, _upToDateSubscribers, /* @__PURE__ */ new Map());
|
|
434
473
|
__privateAdd(this, _lastOffset);
|
|
435
474
|
__privateAdd(this, _liveCacheBuster);
|
|
436
475
|
// Seconds since our Electric Epoch 😎
|
|
@@ -439,19 +478,16 @@ var _ShapeStream = class _ShapeStream {
|
|
|
439
478
|
__privateAdd(this, _isUpToDate, false);
|
|
440
479
|
__privateAdd(this, _connected, false);
|
|
441
480
|
__privateAdd(this, _shapeHandle);
|
|
442
|
-
__privateAdd(this, _databaseId);
|
|
443
481
|
__privateAdd(this, _schema);
|
|
444
|
-
__privateAdd(this,
|
|
445
|
-
__privateAdd(this, _replica);
|
|
482
|
+
__privateAdd(this, _onError);
|
|
446
483
|
var _a, _b, _c;
|
|
447
|
-
validateOptions(options);
|
|
448
484
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
485
|
+
validateOptions(this.options);
|
|
449
486
|
__privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
|
|
450
487
|
__privateSet(this, _liveCacheBuster, ``);
|
|
451
|
-
__privateSet(this, _shapeHandle, this.options.
|
|
452
|
-
__privateSet(this, _databaseId, this.options.databaseId);
|
|
488
|
+
__privateSet(this, _shapeHandle, this.options.handle);
|
|
453
489
|
__privateSet(this, _messageParser, new MessageParser(options.parser));
|
|
454
|
-
__privateSet(this,
|
|
490
|
+
__privateSet(this, _onError, this.options.onError);
|
|
455
491
|
const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
|
|
456
492
|
const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
|
|
457
493
|
onFailedAttempt: () => {
|
|
@@ -463,129 +499,22 @@ var _ShapeStream = class _ShapeStream {
|
|
|
463
499
|
__privateSet(this, _fetchClient2, createFetchWithResponseHeadersCheck(
|
|
464
500
|
createFetchWithChunkBuffer(fetchWithBackoffClient)
|
|
465
501
|
));
|
|
466
|
-
this.
|
|
502
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
467
503
|
}
|
|
468
504
|
get shapeHandle() {
|
|
469
505
|
return __privateGet(this, _shapeHandle);
|
|
470
506
|
}
|
|
507
|
+
get error() {
|
|
508
|
+
return __privateGet(this, _error);
|
|
509
|
+
}
|
|
471
510
|
get isUpToDate() {
|
|
472
511
|
return __privateGet(this, _isUpToDate);
|
|
473
512
|
}
|
|
474
513
|
get lastOffset() {
|
|
475
514
|
return __privateGet(this, _lastOffset);
|
|
476
515
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
async start() {
|
|
481
|
-
var _a, _b;
|
|
482
|
-
__privateSet(this, _isUpToDate, false);
|
|
483
|
-
const { url, table, where, columns, signal } = this.options;
|
|
484
|
-
try {
|
|
485
|
-
while (!(signal == null ? void 0 : signal.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
486
|
-
const fetchUrl = new URL(url);
|
|
487
|
-
if (this.options.params) {
|
|
488
|
-
const reservedParams = Object.keys(this.options.params).filter(
|
|
489
|
-
(key) => RESERVED_PARAMS.has(key)
|
|
490
|
-
);
|
|
491
|
-
if (reservedParams.length > 0) {
|
|
492
|
-
throw new Error(
|
|
493
|
-
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
for (const [key, value] of Object.entries(this.options.params)) {
|
|
497
|
-
fetchUrl.searchParams.set(key, value);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
|
|
501
|
-
if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
|
|
502
|
-
if (columns && columns.length > 0)
|
|
503
|
-
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
|
|
504
|
-
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
505
|
-
if (__privateGet(this, _isUpToDate)) {
|
|
506
|
-
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
507
|
-
fetchUrl.searchParams.set(
|
|
508
|
-
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
509
|
-
__privateGet(this, _liveCacheBuster)
|
|
510
|
-
);
|
|
511
|
-
}
|
|
512
|
-
if (__privateGet(this, _shapeHandle)) {
|
|
513
|
-
fetchUrl.searchParams.set(
|
|
514
|
-
SHAPE_HANDLE_QUERY_PARAM,
|
|
515
|
-
__privateGet(this, _shapeHandle)
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
|
-
if (__privateGet(this, _databaseId)) {
|
|
519
|
-
fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
|
|
520
|
-
}
|
|
521
|
-
if (((_a = __privateGet(this, _replica)) != null ? _a : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
|
|
522
|
-
fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
|
|
523
|
-
}
|
|
524
|
-
let response;
|
|
525
|
-
try {
|
|
526
|
-
response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
527
|
-
signal,
|
|
528
|
-
headers: this.options.headers
|
|
529
|
-
});
|
|
530
|
-
__privateSet(this, _connected, true);
|
|
531
|
-
} catch (e) {
|
|
532
|
-
if (e instanceof FetchBackoffAbortError) break;
|
|
533
|
-
if (e instanceof MissingHeadersError) throw e;
|
|
534
|
-
if (!(e instanceof FetchError)) throw e;
|
|
535
|
-
if (e.status == 409) {
|
|
536
|
-
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
537
|
-
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
538
|
-
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
539
|
-
continue;
|
|
540
|
-
} else if (e.status >= 400 && e.status < 500) {
|
|
541
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToUpToDateSubscribers_fn).call(this, e);
|
|
542
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
543
|
-
throw e;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
const { headers, status } = response;
|
|
547
|
-
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
548
|
-
if (shapeHandle) {
|
|
549
|
-
__privateSet(this, _shapeHandle, shapeHandle);
|
|
550
|
-
}
|
|
551
|
-
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
552
|
-
if (lastOffset) {
|
|
553
|
-
__privateSet(this, _lastOffset, lastOffset);
|
|
554
|
-
}
|
|
555
|
-
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
556
|
-
if (liveCacheBuster) {
|
|
557
|
-
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
558
|
-
}
|
|
559
|
-
const getSchema = () => {
|
|
560
|
-
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
561
|
-
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
562
|
-
};
|
|
563
|
-
__privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
|
|
564
|
-
const messages = status === 204 ? `[]` : await response.text();
|
|
565
|
-
if (status === 204) {
|
|
566
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
567
|
-
}
|
|
568
|
-
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
569
|
-
if (batch.length > 0) {
|
|
570
|
-
const prevUpToDate = __privateGet(this, _isUpToDate);
|
|
571
|
-
const lastMessage = batch[batch.length - 1];
|
|
572
|
-
if (isUpToDateMessage(lastMessage)) {
|
|
573
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
574
|
-
__privateSet(this, _isUpToDate, true);
|
|
575
|
-
}
|
|
576
|
-
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
577
|
-
if (!prevUpToDate && __privateGet(this, _isUpToDate)) {
|
|
578
|
-
__privateMethod(this, _ShapeStream_instances, notifyUpToDateSubscribers_fn).call(this);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
} catch (err) {
|
|
583
|
-
__privateSet(this, _error, err);
|
|
584
|
-
} finally {
|
|
585
|
-
__privateSet(this, _connected, false);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
subscribe(callback, onError) {
|
|
516
|
+
subscribe(callback, onError = () => {
|
|
517
|
+
}) {
|
|
589
518
|
const subscriptionId = Math.random();
|
|
590
519
|
__privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
|
|
591
520
|
return () => {
|
|
@@ -595,16 +524,6 @@ var _ShapeStream = class _ShapeStream {
|
|
|
595
524
|
unsubscribeAll() {
|
|
596
525
|
__privateGet(this, _subscribers).clear();
|
|
597
526
|
}
|
|
598
|
-
subscribeOnceToUpToDate(callback, error) {
|
|
599
|
-
const subscriptionId = Math.random();
|
|
600
|
-
__privateGet(this, _upToDateSubscribers).set(subscriptionId, [callback, error]);
|
|
601
|
-
return () => {
|
|
602
|
-
__privateGet(this, _upToDateSubscribers).delete(subscriptionId);
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
unsubscribeAllUpToDateSubscribers() {
|
|
606
|
-
__privateGet(this, _upToDateSubscribers).clear();
|
|
607
|
-
}
|
|
608
527
|
/** Unix time at which we last synced. Undefined when `isLoading` is true. */
|
|
609
528
|
lastSyncedAt() {
|
|
610
529
|
return __privateGet(this, _lastSyncedAt);
|
|
@@ -620,24 +539,143 @@ var _ShapeStream = class _ShapeStream {
|
|
|
620
539
|
}
|
|
621
540
|
/** True during initial fetch. False afterwise. */
|
|
622
541
|
isLoading() {
|
|
623
|
-
return !this
|
|
542
|
+
return !__privateGet(this, _isUpToDate);
|
|
624
543
|
}
|
|
625
544
|
};
|
|
545
|
+
_error = new WeakMap();
|
|
626
546
|
_fetchClient2 = new WeakMap();
|
|
627
547
|
_messageParser = new WeakMap();
|
|
628
548
|
_subscribers = new WeakMap();
|
|
629
|
-
_upToDateSubscribers = new WeakMap();
|
|
630
549
|
_lastOffset = new WeakMap();
|
|
631
550
|
_liveCacheBuster = new WeakMap();
|
|
632
551
|
_lastSyncedAt = new WeakMap();
|
|
633
552
|
_isUpToDate = new WeakMap();
|
|
634
553
|
_connected = new WeakMap();
|
|
635
554
|
_shapeHandle = new WeakMap();
|
|
636
|
-
_databaseId = new WeakMap();
|
|
637
555
|
_schema = new WeakMap();
|
|
638
|
-
|
|
639
|
-
_replica = new WeakMap();
|
|
556
|
+
_onError = new WeakMap();
|
|
640
557
|
_ShapeStream_instances = new WeakSet();
|
|
558
|
+
start_fn = async function() {
|
|
559
|
+
var _a, _b;
|
|
560
|
+
try {
|
|
561
|
+
while (!((_a = this.options.signal) == null ? void 0 : _a.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
562
|
+
const { url, signal } = this.options;
|
|
563
|
+
const fetchUrl = new URL(url);
|
|
564
|
+
if (this.options.params) {
|
|
565
|
+
const reservedParams = Object.keys(this.options.params).filter(
|
|
566
|
+
(key) => RESERVED_PARAMS.has(key)
|
|
567
|
+
);
|
|
568
|
+
if (reservedParams.length > 0) {
|
|
569
|
+
throw new Error(
|
|
570
|
+
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
const params = toInternalParams(this.options.params);
|
|
574
|
+
if (params.table)
|
|
575
|
+
fetchUrl.searchParams.set(TABLE_QUERY_PARAM, params.table);
|
|
576
|
+
if (params.where)
|
|
577
|
+
fetchUrl.searchParams.set(WHERE_QUERY_PARAM, params.where);
|
|
578
|
+
if (params.columns)
|
|
579
|
+
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, params.columns);
|
|
580
|
+
if (params.replica)
|
|
581
|
+
fetchUrl.searchParams.set(REPLICA_PARAM, params.replica);
|
|
582
|
+
const customParams = __spreadValues({}, params);
|
|
583
|
+
delete customParams.table;
|
|
584
|
+
delete customParams.where;
|
|
585
|
+
delete customParams.columns;
|
|
586
|
+
delete customParams.replica;
|
|
587
|
+
for (const [key, value] of Object.entries(customParams)) {
|
|
588
|
+
fetchUrl.searchParams.set(key, value);
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
592
|
+
if (__privateGet(this, _isUpToDate)) {
|
|
593
|
+
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
594
|
+
fetchUrl.searchParams.set(
|
|
595
|
+
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
596
|
+
__privateGet(this, _liveCacheBuster)
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
if (__privateGet(this, _shapeHandle)) {
|
|
600
|
+
fetchUrl.searchParams.set(
|
|
601
|
+
SHAPE_HANDLE_QUERY_PARAM,
|
|
602
|
+
__privateGet(this, _shapeHandle)
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
fetchUrl.searchParams.sort();
|
|
606
|
+
let response;
|
|
607
|
+
try {
|
|
608
|
+
response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
609
|
+
signal,
|
|
610
|
+
headers: this.options.headers
|
|
611
|
+
});
|
|
612
|
+
__privateSet(this, _connected, true);
|
|
613
|
+
} catch (e) {
|
|
614
|
+
if (e instanceof FetchBackoffAbortError) break;
|
|
615
|
+
if (!(e instanceof FetchError)) throw e;
|
|
616
|
+
if (e.status == 409) {
|
|
617
|
+
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
618
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
619
|
+
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
620
|
+
continue;
|
|
621
|
+
} else if (e.status >= 400 && e.status < 500) {
|
|
622
|
+
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
623
|
+
throw e;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
const { headers, status } = response;
|
|
627
|
+
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
628
|
+
if (shapeHandle) {
|
|
629
|
+
__privateSet(this, _shapeHandle, shapeHandle);
|
|
630
|
+
}
|
|
631
|
+
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
632
|
+
if (lastOffset) {
|
|
633
|
+
__privateSet(this, _lastOffset, lastOffset);
|
|
634
|
+
}
|
|
635
|
+
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
636
|
+
if (liveCacheBuster) {
|
|
637
|
+
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
638
|
+
}
|
|
639
|
+
const getSchema = () => {
|
|
640
|
+
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
641
|
+
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
642
|
+
};
|
|
643
|
+
__privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
|
|
644
|
+
const messages = status === 204 ? `[]` : await response.text();
|
|
645
|
+
if (status === 204) {
|
|
646
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
647
|
+
}
|
|
648
|
+
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
649
|
+
if (batch.length > 0) {
|
|
650
|
+
const lastMessage = batch[batch.length - 1];
|
|
651
|
+
if (isUpToDateMessage(lastMessage)) {
|
|
652
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
653
|
+
__privateSet(this, _isUpToDate, true);
|
|
654
|
+
}
|
|
655
|
+
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
} catch (err) {
|
|
659
|
+
__privateSet(this, _error, err);
|
|
660
|
+
if (__privateGet(this, _onError)) {
|
|
661
|
+
const retryOpts = await __privateGet(this, _onError).call(this, err);
|
|
662
|
+
if (typeof retryOpts === `object`) {
|
|
663
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
|
|
664
|
+
if (`params` in retryOpts) {
|
|
665
|
+
this.options.params = retryOpts.params;
|
|
666
|
+
}
|
|
667
|
+
if (`headers` in retryOpts) {
|
|
668
|
+
this.options.headers = retryOpts.headers;
|
|
669
|
+
}
|
|
670
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
671
|
+
}
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
throw err;
|
|
675
|
+
} finally {
|
|
676
|
+
__privateSet(this, _connected, false);
|
|
677
|
+
}
|
|
678
|
+
};
|
|
641
679
|
publish_fn = async function(messages) {
|
|
642
680
|
await Promise.all(
|
|
643
681
|
Array.from(__privateGet(this, _subscribers).values()).map(async ([callback, __]) => {
|
|
@@ -656,55 +694,38 @@ sendErrorToSubscribers_fn = function(error) {
|
|
|
656
694
|
errorFn == null ? void 0 : errorFn(error);
|
|
657
695
|
});
|
|
658
696
|
};
|
|
659
|
-
notifyUpToDateSubscribers_fn = function() {
|
|
660
|
-
__privateGet(this, _upToDateSubscribers).forEach(([callback]) => {
|
|
661
|
-
callback();
|
|
662
|
-
});
|
|
663
|
-
};
|
|
664
|
-
sendErrorToUpToDateSubscribers_fn = function(error) {
|
|
665
|
-
__privateGet(this, _upToDateSubscribers).forEach(
|
|
666
|
-
([_, errorCallback]) => errorCallback(error)
|
|
667
|
-
);
|
|
668
|
-
};
|
|
669
697
|
/**
|
|
670
698
|
* Resets the state of the stream, optionally with a provided
|
|
671
699
|
* shape handle
|
|
672
700
|
*/
|
|
673
|
-
reset_fn = function(
|
|
701
|
+
reset_fn = function(handle) {
|
|
674
702
|
__privateSet(this, _lastOffset, `-1`);
|
|
675
703
|
__privateSet(this, _liveCacheBuster, ``);
|
|
676
|
-
__privateSet(this, _shapeHandle,
|
|
704
|
+
__privateSet(this, _shapeHandle, handle);
|
|
677
705
|
__privateSet(this, _isUpToDate, false);
|
|
678
706
|
__privateSet(this, _connected, false);
|
|
679
707
|
__privateSet(this, _schema, void 0);
|
|
680
708
|
};
|
|
681
|
-
|
|
709
|
+
ShapeStream.Replica = {
|
|
682
710
|
FULL: `full`,
|
|
683
711
|
DEFAULT: `default`
|
|
684
712
|
};
|
|
685
|
-
var ShapeStream = _ShapeStream;
|
|
686
713
|
function validateOptions(options) {
|
|
687
714
|
if (!options.url) {
|
|
688
|
-
throw new
|
|
715
|
+
throw new MissingShapeUrlError();
|
|
689
716
|
}
|
|
690
717
|
if (options.signal && !(options.signal instanceof AbortSignal)) {
|
|
691
|
-
throw new
|
|
692
|
-
`Invalid signal option. It must be an instance of AbortSignal.`
|
|
693
|
-
);
|
|
718
|
+
throw new InvalidSignalError();
|
|
694
719
|
}
|
|
695
|
-
if (options.offset !== void 0 && options.offset !== `-1` && !options.
|
|
696
|
-
throw new
|
|
697
|
-
`shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`
|
|
698
|
-
);
|
|
720
|
+
if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
|
|
721
|
+
throw new MissingShapeHandleError();
|
|
699
722
|
}
|
|
700
723
|
if (options.params) {
|
|
701
724
|
const reservedParams = Object.keys(options.params).filter(
|
|
702
725
|
(key) => RESERVED_PARAMS.has(key)
|
|
703
726
|
);
|
|
704
727
|
if (reservedParams.length > 0) {
|
|
705
|
-
throw new
|
|
706
|
-
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
707
|
-
);
|
|
728
|
+
throw new ReservedParamError(reservedParams);
|
|
708
729
|
}
|
|
709
730
|
}
|
|
710
731
|
return;
|
|
@@ -725,15 +746,6 @@ var Shape = class {
|
|
|
725
746
|
__privateMethod(this, _Shape_instances, process_fn).bind(this),
|
|
726
747
|
__privateMethod(this, _Shape_instances, handleError_fn).bind(this)
|
|
727
748
|
);
|
|
728
|
-
const unsubscribe = __privateGet(this, _stream).subscribeOnceToUpToDate(
|
|
729
|
-
() => {
|
|
730
|
-
unsubscribe();
|
|
731
|
-
},
|
|
732
|
-
(e) => {
|
|
733
|
-
__privateMethod(this, _Shape_instances, handleError_fn).call(this, e);
|
|
734
|
-
throw e;
|
|
735
|
-
}
|
|
736
|
-
);
|
|
737
749
|
}
|
|
738
750
|
get isUpToDate() {
|
|
739
751
|
return __privateGet(this, _stream).isUpToDate;
|
|
@@ -741,6 +753,9 @@ var Shape = class {
|
|
|
741
753
|
get lastOffset() {
|
|
742
754
|
return __privateGet(this, _stream).lastOffset;
|
|
743
755
|
}
|
|
756
|
+
get handle() {
|
|
757
|
+
return __privateGet(this, _stream).shapeHandle;
|
|
758
|
+
}
|
|
744
759
|
get rows() {
|
|
745
760
|
return this.value.then((v) => Array.from(v.values()));
|
|
746
761
|
}
|