@electric-sql/client 1.5.2 → 1.5.3
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/dist/cjs/index.cjs +760 -271
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +2 -2
- package/dist/index.browser.mjs +3 -3
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.legacy-esm.js +760 -271
- package/dist/index.legacy-esm.js.map +1 -1
- package/dist/index.mjs +760 -271
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/client.ts +304 -375
- package/src/pause-lock.ts +112 -0
- package/src/shape-stream-state.ts +781 -0
package/dist/cjs/index.cjs
CHANGED
|
@@ -1130,6 +1130,544 @@ var SnapshotTracker = class {
|
|
|
1130
1130
|
}
|
|
1131
1131
|
};
|
|
1132
1132
|
|
|
1133
|
+
// src/shape-stream-state.ts
|
|
1134
|
+
var ShapeStreamState = class {
|
|
1135
|
+
// --- Derived booleans ---
|
|
1136
|
+
get isUpToDate() {
|
|
1137
|
+
return false;
|
|
1138
|
+
}
|
|
1139
|
+
// --- Per-state field defaults ---
|
|
1140
|
+
get staleCacheBuster() {
|
|
1141
|
+
return void 0;
|
|
1142
|
+
}
|
|
1143
|
+
get staleCacheRetryCount() {
|
|
1144
|
+
return 0;
|
|
1145
|
+
}
|
|
1146
|
+
get sseFallbackToLongPolling() {
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
get consecutiveShortSseConnections() {
|
|
1150
|
+
return 0;
|
|
1151
|
+
}
|
|
1152
|
+
get replayCursor() {
|
|
1153
|
+
return void 0;
|
|
1154
|
+
}
|
|
1155
|
+
// --- Default no-op methods ---
|
|
1156
|
+
canEnterReplayMode() {
|
|
1157
|
+
return false;
|
|
1158
|
+
}
|
|
1159
|
+
enterReplayMode(_cursor) {
|
|
1160
|
+
return this;
|
|
1161
|
+
}
|
|
1162
|
+
shouldUseSse(_opts) {
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
handleSseConnectionClosed(_input) {
|
|
1166
|
+
return {
|
|
1167
|
+
state: this,
|
|
1168
|
+
fellBackToLongPolling: false,
|
|
1169
|
+
wasShortConnection: false
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
// --- URL param application ---
|
|
1173
|
+
/** Adds state-specific query parameters to the fetch URL. */
|
|
1174
|
+
applyUrlParams(_url, _context) {
|
|
1175
|
+
}
|
|
1176
|
+
// --- Default response/message handlers (Paused/Error never receive these) ---
|
|
1177
|
+
handleResponseMetadata(_input) {
|
|
1178
|
+
return { action: `ignored`, state: this };
|
|
1179
|
+
}
|
|
1180
|
+
handleMessageBatch(_input) {
|
|
1181
|
+
return { state: this, suppressBatch: false, becameUpToDate: false };
|
|
1182
|
+
}
|
|
1183
|
+
pause() {
|
|
1184
|
+
return new PausedState(this);
|
|
1185
|
+
}
|
|
1186
|
+
toErrorState(error) {
|
|
1187
|
+
return new ErrorState(this, error);
|
|
1188
|
+
}
|
|
1189
|
+
markMustRefetch(handle) {
|
|
1190
|
+
return new InitialState({
|
|
1191
|
+
handle,
|
|
1192
|
+
offset: `-1`,
|
|
1193
|
+
liveCacheBuster: ``,
|
|
1194
|
+
lastSyncedAt: this.lastSyncedAt,
|
|
1195
|
+
schema: void 0
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
var _shared;
|
|
1200
|
+
var ActiveState = class extends ShapeStreamState {
|
|
1201
|
+
constructor(shared) {
|
|
1202
|
+
super();
|
|
1203
|
+
__privateAdd(this, _shared);
|
|
1204
|
+
__privateSet(this, _shared, shared);
|
|
1205
|
+
}
|
|
1206
|
+
get handle() {
|
|
1207
|
+
return __privateGet(this, _shared).handle;
|
|
1208
|
+
}
|
|
1209
|
+
get offset() {
|
|
1210
|
+
return __privateGet(this, _shared).offset;
|
|
1211
|
+
}
|
|
1212
|
+
get schema() {
|
|
1213
|
+
return __privateGet(this, _shared).schema;
|
|
1214
|
+
}
|
|
1215
|
+
get liveCacheBuster() {
|
|
1216
|
+
return __privateGet(this, _shared).liveCacheBuster;
|
|
1217
|
+
}
|
|
1218
|
+
get lastSyncedAt() {
|
|
1219
|
+
return __privateGet(this, _shared).lastSyncedAt;
|
|
1220
|
+
}
|
|
1221
|
+
/** Expose shared fields to subclasses for spreading into new instances. */
|
|
1222
|
+
get currentFields() {
|
|
1223
|
+
return __privateGet(this, _shared);
|
|
1224
|
+
}
|
|
1225
|
+
// --- URL param application ---
|
|
1226
|
+
applyUrlParams(url, _context) {
|
|
1227
|
+
url.searchParams.set(OFFSET_QUERY_PARAM, __privateGet(this, _shared).offset);
|
|
1228
|
+
if (__privateGet(this, _shared).handle) {
|
|
1229
|
+
url.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, __privateGet(this, _shared).handle);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
// --- Helpers for subclass handleResponseMetadata implementations ---
|
|
1233
|
+
/** Extracts updated SharedStateFields from response headers. */
|
|
1234
|
+
parseResponseFields(input) {
|
|
1235
|
+
var _a, _b, _c;
|
|
1236
|
+
const responseHandle = input.responseHandle;
|
|
1237
|
+
const handle = responseHandle && responseHandle !== input.expiredHandle ? responseHandle : __privateGet(this, _shared).handle;
|
|
1238
|
+
const offset = (_a = input.responseOffset) != null ? _a : __privateGet(this, _shared).offset;
|
|
1239
|
+
const liveCacheBuster = (_b = input.responseCursor) != null ? _b : __privateGet(this, _shared).liveCacheBuster;
|
|
1240
|
+
const schema = (_c = __privateGet(this, _shared).schema) != null ? _c : input.responseSchema;
|
|
1241
|
+
const lastSyncedAt = input.status === 204 ? input.now : __privateGet(this, _shared).lastSyncedAt;
|
|
1242
|
+
return { handle, offset, schema, liveCacheBuster, lastSyncedAt };
|
|
1243
|
+
}
|
|
1244
|
+
/**
|
|
1245
|
+
* Stale detection. Returns a transition if the response is stale,
|
|
1246
|
+
* or null if it is not stale and the caller should proceed normally.
|
|
1247
|
+
*/
|
|
1248
|
+
checkStaleResponse(input) {
|
|
1249
|
+
const responseHandle = input.responseHandle;
|
|
1250
|
+
const expiredHandle = input.expiredHandle;
|
|
1251
|
+
if (!responseHandle || responseHandle !== expiredHandle) {
|
|
1252
|
+
return null;
|
|
1253
|
+
}
|
|
1254
|
+
if (__privateGet(this, _shared).handle === void 0 || __privateGet(this, _shared).handle === expiredHandle) {
|
|
1255
|
+
const retryCount = this.staleCacheRetryCount + 1;
|
|
1256
|
+
return {
|
|
1257
|
+
action: `stale-retry`,
|
|
1258
|
+
state: new StaleRetryState(__spreadProps(__spreadValues({}, this.currentFields), {
|
|
1259
|
+
staleCacheBuster: input.createCacheBuster(),
|
|
1260
|
+
staleCacheRetryCount: retryCount
|
|
1261
|
+
})),
|
|
1262
|
+
exceededMaxRetries: retryCount > input.maxStaleCacheRetries
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1265
|
+
return { action: `ignored`, state: this };
|
|
1266
|
+
}
|
|
1267
|
+
// --- handleMessageBatch: template method with onUpToDate override point ---
|
|
1268
|
+
handleMessageBatch(input) {
|
|
1269
|
+
if (!input.hasMessages || !input.hasUpToDateMessage) {
|
|
1270
|
+
return { state: this, suppressBatch: false, becameUpToDate: false };
|
|
1271
|
+
}
|
|
1272
|
+
let offset = __privateGet(this, _shared).offset;
|
|
1273
|
+
if (input.isSse && input.upToDateOffset) {
|
|
1274
|
+
offset = input.upToDateOffset;
|
|
1275
|
+
}
|
|
1276
|
+
const shared = {
|
|
1277
|
+
handle: __privateGet(this, _shared).handle,
|
|
1278
|
+
offset,
|
|
1279
|
+
schema: __privateGet(this, _shared).schema,
|
|
1280
|
+
liveCacheBuster: __privateGet(this, _shared).liveCacheBuster,
|
|
1281
|
+
lastSyncedAt: input.now
|
|
1282
|
+
};
|
|
1283
|
+
return this.onUpToDate(shared, input);
|
|
1284
|
+
}
|
|
1285
|
+
/** Override point for up-to-date handling. Default → LiveState. */
|
|
1286
|
+
onUpToDate(shared, _input) {
|
|
1287
|
+
return {
|
|
1288
|
+
state: new LiveState(shared),
|
|
1289
|
+
suppressBatch: false,
|
|
1290
|
+
becameUpToDate: true
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
};
|
|
1294
|
+
_shared = new WeakMap();
|
|
1295
|
+
var FetchingState = class extends ActiveState {
|
|
1296
|
+
handleResponseMetadata(input) {
|
|
1297
|
+
const staleResult = this.checkStaleResponse(input);
|
|
1298
|
+
if (staleResult) return staleResult;
|
|
1299
|
+
const shared = this.parseResponseFields(input);
|
|
1300
|
+
return { action: `accepted`, state: new SyncingState(shared) };
|
|
1301
|
+
}
|
|
1302
|
+
canEnterReplayMode() {
|
|
1303
|
+
return true;
|
|
1304
|
+
}
|
|
1305
|
+
enterReplayMode(cursor) {
|
|
1306
|
+
return new ReplayingState(__spreadProps(__spreadValues({}, this.currentFields), {
|
|
1307
|
+
replayCursor: cursor
|
|
1308
|
+
}));
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
var InitialState = class _InitialState extends FetchingState {
|
|
1312
|
+
constructor(shared) {
|
|
1313
|
+
super(shared);
|
|
1314
|
+
this.kind = `initial`;
|
|
1315
|
+
}
|
|
1316
|
+
withHandle(handle) {
|
|
1317
|
+
return new _InitialState(__spreadProps(__spreadValues({}, this.currentFields), { handle }));
|
|
1318
|
+
}
|
|
1319
|
+
};
|
|
1320
|
+
var SyncingState = class _SyncingState extends FetchingState {
|
|
1321
|
+
constructor(shared) {
|
|
1322
|
+
super(shared);
|
|
1323
|
+
this.kind = `syncing`;
|
|
1324
|
+
}
|
|
1325
|
+
withHandle(handle) {
|
|
1326
|
+
return new _SyncingState(__spreadProps(__spreadValues({}, this.currentFields), { handle }));
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
var _staleCacheBuster, _staleCacheRetryCount;
|
|
1330
|
+
var _StaleRetryState = class _StaleRetryState extends FetchingState {
|
|
1331
|
+
constructor(fields) {
|
|
1332
|
+
const _a = fields, { staleCacheBuster, staleCacheRetryCount } = _a, shared = __objRest(_a, ["staleCacheBuster", "staleCacheRetryCount"]);
|
|
1333
|
+
super(shared);
|
|
1334
|
+
this.kind = `stale-retry`;
|
|
1335
|
+
__privateAdd(this, _staleCacheBuster);
|
|
1336
|
+
__privateAdd(this, _staleCacheRetryCount);
|
|
1337
|
+
__privateSet(this, _staleCacheBuster, staleCacheBuster);
|
|
1338
|
+
__privateSet(this, _staleCacheRetryCount, staleCacheRetryCount);
|
|
1339
|
+
}
|
|
1340
|
+
get staleCacheBuster() {
|
|
1341
|
+
return __privateGet(this, _staleCacheBuster);
|
|
1342
|
+
}
|
|
1343
|
+
get staleCacheRetryCount() {
|
|
1344
|
+
return __privateGet(this, _staleCacheRetryCount);
|
|
1345
|
+
}
|
|
1346
|
+
// StaleRetryState must not enter replay mode — it would lose the retry count
|
|
1347
|
+
canEnterReplayMode() {
|
|
1348
|
+
return false;
|
|
1349
|
+
}
|
|
1350
|
+
withHandle(handle) {
|
|
1351
|
+
return new _StaleRetryState(__spreadProps(__spreadValues({}, this.currentFields), {
|
|
1352
|
+
handle,
|
|
1353
|
+
staleCacheBuster: __privateGet(this, _staleCacheBuster),
|
|
1354
|
+
staleCacheRetryCount: __privateGet(this, _staleCacheRetryCount)
|
|
1355
|
+
}));
|
|
1356
|
+
}
|
|
1357
|
+
applyUrlParams(url, context) {
|
|
1358
|
+
super.applyUrlParams(url, context);
|
|
1359
|
+
url.searchParams.set(CACHE_BUSTER_QUERY_PARAM, __privateGet(this, _staleCacheBuster));
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
_staleCacheBuster = new WeakMap();
|
|
1363
|
+
_staleCacheRetryCount = new WeakMap();
|
|
1364
|
+
var StaleRetryState = _StaleRetryState;
|
|
1365
|
+
var _consecutiveShortSseConnections, _sseFallbackToLongPolling;
|
|
1366
|
+
var _LiveState = class _LiveState extends ActiveState {
|
|
1367
|
+
constructor(shared, sseState) {
|
|
1368
|
+
var _a, _b;
|
|
1369
|
+
super(shared);
|
|
1370
|
+
this.kind = `live`;
|
|
1371
|
+
__privateAdd(this, _consecutiveShortSseConnections);
|
|
1372
|
+
__privateAdd(this, _sseFallbackToLongPolling);
|
|
1373
|
+
__privateSet(this, _consecutiveShortSseConnections, (_a = sseState == null ? void 0 : sseState.consecutiveShortSseConnections) != null ? _a : 0);
|
|
1374
|
+
__privateSet(this, _sseFallbackToLongPolling, (_b = sseState == null ? void 0 : sseState.sseFallbackToLongPolling) != null ? _b : false);
|
|
1375
|
+
}
|
|
1376
|
+
get isUpToDate() {
|
|
1377
|
+
return true;
|
|
1378
|
+
}
|
|
1379
|
+
get consecutiveShortSseConnections() {
|
|
1380
|
+
return __privateGet(this, _consecutiveShortSseConnections);
|
|
1381
|
+
}
|
|
1382
|
+
get sseFallbackToLongPolling() {
|
|
1383
|
+
return __privateGet(this, _sseFallbackToLongPolling);
|
|
1384
|
+
}
|
|
1385
|
+
withHandle(handle) {
|
|
1386
|
+
return new _LiveState(__spreadProps(__spreadValues({}, this.currentFields), { handle }), this.sseState);
|
|
1387
|
+
}
|
|
1388
|
+
applyUrlParams(url, context) {
|
|
1389
|
+
super.applyUrlParams(url, context);
|
|
1390
|
+
if (!context.isSnapshotRequest) {
|
|
1391
|
+
url.searchParams.set(LIVE_CACHE_BUSTER_QUERY_PARAM, this.liveCacheBuster);
|
|
1392
|
+
if (context.canLongPoll) {
|
|
1393
|
+
url.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
get sseState() {
|
|
1398
|
+
return {
|
|
1399
|
+
consecutiveShortSseConnections: __privateGet(this, _consecutiveShortSseConnections),
|
|
1400
|
+
sseFallbackToLongPolling: __privateGet(this, _sseFallbackToLongPolling)
|
|
1401
|
+
};
|
|
1402
|
+
}
|
|
1403
|
+
handleResponseMetadata(input) {
|
|
1404
|
+
const staleResult = this.checkStaleResponse(input);
|
|
1405
|
+
if (staleResult) return staleResult;
|
|
1406
|
+
const shared = this.parseResponseFields(input);
|
|
1407
|
+
return {
|
|
1408
|
+
action: `accepted`,
|
|
1409
|
+
state: new _LiveState(shared, this.sseState)
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
onUpToDate(shared, _input) {
|
|
1413
|
+
return {
|
|
1414
|
+
state: new _LiveState(shared, this.sseState),
|
|
1415
|
+
suppressBatch: false,
|
|
1416
|
+
becameUpToDate: true
|
|
1417
|
+
};
|
|
1418
|
+
}
|
|
1419
|
+
shouldUseSse(opts) {
|
|
1420
|
+
return opts.liveSseEnabled && !opts.isRefreshing && !opts.resumingFromPause && !__privateGet(this, _sseFallbackToLongPolling);
|
|
1421
|
+
}
|
|
1422
|
+
handleSseConnectionClosed(input) {
|
|
1423
|
+
let nextConsecutiveShort = __privateGet(this, _consecutiveShortSseConnections);
|
|
1424
|
+
let nextFallback = __privateGet(this, _sseFallbackToLongPolling);
|
|
1425
|
+
let fellBackToLongPolling = false;
|
|
1426
|
+
let wasShortConnection = false;
|
|
1427
|
+
if (input.connectionDuration < input.minConnectionDuration && !input.wasAborted) {
|
|
1428
|
+
wasShortConnection = true;
|
|
1429
|
+
nextConsecutiveShort = nextConsecutiveShort + 1;
|
|
1430
|
+
if (nextConsecutiveShort >= input.maxShortConnections) {
|
|
1431
|
+
nextFallback = true;
|
|
1432
|
+
fellBackToLongPolling = true;
|
|
1433
|
+
}
|
|
1434
|
+
} else if (input.connectionDuration >= input.minConnectionDuration) {
|
|
1435
|
+
nextConsecutiveShort = 0;
|
|
1436
|
+
}
|
|
1437
|
+
return {
|
|
1438
|
+
state: new _LiveState(this.currentFields, {
|
|
1439
|
+
consecutiveShortSseConnections: nextConsecutiveShort,
|
|
1440
|
+
sseFallbackToLongPolling: nextFallback
|
|
1441
|
+
}),
|
|
1442
|
+
fellBackToLongPolling,
|
|
1443
|
+
wasShortConnection
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1446
|
+
};
|
|
1447
|
+
_consecutiveShortSseConnections = new WeakMap();
|
|
1448
|
+
_sseFallbackToLongPolling = new WeakMap();
|
|
1449
|
+
var LiveState = _LiveState;
|
|
1450
|
+
var _replayCursor;
|
|
1451
|
+
var _ReplayingState = class _ReplayingState extends ActiveState {
|
|
1452
|
+
constructor(fields) {
|
|
1453
|
+
const _a = fields, { replayCursor } = _a, shared = __objRest(_a, ["replayCursor"]);
|
|
1454
|
+
super(shared);
|
|
1455
|
+
this.kind = `replaying`;
|
|
1456
|
+
__privateAdd(this, _replayCursor);
|
|
1457
|
+
__privateSet(this, _replayCursor, replayCursor);
|
|
1458
|
+
}
|
|
1459
|
+
get replayCursor() {
|
|
1460
|
+
return __privateGet(this, _replayCursor);
|
|
1461
|
+
}
|
|
1462
|
+
withHandle(handle) {
|
|
1463
|
+
return new _ReplayingState(__spreadProps(__spreadValues({}, this.currentFields), {
|
|
1464
|
+
handle,
|
|
1465
|
+
replayCursor: __privateGet(this, _replayCursor)
|
|
1466
|
+
}));
|
|
1467
|
+
}
|
|
1468
|
+
handleResponseMetadata(input) {
|
|
1469
|
+
const staleResult = this.checkStaleResponse(input);
|
|
1470
|
+
if (staleResult) return staleResult;
|
|
1471
|
+
const shared = this.parseResponseFields(input);
|
|
1472
|
+
return {
|
|
1473
|
+
action: `accepted`,
|
|
1474
|
+
state: new _ReplayingState(__spreadProps(__spreadValues({}, shared), {
|
|
1475
|
+
replayCursor: __privateGet(this, _replayCursor)
|
|
1476
|
+
}))
|
|
1477
|
+
};
|
|
1478
|
+
}
|
|
1479
|
+
onUpToDate(shared, input) {
|
|
1480
|
+
const suppressBatch = !input.isSse && __privateGet(this, _replayCursor) === input.currentCursor;
|
|
1481
|
+
return {
|
|
1482
|
+
state: new LiveState(shared),
|
|
1483
|
+
suppressBatch,
|
|
1484
|
+
becameUpToDate: true
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
_replayCursor = new WeakMap();
|
|
1489
|
+
var ReplayingState = _ReplayingState;
|
|
1490
|
+
var PausedState = class _PausedState extends ShapeStreamState {
|
|
1491
|
+
constructor(previousState) {
|
|
1492
|
+
super();
|
|
1493
|
+
this.kind = `paused`;
|
|
1494
|
+
this.previousState = previousState;
|
|
1495
|
+
}
|
|
1496
|
+
get handle() {
|
|
1497
|
+
return this.previousState.handle;
|
|
1498
|
+
}
|
|
1499
|
+
get offset() {
|
|
1500
|
+
return this.previousState.offset;
|
|
1501
|
+
}
|
|
1502
|
+
get schema() {
|
|
1503
|
+
return this.previousState.schema;
|
|
1504
|
+
}
|
|
1505
|
+
get liveCacheBuster() {
|
|
1506
|
+
return this.previousState.liveCacheBuster;
|
|
1507
|
+
}
|
|
1508
|
+
get lastSyncedAt() {
|
|
1509
|
+
return this.previousState.lastSyncedAt;
|
|
1510
|
+
}
|
|
1511
|
+
get isUpToDate() {
|
|
1512
|
+
return this.previousState.isUpToDate;
|
|
1513
|
+
}
|
|
1514
|
+
get staleCacheBuster() {
|
|
1515
|
+
return this.previousState.staleCacheBuster;
|
|
1516
|
+
}
|
|
1517
|
+
get staleCacheRetryCount() {
|
|
1518
|
+
return this.previousState.staleCacheRetryCount;
|
|
1519
|
+
}
|
|
1520
|
+
get sseFallbackToLongPolling() {
|
|
1521
|
+
return this.previousState.sseFallbackToLongPolling;
|
|
1522
|
+
}
|
|
1523
|
+
get consecutiveShortSseConnections() {
|
|
1524
|
+
return this.previousState.consecutiveShortSseConnections;
|
|
1525
|
+
}
|
|
1526
|
+
get replayCursor() {
|
|
1527
|
+
return this.previousState.replayCursor;
|
|
1528
|
+
}
|
|
1529
|
+
withHandle(handle) {
|
|
1530
|
+
return new _PausedState(this.previousState.withHandle(handle));
|
|
1531
|
+
}
|
|
1532
|
+
applyUrlParams(url, context) {
|
|
1533
|
+
this.previousState.applyUrlParams(url, context);
|
|
1534
|
+
}
|
|
1535
|
+
pause() {
|
|
1536
|
+
return this;
|
|
1537
|
+
}
|
|
1538
|
+
resume() {
|
|
1539
|
+
return this.previousState;
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
var ErrorState = class _ErrorState extends ShapeStreamState {
|
|
1543
|
+
constructor(previousState, error) {
|
|
1544
|
+
super();
|
|
1545
|
+
this.kind = `error`;
|
|
1546
|
+
this.previousState = previousState;
|
|
1547
|
+
this.error = error;
|
|
1548
|
+
}
|
|
1549
|
+
get handle() {
|
|
1550
|
+
return this.previousState.handle;
|
|
1551
|
+
}
|
|
1552
|
+
get offset() {
|
|
1553
|
+
return this.previousState.offset;
|
|
1554
|
+
}
|
|
1555
|
+
get schema() {
|
|
1556
|
+
return this.previousState.schema;
|
|
1557
|
+
}
|
|
1558
|
+
get liveCacheBuster() {
|
|
1559
|
+
return this.previousState.liveCacheBuster;
|
|
1560
|
+
}
|
|
1561
|
+
get lastSyncedAt() {
|
|
1562
|
+
return this.previousState.lastSyncedAt;
|
|
1563
|
+
}
|
|
1564
|
+
get isUpToDate() {
|
|
1565
|
+
return this.previousState.isUpToDate;
|
|
1566
|
+
}
|
|
1567
|
+
withHandle(handle) {
|
|
1568
|
+
return new _ErrorState(this.previousState.withHandle(handle), this.error);
|
|
1569
|
+
}
|
|
1570
|
+
applyUrlParams(url, context) {
|
|
1571
|
+
this.previousState.applyUrlParams(url, context);
|
|
1572
|
+
}
|
|
1573
|
+
retry() {
|
|
1574
|
+
return this.previousState;
|
|
1575
|
+
}
|
|
1576
|
+
reset(handle) {
|
|
1577
|
+
return this.previousState.markMustRefetch(handle);
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1580
|
+
function createInitialState(opts) {
|
|
1581
|
+
return new InitialState({
|
|
1582
|
+
handle: opts.handle,
|
|
1583
|
+
offset: opts.offset,
|
|
1584
|
+
liveCacheBuster: ``,
|
|
1585
|
+
lastSyncedAt: void 0,
|
|
1586
|
+
schema: void 0
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/pause-lock.ts
|
|
1591
|
+
var _holders, _onAcquired, _onReleased;
|
|
1592
|
+
var PauseLock = class {
|
|
1593
|
+
constructor(callbacks) {
|
|
1594
|
+
__privateAdd(this, _holders, /* @__PURE__ */ new Set());
|
|
1595
|
+
__privateAdd(this, _onAcquired);
|
|
1596
|
+
__privateAdd(this, _onReleased);
|
|
1597
|
+
__privateSet(this, _onAcquired, callbacks.onAcquired);
|
|
1598
|
+
__privateSet(this, _onReleased, callbacks.onReleased);
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Acquire the lock for a given reason. Idempotent — acquiring the same
|
|
1602
|
+
* reason twice is a no-op (but logs a warning since it likely indicates
|
|
1603
|
+
* a caller bug).
|
|
1604
|
+
*
|
|
1605
|
+
* Fires `onAcquired` when the first reason is acquired (transition from
|
|
1606
|
+
* unlocked to locked).
|
|
1607
|
+
*/
|
|
1608
|
+
acquire(reason) {
|
|
1609
|
+
if (__privateGet(this, _holders).has(reason)) {
|
|
1610
|
+
console.warn(
|
|
1611
|
+
`[Electric] PauseLock: "${reason}" already held \u2014 ignoring duplicate acquire`
|
|
1612
|
+
);
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
const wasUnlocked = __privateGet(this, _holders).size === 0;
|
|
1616
|
+
__privateGet(this, _holders).add(reason);
|
|
1617
|
+
if (wasUnlocked) {
|
|
1618
|
+
__privateGet(this, _onAcquired).call(this);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Release the lock for a given reason. Releasing a reason that isn't
|
|
1623
|
+
* held logs a warning (likely indicates an acquire/release mismatch).
|
|
1624
|
+
*
|
|
1625
|
+
* Fires `onReleased` when the last reason is released (transition from
|
|
1626
|
+
* locked to unlocked).
|
|
1627
|
+
*/
|
|
1628
|
+
release(reason) {
|
|
1629
|
+
if (!__privateGet(this, _holders).delete(reason)) {
|
|
1630
|
+
console.warn(
|
|
1631
|
+
`[Electric] PauseLock: "${reason}" not held \u2014 ignoring release (possible acquire/release mismatch)`
|
|
1632
|
+
);
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
if (__privateGet(this, _holders).size === 0) {
|
|
1636
|
+
__privateGet(this, _onReleased).call(this);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
/**
|
|
1640
|
+
* Whether the lock is currently held by any reason.
|
|
1641
|
+
*/
|
|
1642
|
+
get isPaused() {
|
|
1643
|
+
return __privateGet(this, _holders).size > 0;
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Check if a specific reason is holding the lock.
|
|
1647
|
+
*/
|
|
1648
|
+
isHeldBy(reason) {
|
|
1649
|
+
return __privateGet(this, _holders).has(reason);
|
|
1650
|
+
}
|
|
1651
|
+
/**
|
|
1652
|
+
* Release all reasons matching a prefix. Does NOT fire `onReleased` —
|
|
1653
|
+
* this is for cleanup/reset paths where the stream state is being
|
|
1654
|
+
* managed separately.
|
|
1655
|
+
*
|
|
1656
|
+
* This preserves reasons with different prefixes (e.g., 'visibility'
|
|
1657
|
+
* is preserved when clearing 'snapshot-*' reasons).
|
|
1658
|
+
*/
|
|
1659
|
+
releaseAllMatching(prefix) {
|
|
1660
|
+
for (const reason of __privateGet(this, _holders)) {
|
|
1661
|
+
if (reason.startsWith(prefix)) {
|
|
1662
|
+
__privateGet(this, _holders).delete(reason);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
_holders = new WeakMap();
|
|
1668
|
+
_onAcquired = new WeakMap();
|
|
1669
|
+
_onReleased = new WeakMap();
|
|
1670
|
+
|
|
1133
1671
|
// src/client.ts
|
|
1134
1672
|
var RESERVED_PARAMS = /* @__PURE__ */ new Set([
|
|
1135
1673
|
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
@@ -1178,7 +1716,7 @@ function canonicalShapeKey(url) {
|
|
|
1178
1716
|
cleanUrl.searchParams.sort();
|
|
1179
1717
|
return cleanUrl.toString();
|
|
1180
1718
|
}
|
|
1181
|
-
var _error, _fetchClient2, _sseFetchClient, _messageParser, _subscribers, _started,
|
|
1719
|
+
var _error, _fetchClient2, _sseFetchClient, _messageParser, _subscribers, _started, _syncState, _connected, _mode, _onError, _requestAbortController, _refreshCount, _snapshotCounter, _ShapeStream_instances, isRefreshing_get, _tickPromise, _tickPromiseResolver, _tickPromiseRejecter, _messageChain, _snapshotTracker, _pauseLock, _currentFetchUrl, _lastSseConnectionStartTime, _minSseConnectionDuration, _maxShortSseConnections, _sseBackoffBaseDelay, _sseBackoffMaxDelay, _unsubscribeFromVisibilityChanges, _unsubscribeFromWakeDetection, _maxStaleCacheRetries, start_fn, teardown_fn, requestShape_fn, constructUrl_fn, createAbortListener_fn, onInitialResponse_fn, onMessages_fn, fetchShape_fn, requestShapeLongPoll_fn, requestShapeSSE_fn, nextTick_fn, publish_fn, sendErrorToSubscribers_fn, hasBrowserVisibilityAPI_fn, subscribeToVisibilityChanges_fn, subscribeToWakeDetection_fn, reset_fn, buildSubsetBody_fn;
|
|
1182
1720
|
var ShapeStream = class {
|
|
1183
1721
|
constructor(options) {
|
|
1184
1722
|
__privateAdd(this, _ShapeStream_instances);
|
|
@@ -1188,58 +1726,57 @@ var ShapeStream = class {
|
|
|
1188
1726
|
__privateAdd(this, _messageParser);
|
|
1189
1727
|
__privateAdd(this, _subscribers, /* @__PURE__ */ new Map());
|
|
1190
1728
|
__privateAdd(this, _started, false);
|
|
1191
|
-
__privateAdd(this,
|
|
1192
|
-
__privateAdd(this, _lastOffset);
|
|
1193
|
-
__privateAdd(this, _liveCacheBuster);
|
|
1194
|
-
// Seconds since our Electric Epoch 😎
|
|
1195
|
-
__privateAdd(this, _lastSyncedAt);
|
|
1196
|
-
// unix time
|
|
1197
|
-
__privateAdd(this, _isUpToDate, false);
|
|
1198
|
-
__privateAdd(this, _isMidStream, true);
|
|
1729
|
+
__privateAdd(this, _syncState);
|
|
1199
1730
|
__privateAdd(this, _connected, false);
|
|
1200
|
-
__privateAdd(this, _shapeHandle);
|
|
1201
1731
|
__privateAdd(this, _mode);
|
|
1202
|
-
__privateAdd(this, _schema);
|
|
1203
1732
|
__privateAdd(this, _onError);
|
|
1204
1733
|
__privateAdd(this, _requestAbortController);
|
|
1205
|
-
__privateAdd(this,
|
|
1734
|
+
__privateAdd(this, _refreshCount, 0);
|
|
1735
|
+
__privateAdd(this, _snapshotCounter, 0);
|
|
1206
1736
|
__privateAdd(this, _tickPromise);
|
|
1207
1737
|
__privateAdd(this, _tickPromiseResolver);
|
|
1208
1738
|
__privateAdd(this, _tickPromiseRejecter);
|
|
1209
1739
|
__privateAdd(this, _messageChain, Promise.resolve([]));
|
|
1210
1740
|
// promise chain for incoming messages
|
|
1211
1741
|
__privateAdd(this, _snapshotTracker, new SnapshotTracker());
|
|
1212
|
-
__privateAdd(this,
|
|
1213
|
-
// counter for concurrent snapshot requests
|
|
1214
|
-
__privateAdd(this, _midStreamPromise);
|
|
1215
|
-
__privateAdd(this, _midStreamPromiseResolver);
|
|
1216
|
-
__privateAdd(this, _lastSeenCursor);
|
|
1217
|
-
// Last seen cursor from previous session (used to detect cached responses)
|
|
1742
|
+
__privateAdd(this, _pauseLock);
|
|
1218
1743
|
__privateAdd(this, _currentFetchUrl);
|
|
1219
1744
|
// Current fetch URL for computing shape key
|
|
1220
1745
|
__privateAdd(this, _lastSseConnectionStartTime);
|
|
1221
1746
|
__privateAdd(this, _minSseConnectionDuration, 1e3);
|
|
1222
1747
|
// Minimum expected SSE connection duration (1 second)
|
|
1223
|
-
__privateAdd(this, _consecutiveShortSseConnections, 0);
|
|
1224
1748
|
__privateAdd(this, _maxShortSseConnections, 3);
|
|
1225
1749
|
// Fall back to long polling after this many short connections
|
|
1226
|
-
__privateAdd(this, _sseFallbackToLongPolling, false);
|
|
1227
1750
|
__privateAdd(this, _sseBackoffBaseDelay, 100);
|
|
1228
1751
|
// Base delay for exponential backoff (ms)
|
|
1229
1752
|
__privateAdd(this, _sseBackoffMaxDelay, 5e3);
|
|
1230
1753
|
// Maximum delay cap (ms)
|
|
1231
1754
|
__privateAdd(this, _unsubscribeFromVisibilityChanges);
|
|
1232
1755
|
__privateAdd(this, _unsubscribeFromWakeDetection);
|
|
1233
|
-
__privateAdd(this, _staleCacheBuster);
|
|
1234
|
-
// Cache buster set when stale CDN response detected, used on retry requests to bypass cache
|
|
1235
|
-
__privateAdd(this, _staleCacheRetryCount, 0);
|
|
1236
1756
|
__privateAdd(this, _maxStaleCacheRetries, 3);
|
|
1237
1757
|
var _a, _b, _c, _d;
|
|
1238
1758
|
this.options = __spreadValues({ subscribe: true }, options);
|
|
1239
1759
|
validateOptions(this.options);
|
|
1240
|
-
__privateSet(this,
|
|
1241
|
-
|
|
1242
|
-
|
|
1760
|
+
__privateSet(this, _syncState, createInitialState({
|
|
1761
|
+
offset: (_a = this.options.offset) != null ? _a : `-1`,
|
|
1762
|
+
handle: this.options.handle
|
|
1763
|
+
}));
|
|
1764
|
+
__privateSet(this, _pauseLock, new PauseLock({
|
|
1765
|
+
onAcquired: () => {
|
|
1766
|
+
var _a2;
|
|
1767
|
+
__privateSet(this, _syncState, __privateGet(this, _syncState).pause());
|
|
1768
|
+
if (__privateGet(this, _started)) {
|
|
1769
|
+
(_a2 = __privateGet(this, _requestAbortController)) == null ? void 0 : _a2.abort(PAUSE_STREAM);
|
|
1770
|
+
}
|
|
1771
|
+
},
|
|
1772
|
+
onReleased: () => {
|
|
1773
|
+
var _a2;
|
|
1774
|
+
if (!__privateGet(this, _started)) return;
|
|
1775
|
+
if ((_a2 = this.options.signal) == null ? void 0 : _a2.aborted) return;
|
|
1776
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this).catch(() => {
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
}));
|
|
1243
1780
|
let transformer;
|
|
1244
1781
|
if (options.columnMapper) {
|
|
1245
1782
|
const applyColumnMapper = (row) => {
|
|
@@ -1277,16 +1814,16 @@ var ShapeStream = class {
|
|
|
1277
1814
|
__privateMethod(this, _ShapeStream_instances, subscribeToWakeDetection_fn).call(this);
|
|
1278
1815
|
}
|
|
1279
1816
|
get shapeHandle() {
|
|
1280
|
-
return __privateGet(this,
|
|
1817
|
+
return __privateGet(this, _syncState).handle;
|
|
1281
1818
|
}
|
|
1282
1819
|
get error() {
|
|
1283
1820
|
return __privateGet(this, _error);
|
|
1284
1821
|
}
|
|
1285
1822
|
get isUpToDate() {
|
|
1286
|
-
return __privateGet(this,
|
|
1823
|
+
return __privateGet(this, _syncState).isUpToDate;
|
|
1287
1824
|
}
|
|
1288
1825
|
get lastOffset() {
|
|
1289
|
-
return __privateGet(this,
|
|
1826
|
+
return __privateGet(this, _syncState).offset;
|
|
1290
1827
|
}
|
|
1291
1828
|
get mode() {
|
|
1292
1829
|
return __privateGet(this, _mode);
|
|
@@ -1306,28 +1843,28 @@ var ShapeStream = class {
|
|
|
1306
1843
|
(_a = __privateGet(this, _unsubscribeFromVisibilityChanges)) == null ? void 0 : _a.call(this);
|
|
1307
1844
|
(_b = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _b.call(this);
|
|
1308
1845
|
}
|
|
1309
|
-
/** Unix time at which we last synced. Undefined
|
|
1846
|
+
/** Unix time at which we last synced. Undefined until first successful up-to-date. */
|
|
1310
1847
|
lastSyncedAt() {
|
|
1311
|
-
return __privateGet(this,
|
|
1848
|
+
return __privateGet(this, _syncState).lastSyncedAt;
|
|
1312
1849
|
}
|
|
1313
1850
|
/** Time elapsed since last sync (in ms). Infinity if we did not yet sync. */
|
|
1314
1851
|
lastSynced() {
|
|
1315
|
-
if (__privateGet(this,
|
|
1316
|
-
return Date.now() - __privateGet(this,
|
|
1852
|
+
if (__privateGet(this, _syncState).lastSyncedAt === void 0) return Infinity;
|
|
1853
|
+
return Date.now() - __privateGet(this, _syncState).lastSyncedAt;
|
|
1317
1854
|
}
|
|
1318
1855
|
/** Indicates if we are connected to the Electric sync service. */
|
|
1319
1856
|
isConnected() {
|
|
1320
1857
|
return __privateGet(this, _connected);
|
|
1321
1858
|
}
|
|
1322
|
-
/** True during initial fetch. False
|
|
1859
|
+
/** True during initial fetch. False afterwards. */
|
|
1323
1860
|
isLoading() {
|
|
1324
|
-
return !__privateGet(this,
|
|
1861
|
+
return !__privateGet(this, _syncState).isUpToDate;
|
|
1325
1862
|
}
|
|
1326
1863
|
hasStarted() {
|
|
1327
1864
|
return __privateGet(this, _started);
|
|
1328
1865
|
}
|
|
1329
1866
|
isPaused() {
|
|
1330
|
-
return __privateGet(this,
|
|
1867
|
+
return __privateGet(this, _pauseLock).isPaused;
|
|
1331
1868
|
}
|
|
1332
1869
|
/**
|
|
1333
1870
|
* Refreshes the shape stream.
|
|
@@ -1337,12 +1874,15 @@ var ShapeStream = class {
|
|
|
1337
1874
|
*/
|
|
1338
1875
|
async forceDisconnectAndRefresh() {
|
|
1339
1876
|
var _a, _b;
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
(
|
|
1877
|
+
__privateWrapper(this, _refreshCount)._++;
|
|
1878
|
+
try {
|
|
1879
|
+
if (__privateGet(this, _syncState).isUpToDate && !((_a = __privateGet(this, _requestAbortController)) == null ? void 0 : _a.signal.aborted)) {
|
|
1880
|
+
(_b = __privateGet(this, _requestAbortController)) == null ? void 0 : _b.abort(FORCE_DISCONNECT_AND_REFRESH);
|
|
1881
|
+
}
|
|
1882
|
+
await __privateMethod(this, _ShapeStream_instances, nextTick_fn).call(this);
|
|
1883
|
+
} finally {
|
|
1884
|
+
__privateWrapper(this, _refreshCount)._--;
|
|
1343
1885
|
}
|
|
1344
|
-
await __privateMethod(this, _ShapeStream_instances, nextTick_fn).call(this);
|
|
1345
|
-
__privateSet(this, _isRefreshing, false);
|
|
1346
1886
|
}
|
|
1347
1887
|
/**
|
|
1348
1888
|
* Request a snapshot for subset of data and inject it into the subscribed data stream.
|
|
@@ -1364,13 +1904,18 @@ var ShapeStream = class {
|
|
|
1364
1904
|
`Snapshot requests are not supported in ${__privateGet(this, _mode)} mode, as the consumer is guaranteed to observe all data`
|
|
1365
1905
|
);
|
|
1366
1906
|
}
|
|
1367
|
-
if (!__privateGet(this, _started))
|
|
1368
|
-
|
|
1369
|
-
|
|
1907
|
+
if (!__privateGet(this, _started)) {
|
|
1908
|
+
__privateMethod(this, _ShapeStream_instances, start_fn).call(this).catch(() => {
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
const snapshotReason = `snapshot-${++__privateWrapper(this, _snapshotCounter)._}`;
|
|
1912
|
+
__privateGet(this, _pauseLock).acquire(snapshotReason);
|
|
1913
|
+
const snapshotWarnTimer = setTimeout(() => {
|
|
1914
|
+
console.warn(
|
|
1915
|
+
`[Electric] Snapshot "${snapshotReason}" has held the pause lock for 30s \u2014 possible hung request or leaked lock. Current holders: ${[.../* @__PURE__ */ new Set([snapshotReason])].join(`, `)}`
|
|
1916
|
+
);
|
|
1917
|
+
}, 3e4);
|
|
1370
1918
|
try {
|
|
1371
|
-
if (__privateGet(this, _activeSnapshotRequests) === 1) {
|
|
1372
|
-
__privateMethod(this, _ShapeStream_instances, pause_fn).call(this);
|
|
1373
|
-
}
|
|
1374
1919
|
const { metadata, data } = await this.fetchSnapshot(opts);
|
|
1375
1920
|
const dataWithEndBoundary = data.concat([
|
|
1376
1921
|
{ headers: __spreadValues({ control: `snapshot-end` }, metadata) },
|
|
@@ -1386,10 +1931,8 @@ var ShapeStream = class {
|
|
|
1386
1931
|
data
|
|
1387
1932
|
};
|
|
1388
1933
|
} finally {
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
__privateMethod(this, _ShapeStream_instances, resume_fn).call(this);
|
|
1392
|
-
}
|
|
1934
|
+
clearTimeout(snapshotWarnTimer);
|
|
1935
|
+
__privateGet(this, _pauseLock).release(snapshotReason);
|
|
1393
1936
|
}
|
|
1394
1937
|
}
|
|
1395
1938
|
/**
|
|
@@ -1424,7 +1967,7 @@ var ShapeStream = class {
|
|
|
1424
1967
|
fetchUrl = result.fetchUrl;
|
|
1425
1968
|
fetchOptions = { headers: result.requestHeaders };
|
|
1426
1969
|
}
|
|
1427
|
-
const usedHandle = __privateGet(this,
|
|
1970
|
+
const usedHandle = __privateGet(this, _syncState).handle;
|
|
1428
1971
|
let response;
|
|
1429
1972
|
try {
|
|
1430
1973
|
response = await __privateGet(this, _fetchClient2).call(this, fetchUrl.toString(), fetchOptions);
|
|
@@ -1434,7 +1977,8 @@ var ShapeStream = class {
|
|
|
1434
1977
|
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1435
1978
|
expiredShapesCache.markExpired(shapeKey, usedHandle);
|
|
1436
1979
|
}
|
|
1437
|
-
|
|
1980
|
+
const nextHandle = e.headers[SHAPE_HANDLE_HEADER] || `${usedHandle != null ? usedHandle : `handle`}-next`;
|
|
1981
|
+
__privateSet(this, _syncState, __privateGet(this, _syncState).withHandle(nextHandle));
|
|
1438
1982
|
return this.fetchSnapshot(opts);
|
|
1439
1983
|
}
|
|
1440
1984
|
throw e;
|
|
@@ -1442,7 +1986,7 @@ var ShapeStream = class {
|
|
|
1442
1986
|
if (!response.ok) {
|
|
1443
1987
|
throw await FetchError.fromResponse(response, fetchUrl.toString());
|
|
1444
1988
|
}
|
|
1445
|
-
const schema = (_c = __privateGet(this,
|
|
1989
|
+
const schema = (_c = __privateGet(this, _syncState).schema) != null ? _c : getSchemaFromHeaders(response.headers, {
|
|
1446
1990
|
required: true,
|
|
1447
1991
|
url: fetchUrl.toString()
|
|
1448
1992
|
});
|
|
@@ -1460,52 +2004,42 @@ _sseFetchClient = new WeakMap();
|
|
|
1460
2004
|
_messageParser = new WeakMap();
|
|
1461
2005
|
_subscribers = new WeakMap();
|
|
1462
2006
|
_started = new WeakMap();
|
|
1463
|
-
|
|
1464
|
-
_lastOffset = new WeakMap();
|
|
1465
|
-
_liveCacheBuster = new WeakMap();
|
|
1466
|
-
_lastSyncedAt = new WeakMap();
|
|
1467
|
-
_isUpToDate = new WeakMap();
|
|
1468
|
-
_isMidStream = new WeakMap();
|
|
2007
|
+
_syncState = new WeakMap();
|
|
1469
2008
|
_connected = new WeakMap();
|
|
1470
|
-
_shapeHandle = new WeakMap();
|
|
1471
2009
|
_mode = new WeakMap();
|
|
1472
|
-
_schema = new WeakMap();
|
|
1473
2010
|
_onError = new WeakMap();
|
|
1474
2011
|
_requestAbortController = new WeakMap();
|
|
1475
|
-
|
|
2012
|
+
_refreshCount = new WeakMap();
|
|
2013
|
+
_snapshotCounter = new WeakMap();
|
|
2014
|
+
_ShapeStream_instances = new WeakSet();
|
|
2015
|
+
isRefreshing_get = function() {
|
|
2016
|
+
return __privateGet(this, _refreshCount) > 0;
|
|
2017
|
+
};
|
|
1476
2018
|
_tickPromise = new WeakMap();
|
|
1477
2019
|
_tickPromiseResolver = new WeakMap();
|
|
1478
2020
|
_tickPromiseRejecter = new WeakMap();
|
|
1479
2021
|
_messageChain = new WeakMap();
|
|
1480
2022
|
_snapshotTracker = new WeakMap();
|
|
1481
|
-
|
|
1482
|
-
_midStreamPromise = new WeakMap();
|
|
1483
|
-
_midStreamPromiseResolver = new WeakMap();
|
|
1484
|
-
_lastSeenCursor = new WeakMap();
|
|
2023
|
+
_pauseLock = new WeakMap();
|
|
1485
2024
|
_currentFetchUrl = new WeakMap();
|
|
1486
2025
|
_lastSseConnectionStartTime = new WeakMap();
|
|
1487
2026
|
_minSseConnectionDuration = new WeakMap();
|
|
1488
|
-
_consecutiveShortSseConnections = new WeakMap();
|
|
1489
2027
|
_maxShortSseConnections = new WeakMap();
|
|
1490
|
-
_sseFallbackToLongPolling = new WeakMap();
|
|
1491
2028
|
_sseBackoffBaseDelay = new WeakMap();
|
|
1492
2029
|
_sseBackoffMaxDelay = new WeakMap();
|
|
1493
2030
|
_unsubscribeFromVisibilityChanges = new WeakMap();
|
|
1494
2031
|
_unsubscribeFromWakeDetection = new WeakMap();
|
|
1495
|
-
_staleCacheBuster = new WeakMap();
|
|
1496
|
-
_staleCacheRetryCount = new WeakMap();
|
|
1497
2032
|
_maxStaleCacheRetries = new WeakMap();
|
|
1498
|
-
_ShapeStream_instances = new WeakSet();
|
|
1499
|
-
replayMode_get = function() {
|
|
1500
|
-
return __privateGet(this, _lastSeenCursor) !== void 0;
|
|
1501
|
-
};
|
|
1502
2033
|
start_fn = async function() {
|
|
1503
|
-
var _a, _b
|
|
2034
|
+
var _a, _b;
|
|
1504
2035
|
__privateSet(this, _started, true);
|
|
1505
2036
|
try {
|
|
1506
2037
|
await __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
1507
2038
|
} catch (err) {
|
|
1508
2039
|
__privateSet(this, _error, err);
|
|
2040
|
+
if (err instanceof Error) {
|
|
2041
|
+
__privateSet(this, _syncState, __privateGet(this, _syncState).toErrorState(err));
|
|
2042
|
+
}
|
|
1509
2043
|
if (__privateGet(this, _onError)) {
|
|
1510
2044
|
const retryOpts = await __privateGet(this, _onError).call(this, err);
|
|
1511
2045
|
const isRetryable = !(err instanceof MissingHeadersError);
|
|
@@ -1517,6 +2051,9 @@ start_fn = async function() {
|
|
|
1517
2051
|
this.options.headers = __spreadValues(__spreadValues({}, (_b = this.options.headers) != null ? _b : {}), retryOpts.headers);
|
|
1518
2052
|
}
|
|
1519
2053
|
__privateSet(this, _error, null);
|
|
2054
|
+
if (__privateGet(this, _syncState) instanceof ErrorState) {
|
|
2055
|
+
__privateSet(this, _syncState, __privateGet(this, _syncState).retry());
|
|
2056
|
+
}
|
|
1520
2057
|
__privateSet(this, _started, false);
|
|
1521
2058
|
await __privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
1522
2059
|
return;
|
|
@@ -1524,38 +2061,45 @@ start_fn = async function() {
|
|
|
1524
2061
|
if (err instanceof Error) {
|
|
1525
2062
|
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
|
|
1526
2063
|
}
|
|
1527
|
-
|
|
1528
|
-
(_c = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _c.call(this);
|
|
1529
|
-
(_d = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _d.call(this);
|
|
2064
|
+
__privateMethod(this, _ShapeStream_instances, teardown_fn).call(this);
|
|
1530
2065
|
return;
|
|
1531
2066
|
}
|
|
1532
2067
|
if (err instanceof Error) {
|
|
1533
2068
|
__privateMethod(this, _ShapeStream_instances, sendErrorToSubscribers_fn).call(this, err);
|
|
1534
2069
|
}
|
|
1535
|
-
|
|
1536
|
-
(_e = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _e.call(this);
|
|
1537
|
-
(_f = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _f.call(this);
|
|
2070
|
+
__privateMethod(this, _ShapeStream_instances, teardown_fn).call(this);
|
|
1538
2071
|
throw err;
|
|
1539
2072
|
}
|
|
2073
|
+
__privateMethod(this, _ShapeStream_instances, teardown_fn).call(this);
|
|
2074
|
+
};
|
|
2075
|
+
teardown_fn = function() {
|
|
2076
|
+
var _a, _b;
|
|
1540
2077
|
__privateSet(this, _connected, false);
|
|
1541
|
-
(
|
|
1542
|
-
(
|
|
2078
|
+
(_a = __privateGet(this, _tickPromiseRejecter)) == null ? void 0 : _a.call(this);
|
|
2079
|
+
(_b = __privateGet(this, _unsubscribeFromWakeDetection)) == null ? void 0 : _b.call(this);
|
|
1543
2080
|
};
|
|
1544
2081
|
requestShape_fn = async function() {
|
|
1545
2082
|
var _a, _b;
|
|
1546
|
-
if (__privateGet(this,
|
|
1547
|
-
|
|
2083
|
+
if (__privateGet(this, _pauseLock).isPaused) return;
|
|
2084
|
+
if (!this.options.subscribe && (((_a = this.options.signal) == null ? void 0 : _a.aborted) || __privateGet(this, _syncState).isUpToDate)) {
|
|
1548
2085
|
return;
|
|
1549
2086
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
2087
|
+
let resumingFromPause = false;
|
|
2088
|
+
if (__privateGet(this, _syncState) instanceof PausedState) {
|
|
2089
|
+
resumingFromPause = true;
|
|
2090
|
+
__privateSet(this, _syncState, __privateGet(this, _syncState).resume());
|
|
1552
2091
|
}
|
|
1553
|
-
const resumingFromPause = __privateGet(this, _state) === `paused`;
|
|
1554
|
-
__privateSet(this, _state, `active`);
|
|
1555
2092
|
const { url, signal } = this.options;
|
|
1556
2093
|
const { fetchUrl, requestHeaders } = await __privateMethod(this, _ShapeStream_instances, constructUrl_fn).call(this, url, resumingFromPause);
|
|
1557
2094
|
const abortListener = await __privateMethod(this, _ShapeStream_instances, createAbortListener_fn).call(this, signal);
|
|
1558
2095
|
const requestAbortController = __privateGet(this, _requestAbortController);
|
|
2096
|
+
if (__privateGet(this, _pauseLock).isPaused) {
|
|
2097
|
+
if (abortListener && signal) {
|
|
2098
|
+
signal.removeEventListener(`abort`, abortListener);
|
|
2099
|
+
}
|
|
2100
|
+
__privateSet(this, _requestAbortController, void 0);
|
|
2101
|
+
return;
|
|
2102
|
+
}
|
|
1559
2103
|
try {
|
|
1560
2104
|
await __privateMethod(this, _ShapeStream_instances, fetchShape_fn).call(this, {
|
|
1561
2105
|
fetchUrl,
|
|
@@ -1570,10 +2114,6 @@ requestShape_fn = async function() {
|
|
|
1570
2114
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
1571
2115
|
}
|
|
1572
2116
|
if (e instanceof FetchBackoffAbortError) {
|
|
1573
|
-
const currentState = __privateGet(this, _state);
|
|
1574
|
-
if (requestAbortController.signal.aborted && requestAbortController.signal.reason === PAUSE_STREAM && currentState === `pause-requested`) {
|
|
1575
|
-
__privateSet(this, _state, `paused`);
|
|
1576
|
-
}
|
|
1577
2117
|
return;
|
|
1578
2118
|
}
|
|
1579
2119
|
if (e instanceof StaleCacheError) {
|
|
@@ -1581,11 +2121,11 @@ requestShape_fn = async function() {
|
|
|
1581
2121
|
}
|
|
1582
2122
|
if (!(e instanceof FetchError)) throw e;
|
|
1583
2123
|
if (e.status == 409) {
|
|
1584
|
-
if (__privateGet(this,
|
|
2124
|
+
if (__privateGet(this, _syncState).handle) {
|
|
1585
2125
|
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1586
|
-
expiredShapesCache.markExpired(shapeKey, __privateGet(this,
|
|
2126
|
+
expiredShapesCache.markExpired(shapeKey, __privateGet(this, _syncState).handle);
|
|
1587
2127
|
}
|
|
1588
|
-
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER] || `${__privateGet(this,
|
|
2128
|
+
const newShapeHandle = e.headers[SHAPE_HANDLE_HEADER] || `${__privateGet(this, _syncState).handle}-next`;
|
|
1589
2129
|
__privateMethod(this, _ShapeStream_instances, reset_fn).call(this, newShapeHandle);
|
|
1590
2130
|
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, Array.isArray(e.json) ? e.json : [e.json]);
|
|
1591
2131
|
return __privateMethod(this, _ShapeStream_instances, requestShape_fn).call(this);
|
|
@@ -1691,32 +2231,18 @@ constructUrl_fn = async function(url, resumingFromPause, subsetParams) {
|
|
|
1691
2231
|
setQueryParam(fetchUrl, SUBSET_PARAM_ORDER_BY, encodedOrderBy);
|
|
1692
2232
|
}
|
|
1693
2233
|
}
|
|
1694
|
-
|
|
2234
|
+
__privateGet(this, _syncState).applyUrlParams(fetchUrl, {
|
|
2235
|
+
isSnapshotRequest: subsetParams !== void 0,
|
|
2236
|
+
// Don't long-poll when resuming from pause or refreshing — avoids
|
|
2237
|
+
// a 20s hold during which `isConnected` would be false
|
|
2238
|
+
canLongPoll: !__privateGet(this, _ShapeStream_instances, isRefreshing_get) && !resumingFromPause
|
|
2239
|
+
});
|
|
1695
2240
|
fetchUrl.searchParams.set(LOG_MODE_QUERY_PARAM, __privateGet(this, _mode));
|
|
1696
|
-
const isSnapshotRequest = subsetParams !== void 0;
|
|
1697
|
-
if (__privateGet(this, _isUpToDate) && !isSnapshotRequest) {
|
|
1698
|
-
if (!__privateGet(this, _isRefreshing) && !resumingFromPause) {
|
|
1699
|
-
fetchUrl.searchParams.set(LIVE_QUERY_PARAM, `true`);
|
|
1700
|
-
}
|
|
1701
|
-
fetchUrl.searchParams.set(
|
|
1702
|
-
LIVE_CACHE_BUSTER_QUERY_PARAM,
|
|
1703
|
-
__privateGet(this, _liveCacheBuster)
|
|
1704
|
-
);
|
|
1705
|
-
}
|
|
1706
|
-
if (__privateGet(this, _shapeHandle)) {
|
|
1707
|
-
fetchUrl.searchParams.set(SHAPE_HANDLE_QUERY_PARAM, __privateGet(this, _shapeHandle));
|
|
1708
|
-
}
|
|
1709
2241
|
const shapeKey = canonicalShapeKey(fetchUrl);
|
|
1710
2242
|
const expiredHandle = expiredShapesCache.getExpiredHandle(shapeKey);
|
|
1711
2243
|
if (expiredHandle) {
|
|
1712
2244
|
fetchUrl.searchParams.set(EXPIRED_HANDLE_QUERY_PARAM, expiredHandle);
|
|
1713
2245
|
}
|
|
1714
|
-
if (__privateGet(this, _staleCacheBuster)) {
|
|
1715
|
-
fetchUrl.searchParams.set(
|
|
1716
|
-
CACHE_BUSTER_QUERY_PARAM,
|
|
1717
|
-
__privateGet(this, _staleCacheBuster)
|
|
1718
|
-
);
|
|
1719
|
-
}
|
|
1720
2246
|
fetchUrl.searchParams.sort();
|
|
1721
2247
|
return {
|
|
1722
2248
|
fetchUrl,
|
|
@@ -1739,108 +2265,100 @@ createAbortListener_fn = async function(signal) {
|
|
|
1739
2265
|
}
|
|
1740
2266
|
};
|
|
1741
2267
|
onInitialResponse_fn = async function(response) {
|
|
1742
|
-
var _a, _b, _c
|
|
2268
|
+
var _a, _b, _c;
|
|
1743
2269
|
const { headers, status } = response;
|
|
1744
2270
|
const shapeHandle = headers.get(SHAPE_HANDLE_HEADER);
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
`
|
|
1769
|
-
);
|
|
1770
|
-
__privateSet(this, _staleCacheBuster, `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`);
|
|
1771
|
-
throw new StaleCacheError(
|
|
1772
|
-
`Received stale cached response with expired handle "${shapeHandle}". This indicates a proxy/CDN caching misconfiguration. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key.`
|
|
1773
|
-
);
|
|
1774
|
-
} else {
|
|
1775
|
-
console.warn(
|
|
1776
|
-
`[Electric] Received stale cached response with expired shape handle. This should not happen and indicates a proxy/CDN caching misconfiguration. The response contained handle "${shapeHandle}" which was previously marked as expired. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. Ignoring the stale response and continuing with handle "${__privateGet(this, _shapeHandle)}".`
|
|
2271
|
+
const shapeKey = __privateGet(this, _currentFetchUrl) ? canonicalShapeKey(__privateGet(this, _currentFetchUrl)) : null;
|
|
2272
|
+
const expiredHandle = shapeKey ? expiredShapesCache.getExpiredHandle(shapeKey) : null;
|
|
2273
|
+
const transition = __privateGet(this, _syncState).handleResponseMetadata({
|
|
2274
|
+
status,
|
|
2275
|
+
responseHandle: shapeHandle,
|
|
2276
|
+
responseOffset: headers.get(CHUNK_LAST_OFFSET_HEADER),
|
|
2277
|
+
responseCursor: headers.get(LIVE_CACHE_BUSTER_HEADER),
|
|
2278
|
+
responseSchema: getSchemaFromHeaders(headers),
|
|
2279
|
+
expiredHandle,
|
|
2280
|
+
now: Date.now(),
|
|
2281
|
+
maxStaleCacheRetries: __privateGet(this, _maxStaleCacheRetries),
|
|
2282
|
+
createCacheBuster: () => `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`
|
|
2283
|
+
});
|
|
2284
|
+
__privateSet(this, _syncState, transition.state);
|
|
2285
|
+
if (transition.action === `stale-retry`) {
|
|
2286
|
+
await ((_a = response.body) == null ? void 0 : _a.cancel());
|
|
2287
|
+
if (transition.exceededMaxRetries) {
|
|
2288
|
+
throw new FetchError(
|
|
2289
|
+
502,
|
|
2290
|
+
void 0,
|
|
2291
|
+
void 0,
|
|
2292
|
+
{},
|
|
2293
|
+
(_c = (_b = __privateGet(this, _currentFetchUrl)) == null ? void 0 : _b.toString()) != null ? _c : ``,
|
|
2294
|
+
`CDN continues serving stale cached responses after ${__privateGet(this, _maxStaleCacheRetries)} retry attempts. This indicates a severe proxy/CDN misconfiguration. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. For more information visit the troubleshooting guide: https://electric-sql.com/docs/guides/troubleshooting`
|
|
1777
2295
|
);
|
|
1778
|
-
return;
|
|
1779
2296
|
}
|
|
2297
|
+
console.warn(
|
|
2298
|
+
`[Electric] Received stale cached response with expired shape handle. This should not happen and indicates a proxy/CDN caching misconfiguration. The response contained handle "${shapeHandle}" which was previously marked as expired. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. For more information visit the troubleshooting guide: https://electric-sql.com/docs/guides/troubleshooting Retrying with a random cache buster to bypass the stale cache (attempt ${__privateGet(this, _syncState).staleCacheRetryCount}/${__privateGet(this, _maxStaleCacheRetries)}).`
|
|
2299
|
+
);
|
|
2300
|
+
throw new StaleCacheError(
|
|
2301
|
+
`Received stale cached response with expired handle "${shapeHandle}". This indicates a proxy/CDN caching misconfiguration. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key.`
|
|
2302
|
+
);
|
|
1780
2303
|
}
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
if (liveCacheBuster) {
|
|
1787
|
-
__privateSet(this, _liveCacheBuster, liveCacheBuster);
|
|
1788
|
-
}
|
|
1789
|
-
__privateSet(this, _schema, (_d = __privateGet(this, _schema)) != null ? _d : getSchemaFromHeaders(headers));
|
|
1790
|
-
if (status === 204) {
|
|
1791
|
-
__privateSet(this, _lastSyncedAt, Date.now());
|
|
2304
|
+
if (transition.action === `ignored`) {
|
|
2305
|
+
console.warn(
|
|
2306
|
+
`[Electric] Received stale cached response with expired shape handle. This should not happen and indicates a proxy/CDN caching misconfiguration. The response contained handle "${shapeHandle}" which was previously marked as expired. Check that your proxy includes all query parameters (especially 'handle' and 'offset') in its cache key. Ignoring the stale response and continuing with handle "${__privateGet(this, _syncState).handle}".`
|
|
2307
|
+
);
|
|
2308
|
+
return false;
|
|
1792
2309
|
}
|
|
2310
|
+
return true;
|
|
1793
2311
|
};
|
|
1794
2312
|
onMessages_fn = async function(batch, isSseMessage = false) {
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
if (__privateGet(this, _currentFetchUrl)) {
|
|
1819
|
-
const shapeKey = canonicalShapeKey(__privateGet(this, _currentFetchUrl));
|
|
1820
|
-
upToDateTracker.recordUpToDate(shapeKey, __privateGet(this, _liveCacheBuster));
|
|
1821
|
-
}
|
|
2313
|
+
if (batch.length === 0) return;
|
|
2314
|
+
const lastMessage = batch[batch.length - 1];
|
|
2315
|
+
const hasUpToDateMessage = isUpToDateMessage(lastMessage);
|
|
2316
|
+
const upToDateOffset = hasUpToDateMessage ? getOffset(lastMessage) : void 0;
|
|
2317
|
+
const transition = __privateGet(this, _syncState).handleMessageBatch({
|
|
2318
|
+
hasMessages: true,
|
|
2319
|
+
hasUpToDateMessage,
|
|
2320
|
+
isSse: isSseMessage,
|
|
2321
|
+
upToDateOffset,
|
|
2322
|
+
now: Date.now(),
|
|
2323
|
+
currentCursor: __privateGet(this, _syncState).liveCacheBuster
|
|
2324
|
+
});
|
|
2325
|
+
__privateSet(this, _syncState, transition.state);
|
|
2326
|
+
if (hasUpToDateMessage) {
|
|
2327
|
+
if (transition.suppressBatch) {
|
|
2328
|
+
return;
|
|
2329
|
+
}
|
|
2330
|
+
if (__privateGet(this, _currentFetchUrl)) {
|
|
2331
|
+
const shapeKey = canonicalShapeKey(__privateGet(this, _currentFetchUrl));
|
|
2332
|
+
upToDateTracker.recordUpToDate(
|
|
2333
|
+
shapeKey,
|
|
2334
|
+
__privateGet(this, _syncState).liveCacheBuster
|
|
2335
|
+
);
|
|
1822
2336
|
}
|
|
1823
|
-
const messagesToProcess = batch.filter((message) => {
|
|
1824
|
-
if (isChangeMessage(message)) {
|
|
1825
|
-
return !__privateGet(this, _snapshotTracker).shouldRejectMessage(message);
|
|
1826
|
-
}
|
|
1827
|
-
return true;
|
|
1828
|
-
});
|
|
1829
|
-
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, messagesToProcess);
|
|
1830
2337
|
}
|
|
2338
|
+
const messagesToProcess = batch.filter((message) => {
|
|
2339
|
+
if (isChangeMessage(message)) {
|
|
2340
|
+
return !__privateGet(this, _snapshotTracker).shouldRejectMessage(message);
|
|
2341
|
+
}
|
|
2342
|
+
return true;
|
|
2343
|
+
});
|
|
2344
|
+
await __privateMethod(this, _ShapeStream_instances, publish_fn).call(this, messagesToProcess);
|
|
1831
2345
|
};
|
|
1832
2346
|
fetchShape_fn = async function(opts) {
|
|
1833
2347
|
var _a;
|
|
1834
2348
|
__privateSet(this, _currentFetchUrl, opts.fetchUrl);
|
|
1835
|
-
if (!__privateGet(this,
|
|
2349
|
+
if (!__privateGet(this, _syncState).isUpToDate && __privateGet(this, _syncState).canEnterReplayMode()) {
|
|
1836
2350
|
const shapeKey = canonicalShapeKey(opts.fetchUrl);
|
|
1837
2351
|
const lastSeenCursor = upToDateTracker.shouldEnterReplayMode(shapeKey);
|
|
1838
2352
|
if (lastSeenCursor) {
|
|
1839
|
-
__privateSet(this,
|
|
2353
|
+
__privateSet(this, _syncState, __privateGet(this, _syncState).enterReplayMode(lastSeenCursor));
|
|
1840
2354
|
}
|
|
1841
2355
|
}
|
|
1842
2356
|
const useSse = (_a = this.options.liveSse) != null ? _a : this.options.experimentalLiveSse;
|
|
1843
|
-
if (__privateGet(this,
|
|
2357
|
+
if (__privateGet(this, _syncState).shouldUseSse({
|
|
2358
|
+
liveSseEnabled: !!useSse,
|
|
2359
|
+
isRefreshing: __privateGet(this, _ShapeStream_instances, isRefreshing_get),
|
|
2360
|
+
resumingFromPause: !!opts.resumingFromPause
|
|
2361
|
+
})) {
|
|
1844
2362
|
opts.fetchUrl.searchParams.set(EXPERIMENTAL_LIVE_SSE_QUERY_PARAM, `true`);
|
|
1845
2363
|
opts.fetchUrl.searchParams.set(LIVE_SSE_QUERY_PARAM, `true`);
|
|
1846
2364
|
return __privateMethod(this, _ShapeStream_instances, requestShapeSSE_fn).call(this, opts);
|
|
@@ -1854,8 +2372,9 @@ requestShapeLongPoll_fn = async function(opts) {
|
|
|
1854
2372
|
headers
|
|
1855
2373
|
});
|
|
1856
2374
|
__privateSet(this, _connected, true);
|
|
1857
|
-
await __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
|
|
1858
|
-
|
|
2375
|
+
const shouldProcessBody = await __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
|
|
2376
|
+
if (!shouldProcessBody) return;
|
|
2377
|
+
const schema = __privateGet(this, _syncState).schema;
|
|
1859
2378
|
const res = await response.text();
|
|
1860
2379
|
const messages = res || `[]`;
|
|
1861
2380
|
const batch = __privateGet(this, _messageParser).parse(messages, schema);
|
|
@@ -1868,6 +2387,7 @@ requestShapeSSE_fn = async function(opts) {
|
|
|
1868
2387
|
const sseHeaders = __spreadProps(__spreadValues({}, headers), {
|
|
1869
2388
|
Accept: `text/event-stream`
|
|
1870
2389
|
});
|
|
2390
|
+
let ignoredStaleResponse = false;
|
|
1871
2391
|
try {
|
|
1872
2392
|
let buffer = [];
|
|
1873
2393
|
await (0, import_fetch_event_source.fetchEventSource)(fetchUrl.toString(), {
|
|
@@ -1875,11 +2395,15 @@ requestShapeSSE_fn = async function(opts) {
|
|
|
1875
2395
|
fetch: fetch2,
|
|
1876
2396
|
onopen: async (response) => {
|
|
1877
2397
|
__privateSet(this, _connected, true);
|
|
1878
|
-
await __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
|
|
2398
|
+
const shouldProcessBody = await __privateMethod(this, _ShapeStream_instances, onInitialResponse_fn).call(this, response);
|
|
2399
|
+
if (!shouldProcessBody) {
|
|
2400
|
+
ignoredStaleResponse = true;
|
|
2401
|
+
throw new Error(`stale response ignored`);
|
|
2402
|
+
}
|
|
1879
2403
|
},
|
|
1880
2404
|
onmessage: (event) => {
|
|
1881
2405
|
if (event.data) {
|
|
1882
|
-
const schema = __privateGet(this,
|
|
2406
|
+
const schema = __privateGet(this, _syncState).schema;
|
|
1883
2407
|
const message = __privateGet(this, _messageParser).parse(
|
|
1884
2408
|
event.data,
|
|
1885
2409
|
schema
|
|
@@ -1897,6 +2421,9 @@ requestShapeSSE_fn = async function(opts) {
|
|
|
1897
2421
|
signal: requestAbortController.signal
|
|
1898
2422
|
});
|
|
1899
2423
|
} catch (error) {
|
|
2424
|
+
if (ignoredStaleResponse) {
|
|
2425
|
+
return;
|
|
2426
|
+
}
|
|
1900
2427
|
if (requestAbortController.signal.aborted) {
|
|
1901
2428
|
throw new FetchBackoffAbortError();
|
|
1902
2429
|
}
|
|
@@ -1904,46 +2431,33 @@ requestShapeSSE_fn = async function(opts) {
|
|
|
1904
2431
|
} finally {
|
|
1905
2432
|
const connectionDuration = Date.now() - __privateGet(this, _lastSseConnectionStartTime);
|
|
1906
2433
|
const wasAborted = requestAbortController.signal.aborted;
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
}
|
|
1926
|
-
};
|
|
1927
|
-
pause_fn = function() {
|
|
1928
|
-
var _a;
|
|
1929
|
-
if (__privateGet(this, _started) && __privateGet(this, _state) === `active`) {
|
|
1930
|
-
__privateSet(this, _state, `pause-requested`);
|
|
1931
|
-
(_a = __privateGet(this, _requestAbortController)) == null ? void 0 : _a.abort(PAUSE_STREAM);
|
|
1932
|
-
}
|
|
1933
|
-
};
|
|
1934
|
-
resume_fn = function() {
|
|
1935
|
-
var _a;
|
|
1936
|
-
if (__privateGet(this, _started) && (__privateGet(this, _state) === `paused` || __privateGet(this, _state) === `pause-requested`)) {
|
|
1937
|
-
if ((_a = this.options.signal) == null ? void 0 : _a.aborted) {
|
|
1938
|
-
return;
|
|
1939
|
-
}
|
|
1940
|
-
if (__privateGet(this, _state) === `pause-requested`) {
|
|
1941
|
-
__privateSet(this, _state, `active`);
|
|
2434
|
+
const transition = __privateGet(this, _syncState).handleSseConnectionClosed({
|
|
2435
|
+
connectionDuration,
|
|
2436
|
+
wasAborted,
|
|
2437
|
+
minConnectionDuration: __privateGet(this, _minSseConnectionDuration),
|
|
2438
|
+
maxShortConnections: __privateGet(this, _maxShortSseConnections)
|
|
2439
|
+
});
|
|
2440
|
+
__privateSet(this, _syncState, transition.state);
|
|
2441
|
+
if (transition.fellBackToLongPolling) {
|
|
2442
|
+
console.warn(
|
|
2443
|
+
`[Electric] SSE connections are closing immediately (possibly due to proxy buffering or misconfiguration). Falling back to long polling. Your proxy must support streaming SSE responses (not buffer the complete response). Configuration: Nginx add 'X-Accel-Buffering: no', Caddy add 'flush_interval -1' to reverse_proxy. Note: Do NOT disable caching entirely - Electric uses cache headers to enable request collapsing for efficiency.`
|
|
2444
|
+
);
|
|
2445
|
+
} else if (transition.wasShortConnection) {
|
|
2446
|
+
const maxDelay = Math.min(
|
|
2447
|
+
__privateGet(this, _sseBackoffMaxDelay),
|
|
2448
|
+
__privateGet(this, _sseBackoffBaseDelay) * Math.pow(2, __privateGet(this, _syncState).consecutiveShortSseConnections)
|
|
2449
|
+
);
|
|
2450
|
+
const delayMs = Math.floor(Math.random() * maxDelay);
|
|
2451
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
1942
2452
|
}
|
|
1943
|
-
__privateMethod(this, _ShapeStream_instances, start_fn).call(this);
|
|
1944
2453
|
}
|
|
1945
2454
|
};
|
|
1946
2455
|
nextTick_fn = async function() {
|
|
2456
|
+
if (__privateGet(this, _pauseLock).isPaused) {
|
|
2457
|
+
throw new Error(
|
|
2458
|
+
`Cannot wait for next tick while PauseLock is held \u2014 this would deadlock because the request loop is paused`
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
1947
2461
|
if (__privateGet(this, _tickPromise)) {
|
|
1948
2462
|
return __privateGet(this, _tickPromise);
|
|
1949
2463
|
}
|
|
@@ -1958,22 +2472,6 @@ nextTick_fn = async function() {
|
|
|
1958
2472
|
});
|
|
1959
2473
|
return __privateGet(this, _tickPromise);
|
|
1960
2474
|
};
|
|
1961
|
-
waitForStreamEnd_fn = async function() {
|
|
1962
|
-
if (!__privateGet(this, _isMidStream)) {
|
|
1963
|
-
return;
|
|
1964
|
-
}
|
|
1965
|
-
if (__privateGet(this, _midStreamPromise)) {
|
|
1966
|
-
return __privateGet(this, _midStreamPromise);
|
|
1967
|
-
}
|
|
1968
|
-
__privateSet(this, _midStreamPromise, new Promise((resolve) => {
|
|
1969
|
-
__privateSet(this, _midStreamPromiseResolver, resolve);
|
|
1970
|
-
}));
|
|
1971
|
-
__privateGet(this, _midStreamPromise).finally(() => {
|
|
1972
|
-
__privateSet(this, _midStreamPromise, void 0);
|
|
1973
|
-
__privateSet(this, _midStreamPromiseResolver, void 0);
|
|
1974
|
-
});
|
|
1975
|
-
return __privateGet(this, _midStreamPromise);
|
|
1976
|
-
};
|
|
1977
2475
|
publish_fn = async function(messages) {
|
|
1978
2476
|
__privateSet(this, _messageChain, __privateGet(this, _messageChain).then(
|
|
1979
2477
|
() => Promise.all(
|
|
@@ -2002,9 +2500,9 @@ subscribeToVisibilityChanges_fn = function() {
|
|
|
2002
2500
|
if (__privateMethod(this, _ShapeStream_instances, hasBrowserVisibilityAPI_fn).call(this)) {
|
|
2003
2501
|
const visibilityHandler = () => {
|
|
2004
2502
|
if (document.hidden) {
|
|
2005
|
-
|
|
2503
|
+
__privateGet(this, _pauseLock).acquire(`visibility`);
|
|
2006
2504
|
} else {
|
|
2007
|
-
|
|
2505
|
+
__privateGet(this, _pauseLock).release(`visibility`);
|
|
2008
2506
|
}
|
|
2009
2507
|
};
|
|
2010
2508
|
document.addEventListener(`visibilitychange`, visibilityHandler);
|
|
@@ -2035,11 +2533,11 @@ subscribeToWakeDetection_fn = function() {
|
|
|
2035
2533
|
const elapsed = now - lastTickTime;
|
|
2036
2534
|
lastTickTime = now;
|
|
2037
2535
|
if (elapsed > INTERVAL_MS + WAKE_THRESHOLD_MS) {
|
|
2038
|
-
if (__privateGet(this,
|
|
2039
|
-
|
|
2536
|
+
if (!__privateGet(this, _pauseLock).isPaused && __privateGet(this, _requestAbortController)) {
|
|
2537
|
+
__privateWrapper(this, _refreshCount)._++;
|
|
2040
2538
|
__privateGet(this, _requestAbortController).abort(SYSTEM_WAKE);
|
|
2041
2539
|
queueMicrotask(() => {
|
|
2042
|
-
|
|
2540
|
+
__privateWrapper(this, _refreshCount)._--;
|
|
2043
2541
|
});
|
|
2044
2542
|
}
|
|
2045
2543
|
}
|
|
@@ -2056,18 +2554,9 @@ subscribeToWakeDetection_fn = function() {
|
|
|
2056
2554
|
* shape handle
|
|
2057
2555
|
*/
|
|
2058
2556
|
reset_fn = function(handle) {
|
|
2059
|
-
__privateSet(this,
|
|
2060
|
-
__privateSet(this, _liveCacheBuster, ``);
|
|
2061
|
-
__privateSet(this, _shapeHandle, handle);
|
|
2062
|
-
__privateSet(this, _isUpToDate, false);
|
|
2063
|
-
__privateSet(this, _isMidStream, true);
|
|
2557
|
+
__privateSet(this, _syncState, __privateGet(this, _syncState).markMustRefetch(handle));
|
|
2064
2558
|
__privateSet(this, _connected, false);
|
|
2065
|
-
|
|
2066
|
-
__privateSet(this, _activeSnapshotRequests, 0);
|
|
2067
|
-
__privateSet(this, _consecutiveShortSseConnections, 0);
|
|
2068
|
-
__privateSet(this, _sseFallbackToLongPolling, false);
|
|
2069
|
-
__privateSet(this, _staleCacheBuster, void 0);
|
|
2070
|
-
__privateSet(this, _staleCacheRetryCount, 0);
|
|
2559
|
+
__privateGet(this, _pauseLock).releaseAllMatching(`snapshot`);
|
|
2071
2560
|
};
|
|
2072
2561
|
buildSubsetBody_fn = function(opts) {
|
|
2073
2562
|
var _a, _b, _c, _d;
|