leafrelay 0.2.2 → 0.2.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/{chunk-Q3R5N4YE.js → chunk-3RL67IG5.js} +120 -36
- package/dist/chunk-3RL67IG5.js.map +1 -0
- package/dist/{chunk-2EXJYAER.js → chunk-XXR6LFT7.js} +8 -8
- package/dist/chunk-XXR6LFT7.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/daemon.js +10 -9
- package/dist/daemon.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-2EXJYAER.js.map +0 -1
- package/dist/chunk-Q3R5N4YE.js.map +0 -1
|
@@ -152,6 +152,7 @@ var OverleafRealtimeSocket = class {
|
|
|
152
152
|
cookie;
|
|
153
153
|
socket;
|
|
154
154
|
connected = false;
|
|
155
|
+
projectJoined = false;
|
|
155
156
|
explicitlyDisconnected = false;
|
|
156
157
|
reconnectEnabled;
|
|
157
158
|
reconnectAttempts = 0;
|
|
@@ -160,7 +161,6 @@ var OverleafRealtimeSocket = class {
|
|
|
160
161
|
closeTimeoutMs = 6e4;
|
|
161
162
|
acknowledgementId = 0;
|
|
162
163
|
acknowledgements = /* @__PURE__ */ new Map();
|
|
163
|
-
sendBuffer = [];
|
|
164
164
|
io = {
|
|
165
165
|
reconnect: (enabled) => {
|
|
166
166
|
this.reconnectEnabled = enabled;
|
|
@@ -192,15 +192,24 @@ var OverleafRealtimeSocket = class {
|
|
|
192
192
|
if (id && callback) {
|
|
193
193
|
this.acknowledgements.set(id, callback);
|
|
194
194
|
}
|
|
195
|
-
|
|
195
|
+
const packet = encodePacket({
|
|
196
196
|
type: "event",
|
|
197
197
|
id,
|
|
198
198
|
ack: callback ? "data" : void 0,
|
|
199
199
|
name: event,
|
|
200
200
|
args
|
|
201
|
-
})
|
|
201
|
+
});
|
|
202
|
+
if (!this.sendApplicationEvent(packet) && callback) {
|
|
203
|
+
if (id) {
|
|
204
|
+
this.acknowledgements.delete(id);
|
|
205
|
+
}
|
|
206
|
+
queueMicrotask(() => callback(new Error("The Overleaf project session is not connected.")));
|
|
207
|
+
}
|
|
202
208
|
return this;
|
|
203
209
|
}
|
|
210
|
+
markProjectJoined() {
|
|
211
|
+
this.projectJoined = true;
|
|
212
|
+
}
|
|
204
213
|
disconnect() {
|
|
205
214
|
this.explicitlyDisconnected = true;
|
|
206
215
|
this.reconnectEnabled = false;
|
|
@@ -215,6 +224,7 @@ var OverleafRealtimeSocket = class {
|
|
|
215
224
|
}
|
|
216
225
|
async connect() {
|
|
217
226
|
try {
|
|
227
|
+
this.projectJoined = false;
|
|
218
228
|
const handshakeUrl = new URL("/socket.io/1/", this.endpoint.origin);
|
|
219
229
|
for (const [name, value] of this.endpoint.searchParams) {
|
|
220
230
|
handshakeUrl.searchParams.set(name, value);
|
|
@@ -243,7 +253,7 @@ var OverleafRealtimeSocket = class {
|
|
|
243
253
|
this.socket.addEventListener("open", () => this.events.emit("connecting", "websocket"));
|
|
244
254
|
this.socket.addEventListener("message", (event) => void this.receive(event.data));
|
|
245
255
|
this.socket.addEventListener("error", (error2) => this.events.emit("error", error2));
|
|
246
|
-
this.socket.addEventListener("close", () => this.handleClose());
|
|
256
|
+
this.socket.addEventListener("close", (event) => this.handleClose(event));
|
|
247
257
|
} catch (error2) {
|
|
248
258
|
this.events.emit("connect_failed", error2);
|
|
249
259
|
this.events.emit("error", error2);
|
|
@@ -263,7 +273,6 @@ var OverleafRealtimeSocket = class {
|
|
|
263
273
|
const wasReconnecting = this.reconnectAttempts > 0;
|
|
264
274
|
this.connected = true;
|
|
265
275
|
this.events.emit("connect");
|
|
266
|
-
this.flush();
|
|
267
276
|
if (wasReconnecting) {
|
|
268
277
|
this.events.emit("reconnect", "websocket", this.reconnectAttempts);
|
|
269
278
|
this.reconnectAttempts = 0;
|
|
@@ -309,16 +318,16 @@ var OverleafRealtimeSocket = class {
|
|
|
309
318
|
}
|
|
310
319
|
}
|
|
311
320
|
send(packet) {
|
|
312
|
-
if (
|
|
313
|
-
this.
|
|
314
|
-
return;
|
|
321
|
+
if (this.connected && this.socket?.readyState === WebSocket.OPEN) {
|
|
322
|
+
this.socket.send(packet);
|
|
315
323
|
}
|
|
316
|
-
this.socket.send(packet);
|
|
317
324
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
325
|
+
sendApplicationEvent(packet) {
|
|
326
|
+
if (!this.connected || !this.projectJoined || this.socket?.readyState !== WebSocket.OPEN) {
|
|
327
|
+
return false;
|
|
321
328
|
}
|
|
329
|
+
this.socket.send(packet);
|
|
330
|
+
return true;
|
|
322
331
|
}
|
|
323
332
|
resetInactivityTimer() {
|
|
324
333
|
if (this.inactivityTimer) {
|
|
@@ -326,20 +335,25 @@ var OverleafRealtimeSocket = class {
|
|
|
326
335
|
}
|
|
327
336
|
this.inactivityTimer = setTimeout(() => this.closeSocket("timeout"), this.closeTimeoutMs);
|
|
328
337
|
}
|
|
329
|
-
handleClose() {
|
|
338
|
+
handleClose(event) {
|
|
330
339
|
const wasConnected = this.connected;
|
|
331
340
|
this.connected = false;
|
|
341
|
+
this.projectJoined = false;
|
|
332
342
|
if (this.inactivityTimer) {
|
|
333
343
|
clearTimeout(this.inactivityTimer);
|
|
334
344
|
}
|
|
335
345
|
if (wasConnected) {
|
|
336
|
-
|
|
346
|
+
const detail = event.reason ? `: ${event.reason}` : "";
|
|
347
|
+
const reason = `transport close (code ${event.code})${detail}`;
|
|
348
|
+
this.failAcknowledgements(reason);
|
|
349
|
+
this.events.emit("disconnect", reason);
|
|
337
350
|
}
|
|
338
351
|
this.scheduleReconnect();
|
|
339
352
|
}
|
|
340
353
|
closeSocket(reason) {
|
|
341
354
|
const wasConnected = this.connected;
|
|
342
355
|
this.connected = false;
|
|
356
|
+
this.projectJoined = false;
|
|
343
357
|
if (this.inactivityTimer) {
|
|
344
358
|
clearTimeout(this.inactivityTimer);
|
|
345
359
|
}
|
|
@@ -347,12 +361,20 @@ var OverleafRealtimeSocket = class {
|
|
|
347
361
|
this.socket = void 0;
|
|
348
362
|
socket?.close();
|
|
349
363
|
if (wasConnected) {
|
|
364
|
+
this.failAcknowledgements(reason);
|
|
350
365
|
this.events.emit("disconnect", reason);
|
|
351
366
|
}
|
|
352
367
|
if (!this.explicitlyDisconnected) {
|
|
353
368
|
this.scheduleReconnect();
|
|
354
369
|
}
|
|
355
370
|
}
|
|
371
|
+
failAcknowledgements(reason) {
|
|
372
|
+
const error2 = new Error(`Overleaf realtime connection closed: ${reason}`);
|
|
373
|
+
for (const callback of this.acknowledgements.values()) {
|
|
374
|
+
callback(error2);
|
|
375
|
+
}
|
|
376
|
+
this.acknowledgements.clear();
|
|
377
|
+
}
|
|
356
378
|
scheduleReconnect() {
|
|
357
379
|
if (!this.reconnectEnabled || this.explicitlyDisconnected || this.reconnectTimer) {
|
|
358
380
|
return;
|
|
@@ -1188,6 +1210,8 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1188
1210
|
static alternativeSocketFactory;
|
|
1189
1211
|
scheme;
|
|
1190
1212
|
record;
|
|
1213
|
+
joinedProject;
|
|
1214
|
+
pendingProjectJoin;
|
|
1191
1215
|
_handlers = [];
|
|
1192
1216
|
connectedPublicId;
|
|
1193
1217
|
socket;
|
|
@@ -1224,6 +1248,8 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1224
1248
|
break;
|
|
1225
1249
|
case "realtime":
|
|
1226
1250
|
this.record = void 0;
|
|
1251
|
+
this.joinedProject = void 0;
|
|
1252
|
+
this.resetProjectJoin(new Error("The realtime connection was replaced."));
|
|
1227
1253
|
const query = `?projectId=${this.projectId}&t=${Date.now()}`;
|
|
1228
1254
|
this.socket = this.api._initSocketV0(this.identity, query);
|
|
1229
1255
|
break;
|
|
@@ -1266,6 +1292,11 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1266
1292
|
});
|
|
1267
1293
|
this.socket.on("disconnect", (reason) => {
|
|
1268
1294
|
log("SocketIOAPI: disconnect event", { scheme: this.scheme, projectId: this.projectId, reason });
|
|
1295
|
+
if (this.scheme === "realtime") {
|
|
1296
|
+
this.joinedProject = void 0;
|
|
1297
|
+
this.connectedPublicId = void 0;
|
|
1298
|
+
this.resetProjectJoin(new Error(`Overleaf realtime connection closed: ${String(reason ?? "unknown")}`));
|
|
1299
|
+
}
|
|
1269
1300
|
});
|
|
1270
1301
|
this.socket.on("reconnecting", (delay, attempt) => {
|
|
1271
1302
|
log("SocketIOAPI: reconnecting", { scheme: this.scheme, projectId: this.projectId, delay, attempt });
|
|
@@ -1290,22 +1321,50 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1290
1321
|
error(`Overleaf connection error: ${message}`);
|
|
1291
1322
|
});
|
|
1292
1323
|
if (this.scheme === "realtime") {
|
|
1293
|
-
this.
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
}
|
|
1324
|
+
this.pendingProjectJoin ??= this.createProjectJoin();
|
|
1325
|
+
this.socket.on("joinProjectResponse", (res) => {
|
|
1326
|
+
const publicId = res.publicId;
|
|
1327
|
+
const project = res.project;
|
|
1328
|
+
this.socket.markProjectJoined?.();
|
|
1329
|
+
log("SocketIOAPI: project joined", { scheme: this.scheme, projectId: this.projectId, publicId });
|
|
1330
|
+
this.connectedPublicId = publicId;
|
|
1331
|
+
this.joinedProject = project;
|
|
1332
|
+
this.pendingProjectJoin?.resolve(project);
|
|
1333
|
+
for (const handler of this._handlers) {
|
|
1334
|
+
handler.onProjectJoined?.(project);
|
|
1335
|
+
handler.onConnectionAccepted?.(publicId);
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
this.socket.on("connectionRejected", (err) => {
|
|
1339
|
+
this.pendingProjectJoin?.reject(err?.message || err);
|
|
1306
1340
|
});
|
|
1307
1341
|
}
|
|
1308
1342
|
}
|
|
1343
|
+
createProjectJoin() {
|
|
1344
|
+
let resolve6;
|
|
1345
|
+
let reject;
|
|
1346
|
+
const promise = new Promise((resolveProject, rejectProject) => {
|
|
1347
|
+
resolve6 = resolveProject;
|
|
1348
|
+
reject = rejectProject;
|
|
1349
|
+
});
|
|
1350
|
+
void promise.catch(() => void 0);
|
|
1351
|
+
return { promise, resolve: resolve6, reject };
|
|
1352
|
+
}
|
|
1353
|
+
resetProjectJoin(reason) {
|
|
1354
|
+
if (reason !== void 0) {
|
|
1355
|
+
this.pendingProjectJoin?.reject(reason);
|
|
1356
|
+
}
|
|
1357
|
+
this.pendingProjectJoin = this.createProjectJoin();
|
|
1358
|
+
}
|
|
1359
|
+
waitForRealtimeProject() {
|
|
1360
|
+
return this.joinedProject ? Promise.resolve(this.joinedProject) : this.pendingProjectJoin.promise;
|
|
1361
|
+
}
|
|
1362
|
+
async emitProjectEvent(event, ...args) {
|
|
1363
|
+
if (this.scheme === "realtime") {
|
|
1364
|
+
await this.waitForRealtimeProject();
|
|
1365
|
+
}
|
|
1366
|
+
return this.emit(event, ...args);
|
|
1367
|
+
}
|
|
1309
1368
|
disconnect() {
|
|
1310
1369
|
this.socket.disconnect();
|
|
1311
1370
|
}
|
|
@@ -1364,10 +1423,12 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1364
1423
|
});
|
|
1365
1424
|
break;
|
|
1366
1425
|
case handlers.onDisconnected:
|
|
1367
|
-
this.socket.on("disconnect", () => {
|
|
1368
|
-
handler();
|
|
1426
|
+
this.socket.on("disconnect", (reason) => {
|
|
1427
|
+
handler(reason);
|
|
1369
1428
|
});
|
|
1370
1429
|
break;
|
|
1430
|
+
case handlers.onProjectJoined:
|
|
1431
|
+
break;
|
|
1371
1432
|
case handlers.onConnectionAccepted:
|
|
1372
1433
|
this.socket.on("connectionAccepted", (_, publicId) => {
|
|
1373
1434
|
handler(publicId);
|
|
@@ -1437,7 +1498,7 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1437
1498
|
timeoutPromise
|
|
1438
1499
|
]);
|
|
1439
1500
|
case "realtime":
|
|
1440
|
-
return Promise.race([this.
|
|
1501
|
+
return Promise.race([this.waitForRealtimeProject(), timeoutPromise]);
|
|
1441
1502
|
}
|
|
1442
1503
|
}
|
|
1443
1504
|
/**
|
|
@@ -1446,7 +1507,7 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1446
1507
|
* @returns {Promise}
|
|
1447
1508
|
*/
|
|
1448
1509
|
async joinDoc(docId) {
|
|
1449
|
-
return this.
|
|
1510
|
+
return this.emitProjectEvent("joinDoc", docId, { encodeRanges: true }).then((returns) => {
|
|
1450
1511
|
const [docLinesAscii, version, updates, ranges] = returns;
|
|
1451
1512
|
const docLines = docLinesAscii.map((line) => decodePackedUtf8(line));
|
|
1452
1513
|
return { docLines, version, updates, ranges };
|
|
@@ -1458,7 +1519,7 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1458
1519
|
* @returns {Promise}
|
|
1459
1520
|
*/
|
|
1460
1521
|
async leaveDoc(docId) {
|
|
1461
|
-
return this.
|
|
1522
|
+
return this.emitProjectEvent("leaveDoc", docId).then(() => {
|
|
1462
1523
|
return;
|
|
1463
1524
|
});
|
|
1464
1525
|
}
|
|
@@ -1470,7 +1531,7 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1470
1531
|
*/
|
|
1471
1532
|
async applyOtUpdate(docId, update) {
|
|
1472
1533
|
try {
|
|
1473
|
-
await this.
|
|
1534
|
+
await this.emitProjectEvent("applyOtUpdate", docId, update);
|
|
1474
1535
|
} catch (error2) {
|
|
1475
1536
|
let detail;
|
|
1476
1537
|
if (error2 instanceof Error) {
|
|
@@ -1492,7 +1553,7 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1492
1553
|
* @returns {Promise}
|
|
1493
1554
|
*/
|
|
1494
1555
|
async getConnectedUsers() {
|
|
1495
|
-
return this.
|
|
1556
|
+
return this.emitProjectEvent("clientTracking.getConnectedUsers").then((returns) => {
|
|
1496
1557
|
const [connectedUsers] = returns;
|
|
1497
1558
|
return connectedUsers;
|
|
1498
1559
|
});
|
|
@@ -1503,7 +1564,7 @@ var SocketIOAPI = class _SocketIOAPI {
|
|
|
1503
1564
|
* @returns {Promise}
|
|
1504
1565
|
*/
|
|
1505
1566
|
async updatePosition(doc_id, row, column) {
|
|
1506
|
-
return this.
|
|
1567
|
+
return this.emitProjectEvent("clientTracking.updatePosition", { row, column, doc_id }).then(() => {
|
|
1507
1568
|
return;
|
|
1508
1569
|
});
|
|
1509
1570
|
}
|
|
@@ -1768,6 +1829,7 @@ var RemoteProject = class {
|
|
|
1768
1829
|
entries = /* @__PURE__ */ new Map();
|
|
1769
1830
|
idToPath = /* @__PURE__ */ new Map();
|
|
1770
1831
|
changeHandler;
|
|
1832
|
+
reconnectHandler;
|
|
1771
1833
|
externalTransport;
|
|
1772
1834
|
async connect() {
|
|
1773
1835
|
if (this.externalTransport) {
|
|
@@ -1793,6 +1855,9 @@ var RemoteProject = class {
|
|
|
1793
1855
|
onChange(handler) {
|
|
1794
1856
|
this.changeHandler = handler;
|
|
1795
1857
|
}
|
|
1858
|
+
onReconnect(handler) {
|
|
1859
|
+
this.reconnectHandler = handler;
|
|
1860
|
+
}
|
|
1796
1861
|
listEntries() {
|
|
1797
1862
|
return [...this.entries.values()];
|
|
1798
1863
|
}
|
|
@@ -2011,6 +2076,11 @@ var RemoteProject = class {
|
|
|
2011
2076
|
}
|
|
2012
2077
|
registerEvents() {
|
|
2013
2078
|
this.socket.updateEventHandlers({
|
|
2079
|
+
onProjectJoined: (project) => {
|
|
2080
|
+
this.project = project;
|
|
2081
|
+
this.reindex();
|
|
2082
|
+
this.reconnectHandler?.();
|
|
2083
|
+
},
|
|
2014
2084
|
onFileCreated: (parentId, type, entity) => {
|
|
2015
2085
|
const parent = this.folderById(parentId);
|
|
2016
2086
|
if (!parent || type === "outputs") {
|
|
@@ -2211,6 +2281,11 @@ var SyncEngine = class {
|
|
|
2211
2281
|
}
|
|
2212
2282
|
this.schedule(change.path, "remote");
|
|
2213
2283
|
});
|
|
2284
|
+
this.options.remote.onReconnect?.(() => {
|
|
2285
|
+
this.queue = this.queue.then(() => this.reconcileAfterReconnect()).catch((error2) => {
|
|
2286
|
+
this.options.log(`Failed to reconcile after reconnecting: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
2287
|
+
});
|
|
2288
|
+
});
|
|
2214
2289
|
this.options.log(`Synchronization active at Overleaf version ${this.state.remoteVersion}.`);
|
|
2215
2290
|
}
|
|
2216
2291
|
async stop() {
|
|
@@ -2324,6 +2399,15 @@ var SyncEngine = class {
|
|
|
2324
2399
|
this.options.log(`Synchronization remains active; ${this.conflicts.size} conflicting path(s) are paused.`);
|
|
2325
2400
|
}
|
|
2326
2401
|
}
|
|
2402
|
+
async reconcileAfterReconnect() {
|
|
2403
|
+
const currentVersion = await this.options.remote.getCurrentVersion();
|
|
2404
|
+
await this.startupReconcile(
|
|
2405
|
+
currentVersion,
|
|
2406
|
+
await this.options.local.listFiles(),
|
|
2407
|
+
this.remoteFilePaths()
|
|
2408
|
+
);
|
|
2409
|
+
this.options.log(`Realtime connection restored at Overleaf version ${currentVersion}.`);
|
|
2410
|
+
}
|
|
2327
2411
|
schedule(path, source) {
|
|
2328
2412
|
const normalized = normalizePath2(path);
|
|
2329
2413
|
if (this.shouldIgnore(normalized) || this.conflicts.has(normalized)) {
|
|
@@ -2534,4 +2618,4 @@ export {
|
|
|
2534
2618
|
RemoteProject,
|
|
2535
2619
|
startServe
|
|
2536
2620
|
};
|
|
2537
|
-
//# sourceMappingURL=chunk-
|
|
2621
|
+
//# sourceMappingURL=chunk-3RL67IG5.js.map
|