@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/cjs/index.cjs
CHANGED
|
@@ -85,6 +85,91 @@ __export(src_exports, {
|
|
|
85
85
|
});
|
|
86
86
|
module.exports = __toCommonJS(src_exports);
|
|
87
87
|
|
|
88
|
+
// src/error.ts
|
|
89
|
+
var FetchError = class _FetchError extends Error {
|
|
90
|
+
constructor(status, text, json, headers, url, message) {
|
|
91
|
+
super(
|
|
92
|
+
message || `HTTP Error ${status} at ${url}: ${text != null ? text : JSON.stringify(json)}`
|
|
93
|
+
);
|
|
94
|
+
this.url = url;
|
|
95
|
+
this.name = `FetchError`;
|
|
96
|
+
this.status = status;
|
|
97
|
+
this.text = text;
|
|
98
|
+
this.json = json;
|
|
99
|
+
this.headers = headers;
|
|
100
|
+
}
|
|
101
|
+
static fromResponse(response, url) {
|
|
102
|
+
return __async(this, null, function* () {
|
|
103
|
+
const status = response.status;
|
|
104
|
+
const headers = Object.fromEntries([...response.headers.entries()]);
|
|
105
|
+
let text = void 0;
|
|
106
|
+
let json = void 0;
|
|
107
|
+
const contentType = response.headers.get(`content-type`);
|
|
108
|
+
if (contentType && contentType.includes(`application/json`)) {
|
|
109
|
+
json = yield response.json();
|
|
110
|
+
} else {
|
|
111
|
+
text = yield response.text();
|
|
112
|
+
}
|
|
113
|
+
return new _FetchError(status, text, json, headers, url);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var FetchBackoffAbortError = class extends Error {
|
|
118
|
+
constructor() {
|
|
119
|
+
super(`Fetch with backoff aborted`);
|
|
120
|
+
this.name = `FetchBackoffAbortError`;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var MissingShapeUrlError = class extends Error {
|
|
124
|
+
constructor() {
|
|
125
|
+
super(`Invalid shape options: missing required url parameter`);
|
|
126
|
+
this.name = `MissingShapeUrlError`;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var InvalidSignalError = class extends Error {
|
|
130
|
+
constructor() {
|
|
131
|
+
super(`Invalid signal option. It must be an instance of AbortSignal.`);
|
|
132
|
+
this.name = `InvalidSignalError`;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
var MissingShapeHandleError = class extends Error {
|
|
136
|
+
constructor() {
|
|
137
|
+
super(
|
|
138
|
+
`shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`
|
|
139
|
+
);
|
|
140
|
+
this.name = `MissingShapeHandleError`;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var ReservedParamError = class extends Error {
|
|
144
|
+
constructor(reservedParams) {
|
|
145
|
+
super(
|
|
146
|
+
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
147
|
+
);
|
|
148
|
+
this.name = `ReservedParamError`;
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var ParserNullValueError = class extends Error {
|
|
152
|
+
constructor(columnName) {
|
|
153
|
+
super(`Column "${columnName != null ? columnName : `unknown`}" does not allow NULL values`);
|
|
154
|
+
this.name = `ParserNullValueError`;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
var MissingHeadersError = class extends Error {
|
|
158
|
+
constructor(url, missingHeaders) {
|
|
159
|
+
let msg = `The response for the shape request to ${url} didn't include the following required headers:
|
|
160
|
+
`;
|
|
161
|
+
missingHeaders.forEach((h) => {
|
|
162
|
+
msg += `- ${h}
|
|
163
|
+
`;
|
|
164
|
+
});
|
|
165
|
+
msg += `
|
|
166
|
+
This is often due to a proxy not setting CORS correctly so that all Electric headers can be read by the client.`;
|
|
167
|
+
msg += `
|
|
168
|
+
For more information visit the troubleshooting guide: /docs/guides/troubleshooting/missing-headers`;
|
|
169
|
+
super(msg);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
88
173
|
// src/parser.ts
|
|
89
174
|
var parseNumber = (value) => Number(value);
|
|
90
175
|
var parseBool = (value) => value === `true` || value === `t`;
|
|
@@ -186,7 +271,7 @@ function makeNullableParser(parser, columnInfo, columnName) {
|
|
|
186
271
|
return (value) => {
|
|
187
272
|
if (isPgNull(value)) {
|
|
188
273
|
if (!isNullable) {
|
|
189
|
-
throw new
|
|
274
|
+
throw new ParserNullValueError(columnName != null ? columnName : `unknown`);
|
|
190
275
|
}
|
|
191
276
|
return null;
|
|
192
277
|
}
|
|
@@ -208,61 +293,12 @@ function isUpToDateMessage(message) {
|
|
|
208
293
|
return isControlMessage(message) && message.headers.control === `up-to-date`;
|
|
209
294
|
}
|
|
210
295
|
|
|
211
|
-
// src/error.ts
|
|
212
|
-
var FetchError = class _FetchError extends Error {
|
|
213
|
-
constructor(status, text, json, headers, url, message) {
|
|
214
|
-
super(
|
|
215
|
-
message || `HTTP Error ${status} at ${url}: ${text != null ? text : JSON.stringify(json)}`
|
|
216
|
-
);
|
|
217
|
-
this.url = url;
|
|
218
|
-
this.name = `FetchError`;
|
|
219
|
-
this.status = status;
|
|
220
|
-
this.text = text;
|
|
221
|
-
this.json = json;
|
|
222
|
-
this.headers = headers;
|
|
223
|
-
}
|
|
224
|
-
static fromResponse(response, url) {
|
|
225
|
-
return __async(this, null, function* () {
|
|
226
|
-
const status = response.status;
|
|
227
|
-
const headers = Object.fromEntries([...response.headers.entries()]);
|
|
228
|
-
let text = void 0;
|
|
229
|
-
let json = void 0;
|
|
230
|
-
const contentType = response.headers.get(`content-type`);
|
|
231
|
-
if (contentType && contentType.includes(`application/json`)) {
|
|
232
|
-
json = yield response.json();
|
|
233
|
-
} else {
|
|
234
|
-
text = yield response.text();
|
|
235
|
-
}
|
|
236
|
-
return new _FetchError(status, text, json, headers, url);
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
var FetchBackoffAbortError = class extends Error {
|
|
241
|
-
constructor() {
|
|
242
|
-
super(`Fetch with backoff aborted`);
|
|
243
|
-
}
|
|
244
|
-
};
|
|
245
|
-
var MissingHeadersError = class extends Error {
|
|
246
|
-
constructor(url, missingHeaders) {
|
|
247
|
-
let msg = `The response for the shape request to ${url} didn't include the following required headers:
|
|
248
|
-
`;
|
|
249
|
-
missingHeaders.forEach((h) => {
|
|
250
|
-
msg += `- ${h}
|
|
251
|
-
`;
|
|
252
|
-
});
|
|
253
|
-
msg += `
|
|
254
|
-
This is often due to a proxy not setting CORS correctly so that all Electric headers can be read by the client.`;
|
|
255
|
-
super(msg);
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
|
|
259
296
|
// src/constants.ts
|
|
260
297
|
var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
|
|
261
298
|
var SHAPE_HANDLE_HEADER = `electric-handle`;
|
|
262
299
|
var CHUNK_LAST_OFFSET_HEADER = `electric-offset`;
|
|
263
300
|
var SHAPE_SCHEMA_HEADER = `electric-schema`;
|
|
264
301
|
var CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`;
|
|
265
|
-
var DATABASE_ID_QUERY_PARAM = `database_id`;
|
|
266
302
|
var COLUMNS_QUERY_PARAM = `columns`;
|
|
267
303
|
var LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`;
|
|
268
304
|
var SHAPE_HANDLE_QUERY_PARAM = `handle`;
|
|
@@ -355,7 +391,7 @@ function createFetchWithResponseHeadersCheck(fetchClient) {
|
|
|
355
391
|
if (response.ok) {
|
|
356
392
|
const headers = response.headers;
|
|
357
393
|
const missingHeaders = [];
|
|
358
|
-
const addMissingHeaders = (requiredHeaders) => requiredHeaders.filter((h) => !headers.has(h));
|
|
394
|
+
const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
|
|
359
395
|
addMissingHeaders(requiredElectricResponseHeaders);
|
|
360
396
|
const input = args[0];
|
|
361
397
|
const urlString = input.toString();
|
|
@@ -448,6 +484,7 @@ function getNextChunkUrl(url, res) {
|
|
|
448
484
|
if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return;
|
|
449
485
|
nextUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, shapeHandle);
|
|
450
486
|
nextUrl.searchParams.set(OFFSET_QUERY_PARAM, lastOffset);
|
|
487
|
+
nextUrl.searchParams.sort();
|
|
451
488
|
return nextUrl.toString();
|
|
452
489
|
}
|
|
453
490
|
function chainAborter(aborter, sourceSignal) {
|
|
@@ -462,24 +499,26 @@ function chainAborter(aborter, sourceSignal) {
|
|
|
462
499
|
|
|
463
500
|
// src/client.ts
|
|
464
501
|
var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
465
|
-
DATABASE_ID_QUERY_PARAM,
|
|
466
|
-
COLUMNS_QUERY_PARAM,
|
|
467
502
|
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
468
503
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
469
504
|
LIVE_QUERY_PARAM,
|
|
470
|
-
OFFSET_QUERY_PARAM
|
|
471
|
-
TABLE_QUERY_PARAM,
|
|
472
|
-
WHERE_QUERY_PARAM,
|
|
473
|
-
REPLICA_PARAM
|
|
505
|
+
OFFSET_QUERY_PARAM
|
|
474
506
|
]);
|
|
475
|
-
|
|
476
|
-
|
|
507
|
+
function toInternalParams(params) {
|
|
508
|
+
const result = {};
|
|
509
|
+
for (const [key, value] of Object.entries(params)) {
|
|
510
|
+
result[key] = Array.isArray(value) ? value.join(`,`) : value;
|
|
511
|
+
}
|
|
512
|
+
return result;
|
|
513
|
+
}
|
|
514
|
+
var _error, _fetchClient2, _messageParser, _subscribers, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _schema, _onError, _ShapeStream_instances, start_fn, publish_fn, sendErrorToSubscribers_fn, reset_fn;
|
|
515
|
+
var ShapeStream = class {
|
|
477
516
|
constructor(options) {
|
|
478
517
|
__privateAdd(this, _ShapeStream_instances);
|
|
518
|
+
__privateAdd(this, _error, null);
|
|
479
519
|
__privateAdd(this, _fetchClient2);
|
|
480
520
|
__privateAdd(this, _messageParser);
|
|
481
521
|
__privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
|
|
482
|
-
__privateAdd(this, _upToDateSubscribers, /* @__PURE__ */ new Map());
|
|
483
522
|
__privateAdd(this, _lastOffset);
|
|
484
523
|
__privateAdd(this, _liveCacheBuster);
|
|
485
524
|
// Seconds since our Electric Epoch 😎
|
|
@@ -488,19 +527,16 @@ var _ShapeStream = class _ShapeStream {
|
|
|
488
527
|
__privateAdd(this, _isUpToDate, false);
|
|
489
528
|
__privateAdd(this, _connected, false);
|
|
490
529
|
__privateAdd(this, _shapeHandle);
|
|
491
|
-
__privateAdd(this, _databaseId);
|
|
492
530
|
__privateAdd(this, _schema);
|
|
493
|
-
__privateAdd(this,
|
|
494
|
-
__privateAdd(this, _replica);
|
|
531
|
+
__privateAdd(this, _onError);
|
|
495
532
|
var _a, _b, _c;
|
|
496
|
-
validateOptions(options);
|
|
497
533
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
534
|
+
validateOptions(this.options);
|
|
498
535
|
__privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
|
|
499
536
|
__privateSet(this, _liveCacheBuster, ``);
|
|
500
|
-
__privateSet(this, _shapeHandle, this.options.
|
|
501
|
-
__privateSet(this, _databaseId, this.options.databaseId);
|
|
537
|
+
__privateSet(this, _shapeHandle, this.options.handle);
|
|
502
538
|
__privateSet(this, _messageParser, new MessageParser(options.parser));
|
|
503
|
-
__privateSet(this,
|
|
539
|
+
__privateSet(this, _onError, this.options.onError);
|
|
504
540
|
const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
|
|
505
541
|
const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
|
|
506
542
|
onFailedAttempt: () => {
|
|
@@ -512,131 +548,22 @@ var _ShapeStream = class _ShapeStream {
|
|
|
512
548
|
__privateSet(this, _fetchClient2, createFetchWithResponseHeadersCheck(
|
|
513
549
|
createFetchWithChunkBuffer(fetchWithBackoffClient)
|
|
514
550
|
));
|
|
515
|
-
this.
|
|
551
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
516
552
|
}
|
|
517
553
|
get shapeHandle() {
|
|
518
554
|
return __privateGet(this, _shapeHandle);
|
|
519
555
|
}
|
|
556
|
+
get error() {
|
|
557
|
+
return __privateGet(this, _error);
|
|
558
|
+
}
|
|
520
559
|
get isUpToDate() {
|
|
521
560
|
return __privateGet(this, _isUpToDate);
|
|
522
561
|
}
|
|
523
562
|
get lastOffset() {
|
|
524
563
|
return __privateGet(this, _lastOffset);
|
|
525
564
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
start() {
|
|
530
|
-
return __async(this, null, function* () {
|
|
531
|
-
var _a, _b;
|
|
532
|
-
__privateSet(this, _isUpToDate, false);
|
|
533
|
-
const { url, table, where, columns, signal } = this.options;
|
|
534
|
-
try {
|
|
535
|
-
while (!(signal == null ? void 0 : signal.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
536
|
-
const fetchUrl = new URL(url);
|
|
537
|
-
if (this.options.params) {
|
|
538
|
-
const reservedParams = Object.keys(this.options.params).filter(
|
|
539
|
-
(key) => RESERVED_PARAMS.has(key)
|
|
540
|
-
);
|
|
541
|
-
if (reservedParams.length > 0) {
|
|
542
|
-
throw new Error(
|
|
543
|
-
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
for (const [key, value] of Object.entries(this.options.params)) {
|
|
547
|
-
fetchUrl.searchParams.set(key, value);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
|
|
551
|
-
if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
|
|
552
|
-
if (columns && columns.length > 0)
|
|
553
|
-
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
|
|
554
|
-
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
555
|
-
if (__privateGet(this, _isUpToDate)) {
|
|
556
|
-
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
557
|
-
fetchUrl.searchParams.set(
|
|
558
|
-
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
559
|
-
__privateGet(this, _liveCacheBuster)
|
|
560
|
-
);
|
|
561
|
-
}
|
|
562
|
-
if (__privateGet(this, _shapeHandle)) {
|
|
563
|
-
fetchUrl.searchParams.set(
|
|
564
|
-
SHAPE_HANDLE_QUERY_PARAM,
|
|
565
|
-
__privateGet(this, _shapeHandle)
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
if (__privateGet(this, _databaseId)) {
|
|
569
|
-
fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
|
|
570
|
-
}
|
|
571
|
-
if (((_a = __privateGet(this, _replica)) != null ? _a : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
|
|
572
|
-
fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
|
|
573
|
-
}
|
|
574
|
-
let response;
|
|
575
|
-
try {
|
|
576
|
-
response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
577
|
-
signal,
|
|
578
|
-
headers: this.options.headers
|
|
579
|
-
});
|
|
580
|
-
__privateSet(this, _connected, true);
|
|
581
|
-
} catch (e) {
|
|
582
|
-
if (e instanceof FetchBackoffAbortError) break;
|
|
583
|
-
if (e instanceof MissingHeadersError) throw e;
|
|
584
|
-
if (!(e instanceof FetchError)) throw e;
|
|
585
|
-
if (e.status == 409) {
|
|
586
|
-
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
587
|
-
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
588
|
-
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
589
|
-
continue;
|
|
590
|
-
} else if (e.status >= 400 && e.status < 500) {
|
|
591
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToUpToDateSubscribers_fn).call(this, e);
|
|
592
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
593
|
-
throw e;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
const { headers, status } = response;
|
|
597
|
-
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
598
|
-
if (shapeHandle) {
|
|
599
|
-
__privateSet(this, _shapeHandle, shapeHandle);
|
|
600
|
-
}
|
|
601
|
-
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
602
|
-
if (lastOffset) {
|
|
603
|
-
__privateSet(this, _lastOffset, lastOffset);
|
|
604
|
-
}
|
|
605
|
-
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
606
|
-
if (liveCacheBuster) {
|
|
607
|
-
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
608
|
-
}
|
|
609
|
-
const getSchema = () => {
|
|
610
|
-
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
611
|
-
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
612
|
-
};
|
|
613
|
-
__privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
|
|
614
|
-
const messages = status === 204 ? `[]` : yield response.text();
|
|
615
|
-
if (status === 204) {
|
|
616
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
617
|
-
}
|
|
618
|
-
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
619
|
-
if (batch.length > 0) {
|
|
620
|
-
const prevUpToDate = __privateGet(this, _isUpToDate);
|
|
621
|
-
const lastMessage = batch[batch.length - 1];
|
|
622
|
-
if (isUpToDateMessage(lastMessage)) {
|
|
623
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
624
|
-
__privateSet(this, _isUpToDate, true);
|
|
625
|
-
}
|
|
626
|
-
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
627
|
-
if (!prevUpToDate && __privateGet(this, _isUpToDate)) {
|
|
628
|
-
__privateMethod(this, _ShapeStream_instances, notifyUpToDateSubscribers_fn).call(this);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
} catch (err) {
|
|
633
|
-
__privateSet(this, _error, err);
|
|
634
|
-
} finally {
|
|
635
|
-
__privateSet(this, _connected, false);
|
|
636
|
-
}
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
subscribe(callback, onError) {
|
|
565
|
+
subscribe(callback, onError = () => {
|
|
566
|
+
}) {
|
|
640
567
|
const subscriptionId = Math.random();
|
|
641
568
|
__privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
|
|
642
569
|
return () => {
|
|
@@ -646,16 +573,6 @@ var _ShapeStream = class _ShapeStream {
|
|
|
646
573
|
unsubscribeAll() {
|
|
647
574
|
__privateGet(this, _subscribers).clear();
|
|
648
575
|
}
|
|
649
|
-
subscribeOnceToUpToDate(callback, error) {
|
|
650
|
-
const subscriptionId = Math.random();
|
|
651
|
-
__privateGet(this, _upToDateSubscribers).set(subscriptionId, [callback, error]);
|
|
652
|
-
return () => {
|
|
653
|
-
__privateGet(this, _upToDateSubscribers).delete(subscriptionId);
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
unsubscribeAllUpToDateSubscribers() {
|
|
657
|
-
__privateGet(this, _upToDateSubscribers).clear();
|
|
658
|
-
}
|
|
659
576
|
/** Unix time at which we last synced. Undefined when `isLoading` is true. */
|
|
660
577
|
lastSyncedAt() {
|
|
661
578
|
return __privateGet(this, _lastSyncedAt);
|
|
@@ -671,24 +588,145 @@ var _ShapeStream = class _ShapeStream {
|
|
|
671
588
|
}
|
|
672
589
|
/** True during initial fetch. False afterwise. */
|
|
673
590
|
isLoading() {
|
|
674
|
-
return !this
|
|
591
|
+
return !__privateGet(this, _isUpToDate);
|
|
675
592
|
}
|
|
676
593
|
};
|
|
594
|
+
_error = new WeakMap();
|
|
677
595
|
_fetchClient2 = new WeakMap();
|
|
678
596
|
_messageParser = new WeakMap();
|
|
679
597
|
_subscribers = new WeakMap();
|
|
680
|
-
_upToDateSubscribers = new WeakMap();
|
|
681
598
|
_lastOffset = new WeakMap();
|
|
682
599
|
_liveCacheBuster = new WeakMap();
|
|
683
600
|
_lastSyncedAt = new WeakMap();
|
|
684
601
|
_isUpToDate = new WeakMap();
|
|
685
602
|
_connected = new WeakMap();
|
|
686
603
|
_shapeHandle = new WeakMap();
|
|
687
|
-
_databaseId = new WeakMap();
|
|
688
604
|
_schema = new WeakMap();
|
|
689
|
-
|
|
690
|
-
_replica = new WeakMap();
|
|
605
|
+
_onError = new WeakMap();
|
|
691
606
|
_ShapeStream_instances = new WeakSet();
|
|
607
|
+
start_fn = function() {
|
|
608
|
+
return __async(this, null, function* () {
|
|
609
|
+
var _a, _b;
|
|
610
|
+
try {
|
|
611
|
+
while (!((_a = this.options.signal) == null ? void 0 : _a.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
612
|
+
const { url, signal } = this.options;
|
|
613
|
+
const fetchUrl = new URL(url);
|
|
614
|
+
if (this.options.params) {
|
|
615
|
+
const reservedParams = Object.keys(this.options.params).filter(
|
|
616
|
+
(key) => RESERVED_PARAMS.has(key)
|
|
617
|
+
);
|
|
618
|
+
if (reservedParams.length > 0) {
|
|
619
|
+
throw new Error(
|
|
620
|
+
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
const params = toInternalParams(this.options.params);
|
|
624
|
+
if (params.table)
|
|
625
|
+
fetchUrl.searchParams.set(TABLE_QUERY_PARAM, params.table);
|
|
626
|
+
if (params.where)
|
|
627
|
+
fetchUrl.searchParams.set(WHERE_QUERY_PARAM, params.where);
|
|
628
|
+
if (params.columns)
|
|
629
|
+
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, params.columns);
|
|
630
|
+
if (params.replica)
|
|
631
|
+
fetchUrl.searchParams.set(REPLICA_PARAM, params.replica);
|
|
632
|
+
const customParams = __spreadValues({}, params);
|
|
633
|
+
delete customParams.table;
|
|
634
|
+
delete customParams.where;
|
|
635
|
+
delete customParams.columns;
|
|
636
|
+
delete customParams.replica;
|
|
637
|
+
for (const [key, value] of Object.entries(customParams)) {
|
|
638
|
+
fetchUrl.searchParams.set(key, value);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
642
|
+
if (__privateGet(this, _isUpToDate)) {
|
|
643
|
+
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
644
|
+
fetchUrl.searchParams.set(
|
|
645
|
+
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
646
|
+
__privateGet(this, _liveCacheBuster)
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
if (__privateGet(this, _shapeHandle)) {
|
|
650
|
+
fetchUrl.searchParams.set(
|
|
651
|
+
SHAPE_HANDLE_QUERY_PARAM,
|
|
652
|
+
__privateGet(this, _shapeHandle)
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
fetchUrl.searchParams.sort();
|
|
656
|
+
let response;
|
|
657
|
+
try {
|
|
658
|
+
response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
659
|
+
signal,
|
|
660
|
+
headers: this.options.headers
|
|
661
|
+
});
|
|
662
|
+
__privateSet(this, _connected, true);
|
|
663
|
+
} catch (e) {
|
|
664
|
+
if (e instanceof FetchBackoffAbortError) break;
|
|
665
|
+
if (!(e instanceof FetchError)) throw e;
|
|
666
|
+
if (e.status == 409) {
|
|
667
|
+
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
668
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
669
|
+
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
670
|
+
continue;
|
|
671
|
+
} else if (e.status >= 400 && e.status < 500) {
|
|
672
|
+
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
673
|
+
throw e;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
const { headers, status } = response;
|
|
677
|
+
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
678
|
+
if (shapeHandle) {
|
|
679
|
+
__privateSet(this, _shapeHandle, shapeHandle);
|
|
680
|
+
}
|
|
681
|
+
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
682
|
+
if (lastOffset) {
|
|
683
|
+
__privateSet(this, _lastOffset, lastOffset);
|
|
684
|
+
}
|
|
685
|
+
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
686
|
+
if (liveCacheBuster) {
|
|
687
|
+
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
688
|
+
}
|
|
689
|
+
const getSchema = () => {
|
|
690
|
+
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
691
|
+
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
692
|
+
};
|
|
693
|
+
__privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
|
|
694
|
+
const messages = status === 204 ? `[]` : yield response.text();
|
|
695
|
+
if (status === 204) {
|
|
696
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
697
|
+
}
|
|
698
|
+
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
699
|
+
if (batch.length > 0) {
|
|
700
|
+
const lastMessage = batch[batch.length - 1];
|
|
701
|
+
if (isUpToDateMessage(lastMessage)) {
|
|
702
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
703
|
+
__privateSet(this, _isUpToDate, true);
|
|
704
|
+
}
|
|
705
|
+
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
} catch (err) {
|
|
709
|
+
__privateSet(this, _error, err);
|
|
710
|
+
if (__privateGet(this, _onError)) {
|
|
711
|
+
const retryOpts = yield __privateGet(this, _onError).call(this, err);
|
|
712
|
+
if (typeof retryOpts === `object`) {
|
|
713
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
|
|
714
|
+
if (`params` in retryOpts) {
|
|
715
|
+
this.options.params = retryOpts.params;
|
|
716
|
+
}
|
|
717
|
+
if (`headers` in retryOpts) {
|
|
718
|
+
this.options.headers = retryOpts.headers;
|
|
719
|
+
}
|
|
720
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
721
|
+
}
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
throw err;
|
|
725
|
+
} finally {
|
|
726
|
+
__privateSet(this, _connected, false);
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
};
|
|
692
730
|
publish_fn = function(messages) {
|
|
693
731
|
return __async(this, null, function* () {
|
|
694
732
|
yield Promise.all(
|
|
@@ -709,55 +747,38 @@ sendErrorToSubscribers_fn = function(error) {
|
|
|
709
747
|
errorFn == null ? void 0 : errorFn(error);
|
|
710
748
|
});
|
|
711
749
|
};
|
|
712
|
-
notifyUpToDateSubscribers_fn = function() {
|
|
713
|
-
__privateGet(this, _upToDateSubscribers).forEach(([callback]) => {
|
|
714
|
-
callback();
|
|
715
|
-
});
|
|
716
|
-
};
|
|
717
|
-
sendErrorToUpToDateSubscribers_fn = function(error) {
|
|
718
|
-
__privateGet(this, _upToDateSubscribers).forEach(
|
|
719
|
-
([_, errorCallback]) => errorCallback(error)
|
|
720
|
-
);
|
|
721
|
-
};
|
|
722
750
|
/**
|
|
723
751
|
* Resets the state of the stream, optionally with a provided
|
|
724
752
|
* shape handle
|
|
725
753
|
*/
|
|
726
|
-
reset_fn = function(
|
|
754
|
+
reset_fn = function(handle) {
|
|
727
755
|
__privateSet(this, _lastOffset, `-1`);
|
|
728
756
|
__privateSet(this, _liveCacheBuster, ``);
|
|
729
|
-
__privateSet(this, _shapeHandle,
|
|
757
|
+
__privateSet(this, _shapeHandle, handle);
|
|
730
758
|
__privateSet(this, _isUpToDate, false);
|
|
731
759
|
__privateSet(this, _connected, false);
|
|
732
760
|
__privateSet(this, _schema, void 0);
|
|
733
761
|
};
|
|
734
|
-
|
|
762
|
+
ShapeStream.Replica = {
|
|
735
763
|
FULL: `full`,
|
|
736
764
|
DEFAULT: `default`
|
|
737
765
|
};
|
|
738
|
-
var ShapeStream = _ShapeStream;
|
|
739
766
|
function validateOptions(options) {
|
|
740
767
|
if (!options.url) {
|
|
741
|
-
throw new
|
|
768
|
+
throw new MissingShapeUrlError();
|
|
742
769
|
}
|
|
743
770
|
if (options.signal && !(options.signal instanceof AbortSignal)) {
|
|
744
|
-
throw new
|
|
745
|
-
`Invalid signal option. It must be an instance of AbortSignal.`
|
|
746
|
-
);
|
|
771
|
+
throw new InvalidSignalError();
|
|
747
772
|
}
|
|
748
|
-
if (options.offset !== void 0 && options.offset !== `-1` && !options.
|
|
749
|
-
throw new
|
|
750
|
-
`shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`
|
|
751
|
-
);
|
|
773
|
+
if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
|
|
774
|
+
throw new MissingShapeHandleError();
|
|
752
775
|
}
|
|
753
776
|
if (options.params) {
|
|
754
777
|
const reservedParams = Object.keys(options.params).filter(
|
|
755
778
|
(key) => RESERVED_PARAMS.has(key)
|
|
756
779
|
);
|
|
757
780
|
if (reservedParams.length > 0) {
|
|
758
|
-
throw new
|
|
759
|
-
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
760
|
-
);
|
|
781
|
+
throw new ReservedParamError(reservedParams);
|
|
761
782
|
}
|
|
762
783
|
}
|
|
763
784
|
return;
|
|
@@ -778,15 +799,6 @@ var Shape = class {
|
|
|
778
799
|
__privateMethod(this, _Shape_instances, process_fn).bind(this),
|
|
779
800
|
__privateMethod(this, _Shape_instances, handleError_fn).bind(this)
|
|
780
801
|
);
|
|
781
|
-
const unsubscribe = __privateGet(this, _stream).subscribeOnceToUpToDate(
|
|
782
|
-
() => {
|
|
783
|
-
unsubscribe();
|
|
784
|
-
},
|
|
785
|
-
(e) => {
|
|
786
|
-
__privateMethod(this, _Shape_instances, handleError_fn).call(this, e);
|
|
787
|
-
throw e;
|
|
788
|
-
}
|
|
789
|
-
);
|
|
790
802
|
}
|
|
791
803
|
get isUpToDate() {
|
|
792
804
|
return __privateGet(this, _stream).isUpToDate;
|
|
@@ -794,6 +806,9 @@ var Shape = class {
|
|
|
794
806
|
get lastOffset() {
|
|
795
807
|
return __privateGet(this, _stream).lastOffset;
|
|
796
808
|
}
|
|
809
|
+
get handle() {
|
|
810
|
+
return __privateGet(this, _stream).shapeHandle;
|
|
811
|
+
}
|
|
797
812
|
get rows() {
|
|
798
813
|
return this.value.then((v) => Array.from(v.values()));
|
|
799
814
|
}
|