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