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.
@@ -1,4 +1,4 @@
1
- // ../daemon/dist/chunk-PSHSXZIB.js
1
+ // ../daemon/dist/chunk-QRLVFB6A.js
2
2
  import { createHash } from "crypto";
3
3
  import { homedir } from "os";
4
4
  import { join, resolve } from "path";
@@ -4600,6 +4600,9 @@ function write(level, args) {
4600
4600
  function log(...args) {
4601
4601
  write("info", args);
4602
4602
  }
4603
+ function error(...args) {
4604
+ write("error", args);
4605
+ }
4603
4606
  function retryAfterMilliseconds(response, now) {
4604
4607
  const retryAfter = response.headers.get("retry-after");
4605
4608
  if (retryAfter) {
@@ -4633,7 +4636,7 @@ var HttpRequestScheduler = class {
4633
4636
  fetchImplementation;
4634
4637
  now;
4635
4638
  wait;
4636
- fetch(input, init) {
4639
+ fetch(input, init, timeoutMs = this.options.requestTimeoutMs ?? 6e4) {
4637
4640
  const url = input instanceof Request ? input.url : String(input);
4638
4641
  const history = url.includes("/updates?") || url.includes("/filetree/diff?") || url.includes("/diff?");
4639
4642
  return this.enqueue(async () => {
@@ -4647,7 +4650,20 @@ var HttpRequestScheduler = class {
4647
4650
  if (history) {
4648
4651
  this.historyNextRequestAt = requestAt + this.historyIntervalMs;
4649
4652
  }
4650
- const response = await this.fetchImplementation(input, init);
4653
+ const timeout = AbortSignal.timeout(timeoutMs);
4654
+ const signals = [timeout, init?.signal, input instanceof Request ? input.signal : void 0].filter((signal) => Boolean(signal));
4655
+ let response;
4656
+ try {
4657
+ response = await this.fetchImplementation(input, { ...init, signal: AbortSignal.any(signals) });
4658
+ } catch (error2) {
4659
+ const destination = new URL(url);
4660
+ error("HTTP request failed", {
4661
+ method: init?.method ?? (input instanceof Request ? input.method : "GET"),
4662
+ url: `${destination.origin}${destination.pathname}`,
4663
+ error: error2
4664
+ });
4665
+ throw error2;
4666
+ }
4651
4667
  if (response.status === 429) {
4652
4668
  this.rateLimitedUntil = Math.max(
4653
4669
  this.rateLimitedUntil,
@@ -4746,6 +4762,8 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4746
4762
  connected = false;
4747
4763
  closing = false;
4748
4764
  reconnecting;
4765
+ reconnectTimer;
4766
+ cancelReconnectWait;
4749
4767
  connectionClosed = Promise.resolve();
4750
4768
  initializeState;
4751
4769
  paths;
@@ -4759,7 +4777,15 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4759
4777
  clientId;
4760
4778
  static async connect(options) {
4761
4779
  const client = new _LeafRelayDaemonClient(options);
4762
- await client.connectTransport();
4780
+ client.reconnecting = client.connectTransport();
4781
+ try {
4782
+ await client.reconnecting;
4783
+ } catch (error2) {
4784
+ client.close();
4785
+ throw error2;
4786
+ } finally {
4787
+ client.reconnecting = void 0;
4788
+ }
4763
4789
  return client;
4764
4790
  }
4765
4791
  get initializeResult() {
@@ -4769,16 +4795,42 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4769
4795
  return this.request(RPC_METHOD.daemonStatus);
4770
4796
  }
4771
4797
  async attachReplica(params) {
4772
- const result = await this.request(RPC_METHOD.replicaAttach, params);
4773
- this.replicas.set(result.replicaId, { params, currentId: result.replicaId });
4774
- return result;
4798
+ const replicaId = randomUUID();
4799
+ const registration = { params };
4800
+ this.replicas.set(replicaId, registration);
4801
+ try {
4802
+ if (!this.connected) {
4803
+ await this.ensureReconnected();
4804
+ }
4805
+ if (!registration.result) {
4806
+ const rpc = this.rpc;
4807
+ try {
4808
+ await this.registerReplica(replicaId, registration);
4809
+ } catch (error2) {
4810
+ if (this.closing || this.rpc === rpc && this.connected) {
4811
+ throw error2;
4812
+ }
4813
+ await this.ensureReconnected();
4814
+ }
4815
+ }
4816
+ return registration.result;
4817
+ } catch (error2) {
4818
+ this.replicas.delete(replicaId);
4819
+ throw error2;
4820
+ }
4775
4821
  }
4776
4822
  async detachReplica(replicaId) {
4823
+ if (!this.connected) {
4824
+ await this.ensureReconnected();
4825
+ }
4777
4826
  const registration = this.replicas.get(replicaId);
4778
- await this.request(RPC_METHOD.replicaDetach, { replicaId: registration?.currentId ?? replicaId });
4779
4827
  this.replicas.delete(replicaId);
4828
+ await this.request(RPC_METHOD.replicaDetach, { replicaId: registration?.currentId ?? replicaId });
4780
4829
  }
4781
4830
  async resolveConflict(params) {
4831
+ if (!this.connected) {
4832
+ await this.ensureReconnected();
4833
+ }
4782
4834
  const registration = this.replicas.get(params.replicaId);
4783
4835
  await this.request(RPC_METHOD.replicaResolveConflict, {
4784
4836
  ...params,
@@ -4786,6 +4838,9 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4786
4838
  });
4787
4839
  }
4788
4840
  async retry(replicaId, path2) {
4841
+ if (!this.connected) {
4842
+ await this.ensureReconnected();
4843
+ }
4789
4844
  const registration = this.replicas.get(replicaId);
4790
4845
  await this.request(RPC_METHOD.replicaRetry, { replicaId: registration?.currentId ?? replicaId, path: path2 });
4791
4846
  }
@@ -4821,9 +4876,32 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4821
4876
  return decodeRpcValue(result);
4822
4877
  }
4823
4878
  async openProject(server, projectId) {
4824
- const result = await this.request(RPC_METHOD.projectOpen, { server, projectId });
4825
- this.projects.set(result.projectKey, { server, projectId, currentKey: result.projectKey });
4826
- return result;
4879
+ const host = new URL(server.includes("://") ? server : `https://${server}`).host;
4880
+ const projectKey = `${host}:${projectId}`;
4881
+ const registration = { server, projectId, currentKey: projectKey };
4882
+ this.projects.set(projectKey, registration);
4883
+ try {
4884
+ if (!this.connected) {
4885
+ await this.ensureReconnected();
4886
+ }
4887
+ if (!registration.result) {
4888
+ const rpc = this.rpc;
4889
+ try {
4890
+ await this.registerProject(registration);
4891
+ } catch (error2) {
4892
+ if (this.closing || this.rpc === rpc && this.connected) {
4893
+ throw error2;
4894
+ }
4895
+ await this.ensureReconnected();
4896
+ }
4897
+ }
4898
+ return registration.result;
4899
+ } catch (error2) {
4900
+ if (this.projects.get(projectKey) === registration) {
4901
+ this.projects.delete(projectKey);
4902
+ }
4903
+ throw error2;
4904
+ }
4827
4905
  }
4828
4906
  async closeProject(projectKey) {
4829
4907
  const registration = this.projects.get(projectKey);
@@ -4848,6 +4926,10 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4848
4926
  }
4849
4927
  close() {
4850
4928
  this.closing = true;
4929
+ if (this.reconnectTimer) {
4930
+ clearTimeout(this.reconnectTimer);
4931
+ }
4932
+ this.cancelReconnectWait?.();
4851
4933
  this.connected = false;
4852
4934
  this.rpc?.dispose();
4853
4935
  this.socket?.destroy();
@@ -4858,15 +4940,7 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4858
4940
  if (!this.connected) {
4859
4941
  await this.ensureReconnected();
4860
4942
  }
4861
- try {
4862
- return await this.rawRequest(method, params);
4863
- } catch (requestError) {
4864
- if (this.connected || this.closing) {
4865
- throw requestError;
4866
- }
4867
- await this.ensureReconnected();
4868
- return this.rawRequest(method, params);
4869
- }
4943
+ return this.rawRequest(method, params);
4870
4944
  }
4871
4945
  rawRequest(method, params) {
4872
4946
  if (!this.rpc) {
@@ -4882,11 +4956,38 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4882
4956
  if (this.closing) {
4883
4957
  throw new Error("LeafRelay daemon client is closed.");
4884
4958
  }
4885
- this.reconnecting ??= this.connectTransport().finally(() => {
4959
+ this.reconnecting ??= this.reconnectTransport().finally(() => {
4886
4960
  this.reconnecting = void 0;
4887
4961
  });
4888
4962
  return this.reconnecting;
4889
4963
  }
4964
+ async reconnectTransport() {
4965
+ let attempt = 0;
4966
+ while (!this.closing) {
4967
+ try {
4968
+ await this.connectTransport();
4969
+ return;
4970
+ } catch (error2) {
4971
+ const code = error2.code;
4972
+ if (code === -32001 || code === -32002 || this.closing) {
4973
+ throw error2;
4974
+ }
4975
+ const delay = Math.min(1e3 * 2 ** attempt++, 16e3);
4976
+ dispatch(this.logListeners, {
4977
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4978
+ level: "warn",
4979
+ message: `Daemon reconnection failed; retrying in ${delay}ms: ${error2 instanceof Error ? error2.message : String(error2)}`
4980
+ });
4981
+ await new Promise((resolveWait) => {
4982
+ this.cancelReconnectWait = resolveWait;
4983
+ this.reconnectTimer = setTimeout(resolveWait, delay);
4984
+ });
4985
+ this.cancelReconnectWait = void 0;
4986
+ this.reconnectTimer = void 0;
4987
+ }
4988
+ }
4989
+ throw new Error("LeafRelay daemon client is closed.");
4990
+ }
4890
4991
  async connectTransport() {
4891
4992
  const metadata = await this.findOrStartDaemon();
4892
4993
  const socket = await tryConnect(metadata);
@@ -4909,8 +5010,19 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4909
5010
  this.connected = false;
4910
5011
  this.rpc = void 0;
4911
5012
  this.socket = void 0;
5013
+ for (const registration of this.replicas.values()) {
5014
+ registration.currentId = void 0;
5015
+ registration.result = void 0;
5016
+ }
5017
+ for (const registration of this.projects.values()) {
5018
+ registration.result = void 0;
5019
+ }
4912
5020
  if (!this.closing) {
4913
- void this.ensureReconnected();
5021
+ void this.ensureReconnected().catch((error2) => dispatch(this.logListeners, {
5022
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5023
+ level: "error",
5024
+ message: `Daemon reconnection failed: ${error2 instanceof Error ? error2.message : String(error2)}`
5025
+ }));
4914
5026
  }
4915
5027
  });
4916
5028
  rpc.listen();
@@ -4922,9 +5034,10 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4922
5034
  clientVersion: this.options.clientVersion,
4923
5035
  protocolVersion: LEAFRELAY_PROTOCOL_VERSION
4924
5036
  });
5037
+ await this.restoreRegistrations();
4925
5038
  this.connected = true;
4926
- await this.restoreRegistrations(rpc);
4927
5039
  } catch (connectError) {
5040
+ this.connected = false;
4928
5041
  rpc.dispose();
4929
5042
  socket.destroy();
4930
5043
  if (this.rpc === rpc) {
@@ -4934,17 +5047,49 @@ var LeafRelayDaemonClient = class _LeafRelayDaemonClient {
4934
5047
  throw connectError;
4935
5048
  }
4936
5049
  }
4937
- async restoreRegistrations(rpc) {
5050
+ async restoreRegistrations() {
4938
5051
  for (const registration of this.projects.values()) {
4939
- const result = await rpc.sendRequest(RPC_METHOD.projectOpen, {
4940
- server: registration.server,
4941
- projectId: registration.projectId
5052
+ await this.registerProject(registration);
5053
+ }
5054
+ for (const [replicaId, registration] of this.replicas) {
5055
+ await this.registerReplica(replicaId, registration);
5056
+ }
5057
+ if (this.projects.size !== 0 || this.replicas.size !== 0) {
5058
+ dispatch(this.logListeners, {
5059
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5060
+ level: "info",
5061
+ message: `Daemon registrations restored: ${this.projects.size} project(s), ${this.replicas.size} local replica(s).`
4942
5062
  });
4943
- registration.currentKey = result.projectKey;
4944
5063
  }
4945
- for (const registration of this.replicas.values()) {
4946
- const result = await rpc.sendRequest(RPC_METHOD.replicaAttach, registration.params);
4947
- registration.currentId = result.replicaId;
5064
+ }
5065
+ async registerProject(registration) {
5066
+ const rpc = this.rpc;
5067
+ const result = await this.rawRequest(RPC_METHOD.projectOpen, {
5068
+ server: registration.server,
5069
+ projectId: registration.projectId
5070
+ });
5071
+ if (this.rpc !== rpc) {
5072
+ throw new Error("LeafRelay daemon connection changed during project attachment.");
5073
+ }
5074
+ registration.currentKey = result.projectKey;
5075
+ registration.result = result;
5076
+ }
5077
+ async registerReplica(replicaId, registration) {
5078
+ const rpc = this.rpc;
5079
+ const result = await this.rawRequest(RPC_METHOD.replicaAttach, registration.params);
5080
+ if (this.rpc !== rpc) {
5081
+ throw new Error("LeafRelay daemon connection changed during replica attachment.");
5082
+ }
5083
+ registration.currentId = result.replicaId;
5084
+ registration.result = { ...result, replicaId };
5085
+ dispatch(this.statusListeners, { replicaId, status: result.status });
5086
+ for (const path2 of result.conflicts) {
5087
+ dispatch(this.conflictListeners, {
5088
+ replicaId,
5089
+ path: path2,
5090
+ conflicts: result.conflicts,
5091
+ reason: "This path has an unresolved synchronization conflict."
5092
+ });
4948
5093
  }
4949
5094
  }
4950
5095
  registerNotifications(rpc) {
@@ -5067,7 +5212,7 @@ async function tryConnect(metadata) {
5067
5212
  function connectCliDaemon(autoStart = true) {
5068
5213
  return LeafRelayDaemonClient.connect({
5069
5214
  clientName: "cli",
5070
- clientVersion: "0.2.5",
5215
+ clientVersion: "0.2.7",
5071
5216
  daemonEntrypoint: new URL("./daemon.js", import.meta.url),
5072
5217
  autoStart
5073
5218
  });
@@ -5092,9 +5237,9 @@ async function startServe(directory = process.cwd()) {
5092
5237
  client.close();
5093
5238
  }
5094
5239
  };
5095
- } catch (error) {
5240
+ } catch (error2) {
5096
5241
  client.close();
5097
- throw error;
5242
+ throw error2;
5098
5243
  }
5099
5244
  }
5100
5245
  async function serve(directory = process.cwd()) {
@@ -5104,8 +5249,8 @@ async function serve(directory = process.cwd()) {
5104
5249
  const stop = () => {
5105
5250
  process.off("SIGINT", stop);
5106
5251
  process.off("SIGTERM", stop);
5107
- running.stop().then(resolveStop, (error) => {
5108
- console.error(error);
5252
+ running.stop().then(resolveStop, (error2) => {
5253
+ console.error(error2);
5109
5254
  resolveStop();
5110
5255
  });
5111
5256
  };
@@ -5157,4 +5302,4 @@ export {
5157
5302
  chokidar/index.js:
5158
5303
  (*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) *)
5159
5304
  */
5160
- //# sourceMappingURL=chunk-IOX2PULH.js.map
5305
+ //# sourceMappingURL=chunk-VXZVIMCG.js.map