@calimero-network/mero-js 26.4.1 → 26.5.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.
@@ -4,8 +4,8 @@ export { resolveRoot } from './account.js';
4
4
  export type { RootSource } from './account.js';
5
5
  export { resolveRootPair } from './account.js';
6
6
  export type { ResolvedRoot } from './account.js';
7
- export type { DelegatedCredential, DelegatedSession as DelegatedAccountSession, RelayMap, RelayNodeKeyAttempt } from './session.js';
8
- export { readDelegatedSession, saveDelegatedSession, clearDelegatedSession, readDelegatedCredential, saveDelegatedCredential, clearDelegatedCredential, persistedNonces, markContextNonceSpent, pinRelayNodeKey, readPinnedRelayNodeKey, attemptRelayNodeKey, resolveRelayNodeKey, learnRelayNodeKey, RELAY_NODE_KEY_RETRY_MS, forgetMethodKinds, forgetRelaySealing, relayTransportFetch, buildDelegatedClient, readRelayMap, rememberRelay, relayForContext, carryExecutorAccount, knownRelays, listDelegatedContexts, listDelegatedNamespaces, } from './session.js';
7
+ export type { DelegatedCredential, DelegatedSession as DelegatedAccountSession, RelayMap, RelayNodeKeyAttempt, RelayListingFailure } from './session.js';
8
+ export { readDelegatedSession, saveDelegatedSession, clearDelegatedSession, readDelegatedCredential, saveDelegatedCredential, clearDelegatedCredential, persistedNonces, markContextNonceSpent, pinRelayNodeKey, readPinnedRelayNodeKey, attemptRelayNodeKey, resolveRelayNodeKey, learnRelayNodeKey, RELAY_NODE_KEY_RETRY_MS, forgetMethodKinds, forgetRelaySealing, relayTransportFetch, buildDelegatedClient, readRelayMap, rememberRelay, relayForContext, carryExecutorAccount, knownRelays, listDelegatedContexts, listDelegatedNamespaces, RelaysUnavailableError, } from './session.js';
9
9
  export { resolveRelayFromInvitation, normaliseAccount } from './relay-from-invitation.js';
10
10
  export type { RelayResolutionStep, RelayResolutionFailure, ResolvedInvitationRelay, ResolveRelayResult, ResolveRelayFromInvitationInput, } from './relay-from-invitation.js';
11
11
  export { joinWithNode } from './join-with-node.js';
@@ -2,7 +2,7 @@
2
2
  export { generateAccountRoot, accountRootFromPhrase, accountRootFromSecret, createAccountRootSigner, accountRootSignerFromPhrase, signAccountLink, signAccountLogin, } from './account.js';
3
3
  export { resolveRoot } from './account.js';
4
4
  export { resolveRootPair } from './account.js';
5
- export { readDelegatedSession, saveDelegatedSession, clearDelegatedSession, readDelegatedCredential, saveDelegatedCredential, clearDelegatedCredential, persistedNonces, markContextNonceSpent, pinRelayNodeKey, readPinnedRelayNodeKey, attemptRelayNodeKey, resolveRelayNodeKey, learnRelayNodeKey, RELAY_NODE_KEY_RETRY_MS, forgetMethodKinds, forgetRelaySealing, relayTransportFetch, buildDelegatedClient, readRelayMap, rememberRelay, relayForContext, carryExecutorAccount, knownRelays, listDelegatedContexts, listDelegatedNamespaces, } from './session.js';
5
+ export { readDelegatedSession, saveDelegatedSession, clearDelegatedSession, readDelegatedCredential, saveDelegatedCredential, clearDelegatedCredential, persistedNonces, markContextNonceSpent, pinRelayNodeKey, readPinnedRelayNodeKey, attemptRelayNodeKey, resolveRelayNodeKey, learnRelayNodeKey, RELAY_NODE_KEY_RETRY_MS, forgetMethodKinds, forgetRelaySealing, relayTransportFetch, buildDelegatedClient, readRelayMap, rememberRelay, relayForContext, carryExecutorAccount, knownRelays, listDelegatedContexts, listDelegatedNamespaces, RelaysUnavailableError, } from './session.js';
6
6
  export { resolveRelayFromInvitation, normaliseAccount } from './relay-from-invitation.js';
7
7
  export { joinWithNode } from './join-with-node.js';
8
8
  export { bootstrapFromInvitation } from './bootstrap-from-invitation.js';
