@calimero-network/mero-js 26.4.1 → 26.6.0

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/index.cjs CHANGED
@@ -69,10 +69,13 @@ __export(index_exports, {
69
69
  RELAY_NODE_KEY_RETRY_MS: () => RELAY_NODE_KEY_RETRY_MS,
70
70
  REQUEST_PROOF_HEADER: () => REQUEST_PROOF_HEADER,
71
71
  RelayClient: () => RelayClient,
72
+ RelayLoginError: () => RelayLoginError,
72
73
  RelayNotDialableError: () => RelayNotDialableError,
73
74
  RelayObserver: () => RelayObserver,
74
75
  RelayPresenceClient: () => RelayPresenceClient,
75
76
  RelayTransport: () => RelayTransport,
77
+ RelaysUnavailableError: () => RelaysUnavailableError,
78
+ RequestAuthError: () => RequestAuthError,
76
79
  RpcClient: () => RpcClient,
77
80
  RpcError: () => RpcError,
78
81
  SEALED_CONTENT_TYPE: () => SEALED_CONTENT_TYPE,
@@ -612,6 +615,12 @@ var AuthRevokedError = class extends HTTPError {
612
615
  this.message = `Authentication revoked (${reason}): HTTP ${status} ${statusText}`;
613
616
  }
614
617
  };
618
+ var RequestAuthError = class extends Error {
619
+ constructor() {
620
+ super(...arguments);
621
+ this.name = "RequestAuthError";
622
+ }
623
+ };
615
624
  function headersToRecord(headers) {
616
625
  const record = {};
617
626
  headers.forEach((value, key) => {
@@ -939,6 +948,7 @@ var WebHttpClient = class {
939
948
  headers.Authorization = `Bearer ${token}`;
940
949
  }
941
950
  } catch (error) {
951
+ if (error instanceof RequestAuthError) throw error;
942
952
  }
943
953
  }
944
954
  if (initHeaders) {
@@ -1374,6 +1384,30 @@ function withoutCap(mask, cap) {
1374
1384
  return (mask & ~cap) >>> 0;
1375
1385
  }
1376
1386
 
1387
+ // src/account/relay-login-error.ts
1388
+ var RelayLoginError = class extends RequestAuthError {
1389
+ constructor(relayUrl, cause) {
1390
+ const kind = relayLoginFailureKind(cause);
1391
+ const detail = cause instanceof Error ? cause.message : String(cause);
1392
+ super(
1393
+ kind === "signed-out" ? `the relay ${relayUrl} refused this device's credential (${detail}); sign in again` : `could not log in to the relay ${relayUrl}: ${detail}`
1394
+ );
1395
+ this.name = "RelayLoginError";
1396
+ this.cause = cause;
1397
+ this.relayUrl = relayUrl;
1398
+ this.kind = kind;
1399
+ this.loginStatus = loginStatusOf(cause);
1400
+ }
1401
+ };
1402
+ function relayLoginFailureKind(cause) {
1403
+ const status = loginStatusOf(cause);
1404
+ return status === 401 || status === 403 ? "signed-out" : "unavailable";
1405
+ }
1406
+ function loginStatusOf(cause) {
1407
+ const status = cause?.status;
1408
+ return typeof status === "number" && Number.isFinite(status) ? status : void 0;
1409
+ }
1410
+
1377
1411
  // src/errors/classify.ts
1378
1412
  var RETRYABLE = {
1379
1413
  invalid: false,
@@ -1444,6 +1478,11 @@ function messageOf(err, fallback = "Request failed") {
1444
1478
  return fallback;
1445
1479
  }
1446
1480
  function classifyError(err) {
1481
+ if (err instanceof RelayLoginError) {
1482
+ const status2 = err.loginStatus;
1483
+ const kind2 = err.kind === "signed-out" ? "unauthorized" : status2 ? kindForStatus(status2) : "unreachable";
1484
+ return { kind: kind2, status: status2, retryable: RETRYABLE[kind2], message: messageOf(err) };
1485
+ }
1447
1486
  const status = statusOf(err);
1448
1487
  let kind;
1449
1488
  if (status !== void 0) {
@@ -1498,6 +1537,9 @@ function reasonFromMessage(message) {
1498
1537
  }
1499
1538
  var FINAL_FROM_TEXT = /* @__PURE__ */ new Set(["already-used", "removed"]);
1500
1539
  function classifyJoinFailure(err, message) {
1540
+ if (err instanceof RelayLoginError) {
1541
+ return { reason: err.kind === "signed-out" ? "signed-out" : "node-unreachable", retryable: true };
1542
+ }
1501
1543
  const { kind, status } = classifyError(err);
1502
1544
  const fromText = reasonFromMessage(message);
1503
1545
  const code = refusalCodeOf(err);
@@ -9701,9 +9743,8 @@ function relaySession(s, nodeKey) {
9701
9743
  token = minted.accessToken;
9702
9744
  return token;
9703
9745
  }).catch((e) => {
9704
- console.warn("[mero-js] relay login failed", e);
9705
9746
  token = null;
9706
- return void 0;
9747
+ throw new RelayLoginError(s.relayUrl, e);
9707
9748
  }).finally(() => {
9708
9749
  inflight = null;
9709
9750
  });
@@ -9865,24 +9906,53 @@ function knownRelays(s) {
9865
9906
  const all = [s.relayUrl, ...Object.values(map.namespaces), ...Object.values(map.contexts)].filter((u) => typeof u === "string" && u.length > 0).map(relayOrigin);
9866
9907
  return [...new Set(all)];
9867
9908
  }
9909
+ var RelaysUnavailableError = class extends Error {
9910
+ constructor(failures) {
9911
+ super(`none of this account's relays answered: ${failures.map((f) => `${f.relayUrl} (${f.kind})`).join(", ")}`);
9912
+ this.failures = failures;
9913
+ this.name = "RelaysUnavailableError";
9914
+ this.relays = failures.map((f) => f.relayUrl);
9915
+ }
9916
+ /** Every relay refused its attestation: retrying will not help. */
9917
+ get refused() {
9918
+ return this.failures.every((f) => f.kind === "refused");
9919
+ }
9920
+ };
9921
+ async function listingClient(s, relayUrl) {
9922
+ const key = await attemptRelayNodeKey(relayUrl);
9923
+ if (key.kind !== "learned") {
9924
+ console.warn("[mero-js] could not learn the relay node key from its attestation", key.error);
9925
+ return { failure: { relayUrl, kind: key.kind, error: key.error } };
9926
+ }
9927
+ const client2 = buildDelegatedClient({ ...s, relayUrl }, null);
9928
+ if (!client2) return { failure: { relayUrl, kind: "unavailable", error: new Error(`no client for ${relayUrl}`) } };
9929
+ return { client: client2 };
9930
+ }
9868
9931
  async function listDelegatedContexts(s) {
9869
9932
  const out = [];
9870
9933
  const map = readRelayMap(s.account);
9871
- for (const relayUrl of knownRelays(s)) {
9934
+ const relays = knownRelays(s);
9935
+ const failures = [];
9936
+ for (const relayUrl of relays) {
9937
+ const got = await listingClient(s, relayUrl);
9938
+ if ("failure" in got) {
9939
+ failures.push(got.failure);
9940
+ continue;
9941
+ }
9942
+ const { client: client2 } = got;
9872
9943
  try {
9873
- if (!await resolveRelayNodeKey(relayUrl)) continue;
9874
- const client2 = buildDelegatedClient({ ...s, relayUrl }, null);
9875
- if (!client2) continue;
9876
9944
  const { contexts } = await client2.admin.getContexts();
9877
9945
  for (const c of contexts ?? []) {
9878
9946
  out.push({ ...c, relayUrl });
9879
9947
  map.contexts[c.id] = relayUrl;
9880
9948
  }
9881
9949
  } catch (e) {
9950
+ failures.push({ relayUrl, kind: "unavailable", error: e });
9882
9951
  console.warn(`[mero-js] could not list contexts on ${relayUrl}`, e);
9883
9952
  }
9884
9953
  }
9885
9954
  writeRelayMap(s.account, map);
9955
+ if (relays.length > 0 && failures.length === relays.length) throw new RelaysUnavailableError(failures);
9886
9956
  return out;
9887
9957
  }
9888
9958
  async function listDelegatedNamespaces(s) {
@@ -9890,11 +9960,16 @@ async function listDelegatedNamespaces(s) {
9890
9960
  const out = [];
9891
9961
  const seen = /* @__PURE__ */ new Set();
9892
9962
  const map = readRelayMap(s.account);
9893
- for (const relayUrl of knownRelays(s)) {
9963
+ const relays = knownRelays(s);
9964
+ const failures = [];
9965
+ for (const relayUrl of relays) {
9966
+ const got = await listingClient(s, relayUrl);
9967
+ if ("failure" in got) {
9968
+ failures.push(got.failure);
9969
+ continue;
9970
+ }
9971
+ const { client: client2 } = got;
9894
9972
  try {
9895
- if (!await resolveRelayNodeKey(relayUrl)) continue;
9896
- const client2 = buildDelegatedClient({ ...s, relayUrl }, null);
9897
- if (!client2) continue;
9898
9973
  for (const ns of await client2.admin.listNamespaces()) {
9899
9974
  if (seen.has(ns.namespaceId)) continue;
9900
9975
  seen.add(ns.namespaceId);
@@ -9902,10 +9977,12 @@ async function listDelegatedNamespaces(s) {
9902
9977
  (_a = map.namespaces)[_b = ns.namespaceId] ?? (_a[_b] = relayUrl);
9903
9978
  }
9904
9979
  } catch (e) {
9980
+ failures.push({ relayUrl, kind: "unavailable", error: e });
9905
9981
  console.warn(`[mero-js] could not list namespaces on ${relayUrl}`, e);
9906
9982
  }
9907
9983
  }
9908
9984
  writeRelayMap(s.account, map);
9985
+ if (relays.length > 0 && failures.length === relays.length) throw new RelaysUnavailableError(failures);
9909
9986
  return out;
9910
9987
  }
9911
9988