leafrelay 0.2.5 → 0.2.7
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-OZ4FLSVP.js → chunk-3TTRDNHP.js} +195 -46
- package/dist/chunk-3TTRDNHP.js.map +1 -0
- package/dist/{chunk-IOX2PULH.js → chunk-VXZVIMCG.js} +182 -37
- package/dist/chunk-VXZVIMCG.js.map +1 -0
- package/dist/cli.js +3 -3
- package/dist/daemon.js +27 -6
- package/dist/daemon.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/dist/chunk-IOX2PULH.js.map +0 -1
- package/dist/chunk-OZ4FLSVP.js.map +0 -1
|
@@ -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
|
-
|
|
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
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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 ??
|
|
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
|
|
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);
|
|
@@ -2394,18 +2449,30 @@ var SyncEngine = class {
|
|
|
2394
2449
|
stopRemoteReconnect;
|
|
2395
2450
|
eventTimers = /* @__PURE__ */ new Map();
|
|
2396
2451
|
stopped = false;
|
|
2452
|
+
failedPaths = /* @__PURE__ */ new Map();
|
|
2453
|
+
retryTimer;
|
|
2454
|
+
retryDelayMs = 3e4;
|
|
2455
|
+
pendingRemoteVersion;
|
|
2456
|
+
pendingInitialization = false;
|
|
2457
|
+
get syncError() {
|
|
2458
|
+
if (this.failedPaths.size === 0) {
|
|
2459
|
+
return void 0;
|
|
2460
|
+
}
|
|
2461
|
+
const [path, failure] = this.failedPaths.entries().next().value;
|
|
2462
|
+
return `${this.failedPaths.size} path(s) could not synchronize, including ${path}: ${failure.message}. Other paths remain active; failed paths will retry automatically.`;
|
|
2463
|
+
}
|
|
2397
2464
|
async start() {
|
|
2398
2465
|
this.stopped = false;
|
|
2399
|
-
this.
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
} else {
|
|
2406
|
-
this.restoreConflicts();
|
|
2407
|
-
await this.startupReconcile(currentVersion, localFiles, remoteFiles);
|
|
2466
|
+
const startup = this.enqueue(() => this.initialize());
|
|
2467
|
+
try {
|
|
2468
|
+
await startup;
|
|
2469
|
+
} catch (error2) {
|
|
2470
|
+
await this.stop();
|
|
2471
|
+
throw error2;
|
|
2408
2472
|
}
|
|
2473
|
+
}
|
|
2474
|
+
async initialize() {
|
|
2475
|
+
this.state = await this.options.stateStore.load();
|
|
2409
2476
|
this.stopLocalWatch = await this.options.local.watch((path) => this.schedule(path, "local"));
|
|
2410
2477
|
this.stopRemoteChange = this.options.remote.onChange((change) => {
|
|
2411
2478
|
if (this.stopped) {
|
|
@@ -2424,10 +2491,24 @@ var SyncEngine = class {
|
|
|
2424
2491
|
this.options.log(`Failed to reconcile after reconnecting: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
2425
2492
|
});
|
|
2426
2493
|
});
|
|
2427
|
-
|
|
2494
|
+
const currentVersion = await this.options.remote.getCurrentVersion();
|
|
2495
|
+
const localFiles = await this.options.local.listFiles();
|
|
2496
|
+
const remoteFiles = this.remoteFilePaths();
|
|
2497
|
+
this.options.log(`Checking replica at Overleaf version ${currentVersion}: ${localFiles.length} local file(s), ${remoteFiles.length} remote file(s).`);
|
|
2498
|
+
if (!this.state || !this.state.initialized) {
|
|
2499
|
+
await this.initializeState(currentVersion, localFiles, remoteFiles);
|
|
2500
|
+
} else {
|
|
2501
|
+
this.restoreConflicts();
|
|
2502
|
+
await this.startupReconcile(currentVersion, localFiles, remoteFiles);
|
|
2503
|
+
}
|
|
2504
|
+
this.options.log(this.syncError ? `Synchronization active with pending retries. ${this.syncError}` : `Synchronization active at Overleaf version ${this.state.remoteVersion}.`);
|
|
2428
2505
|
}
|
|
2429
2506
|
async stop() {
|
|
2430
2507
|
this.stopped = true;
|
|
2508
|
+
if (this.retryTimer) {
|
|
2509
|
+
clearTimeout(this.retryTimer);
|
|
2510
|
+
this.retryTimer = void 0;
|
|
2511
|
+
}
|
|
2431
2512
|
this.stopRemoteChange?.();
|
|
2432
2513
|
this.stopRemoteChange = void 0;
|
|
2433
2514
|
this.stopRemoteReconnect?.();
|
|
@@ -2501,21 +2582,25 @@ var SyncEngine = class {
|
|
|
2501
2582
|
await this.options.stateStore.save(this.state);
|
|
2502
2583
|
}
|
|
2503
2584
|
this.restoreConflicts();
|
|
2585
|
+
this.pendingRemoteVersion = currentVersion;
|
|
2586
|
+
this.pendingInitialization = true;
|
|
2504
2587
|
const paths = /* @__PURE__ */ new Set([...localFiles, ...remoteFiles, ...Object.keys(this.state.files)]);
|
|
2505
2588
|
for (const path of [...paths].sort()) {
|
|
2506
2589
|
const normalized = normalizePath2(path);
|
|
2507
2590
|
if (this.conflicts.has(normalized)) {
|
|
2508
2591
|
continue;
|
|
2509
2592
|
}
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2593
|
+
try {
|
|
2594
|
+
if (this.state.files[normalized] !== void 0) {
|
|
2595
|
+
await this.synchronizePath(normalized, "local");
|
|
2596
|
+
} else {
|
|
2597
|
+
await this.initializePath(normalized);
|
|
2598
|
+
}
|
|
2599
|
+
} catch (error2) {
|
|
2600
|
+
this.recordFailure(normalized, "local", error2);
|
|
2513
2601
|
}
|
|
2514
|
-
await this.initializePath(normalized);
|
|
2515
2602
|
}
|
|
2516
|
-
this.
|
|
2517
|
-
this.state.initialized = true;
|
|
2518
|
-
await this.options.stateStore.save(this.state);
|
|
2603
|
+
await this.finishRemoteBatch();
|
|
2519
2604
|
if (this.conflicts.size !== 0) {
|
|
2520
2605
|
this.options.log(`Synchronization remains active; ${this.conflicts.size} conflicting path(s) are paused.`);
|
|
2521
2606
|
}
|
|
@@ -2588,7 +2673,7 @@ var SyncEngine = class {
|
|
|
2588
2673
|
throw new Error("Synchronization paused: Overleaf history is unavailable, so remote changes cannot be distinguished safely.");
|
|
2589
2674
|
}
|
|
2590
2675
|
}
|
|
2591
|
-
const candidates = new Set(localChanged);
|
|
2676
|
+
const candidates = /* @__PURE__ */ new Set([...localChanged, ...this.failedPaths.keys()]);
|
|
2592
2677
|
const knownFiles = /* @__PURE__ */ new Set([...Object.keys(state.files), ...localFiles, ...remoteFiles]);
|
|
2593
2678
|
for (const path of [...localFiles, ...remoteFiles]) {
|
|
2594
2679
|
if (state.files[path] === void 0) {
|
|
@@ -2596,6 +2681,9 @@ var SyncEngine = class {
|
|
|
2596
2681
|
}
|
|
2597
2682
|
}
|
|
2598
2683
|
for (const change of remoteDiff.diff) {
|
|
2684
|
+
if (!change.operation) {
|
|
2685
|
+
continue;
|
|
2686
|
+
}
|
|
2599
2687
|
const paths = [change.pathname, change.newPathname].filter((path) => Boolean(path));
|
|
2600
2688
|
for (const changedPath of paths.map(normalizePath2)) {
|
|
2601
2689
|
let matched = false;
|
|
@@ -2610,20 +2698,19 @@ var SyncEngine = class {
|
|
|
2610
2698
|
}
|
|
2611
2699
|
}
|
|
2612
2700
|
}
|
|
2613
|
-
|
|
2701
|
+
this.pendingRemoteVersion = currentVersion;
|
|
2702
|
+
this.options.log(`Reconciling ${candidates.size} changed path(s).`);
|
|
2614
2703
|
for (const path of [...candidates].sort()) {
|
|
2704
|
+
if (this.stopped) {
|
|
2705
|
+
return;
|
|
2706
|
+
}
|
|
2615
2707
|
try {
|
|
2616
|
-
await this.
|
|
2708
|
+
await this.synchronizePath(path, localChanged.has(path) ? "local" : "remote");
|
|
2617
2709
|
} catch (error2) {
|
|
2618
|
-
|
|
2619
|
-
this.options.log(`Failed to synchronize ${path}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
2710
|
+
this.recordFailure(path, localChanged.has(path) ? "local" : "remote", error2);
|
|
2620
2711
|
}
|
|
2621
2712
|
}
|
|
2622
|
-
|
|
2623
|
-
throw new Error("Synchronization paused for failed paths; successful paths were checkpointed.");
|
|
2624
|
-
}
|
|
2625
|
-
state.remoteVersion = currentVersion;
|
|
2626
|
-
await this.options.stateStore.save(state);
|
|
2713
|
+
await this.finishRemoteBatch();
|
|
2627
2714
|
if (this.conflicts.size !== 0) {
|
|
2628
2715
|
this.options.log(`Synchronization remains active; ${this.conflicts.size} conflicting path(s) are paused.`);
|
|
2629
2716
|
}
|
|
@@ -2638,7 +2725,7 @@ var SyncEngine = class {
|
|
|
2638
2725
|
await this.options.local.listFiles(),
|
|
2639
2726
|
this.remoteFilePaths()
|
|
2640
2727
|
);
|
|
2641
|
-
this.options.log(`Realtime connection restored at Overleaf version ${currentVersion}.`);
|
|
2728
|
+
this.options.log(this.syncError ? `Realtime connection restored; ${this.syncError}` : `Realtime connection restored at Overleaf version ${currentVersion}.`);
|
|
2642
2729
|
}
|
|
2643
2730
|
schedule(path, source, actor) {
|
|
2644
2731
|
const normalized = normalizePath2(path);
|
|
@@ -2668,13 +2755,72 @@ var SyncEngine = class {
|
|
|
2668
2755
|
return;
|
|
2669
2756
|
}
|
|
2670
2757
|
const remoteActor = pending.unknownRemoteActor ? void 0 : pending.actors.size === 1 ? [...pending.actors][0] : pending.actors.size > 1 ? "multiple Overleaf collaborators" : void 0;
|
|
2671
|
-
this.queue = this.queue.then(() => this.
|
|
2672
|
-
this.
|
|
2758
|
+
this.queue = this.queue.then(() => this.synchronizePath(normalized, pending.source, remoteActor)).catch((error2) => {
|
|
2759
|
+
this.recordFailure(normalized, pending.source, error2, remoteActor);
|
|
2673
2760
|
});
|
|
2674
2761
|
}, 250)
|
|
2675
2762
|
};
|
|
2676
2763
|
this.eventTimers.set(normalized, pending);
|
|
2677
2764
|
}
|
|
2765
|
+
async synchronizePath(path, source, actor) {
|
|
2766
|
+
if (this.pendingInitialization && this.state.files[path] === void 0) {
|
|
2767
|
+
await this.initializePath(path);
|
|
2768
|
+
} else {
|
|
2769
|
+
await this.reconcilePath(path, source, actor);
|
|
2770
|
+
}
|
|
2771
|
+
if (this.failedPaths.delete(path)) {
|
|
2772
|
+
this.options.onSyncError?.(this.syncError);
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
recordFailure(path, source, error2, actor) {
|
|
2776
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
2777
|
+
this.failedPaths.set(path, { source, actor, message });
|
|
2778
|
+
this.options.log(`Failed to synchronize ${path}: ${message}; retrying without stopping other paths.`);
|
|
2779
|
+
this.options.onSyncError?.(this.syncError);
|
|
2780
|
+
this.scheduleRetry();
|
|
2781
|
+
}
|
|
2782
|
+
scheduleRetry() {
|
|
2783
|
+
if (this.stopped || this.retryTimer || this.failedPaths.size === 0) {
|
|
2784
|
+
return;
|
|
2785
|
+
}
|
|
2786
|
+
this.retryTimer = setTimeout(() => {
|
|
2787
|
+
this.retryTimer = void 0;
|
|
2788
|
+
void this.enqueue(async () => {
|
|
2789
|
+
if (this.stopped) {
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
for (const [path, failure] of [...this.failedPaths]) {
|
|
2793
|
+
if (this.stopped) {
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
try {
|
|
2797
|
+
await this.synchronizePath(path, failure.source, failure.actor);
|
|
2798
|
+
this.options.log(`Recovered synchronization for ${path}.`);
|
|
2799
|
+
} catch (error2) {
|
|
2800
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
2801
|
+
this.failedPaths.set(path, { ...failure, message });
|
|
2802
|
+
this.options.log(`Retry failed for ${path}: ${message}`);
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2805
|
+
await this.finishRemoteBatch();
|
|
2806
|
+
}).catch((error2) => this.options.log(`Could not checkpoint retry batch: ${String(error2)}`)).finally(() => {
|
|
2807
|
+
this.retryDelayMs = this.failedPaths.size ? Math.min(this.retryDelayMs * 2, 12e4) : 3e4;
|
|
2808
|
+
this.scheduleRetry();
|
|
2809
|
+
});
|
|
2810
|
+
}, this.retryDelayMs);
|
|
2811
|
+
}
|
|
2812
|
+
async finishRemoteBatch() {
|
|
2813
|
+
if (this.stopped || this.failedPaths.size || this.pendingRemoteVersion === void 0) {
|
|
2814
|
+
return;
|
|
2815
|
+
}
|
|
2816
|
+
this.state.remoteVersion = this.pendingRemoteVersion;
|
|
2817
|
+
if (this.pendingInitialization) {
|
|
2818
|
+
this.state.initialized = true;
|
|
2819
|
+
}
|
|
2820
|
+
await this.options.stateStore.save(this.state);
|
|
2821
|
+
this.pendingRemoteVersion = void 0;
|
|
2822
|
+
this.pendingInitialization = false;
|
|
2823
|
+
}
|
|
2678
2824
|
enqueue(operation) {
|
|
2679
2825
|
const result = this.queue.then(operation);
|
|
2680
2826
|
this.queue = result.then(() => void 0, () => void 0);
|
|
@@ -3137,6 +3283,7 @@ async function startServe(directory = process.cwd(), options = {}) {
|
|
|
3137
3283
|
remote,
|
|
3138
3284
|
stateStore: new FileSyncStateStore(root, settings.uri),
|
|
3139
3285
|
ignore,
|
|
3286
|
+
onSyncError: options.onSyncError,
|
|
3140
3287
|
log: options.log ?? log,
|
|
3141
3288
|
onConflict: options.onConflict ?? ((path, reason) => {
|
|
3142
3289
|
(options.log ?? log)(`Conflict paused for ${path}: ${reason}`);
|
|
@@ -3154,6 +3301,7 @@ async function startServe(directory = process.cwd(), options = {}) {
|
|
|
3154
3301
|
serverName: settings.serverName,
|
|
3155
3302
|
projectId: settings.projectId,
|
|
3156
3303
|
projectName: settings.projectName,
|
|
3304
|
+
syncError: () => engine.syncError,
|
|
3157
3305
|
conflicts: () => engine.getConflicts(),
|
|
3158
3306
|
resolveConflict: (path, winner) => engine.resolveConflict(path, winner),
|
|
3159
3307
|
retry: (path) => engine.retry(path),
|
|
@@ -3165,6 +3313,7 @@ async function startServe(directory = process.cwd(), options = {}) {
|
|
|
3165
3313
|
}
|
|
3166
3314
|
|
|
3167
3315
|
export {
|
|
3316
|
+
setLogSink,
|
|
3168
3317
|
HttpRequestScheduler,
|
|
3169
3318
|
ExtendedBaseAPI,
|
|
3170
3319
|
SocketIOAPI,
|
|
@@ -3181,4 +3330,4 @@ export {
|
|
|
3181
3330
|
RemoteProject,
|
|
3182
3331
|
startServe
|
|
3183
3332
|
};
|
|
3184
|
-
//# sourceMappingURL=chunk-
|
|
3333
|
+
//# sourceMappingURL=chunk-3TTRDNHP.js.map
|