@@ -209,6 +209,36 @@ export declare function relayForContext(account: string, contextId: string): str
209
209
  export declare function carryExecutorAccount(prev: DelegatedSession | null, next: DelegatedSession): DelegatedSession;
210
210
  /** Every relay this account is known to use: the session's, and each in the map. */
211
211
  export declare function knownRelays(s: DelegatedSession): string[];
212
+ /**
213
+ * Thrown by {@link listDelegatedContexts} and {@link listDelegatedNamespaces}
214
+ * when the account knows relays and **none** of them answered.
215
+ *
216
+ * One relay down is skipped, as before: the others still answer for what they
217
+ * serve. Every relay down is different. Returning `[]` then reads as "a member
218
+ * of nothing", and an app renders an empty account, with no error, for what is
219
+ * really "could not ask". A relay whose hostname no longer resolves (a node
220
+ * rebuilt or deleted in the cloud) did exactly that.
221
+ */
222
+ /**
223
+ * Why one relay did not answer a listing.
224
+ *
225
+ * `refused`: its attestation did not verify (an untrusted image, a release
226
+ * below the floor). Asking again cannot change that, and it may be worth a
227
+ * closer look. `unavailable`: something on the way did not answer, or the
228
+ * relay refused the request itself; trying again later may.
229
+ */
230
+ export interface RelayListingFailure {
231
+ relayUrl: string;
232
+ kind: 'refused' | 'unavailable';
233
+ error: unknown;
234
+ }
235
+ export declare class RelaysUnavailableError extends Error {
236
+ readonly failures: readonly RelayListingFailure[];
237
+ readonly relays: readonly string[];
238
+ constructor(failures: readonly RelayListingFailure[]);
239
+ /** Every relay refused its attestation: retrying will not help. */
240
+ get refused(): boolean;
241
+ }
212
242
  type ListedContext = Awaited<ReturnType<MeroClient['admin']['getContexts']>>['contexts'][number];
213
243
  /**
214
244
  * This account's contexts on every relay it knows, each tagged with the relay
@@ -216,7 +246,8 @@ type ListedContext = Awaited<ReturnType<MeroClient['admin']['getContexts']>>['co
216
246
  *
217
247
  * Each relay answers only for itself and only for this caller, so the union is
218
248
  * exactly the account's contexts across relays. A relay that cannot be reached
219
- * or logged in to is skipped (its contexts simply do not appear), not fatal.
249
+ * or logged in to is skipped (its contexts simply do not appear), not fatal,
250
+ * unless it is every relay: then {@link RelaysUnavailableError}.
220
251
  */
