@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.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,61 +266,12 @@ 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
|
-
var MissingHeadersError = class extends Error {
|
|
219
|
-
constructor(url, missingHeaders) {
|
|
220
|
-
let msg = `The response for the shape request to ${url} didn't include the following required headers:
|
|
221
|
-
`;
|
|
222
|
-
missingHeaders.forEach((h) => {
|
|
223
|
-
msg += `- ${h}
|
|
224
|
-
`;
|
|
225
|
-
});
|
|
226
|
-
msg += `
|
|
227
|
-
This is often due to a proxy not setting CORS correctly so that all Electric headers can be read by the client.`;
|
|
228
|
-
super(msg);
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
|
|
232
269
|
// src/constants.ts
|
|
233
270
|
var LIVE_CACHE_BUSTER_HEADER = `electric-cursor`;
|
|
234
271
|
var SHAPE_HANDLE_HEADER = `electric-handle`;
|
|
235
272
|
var CHUNK_LAST_OFFSET_HEADER = `electric-offset`;
|
|
236
273
|
var SHAPE_SCHEMA_HEADER = `electric-schema`;
|
|
237
274
|
var CHUNK_UP_TO_DATE_HEADER = `electric-up-to-date`;
|
|
238
|
-
var DATABASE_ID_QUERY_PARAM = `database_id`;
|
|
239
275
|
var COLUMNS_QUERY_PARAM = `columns`;
|
|
240
276
|
var LIVE_CACHE_BUSTER_QUERY_PARAM = `cursor`;
|
|
241
277
|
var SHAPE_HANDLE_QUERY_PARAM = `handle`;
|
|
@@ -328,7 +364,7 @@ function createFetchWithResponseHeadersCheck(fetchClient) {
|
|
|
328
364
|
if (response.ok) {
|
|
329
365
|
const headers = response.headers;
|
|
330
366
|
const missingHeaders = [];
|
|
331
|
-
const addMissingHeaders = (requiredHeaders) => requiredHeaders.filter((h) => !headers.has(h));
|
|
367
|
+
const addMissingHeaders = (requiredHeaders) => missingHeaders.push(...requiredHeaders.filter((h) => !headers.has(h)));
|
|
332
368
|
addMissingHeaders(requiredElectricResponseHeaders);
|
|
333
369
|
const input = args[0];
|
|
334
370
|
const urlString = input.toString();
|
|
@@ -421,6 +457,7 @@ function getNextChunkUrl(url, res) {
|
|
|
421
457
|
if (nextUrl.searchParams.has(LIVE_QUERY_PARAM)) return;
|
|
422
458
|
nextUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, shapeHandle);
|
|
423
459
|
nextUrl.searchParams.set(OFFSET_QUERY_PARAM, lastOffset);
|
|
460
|
+
nextUrl.searchParams.sort();
|
|
424
461
|
return nextUrl.toString();
|
|
425
462
|
}
|
|
426
463
|
function chainAborter(aborter, sourceSignal) {
|
|
@@ -435,24 +472,26 @@ function chainAborter(aborter, sourceSignal) {
|
|
|
435
472
|
|
|
436
473
|
// src/client.ts
|
|
437
474
|
var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
438
|
-
DATABASE_ID_QUERY_PARAM,
|
|
439
|
-
COLUMNS_QUERY_PARAM,
|
|
440
475
|
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
441
476
|
SHAPE_HANDLE_QUERY_PARAM,
|
|
442
477
|
LIVE_QUERY_PARAM,
|
|
443
|
-
OFFSET_QUERY_PARAM
|
|
444
|
-
TABLE_QUERY_PARAM,
|
|
445
|
-
WHERE_QUERY_PARAM,
|
|
446
|
-
REPLICA_PARAM
|
|
478
|
+
OFFSET_QUERY_PARAM
|
|
447
479
|
]);
|
|
448
|
-
|
|
449
|
-
|
|
480
|
+
function toInternalParams(params) {
|
|
481
|
+
const result = {};
|
|
482
|
+
for (const [key, value] of Object.entries(params)) {
|
|
483
|
+
result[key] = Array.isArray(value) ? value.join(`,`) : value;
|
|
484
|
+
}
|
|
485
|
+
return result;
|
|
486
|
+
}
|
|
487
|
+
var _error, _fetchClient2, _messageParser, _subscribers, _lastOffset, _liveCacheBuster, _lastSyncedAt, _isUpToDate, _connected, _shapeHandle, _schema, _onError, _ShapeStream_instances, start_fn, publish_fn, sendErrorToSubscribers_fn, reset_fn;
|
|
488
|
+
var ShapeStream = class {
|
|
450
489
|
constructor(options) {
|
|
451
490
|
__privateAdd(this, _ShapeStream_instances);
|
|
491
|
+
__privateAdd(this, _error, null);
|
|
452
492
|
__privateAdd(this, _fetchClient2);
|
|
453
493
|
__privateAdd(this, _messageParser);
|
|
454
494
|
__privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
|
|
455
|
-
__privateAdd(this, _upToDateSubscribers, /* @__PURE__ */ new Map());
|
|
456
495
|
__privateAdd(this, _lastOffset);
|
|
457
496
|
__privateAdd(this, _liveCacheBuster);
|
|
458
497
|
// Seconds since our Electric Epoch 😎
|
|
@@ -461,19 +500,16 @@ var _ShapeStream = class _ShapeStream {
|
|
|
461
500
|
__privateAdd(this, _isUpToDate, false);
|
|
462
501
|
__privateAdd(this, _connected, false);
|
|
463
502
|
__privateAdd(this, _shapeHandle);
|
|
464
|
-
__privateAdd(this, _databaseId);
|
|
465
503
|
__privateAdd(this, _schema);
|
|
466
|
-
__privateAdd(this,
|
|
467
|
-
__privateAdd(this, _replica);
|
|
504
|
+
__privateAdd(this, _onError);
|
|
468
505
|
var _a, _b, _c;
|
|
469
|
-
validateOptions(options);
|
|
470
506
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
507
|
+
validateOptions(this.options);
|
|
471
508
|
__privateSet(this, _lastOffset, (_a = this.options.offset) != null ? _a : `-1`);
|
|
472
509
|
__privateSet(this, _liveCacheBuster, ``);
|
|
473
|
-
__privateSet(this, _shapeHandle, this.options.
|
|
474
|
-
__privateSet(this, _databaseId, this.options.databaseId);
|
|
510
|
+
__privateSet(this, _shapeHandle, this.options.handle);
|
|
475
511
|
__privateSet(this, _messageParser, new MessageParser(options.parser));
|
|
476
|
-
__privateSet(this,
|
|
512
|
+
__privateSet(this, _onError, this.options.onError);
|
|
477
513
|
const baseFetchClient = (_b = options.fetchClient) != null ? _b : (...args) => fetch(...args);
|
|
478
514
|
const fetchWithBackoffClient = createFetchWithBackoff(baseFetchClient, __spreadProps(__spreadValues({}, (_c = options.backoffOptions) != null ? _c : BackoffDefaults), {
|
|
479
515
|
onFailedAttempt: () => {
|
|
@@ -485,131 +521,22 @@ var _ShapeStream = class _ShapeStream {
|
|
|
485
521
|
__privateSet(this, _fetchClient2, createFetchWithResponseHeadersCheck(
|
|
486
522
|
createFetchWithChunkBuffer(fetchWithBackoffClient)
|
|
487
523
|
));
|
|
488
|
-
this.
|
|
524
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
489
525
|
}
|
|
490
526
|
get shapeHandle() {
|
|
491
527
|
return __privateGet(this, _shapeHandle);
|
|
492
528
|
}
|
|
529
|
+
get error() {
|
|
530
|
+
return __privateGet(this, _error);
|
|
531
|
+
}
|
|
493
532
|
get isUpToDate() {
|
|
494
533
|
return __privateGet(this, _isUpToDate);
|
|
495
534
|
}
|
|
496
535
|
get lastOffset() {
|
|
497
536
|
return __privateGet(this, _lastOffset);
|
|
498
537
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
start() {
|
|
503
|
-
return __async(this, null, function* () {
|
|
504
|
-
var _a, _b;
|
|
505
|
-
__privateSet(this, _isUpToDate, false);
|
|
506
|
-
const { url, table, where, columns, signal } = this.options;
|
|
507
|
-
try {
|
|
508
|
-
while (!(signal == null ? void 0 : signal.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
509
|
-
const fetchUrl = new URL(url);
|
|
510
|
-
if (this.options.params) {
|
|
511
|
-
const reservedParams = Object.keys(this.options.params).filter(
|
|
512
|
-
(key) => RESERVED_PARAMS.has(key)
|
|
513
|
-
);
|
|
514
|
-
if (reservedParams.length > 0) {
|
|
515
|
-
throw new Error(
|
|
516
|
-
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
517
|
-
);
|
|
518
|
-
}
|
|
519
|
-
for (const [key, value] of Object.entries(this.options.params)) {
|
|
520
|
-
fetchUrl.searchParams.set(key, value);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
if (table) fetchUrl.searchParams.set(TABLE_QUERY_PARAM, table);
|
|
524
|
-
if (where) fetchUrl.searchParams.set(WHERE_QUERY_PARAM, where);
|
|
525
|
-
if (columns && columns.length > 0)
|
|
526
|
-
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, columns.join(`,`));
|
|
527
|
-
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
528
|
-
if (__privateGet(this, _isUpToDate)) {
|
|
529
|
-
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
530
|
-
fetchUrl.searchParams.set(
|
|
531
|
-
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
532
|
-
__privateGet(this, _liveCacheBuster)
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
if (__privateGet(this, _shapeHandle)) {
|
|
536
|
-
fetchUrl.searchParams.set(
|
|
537
|
-
SHAPE_HANDLE_QUERY_PARAM,
|
|
538
|
-
__privateGet(this, _shapeHandle)
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
if (__privateGet(this, _databaseId)) {
|
|
542
|
-
fetchUrl.searchParams.set(DATABASE_ID_QUERY_PARAM, __privateGet(this, _databaseId));
|
|
543
|
-
}
|
|
544
|
-
if (((_a = __privateGet(this, _replica)) != null ? _a : _ShapeStream.Replica.DEFAULT) != _ShapeStream.Replica.DEFAULT) {
|
|
545
|
-
fetchUrl.searchParams.set(REPLICA_PARAM, __privateGet(this, _replica));
|
|
546
|
-
}
|
|
547
|
-
let response;
|
|
548
|
-
try {
|
|
549
|
-
response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
550
|
-
signal,
|
|
551
|
-
headers: this.options.headers
|
|
552
|
-
});
|
|
553
|
-
__privateSet(this, _connected, true);
|
|
554
|
-
} catch (e) {
|
|
555
|
-
if (e instanceof FetchBackoffAbortError) break;
|
|
556
|
-
if (e instanceof MissingHeadersError) throw e;
|
|
557
|
-
if (!(e instanceof FetchError)) throw e;
|
|
558
|
-
if (e.status == 409) {
|
|
559
|
-
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
560
|
-
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
561
|
-
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
562
|
-
continue;
|
|
563
|
-
} else if (e.status >= 400 && e.status < 500) {
|
|
564
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToUpToDateSubscribers_fn).call(this, e);
|
|
565
|
-
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
566
|
-
throw e;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
const { headers, status } = response;
|
|
570
|
-
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
571
|
-
if (shapeHandle) {
|
|
572
|
-
__privateSet(this, _shapeHandle, shapeHandle);
|
|
573
|
-
}
|
|
574
|
-
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
575
|
-
if (lastOffset) {
|
|
576
|
-
__privateSet(this, _lastOffset, lastOffset);
|
|
577
|
-
}
|
|
578
|
-
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
579
|
-
if (liveCacheBuster) {
|
|
580
|
-
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
581
|
-
}
|
|
582
|
-
const getSchema = () => {
|
|
583
|
-
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
584
|
-
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
585
|
-
};
|
|
586
|
-
__privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
|
|
587
|
-
const messages = status === 204 ? `[]` : yield response.text();
|
|
588
|
-
if (status === 204) {
|
|
589
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
590
|
-
}
|
|
591
|
-
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
592
|
-
if (batch.length > 0) {
|
|
593
|
-
const prevUpToDate = __privateGet(this, _isUpToDate);
|
|
594
|
-
const lastMessage = batch[batch.length - 1];
|
|
595
|
-
if (isUpToDateMessage(lastMessage)) {
|
|
596
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
597
|
-
__privateSet(this, _isUpToDate, true);
|
|
598
|
-
}
|
|
599
|
-
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
600
|
-
if (!prevUpToDate && __privateGet(this, _isUpToDate)) {
|
|
601
|
-
__privateMethod(this, _ShapeStream_instances, notifyUpToDateSubscribers_fn).call(this);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
} catch (err) {
|
|
606
|
-
__privateSet(this, _error, err);
|
|
607
|
-
} finally {
|
|
608
|
-
__privateSet(this, _connected, false);
|
|
609
|
-
}
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
|
-
subscribe(callback, onError) {
|
|
538
|
+
subscribe(callback, onError = () => {
|
|
539
|
+
}) {
|
|
613
540
|
const subscriptionId = Math.random();
|
|
614
541
|
__privateGet(this, _subscribers).set(subscriptionId, [callback, onError]);
|
|
615
542
|
return () => {
|
|
@@ -619,16 +546,6 @@ var _ShapeStream = class _ShapeStream {
|
|
|
619
546
|
unsubscribeAll() {
|
|
620
547
|
__privateGet(this, _subscribers).clear();
|
|
621
548
|
}
|
|
622
|
-
subscribeOnceToUpToDate(callback, error) {
|
|
623
|
-
const subscriptionId = Math.random();
|
|
624
|
-
__privateGet(this, _upToDateSubscribers).set(subscriptionId, [callback, error]);
|
|
625
|
-
return () => {
|
|
626
|
-
__privateGet(this, _upToDateSubscribers).delete(subscriptionId);
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
unsubscribeAllUpToDateSubscribers() {
|
|
630
|
-
__privateGet(this, _upToDateSubscribers).clear();
|
|
631
|
-
}
|
|
632
549
|
/** Unix time at which we last synced. Undefined when `isLoading` is true. */
|
|
633
550
|
lastSyncedAt() {
|
|
634
551
|
return __privateGet(this, _lastSyncedAt);
|
|
@@ -644,24 +561,145 @@ var _ShapeStream = class _ShapeStream {
|
|
|
644
561
|
}
|
|
645
562
|
/** True during initial fetch. False afterwise. */
|
|
646
563
|
isLoading() {
|
|
647
|
-
return !this
|
|
564
|
+
return !__privateGet(this, _isUpToDate);
|
|
648
565
|
}
|
|
649
566
|
};
|
|
567
|
+
_error = new WeakMap();
|
|
650
568
|
_fetchClient2 = new WeakMap();
|
|
651
569
|
_messageParser = new WeakMap();
|
|
652
570
|
_subscribers = new WeakMap();
|
|
653
|
-
_upToDateSubscribers = new WeakMap();
|
|
654
571
|
_lastOffset = new WeakMap();
|
|
655
572
|
_liveCacheBuster = new WeakMap();
|
|
656
573
|
_lastSyncedAt = new WeakMap();
|
|
657
574
|
_isUpToDate = new WeakMap();
|
|
658
575
|
_connected = new WeakMap();
|
|
659
576
|
_shapeHandle = new WeakMap();
|
|
660
|
-
_databaseId = new WeakMap();
|
|
661
577
|
_schema = new WeakMap();
|
|
662
|
-
|
|
663
|
-
_replica = new WeakMap();
|
|
578
|
+
_onError = new WeakMap();
|
|
664
579
|
_ShapeStream_instances = new WeakSet();
|
|
580
|
+
start_fn = function() {
|
|
581
|
+
return __async(this, null, function* () {
|
|
582
|
+
var _a, _b;
|
|
583
|
+
try {
|
|
584
|
+
while (!((_a = this.options.signal) == null ? void 0 : _a.aborted) && !__privateGet(this, _isUpToDate) || this.options.subscribe) {
|
|
585
|
+
const { url, signal } = this.options;
|
|
586
|
+
const fetchUrl = new URL(url);
|
|
587
|
+
if (this.options.params) {
|
|
588
|
+
const reservedParams = Object.keys(this.options.params).filter(
|
|
589
|
+
(key) => RESERVED_PARAMS.has(key)
|
|
590
|
+
);
|
|
591
|
+
if (reservedParams.length > 0) {
|
|
592
|
+
throw new Error(
|
|
593
|
+
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
594
|
+
);
|
|
595
|
+
}
|
|
596
|
+
const params = toInternalParams(this.options.params);
|
|
597
|
+
if (params.table)
|
|
598
|
+
fetchUrl.searchParams.set(TABLE_QUERY_PARAM, params.table);
|
|
599
|
+
if (params.where)
|
|
600
|
+
fetchUrl.searchParams.set(WHERE_QUERY_PARAM, params.where);
|
|
601
|
+
if (params.columns)
|
|
602
|
+
fetchUrl.searchParams.set(COLUMNS_QUERY_PARAM, params.columns);
|
|
603
|
+
if (params.replica)
|
|
604
|
+
fetchUrl.searchParams.set(REPLICA_PARAM, params.replica);
|
|
605
|
+
const customParams = __spreadValues({}, params);
|
|
606
|
+
delete customParams.table;
|
|
607
|
+
delete customParams.where;
|
|
608
|
+
delete customParams.columns;
|
|
609
|
+
delete customParams.replica;
|
|
610
|
+
for (const [key, value] of Object.entries(customParams)) {
|
|
611
|
+
fetchUrl.searchParams.set(key, value);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
fetchUrl.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _lastOffset));
|
|
615
|
+
if (__privateGet(this, _isUpToDate)) {
|
|
616
|
+
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
617
|
+
fetchUrl.searchParams.set(
|
|
618
|
+
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
619
|
+
__privateGet(this, _liveCacheBuster)
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
if (__privateGet(this, _shapeHandle)) {
|
|
623
|
+
fetchUrl.searchParams.set(
|
|
624
|
+
SHAPE_HANDLE_QUERY_PARAM,
|
|
625
|
+
__privateGet(this, _shapeHandle)
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
fetchUrl.searchParams.sort();
|
|
629
|
+
let response;
|
|
630
|
+
try {
|
|
631
|
+
response = yield __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), {
|
|
632
|
+
signal,
|
|
633
|
+
headers: this.options.headers
|
|
634
|
+
});
|
|
635
|
+
__privateSet(this, _connected, true);
|
|
636
|
+
} catch (e) {
|
|
637
|
+
if (e instanceof FetchBackoffAbortError) break;
|
|
638
|
+
if (!(e instanceof FetchError)) throw e;
|
|
639
|
+
if (e.status == 409) {
|
|
640
|
+
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER];
|
|
641
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
642
|
+
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, e.json);
|
|
643
|
+
continue;
|
|
644
|
+
} else if (e.status >= 400 && e.status < 500) {
|
|
645
|
+
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, e);
|
|
646
|
+
throw e;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
const { headers, status } = response;
|
|
650
|
+
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
651
|
+
if (shapeHandle) {
|
|
652
|
+
__privateSet(this, _shapeHandle, shapeHandle);
|
|
653
|
+
}
|
|
654
|
+
const lastOffset = headers.get(CHUNK_LAST_OFFSET_HEADER);
|
|
655
|
+
if (lastOffset) {
|
|
656
|
+
__privateSet(this, _lastOffset, lastOffset);
|
|
657
|
+
}
|
|
658
|
+
const liveCacheBuster = headers.get(LIVE_CACHE_BUSTER_HEADER);
|
|
659
|
+
if (liveCacheBuster) {
|
|
660
|
+
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
661
|
+
}
|
|
662
|
+
const getSchema = () => {
|
|
663
|
+
const schemaHeader = headers.get(SHAPE_SCHEMA_HEADER);
|
|
664
|
+
return schemaHeader ? JSON.parse(schemaHeader) : {};
|
|
665
|
+
};
|
|
666
|
+
__privateSet(this, _schema, (_b = __privateGet(this, _schema)) != null ? _b : getSchema());
|
|
667
|
+
const messages = status === 204 ? `[]` : yield response.text();
|
|
668
|
+
if (status === 204) {
|
|
669
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
670
|
+
}
|
|
671
|
+
const batch = __privateGet(this, _messageParser).parse(messages, __privateGet(this, _schema));
|
|
672
|
+
if (batch.length > 0) {
|
|
673
|
+
const lastMessage = batch[batch.length - 1];
|
|
674
|
+
if (isUpToDateMessage(lastMessage)) {
|
|
675
|
+
__privateSet(this, _lastSyncedAt, Date.now());
|
|
676
|
+
__privateSet(this, _isUpToDate, true);
|
|
677
|
+
}
|
|
678
|
+
yield __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, batch);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
} catch (err) {
|
|
682
|
+
__privateSet(this, _error, err);
|
|
683
|
+
if (__privateGet(this, _onError)) {
|
|
684
|
+
const retryOpts = yield __privateGet(this, _onError).call(this, err);
|
|
685
|
+
if (typeof retryOpts === `object`) {
|
|
686
|
+
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this);
|
|
687
|
+
if (`params` in retryOpts) {
|
|
688
|
+
this.options.params = retryOpts.params;
|
|
689
|
+
}
|
|
690
|
+
if (`headers` in retryOpts) {
|
|
691
|
+
this.options.headers = retryOpts.headers;
|
|
692
|
+
}
|
|
693
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
694
|
+
}
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
throw err;
|
|
698
|
+
} finally {
|
|
699
|
+
__privateSet(this, _connected, false);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
};
|
|
665
703
|
publish_fn = function(messages) {
|
|
666
704
|
return __async(this, null, function* () {
|
|
667
705
|
yield Promise.all(
|
|
@@ -682,55 +720,38 @@ sendErrorToSubscribers_fn = function(error) {
|
|
|
682
720
|
errorFn == null ? void 0 : errorFn(error);
|
|
683
721
|
});
|
|
684
722
|
};
|
|
685
|
-
notifyUpToDateSubscribers_fn = function() {
|
|
686
|
-
__privateGet(this, _upToDateSubscribers).forEach(([callback]) => {
|
|
687
|
-
callback();
|
|
688
|
-
});
|
|
689
|
-
};
|
|
690
|
-
sendErrorToUpToDateSubscribers_fn = function(error) {
|
|
691
|
-
__privateGet(this, _upToDateSubscribers).forEach(
|
|
692
|
-
([_, errorCallback]) => errorCallback(error)
|
|
693
|
-
);
|
|
694
|
-
};
|
|
695
723
|
/**
|
|
696
724
|
* Resets the state of the stream, optionally with a provided
|
|
697
725
|
* shape handle
|
|
698
726
|
*/
|
|
699
|
-
reset_fn = function(
|
|
727
|
+
reset_fn = function(handle) {
|
|
700
728
|
__privateSet(this, _lastOffset, `-1`);
|
|
701
729
|
__privateSet(this, _liveCacheBuster, ``);
|
|
702
|
-
__privateSet(this, _shapeHandle,
|
|
730
|
+
__privateSet(this, _shapeHandle, handle);
|
|
703
731
|
__privateSet(this, _isUpToDate, false);
|
|
704
732
|
__privateSet(this, _connected, false);
|
|
705
733
|
__privateSet(this, _schema, void 0);
|
|
706
734
|
};
|
|
707
|
-
|
|
735
|
+
ShapeStream.Replica = {
|
|
708
736
|
FULL: `full`,
|
|
709
737
|
DEFAULT: `default`
|
|
710
738
|
};
|
|
711
|
-
var ShapeStream = _ShapeStream;
|
|
712
739
|
function validateOptions(options) {
|
|
713
740
|
if (!options.url) {
|
|
714
|
-
throw new
|
|
741
|
+
throw new MissingShapeUrlError();
|
|
715
742
|
}
|
|
716
743
|
if (options.signal && !(options.signal instanceof AbortSignal)) {
|
|
717
|
-
throw new
|
|
718
|
-
`Invalid signal option. It must be an instance of AbortSignal.`
|
|
719
|
-
);
|
|
744
|
+
throw new InvalidSignalError();
|
|
720
745
|
}
|
|
721
|
-
if (options.offset !== void 0 && options.offset !== `-1` && !options.
|
|
722
|
-
throw new
|
|
723
|
-
`shapeHandle is required if this isn't an initial fetch (i.e. offset > -1)`
|
|
724
|
-
);
|
|
746
|
+
if (options.offset !== void 0 && options.offset !== `-1` && !options.handle) {
|
|
747
|
+
throw new MissingShapeHandleError();
|
|
725
748
|
}
|
|
726
749
|
if (options.params) {
|
|
727
750
|
const reservedParams = Object.keys(options.params).filter(
|
|
728
751
|
(key) => RESERVED_PARAMS.has(key)
|
|
729
752
|
);
|
|
730
753
|
if (reservedParams.length > 0) {
|
|
731
|
-
throw new
|
|
732
|
-
`Cannot use reserved Electric parameter names in custom params: ${reservedParams.join(`, `)}`
|
|
733
|
-
);
|
|
754
|
+
throw new ReservedParamError(reservedParams);
|
|
734
755
|
}
|
|
735
756
|
}
|
|
736
757
|
return;
|
|
@@ -751,15 +772,6 @@ var Shape = class {
|
|
|
751
772
|
__privateMethod(this, _Shape_instances, process_fn).bind(this),
|
|
752
773
|
__privateMethod(this, _Shape_instances, handleError_fn).bind(this)
|
|
753
774
|
);
|
|
754
|
-
const unsubscribe = __privateGet(this, _stream).subscribeOnceToUpToDate(
|
|
755
|
-
() => {
|
|
756
|
-
unsubscribe();
|
|
757
|
-
},
|
|
758
|
-
(e) => {
|
|
759
|
-
__privateMethod(this, _Shape_instances, handleError_fn).call(this, e);
|
|
760
|
-
throw e;
|
|
761
|
-
}
|
|
762
|
-
);
|
|
763
775
|
}
|
|
764
776
|
get isUpToDate() {
|
|
765
777
|
return __privateGet(this, _stream).isUpToDate;
|
|
@@ -767,6 +779,9 @@ var Shape = class {
|
|
|
767
779
|
get lastOffset() {
|
|
768
780
|
return __privateGet(this, _stream).lastOffset;
|
|
769
781
|
}
|
|
782
|
+
get handle() {
|
|
783
|
+
return __privateGet(this, _stream).shapeHandle;
|
|
784
|
+
}
|
|
770
785
|
get rows() {
|
|
771
786
|
return this.value.then((v) => Array.from(v.values()));
|
|
772
787
|
}
|