leafrelay 0.2.5 → 0.2.6

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.
@@ -160,6 +160,7 @@ var OverleafRealtimeSocket = class {
160
160
  reconnectAttempts = 0;
161
161
  reconnectTimer;
162
162
  inactivityTimer;
163
+ connectionAbort;
163
164
  closeTimeoutMs = 6e4;
164
165
  acknowledgementId = 0;
165
166
  acknowledgements = /* @__PURE__ */ new Map();
@@ -225,6 +226,12 @@ var OverleafRealtimeSocket = class {
225
226
  return this;
226
227
  }
227
228
  async connect() {
229
+ if (this.explicitlyDisconnected) {
230
+ return;
231
+ }
232
+ const controller = new AbortController();
233
+ this.connectionAbort?.abort();
234
+ this.connectionAbort = controller;
228
235
  try {
229
236
  this.projectJoined = false;
230
237
  const handshakeUrl = new URL("/socket.io/1/", this.endpoint.origin);
@@ -234,7 +241,8 @@ var OverleafRealtimeSocket = class {
234
241
  handshakeUrl.searchParams.set("t", String(Date.now()));
235
242
  const response = await (this.options.fetch ?? fetch)(handshakeUrl, {
236
243
  headers: { origin: this.origin, cookie: this.cookie },
237
- redirect: "manual"
244
+ redirect: "manual",
245
+ signal: AbortSignal.any([controller.signal, AbortSignal.timeout(15e3)])
238
246
  });
239
247
  if (!response.ok) {
240
248
  throw new Error(`Socket.IO handshake failed: HTTP ${response.status} ${await response.text()}`);
@@ -249,22 +257,50 @@ var OverleafRealtimeSocket = class {
249
257
  const websocketUrl = new URL(`/socket.io/1/websocket/${sessionId}`, this.endpoint.origin);
250
258
  websocketUrl.protocol = websocketUrl.protocol === "https:" ? "wss:" : "ws:";
251
259
  websocketUrl.search = this.endpoint.search;
252
- this.socket = new WebSocket(websocketUrl, {
260
+ if (controller.signal.aborted) {
261
+ return;
262
+ }
263
+ const socket = new WebSocket(websocketUrl, {
253
264
  headers: { origin: this.origin, cookie: this.cookie }
254
265
  });
255
- this.socket.addEventListener("open", () => this.events.emit("connecting", "websocket"));
256
- this.socket.addEventListener("message", (event) => void this.receive(event.data));
257
- this.socket.addEventListener("error", (error2) => this.events.emit("error", error2));
258
- this.socket.addEventListener("close", (event) => this.handleClose(event));
266
+ this.socket = socket;
267
+ socket.addEventListener("open", () => {
268
+ if (this.socket !== socket) {
269
+ return;
270
+ }
271
+ this.resetInactivityTimer();
272
+ this.events.emit("connecting", "websocket");
273
+ });
274
+ socket.addEventListener("message", (event) => {
275
+ if (this.socket === socket) {
276
+ void this.receive(event.data, socket);
277
+ }
278
+ });
279
+ socket.addEventListener("error", (error2) => {
280
+ if (this.socket === socket) {
281
+ this.events.emit("error", error2);
282
+ }
283
+ });
284
+ socket.addEventListener("close", (event) => {
285
+ if (this.socket === socket) {
286
+ this.handleClose(event);
287
+ }
288
+ });
259
289
  } catch (error2) {
290
+ if (controller.signal.aborted) {
291
+ return;
292
+ }
260
293
  this.events.emit("connect_failed", error2);
261
294
  this.events.emit("error", error2);
262
295
  this.scheduleReconnect();
263
296
  }
264
297
  }
265
- async receive(data) {
298
+ async receive(data, socket) {
266
299
  this.resetInactivityTimer();
267
300
  const value = typeof data === "string" ? data : data instanceof Blob ? await data.text() : Buffer.from(data).toString("utf8");
301
+ if (this.socket !== socket) {
302
+ return;
303
+ }
268
304
  for (const packet of decodePayload(value)) {
269
305
  this.handlePacket(packet);
270
306
  }
@@ -339,6 +375,7 @@ var OverleafRealtimeSocket = class {
339
375
  }
340
376
  handleClose(event) {
341
377
  const wasConnected = this.connected;
378
+ this.socket = void 0;
342
379
  this.connected = false;
343
380
  this.projectJoined = false;
344
381
  if (this.inactivityTimer) {
@@ -353,6 +390,8 @@ var OverleafRealtimeSocket = class {
353
390
  this.scheduleReconnect();
354
391
  }
355
392
  closeSocket(reason) {
393
+ this.connectionAbort?.abort();
394
+ this.connectionAbort = void 0;
356
395
  const wasConnected = this.connected;
357
396
  this.connected = false;
358
397
  this.projectJoined = false;
@@ -381,7 +420,7 @@ var OverleafRealtimeSocket = class {
381
420
  if (!this.reconnectEnabled || this.explicitlyDisconnected || this.reconnectTimer) {
382
421
  return;
383
422
  }
384
- const maxAttempts = this.options.maxReconnectionAttempts ?? 10;
423
+ const maxAttempts = this.options.maxReconnectionAttempts ?? Infinity;
385
424
  if (this.reconnectAttempts >= maxAttempts) {
386
425
  this.events.emit("reconnect_failed");
387
426
  return;
@@ -398,6 +437,9 @@ var OverleafRealtimeSocket = class {
398
437
  }
399
438
  };
400
439
  var sink;
440
+ function setLogSink(nextSink) {
441
+ sink = nextSink;
442
+ }
401
443
  function write(level, args) {
402
444
  if (sink !== void 0) {
403
445
  sink(level, args);
@@ -445,7 +487,7 @@ var HttpRequestScheduler = class {
445
487
  fetchImplementation;
446
488
  now;
447
489
  wait;
448
- fetch(input, init) {
490
+ fetch(input, init, timeoutMs = this.options.requestTimeoutMs ?? 6e4) {
449
491
  const url = input instanceof Request ? input.url : String(input);
450
492
  const history = url.includes("/updates?") || url.includes("/filetree/diff?") || url.includes("/diff?");
451
493
  return this.enqueue(async () => {
@@ -459,7 +501,20 @@ var HttpRequestScheduler = class {
459
501
  if (history) {
460
502
  this.historyNextRequestAt = requestAt + this.historyIntervalMs;
461
503
  }
462
- const response = await this.fetchImplementation(input, init);
504
+ const timeout = AbortSignal.timeout(timeoutMs);
505
+ const signals = [timeout, init?.signal, input instanceof Request ? input.signal : void 0].filter((signal) => Boolean(signal));
506
+ let response;
507
+ try {
508
+ response = await this.fetchImplementation(input, { ...init, signal: AbortSignal.any(signals) });
509
+ } catch (error2) {
510
+ const destination = new URL(url);
511
+ error("HTTP request failed", {
512
+ method: init?.method ?? (input instanceof Request ? input.method : "GET"),
513
+ url: `${destination.origin}${destination.pathname}`,
514
+ error: error2
515
+ });
516
+ throw error2;
517
+ }
463
518
  if (response.status === 429) {
464
519
  this.rateLimitedUntil = Math.max(
465
520
  this.rateLimitedUntil,
@@ -655,7 +710,7 @@ var BaseAPI = class {
655
710
  }
656
711
  return false;
657
712
  }
658
- async request(type, route, body, callback, extraHeaders) {
713
+ async request(type, route, body, callback, extraHeaders, timeoutMs) {
659
714
  if (this.identity === void 0) {
660
715
  return Promise.reject();
661
716
  }
@@ -674,7 +729,7 @@ var BaseAPI = class {
674
729
  "Cookie": this.identity.cookies,
675
730
  ...extraHeaders
676
731
  }
677
- });
732
+ }, timeoutMs);
678
733
  break;
679
734
  case "POST":
680
735
  const content_type = body instanceof FormData ? void 0 : { "Content-Type": "application/json" };
@@ -692,7 +747,7 @@ var BaseAPI = class {
692
747
  ...extraHeaders
693
748
  },
694
749
  body: raw_body
695
- });
750
+ }, timeoutMs);
696
751
  break;
697
752
  case "PUT":
698
753
  break;
@@ -706,7 +761,7 @@ var BaseAPI = class {
706
761
  "X-Csrf-Token": this.identity.csrfToken,
707
762
  ...extraHeaders
708
763
  }
709
- });
764
+ }, timeoutMs);
710
765
  break;
711
766
  }
712
767
  ;
@@ -956,7 +1011,7 @@ var BaseAPI = class {
956
1011
  return this.request("POST", `project/${projectId}/compile?auto_compile=true`, body, (res) => {
957
1012
  const compile = JSON.parse(res);
958
1013
  return { compile };
959
- }, { "X-Csrf-Token": identity.csrfToken });
1014
+ }, { "X-Csrf-Token": identity.csrfToken }, 3e5);
960
1015
  }
961
1016
  async stopCompile(identity, projectId) {
962
1017
  this.setIdentity(identity);
@@ -2400,6 +2455,7 @@ var SyncEngine = class {
2400
2455
  const currentVersion = await this.options.remote.getCurrentVersion();
2401
2456
  const localFiles = await this.options.local.listFiles();
2402
2457
  const remoteFiles = this.remoteFilePaths();
2458
+ this.options.log(`Checking replica at Overleaf version ${currentVersion}: ${localFiles.length} local file(s), ${remoteFiles.length} remote file(s).`);
2403
2459
  if (!this.state || !this.state.initialized) {
2404
2460
  await this.initializeState(currentVersion, localFiles, remoteFiles);
2405
2461
  } else {
@@ -2611,6 +2667,7 @@ var SyncEngine = class {
2611
2667
  }
2612
2668
  }
2613
2669
  let failed = false;
2670
+ this.options.log(`Reconciling ${candidates.size} changed path(s).`);
2614
2671
  for (const path of [...candidates].sort()) {
2615
2672
  try {
2616
2673
  await this.reconcilePath(path, localChanged.has(path) ? "local" : "remote");
@@ -3165,6 +3222,7 @@ async function startServe(directory = process.cwd(), options = {}) {
3165
3222
  }
3166
3223
 
3167
3224
  export {
3225
+ setLogSink,
3168
3226
  HttpRequestScheduler,
3169
3227
  ExtendedBaseAPI,
3170
3228
  SocketIOAPI,
@@ -3181,4 +3239,4 @@ export {
3181
3239
  RemoteProject,
3182
3240
  startServe
3183
3241
  };
3184
- //# sourceMappingURL=chunk-OZ4FLSVP.js.map
3242
+ //# sourceMappingURL=chunk-FBT2UYMY.js.map