221
252
  export declare function listDelegatedContexts(s: DelegatedSession): Promise<Array<ListedContext & {
222
253
  relayUrl: string;
@@ -226,7 +257,8 @@ type ListedNamespace = Awaited<ReturnType<MeroClient['admin']['listNamespaces']>
226
257
  * This account's namespaces on every relay it knows, each tagged with the relay
227
258
  * that listed it and recorded as that namespace's relay. Same rules as
228
259
  * {@link listDelegatedContexts}: each relay answers for itself and this caller
229
- * only (`namespace:list-own`); an unreachable relay is skipped.
260
+ * only (`namespace:list-own`); an unreachable relay is skipped, and every
261
+ * relay unreachable is {@link RelaysUnavailableError}.
230
262
  */
231
263
  export declare function listDelegatedNamespaces(s: DelegatedSession): Promise<Array<ListedNamespace & {
232
264
  relayUrl: string;
@@ -804,24 +804,56 @@ export function knownRelays(s) {
804
804
  .map(relayOrigin);
805
805
  return [...new Set(all)];
806
806
  }
807
+ export class RelaysUnavailableError extends Error {
808
+ constructor(failures) {
809
+ super(`none of this account's relays answered: ${failures.map((f) => `${f.relayUrl} (${f.kind})`).join(', ')}`);
810
+ this.failures = failures;
811
+ this.name = 'RelaysUnavailableError';
812
+ this.relays = failures.map((f) => f.relayUrl);
813
+ }
814
+ /** Every relay refused its attestation: retrying will not help. */
815
+ get refused() {
816
+ return this.failures.every((f) => f.kind === 'refused');
817
+ }
818
+ }
819
+ /**
820
+ * One relay's client for a listing, or the failure that stopped it. The key's
821
+ * own outcome is kept, so a refused attestation is not reported as a relay
822
+ * that merely did not answer.
823
+ */
824
+ async function listingClient(s, relayUrl) {
825
+ const key = await attemptRelayNodeKey(relayUrl);
826
+ if (key.kind !== 'learned') {
827
+ console.warn('[mero-js] could not learn the relay node key from its attestation', key.error);
828
+ return { failure: { relayUrl, kind: key.kind, error: key.error } };
829
+ }
830
+ const client = buildDelegatedClient({ ...s, relayUrl }, null);
831
+ if (!client)
832
+ return { failure: { relayUrl, kind: 'unavailable', error: new Error(`no client for ${relayUrl}`) } };
833
+ return { client };
834
+ }
807
835
  /**
808
836
  * This account's contexts on every relay it knows, each tagged with the relay
809
837
  * that listed it — and that relay recorded as the one serving it.
810
838
  *
811
839
  * Each relay answers only for itself and only for this caller, so the union is
812
840
  * exactly the account's contexts across relays. A relay that cannot be reached
813
- * or logged in to is skipped (its contexts simply do not appear), not fatal.
841
+ * or logged in to is skipped (its contexts simply do not appear), not fatal,
842
+ * unless it is every relay: then {@link RelaysUnavailableError}.
814
843
  */
815
844
  export async function listDelegatedContexts(s) {
816
845
  const out = [];
817
846
  const map = readRelayMap(s.account);
818
- for (const relayUrl of knownRelays(s)) {
847
+ const relays = knownRelays(s);
848
+ const failures = [];
849
+ for (const relayUrl of relays) {
850
+ const got = await listingClient(s, relayUrl);
851
+ if ('failure' in got) {
852
+ failures.push(got.failure);
853
+ continue;
854
+ }
855
+ const { client } = got;
819
856
  try {
820
- if (!(await resolveRelayNodeKey(relayUrl)))
821
- continue;
822
- const client = buildDelegatedClient({ ...s, relayUrl }, null);
823
- if (!client)
824
- continue;
825
857
  const { contexts } = await client.admin.getContexts();
826
858
  for (const c of contexts ?? []) {
827
859
  out.push({ ...c, relayUrl });
@@ -829,30 +861,37 @@ export async function listDelegatedContexts(s) {
829
861
  }
830
862
  }
831
863
  catch (e) {
864
+ failures.push({ relayUrl, kind: 'unavailable', error: e });
832
865
  console.warn(`[mero-js] could not list contexts on ${relayUrl}`, e);
833
866
  }
834
867
  }
835
868
  writeRelayMap(s.account, map);
869
+ if (relays.length > 0 && failures.length === relays.length)
870
+ throw new RelaysUnavailableError(failures);
836
871
  return out;
837
872
  }
838
873
  /**
839
874
  * This account's namespaces on every relay it knows, each tagged with the relay
840
875
  * that listed it and recorded as that namespace's relay. Same rules as
841
876
  * {@link listDelegatedContexts}: each relay answers for itself and this caller
842
- * only (`namespace:list-own`); an unreachable relay is skipped.
877
+ * only (`namespace:list-own`); an unreachable relay is skipped, and every
878
+ * relay unreachable is {@link RelaysUnavailableError}.
843
879
  */
844
880
  export async function listDelegatedNamespaces(s) {
845
881
  var _a, _b;
846
882
  const out = [];
847
883
  const seen = new Set();
848
884
  const map = readRelayMap(s.account);
849
- for (const relayUrl of knownRelays(s)) {
885
+ const relays = knownRelays(s);
886
+ const failures = [];
887
+ for (const relayUrl of relays) {
888
+ const got = await listingClient(s, relayUrl);
889
+ if ('failure' in got) {
890
+ failures.push(got.failure);
891
+ continue;
892
+ }
893
+ const { client } = got;
850
894
  try {
851
- if (!(await resolveRelayNodeKey(relayUrl)))
852
- continue;
853
- const client = buildDelegatedClient({ ...s, relayUrl }, null);
854
- if (!client)
855
- continue;
856
895
  for (const ns of await client.admin.listNamespaces()) {
857
896
  if (seen.has(ns.namespaceId))
858
897
  continue;
@@ -862,9 +901,12 @@ export async function listDelegatedNamespaces(s) {
862
901
  }
863
902
  }
864
903
  catch (e) {
904
+ failures.push({ relayUrl, kind: 'unavailable', error: e });
865
905
  console.warn(`[mero-js] could not list namespaces on ${relayUrl}`, e);
866
906
  }
867
907
  }
868
908
  writeRelayMap(s.account, map);
909
+ if (relays.length > 0 && failures.length === relays.length)
910
+ throw new RelaysUnavailableError(failures);
869
911
  return out;
870
912
  }